Skip to content

feat(device-link): 远程会话状态栏用量显示对齐本机(订阅/网关全形态) - #3789

Open
fico-hub wants to merge 6 commits into
makecindy:mainfrom
fico-hub:feat/device-link-remote-claude-subscription-usage
Open

feat(device-link): 远程会话状态栏用量显示对齐本机(订阅/网关全形态)#3789
fico-hub wants to merge 6 commits into
makecindy:mainfrom
fico-hub:feat/device-link-remote-claude-subscription-usage

Conversation

@fico-hub

@fico-hub fico-hub commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

这次改了什么

摘要

控制端通过设备互联(device-link)打开远程会话时,底部用量 chip 此前一律被抑制为「仅会话金额 / 货币符号占位」,用户看不到被控端订阅还剩多少额度(维护者 @dashhuang 已确认可以调整这一设计)。本 PR 把被控端的 Claude 订阅余量快照经 device-link 镜像到控制端:显式选择 anthropic 供应商的远程会话,chip 与悬停额度卡按与本机订阅会话完全一致的形态渲染(5h/周/分模型窗口剩余 + 会话金额),数据事实全部来自被控端,不读任何本机账号状态。

范围更新(第二批 commit 59d501f,应发起人要求扩展):显示对齐从 Claude 订阅扩展到本机 chip 支持的全部计费形态——Codex 订阅窗口(app-server 分桶 + chatgpt-bridge WHAM 槽)、xAI 订阅周用量与限流头、网关 LiteLLM daily/monthly 配额;cc 默认路由会话的订阅/网关形态判定改为镜像被控端 proxy 的按请求路由观察值(不再要求显式选择 anthropic)。全部复用同一镜像模式(invoke warm-start + push 整帧替换 + owner 代次栅栏 + CHANNEL_NOT_ALLOWED TTL 负缓存)。

数据通路(全部复用既有基础设施):

  • warm-start:控制端经既有隧道 deviceLink.invoke(deviceId, 'maker:usage:claude-subscription') 读被控端 cached-first 快照(被控端 read() 自带 180s 节流的后台端点刷新);
  • 实时更新:被控端 broadcastClaudeSubscriptionUsage 补一行 tapWindowBroadcast,经既有 broadcast-tap → sessions topic 转发;控制端 onRemotePush 过滤 channel + deviceId + owner 栅栏(isDeviceLinkRemotePushCurrent,与其它 push 消费者同款)后覆盖镜像;
  • 降级:老被控端无此 channel → CHANNEL_NOT_ALLOWED → 控制端回原「仅会话金额」占位显示,不报错。

变更类型

  • feat 新功能

范围

  • 关联 Issue / 需求:无对应 issue(维护者当面确认的方向调整;allowlist 内 M4 注释「将扩展 usage 等完整控制面」已预留此路线)
  • 本 PR 包含:device-link 远程会话状态栏的全形态用量镜像显示 —— Claude 订阅(显式 anthropic 或路由观察=subscription)、Codex 订阅窗口、xAI 周用量/限流头、网关 LiteLLM 配额、cc 默认路由的被控端路由观察镜像;协议 allowlist / topics 登记(全部 append-only);被控端 broadcast tap 与 xai dispatch 拦截(telegram:* 先例,assertTrustedSender 不放宽);控制端镜像 hook 集与 chip 渲染分支
  • 明确不包含:
    • 默认路由远程会话在被控端尚无路由观察值(会话没跑过请求)或老被控端时,维持原「仅会话金额」占位——与本机「形态未定不乱渲染」同一取向,不做本机启发式猜测;
    • 网关形态的 credit 三池(Model Access 个人租户)——其 handler 绑定主窗口 sender + billing 面,不镜像;按本机「拿不到就隐藏」的既有降级语义,远程不显示 credit 行;
    • codex reset-credit 摘要(tooltip 元数据;maker:usage:codex-rate-limits invoke 已在 allowlist,后续可按同一模式接入);
    • 手机版(apps/mobile)入口——协议能力对 mobile 控制端同样可用,mobile 自有 UI 未接入。
  • 用户可见变化:控制端打开远程会话时,右下角 chip 按被控端实际计费形态显示与本机一致的内容(订阅窗口段 / 网关今日额度 / xAI 周用量 + 对应悬停卡或 tooltip);chip 不可点(看板属被控端账号,维持既有 null 语义)。形态未定 / 老被控端维持原货币符号占位
  • 是否存在 breaking change:无(allowlist append-only;新 push 对老控制端是未知 channel,按既有 channel 过滤天然忽略)

