Chancel's blog

2549 字 9 分钟阅读

OpenCode Universal Status Sidebar

  • prompt

OpenCode 通用状态侧边栏

实现一个 单文件 OpenCode TUI 状态侧边栏插件,用于充分利用 OpenCode 右侧 sidebar 空间。

目标不是重新实现 OpenCode 自带的信息面板,而是在 当前 OpenCode 原生 sidebar 的基础上补充真正缺失的信息

主要实现如下效果:

  1. 当前订阅 / API Key 的真实额度、余额或使用量。
  2. 当前 Sub-agent 的执行状态。
  3. 当前会话实际调用过的 Skills。

实现原则

不要针对某个固定 OpenCode 版本写死实现。

开始编码前,先检查:

  • 当前安装 / 当前最新稳定版 OpenCode 的官方文档
  • 当前 Plugin / TUI Plugin API
  • 当前 sidebar 扩展机制
  • 当前 session / message / part / task 数据结构
  • 当前 OpenCode 已经原生显示的信息
  • 当前 authentication 存储和 provider 数据结构

然后基于 当前版本的实际 API 和已有插件进行实现,如果插件已经实现就不要重复实现了。

交付形式

最终只交付一个主要文件:

status-sidebar.tsx

如果当前 OpenCode 插件机制要求 .ts 而非 .tsx,则按照当前实际机制处理。

要求:

  • 单文件实现
  • 不创建独立项目
  • 不增加 daemon
  • 不增加 server
  • 不运行 npm install
  • 不运行 bun add
  • 不运行 pnpm add
  • 不引入第三方 runtime dependency

允许使用:

  • OpenCode 本身已经提供的 Plugin / TUI runtime
  • OpenCode 已经依赖并暴露给插件的 UI/runtime 能力
  • Node/Bun builtin
  • 原生 fetch
  • AbortController

不要为了代码方便新增 npm 包。

落地效果

理想 sidebar 大致如下:

 AGENTS · 3
  ● explore       grep · 1m24s
  ● reviewer      read · 42s
  ✓ test                18s

USAGE
  OpenAI · Subscription
  5h       ███████░░ 72%
            ↻ 2h14m
  Weekly   █████░░░░ 48%
            ↻ 3d8h
  Z.AI · Lite
  5h    ████████░░░░
  ↻ 1h 0m
  Week  ███████████░
  ↻ 14h 3m
  DeepSeek · API
  Balance   CNY 108.42
  OpenRouter · API
  Monthly   ███████░░ 74%
  $74.50 / $100 left

SKILLS · 4
  debugging · code-review
  browser-testing · +1

如果 Provider 本身无法取得任何可靠额度信息,则隐藏其信息,如果所有 Provider 都无法取得可靠额度信息,则整块 USAGE 都隐藏。

要先调查各个 Provider 的实际能力和返回的数据结构,再决定如何在 sidebar 中展示,而非盲目的认定无接口然后隐藏。

四、USAGE:核心设计原则

不要假设所有 Provider 都有:

5h
Weekly

也不要假设:

Subscription = 5h + Weekly
API Key = Balance

不同 Provider 的限制模型完全不同。

必须显示 Provider 实际提供的数据

可能包括:

5h quota
daily quota
weekly quota
monthly quota

API balance
budget
credits
spend
rate limit
AI credits
其它 Provider-specific usage

UI 根据真实数据动态生成。


五、Quota Window 必须动态

例如 Provider 实际返回:

5 hours

才显示:

5h

实际返回:

weekly

才显示:

Weekly

实际只有:

Monthly AI Credits

就显示:

Monthly

绝对不要因为它是订阅账户就人为补:

5h
Weekly

UI 层不应该知道某个 Provider “理论上应该有什么”。

Provider adapter 返回什么 metric,就显示什么 metric。


六、不同 Usage 类型必须区分

不要把以下概念混在一起:

Quota

例如:

72% remaining

可以显示进度条:

5h       ███████░░ 72%

Balance

例如:

CNY 108.42

直接显示:

Balance   CNY 108.42

余额没有总额度时不要画 progress bar。


Budget

例如:

limit = $100
remaining = $74

可以显示:

Monthly   ███████░░ 74%
$74 / $100 left

Spend

例如 Provider 只能返回:

$25.50 used

显示:

Spent     $25.50

不能把它转换成:

74.5% remaining

除非确实知道总 budget。


Credits

例如:

AI Credits
Premium Requests
Compute Credits

按照 Provider 自己的单位展示。

不要强制转换成美元或百分比。


七、Provider 必须逐个验证

这是实现中的硬要求。

参考:

  • OpenCode 官方实现
  • Provider 官方文档
  • slkiser/opencode-quota
  • 其他成熟实现

