CodexBar OpenCode Go 多工作区用量:自动扇出与堆叠卡片的决策设计与契约边界
2026/9/13 3:42:33 网站建设 项目流程

CodexBar OpenCode Go 多工作区用量:自动扇出与堆叠卡片的决策设计与契约边界

【免费下载链接】CodexBarShow usage stats for OpenAI Codex and Claude Code, without having to login.项目地址: https://gitcode.com/GitHub_Trending/co/CodexBar

本篇技术文章围绕 CodexBar 仓库中的决策文档 2026-07-01-opencode-go-multi-workspace-decision.md 展开:当一个 OpenCode 账号下存在多个各自持有 Go 订阅的工作区(workspace)时,CodexBar 如何从"只取第一个工作区"演进为"自动扇出、每个工作区一张堆叠卡片"。读完后,你可以掌握该特性的完整问题定义、三个候选方案的取舍理由、九条已接受的实现契约(含截断上限、并发上限、失败隔离等硬性边界),以及结合 OpenCodeGoUsageFetcher.swift 源码理解当前单工作区实现的具体调用链。

问题定义:发现多个工作区,却只展示一个

CodexBar 通过opencode.ai的浏览器会话读取 OpenCode Go 订阅用量。决策文档指出的核心问题是:

  • 一个 OpenCode 账号可以拥有多个工作区,每个工作区各自持有一份 Go 订阅;
  • CodexBar 能够发现所有工作区标识符,但只选取第一个;
  • 全链路(快照模型、刷新状态、配置项、CLI 投影、菜单栏卡片)都是单工作区标量设计;
  • 用户想查看另一个工作区,只能手动替换 override 再刷新。

文档特别强调:这不是多账号问题——浏览器会话是共享的,工作区身份只是对用量请求和渲染结果的"作用域限定"。这一区分直接决定了后文的身份模型设计(见"存储与投影边界"一节)。

当前实现与文档描述完全吻合。在 OpenCodeGoUsageFetcher.swift 中,工作区发现走的是opencode.ai/_server的服务函数端点:

  • fetchWorkspaceID(第 338–383 行)先以 GET 请求workspaces服务函数(服务函数 ID 硬编码为def39973...4f,见第 32 行workspacesServerID);
  • 解析函数parseWorkspaceIDs(第 408–416 行)用正则id\s*:\s*"(wrk_[^"]+)"text/javascript序列化响应中提取所有wrk_前缀标识符;
  • GET 拿不到时回退为parseWorkspaceIDsFromJSON(第 418–448 行),对 JSON 树做递归收集并天然去重;再不行则用 POST 重试;
  • 但无论解析出多少个,最终都执行return ids[0](第 382 行)——这正是文档所述"selects only the first one"的源码证据。

override 机制同样可以在源码中逐行印证:fetchUsage(第 120–126 行)接受workspaceIDOverride参数,若normalizeWorkspaceID能规范化该值就跳过发现流程。规范化规则(第 385–406 行)接受三种输入:裸的wrk_...ID、形如https://opencode.ai/workspace/...的完整 URL、或任意包含wrk_[A-Za-z0-9]+片段的文本;都不满足则返回nil

override 的两个来源在 OpenCodeGoProviderDescriptor.swift 中可见:设置项settings?.opencodego?.workspaceID优先,其次是环境变量CODEXBAR_OPENCODEGO_WORKSPACE_ID(第 437–438 行),docs/opencode.md 也记录了这个环境变量与 URL 形式的用法。此外,一旦设置了 override(或选择了 token 账号、手动 cookie),requiresScopedWebStrategy(第 147–159 行)会切换到 Web 优先的抓取策略链——因为此时数据已是"按工作区限定"的,设备级本地历史不应排在前面。

已验证的约束:哪些假设不能动摇