UI 变化

不涉及视觉规范变化:远程订阅形态完全复用本机订阅形态的既有渲染路径(TodaySpendChip 窗口段 + QuotaHoverCard),无新增视觉元素、颜色、间距或文案,仅数据来源从本机 hook 切换为被控端镜像 hook。Light/Dark 双模式随既有语义 token 生效,无单模式硬编码。实机目检未执行(开发环境即被控端本机,无第二台装本分支构建的控制端),渲染行为由组件级 jsdom 测试覆盖(TodaySpendChip.deviceLink.test.tsx 四例:窗口段渲染 / 悬停额度卡 / 镜像不可用降级 / 默认路由不变);如实说明,两种模式均未实机目检。

  • 引用的设计规范:不涉及新视觉决策——复用 DESIGN.md 已约束的 TodaySpendChip / QuotaHoverCard 既有形态,本 PR 不改任何视觉属性。

改动前后效果示意(Light / Dark × 改动前 / 改动后 / 悬停额度卡与降级,演示数据、近似 token 色值;非实机截图,原因见上——HTML 源码见 assets 分支):

PR 3789 状态栏示意

怎么验证的

自动验证

pnpm test:unit:related(repo 根)
结果:PASS apps/desktop unit(2263 文件 / 30631 用例,2 个既有源码契约断言按新行为更新后全绿)、PASS apps/mobile unit、PASS packages/device-link unit,EXIT=0

pnpm --filter desktop run typecheck
结果:通过(exit 0)

pnpm --filter @cindy/device-link run typecheck
结果:通过(exit 0)

第二批(59d501f67)后复跑:pnpm test:unit:related 全绿(desktop 2264 文件 / 30542 用例,
含 deviceLinkDispatch 107 例 —— xai 拦截改注入式后 dispatch 模块图保持纯净)、
desktop 与 @cindy/device-link typecheck 通过;新增 useRemoteDeviceUsage(7)、
TodaySpendChip.deviceLink 扩至 9 例(codex 选桶 / 网关 / xai / 路由观察分支)、
usageBroadcaster tap 扩至 13 例、allowlist/topics 扩至 63 例。

定向(第一批):packages/device-link allowlist/topics(60 用例)、
apps/desktop usageBroadcasterClaudeSubscription(11,含新增 tap 断言)、
useRemoteClaudeSubscriptionUsage(3)、TodaySpendChip.deviceLink(4)、
todaySpendChip 源码契约(18)、TodaySpendChip.popover(24)全部通过

手工验证

不涉及:端到端手工验证需要两台设备各跑本分支构建(控制端 + 被控端),开发环境只有被控端一台。数据通路的两端(invoke handler 无 sender 依赖、tap 转发、topic 路由、owner 栅栏、CHANNEL_NOT_ALLOWED 降级)均有单测覆盖。

未执行的验证

  • 双机实机联调(原因见上);渲染形态已由 jsdom 组件测试覆盖。
  • 手机版控制端行为未验证(本 PR 未接入 mobile UI,协议为 append-only 不影响其现状)。

风险

风险分类

  • 协议兼容

影响与回滚

  • 影响范围:device-link 协议面 append-only 扩展(3 个只读 invoke channel:claude-subscription / xai-subscription(dispatch 拦截执行)/ claude-session-route:get;6 个 push channel:claude-subscription / codex-account / claude-account / xai-subscription / xai-rate-limit(账号级 → sessions topic)与 claude-session-route-changed(sessionId → session: topic))。推送频率上限:codex 每 turn 一记录、WHAM/LiteLLM 刷新 10s 节流、claude 订阅签名去抖 + 180s 节流、xai 低频、route 每会话生命周期通常一次;无 active 链路时 tap 均为 O(1) no-op。远程与手机适配三问:① 不涉及 workdir / agent 进程(纯账号级快照读);② 新 IPC / push 已按 packages/device-link/src/allowlist.ts 准入判据登记 invoke / push 白名单并在 topics.ts 登记账号级路由(判据核对:handler 无 event.sender 依赖、无本机 UI/shell 副作用、数据真相在被控端);③ 手机版入口未接入,见「明确不包含」。版本偏差矩阵:新控制端 + 老被控端 → CHANNEL_NOT_ALLOWED,降级回原占位显示;老控制端 + 新被控端 → 新 push 是未知 channel,老控制端按 channel 过滤天然忽略;computeAllowlistHash 随表变化,仅用于「版本不一致」提示,不阻断。
  • 故障半径:不触及 device-link 重试 / 超时 / teardown / 重连逻辑。新增推送量有确定上限——被控端 headers 观察器按 (5h, 7d, status) 三元组签名去抖、端点刷新 180s 节流 + 429 退避,只有数值变化才广播,不随 turn 内逐请求刷帧;无 active 控制链路时 tap 为 O(1) no-op。
  • 回滚 / 降级方式:revert 本 PR 即回到「远程会话仅显示会话金额」的原状;协议为 append-only,单端回滚不产生不兼容(对端多出的 channel 走上述偏差矩阵降级)。

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名(git commit -s,见 DCO)
  • UI 改动已在「UI 变化」注明引用的设计规范章节(不涉及 UI 则跳过)
  • 未提交凭证、令牌或授权文件
  • 已补充必要文档(协议语义随 allowlist / topics 注释落地;无独立文档需更新)
  • 已确认测试结果或说明未执行原因

