2549 字 9 分钟阅读
OpenCode Universal Status Sidebar
OpenCode 通用状态侧边栏
实现一个 单文件 OpenCode TUI 状态侧边栏插件,用于充分利用 OpenCode 右侧 sidebar 空间。
目标不是重新实现 OpenCode 自带的信息面板,而是在 当前 OpenCode 原生 sidebar 的基础上补充真正缺失的信息。
主要实现如下效果:
- 当前订阅 / API Key 的真实额度、余额或使用量。
- 当前 Sub-agent 的执行状态。
- 当前会话实际调用过的 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 功能。