feat(cc-config): switch between Claude Code accounts, keeping the workspace
Adds a "Switch profile" button left of Backups on /settings?tool=claude-code. A *profile* is one Anthropic account - one quota - saved under a name, so the local `claude` CLI can be pointed at a different account without disturbing anything else. A switch swaps exactly two identity artifacts and nothing more: `claudeAiOauth` inside `<CLAUDE_HOME>/.credentials.json`, and `oauthAccount` + `userID` inside the `.claude.json` config file(s). Those files are key-merged in place, and only where they already exist, so `projects`/`numStartups`/everything else survives. Skills, subagents, commands, memory, plugins, marketplaces, MCP servers and settings are never read or written here - that is what makes a switch keep the whole workspace and change only the account. `mcpOAuth` stays put too: those are the machine's MCP server tokens, not account identity. Profiles are one JSON file each under `<CLAUDE_HOME>/agenthub-profiles/` (dir 0700, files 0600, atomic temp+rename) and hold live OAuth tokens, so no route ever returns token material - `GET /profiles` answers identity metadata only. The dashboard cannot log an account in; only `claude auth login` mints tokens. Adding a profile is therefore always "log in from a terminal, then snapshot what the CLI wrote". `activateProfile` re-snapshots the outgoing account first so tokens Claude Code refreshed since its last save are not lost, and refuses (`EUNSAVED`) to switch away from an account that was never saved - that would discard the only copy of its credentials. A switch applies to new sessions only, since a running session rewrites the config on exit. Routes: GET/POST/DELETE /api/cc-config/profiles, POST /profiles/activate. Verified: server 1420/1420 (14 new, incl. token-free responses and 0600/0700 permissions), client 459/459.
This commit is contained in:
@@ -215,6 +215,29 @@
|
||||
"scopeLabel": "Scope",
|
||||
"filterAll": "All types"
|
||||
},
|
||||
"profiles": {
|
||||
"title": "Claude Code profiles",
|
||||
"openButton": "Switch profile",
|
||||
"subtitle": "A profile is one Anthropic account - its own quota. Switching swaps only the login (credentials + account identity); skills, subagents, memory, plugins, marketplaces, MCP servers and settings all stay exactly as they are.",
|
||||
"warning": "Close your running Claude Code sessions before switching - a live session rewrites the config on exit and would put the old account back.",
|
||||
"activeLabel": "Logged in now",
|
||||
"noActive": "No Claude account is logged in on this machine.",
|
||||
"unsavedActive": "This account is not saved as a profile yet - save it below, otherwise you cannot switch away without losing its credentials.",
|
||||
"saveLabel": "Save the current account as a profile",
|
||||
"namePlaceholder": "profile name (e.g. work)",
|
||||
"saveButton": "Save",
|
||||
"saveHint": "The dashboard cannot log in for you. To add another account: run `claude auth login` in a terminal, sign in, then come back and save it under a new name.",
|
||||
"saveSuccess": "Saved the current account as profile \"{{name}}\".",
|
||||
"empty": "No profiles saved yet - save the account you are logged in as to get started.",
|
||||
"activeBadge": "active",
|
||||
"switchButton": "Switch to",
|
||||
"switching": "Switching…",
|
||||
"switchSuccess": "Switched to profile \"{{name}}\". Start a new Claude Code session to pick it up.",
|
||||
"deleteButton": "Delete profile",
|
||||
"deleteSuccess": "Deleted profile \"{{name}}\".",
|
||||
"savedAt": "Saved {{time}}",
|
||||
"loginHint": "Add another account by logging in from a terminal first:"
|
||||
},
|
||||
"explain": {
|
||||
"hooks": {
|
||||
"title": "Editing hooks writes settings.json",
|
||||
|
||||
@@ -210,6 +210,29 @@
|
||||
"scopeLabel": "Phạm vi",
|
||||
"filterAll": "Tất cả loại"
|
||||
},
|
||||
"profiles": {
|
||||
"title": "Profile Claude Code",
|
||||
"openButton": "Đổi profile",
|
||||
"subtitle": "Mỗi profile là một tài khoản Anthropic - quota riêng. Khi đổi, chỉ phần đăng nhập (credentials + danh tính tài khoản) được hoán đổi; skills, subagent, memory, plugin, marketplace, MCP server và settings vẫn giữ nguyên.",
|
||||
"warning": "Hãy đóng các phiên Claude Code đang chạy trước khi đổi - phiên đang chạy sẽ ghi lại config khi thoát và đưa tài khoản cũ quay về.",
|
||||
"activeLabel": "Đang đăng nhập",
|
||||
"noActive": "Chưa có tài khoản Claude nào đăng nhập trên máy này.",
|
||||
"unsavedActive": "Tài khoản này chưa được lưu thành profile - hãy lưu bên dưới, nếu không sẽ không thể đổi sang profile khác mà không mất credentials của nó.",
|
||||
"saveLabel": "Lưu tài khoản hiện tại thành profile",
|
||||
"namePlaceholder": "tên profile (vd: work)",
|
||||
"saveButton": "Lưu",
|
||||
"saveHint": "Dashboard không thể đăng nhập thay bạn. Để thêm tài khoản khác: chạy `claude auth login` trong terminal, đăng nhập, rồi quay lại đây lưu với tên mới.",
|
||||
"saveSuccess": "Đã lưu tài khoản hiện tại thành profile \"{{name}}\".",
|
||||
"empty": "Chưa có profile nào - hãy lưu tài khoản đang đăng nhập để bắt đầu.",
|
||||
"activeBadge": "đang dùng",
|
||||
"switchButton": "Chuyển sang",
|
||||
"switching": "Đang chuyển…",
|
||||
"switchSuccess": "Đã chuyển sang profile \"{{name}}\". Mở phiên Claude Code mới để áp dụng.",
|
||||
"deleteButton": "Xoá profile",
|
||||
"deleteSuccess": "Đã xoá profile \"{{name}}\".",
|
||||
"savedAt": "Lưu lúc {{time}}",
|
||||
"loginHint": "Thêm tài khoản khác bằng cách đăng nhập từ terminal trước:"
|
||||
},
|
||||
"explain": {
|
||||
"hooks": {
|
||||
"title": "Sửa hooks tức là ghi vào settings.json",
|
||||
|
||||
Reference in New Issue
Block a user