控制端打开 device-link 远程会话时,底部 TodaySpendChip 此前一律抑制为
「仅会话金额 / 货币符号占位」,看不到被控端订阅的 5h/周/分模型窗口剩余。
本次把被控端 Claude 订阅余量快照经 device-link 镜像到控制端,显式选择
anthropic 供应商的远程会话按与本机订阅会话完全一致的形态渲染(窗口段 +
悬停额度卡);数据事实仍全部来自被控端,不读任何本机账号状态。

- packages/device-link:REMOTE_INVOKE_ALLOWLIST 放行只读
  maker:usage:claude-subscription;PUSH_FORWARD_ALLOWLIST + ACCOUNT_CHANNELS
  登记 usage:claude-subscription-changed(账号级 → sessions topic)。
- 被控端:broadcastClaudeSubscriptionUsage 补 tapWindowBroadcast,登出 /
  换号的 null 清除同步转发。
- 控制端:新增 useRemoteClaudeSubscriptionUsage(warm-start 隧道读 +
  onRemotePush 实时镜像 + owner 栅栏;CHANNEL_NOT_ALLOWED 老被控端降级回
  原占位显示);TodaySpendChip 远程订阅分支复用本机订阅渲染路径。
- 默认路由(providerId=null)的远程会话不做本机启发式猜测,维持原显示。

Signed-off-by: ficowang <fico@xd.com>
@fico-hub
fico-hub requested a review from a team as a code owner September 2, 2026 06:57
@greptile-apps

greptile-apps Bot commented Sep 2, 2026

Copy link
Copy Markdown

Greptile Summary

此 PR 将被控端 Claude 订阅余量通过 device-link invoke 与 push 镜像到控制端,并让显式 Anthropic 远程会话复用本机额度 chip 和悬停卡片。

  • 扩展远程 invoke、push allowlist 与 sessions topic 路由
  • 新增按设备缓存的远程 Claude 订阅余量 hook
  • 为远程订阅会话接入额度展示、刷新和降级逻辑
  • 增加协议、广播、hook 与组件测试

Confidence Score: 3/5

当前不宜合并,因为远程快照的晚到 invoke 结果可能跨账户 owner 边界进入新账户界面。

新 hook 对实时 push 使用了 owner fence,却无条件接收异步 invoke 结果并写入跨 React owner 重挂载持续存在的 deviceId 缓存;此外,对旧端不支持状态的永久缓存会妨碍升级后的能力恢复。

Files Needing Attention: apps/desktop/src/renderer/hooks/useRemoteClaudeSubscriptionUsage.ts

Security Review

新增 invoke 回填没有绑定数据 owner;账户切换期间的晚到响应可写入跨 owner 持续存在的 deviceId 缓存,使新账户看到旧账户下取得的订阅余量。需要在应用结果前校验请求发起时的 owner/generation,或将缓存纳入 owner 作用域。

Important Files Changed

Filename Overview
apps/desktop/src/renderer/hooks/useRemoteClaudeSubscriptionUsage.ts 新增远程快照缓存和 invoke/push 装配,但 invoke 回填缺少 owner fence,且不支持能力缓存没有生产失效路径。
apps/desktop/src/renderer/components/status/TodaySpendChip.tsx 显式 Anthropic 的远程 cc/pi 会话改用被控端镜像快照,并保留不可用时的原占位降级。
apps/desktop/src/main/usageBroadcaster.ts Claude 订阅快照广播新增 device-link tap,使记录和清空事件可转发到控制端。
packages/device-link/src/allowlist.ts 以 append-only 方式放行只读 Claude 订阅 invoke 和对应 push channel。
packages/device-link/src/topics.ts 将账号级 Claude 订阅余量 push 路由到常开的 sessions topic。
Prompt To Fix All With AI
### Issue 1
apps/desktop/src/renderer/hooks/useRemoteClaudeSubscriptionUsage.ts:67-70
**异步回填越过账户边界**