文档列出了五条"Verified constraints",它们共同划定了方案的可行域:

  1. 订阅归属工作区。OpenCode 的公开 Go 文档说明每个工作区至多一名成员可订阅 Go,因此"多工作区"等价于"多份订阅",这是扇出必要性的业务前提。
  2. 发现响应只给出标识符,不给展示名。当前解析只能得到wrk_...ID;在拿到脱敏后的真实认证响应证明之前,展示名(display name)不能作为持久化契约。这一约束后来体现为契约第 8 条。
  3. 现有链路全是单工作区。快照、刷新状态、设置字段、CLI 投影、菜单卡片都是标量模型——这解释了为什么实现不能只改解析器,而必须引入工作区作用域的快照模型。
  4. 周窗口是可选的。已合并的 PR #1788 让 weekly usage 变为可选项;多工作区投影必须为每个工作区独立保留"rolling-only"的结果形态。这一点在当前快照模型中已有体现:OpenCodeGoUsageSnapshot.swift 定义了hasWeeklyUsage/hasMonthlyUsage布尔位(第 5–6 行),toUsageSnapshot()仅在hasWeeklyUsage为真时才生成 secondary 窗口(第 80–90 行),fan-out 实现必须逐工作区保持该语义。
  5. token 账号行是错误身份模型。CodexBar 的 token account 机制("Session tokens",见 OpenCodeGoProviderDescriptor.swift 第 15–21 行)用于存储多份凭证;而多工作区场景下所有工作区复用同一份凭证,若把不同工作区的结果塞进 token 账号行,会混淆"身份"与"作用域"两种概念。

三个候选方案的取舍

方案 A:自动扇出 + 堆叠卡片(已接受)

刷新时发现全部工作区,用同一个认证会话并发拉取,每个工作区渲染一张堆叠卡片;保留现有 override 作为显式的单工作区过滤器,服务于排障和超大账号场景。

文档给出的收益与代价是:符合用户预期、无需复制 cookie、且可复用 Kilo 已落地的"作用域快照 + 堆叠卡片"模式;代价是刷新扇出的网络开销、部分失败状态、卡片排序,以及新的工作区作用域快照模型。仓库中确实存在可复用的先例:KiloOrganization.swift 定义了一个仅含id/name/role的轻量身份结构(第 3–13 行),配合 KiloUsageFetcher.swift 实现了"一个凭证、多个组织、多张卡片"的投影——这正是方案 A 想要迁移到 OpenCode Go 的既有形态。

方案 B:设置页勾选工作区

在 Preferences 中提供发现/选择列表,只拉取勾选的工作区。收益是网络开销可控、行为显式;代价是要额外维护缓存的工作区元数据、处理失效选择、增加配置步骤,并且对"默认看到所有订阅"的用户是一个反直觉的默认值。被否决。

方案 C:工作区子菜单

保留一张 provider 卡片,把工作区结果放进子菜单。收益是菜单紧凑;代价是隐藏用量、引入 provider 专属导航,且无法复用共享的堆叠卡片展示组件。被否决。

已接受的契约:九条实现边界

方案 A 被接受,但附带九条硬性契约。下面逐条解读,并指出与现有代码的衔接点。

  1. 新增OpenCodeGoWorkspace模型:含标识符和可选展示名;永远不得把名字当作请求键。请求路径上只能出现wrk_...ID,名字只用于展示层。
  2. 发现一次、归一化、去重、排序:每次刷新只做一次发现;卡片顺序必须按归一化后的标识符排序。响应到达时间、服务端返回顺序、展示名变化都不得改变卡片顺序——这保证 UI 稳定、可被截图对比测试。
  3. 数量与并发双上限:每次刷新最多处理 20 个已发现工作区,最多 4 条工作区流水线并发。发现数超过 20 时必须展示"结果被截断"状态,并引导用户去单工作区 override。
  4. 结果按工作区隔离:单个工作区失败不得抹掉或改写其他工作区的成功结果;若保留上一轮快照,需将其标记为 stale,并把当前错误关联到同一个稳定标识符。
  5. 工作区快照独立于 token 账号存储:通过 provider identity 投影出安全的工作区标签供堆叠卡片和 CLI 输出使用,但关联键始终是标识符而非标签。
  6. override 降级为过滤器:设置后跳过发现,精确拉取该归一化标识符;非法输入必须在发起网络请求前失败;请求失败绝不回退到其他工作区。对照现有实现,fetchUsage中 override 的归一化校验(第 133–140 行)已经做到"非法则走发现流程",而契约进一步要求非法 override 直接报错而非静默走发现。
  7. 复用同一内存认证会话:所有工作区流水线共用一份 cookie,绝不按工作区复制或持久化 cookie;日志中不得出现原始凭证或工作区标识符。这与 OpenCodeGoUsageFetcher.swift 当前传入cookieHeader字符串、会话本身为 ephemeral 且禁用 cookie 存储(第 111–118 行)的做法一致,fan-out 只需把同一 header 传给每条流水线。
  8. 展示名暂不持久化:若线上契约没有稳定展示名,则显示简短的脱敏序号或标识符后缀,推迟名字持久化;持久化名字需要单独的认证契约证明。
  9. 数据边界隔离:工作区数据留在 OpenCode Go provider 内部,不借用其他 provider 的身份或 plan 字段。

