账号、Coding Plan 与闲时计划

zcode login 怎样经 ZCode 平台中转完成浏览器授权,z.ai 与 bigmodel 两个账号区域怎样区分,令牌与 API Key 存在哪、怎样加密;个人、团队 Coding Plan、Start Plan 与隐藏的闲时计划各自用什么凭据、配哪些模型,额度又从哪里查。

作者 David更新于 25 篇(共 47 篇)

智谱把 ZCode 的模型服务做成了订阅:登录 z.ai 或 bigmodel 账号,开通 Coding Plan,就能用上 GLM 系列模型,不必自己填 API Key。这一篇讲这条“账号型”路径:命令行登录怎样完成授权,登录态存在哪里,怎样在每次模型请求前变成一份凭据;各档套餐对应哪些内置模型,隐藏的闲时计划又是什么。套餐在规则集里长什么样见Provider 规则、模型目录与选项映射,凭据怎样被塞进请求见模型适配层

代码分两处。Agent CLI 自己能完成登录:apps/zcode-cli/packages/adapters/src/auth 负责授权客户端、API Key 解析与凭据文件,bootstrap/src/auth-login*.ts 串起流程,cli/src/login-command.tstui-auth.ts 是入口。桌面端的登录、权益判定、购买与额度在 packages/services/srcoauthmodel-providerbigmodelcoding-plan-subscriptionusage-stats 里,跨端类型在 packages/shared/src

怎么用

场景做法
用 z.ai 账号登录zcode login,等价于 zcode login zai
用 bigmodel 账号登录zcode login bigmodel
不自动打开浏览器--no-browser,只打印授权地址
机器可读输出--json,授权地址改写到 stderr,stdout 只有一段 JSON
退出登录zcode logout
TUI 里配置/login 弹出四个选项:Z.AI、BigModel 两家的浏览器授权,以及两家的 Coding Plan API Key 手工粘贴

用法字符串是 zcode login [zai|bigmodel] [--no-browser],别的参数一律报错(apps/zcode-cli/packages/cli/src/login-command.ts:15)。TUI 的四个选项分别对应 /login zai-coding-plan/login bigmodel-coding-plan/login zai-coding-plan-api-key <key>/login bigmodel-coding-plan-api-key <key>,粘贴 Key 的输入框会掩码显示(apps/zcode-cli/packages/cli/src/command-center/login-flow.ts:1249)。登录成功后打印用户名、选中的模型、凭据文件与模型选择文件的路径(login-command.ts:67)。

登录流程:经平台中转的轮询授权

命令行登录既不是标准的设备码流程,也不在本机起回调端口:CLI 生成一个随机的轮询令牌,请 ZCode 平台开一个短期 flow,用户在浏览器里授权,CLI 凭同一个令牌轮询结果。

图表加载中…
  • 地址:平台 API 根取控制面地址加 /api/v1,缺省 https://zcode.z.ai/api/v1apps/zcode-cli/packages/adapters/src/auth/cli-oauth.ts:4apps/zcode-cli/packages/bootstrap/src/auth-login.ts:366)。轮询令牌是 32 字节随机数的十六进制串,init 与 poll 都以 Authorization: Bearer <令牌> 发送(cli-oauth.ts:689110)。
  • 校验:init 的返回必须有 flow_id、https 的 authorize_urlexpires_at,且 poll_interval_sec 不小于 1;每个响应最多读 64 KB(cli-oauth.ts:138172)。
  • 时限:整个登录默认 5 分钟,并且不会超过服务端给的 expires_atauth-login.ts:39148)。被取消或超时的尝试,即使之后收到迟到的 ready,也不会落盘(auth-login.ts:186)。
  • 结果:ready 里 token 是 ZCode 平台的 JWT,平台账号令牌按家族放在 zaibigmodel 字段下,含 access_token 与可选的 refresh_tokencli-oauth.ts:214)。