当 owner A 发起读取后切换到 owner B,并在新 owner 下打开同一 deviceId 的远程会话时,旧请求仍会把 A 的快照写入仅按 deviceId 区分的模块缓存,导致 B 的状态栏显示旧账户下取得的订阅余量;若新读取失败,该值会持续保留。请在应用结果前校验请求发起时的 owner/generation,或将缓存纳入 owner 作用域。

**How this was verified:** owner 切换仅重挂载 React 路由树,而此 invoke 回调无 owner fence,并继续写入跨重挂载存活的模块缓存。

### Issue 2
apps/desktop/src/renderer/hooks/useRemoteClaudeSubscriptionUsage.ts:64
**不支持状态永久缓存**

设备首次返回 `CHANNEL_NOT_ALLOWED` 后,其 deviceId 会在整个 renderer 生命周期内被永久跳过;被控端随后升级并以相同 deviceId 重连时,挂载和主动刷新仍不会重新探测,导致额度 chip 一直停留在降级占位,直至控制端 renderer 重启。请在重连或 allowlist 能力变化时使该记录失效。

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "feat(device-link): 远程订阅会话状态栏对齐被控端 Claude..." | Re-trigger Greptile

Comment thread apps/desktop/src/renderer/hooks/useRemoteClaudeSubscriptionUsage.ts
Comment thread apps/desktop/src/renderer/hooks/useRemoteClaudeSubscriptionUsage.ts Outdated
@MagicLizi

Copy link
Copy Markdown
Contributor

@fico-hub 👋 这个 PR 还有 2 条 review conversation 没 resolve(apps/desktop/src/renderer/hooks/useRemoteClaudeSubscriptionUsage.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

- P1:invoke 回填应用前校验发起时 owner 代次,module 缓存整体绑定当前
  owner(变化即清空)——切号后迟到的旧账号快照整帧丢弃,不再顶给新账号。
- P2:CHANNEL_NOT_ALLOWED 负缓存从 renderer 终身改为 15 分钟 TTL,
  到期重探测;收到该设备的订阅 push(能力已具备)立即解除。

Signed-off-by: ficowang <fico@xd.com>
@MagicLizi

Copy link
Copy Markdown
Contributor

这条 PR 会改变控制端远程会话状态栏的展示:显式 anthropic 远程会话将显示被控端 Claude 订阅余量,而不再是货币符号占位。这属于用户可见的产品行为,已开维护者确认讨论。\n\n请维护者在 PR 上 Approve 放行;若方向需要改,请 Request Changes。讨论 issue:#3790

@MagicLizi MagicLizi added awaiting-discussion 等待维护者讨论(review-pr) touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示) labels Sep 2, 2026
@MagicLizi

Copy link
Copy Markdown
Contributor