从源码结构看,第 3、7 条意味着新实现应在fetchUsage的调用层做扇出(每条流水线独立携带workspaceIDOverride调用同一静态方法),而不是重写抓取内核——现有的fetchUsage(cookieHeader:timeout:workspaceIDOverride:...)签名天然支持按工作区复用。

实现前必须补齐的证明材料

文档明确:决策本身不改变运行时行为,实现前需要四份证据——

  • 脱敏的认证发现响应,证明存在稳定的工作区标识符与名字字段,或证明名字不可用;
  • 同一会话下两个工作区的脱敏用量响应(验证"一会话多工作区"假设);
  • 打包应用截图:两张堆叠卡片,且不含任何账号/工作区机密;
  • 失败证明:一个工作区失败时,另一个工作区仍然可见。

前三项中,第二、四项直接对应契约第 4、7 条;第一项则决定契约第 8 条走向"显示脱敏后缀"还是"持久化名字"。

验收测试清单

文档把可执行的验收标准写得非常具体,可作为实现时的测试大纲:

验收项验证点
发现去重两个以上工作区标识符被去重,缺失名字不崩溃
共享凭证一个凭证产生每工作区一次请求,不产生重复 cookie 持久化
乱序完成响应乱序返回时,结果仍正确关联到各自工作区
部分失败兄弟工作区的成功卡片得以保留
截断超过 20 个工作区产生确定性的 20 条结果 + 可见的截断状态
并发至多 4 条工作区流水线同时运行
override 生效手动 override 只拉取目标工作区
override 失败语义非法 override 在网络请求前失败;合法的失败 override 不回退
确定性标签CLI JSON 与菜单模型对每个工作区给出确定性的标签
门禁在确切实现头提交上make checkmake test通过

最后一条中的make test对应 Makefile 中的test目标(第 33 行)。仓库中已有的 OpenCode Go 相关测试(如 OpenCodeGoProviderStrategyTests.swift、OpenCodeGoUsageFetcherCLIWaitTests.swift)覆盖了单工作区 override 的 CLI 等待与超时路径,为 fan-out 的"乱序完成""部分失败"测试提供了可参照的测试基础设施。

决策结论与适用范围

文档最终决策为:CodexBar 接受"自动工作区扇出 + 堆叠卡片",并保留现有单工作区 override,全部实现以九条契约为边界。同时声明:本文档不改变运行时行为;实现仍需脱敏的认证多工作区证明、聚焦的解析器/模型测试、打包 UI 证明与独立评审;工作区名字持久化在认证响应契约被证明之前保持范围之外

适用前提与限制:该决策仅适用于 Web 会话(cookie)认证路径下的 OpenCode Go provider;API key 路径(fetchAPIUsage,第 208–241 行)走GET https://opencode.ai/zen/go/v1/usage,不涉及工作区发现,不在本契约范围内;决策状态为"accepted; not implemented",即契约已定、代码尚未落地,阅读时请以仓库当前实现为准。

【免费下载链接】CodexBarShow usage stats for OpenAI Codex and Claude Code, without having to login.项目地址: https://gitcode.com/GitHub_Trending/co/CodexBar

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询