跳转到主要内容
6 分钟阅读

在 OpenCode 侧边栏显示 Go 套餐用量:TUI 插件实现与使用

前言

OpenCode Go 有滚动 / 每周 / 每月三档配额,但 TUI 里默认看不到。我做了一个侧边栏插件 opencode-usage-widget,把用量画进 Files 区块下面,并支持自动刷新。本文记录实现思路、几个真正踩过的坑,以及怎么装、怎么用。

仓库:github.com/crayonxiaoxin/opencode-go-usage · npm:opencode-usage-widget

它长什么样

侧边栏会出现可折叠的 Go Usage。默认展开;点击标题折叠。折叠状态写在 OpenCode KV(opencode.usage.open),下次启动会恢复。

展开时,Rolling / Weekly / Monthly 各自一条进度条、百分比,以及该窗口自己的重置倒计时(精确到剩余分钟,例如 in 3h 15min 2d 4h 15m)。折叠时标题旁显示三档里最高的百分比,例如 ▶ Go Usage 90%

实现思路

这是一个 TUI 插件,不是 server 插件。入口导出 { id, tui },在 tui(api, options) 里做三件事:注册侧边栏插槽、注册手动刷新命令、在 dispose 时清掉定时器和进行中的请求。

code
api.slots.register({
  order: 600,
  slots: {
    sidebar_content() {
      return <UsageWidget api={api} store={store} />
    },
  },
})

sidebar_content 和内置的 Context / LSP / Files 并列渲染,order 默认 600,排在 files(500)后面。

模块大致分成:

  • usage-api.ts:请求 GET {base}/zen/go/v1/usage,解析 rolling / weekly / monthly。
  • credential.ts:按优先级找 API key。
  • use-usage.ts:Solid signal 状态机(loading / ready / error),被动刷新。
  • format.ts + widget.tsx:颜色阈值、倒计时文案、可折叠 UI。

刷新是被动的:启动拉一次;订阅 session.idle(每次回复结束);默认 300 秒定时;命令面板里的 usage.refresh。失败会退避,dispose 时 AbortController 取消进行中的请求。

几个关键坑

1. 密钥不在 path.state 里

api.state.path.state 是 XDG state 目录(常见 ~/.local/state/opencode),不是密钥所在处。API key 在 data 目录:~/.local/share/opencode/auth.json(先 opencode-goopencode,且必须是 type: "api")。OAuth token 过不了用量接口的 KeyTable 校验,需要在 opencode.ai/auth 生成 API key,或设 OPENCODE_API_KEY

2. TUI 里的 fetch 会被包到本地 server

TUI 的 globalThis.fetch 可能被转到本机 OpenCode server,用量请求会 404。插件默认走 node:httpsnativeFetch,绕过包装。

3. 本地开发不要加载 dist

OpenCode 能直接编译源码里的 JSX。本地把 tui.json 指到 file://…/src/index.tsx。如果指到打包后的 dist/tui.js,很容易打进第二份 solid-js / @opentui/solid,侧边栏渲染异常。

4. bun build 打出来的包:插件 active,侧边栏却是空的

这是发 npm 时踩到的。命令面板里 Refresh usage 在,说明 tui() 已经执行、slots.register 也成功了。但 bun 默认 JSX 会生成:

code
import { jsxDEV } from "@opentui/solid/jsx-dev-runtime"

插槽返回的节点和宿主 TUI 对不上,区块就是空白。正确做法是用 esbuild-plugin-solidgenerate: "universal"moduleName: "@opentui/solid"),让产物 import 宿主的 createElement,并把 solid-js / @opentui/* 全部 external。另外:不要设 main,否则会被当成 server 插件;opencode plug 安装带 --ignore-scripts,tarball 里必须已经有 dist/tui.js

怎么用

需要 OpenCode >= 1.18.0,以及带套餐的账号和 API key。

从 npm 安装(推荐):

code
opencode plug opencode-usage-widget -g

去掉 -g 则只装当前项目。也可以在 TUI 的 Plugins 对话框用 shift+i。装完后完全退出再打开 OpenCode。钉死版本:opencode plug opencode-usage-widget@0.1.1 -g;覆盖已有条目加 --force

从源码安装(开发时):~/.config/opencode/tui.json 写入:

code
{
  "$schema": "https://opencode.ai/tui.json",
  "plugin": [
    ["file:///绝对路径/opencode-go-usage/src/index.tsx", { "order": 600 }]
  ]
}

必须用 file://,不要用裸路径。改源码后同样要完全重启 TUI。

常用选项:apiKeybaseUrl(自托管)、orderrefreshInterval(秒,0 关定时器)、showWhenUnavailable(无凭证时是否隐藏整块)。手动刷新走命令面板 usage.refresh

总结

  • TUI 插件走 sidebar_content 插槽 + Solid 状态,不要和 server 插件混在一个入口。
  • 密钥在 data 目录的 auth.json,用量请求必须绕过 TUI 包装过的 fetch
  • 本地用源文件;npm 产物必须用 Solid 的 universal 编译,而不是 bun 默认 JSX。

如果你也在给 OpenCode 写侧边栏插件,这三件事基本能避开我们走过的弯路。

/ DISCUSS

讨论

0
No Comments

还没有留言,来留下第一条评论吧!