fix(cc-config): one account is one profile, and rename it in place
Four defects, all from one design mistake: a profile's display name and its filename were the same string. - Saving the logged-in account made a SECOND profile of an account another profile already held. Both then reported themselves active, so there was nothing to switch between. `POST /profiles` now refuses `EDUPLICATE` and names the profile that already holds the account. Re-saving into the account's own profile stays allowed - that is the token-refresh path. - Worse, an accidental duplicate could not be cleaned up: two profiles of one account are both active, and the delete guard refused any active profile, so neither was ever deletable. The guard now blocks only the LAST saved copy of the live account, which is the actual credential-loss condition it exists for. The trash button had to follow, or the server would allow what the UI hid. - A name could not contain spaces or diacritics, because it had to be filename-safe. A profile now carries `name` (internal identifier, still the filename, still what every route takes) and `label` (what the user reads and edits: 1-60 free-form characters, control characters rejected, stored trimmed, unique case-insensitively). A label never touches a path, which is what makes "Tài khoản chính" a legal name. - Naming happened up front in a "save current account as" field. That field is gone. The server derives the identifier, the save button lives on the live account's card and appears only while it is unsaved, and the label is edited by clicking it (Enter saves, Escape cancels). Renaming rewrites the label in place and deliberately does NOT move `name`, so it cannot half-fail and leave a profile unreachable. Profiles written before labels existed report `label` equal to their `name`, so nothing needs migrating. Adds `PATCH /api/cc-config/profiles`; `POST` now takes an optional `name`. Verified: server 1431/1431 (11 new), client 459/459, tsc clean. Also exercised end to end against a COPY of the real profile store on this machine: the duplicate deleted, the last copy still refused, a Vietnamese label accepted, and the stored access/refresh tokens and 0600 mode intact after a rename.
This commit is contained in:
+3
-2
@@ -678,9 +678,10 @@ Reads — and carefully gated mutations for low-risk text-file artifacts — for
|
||||
| `PUT` | `/api/cc-config/file` | Create or overwrite a text-file artifact (skills/agents/commands/output-styles/memory). Body: `{ scope, type, name?, content }`. Auto-backs-up if file exists. Atomic temp + rename. 256 KB cap. Per-project file-based memory is also editable via `{ scope: "auto-memory", type: "auto-memory", project, name }` — backups land under `<memory-dir>/.cc-config-backups/auto-memory/`, and an invalid project slug returns `EBADPROJECT` |
|
||||
| `DELETE` | `/api/cc-config/file` | Backup-then-delete a text-file artifact. Skill dirs are backed up whole before recursive removal |
|
||||
| `GET` | `/api/cc-config/profiles` | `{ items, active }` — saved auth profiles + the account logged in right now. Identity metadata only, **never token material** |
|
||||
| `POST` | `/api/cc-config/profiles` | Snapshot the logged-in account as a profile. Body: `{ name }`. `ENOAUTH` when nobody is logged in |
|
||||
| `POST` | `/api/cc-config/profiles` | Snapshot the logged-in account as a profile. Body: `{ name? }` — omitted, the identifier and initial label are derived from the account. `EDUPLICATE` when another profile already holds this account, `ENOAUTH` when nobody is logged in |
|
||||
| `PATCH` | `/api/cc-config/profiles` | Rename a profile's display `label` (1-60 chars, spaces and diacritics fine, unique case-insensitively). Body: `{ name, label }`. The `name` identifier never moves; `EBADLABEL` / `ELABELTAKEN` |
|
||||
| `POST` | `/api/cc-config/profiles/activate` | Make a saved profile the live account — swaps `claudeAiOauth` + `oauthAccount`/`userID` and nothing else. Body: `{ name }`. Re-snapshots the outgoing account first (`savedOutgoing`); `EUNSAVED` if it was never saved |
|
||||
| `DELETE` | `/api/cc-config/profiles` | Forget a saved profile. Body: `{ name }`. `EACTIVE` for the active one |
|
||||
| `DELETE` | `/api/cc-config/profiles` | Forget a saved profile. Body: `{ name }`. `EACTIVE` only for the **last** saved copy of the live account — a redundant copy stays deletable |
|
||||
|
||||
### Run a lane's CLI (`/api/run`)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user