@chenlongapps/opencode-token-usage 0.4.8 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/usage.d.ts CHANGED
@@ -143,9 +143,10 @@ export declare function contextUsage(messages: readonly UsageMessage[], limit?:
143
143
  export declare function formatTokens(value: number): string;
144
144
  /** Native sub-minute precision; longer durations keep compact, readable units. */
145
145
  export declare function formatDuration(milliseconds: number, compact?: boolean): string;
146
- export declare function formatRunTime(runtime?: RuntimeSummary, compact?: boolean): string;
146
+ /** Hide confirmed zero totals, but preserve unavailable and stale timing. */
147
+ export declare function formatRunTime(runtime?: RuntimeSummary, compact?: boolean): string | undefined;
147
148
  /** Preserve session-local time on narrow terminals without adding composer height. */
148
- export declare function fitUsageSummary(fields: readonly (readonly [string, string])[], width: number, stale?: boolean): string;
149
+ export declare function fitUsageSummary(fields: readonly (readonly [string, string | undefined])[], width: number, stale?: boolean): string;
149
150
  export declare const formatCost: (value: number) => string;
150
151
  export declare function formatEstimatedCost(cost: number, status: CostStatus): string | undefined;
151
152
  /** Per-million rates need more precision than a rounded total cost. */
package/dist/usage.js CHANGED
@@ -209,27 +209,35 @@ export function formatDuration(milliseconds, compact = false) {
209
209
  return compact ? `${minutes}m${rest}s` : `${minutes}m ${String(rest).padStart(2, "0")}s`;
210
210
  return `${rest}s`;
211
211
  }
212
+ /** Hide confirmed zero totals, but preserve unavailable and stale timing. */
212
213
  export function formatRunTime(runtime, compact = false) {
213
214
  if (runtime?.milliseconds === undefined)
214
215
  return "—";
215
- const time = formatDuration(runtime.milliseconds, compact);
216
+ const estimated = runtime.estimatedMilliseconds !== undefined;
217
+ const milliseconds = runtime.estimatedMilliseconds ?? runtime.milliseconds;
218
+ if (runtime.status === "ready" && milliseconds === 0)
219
+ return undefined;
220
+ const duration = formatDuration(milliseconds, compact);
221
+ const time = estimated && duration !== "—" ? `~${duration}` : duration;
216
222
  return runtime.status === "stale" ? `${time} · ${compact ? "stale" : "Not updated"}` : time;
217
223
  }
218
224
  /** Preserve session-local time on narrow terminals without adding composer height. */
219
225
  export function fitUsageSummary(fields, width, stale = false) {
220
- let visible = [...fields];
226
+ let visible = fields.filter((field) => field[1] !== undefined);
221
227
  const suffix = stale ? " · Not updated" : "";
222
- const render = (title) => `${title} · ${visible.map(([label, value]) => `${label} ${value}`).join(" · ")}${suffix}`;
228
+ const render = (title) => `${[title, ...visible.map(([label, value]) => `${label} ${value}`)].join(" · ")}${suffix}`;
223
229
  for (const label of ["TPS", "Context", "Cost", "Total"]) {
224
230
  if (render("Token Usage").length <= width)
225
231
  return render("Token Usage");
226
- visible = visible.filter(([name]) => name !== label);
232
+ // Keep a useful metric even when the zero-time field is hidden.
233
+ if (visible.length > 1)
234
+ visible = visible.filter(([name]) => name !== label);
227
235
  }
228
236
  if (render("Token Usage").length <= width)
229
237
  return render("Token Usage");
230
238
  if (render("Usage").length <= width)
231
239
  return render("Usage");
232
- // Remove the title before clipping any value; time is the final retained field.
240
+ // Remove the title before clipping any value; retain time when it is present.
233
241
  return `${visible.map(([label, value]) => `${label} ${value}`).join(" · ")}${stale ? " · stale" : ""}`.slice(0, Math.max(0, width));
234
242
  }
235
243
  export const formatCost = (value) => {
@@ -335,7 +343,8 @@ export function usageRows(summary, context, performance, runtime) {
335
343
  if (performance?.ttft !== undefined && Number.isFinite(performance.ttft)) {
336
344
  rows.push(["TTFT", `${(Math.max(0, performance.ttft) / 1_000).toFixed(1)}s`]);
337
345
  }
338
- if (runtime)
339
- rows.push(["Run Time", formatRunTime(runtime)]);
346
+ const time = runtime && formatRunTime(runtime);
347
+ if (time !== undefined)
348
+ rows.push(["Run Time", time]);
340
349
  return rows;
341
350
  }
@@ -1,5 +1,117 @@
1
1
  # 验证记录
2
2
 
3
+ ## 2026-10-10 · 移除 Run Time 运行状态动画(未发布)
4
+
5
+ 侧边栏、子代理摘要/完整浮窗以及 `/usage` 紧凑/详细模式直接显示耗时,不再附加 `Running.`/`Running..`/`Running...`。移除 `src/running.ts`、动画专用计时器及其 5 项测试,清理仅供动画使用的会话 ID 传递;各入口的原生状态仍用于控制 `~` 实时估算,共享 500ms 耗时 ticker 与原生累计算法未改动。零值隐藏、不可用/未更新标记及面板末行顺序保持不变。
6
+
7
+ 新增子代理摘要回归测试覆盖实时估算、未更新/不可用值与窄终端;smoke 改为检查耗时行不含运行提示、没有新增量时估算仍推进、完成/中断后恢复原生值,并断言打包产物不包含旧动画模块。中英文 README 与产品约定已同步,旧动画验证记录保留为历史记录。
8
+
9
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.8、SDK 2.0.24、OpenTUI 0.5.14,真实宿主 OpenCode 2.0.26;沿用 V2 CLI 插件 API。
10
+
11
+ | 检查 | 结果 |
12
+ | --- | --- |
13
+ | `npm run typecheck` | 通过 |
14
+ | `npm test` | 205 项通过:移除 5 项动画测试,新增 1 项摘要回归测试 |
15
+ | `npm run build` | 通过;已清理本地旧动画构建产物 |
16
+ | `node --check scripts/smoke.mjs`、`git diff --check` | 通过 |
17
+ | `npm run test:smoke` | 真实 OpenCode 2.0.26 打包/隔离安装的完整检查通过 |
18
+
19
+ 真实 TUI 在侧边栏、两种 `/usage`、子代理摘要/完整浮窗及 48 列子代理视图中均不再显示运行提示,并在没有新流式增量时继续推进 `~` 耗时。同一耗时行在观察期间位置不变;完成/中断后恢复不带 `~` 的原生累计值,空闲父会话不随子代理增长。零值隐藏以及原有标题点击、上下文、定价、TPS/TTFT、Responses 突发/静默、分叉与压缩检查全部通过,没有调用真实付费模型或修改现有 OpenCode 配置。
20
+
21
+ 验证产物:`/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/token-usage-smoke-kuK7vo`,其中 `10-running-*-start.txt`/`advanced.txt` 保存各入口没有运行提示且耗时继续增长的画面,`result.json` 保存原生耗时、宿主版本与不含旧动画模块的打包文件列表。Node 22 的既有 OpenTUI 引擎警告与 MockTimers 实验警告保留。
22
+
23
+ 插件版本、SDK、价格快照未变;未升级版本、提交、推送或发布。
24
+
25
+ ## 2026-10-10 · 实时 TPS 突发增量保护(未发布)
26
+
27
+ `src/performance.ts` 合并 100ms 内的可观察增量,以每个近期窗口的首批增量作为计数基线,不把基线字节计入其时间戳之后的输出;有效跨度至少为 500ms,窗口仍为约 2 秒。EWMA 按经过时间计算权重,最多每 100ms 推进一次,与 delta 条数和视图读取次数解耦。超过窗口的服务端事件间隔或本地静默会重建基线并清除旧平滑值,不使用模型专属倍率、TPS 硬上限或隐藏 reasoning 猜测。
28
+
29
+ 共享性能监视器的超时任务同时负责近期窗口与已有 TTFT 保留期清理:连续 2 秒没有新 delta 后,使实时估算过期并通知视图,回退到已准备的历史 TPS,没有历史值则隐藏该行。静默年龄只使用本地单调时钟,不拿客户端 epoch 减服务端时间;超时和增量均不读取历史或调用 API,也不改变原生 Run Time。TTFT 仍取 step 开始到首个原始增量,完成后的全树 assistant `Σ(Output + Reasoning) ÷ Σ(time.streamed − time.created)` 口径不变。读取失败、切换会话、多视图共享和子代理范围沿用现有控制器。
30
+
31
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.8、SDK 2.0.24、OpenTUI 0.5.14。已复核 V2 插件/CLI 文档与版本固定的 OpenCode 2.0.26 Responses 适配器和事件总线:事件 `created` 是宿主发布时间,done-only 摘要可回退为 delta,并非内部 token 生成时钟。
32
+
33
+ | 检查 | 结果 |
34
+ | --- | --- |
35
+ | `npm run typecheck` | 通过 |
36
+ | `npm test` | 209 项通过,新增 12 项近期 TPS、突发、时钟、过期及共享控制器回归测试 |
37
+ | `npm run build` | 通过 |
38
+ | `node --check scripts/smoke.mjs`、`node --check test/fixtures/responses.mjs`、`git diff --check` | 通过 |
39
+ | `npm run test:smoke` | 真实 OpenCode 2.0.26 打包/隔离安装的完整检查通过,包含新增 Responses SSE 场景 |
40
+
41
+ 自动化测试覆盖 10ms 摘要突发、同时间戳与碎片化基线、长暂停后恢复、500ms 边界、乱序/重复/空增量、UTF-8、时间加权平滑、有效高速不限幅、客户端/远程 epoch 偏差、无事件过期、并发独立过期、删除/销毁清理、TTFT 保留及多视图不读取历史。中英文 README 已同步说明首次测速延迟、静默回退及实测/估算口径差异。
42
+
43
+ 新增本地 Responses SSE 模拟提供商,包含摘要 delta 突发、第二段 done-only 摘要、间隔 600ms 的文本和静默等待;宿主仍为真实 OpenCode,不调用付费模型或修改现有配置。首轮事件记录中,宿主将摘要交付为相隔 5ms 的 800/400 字节 delta,TUI 正确只显示 TTFT、不显示 TPS;失败的是标题误匹配断言,而非插件指标。限定为数值指标行后,完整复验通过:摘要突发不显示 TPS,后续文本显示 `~11.3 tok/s`,静默后隐藏 TPS、仍保留 `TTFT 0.3s`,完成后按服务端上报的 Output 200 与 Reasoning 800 恢复无 `~` 的历史吞吐。原有侧边栏标题、详情、窄终端、子代理、原生耗时、定价、分叉和压缩检查全部通过;不宣称真实 OpenAI 服务已验证。
44
+
45
+ 验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-ZKisPX`,其中 `11-responses-burst.txt`、`11-responses-live.txt`、`11-responses-quiet.txt`、`11-responses-completed.txt` 保存四个阶段,`stream-events.json` 保存真实宿主事件,`result.json` 记录 Responses 实时与历史 TPS 及原有全套结果。Node 22 的既有 OpenTUI 引擎警告与 MockTimers 实验警告保留。
46
+
47
+ 插件版本、SDK、价格快照未变;未升级版本、提交、推送或发布。
48
+
49
+ ## 2026-10-10 · 点击侧边栏标题查看详情(未发布)
50
+
51
+ 侧边栏 `Token Usage` 标题的左键单击复用 `/usage` 的 `UsageDialog` 与统一打开函数,默认紧凑模式,保留 `d` 切换、居中 large 弹窗、滚动及 Escape/esc 关闭。按标题所属的被查看会话打开,不改变统计口径或子代理摘要入口;仅弹窗挂载期间保留原有独立详情控制器,关闭沿用现有取消读取、订阅与 ticker 清理。
52
+
53
+ 新增 `src/click.ts`:匹配左键按下/同一位置松开,拖动取消,选中文字不触发;只有有效点击才阻止鼠标事件冒泡。OpenTUI 的文本普通点击也可能带 `isDragging: true`,因此不把该标记直接当作实际拖动。其他统计行未绑定打开事件,保留文字选择。
54
+
55
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.8、SDK 2.0.24、OpenTUI 0.5.14,真实宿主 OpenCode 2.0.26;已复核 V2 插件/CLI 文档及已安装 OpenTUI 的鼠标选择语义。
56
+
57
+ | 检查 | 结果 |
58
+ | --- | --- |
59
+ | `npm run typecheck` | 通过 |
60
+ | `npm test` | 197 项通过,新增 5 项点击、防误触与选择事件回归测试 |
61
+ | `npm run build` | 通过,包含 click 模块与类型声明 |
62
+ | `node --check scripts/smoke.mjs`、`git diff --check` | 通过 |
63
+ | `npm run test:smoke` | 真实 2.0.26 打包/隔离安装的完整检查通过 |
64
+
65
+ 真实终端新增断言覆盖空会话与已有用量、标题中键/右键、标题拖选、普通统计行不打开、切换会话后的正确身份、默认紧凑与 `d` 详细模式、Escape/点击 esc 关闭及重复打开后恢复默认模式。检查消息数量与模拟提供商请求数量均不增加;运行中的标题入口继续显示、推进并结算实时耗时。原有窄终端、子代理、全树累计、逐消息计价、未知上下文上限、分叉及已完成压缩检查全部通过。
66
+
67
+ 验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-PAneuj`,其中 `01-sidebar-usage-dialog.txt`、`06-sidebar-usage-dialog.txt` 保存标题入口的空/有用量弹窗,`10-running-usage-*` 保存运行中的详情与三帧动画,`result.json` 记录真实宿主版本、打包文件与最终统计。Node 22 的既有 OpenTUI 引擎警告与 MockTimers 实验警告保留。
68
+
69
+ 中英文 README 已同步入口与操作说明,并明确新入口仅在 2.0.26 完成集成验证。插件版本仍为 0.4.8,SDK 与 OpenTUI 依赖未变;未升级版本、提交、推送或发布。
70
+
71
+ ## 2026-10-10 · 当前已耗时实时估算(未发布)
72
+
73
+ 运行中 `Run Time`/`Time` 用 `~` 显示截至当前的累计耗时估算,首轮非零估算也可见,不预测最终或剩余时间。`src/runtime.ts` 从同一次完整历史扫描准备当前轮起点及已计入的重叠时长;显示值用当前轮估算替换同轮已有时长,`milliseconds` 仍只保存原生已完成累计。工具、重试及前台等待继续计时;terminal 回答/执行结束先冻结显示,刷新后以原生完成时间结算并移除 `~`,允许向下校正。读取失败保留冻结的估算及未更新标记,未知历史时间仍显示不可用。
74
+
75
+ 新增 `src/runtime-monitor.ts`,以服务端时间锚点和本地单调时钟推进共享的 500ms ticker;时钟 tick 不扫描历史、不读取 API。启动/重连通过现有 `opencode-token-usage.context` RPC 的只读 `clock` 方法取得时间,不保存数据、不读 MCP、不进入模型调用链路;RPC 不可用或慢响应时,可用带时间戳的实时事件建立锚点,不阻塞实测统计。无可信锚点时保留原生累计值,不用客户端 epoch 时间猜测远程时间。各入口使用同一监视器,关闭最后一个估算视图后取消 ticker;会话切换隔离旧结果,空闲父会话不继承子代理的活动。
76
+
77
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包仍为 0.4.8、SDK 2.0.24、OpenTUI 0.5.14,真实宿主 OpenCode 2.0.26。已复核 V2 插件/CLI/RPC 文档及已安装 SDK;实际 2.0.26 的 synthetic `server.connected` 握手缺少 `created`。首轮集成因此暴露了新开安静子代理视图没有锚点的问题,已用可选只读 RPC 修复,并加入实际握手形状的回归夹具。
78
+
79
+ | 检查 | 结果 |
80
+ | --- | --- |
81
+ | `npm run typecheck` | 通过 |
82
+ | `npm test` | 192 项通过,新增 24 项估算、时钟、控制器及显示回归测试 |
83
+ | `npm run build` | 通过,包含 runtime monitor 与类型声明 |
84
+ | `node --check scripts/smoke.mjs`、`git diff --check` | 通过 |
85
+ | `npm run test:smoke` | 真实 2.0.26 打包/隔离安装的完整检查通过 |
86
+
87
+ 自动化测试覆盖首轮、同轮 steer/retry 重叠替换、工具等待、历史空闲、legacy/无输入/分叉起点、terminal 完成后的迟到快照、失败冻结与恢复、无锚点降级、远程时间与客户端 epoch 偏差、共享 ticker 清理、可选 RPC 的慢响应/失败/取消/迟到结果及只读性。时钟偏差与读取失败由固定夹具验证,没有宣称在真实远程宿主注入这些故障。真实 TUI 在侧边栏、两种 `/usage`、子代理摘要/完整浮窗及 48 列视图分别捕获三个 Running 动画帧并确认无新流式增量时估算仍增长;验证首轮显示、切换/新开正在运行的会话、正常完成和中断后的精确结算、空闲父会话不随子代理增长,以及原有分叉、压缩、定价、上下文和 TPS/TTFT 检查。
88
+
89
+ 验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-FxP0I5`,其中 `02-switched-streaming.txt` 与 `04-streaming.txt` 保存首轮实时值,`10-running-*` 保存各入口实时值及三帧动画,`result.json` 保存最终原生累计值。Node 22 的既有 OpenTUI 引擎警告与 MockTimers 实验警告保留。本次未升级版本、提交、推送或发布。
90
+
91
+ ## 2026-10-10 · Run Time 运行状态动画
92
+
93
+ 可见的 `Run Time`/`Time` 值按当前查看会话的原生 `context.data.session.status(sessionID)` 附加运行状态。新增 `src/running.ts`,每 500ms 循环 `Running.`/`Running..`/`Running...`,用空格补齐三个点的位置,避免右对齐数值及摘要后续字段移动。动画只更新显示信号,不读取消息历史、不估算未完成耗时,不把子代理活动状态归给空闲的父会话。零值隐藏、不可用/未更新标记以及 Run Time 末行顺序保持不变;进入空闲、隐藏时间项、关闭视图或切换会话时清理原计时器。
94
+
95
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.8、SDK 2.0.24、OpenTUI 0.5.14,真实宿主 OpenCode 2.0.26。
96
+
97
+ 已通过 `npm run typecheck`、168 项 `npm test`(新增 5 项动画循环、固定宽度、清理/重启与格式兼容回归测试)、`npm run build`、smoke 脚本语法检查、`git diff --check` 和完整 `npm run test:smoke`。真实打包/隔离安装在侧边栏、`/usage` 两种模式、子代理摘要/完整浮窗与 48 列子代理视图中分别捕获三个动画帧,确认行位置、累计数值及 Running 起始列不移动,生成中累计值不增长。中断/完成后移除运行提示并恢复已完成原生耗时;打开已经运行的子代理能恢复状态,空闲父会话不继承子代理的运行标记。零值隐藏及原有全套集成断言均通过。
98
+
99
+ Node 22 的既有 OpenTUI 引擎警告保留。本次未升级版本、提交、推送或发布。
100
+
101
+ 验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-4anV5A`,其中 `10-running-*-1-dot.txt`/`2-dot.txt`/`3-dot.txt` 保存各入口的三个帧,`result.json` 保存最终原生耗时及原有集成结果。
102
+
103
+ ## 2026-10-10 · 隐藏零耗时项
104
+
105
+ 正常状态下确认累计原生耗时为零时,侧边栏、子代理摘要/完整浮窗以及 `/usage` 紧凑/详细模式均隐藏整个 `Run Time`/`Time` 项,不留空行或空字段。非零毫秒值、未知值 `—` 与 `Not updated`/`stale` 提示保留。耗时统计与刷新逻辑不变;窄终端没有 Time 时仍保留可用指标,而不是只剩标题。
106
+
107
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.8、SDK 2.0.24、OpenTUI 0.5.14,真实宿主 OpenCode 2.0.26。
108
+
109
+ 已通过 `npm run typecheck`、163 项 `npm test`、`npm run build` 和完整 `npm run test:smoke`。新增 3 项显示回归测试,覆盖零值隐藏、非零毫秒、无效/未知时间、未更新的零值仍带提示、其他面板行不变,以及窄终端摘要的字段和分隔符。真实打包/隔离安装检查覆盖空侧边栏、两种 `/usage` 模式、空子代理摘要/浮窗与 48 列视图;生成中继续隐藏零值,完成后恢复非零耗时,原有全套集成断言均通过。
110
+
111
+ Node 22 的既有 OpenTUI 引擎警告保留。本次未升级版本、提交、推送或发布。
112
+
113
+ 验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-sC9gcQ`,其中 `01-empty-usage-dialog.txt` 与 `01-empty-child-narrow.txt` 保存零耗时隐藏后的界面。
114
+
3
115
  ## 2026-10-09 · Run Time 移至面板末行
4
116
 
5
117
  将侧边栏与子代理完整浮窗的行序调整为 TPS → TTFT → Run Time;任一性能指标缺失时,Run Time 仍为最后一行。保留原有分组空行,统计口径与子代理单行摘要不变,`/usage` 继续在 Session 区域末行显示耗时。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chenlongapps/opencode-token-usage",
3
- "version": "0.4.8",
3
+ "version": "0.4.9",
4
4
  "description": "Session-tree token usage, live performance, context window and estimated cost for OpenCode 2",
5
5
  "keywords": [
6
6
  "opencode",