feat(mobile): 任务菜单显示 Claude、SuperGrok 订阅额度与 ChatGPT credits - #5367
Conversation
手机任务菜单原先只读 ChatGPT 额度,Claude 与 SuperGrok 订阅一律显示 「暂未获取账号配额」,ChatGPT 的 credits 余额也不显示。 - 复用被控端已有的 maker:usage:claude-subscription / xai-subscription 只读接口;未选供应商的 Claude Code 任务按被控端观察到的计费路由归属。 - Claude 窗口匹配与 SuperGrok 新鲜度判定移到 maker-shared,桌面与手机共用。 - 订阅家族收敛为 NATIVE_SUBSCRIPTION_AUTHS,手机端各映射以它为键,新增 家族漏接即编译失败;另有测试守护被控端开放的订阅快照 channel 均已接入。 Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e0a3d51a09
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
- SuperGrok 已购余额与周额度一起按快照新鲜度判断,过期不再显示旧余额。 - ChatGPT credits 同时保留余额与已耗尽状态,与桌面额度卡一致。 - Codex 任务的官方读取不带 credits,改从被控端账号快照读取(桌面额度卡 同源);读取失败只隐藏该行,不影响额度。 - claudeModelFamily 去后缀改为线性实现,结果与原正则一致(CodeQL)。 Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2ff9862ca4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
被控端 proxy 只按模型前缀把这类请求送进订阅直连,桌面额度卡也按默认订阅 账号展示;手机端此前判为未确认来源而不显示。明确选择了其他家族账号的任务 仍不归属。 Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b85984c99a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
路由查询只在老被控端 CHANNEL_NOT_ALLOWED 时视为未知,其它失败照常报错; credits 补读失败也不再静默隐藏。两者都让菜单保留上次的完整额度并标记过期, 不会把已有额度刷成空。 Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
MagicLizi
left a comment
There was a problem hiding this comment.
PR #5367 独立代码审查报告(auto / standard 档)
- head:
8e40aa99a5368034bcfaee82e085609f7906fc3f - base:
bade9ff80335c4125ad8179621ea63595455d853(main) - diffHash:
a006b5c6ffcbf4c32014e5b39e0d26b92d1c12c4b866c8a91bc83b7f36266aa5 - reviewEvidence: head/base/baseRefName/diffHash 见上(与 context
evidence一致)
Findings
没有 P0/P1。
(已核对过的非阻断观察,不发作者:bot 已解决 thread 中的两条 P1 意见——「正常 Codex 读取路径缺失 Credits」「过期 SuperGrok 余额被展示为当前余额」——在当前 head 均已落实并有测试覆盖,见 readSessionMenuAccountUsage.test.ts「ChatGPT credits」与 readXaiAccount 的 current 守卫。)
Rule coverage — 读取的规则文件及逐条结论
AGENTS.md/docs/dev-rules/development-workflow.md:PR 模板三段齐整、验证段如实(明确写了未执行的实机目检);DCO 4/4 commit 带签名且与 author 一致;无凭证入库。docs/dev-rules/architecture-invariants.md(共享 package 依赖方向):packages/maker-shared/src/subscriptionUsage.ts纯函数、零 import,不依赖 render/main ✔;- 无 main 进程动态 import ✔;
3/4. 不涉及布局树 ✔。desktop shared →@cindy/maker-shared为既有依赖方向(apps/desktop/package.json已声明),maker-shared 未反向依赖任何端。
docs/dev-rules/mobile-development.md「冷更边界」:本 PR 只改 TS 源码与 workspace package 的 export map,未触碰app.json/app.config.js/eas.json/apps/mobile/package.json/plugins//modules/等 fingerprint 输入,不触发冷更,可 OTA ✔(PR 风险段已写明)。docs/dev-rules/remote-and-mobile-adaptation.md(新 IPC channel / 手机入口):- 不读 workdir/agent/会话数据文件,快照读取全走 device-link 到被控端,
remoteHostId会话直接返回 unavailable(有测试)✔; - 三个 channel 在
packages/device-link/src/allowlist.ts的REMOTE_INVOKE_ALLOWLIST早已登记(被控端 #3979/#3789 已上线),本 PR 只把它们登记进移动端契约MOBILE_REMOTE_INVOKE_CHANNELS,且有防漏测试强制两端清单对齐 ✔; - 手机版入口/UI 即本 PR 本体 ✔。
PR 风险段以「协议兼容 + 老被控端降级行为」给出了各形态结论(三选一实质已答:不涉及 SSH workdir、channel 已登记、手机 UI 本 PR 交付)。
- 不读 workdir/agent/会话数据文件,快照读取全走 device-link 到被控端,
docs/dev-rules/protocol-compatibility.mdReview 清单:- 跨端 wire protocol 为加通道的兼容演进:新手机+老被控端
CHANNEL_NOT_ALLOWED→ 保留「暂未获取」/不归属(有测试),老手机+新被控端无感,无需协调窗口 ✔; - 不涉及两仓本地协议 package ✔;
- 业务代码放在 maker-shared(非协议 package)✔;
- 未绕过
packages/device-link自造协议 ✔。
- 跨端 wire protocol 为加通道的兼容演进:新手机+老被控端
- UI(
DESIGN.md/ design-system):无新样式、无布局变更;额度行复用SessionUsageSummary既有行组件与语义 token(label/value/warning),diff 中无硬编码颜色。Light/Dark 由既有 themed 样式覆盖,未实机目检(PR 已如实声明)。UI 证据缺失按非阻断提醒规则处理。 - i18n 术语(
i18n/GLOSSARY.md):Credits保留第三方原生叫法,符合 credits 条目裁决(第三方原生 credits 走 alsoAllowed);zh 用「剩余/不限/已耗尽/可用」,未触「额度」禁用词。 - 产品语义:订阅家族收敛为
NATIVE_SUBSCRIPTION_AUTHS(Record 约束漏接即编译失败)+ allowlist channel 全覆盖测试 + 五语言来源名测试,防漏机制真实存在且被验证(PR 声称的「临时加 gemini 报 7 处错」属一次性验证,未复跑)。
共享符号/调用方/错误路径核查
claudeSubscriptionUsage.ts/xaiSubscriptionUsage.ts的类型与matchScopedWindowForModel/claudeModelFamily/isXaiWeeklyUsageCurrent迁至 maker-shared 后 desktop 原路径 re-export,desktop 既有消费方(todaySpendChip/QuotaHoverCard/ main usage 刷新链)回归测试全绿,行为不变;scopedWindowBelongsToFamily供 desktop legacy 兜底去重与 matcher 共用一份口径的注释与实现相符。- 正则→线性扫描的
stripTrailingBracketSuffix(CodeQL 多项式回溯)对合法 model id 行为等价,附带 20 万[的计时测试;仅在畸形括号尾巴上有极小语义差异(更彻底地剥掉畸形后缀),不影响任何真实 id 形态。 - 独立账号 scoped 读取的
providerId回显校验与被控端dispatch.ts现状({ ...snapshot, providerId }回显)一致;不回显按旧被控端拒绝,绝不显示默认账号额度——失败面收敛为「暂未获取」。 - 错误路径:
CHANNEL_NOT_ALLOWED(消息或 code)→ 按旧被控端降级;瞬时错误(NOT_CONNECTED / timeout)→ reject 交给useSessionMenuUsage保留上次结果并标 stale(accountFailed不清account,仅 PRECONDITION_FAILED 清空)——与最新 fix commit「临时失败保留上次结果」一致,有测试。 - 默认 cc 任务归属严格跟
maker:claude-session-route:get的被控端观察值,无观察值/老被控端不归属、不按登录状态猜测;桥接前缀 (chatgpt/、xai/) 不会错归属到其它家族的已选账号(均有测试)。 - credits 投影顺序(balance → unlimited → depleted → available)与桌面
usageCardModel.buildCodexUsageCard一致;过期/重置过的窗口一律跳过不臆造额度。
Verification — 实际运行的命令、结果、未执行项及原因
在审查 worktree(head=8e40aa99)内执行,全部退出码 0:
| 命令 | 结果 |
|---|---|
pnpm --filter @cindy/model-providers run typecheck |
PASS |
pnpm --filter @cindy/maker-shared run typecheck |
PASS |
pnpm --filter mobile run typecheck |
PASS |
pnpm --filter desktop run typecheck |
PASS |
pnpm --filter mobile exec vitest run mobileMakerTransport / readSessionMenuAccountUsage / sessionUsagePresentation / useSessionMenuUsage |
4 files / 134 tests PASS |
pnpm --filter @cindy/maker-shared exec vitest run src/__tests__/subscriptionUsage.test.ts |
9 tests PASS |
pnpm --filter desktop exec vitest run shared/xaiSubscriptionUsage + main/usage/{claude,xai}SubscriptionUsage{,Refresh} + subscriptionAccountUsage + todaySpendChip + QuotaHoverCard |
7 files / 142 tests PASS(迁移行为不变的回归) |
pnpm check:i18n |
PASS(11117 key 五语言一致) |
pnpm check:i18n-glossary |
PASS(无新增违规) |
git log DCO trailers 核对 |
4/4 commit Signed-off-by 与 author 一致 |
未执行:
security-scan.mjs:standard 档不强制(contextsecurity扫描 0 hard / 0 soft 命中、无新增依赖,heavyReasons 为空)。- 手机实机 / 模拟器目检(含 Light / Dark):未执行,PR 已声明;复用既有行样式,属非阻断证据缺失。
- 全量
pnpm test:unit:定向测试覆盖本次改动的全部测试面,完整单测由 CI 兜底(CI 全绿,见 gate.ci)。 - 「临时加入 gemini 报 7 处编译错」的一次性验证:未复跑(非测试代码,类型约束由 Record 约束真实存在)。
Overall — pass
没有 P0/P1。PR 描述与 diff 完全吻合(feat/fix 主题即手机订阅额度补齐 + 共享判定迁移 + 防漏约束),无夹带私货;无维护者确认门讨论 issue,被解决的 bot thread 意见在当前 head 均已核实落实。结论:通过,进入同轮 guarded Approve + Merge(squash)。
这次改了什么
摘要
手机任务菜单的额度卡此前只读 ChatGPT:Claude、SuperGrok 订阅任务一律显示「暂未获取账号配额」,ChatGPT 的 credits 余额也不显示。原因是手机端早于被控端开放
maker:usage:claude-subscription/maker:usage:xai-subscription(#3979 早于 #3789 合入),之后没有接上。本 PR 让手机端读取被控端已有的只读快照接口,补齐所有订阅家族,并加防漏机制,避免以后新增订阅时再漏掉手机端:
chatgpt/桥接任务(读 web 槽)都显示。maker:claude-session-route:get)归属为订阅或 Cindy AI;没有观察值或老被控端时保持不归属,不按登录状态猜测。@cindy/maker-shared/subscription-usage,桌面原路径改为 re-export,行为不变。model-providers的NATIVE_SUBSCRIPTION_AUTHS(auth.native类型同源),手机端的读取函数、快照 channel、展示来源、默认账号 id 都以它为键,漏接任何一处即编译失败(已用临时加入的gemini验证会报 7 处错误)。maker:usage:*-subscriptionchannel 必须都被手机端接入;每个订阅来源在五种语言里都有名称。变更类型
feat新功能fix缺陷修复refactor/perf重构或性能优化docs/test/chore文档、测试或工程维护范围
chatgpt/桥接任务的重置次数展示;桌面端行为改动;feat(mobile): 复用电脑订阅授权并新增额度小组件(iOS优先) #5252 的桌面小组件UI 变化
手机任务菜单额度卡新增数据行(窗口行、Credits 行),复用卡片既有的额度行组件与样式,未新增样式或布局。未附截图(见「未执行的验证」)。
SessionUsageSummary既有额度行(label / value / warning 语义 token,Light / Dark 由既有 themed 样式实现);新增文案按i18n/GLOSSARY.md的 credits 条目保留第三方原生叫法「Credits」,状态用词对齐桌面todaySpend.codex.*。怎么验证的
自动验证
新增测试覆盖:Claude 三类窗口与过期 / 畸形数据、独立账号按 id 读取、SuperGrok 周额度 / 余额 / 过期快照、桥接模型不错误归属、默认路由三种观察值与老被控端、老被控端缺 channel 时报读取失败、ChatGPT credits 五种状态(含 web 槽)、新家族防漏(每个家族路由到自己的来源、allowlist channel 全覆盖、五语言来源名称)。
手工验证
未在真机 / 模拟器上手工验证。
未执行的验证
pnpm test:unit:按仓库规则以test:unit:related定向验证,完整单测由 CI 兜底。风险
风险分类
影响与回滚
CHANNEL_NOT_ALLOWED时,订阅快照读取按失败处理,卡片保留原有「暂未获取账号配额」;路由读取失败时任务不归属任何订阅。独立账号读取要求回包回显 providerId,否则按旧被控端拒绝,不会显示默认账号的额度。不涉及原生层与指纹,可 OTA。提交前检查
git commit -s,见 DCO)