Skip to content

zcode-cli 3.8.1-27

Choose a tag to compare

@xhqing xhqing released this 05 Sep 16:50
· 17 commits to main since this release

Treat official-slot API key sign-in as login (vault token first, key second), clear official-slot keys on logout, and force-collect a user name on BigModel login bound to the key mapping

  • API key sign-in counts as signed-in: sign-in detection upgrades from "vault OAuth token only" to a two-layer check (vault token first, official-slot key second), and logout clears official-slot keys as well (src/identity.ts, src/launcher.ts, packages/zcode-tui/src/index.ts, test/identity.test.ts, test/login-identity.test.ts, README.md, README_zh_hans.md, README_zh_hant.md, docs/CONFIGURATION.md).
    • Why: 3.8.1-26 anchored sign-in state solely to the vault oauth:<provider>:access_token. A user who completed /login by pasting an API key (the wizard reported success, the key landed in the official slot, the model switched to the official slot) still saw a "Not signed in" banner and no identity field in the status bar — the login flow said success while the identity display said signed out, a contradiction; it was also a display regression versus 3.8.1-25 (an official slot holding a key already showed a key identity) and violated the 3.8.1-26 principle that "a user action must produce correct feedback". The product ruling: a key pasted via /login is a login; only access through a custom-provider file (env- slots) counts as signed out.
    • What changed: (1) layered sign-in detection (new readSignedInProvider() in src/identity.ts): a vault OAuth token wins; with no token, a key in the official zai/bigmodel slots also counts as signed in (the official slot model.main points to is checked first, otherwise scan zai-first); (2) key identity restored for key sign-ins (readLoginIdentitySnapshot()): a key sign-in shows the key identity while an OAuth sign-in still shows the account name with absolute priority; signedOut narrows to env--slot-only access; (3) mapping names never impersonate account identity (a second same-day ruling): names in bigmodel-users.json are user-chosen key aliases and two accounts may share a name, so the "Signed in as " phrasing is reserved for OAuth account sign-ins (the account name the system actually read); key sign-ins always use the "API key" phrasing — with a mapping: API key <name> (<masked key>) (the snapshot carries a new keyMasked field), without: API key <masked key> — switching accounts (changing keys) necessarily changes the display, with one consistent reading across the banner / status bar / zcode identity / the zcode login already-signed-in notice; (4) the launcher adopts the new detection (two places in src/launcher.ts): the startup-sync skipModelBlock (a signed-in user's model block is not taken over by the env file) and the bare zcode login "already signed in" gate both cover key sign-ins — after pasting a key and restarting, the model is no longer pinned back to an env- slot; (5) logout clears official-slot keys (clearOAuthLoginCredentials() extended): beyond the vault it also clears the config official slots' apiKey (env- slots are kept to serve the signed-out path) — a key sign-out now truly returns to signed-out instead of "logged out but the identity lingers"; (6) TUI status bar fix (index.ts): a signedOut identity no longer emits a status-bar field (the old logic rendered a stray empty user label); a named identity's prefix changes from user to key and carries the masked key; (7) zcode identity set is refused in a key sign-in state and points to the bigmodel-users.json mapping (a key's display name belongs to the mapping file; only OAuth account names belong to identity set); (8) BigModel login force-collects a user name (user ruling: "however you sign in, running /login first forces a user-name entry"): any TUI /login that goes through a BigModel option (OAuth or pasting a key) — whether picked from the plain /login menu or typed as the command — pops a name input before the login runs (non-empty enforced, Esc cancels the login; the placeholder previews the key's existing mapping name); on success the name is automatically upserted into bigmodel-users.json bound to the landed key (new writeBigmodelUserName() in src/bigmodel-users.ts; hand-edited and login-collected entries are equivalent and freely mixable), and the display immediately refreshes to API key <name> (<masked key>). Z.AI sign-ins are not asked (the OAuth flow itself writes back the account name); a failed or cancelled login writes nothing; (9) docs synced (the sign-in permission sections of all three READMEs, the Sign-in identity and custom-provider sections, CONFIGURATION.md's login definition, logout behavior, and key-mapping sections).
    • Behavior map: /login with a pasted key → the key lands in the official slot + the model switches to the official slot + the banner immediately shows "API key ()" or "API key " + the status bar shows a key <name> (<masked>) field; restarting in a key sign-in state keeps the model on the official slot (the env file no longer takes over); /logout → vault + official-slot keys fully cleared, the banner returns to "Not signed in", and the env file takes the model back on the next start. OAuth sign-ins keep showing "Signed in as " exactly as in 3.8.1-26.
    • Verification: tsc --noEmit passes; full bun test 689 pass / 0 fail (78 files; identity gains 4 key-sign-in identity + 3 readSignedInProvider + 1 identity-set-in-key-state + 2 writeBigmodelUserName cases, the logout cases now assert official-slot keys are cleared, login-identity gains 3 key sign-in + 2 shouldPromptForLoginUserName cases); the release build's TUI smoke (scripts/smoke-tui.ts) adds live coverage of the user-name prompt — in the BigModel paste-key flow it waits for the name prompt after the key enter, types smoke to continue the login, and adds a banner API key smoke (<masked>) assertion plus a bigmodel-users.json binding assertion (the mapping file is addressed by the raw key, permission 0600, sits beside config.json as a sanctioned key store, joins the smoke leak whitelist, and is positively asserted).
    • Upstream research (2026-09-05, no code changes): confirmed upstream 3.11.2 (the official stable, three minors ahead of this project's locked 3.8.1) still fails to obtain the BigModel login user name — reversing the 3.11.2 runtime shows the shared credential store is structurally identical to 3.8.1 (only zai has a user_info slot) and loginBigmodelCodingPlan is structure-for-structure isomorphic (authorize → exchangeCode → exchange key → write config, never fetching a user name); the official changelog corroborates (the only login-related entries after 3.8.1 are three stability fixes: expired sign-in state / authorization callback failure / mcp oauth failure). Incidental finding: during the key exchange the runtime calls GET bigmodel.cn/api/biz/customer/getCustomerInfo, whose response carries account / organization info but only the ID is kept and then discarded, and the accessToken is never persisted nor callable outside the flow — this spawned two follow-ups (T3: evaluate upgrading the runtime to 3.11.2; T4: research the key → account info API).

Install

npm install -g https://github.com/xhqing/zcode-cli/releases/download/v3.8.1-27/zcode-cli-3.8.1-27.tgz