轮询循环本身(apps/zcode-cli/packages/bootstrap/src/auth-login-polling.ts:23):

  const expiresAtMs = input.initData.expires_at * 1_000;
  const deadlineMs = Math.min(input.now() + input.timeoutMs, expiresAtMs);
  const pollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, input.initData.poll_interval_sec * 1_000);

  while (input.now() < deadlineMs) {
    throwIfAborted(input.abortSignal);
    let data: CliOAuthPollData = { status: "pending" };
    try {
      data = await waitWithAbort(
        input.oauthClient.poll(
          {
            flowId: input.initData.flow_id,
            pollToken: input.pollToken,
          },
          { signal: input.abortSignal },
        ),
        input.abortSignal,
      );
    } catch (error) {
      throwIfAborted(input.abortSignal);
      if (
        error instanceof CliOAuthError &&
        !(error.httpStatus === 408 || error.httpStatus === 429 || (error.httpStatus ?? 0) >= 500)
      )
        throw error;
      // Match App polling: transient network and server failures retry at the server interval.
    }

408、429、5xx 和网络错误都当作暂时失败,按原间隔继续轮询;其余平台错误立即结束登录。桌面端 OAuth 服务的 startOAuthWithPolling 用的是同一对 /api/v1/oauth/cli/initpoll 接口,只是浏览器回调最终经官网中转页跳回 zcode://oauth/callbackpackages/services/src/oauth/oauthService.ts:609packages/services/src/oauth/providers/configUtils.ts:34)。接口注释说后端 flow “当前仅 Z.AI 支持”(packages/services/src/oauth/oauth.ts:40),实际实现对 z.ai 和 bigmodel 都走这条路(oauthService.ts:595)。adapters/src/auth 里还有一个 bigmodel 授权码交换客户端 bigmodel-oauth.ts,非测试代码里找不到调用方;localhost-callback.ts 的本地回调服务器只给 MCP 的 OAuth 用(apps/zcode-cli/packages/adapters/src/mcp/oauth-interactive.ts:15)。

两个账号区域:z.ai 与 bigmodel

两个区域在代码里叫 Provider Family,各自一套 ID 与域名(packages/shared/src/model-provider-family.ts:26):

家族根域名OAuth provider账号型 Provider
zai(Z.ai)z.aizaiaccount:zai-individual-coding-planaccount:zai-team-coding-planaccount:zai-start-plan
bigmodel(BigModel)bigmodel.cnbigmodelaccount:bigmodel-individual-coding-planaccount:bigmodel-team-coding-planaccount:bigmodel-start-plan

区分靠几处事实:命令行登录时显式指定,缺省 zailogin-command.ts:15);凭据文件里的 oauth:active_provider 记下当前登录的是哪家(apps/zcode-cli/packages/adapters/src/auth/shared-credentials.ts:16);桌面端只展示与当前登录同一家族的套餐(model-provider-family.ts:139);拿不到身份时还可以按 Provider 地址的根域名反推(model-provider-family.ts:78)。

登录拿到的 OAuth 令牌并不直接用于模型请求。CLI 随后用它在开放平台上找一把 API Key,两家只在第一步不同(apps/zcode-cli/packages/adapters/src/auth/coding-plan-api-key.ts:81):bigmodel 直接拿 access token 调 bigmodel.cn 的业务接口;z.ai 要先在 https://api.z.ai/api/auth/z/login 把它换成业务令牌(coding-plan-api-key.ts:108)。接下来两家一样:读客户信息,挑名字含“默认机构”的组织和含“默认项目”的项目,找不到就取第一个;在该项目下找名为 zcode-api-key 的 Key,没有就新建一把;最后取出密钥,有 secretKey 时拼成 apiKey.secretKey 的形式,z.ai 缺了它就报错(coding-plan-api-key.ts:143172190237)。桌面端的个人套餐走同样的逻辑,团队套餐则在团队项目下维护一把 zcode-team-api-key,类型号为 2(packages/services/src/model-provider/accountProviderApiKeyResolver.ts:123packages/services/src/bigmodel/teamPlanApiKey.ts:4)。

令牌存在哪、怎样续期

CLI 与桌面端共用一个凭据文件:~/.zcode/v2/credentials.json,数据基目录可用 ZCODE_DATA_BASE_DIR 改(shared-credentials.ts:280packages/services/src/credential/credentialService.ts:31)。里面有这些键,前几个是固定键名(shared-credentials.ts:15):

内容
oauth:active_provider当前登录的家族
zcodejwttokenZCode 平台 JWT
oauth:zai:access_tokenoauth:zai:user_infooauth:zai:refresh_tokenz.ai 账号令牌与用户信息
oauth:bigmodel:access_tokenoauth:bigmodel:user_infooauth:bigmodel:refresh_tokenbigmodel 同上
account-provider:coding-plan:<providerId>:account:<身份>:api-key某个账号型 Provider 的请求 Key
account-provider:<providerId>:identity这个 Provider 当前绑定的账号身份

