@chenlongapps/opencode-token-usage 0.4.6 → 0.4.8
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/README.md +15 -9
- package/README.zh-CN.md +15 -9
- package/ROADMAP.md +9 -1
- package/dist/context-capture.d.ts +5 -3
- package/dist/context-capture.js +31 -14
- package/dist/controller.d.ts +4 -2
- package/dist/controller.js +15 -7
- package/dist/performance.js +5 -3
- package/dist/runtime.d.ts +15 -0
- package/dist/runtime.js +72 -0
- package/dist/tui.js +279 -237
- package/dist/usage.d.ts +16 -5
- package/dist/usage.js +63 -10
- package/docs/verification.md +97 -0
- package/package.json +1 -1
package/dist/usage.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PerformanceSummary } from "./performance.js";
|
|
2
|
+
import type { RuntimeSummary } from "./runtime.js";
|
|
2
3
|
export interface Tokens {
|
|
3
4
|
input: number;
|
|
4
5
|
output: number;
|
|
@@ -42,6 +43,9 @@ export interface UsageMessage {
|
|
|
42
43
|
id: string;
|
|
43
44
|
type: string;
|
|
44
45
|
status?: string;
|
|
46
|
+
finish?: string;
|
|
47
|
+
error?: unknown;
|
|
48
|
+
retry?: unknown;
|
|
45
49
|
model?: ModelRef;
|
|
46
50
|
cost?: number;
|
|
47
51
|
tokens?: TokenInput;
|
|
@@ -128,13 +132,20 @@ export interface ContextUsage {
|
|
|
128
132
|
limit: number;
|
|
129
133
|
percent: number;
|
|
130
134
|
}
|
|
131
|
-
export interface
|
|
132
|
-
usage: ContextUsage;
|
|
135
|
+
export interface RequestDetails {
|
|
133
136
|
tokens: Tokens;
|
|
137
|
+
total: number;
|
|
138
|
+
/** The selected assistant's stream end, completion, or creation time, in that order. */
|
|
139
|
+
time?: number | undefined;
|
|
134
140
|
}
|
|
135
|
-
export declare function
|
|
141
|
+
export declare function requestDetails(messages: readonly UsageMessage[], revertMessageID?: string): RequestDetails | undefined;
|
|
136
142
|
export declare function contextUsage(messages: readonly UsageMessage[], limit?: number, revertMessageID?: string): ContextUsage | undefined;
|
|
137
143
|
export declare function formatTokens(value: number): string;
|
|
144
|
+
/** Native sub-minute precision; longer durations keep compact, readable units. */
|
|
145
|
+
export declare function formatDuration(milliseconds: number, compact?: boolean): string;
|
|
146
|
+
export declare function formatRunTime(runtime?: RuntimeSummary, compact?: boolean): string;
|
|
147
|
+
/** 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;
|
|
138
149
|
export declare const formatCost: (value: number) => string;
|
|
139
150
|
export declare function formatEstimatedCost(cost: number, status: CostStatus): string | undefined;
|
|
140
151
|
/** Per-million rates need more precision than a rounded total cost. */
|
|
@@ -152,9 +163,9 @@ export declare function bar(percent: number, width: number): {
|
|
|
152
163
|
* One model request: the five categories of the last assistant call plus its cache rate.
|
|
153
164
|
* Compact mode hides zero rows and drops percentages; detailed mode keeps both.
|
|
154
165
|
*/
|
|
155
|
-
export declare function requestRows(details:
|
|
166
|
+
export declare function requestRows(details: RequestDetails, compact: boolean): readonly (readonly [string, string])[];
|
|
156
167
|
/** Tree summary rows for the dialog's detailed Session view, calls included. */
|
|
157
168
|
export declare function summaryRows(summary: Summary): readonly (readonly [string, string])[];
|
|
158
169
|
/** `1 step` reads better than `1 steps` in the dialog's single-line summaries. */
|
|
159
170
|
export declare const countLabel: (value: number, noun: string) => string;
|
|
160
|
-
export declare function usageRows(summary?: Summary, context?: ContextUsage, performance?: PerformanceSummary): readonly (readonly [string, string])[];
|
|
171
|
+
export declare function usageRows(summary?: Summary, context?: ContextUsage, performance?: PerformanceSummary, runtime?: RuntimeSummary): readonly (readonly [string, string])[];
|
package/dist/usage.js
CHANGED
|
@@ -150,13 +150,12 @@ export function summarizeModels(messages, catalog = new Map()) {
|
|
|
150
150
|
})).sort((a, b) => b.cost - a.cost || a.model.localeCompare(b.model));
|
|
151
151
|
}
|
|
152
152
|
const usableLimit = (value) => value !== undefined && Number.isFinite(value) && value > 0;
|
|
153
|
+
const usableTime = (value) => value !== undefined && value >= 0 && Number.isFinite(new Date(value).getTime());
|
|
153
154
|
// Matches the OpenCode 2.0.10 sidebar: the last assistant message with tokens that follows
|
|
154
155
|
// the last completed compaction carries the current context size. Running or failed
|
|
155
|
-
// compactions do not reset it
|
|
156
|
-
//
|
|
157
|
-
export function
|
|
158
|
-
if (!usableLimit(limit))
|
|
159
|
-
return undefined;
|
|
156
|
+
// compactions do not reset it. Missing token reports are skipped, while an explicit
|
|
157
|
+
// zero report is not replaced by older usage. Request details need no model limit.
|
|
158
|
+
export function requestDetails(messages, revertMessageID) {
|
|
160
159
|
// The staged revert excludes its boundary message and every later message.
|
|
161
160
|
// Session.message.list is ascending; an absent marker can occur during a
|
|
162
161
|
// concurrent commit, in which case the ordered ID still gives the cutoff.
|
|
@@ -178,17 +177,69 @@ export function contextDetails(messages, limit, revertMessageID) {
|
|
|
178
177
|
continue;
|
|
179
178
|
const tokens = normalize(message.tokens);
|
|
180
179
|
const used = total(tokens);
|
|
181
|
-
|
|
180
|
+
const time = [message.time?.streamed, message.time?.completed, message.time?.created].find(usableTime);
|
|
181
|
+
return used > 0 ? { tokens, total: used, time } : undefined;
|
|
182
182
|
}
|
|
183
183
|
return undefined;
|
|
184
184
|
}
|
|
185
185
|
export function contextUsage(messages, limit, revertMessageID) {
|
|
186
|
-
|
|
186
|
+
if (!usableLimit(limit))
|
|
187
|
+
return undefined;
|
|
188
|
+
const request = requestDetails(messages, revertMessageID);
|
|
189
|
+
return request ? { used: request.total, limit, percent: request.total / limit * 100 } : undefined;
|
|
187
190
|
}
|
|
188
191
|
export function formatTokens(value) {
|
|
189
192
|
return Math.round(safe(value)).toLocaleString("en-US");
|
|
190
193
|
}
|
|
191
|
-
|
|
194
|
+
/** Native sub-minute precision; longer durations keep compact, readable units. */
|
|
195
|
+
export function formatDuration(milliseconds, compact = false) {
|
|
196
|
+
if (!Number.isFinite(milliseconds) || milliseconds < 0)
|
|
197
|
+
return "—";
|
|
198
|
+
if (milliseconds === 0)
|
|
199
|
+
return "0s";
|
|
200
|
+
if (milliseconds < 1_000)
|
|
201
|
+
return `${milliseconds}ms`;
|
|
202
|
+
if (milliseconds < 60_000)
|
|
203
|
+
return `${(milliseconds / 1_000).toFixed(1)}s`;
|
|
204
|
+
const seconds = Math.floor(milliseconds / 1_000);
|
|
205
|
+
const hours = Math.floor(seconds / 3_600), minutes = Math.floor(seconds / 60) % 60, rest = seconds % 60;
|
|
206
|
+
if (hours > 0)
|
|
207
|
+
return compact ? `${hours}h${minutes}m${rest}s` : `${hours}h ${String(minutes).padStart(2, "0")}m ${String(rest).padStart(2, "0")}s`;
|
|
208
|
+
if (minutes > 0)
|
|
209
|
+
return compact ? `${minutes}m${rest}s` : `${minutes}m ${String(rest).padStart(2, "0")}s`;
|
|
210
|
+
return `${rest}s`;
|
|
211
|
+
}
|
|
212
|
+
export function formatRunTime(runtime, compact = false) {
|
|
213
|
+
if (runtime?.milliseconds === undefined)
|
|
214
|
+
return "—";
|
|
215
|
+
const time = formatDuration(runtime.milliseconds, compact);
|
|
216
|
+
return runtime.status === "stale" ? `${time} · ${compact ? "stale" : "Not updated"}` : time;
|
|
217
|
+
}
|
|
218
|
+
/** Preserve session-local time on narrow terminals without adding composer height. */
|
|
219
|
+
export function fitUsageSummary(fields, width, stale = false) {
|
|
220
|
+
let visible = [...fields];
|
|
221
|
+
const suffix = stale ? " · Not updated" : "";
|
|
222
|
+
const render = (title) => `${title} · ${visible.map(([label, value]) => `${label} ${value}`).join(" · ")}${suffix}`;
|
|
223
|
+
for (const label of ["TPS", "Context", "Cost", "Total"]) {
|
|
224
|
+
if (render("Token Usage").length <= width)
|
|
225
|
+
return render("Token Usage");
|
|
226
|
+
visible = visible.filter(([name]) => name !== label);
|
|
227
|
+
}
|
|
228
|
+
if (render("Token Usage").length <= width)
|
|
229
|
+
return render("Token Usage");
|
|
230
|
+
if (render("Usage").length <= width)
|
|
231
|
+
return render("Usage");
|
|
232
|
+
// Remove the title before clipping any value; time is the final retained field.
|
|
233
|
+
return `${visible.map(([label, value]) => `${label} ${value}`).join(" · ")}${stale ? " · stale" : ""}`.slice(0, Math.max(0, width));
|
|
234
|
+
}
|
|
235
|
+
export const formatCost = (value) => {
|
|
236
|
+
const amount = safe(value);
|
|
237
|
+
if (amount === 0)
|
|
238
|
+
return "$0.00";
|
|
239
|
+
if (amount < 0.001)
|
|
240
|
+
return "<$0.001";
|
|
241
|
+
return `$${amount.toFixed(amount > 0.01 ? 2 : 3)}`;
|
|
242
|
+
};
|
|
192
243
|
export function formatEstimatedCost(cost, status) {
|
|
193
244
|
if (status === "empty")
|
|
194
245
|
return undefined;
|
|
@@ -251,7 +302,7 @@ export function requestRows(details, compact) {
|
|
|
251
302
|
return [
|
|
252
303
|
...rows.map(([label, count]) => [
|
|
253
304
|
label,
|
|
254
|
-
compact ? formatCompact(count) : `${formatTokens(count)} (${(count / details.
|
|
305
|
+
compact ? formatCompact(count) : `${formatTokens(count)} (${(count / details.total * 100).toFixed(1)}%)`,
|
|
255
306
|
]),
|
|
256
307
|
["Cache Rate", `${(incomingTokens ? t.cache.read / incomingTokens * 100 : 0).toFixed(1)}%`],
|
|
257
308
|
];
|
|
@@ -265,7 +316,7 @@ export function summaryRows(summary) {
|
|
|
265
316
|
}
|
|
266
317
|
/** `1 step` reads better than `1 steps` in the dialog's single-line summaries. */
|
|
267
318
|
export const countLabel = (value, noun) => `${formatTokens(value)} ${noun}${value === 1 ? "" : "s"}`;
|
|
268
|
-
export function usageRows(summary, context, performance) {
|
|
319
|
+
export function usageRows(summary, context, performance, runtime) {
|
|
269
320
|
const t = summary?.tokens;
|
|
270
321
|
const number = (value) => value === undefined ? "—" : formatTokens(value);
|
|
271
322
|
const rows = [];
|
|
@@ -284,5 +335,7 @@ export function usageRows(summary, context, performance) {
|
|
|
284
335
|
if (performance?.ttft !== undefined && Number.isFinite(performance.ttft)) {
|
|
285
336
|
rows.push(["TTFT", `${(Math.max(0, performance.ttft) / 1_000).toFixed(1)}s`]);
|
|
286
337
|
}
|
|
338
|
+
if (runtime)
|
|
339
|
+
rows.push(["Run Time", formatRunTime(runtime)]);
|
|
287
340
|
return rows;
|
|
288
341
|
}
|
package/docs/verification.md
CHANGED
|
@@ -1,5 +1,102 @@
|
|
|
1
1
|
# 验证记录
|
|
2
2
|
|
|
3
|
+
## 2026-10-09 · Run Time 移至面板末行
|
|
4
|
+
|
|
5
|
+
将侧边栏与子代理完整浮窗的行序调整为 TPS → TTFT → Run Time;任一性能指标缺失时,Run Time 仍为最后一行。保留原有分组空行,统计口径与子代理单行摘要不变,`/usage` 继续在 Session 区域末行显示耗时。
|
|
6
|
+
|
|
7
|
+
已通过 `npm run typecheck`、160 项 `npm test`、`npm run build` 与真实 OpenCode 2.0.26 的完整 `npm run test:smoke`。行序测试覆盖只有 TPS、只有 TTFT、无性能数据及无效性能数值;真实终端分别核对侧边栏与子代理浮窗的末行位置。本次未升级版本、提交、推送或发布。
|
|
8
|
+
|
|
9
|
+
验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-8HSjAl`,其中 `05-message.txt` 显示 TPS、TTFT、Run Time 的最终行序。
|
|
10
|
+
|
|
11
|
+
## 2026-10-09 · 当前会话累计原生对话耗时(未发布)
|
|
12
|
+
|
|
13
|
+
环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包仍为 0.4.7、SDK 2.0.24、OpenTUI 0.5.14,真实宿主为 PATH 中的 OpenCode 2.0.26。本次未升级插件或 SDK 版本、未修改价格快照,未提交、推送或发布。
|
|
14
|
+
|
|
15
|
+
新增 `src/runtime.ts`,复用 `message.list(order: "asc")` 的完整保留历史。口径核对版本固定的宿主源码 [`turnDuration` / `legacyTurns` / `inputIndex`](https://github.com/anomalyco/opencode/blob/v2.0.26/packages/tui/src/routes/session/rows.ts):assistant 完成时间减去上一条 idle 之后首个 user/synthetic 输入的创建时间;无 idle 的旧历史取最近输入,无输入时取 assistant 创建时间,负差值按原生算法截为零。每个轮次只保留最终已完成回答的时长,工具调用等中间 step 不重复计入;未完成的 steer 保留同轮上一条已完成回答的值,后续完成时替换而不是叠加。先累加毫秒,再格式化为毫秒/一位小数秒/分时单位。
|
|
16
|
+
|
|
17
|
+
累计范围仅为当前查看会话,不额外加入子会话耗时;主会话等待子代理的跨度自然包含在主轮次内。轮次之间的空闲不增长,生成中的回答不做实时计时估算。失败/中断回答有原生完成时间戳时累计;历史完成时间缺失或无效时显示 `—`,不冒充完整的零值或小计。普通读取失败保留上次快照并标注未更新。分叉继承副本不继承累计值;压缩保留历史轮次,暂存回退不扣除,但已提交回退删除消息后相应耗时也移除。
|
|
18
|
+
|
|
19
|
+
侧边栏在用量与费用之后、TPS/TTFT 之前显示 `Run Time`;子代理摘要加入 `Time`,窄终端省略其他字段以保持单行,点击浮窗复用同一控制器;`/usage` 的 Session 区域独立显示 `Run Time (this session)`。原有 token、费用及性能统计仍覆盖会话树。已移除先前尝试的执行日志适配器、监视器与实时估算,不依赖默认未保存正文的实验性 `session.log`,也不需要服务端插件新增持久化。
|
|
20
|
+
|
|
21
|
+
| 检查 | 结果 |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `npm run typecheck` | 通过 |
|
|
24
|
+
| `npm test` | 160 项通过,新增 23 项时间算法、5 项控制器与 3 项格式/行序/窄终端回归测试 |
|
|
25
|
+
| `npm run build` | 通过,产物包含 `dist/runtime.js` 与类型声明 |
|
|
26
|
+
| `npm run test:smoke` | 完整通过,真实打包/隔离安装 + OpenCode 2.0.26 + 本地模拟提供商 |
|
|
27
|
+
|
|
28
|
+
自动化测试覆盖多轮空闲、重叠页脚去重、首个/最近输入、synthetic/steer、没有输入时的回退、无用量消息、时间缺失及无效日期、失败/中断、压缩、分叉副本及开放轮次边界、累计值重建、会话切换迟到结果隔离、子代理刷新范围、刷新失败保留快照与暂存/已提交回退。时间计算没有额外请求或每秒读取,沿用原有分页与消息快照。
|
|
29
|
+
|
|
30
|
+
真实 TUI smoke 直接比对首次回答同屏的原生页脚与插件累计值,并以独立的向后扫描 oracle 核对逐轮毫秒合计;另验证空会话为 `0s`、生成中不伪造时长、空闲不增长、累计父轮次不额外加入子代理、切换和新 TUI 从消息历史恢复、子代理浮窗与 48 列单行摘要、中断增加已上报的原生时长,以及真实分叉不继承时间、已完成 compaction 保留升序完整历史。原有 `/usage`、模型切换、TPS/TTFT、未知上下文上限、逐消息定价等全部断言继续通过。
|
|
31
|
+
|
|
32
|
+
本轮原生耗时的真实集成仅验证 2.0.26;旧历史语义由固定夹具覆盖,没有据此宣称在所有旧宿主重新完成验证。SDK 的已验证宿主记录加入 2.0.26,保留 2.0.11/2.0.22/2.0.24 的既有记录。Node 22 MockTimers 与 OpenTUI 引擎警告保留,没有隐藏或强制绕过。
|
|
33
|
+
|
|
34
|
+
验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-22Efc0`,其中 `05-message.txt` 保存同屏原生页脚与 `Run Time`,`result.json` 保存各会话累计毫秒和宿主版本。
|
|
35
|
+
|
|
36
|
+
## 2026-10-09 · 统计钩子的 MCP 等待上限
|
|
37
|
+
|
|
38
|
+
环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包仍为 0.4.6、SDK 2.0.24,真实宿主为 PATH 中的 OpenCode 2.0.25。未升级版本、提交、推送或发布。
|
|
39
|
+
|
|
40
|
+
MCP 元数据缓存缺失或失效时,共享读取默认最多等待 100 ms,构造参数可注入测试预算。独立定时器结束等待,同时通过 SDK 的 `AbortSignal` 请求中止读取;忽略取消的迟到成功/失败不会更新缓存或清除后续读取。超时或错误时复用旧元数据,不延长缓存 TTL,后续请求可重试;无缓存则跳过本次估算,保留已有估算与时间戳,不写入伪造零值。成功读取的空列表仍是有效缓存。持久化继续按会话顺序异步执行。
|
|
41
|
+
|
|
42
|
+
| 检查 | 结果 |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `npm run typecheck` | 通过 |
|
|
45
|
+
| `node --import tsx --test test/context-capture.test.ts` | 专项 14 项通过,新增 11 项回归测试 |
|
|
46
|
+
| `npm test` | 129 项通过 |
|
|
47
|
+
| `npm run build` | 通过 |
|
|
48
|
+
| `npm run test:smoke` | 完整通过,打包/隔离安装 + 真实 OpenCode 2.0.25 + 本地模拟提供商 |
|
|
49
|
+
|
|
50
|
+
新增测试覆盖永久挂起、100 ms 默认预算与可注入预算、并发共享读取、TTL/事件失效后的旧缓存回退、空缓存、超时后重试、迟到成功/失败与新读取的竞争、同步/异步错误、定时器清理,以及会话删除、flush 和服务端钩子的有界 MCP 等待。挂起与迟到竞态使用可控 Promise 和模拟时钟验证;真实 smoke 验证正常宿主链路,未在真实宿主中注入 MCP 挂起。Node 22 的 MockTimers 实验性警告和 OpenTUI 安装引擎警告未隐藏。
|
|
51
|
+
|
|
52
|
+
验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-Syw70N`。
|
|
53
|
+
|
|
54
|
+
## 2026-10-09 · Last Request 与上下文上限解耦
|
|
55
|
+
|
|
56
|
+
环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.6、SDK 2.0.24,真实宿主为 PATH 中的 OpenCode 2.0.25。
|
|
57
|
+
|
|
58
|
+
`requestDetails` 独立提取所选 assistant 的五类 token、总量和消息时间,`contextUsage` 单独校验模型上限。Last Request 的分类占比以请求总量为分母;上限缺失、为零、负值或非有限值时,实测请求仍可显示。时间依次取同一条消息有效的 `streamed`、`completed`、`created`,缺失时隐藏;来源估算的 `capturedAt` 仅显示在 Context Breakdown。
|
|
59
|
+
|
|
60
|
+
| 检查 | 结果 |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| `npm run typecheck` | 通过 |
|
|
63
|
+
| `npm test` | 116 项通过,新增 3 项请求明细、时间与边界回归测试,并扩展控制器生命周期断言 |
|
|
64
|
+
| `npm run build` | 通过 |
|
|
65
|
+
| `npm run test:smoke` | 完整通过,打包/隔离安装 + 真实 OpenCode 2.0.25 + 本地模拟提供商 |
|
|
66
|
+
|
|
67
|
+
自动化测试覆盖有效/无效上限切换、请求分类与缓存率、时间优先级及无效日期、压缩与回退边界、子会话本地请求、刷新失败保留快照、跨会话清理,以及来源 RPC 缓慢/失败/返回另一轮请求的估算时间。
|
|
68
|
+
|
|
69
|
+
真实终端验证新增上限为 0 的活动模型:Context Window 显示不可用,Last Request 在紧凑/详细模式下仍显示 `Cache Read 1.0K`/`1,000 (78.7%)` 与 `Cache Rate 83.3%`,切回有效上限后恢复 Context。另核对两个标题各自显示实测 assistant 的流式结束时间和来源估算采样时间,并验证空请求不显示时间。完整 smoke 同时通过 100/48 列布局、子代理全树汇总、TPS/TTFT、定价、真实分叉及已完成压缩与消息排序检查。启动时按既有版本策略提示 2.0.25 未验证,随后实际完成全部检查。
|
|
70
|
+
|
|
71
|
+
验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-12xAKR`,其中 `08-unknown-context-limit.txt` 保存未知上限下的详细弹窗。
|
|
72
|
+
|
|
73
|
+
## 2026-10-07 · npm 发布可见性校验“假失败”修复
|
|
74
|
+
|
|
75
|
+
环境:macOS、Node.js v22.23.2、npm 10.9.8,本地包版本为 0.4.5、SDK 为 2.0.24。本轮仅修改发布校验、工作流、测试与文档,未修改价格快照或插件运行时代码,未升级版本、移动标签、提交、推送、重跑 GitHub 工作流或实际发布 npm。
|
|
76
|
+
|
|
77
|
+
只读诊断确认已有真实自动发布链路:
|
|
78
|
+
|
|
79
|
+
- [价格更新运行 37555311596](https://github.com/chenlongapps/opencode-token-usage/actions/runs/37555311596) 由 `schedule` 触发,验证、patch 提交/原子推送、GitHub Release 和 dispatch 均成功。
|
|
80
|
+
- [发布运行 37555363814](https://github.com/chenlongapps/opencode-token-usage/actions/runs/37555363814) 固定在 `v0.4.5`(`66e0584e1f125f2e5012b081014e42d7d8d57d19`)。`npm publish` 成功,生成 provenance,并在 UTC 01:06:28 提示包仍在处理、可能需几分钟才能可用;失败的是 UTC 01:08:21 的 registry 可见性校验。旧实现只查询 12 次、间隔 10 秒,约 2 分钟即超时。
|
|
81
|
+
- 当前 registry 查询已返回目标版本与 `latest` 均为 `0.4.5`,`gitHead` 匹配上述提交,`dist.shasum` 为 `75f8490aa51a974a5d76942a4e1ffa2d9d13fd3c`,与真实发布日志一致。因此不能把该工作流的失败结论当作 npm 未收到版本,更不能为此另升 patch。
|
|
82
|
+
|
|
83
|
+
修复将按次数重试改为默认 10 分钟总时限,使用单调时钟计算剩余预算;每次版本/`latest` 请求最多 30 秒,且不得超过剩余预算,等待同样按剩余时间截短。`NPM_VERIFY_TIMEOUT_MS` 与 `NPM_VERIFY_INTERVAL_MS` 可配置并校验合法性。日志显示轮询进度、当前 `latest` 与具体查询错误;最终超时仍返回非零退出码,在 Actions 摘要中保留最后原因及原标签重试提示,不用 `continue-on-error` 掩盖故障。发布校验步骤上限为 11 分钟,整个任务仍为 20 分钟;已发布版本跳过发布与 `latest` 核验,原有禁止倒退 `latest`、registry 故障不等于版本不存在等保护保持不变。
|
|
84
|
+
|
|
85
|
+
| 检查 | 结果 |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| `npm run typecheck` | 通过 |
|
|
88
|
+
| `node --test test/release.test.mjs` | 发布专项 23 项通过 |
|
|
89
|
+
| `npm test` | 113 项通过,比上轮净增 10 项发布校验回归测试 |
|
|
90
|
+
| `npm run build` | 通过 |
|
|
91
|
+
| `npm pack --dry-run` | 通过,执行 `prepack` 构建,包含 37 个发布文件 |
|
|
92
|
+
| `npm publish --dry-run` | 通过,执行类型检查、113 项测试与构建;未写入 registry |
|
|
93
|
+
| GitHub 工作流 | 三个工作流均通过官方 actionlint 1.7.12 检查,下载产物已校验 SHA-256 |
|
|
94
|
+
| npm 只读查询 | `publish-state` 返回 `published=true`;新版 `verify-published` 确认 `0.4.5` 与 `latest` |
|
|
95
|
+
|
|
96
|
+
回归测试用注入时钟模拟版本在 3 分钟后可见、`latest` 在 4 分钟后更新;同时覆盖 404、HTTP 401/403/429/500/503、网络及无效响应、最后原因保留、单次请求 30 秒上限、请求与等待共用预算、到期后不再发起 `latest` 查询或判定成功,以及真实 AbortSignal 中止挂起请求。CLI 测试预载本地 registry 模拟,不访问真实 npm,验证环境配置、超时非零退出码和失败/成功摘要。
|
|
97
|
+
|
|
98
|
+
以上真实 GitHub 运行属于修复前的已有标签;新校验逻辑尚未在新的真实发布工作流中验证。本次没有重跑 TUI smoke,之前的宿主验证记录保留。修复提交进入 `main` 后仅供后续新标签使用;旧标签不移动,重跑已可见的 `v0.4.5` 会由原实现跳过发布并核验现有版本。
|
|
99
|
+
|
|
3
100
|
## 2026-10-07 · SDK 2.0.24 升级
|
|
4
101
|
|
|
5
102
|
环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包仍为 0.4.4。`@opencode/plugin`、`@opencode/client`、`@opencode/schema` 和 `@opencode/theme` 从 2.0.11 同步精确升级到 2.0.24;锁文件中的全部七个 OpenCode 包均为 2.0.24。OpenTUI 从 0.5.10 同步到 SDK 与 theme 配套的 0.5.14,没有追随独立发布的 0.5.15。未修改价格快照、未升插件包版本、未提交或推送、未实际发布 npm。
|