opencode 插件:每个 turn 结束时,把本轮修改的文件 diff 逐个上报到统计服务器,用于统计 AI 编写的代码量。
插件只监听一个事件:session.idle(一个 turn 结束的信号)。收到后:
- 查该 session 最后一条 user 消息 ID(
client.session.messages) - 调 opencode 官方接口
client.session.diff({ sessionID, messageID })拿本 turn 的文件级 diff - 把每个文件转成标准 git 格式 unified diff,逐个 POST 上报
session.diff 内部走的正是 opencode 的 snapshot 机制(每个 step-finish 后重算并持久化到消息的
summary.diffs),实测在 session.idle 触发前就已经算好,idle 时查一定拿得到。用官方接口的好处是
不依赖影子仓的物理路径,抗 opencode 版本变化。
这样天然满足:turn 内同一文件的多次修改只在结束后统一上报一次;并发 turn 按 sessionID 隔离不会串。
兼容性:
session.diff的返回结构在不同 opencode 版本间变过 ({file, patch, status}与{file, before, after}两套),插件对两种都做了适配, 有patch直接用,只有before/after就本地生成 diff。
| 情况 | 处理 |
|---|---|
| 新增、修改文件 | 上报 |
| 删除文件 | 不上报 |
| 纯重命名(内容零变更) | 不上报,没写代码(靠内容指纹配对「删旧+加新」识别) |
| 二进制文件 | 不上报 |
| 前台 subagent | 不单独上报,其改动已包含在父 turn 的 diff 内 |
后台 subagent(task(background:true)) |
单独上报,因为它可能在父 turn 结束后才写完 |
上报失败重试 3 次(200ms / 500ms / 1000ms 退避),仍失败则打日志丢弃。 所有上报走后台串行队列,不阻塞 opencode 主流程。
- opencode 的 diff 是整个 worktree 的快照对比,是 turn 执行期间的改动,并不确定是谁写的(agent/人工)。
- 被
.gitignore忽略的文件不进快照,不会上报。 - opencode 配置里
snapshot: false时session.diff返回空,插件打印警告后躺平。 - 改名+改内容因缺少 rename 关联,会被当作全新文件上报(整文件算新增行)。纯改名能正确跳过。
- 后台 subagent 在父 turn 结束前写的文件会被父子各上报一次,依赖服务端去重。
三层取值,优先级:环境变量 > 插件 options > 内置默认值。
| 配置项 | 环境变量 | 默认值 |
|---|---|---|
url |
OPENCODE_DIFF_DETAIL_URL |
http://127.0.0.1:9070/test |
token |
OPENCODE_DIFF_DETAIL_TOKEN |
xxxxxx |
userId |
OPENCODE_DIFF_DETAIL_USER_ID |
1234566 |
userName |
OPENCODE_DIFF_DETAIL_USER_NAME |
张三 |
logSource |
OPENCODE_DIFF_DETAIL_LOG_SOURCE |
9 |
retries |
OPENCODE_DIFF_DETAIL_RETRIES |
3 |
timeoutMs |
OPENCODE_DIFF_DETAIL_TIMEOUT_MS |
5000 |
enabled |
OPENCODE_DIFF_DETAIL_ENABLED |
true |
在 opencode.json 里注册插件并传 options:
{
"plugin": [
[
"/Users/j/project/opencode-diff-detail/src/server.ts",
{ "url": "http://127.0.0.1:9070/test" }
]
]
}bun install
bun run typecheck # tsc 类型检查(noEmit,不产出 dist)
bun run test-server # 本地上报接收端,监听 9070,请求体落盘到 ./receivedtest-server 支持 PORT 和 OUT_DIR 环境变量。
- 不构建 dist:opencode 用 Bun 直接加载
src/server.ts源码运行,import type在 转译时被剥掉,所以插件运行时不需要@opencode-ai/*,也不产出编译结果。 - 源码依赖 opencode:
@opencode-ai/plugin/@opencode-ai/sdk不装进 node_modules, 而是通过tsconfig.json的paths指向相邻的 opencode 仓源码 (默认../opencode/packages/...)。git pullopencode 后类型自动跟着更新, 避免 node_modules 里的版本与实际运行的 opencode 漂移。- 因此类型检查依赖相邻存在
../opencode源码仓;路径不同请改paths。 - 因为会一并检查 opencode 源码,本项目 tsconfig 与 opencode 对齐关闭了
exactOptionalPropertyTypes/noUncheckedIndexedAccess两个额外严格项。
- 因此类型检查依赖相邻存在
- 运行时唯一的第三方依赖是
diff(由 before/after 结构生成 patch 时用)。