后两类键名由 standaloneAccountProviderCredentialKeystandaloneAccountIdentityCredentialKey 生成,团队与 Start Plan 在桌面端有各自的变体(apps/zcode-cli/packages/bootstrap/src/app/standalone-account-provider-runtime.ts:82packages/services/src/model-provider/accountProviderCredentialKey.ts:20)。CLI 的 z.ai 登录只写 access token、JWT 与用户信息,不保存 refresh token;bigmodel 登录在服务端给了时才保存(auth-login.ts:189196)。

每个值写入前都单独加密,格式是 enc:v1: 加 AES-256-GCM 的 IV、认证标签和密文。密钥是一个口令的 SHA-256(apps/zcode-cli/packages/adapters/src/auth/credential-cipher.ts:83),口令优先取环境变量 ZCODE_CREDENTIAL_SECRET,没设时退回一个由本机事实拼出的字符串(credential-cipher.ts:87):

function resolveCredentialSecret(env: Record<string, string | undefined>): string {
  const configuredSecret = env[CREDENTIAL_SECRET_ENV_KEY]?.trim();
  if (configuredSecret) {
    return configuredSecret;
  }

  let username = "unknown";
  try {
    username = userInfo().username;
  } catch {
    // Some packaged or sandboxed runtimes cannot resolve OS user info.
  }

  return `zcode-credential-fallback:${platform()}:${homedir()}:${username}`;
}

从代码看,缺省密钥只由平台、主目录和用户名决定,能挡住“直接打开文件看到明文”,挡不住同一用户下的其他程序;桌面端的注释也写着以后可能改用 Electron safeStorage 托管密钥(credentialService.ts:22)。写入在文件锁内完成整段读改写,再以 0600 权限原子替换;文件损坏时先备份现场再报错,不会当成空文件覆盖掉别的进程写的凭据(shared-credentials.ts:306316)。

续期。桌面端的 OAuth 服务接口里有 refreshToken,但 z.ai 与 bigmodel 两个适配器都没有实现刷新方法,调用会直接报“暂未提供 refresh token 交换接口,请重新登录”(oauthService.ts:964packages/services/src/oauth/providers/providerAdapter.ts:31)。桌面端的做法是被动失效:带当前 JWT 的请求,或带当前 access token 查询用户信息的请求返回 401,就退出当前会话(packages/services/src/oauth/oauthUnauthorizedRequest.ts:21)。CLI 里没有刷新逻辑:模型请求用的是登录时取到的那把 API Key,不依赖 OAuth 令牌。zcode logout 删除所有共享键和各个人套餐 Provider 的身份与 Key,而且只删除值仍与读取时相同的项,避免误删并发登录刚写入的凭据(auth-login.ts:282)。

从登录态到请求凭据

账号型 Provider 的配置里没有 Key。Registry 只从账号层拿到“有没有权益、是不是当前连接”,真正的凭据在每次模型请求尝试之前现取,用完不进配置、不进协议(模型适配层讲了它怎样被塞进请求)。

图表加载中…

桌面端按套餐类型决定凭据(packages/services/src/model-provider/accountProviderRequestAuthService.ts:68):

  async resolveCurrent(input: AccountRequestAuthInput): Promise<AccountRequestAuthMaterial> {
    const providerId = input.providerId.trim();
    const access = await this.#resolveAccess(input.accountAccess);
    if (!access) throw new AccountRequestCredentialUnavailableError(providerId);

    if (access.planKind === "start-plan") {
      const tokenSet = await this.#options.loadOAuthTokenSet(resolveOAuthProviderId(access.family));
      return { apiKey: requireApiKey(tokenSet?.zcodeJwtToken, providerId) };
    }

    if (access.planKind === "individual-coding-plan") {
      const apiKey = await this.#options.loadIndividualPlanApiKey(providerId, access.family);
      return { apiKey: requireApiKey(apiKey, providerId) };
    }

    const apiKey = await this.#options.resolveTeamPlanApiKey(access);
    return { apiKey: requireApiKey(apiKey, providerId) };
  }

个人套餐用那把 zcode-api-key,团队套餐用团队 Key,Start Plan 则直接把 ZCode JWT 当作 API Key。桌面端拉起的 Agent 进程通过协议向 Host 请求这份材料,单次最多等 180000 毫秒(apps/zcode-cli/packages/bootstrap/src/zcode-protocol/provider-runtime-headers.ts:16)。