命中 UI 路径(apps/desktop/src/renderer/components/status/TodaySpendChip.tsx / apps/desktop/src/renderer/hooks/useRemoteClaudeSubscriptionUsage.ts)但 description 未附界面效果证据——建议补充改动后效果:截图/录屏,或改动后界面的 HTML 页面(```html 代码块、.html 附件或在线预览链接),便于确认界面符合 DESIGN.md 设计规范。

本条为非阻断提醒,不挡审查。

@fico-hub

fico-hub commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

两条 review conversation 已修复(cdeda5e)并逐条回复后 resolve;界面效果示意已补进 description「UI 变化」小节(Light/Dark × 改动前/后/悬停额度卡/降级占位,演示数据 HTML 渲染,源码在 assets/pr3789-previews 分支——双机实机截图不可得的原因 description 已说明)。

用户口径:本机桌面版 chip 能显示的,远程会话都要能显示。在 Claude 订阅镜像
之上补齐其余形态,全部复用同一镜像模式(invoke warm-start + push 整帧替换 +
owner 栅栏 + CHANNEL_NOT_ALLOWED TTL):

- Codex 订阅窗口:invoke 'maker:usage:account'('codex',已在 allowlist)返回
  权威组合 payload;push 'usage:codex-account-changed' 转发;控制端按本机同
  口径选槽 / 选桶(chatgpt-bridge → WHAM 槽,CLI → 按模型匹配桶)。
- 网关 LiteLLM 配额:同 channel('claude-code');push
  'usage:claude-account-changed' 转发。credit 三池 handler 绑定主窗口 sender,
  不镜像 —— 按本机「拿不到就隐藏」既有降级。
- xAI 订阅周用量 + 限流头:该 invoke 的 ipcMain handler 挂 assertTrustedSender
  (那道闸不为远程放宽),按 telegram:* 先例由被控端 dispatch 拦截、直读
  register.ts 注入的 reader(dispatch 模块图保持纯净);两条 push 转发,限流头
  push-only 与本机同语义。
- cc 默认路由形态判定:镜像被控端 proxy 的按请求路由观察
  ('maker:claude-session-route:get' + route-changed push,session topic);
  无观察值 / 老被控端维持原「仅会话金额」占位,不做本机启发式。
- codex 远程与 SSH 远程同口径按订阅形态处理;折扣模型 / 显式 XD 走网关形态。

协议改动均 append-only;push 频率上限:codex 每 turn 一记录、WHAM/LiteLLM
10s 节流、xai 低频、route 每会话生命周期通常一次。

Signed-off-by: ficowang <fico@xd.com>
@fico-hub fico-hub changed the title feat(device-link): 远程订阅会话状态栏对齐被控端 Claude 订阅余量显示 feat(device-link): 远程会话状态栏用量显示对齐本机(订阅/网关全形态) Sep 2, 2026
@fico-hub

fico-hub commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

范围更新(59d501f):应发起人要求,远程会话用量显示从「显式 anthropic 的 Claude 订阅」扩展为与本机 chip 全形态对齐——Codex 订阅窗口(分桶/WHAM 槽同口径)、xAI 周用量与限流头、网关 LiteLLM 配额、cc 默认路由按被控端路由观察值判形态。协议仍为 append-only(明细见 description「影响与回滚」);xAI invoke 按 telegram:* 先例走 dispatch 拦截,不放宽 assertTrustedSender。@dashhuang 产品门(#3790)请按更新后的完整范围评审。

@MagicLizi MagicLizi added the touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示) label Sep 2, 2026
@fico-hub

fico-hub commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

@dashhuang 这边 CI 已全部通过,之前的 review conversation 也已处理并 resolve。目前还在等待产品方向审核,麻烦有空时帮忙 review;如果方向或范围需要调整,也请直接指出。用户侧正在等待远程 Claude / Codex 额度显示修复,感谢。

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查发现 P1 ×1,请在当前 head 上修复后再请求 review。

  • [P1] apps/desktop/src/renderer/hooks/useRemoteClaudeSessionRoute.ts:38 — cacheKey 用源码里的原始 NUL (0x00) 拼接 deviceId/sessionId。git diff 将该文件标成二进制;GitHub PR files 上本文件 additions=0。默认路由形态判定整份实现在 PR diff 里不可审。请改成可见的转义(例如 \u0000)或可打印分隔符,使文件保持文本。验证:文件不再含原始 NUL,git diff 不再显示 Bin。

…文件为文本

review P1:cacheKey 用源码里的原始 NUL(0x00)拼接 deviceId/sessionId,git 将文件判为二进制,PR diff 不可审。
改为 JS 字符串转义 \u0000,运行时键值逐字节不变(与 useModelPricing 的既有写法一致);文件不再含原始 NUL。

Signed-off-by: ficowang <fico@xd.com>
@fico-hub

fico-hub commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

@MagicLizi P1 已修(8922fbb):useRemoteClaudeSessionRoute.ts 的 cacheKey 分隔符由源码内原始 NUL(0x00)改为字符串转义 \u0000,运行时键值逐字节不变(与 useModelPricing.ts 既有写法一致)。

验证:文件不再含原始 NUL(od 计数 0);git diff main...HEAD 该文件已按文本显示,GitHub files 视图 additions 138 / 不再为 0;定向测试 4 文件 41 例通过,desktop typecheck 0 错误,仓库根 related 归因 PASS。请复审。

@MagicLizi
MagicLizi dismissed their stale review September 4, 2026 22:20

这些问题已在当前 head 的后续 commit 中修复(格式门/安全门已通过),自动 dismiss 旧的 CHANGES_REQUESTED 以解除合并阻塞。

@MagicLizi

Copy link
Copy Markdown
Contributor

@fico-hub 👋 这个 PR 目前与 main 有合并冲突,auto-review 因此暂时跳过、没法继续审查 / 合并。

请在本地 merge 最新的 origin/main 解决冲突后推送;冲突解除后,下一轮 auto-review 会自动重新处理这个 PR。

…sage

解决 apps/desktop/src/main/maker-ipc/register.ts 导入冲突:main(makecindy#3939)已把回合用量记账迁出 register.ts,
本分支在该导入块仅新增 readXaiSubscriptionUsageSnapshotForDeviceLink,保留该单行导入,其余按 main。

Signed-off-by: ficowang <fico@xd.com>
@fico-hub

fico-hub commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

@MagicLizi 已 merge 最新 origin/main(87aec01)。唯一冲突在 maker-ipc/register.ts 的导入块:main(#3939)把回合用量记账迁出了 register.ts,本分支在该块只新增了 readXaiSubscriptionUsageSnapshotForDeviceLink 一行导入,按 main 取舍后保留该单行。验证:desktop typecheck 0 错误;定向测试 38 文件 683 例 + device-link 包 255 例通过;仓库根 related 归因全部 PASS。请 auto-review 重新处理。

@MagicLizi

Copy link
Copy Markdown
Contributor

@fico-hub 👋 这个 PR 目前与 main 有合并冲突,auto-review 因此暂时跳过、没法继续审查 / 合并。

请在本地 merge 最新的 origin/main 解决冲突后推送;冲突解除后,下一轮 auto-review 会自动重新处理这个 PR。

…sage

Resolve conflicts with makecindy#3972 (统一套餐用量卡片): keep device-link remote
form detection and mirror hooks on the data layer, and feed the mirrored
snapshots into the shared usage-card builders; only SSH remote sessions
stay session-money-only. Source-contract tests ported to the compact()
style; device-link component test follows the always-button trigger.

Signed-off-by: ficowang <fico@xd.com>
@fico-hub

fico-hub commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

@MagicLizi 已再次 merge 最新 origin/main(667450b),PR 回到 MERGEABLE。这次冲突来自 #3972(统一套餐用量卡片),集中在 TodaySpendChip.tsx 与其 source-contract 测试,取舍如下:

  • 形态判定 / 数据层:保留本分支的 device-link 远程判定(isDeviceLinkRemoteClaudeSubscription / isDeviceLinkRemoteCodexOauth / isDeviceLinkRemoteGateway,cc 默认路由用被控端路由观察镜像)与「本机 hook ↔ 被控端镜像 hook」按 isDeviceLinkRemote 互斥切换;fix(desktop): 统一套餐用量卡片并理清配额与明细层级 #3972 的重排格式与其余逻辑按 main。
  • 渲染层:改用 fix(desktop): 统一套餐用量卡片并理清配额与明细层级 #3972UsageCardAccount 卡片模型——远程形态已解析的 device-link 会话把镜像快照喂给同一套 buildClaude/Codex/XaiUsageCard,与本机渲染完全一致;仍未解析(默认路由无观察值 / 老被控端)才走「仅会话金额」占位。原「只有 SSH 远程不建账号卡」的条件由 !isAnyRemoteSession 收窄为 !remoteHostId,网关分支加 || isDeviceLinkRemoteGateway(被控端 LiteLLM 配额镜像;credit 三池不镜像,沿用「拿不到就隐藏」)。看板链接对 device-link 仍为 null,chip 不可点。
  • 测试:contract 测试改为 fix(desktop): 统一套餐用量卡片并理清配额与明细层级 #3972compact() 风格并补齐远程分支断言;TodaySpendChip.deviceLink.test.tsx 跟随 fix(desktop): 统一套餐用量卡片并理清配额与明细层级 #3972「trigger 恒为按钮」的语义(断言点击不开外链、卡片内无看板按钮、卡片有镜像进度条);TodaySpendChip.popover.test.tsx 的远程用例补 mock 三个镜像 hook 返回 null(该文件不铺隧道 API),既有「不混入本机配额」断言不变。

验证:desktop typecheck 0 错误;eslint / prettier / git diff --check 通过;chip 三个测试文件 68/68;仓库根 pnpm test:unit:related 全部 PASS(desktop / mobile / maker-core / device-link / lizi-mcps 等)。请 auto-review 重新处理。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-discussion 等待维护者讨论(review-pr) touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示) touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants