Skip to content

zcode-cli 3.8.1-25

Choose a tag to compare

@xhqing xhqing released this 05 Sep 07:42
· 21 commits to main since this release

Add BigModel key-name mapping, fix logout leaving BigModel credentials, and isolate env-file config into env- slots

  • Add a BigModel API key → display-name mapping file ~/.zcode/cli/bigmodel-users.json: per-key custom sign-in display names (a user name, a key name, or any label — purely local display text), so account switches no longer need re-pinning; a hint is printed after login when no mapping exists yet (new src/bigmodel-users.ts, src/identity.ts, src/launcher.ts, packages/zcode-tui/src/login-identity.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: BigModel logins (OAuth and API-key variants) only write the exchanged / pasted API key into config.json and never learn the account name, so the identity display falls back to the masked key; multi-account users cannot tell who is signed in after a switch, and the existing zcode identity set mechanism is a provider-level snapshot that must be manually re-pinned on every switch — miss it and the wrong name shows. With a per-key mapping, every key carries its own display name and switches are correct by construction.
    • What changed: (1) new src/bigmodel-users.ts — bigmodelUsersPath() (~/.zcode/cli/bigmodel-users.json, owned by the BigModel login channel and deliberately kept out of the model-access .env), readBigmodelUserNames() (fault-tolerant read: a missing file, bad JSON, or a non-object all yield an empty map; non-string and blank entries are skipped), resolveBigmodelUserName(); (2) identity resolution (readLoginIdentitySnapshot in src/identity.ts and its TUI mirror readLoginIdentity in login-identity.ts): priority is vault user_info snapshot → BigModel key lookup in the mapping (a hit returns the new kind named; the banner / status bar show Signed in as <name>, status-bar prefix user) → masked-key fallback; the mapping only applies to provider bigmodel (zai / custom providers are not looked up); (3) hints in three places: readBigModelKeyNameHint() detects "bigmodel + identity resolved to the masked key + no mapping"; the TUI prints it via suggestBigModelKeyName() after the non-attributable /login variants (bigmodel-coding-plan and the two api-key variants), the bare CLI zcode login prints it via printBigModelKeyNameHint() after success, and zcode identity appends a Tip line when showing a masked key; hints contain only the masked key and the file path, never the full key; (4) 13 new test cases (8 src-side: named resolution, oauth priority, provider restriction, read fault tolerance, hint positive/negative cases, identity-command Tip presence; 5 TUI-side: named / not covered / bad file / non-bigmodel / oauth priority, plus a loginIdentityText assertion for the named wording).
    • Scope note: the mapped value is entirely up to the user — a user name, a key remark, an account name, or any custom label works; neither the implementation nor the documentation constrains its semantics (purely local display text, no effect on authentication or requests). The .env channel's env-bigmodel slot is not looked up (the established boundary keeping it decoupled from /login). The zcode identity set mechanism is kept unchanged (it suits renaming within the same account; the mapping suits multiple keys / multiple accounts — the two complement each other).
  • Fix /logout failing to remove BigModel credentials: intercepted on both the CLI and TUI sides with a complete deletion list (zai + bigmodel + shared markers) (src/identity.ts, src/launcher.ts, packages/zcode-tui/src/index.ts, test/identity.test.ts).
    • Why: users reported that after /logout in the TUI the model still worked and the account username kept showing at the bottom. Reverse engineering vendor/zcode.cjs confirmed the root cause — the runtime's logout ultimately calls clearZaiLoginCredentials(), hardcoded to delete only 4 fixed keys (oauth:zai:access_token / oauth:zai:refresh_token / oauth:zai:user_info / zcodejwttoken, plus oauth:active_provider only when it decrypts to zai), while the keys written by the BigModel OAuth flow — oauth:bigmodel:access_token, oauth:bigmodel:user_info, oauth:login_attribution — are not on the list: the CLI zcode logout really prints "Logged out from Z.AI" and the credential file is indeed written, yet not a single vault entry is removed. The direct cause of the lingering username is oauth:bigmodel:user_info never being deleted (the TUI identity display reads it first).
    • What changed: (1) src/identity.ts adds clearOAuthLoginCredentials() — the deletion list covers both providers' full credential sets plus shared markers (the zai trio, bigmodel access/refresh/user_info, oauth:login_attribution, oauth:active_provider, zcodejwttoken), preserving unrelated entries (e.g. the zcodefeedbackclientid telemetry ID), idempotent (a missing or already-empty vault both succeed); runLogoutCommand() / isLogoutInvocation() form the CLI command entry; (2) launcher.ts intercepts zcode logout after the identity routing instead of passing it through to the runtime (the runtime's deletion list is a subset of this implementation, so the intercepted behavior is a superset); (3) the TUI submit() intercepts /logout locally (same layer as the suspended /login zai-coding-plan) with the new handleLocalLogout(): clear credentials → notice feedback → re-check the login state via readConfiguredModelAccess() (the loginRequired warning is only set when no model access is configured; .env / manual-config users keep model access after logout, matching the semantic boundary that an API key is model-access configuration, not a login state) → setLoginRequired triggers an inline identity refresh (user_info is gone, the display falls back to the masked key or disappears).
    • Scope note: logout only clears vault login credentials, not the API key in config.json (consistent with official runtime semantics — the OAuth-exchanged key is an independently valid credential; to cut off model access, delete .env or edit config).
  • .env configuration now writes its own env-<provider-id> slot, fully decoupled from the /login / /logout OAuth system (src/env-config.ts, packages/zcode-tui/src/index.ts, test/env-config.test.ts).
    • Why: the intent of .env is a custom-provider channel (universal for z.ai / bigmodel.cn / deepseek or any provider, without affecting normal login/logout), but the old implementation wrote the .env key directly into the config slot of the declared ID (ZCODE_PROVIDER_ID=bigmodel wrote provider.bigmodel) — the same official slot OAuth logins write to — creating a three-way tangle: the .env key was treated as an official login artifact (the TUI showed the OAuth username), an OAuth login would overwrite the .env configuration, and zcode login reported "no login needed" because the official slot had a key. The default was traced back to a deliberate design (borrowing the official slot to satisfy the upstream login gate — the runtime's hasConfiguredCodingPlanApiKey only recognizes the zai/bigmodel slots), but the cost was exactly the tangle above, conflicting with .env's positioning as a universal channel for any provider.
    • What changed: (1) buildProviderConfig() in env-config.ts now always outputs the env-<declared id> slot (ZCODE_PROVIDER_ID=bigmodel → writes provider["env-bigmodel"], model.main = "env-bigmodel/glm-5.2"; the declared id is still used for the base-URL default table and the provider display name); the envProviderSlotPrefix constant is exported; the official zai/bigmodel slots now belong exclusively to the OAuth flow — .env and /login never overwrite each other; (2) the TUI's handleResult() re-checks a runtime-pushed loginRequired=true with readConfiguredModelAccess() (slot-agnostic: only checks whether the provider pointed to by model.main has a non-empty key) and suppresses the "Model access is not configured" warning when configured — compensating for the runtime login gate only recognizing the two official slots; env-* slots and manually configured custom providers both benefit; (3) the identity display needed no changes and degrades automatically (login-identity.ts only consults the vault's user_info for zai/bigmodel; env-* slots show the masked key).
    • Migration note: on the first start after upgrading, the .env sync switches to the env-* slot; keys left behind in the old official slots are kept (harmless residue an OAuth login can overwrite normally). Existing users will see the model identifier change from bigmodel/glm-5.3 to env-bigmodel/glm-5.3 (semantically more accurate: this is .env-channel configuration).
    • Verification: tsc --noEmit passes; full bun test 644 pass / 0 fail (78 files; env-config slot assertions updated plus a strengthened "official slots are never touched by .env" case; 5 new identity logout cases).

Install

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