独立运行的 CLI 更简单,也更窄:它只认各家的个人 Coding Plan Provider(standalone-account-provider-runtime.ts:40)。凭据文件里有这个 Provider 的身份和 Key,账号层就把它标成有权益,否则显式标成无权益,好让它退出 Registry(standalone-account-provider-runtime.ts:137);请求前的端口每次重新读身份与 Key(standalone-account-provider-runtime.ts:169)。也就是说,CLI 本地并不查询订阅状态,是否真的开通了套餐,要等请求发到官方端点、被改写到 ZCode 平台网关后由服务端判定(apps/zcode-cli/packages/adapters/src/model/official-coding-plan-gateway.ts:7)。从代码看,团队套餐、Start Plan 和闲时计划在独立 CLI 里拿不到权益,都进不了 Registry。

Coding Plan 各档与内置模型

四档账号型套餐都用 anthropic-messages 协议,端点与内置模型清单以规则集为准(config/provider/zcode-builtin.json:697739823):

套餐端点内置模型说明
个人 Coding Planz.ai:https://api.z.ai/api/anthropic;bigmodel:https://open.bigmodel.cn/api/anthropicGLM-5.3、GLM-5.3-Flash独立 CLI 登录后默认选中 GLM-5.3
团队 Coding Plan同个人版GLM-5.3、GLM-5.3-Flash仅桌面端,按组织与项目选连接
Start Plan两家都是 https://zcode.z.ai/api/v1/zcode-plan/anthropicGLM-5.3-Flash、GLM-5.2、GLM-5-Turbo实际清单由服务端下发
闲时(Idle plan)两家都是 https://zcode.z.ai/api/v1/off-peak/anthropicGLM-5.3、GLM-5.3-Flash隐藏,只供闲时任务

builtinProviderModelRules 另外为个人版与团队版登记了 GLM-5.2、GLM-5-Turbo 的启用规则(zcode-builtin.json:6026),但一个 Provider 的模型列表只由内置清单加个人追加的模型 ID 组成(packages/provider/src/resolver.ts:237);从代码看,这两个模型要用户自己追加才会出现在个人与团队套餐下。Start Plan 的内置清单只是起点,账号层会用服务端返回的模型名单整体替换它,空名单也算数(packages/provider/src/account-provider-resolution.ts:106)。代码注释里 Start Plan 又叫“体验套餐”,是免费档(packages/ui/src/settings/model-provider-section/StatusCards.tsx:447)。

按规则集的叠加顺序算下来(叠加方式见Provider 规则一篇),Coding Plan 站点上四个模型的有效能力如下:

模型上下文输入推理档位输出上限
GLM-5.31000000文本、图片、视频lowhighmax128000
GLM-5.3-Flash1000000文本、图片、视频、PDFlowhighmax128000
GLM-5.21000000文本、图片、视频disabledhighmax128000
GLM-5-Turbo200000文本、图片、视频disabledenabled64000

图片与视频输入是 Coding Plan 与 Start Plan 站点规则统一打开的(zcode-builtin.json:4012);闲时端点没有这条规则,GLM-5.3 在那里只收文本,也不开原生 WebSearch(zcode-builtin.json:4125)。

桌面端判定权益的规则(packages/services/src/bigmodel/codingPlanEntitlement.ts:45119):

  • 个人版:订阅列表里有一条产品 ID 或名称含 coding 的记录,状态为 VALID 且在当前周期内;列表请求超时 15 秒(packages/services/src/model-provider/codingPlanProviderAvailability.ts:33)。
  • 团队版EXPIRED 判为过期,EFFECTIVE 但成员未分配(UNASSIGNED)判为未分配,EFFECTIVE 且成员授权 VALID 才算可用,其余新状态一律记为未知,不擅自当成无权益。

不可用的原因只有四种:未登录、未连接、凭据获取失败、明确无权益(packages/shared/src/account-provider-state.ts:5)。已保存的模型选择若指向个人或团队套餐,执行前会被换成当前账号唯一处于“当前连接”的那档(packages/provider/src/effective-model-selection.ts:29),换了套餐不必重选模型。购买与续费(国内支付宝、微信,海外 Stripe、PayPal,以及团队的企业订单)都是桌面端服务,CLI 不涉及(packages/shared/src/coding-plan-subscription.ts:5)。

API Key 模式与账号模式

“用 API Key 接 Coding Plan”在代码里有两种形态,结果不一样:

TUI 粘贴 Coding Plan KeyCoding Plan API Key 模板
入口/login zai-coding-plan-api-key <key>新建 Provider 时选 zai-apibigmodel-api
落在哪个 Provider账号型的个人 Coding Plan Provider个人 Provider,访问方式 zhipu-coding-plan-api-key
Key 存在哪credentials.json,逐项加密provider_config.json,明文
身份Key 的 SHA-256 前 24 位,形如 key-…
权益与当前连接参与账号层判定不参与
请求路径官方端点,经平台网关同一端点,同样经网关