但不能仅仅因为某个参考插件写了一个 endpoint 就直接复制。

每实现一个 Provider adapter,都必须验证:

1. 当前认证方式
2. credential 类型
3. quota / billing endpoint
4. endpoint 是否仍然有效
5. response schema
6. 数据字段真实语义
7. reset/window 的真实含义
8. 普通 API key 是否有权限访问
9. Subscription 与 API key 是否使用不同接口
10. 是否存在不同 plan/account 类型差异

能进行真实请求验证时,应进行真实验证。

不能验证真实账号时:

至少完成:

官方文档
+
当前 OpenCode 实现
+
参考插件实现

三方交叉验证。


八、不要把“还没验证”当成“不支持”

内部必须明确区分:

已验证可获取
条件可获取
暂时请求失败
认证不足
尚未验证
已验证确实无法获得

其中:

尚未验证

绝对不等于:

Provider 不支持

例如暂时还没有研究 MiniMax:

正确结论:

尚未验证

而不是:

MiniMax 无法读取额度

只有在真正验证后确认:

Provider 没有暴露对应数据

才能得出“无法读取”的结论。


九、第一阶段 Provider

优先研究并实现主流 OpenCode 使用场景。

包括但不限于:

OpenAI / Codex Subscription
OpenAI API
Anthropic / Claude Subscription
Anthropic API
OpenCode Go
OpenRouter
DeepSeek
GitHub Copilot
xAI
Kimi
MiniMax
Z.ai
Google 相关 Coding Provider

不要因为列表里有这些 Provider 就强制全部写 adapter。

原则是:

验证清楚一个,实现一个。

最终报告里明确写:

已验证并实现
已验证但受 credential/plan 限制
尚未完成验证
验证后确认无法取得

不要写模糊的:

可能支持
应该支持
理论支持

十、Subscription 与 API Key 必须分开

同一个 Provider 可能同时支持:

Subscription / OAuth
API Key
Admin API Key
Organization credential

它们不能使用同一个 quota 逻辑。

例如:

OpenAI Subscription

可能存在某类 session / weekly quota。

但:

OpenAI API key

是完全不同的 billing 模型。

同理:

Claude Subscription

与:

Anthropic API

不能混为一谈。

Provider detection 必须尽量识别当前真实 credential 类型。

无法确认:

不要猜。

顶部可以只显示:

OpenAI

而不是猜:

OpenAI · Pro

十一、不要猜 Plan

除非 Provider 返回明确 plan 信息,否则不要根据:

quota 大小
模型名称
OAuth 类型
使用量

推断:

Plus
Pro
Max
Team
Business

如果只能确定它是订阅:

OpenAI · Subscription

即可。

如果只能确定 Provider:

OpenAI

即可。


十二、Credential 安全

这是最高优先级。

API key / OAuth token 可以读取,但只能:

本机内存
对应 Provider 官方服务

禁止进入:

日志
UI
异常消息
缓存文件
临时文件
KV storage
Telemetry
第三方 API

不要显示:

API key 后四位
Account email
Account ID
Organization ID

除非真正有 UI 必要,并且不存在隐私问题;默认全部不显示。


十三、Credential 来源

优先使用:

OpenCode 当前 authentication store
OpenCode runtime 已解析的认证信息
明确支持的环境变量

不要扫描项目寻找 secret。

禁止扫描:

.env
.env.*
源码
shell history
git history
任意 project files

不要因为 quota 获取不到就到处找 key。

Credential resolver 必须有明确 trust boundary。


十四、网络安全

Quota adapter 只能访问已经验证过的 Provider endpoint。

Endpoint 应由代码中的 Provider adapter 固定定义。

禁止:

从 Prompt 获取 endpoint
从项目文件获取 endpoint
从模型回复获取 endpoint
跟随任意 redirect
请求第三方 quota proxy

请求必须:

  • HTTPS
  • timeout
  • AbortController
  • 限制 response size
  • 校验 response schema
  • 尽量禁止 redirect

任何 credential 都不能离开其对应 Provider 的可信域名范围。


十五、错误处理

Quota 获取失败不得影响 OpenCode 正常运行。

例如:

timeout
401
403
429
500
schema changed

应该:

Sidebar 继续正常运行
Agents 继续显示
Skills 继续显示
Quota 静默降级

不要弹 toast 干扰用户。

也不要显示:

ERROR FETCHING OPENAI QUOTA

日常情况下直接隐藏或保留短期 stale data 即可。


十六、Quota 缓存

Quota API 不应该随着 UI render 重复请求。

需要:

memory cache
TTL
in-flight dedupe
failure backoff

建议量级:

正常 quota:
2~5 分钟请求一次