TUI 粘贴的 Key 由 configureCodingPlanApiKey 当作一次“登录”处理:用 Key 的摘要当账号身份,写进与 OAuth 登录相同的凭据键(auth-login.ts:258standalone-account-provider-runtime.ts:101)。所以在独立 CLI 里,OAuth 登录与粘贴 Key 最终都表现为同一个 account:*-individual-coding-plan Provider,差别只是 Key 的来源。模板方式则完全是普通的 BYOK:两个模板默认启用 GLM-5.3 与 GLM-5.3-Flash(zcode-builtin.json:4507),因为地址相同,请求照样被改写到平台网关,但它们不算“账号连接”,闲时任务用不了(packages/services/src/session/offPeakRuntimeModel.ts:91)。

闲时计划(Idle plan)

闲时计划是两个隐藏的账号型 Provider,account:zai-offpeak-idle-planaccount:bigmodel-offpeak-idle-planpackages/shared/src/off-peak-types.ts:41)。隐藏意味着它们不出现在模型选择器里,只由桌面端的调度器在派发闲时任务时使用;任务怎样排队、派发和防递归,见定时任务与闲时任务。这里只看与套餐相关的部分:

  • 什么时候可用:远端 client/configs 里的灰度开关 offPeak.enable_offpeak_task 为真,且 Registry 里确有闲时模型(packages/services/src/coding-plan-subscription/bigmodelCodingPlanSubscriptionProvider.ts:1328)。服务端的票据先排队,低峰窗口开且排到号才进入可派发状态;类型注释记录了两个时限,就绪票据 5 分钟、执行中票据 3 小时(off-peak-types.ts:32157)。
  • 谁能用:当前选中的连接必须是同一家族的个人或团队 Coding Plan,并且已登录、有 ZCode JWT;Start Plan 明确不支持(offPeakRuntimeModel.ts:98164)。
  • 凭据:一次闲时执行同时带三样东西,Authorization: Bearer <JWT>X-Coding-Plan-Api-KeyX-Off-Peak-Ticket-ID,BigModel 团队版另带组织与项目头(offPeakRuntimeModel.ts:246)。
  • 排队语义:适配层只对闲时 Provider 特判,429 或业务码 3105 表示还在排队,按服务端给的等待时间、最多 5 分钟探测一次,缺省 60 秒,不消耗重试预算;400 带 3102(兼容旧的 3001)表示票据失效,错误信息前缀 off-peak-ticket-expired,桌面端据此重新取号并续跑同一个会话(apps/zcode-cli/packages/adapters/src/model/offpeak-retry.ts:140)。
  • 工具限制OffPeakCreateOffPeakList 两个工具让模型在对话里创建和查看闲时任务;闲时派发的回合里,它们以及会绕过本轮模型重新拉起子 Agent、落到用户付费套餐上的工具都会被拒绝(apps/zcode-cli/packages/core/src/tool/handlers/off-peak.ts:39)。额度用尽(3103)、没有合格套餐(3101)、模型不在允许名单等失败,会翻译成模型能转述给用户的固定文案(off-peak.ts:80)。

用量与额度

从代码看,CLI 与 TUI 里没有额度查询,用量与额度只在桌面端展示,服务接口是 IUsageStatsServicepackages/services/src/usage-stats/usageStats.ts:20):

数据来源出处
Coding Plan 额度所属家族业务域的 /api/monitor/usage/quota/limit,超时 15 秒packages/services/src/usage-stats/providers/bigmodelUsageQuotaProvider.ts:72
额度重置机会控制面 /api/v1/coding-plan/reset 下的状态、领取、使用接口,分 5 小时与每周两种bigmodelUsageQuotaProvider.ts:528packages/shared/src/coding-plan-reset.ts:1
Start Plan 余额控制面的 /api/v1/zcode-plan/billing/balancepackages/shared/src/zcodeEndpoint.ts:269
官方 MCP 调用额度GET /api/v1/mcp/usagepackages/services/src/usage-stats/providers/zcodeMcpQuotaProvider.ts:24

额度快照是一个等级加若干条限额,每条有类型、已用、剩余、已用占比、下次重置时间和按模型的用量明细;Start Plan 的额度桶还带桶 ID、套餐实例与周期起止,用于提醒去重(packages/shared/src/usage-quota.ts:9)。

下一篇:子 Agent——主会话怎样派出 explore、general-purpose 等子 Agent,它们用什么模型、带什么工具、怎样把结果交回来。

本页目录