失败:
至少 30~60 秒后再重试

具体可以根据 Provider reset window 调整。

例如分钟级 quota:

可以更频繁。

Weekly:

没必要每几秒请求。

缓存只保存在进程内存。

不要把 quota / credential 持久化到磁盘。


十七、Sub-agent

这里显示的是:

当前工作中真正执行的 sub-agent

不是:

配置里有哪些 agent

优先利用 OpenCode 当前原生 Task / Session 数据结构。

实现时检查当前 OpenCode:

  • task ToolPart
  • child session
  • parent session
  • task metadata
  • message part status

找到当前最可靠的关联方式。

不要依赖 agent 名称或文本内容做模糊匹配。


十八、Sub-agent UI

目标:

AGENTS · 3
● explore       grep · 1m24s
● reviewer      read · 42s
✓ test                18s

优先显示:

agent name/type
当前状态
当前 activity
elapsed

不要显示:

完整 Prompt
完整 tool arguments
完整 shell command
绝对文件路径
tool output

这既会污染 UI,也可能泄漏项目数据。


十九、Sub-agent 状态必须来自真实状态

只显示 OpenCode 当前数据结构能够明确识别的状态。

例如实际存在:

pending
running
completed
error

就使用这些。

不要人为通过时间推断:

waiting
stuck
paused

例如:

10 秒没更新

不能自动判断为:

waiting

除非 OpenCode 本身提供了这个状态。


二十、Sub-agent Activity

如果 child session 当前正在执行:

read
grep
glob
bash
edit
skill
task

可以显示短 activity:

● explore       grep · 1m24s

或者:

● reviewer      read · 42s

原则:

显示 tool 类型
不显示 tool 内容

不要把:

grep "password" /srv/company/private/...

展示出来。


二十一、已完成 Agent 的保留策略

不要永久显示所有完成过的 agent。

建议:

running     全部
pending     全部
error       最近少量
completed   最近 2~3 个

这样并行任务很多时 sidebar 不会无限增长。


二十二、Skills

显示的是:

当前工作 session 实际调用过的 skills

不是:

安装的 skills
可用 skills
Prompt 中提到的 skills

必须从 OpenCode structured tool invocation 中获取。

例如当前 OpenCode 如果是:

tool = skill
input.name = xxx

则基于这个结构处理。

不要通过:

搜索 assistant 文本
搜索日志
搜索项目文件

来判断 Skill 是否用过。


二十三、Skills 范围

应覆盖:

当前 root session
+
属于该工作流的 sub-agent sessions

因此如果主 agent 没调用某个 skill,但 explore sub-agent 调用了:

browser-testing

最终 sidebar 也应该出现:

browser-testing

二十四、Skills UI

例如:

SKILLS · 4
debugging · code-review
browser-testing · +1

建议:

  • 去重
  • 最近使用优先
  • 重复多次可以显示 ×N
  • 最多显示约 4~6 个
  • 剩余用 +N

例如:

SKILLS · 7
debugging · code-review ×3
browser-testing · +4

不要让 Skill 列表无限向下增长。


二十五、数据恢复

不能只依赖插件启动之后监听到的 event。

用户可能:

重启 OpenCode
重新加载插件
打开旧 session
从历史 session 恢复

首次 render 时必须从当前 session 已有结构化数据重新计算:

Agents
Skills

然后再通过 event 增量更新。

换句话说:

Event 是实时更新机制
Session data 才是 source of truth

二十六、UI 风格

目标不是炫技。

不要:

大量 border
emoji 彩虹
spinner
大标题
动画
复杂 dashboard

推荐:

USAGE
OpenAI · Subscription
5h       ███████░░ 72%
          ↻ 2h14m
Weekly   █████░░░░ 48%
          ↻ 3d8h

AGENTS · 2
● explore       grep · 1m24s
✓ reviewer            42s

SKILLS · 3
debugging · code-review
browser-testing

section 之间空一行即可。


二十七、颜色

尽量使用 OpenCode 当前主题系统。

语义:

普通信息     默认文字
次要信息     muted
运行         normal / accent
成功         success
warning      warning
error        error

Quota 剩余:

> 50%     正常
20~50%    warning
< 20%     error

颜色只是辅助。

不要整行染色。


二十八、Progress Bar

自行实现简单固定宽度 progress bar 即可:

██████░░ 72%

不要因为 progress bar 再引入库。

统一约定:

bar 表示 remaining

不要一个 Provider 显示 used、另一个显示 remaining。

如果原始 API 返回 used:

在能够确定 denominator 的情况下转换成 remaining。

否则直接显示原始真实指标:

Spent $25.50

二十九、空间控制

按照 OpenCode sidebar 的实际宽度设计一个本身就足够紧凑的布局。

不要为了“响应式”构造复杂 UI engine。

主要策略:

短 label
固定长度 bar
截断过长 provider/model 名称
限制 Agent 数量
限制 Skill 数量
隐藏空 section

Sidebar 本身应该在常见宽度下自然成立。


三十、不要重复这些低价值信息

除非当前 OpenCode 原生完全没有且确实对编码有明显帮助,否则不要增加:

CPU
RAM
hostname
username
IP
当前时间
完整 cwd
系统 uptime
网络速度

这是 coding sidebar,不是系统监控面板。


三十一、可以自行判断是否增加的信息

完成:

USAGE
AGENTS
SKILLS

以后,可以检查当前 OpenCode sidebar 是否还缺少真正高价值的信息。

可以考虑的方向:

pending permission/question
异常 task 汇总
极少量 workflow attention

但必须遵循:

高价值
低噪声
OpenCode 原生没有
数据来源可靠

否则不加。

宁愿保持三块信息,也不要为了“功能多”把 sidebar 塞满。


三十二、参考项目

重点阅读:

https://github.com/Joaquinvesapa/sub-agent-statusline
https://github.com/slkiser/opencode-quota

从它们学习:

Provider quota 获取方法
Quota normalization
Sub-agent tracking
缓存
失败降级
UI 信息密度

但:

不要引入它们作为 dependency
不要运行它们的 npm package
不要机械复制它们的全部 Provider 假设

尤其 Provider endpoint 必须重新验证当前有效性。


三十三、安全审查

完成实现后,专门做一次 credential security review。

至少检查:

有没有 console.log credential
有没有 log raw headers
有没有 log raw Error
有没有保存完整 response
有没有写 cache 文件
有没有写 token 到 KV
有没有扫描 .env
有没有向非官方 endpoint 发请求
有没有自动 follow redirect
有没有 UI 泄露 account 信息

特别检查异常路径。

很多 credential 泄漏不是正常流程,而是:

throw new Error(JSON.stringify(response))

或者:

console.error(error)

导致的。


三十四、Provider 验证报告

最终不要只说:

支持 OpenAI、Claude、DeepSeek……

需要给出一个简洁的验证结果,例如:

OpenAI Subscription
✓ quota
来源:已验证当前 subscription usage endpoint
实际数据:5h / weekly(根据账号返回动态显示)

OpenRouter API
✓ usage
✓ budget(存在 key limit 时)
来源:官方 API

DeepSeek API
✓ balance
来源:官方 API

Provider X
△ 尚未完成验证

Provider Y
○ 已验证 API 存在,但当前 credential 类型无法读取

Provider Z
— 已验证没有可取得的账户级 usage/balance 数据

注意:

△ 尚未验证

和:

— 已验证拿不到

必须严格区分。


三十五、测试要求

至少验证以下情况。

Quota

正常返回
缺少某个 window
只有一个 window
多个 window
balance
budget
limit=null
reset 缺失
401
403
429
500
timeout
invalid JSON
schema changed

UI 均不能崩。


Sub-agent

无 agent
单 agent
多个并发 agent
pending
running
completed
error
sub-agent 内继续调用 tool

状态必须和 OpenCode 实际状态一致。


Skills

主 session 调 skill
sub-agent 调 skill
同一 skill 多次调用
多个 skill
打开已有 session

都应该正确重建。


Security

使用真实 credential 后检查:

stdout
stderr
OpenCode logs
插件缓存
临时目录
UI
错误输出

不得出现 API key / OAuth token。


三十六、完成标准

最终交付:

1. status-sidebar.tsx 完整可运行代码
2. 当前 OpenCode 所需的最小加载配置
3. 安装路径
4. 已验证 Provider 清单
5. 各 Provider 实际显示的数据
6. 尚未验证 Provider 清单
7. 已验证确实无法取得对应数据的清单
8. 安全审查结果
9. 实际运行验证结果

不要给伪代码。

不要只根据文档“认为可以”。

能够实际运行验证的部分必须运行验证。


三十七、实现优先级

发生冲突时遵循:

数据真实性
Credential 安全
兼容当前 OpenCode
Graceful degradation
低运行开销
信息密度
视觉效果

最重要的一条:

不知道不等于拿不到。

任何 Provider 在标记为“无法获取 quota / balance”之前,都必须先完成验证。

第二重要的一条:

Provider 返回什么,就显示什么。

不要为了让所有 Provider 看起来统一,而人为创造不存在的 5h、weekly、balance 或百分比。

第三重要的一条:

这个插件只补充 OpenCode 缺少的信息。

不要重复 OpenCode 已经做好的 sidebar 功能。