lark-remote 0.5.19 → 0.5.21
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/CHANGELOG.md +29 -0
- package/dist/bridge/approval-actions.d.ts +1 -1
- package/dist/bridge/approval-actions.js +1 -1
- package/dist/bridge/bash-run.d.ts +71 -0
- package/dist/bridge/bash-run.js +217 -0
- package/dist/bridge/compact.d.ts +155 -0
- package/dist/bridge/compact.js +450 -0
- package/dist/bridge/final-usage.js +4 -4
- package/dist/bridge/inbound-media.js +72 -46
- package/dist/bridge/index.d.ts +106 -153
- package/dist/bridge/index.js +400 -1000
- package/dist/bridge/job-watch.d.ts +68 -0
- package/dist/bridge/job-watch.js +146 -0
- package/dist/bridge/queue-manager.d.ts +3 -3
- package/dist/bridge/queue-manager.js +10 -29
- package/dist/card/approval-render.d.ts +2 -2
- package/dist/card/approval-render.js +141 -130
- package/dist/card/card-budget.js +123 -98
- package/dist/card/card-helpers.d.ts +3 -3
- package/dist/card/card-helpers.js +6 -6
- package/dist/card/final-answer-file.js +1 -1
- package/dist/card/run-renderer.d.ts +3 -3
- package/dist/card/run-renderer.js +114 -159
- package/dist/card/run-state.d.ts +1 -15
- package/dist/card/run-state.js +358 -292
- package/dist/card/tool-render.d.ts +1 -1
- package/dist/card/tool-render.js +21 -17
- package/dist/card/usage-format.d.ts +22 -7
- package/dist/card/usage-format.js +119 -100
- package/dist/cli.js +15 -3
- package/dist/clone.js +2 -2
- package/dist/common/text-width.js +36 -24
- package/dist/common/tool-summary.js +132 -90
- package/dist/config/codex-config.js +130 -106
- package/dist/config/index.d.ts +16 -2
- package/dist/config/index.js +23 -8
- package/dist/connector/index.js +6 -6
- package/dist/error-classification.js +3 -2
- package/dist/inbound/placeholder.d.ts +1 -1
- package/dist/inbound/placeholder.js +1 -1
- package/dist/index.js +387 -281
- package/dist/logger/index.js +6 -6
- package/dist/message-handlers.d.ts +0 -5
- package/dist/message-handlers.js +254 -237
- package/dist/model-catalog.d.ts +2 -2
- package/dist/model-catalog.js +3 -8
- package/dist/platform/identity.js +1 -1
- package/dist/platform/path.d.ts +1 -1
- package/dist/platform/path.js +1 -1
- package/dist/platform/sleep-blocker.js +1 -1
- package/dist/platform/terminator-win32.js +2 -1
- package/dist/router/commands/ls.d.ts +3 -0
- package/dist/router/commands/ls.js +256 -175
- package/dist/router/commands/order.js +184 -159
- package/dist/router/commands/resume.js +190 -152
- package/dist/router/commands/ws.js +219 -174
- package/dist/router/config/dsh.d.ts +4 -8
- package/dist/router/config/dsh.js +3 -10
- package/dist/router/config/kimi.js +1 -1
- package/dist/router/config/types.d.ts +1 -1
- package/dist/router/config-diff.js +1 -1
- package/dist/router/index.d.ts +56 -11
- package/dist/router/index.js +401 -391
- package/dist/runner/bash/runner.js +2 -4
- package/dist/runner/claude/session.d.ts +1 -1
- package/dist/runner/claude/session.js +6 -4
- package/dist/runner/codex/app-server/runner.d.ts +9 -2
- package/dist/runner/codex/app-server/runner.js +30 -25
- package/dist/runner/codex/app-server/translator.d.ts +2 -5
- package/dist/runner/codex/app-server/translator.js +291 -238
- package/dist/runner/common/acp/acp-approval.d.ts +1 -1
- package/dist/runner/common/acp/acp-approval.js +1 -1
- package/dist/runner/common/acp/base-acp-runner.d.ts +5 -2
- package/dist/runner/common/acp/base-acp-runner.js +26 -65
- package/dist/runner/common/acp/base-translator.d.ts +12 -2
- package/dist/runner/common/acp/base-translator.js +50 -26
- package/dist/runner/common/acp/protocol-types.d.ts +1 -1
- package/dist/runner/common/acp/protocol-types.js +2 -2
- package/dist/runner/common/connection-based-runner.d.ts +32 -1
- package/dist/runner/common/connection-based-runner.js +32 -2
- package/dist/runner/common/jsonl-stream.js +154 -187
- package/dist/runner/common/jsonrpc/client.d.ts +29 -0
- package/dist/runner/common/jsonrpc/client.js +43 -12
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +2 -2
- package/dist/runner/common/jsonrpc/connection-manager.js +4 -4
- package/dist/runner/common/spawning-runner.d.ts +6 -1
- package/dist/runner/common/spawning-runner.js +9 -4
- package/dist/runner/common/turn-started.d.ts +8 -0
- package/dist/runner/common/turn-started.js +11 -0
- package/dist/runner/dsh/approval.js +84 -51
- package/dist/runner/dsh/catalog-probe.d.ts +17 -0
- package/dist/runner/dsh/catalog-probe.js +10 -0
- package/dist/runner/dsh/client.d.ts +7 -11
- package/dist/runner/dsh/client.js +356 -289
- package/dist/runner/dsh/exit-cancel.d.ts +2 -2
- package/dist/runner/dsh/exit-cancel.js +2 -2
- package/dist/runner/dsh/inflight.d.ts +1 -1
- package/dist/runner/dsh/inflight.js +1 -1
- package/dist/runner/dsh/mux-stream.js +28 -8
- package/dist/runner/dsh/runner.d.ts +44 -4
- package/dist/runner/dsh/runner.js +296 -199
- package/dist/runner/dsh/translator.d.ts +1 -31
- package/dist/runner/dsh/translator.js +180 -178
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +1 -2
- package/dist/runner/kimi/acp/runner.js +82 -46
- package/dist/runner/kimi/acp/translator.d.ts +1 -1
- package/dist/runner/kimi/acp/translator.js +3 -4
- package/dist/runner/opencode/acp/error-monitor.js +5 -5
- package/dist/runner/opencode/acp/runner.d.ts +2 -2
- package/dist/runner/opencode/acp/runner.js +4 -4
- package/dist/runner/pi/rpc/client.js +4 -13
- package/dist/runner/pi/rpc/protocol-types.d.ts +1 -1
- package/dist/runner/pi/rpc/protocol-types.js +1 -1
- package/dist/runner/pi/rpc/runner.d.ts +3 -0
- package/dist/runner/pi/rpc/runner.js +27 -42
- package/dist/runner/pi/rpc/translator.js +61 -60
- package/dist/runner/types.d.ts +2 -2
- package/dist/runner/types.js +1 -1
- package/dist/session/claude/sessions.d.ts +45 -0
- package/dist/session/claude/sessions.js +160 -122
- package/dist/session/codex/rollout-reader.js +181 -182
- package/dist/session/common/throw-session-list-error.d.ts +10 -0
- package/dist/session/common/throw-session-list-error.js +16 -0
- package/dist/session/dsh/sessions.js +291 -235
- package/dist/session/kimi/sessions.d.ts +40 -8
- package/dist/session/kimi/sessions.js +373 -301
- package/dist/session/opencode/sessions.d.ts +142 -1
- package/dist/session/opencode/sessions.js +378 -321
- package/dist/session/pi/sessions.js +4 -5
- package/dist/session/session-store.d.ts +9 -1
- package/dist/session/session-store.js +99 -99
- package/dist/update/version-check.js +2 -1
- package/dist/workspace/index.d.ts +1 -1
- package/dist/workspace/index.js +1 -1
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ export { displayWidth, truncateMiddleToDisplayWidth, truncateToDisplayWidth, } f
|
|
|
17
17
|
* 手机(~390px 内容宽)上折叠面板的标题只有**一行**可用:减去图标与内边距约
|
|
18
18
|
* 320px,按 ~7.5px/半角算 ≈ 42 个半角宽度(iPhone SE 更窄,超出时按 `·`/空格
|
|
19
19
|
* 折行仍可读,但不能整段换行)。超过就折行,断点常常落在 URL 或命令
|
|
20
|
-
*
|
|
20
|
+
* 中间。完整参数在展开后的正文里,标题只负责"哪个工具、
|
|
21
21
|
* 动了什么",并带上 `HH:mm` 时间戳(一起计入预算)。
|
|
22
22
|
*/
|
|
23
23
|
export declare const HEADER_DISPLAY_MAX = 42;
|
package/dist/card/tool-render.js
CHANGED
|
@@ -20,7 +20,7 @@ export { displayWidth, truncateMiddleToDisplayWidth, truncateToDisplayWidth, } f
|
|
|
20
20
|
* 手机(~390px 内容宽)上折叠面板的标题只有**一行**可用:减去图标与内边距约
|
|
21
21
|
* 320px,按 ~7.5px/半角算 ≈ 42 个半角宽度(iPhone SE 更窄,超出时按 `·`/空格
|
|
22
22
|
* 折行仍可读,但不能整段换行)。超过就折行,断点常常落在 URL 或命令
|
|
23
|
-
*
|
|
23
|
+
* 中间。完整参数在展开后的正文里,标题只负责"哪个工具、
|
|
24
24
|
* 动了什么",并带上 `HH:mm` 时间戳(一起计入预算)。
|
|
25
25
|
*/
|
|
26
26
|
export const HEADER_DISPLAY_MAX = 42;
|
|
@@ -98,11 +98,23 @@ export function toolBodyMd(tool, opts = {}) {
|
|
|
98
98
|
// 同上:走 truncate 保证切点不落在代理对中间(末尾 '…' 由 truncate 附加以对齐预算)。
|
|
99
99
|
return `${truncate(body, BODY_TOTAL_MAX)}\n\n_(body 已截断,完整内容查日志)_`;
|
|
100
100
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
function renderFieldInput(str, spec) {
|
|
102
|
+
const value = str(spec.key);
|
|
103
|
+
if (!value)
|
|
104
|
+
return '';
|
|
105
|
+
const shown = spec.truncate ? truncate(value, BODY_FIELD_MAX) : value;
|
|
106
|
+
return spec.fence
|
|
107
|
+
? `**${spec.label}**\n\`\`\`${spec.fence}\n${shown}\n\`\`\``
|
|
108
|
+
: `**${spec.label}** \`${shown}\``;
|
|
105
109
|
}
|
|
110
|
+
const BASH_FIELD = {
|
|
111
|
+
key: 'command',
|
|
112
|
+
label: 'Command',
|
|
113
|
+
truncate: true,
|
|
114
|
+
fence: 'bash',
|
|
115
|
+
};
|
|
116
|
+
const GLOB_FIELD = { key: 'pattern', label: 'Pattern', truncate: true };
|
|
117
|
+
const LS_FIELD = { key: 'path', label: 'Path', truncate: false };
|
|
106
118
|
function renderFileInput(str) {
|
|
107
119
|
const fp = str('file_path') || str('path');
|
|
108
120
|
return fp ? `**File** \`${fp}\`` : '';
|
|
@@ -115,14 +127,6 @@ function renderGrepInput(str) {
|
|
|
115
127
|
lines.push(`**Path** \`${str('path')}\``);
|
|
116
128
|
return lines.join('\n');
|
|
117
129
|
}
|
|
118
|
-
function renderGlobInput(str) {
|
|
119
|
-
const p = str('pattern');
|
|
120
|
-
return p ? `**Pattern** \`${truncate(p, BODY_FIELD_MAX)}\`` : '';
|
|
121
|
-
}
|
|
122
|
-
function renderLsInput(str) {
|
|
123
|
-
const p = str('path');
|
|
124
|
-
return p ? `**Path** \`${p}\`` : '';
|
|
125
|
-
}
|
|
126
130
|
function renderWebFetchInput(str) {
|
|
127
131
|
return str('url') ? `**URL** ${str('url')}` : '';
|
|
128
132
|
}
|
|
@@ -149,7 +153,7 @@ function renderInput(tool) {
|
|
|
149
153
|
// 完全一致。hint 分支与 name case 共用同一渲染函数,无复制粘贴。
|
|
150
154
|
switch (tool.toolHint) {
|
|
151
155
|
case 'shell':
|
|
152
|
-
return
|
|
156
|
+
return renderFieldInput(str, BASH_FIELD);
|
|
153
157
|
case 'file':
|
|
154
158
|
case 'patch':
|
|
155
159
|
return renderFileInput(str);
|
|
@@ -162,15 +166,15 @@ function renderInput(tool) {
|
|
|
162
166
|
}
|
|
163
167
|
switch (toolKindOf(tool.name)) {
|
|
164
168
|
case 'shell':
|
|
165
|
-
return
|
|
169
|
+
return renderFieldInput(str, BASH_FIELD);
|
|
166
170
|
case 'file':
|
|
167
171
|
return renderFileInput(str);
|
|
168
172
|
case 'grep':
|
|
169
173
|
return renderGrepInput(str);
|
|
170
174
|
case 'glob':
|
|
171
|
-
return
|
|
175
|
+
return renderFieldInput(str, GLOB_FIELD);
|
|
172
176
|
case 'ls':
|
|
173
|
-
return
|
|
177
|
+
return renderFieldInput(str, LS_FIELD);
|
|
174
178
|
case 'webfetch':
|
|
175
179
|
return renderWebFetchInput(str);
|
|
176
180
|
case 'websearch':
|
|
@@ -9,17 +9,24 @@ import type { UsageSnapshot } from '../session-contract.js';
|
|
|
9
9
|
/** `formatUsageStats` 只读这几项:会话/进度卡片上的紧凑用量行。 */
|
|
10
10
|
export type SessionDisplayUsage = Pick<UsageSnapshot, 'contextLength' | 'contextLimit' | 'compactCount' | 'cacheReadTokens' | 'cacheCreationTokens'>;
|
|
11
11
|
/**
|
|
12
|
-
* Format
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* Format number to human-readable unit string.
|
|
13
|
+
* - >= 999_500: M with at most one decimal (e.g., 999_600 → 1M, 1200000 → 1.2M,
|
|
14
|
+
* 25000000 → 25M)
|
|
15
|
+
* - >= 1K: rounded to nearest integer K (e.g., 1500 → 2K, 120000 → 120K)
|
|
16
|
+
* - < 1K: as-is (e.g., 500 → "500")
|
|
17
|
+
*
|
|
18
|
+
* 千位边界取 M 档:999_500 起按 K 四舍五入会得到 "1000K",直接按 M 档展示。
|
|
19
|
+
* 此前 run-renderer 另有一份 formatTokens 只在这边带此边界护栏,两处已收敛
|
|
20
|
+
* 到本函数(run-renderer 的状态行 `📊 N` 也走这里)。
|
|
16
21
|
*/
|
|
17
|
-
export declare function
|
|
22
|
+
export declare function formatTokenK(n: number): string;
|
|
23
|
+
interface UsageStatsInput extends UsageSnapshot {
|
|
18
24
|
/** 实际生效模型(system.init 携带)。非空字符串才显示。 */
|
|
19
25
|
model?: string;
|
|
20
26
|
/** 本次 run 花费(USD)。存在才显示。 */
|
|
21
27
|
costUsd?: number;
|
|
22
|
-
}
|
|
28
|
+
}
|
|
29
|
+
interface UsageDisplayOptions {
|
|
23
30
|
showResult?: boolean;
|
|
24
31
|
result?: string;
|
|
25
32
|
/**
|
|
@@ -27,4 +34,12 @@ export declare function formatUsageStats(usage?: UsageSnapshot & {
|
|
|
27
34
|
* 本轮 turn 结束不等于任务结束。
|
|
28
35
|
*/
|
|
29
36
|
completedLabel?: string;
|
|
30
|
-
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Format usage stats (context length, compact count, cache tokens) into a display string.
|
|
40
|
+
* Each stat on its own line with blank lines between sections.
|
|
41
|
+
* @param usage - Usage stats object
|
|
42
|
+
* @param options - Optional display options (showResult adds result line)
|
|
43
|
+
*/
|
|
44
|
+
export declare function formatUsageStats(usage?: UsageStatsInput, options?: UsageDisplayOptions): string;
|
|
45
|
+
export {};
|
|
@@ -7,12 +7,17 @@
|
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
9
|
* Format number to human-readable unit string.
|
|
10
|
-
* - >=
|
|
10
|
+
* - >= 999_500: M with at most one decimal (e.g., 999_600 → 1M, 1200000 → 1.2M,
|
|
11
|
+
* 25000000 → 25M)
|
|
11
12
|
* - >= 1K: rounded to nearest integer K (e.g., 1500 → 2K, 120000 → 120K)
|
|
12
13
|
* - < 1K: as-is (e.g., 500 → "500")
|
|
14
|
+
*
|
|
15
|
+
* 千位边界取 M 档:999_500 起按 K 四舍五入会得到 "1000K",直接按 M 档展示。
|
|
16
|
+
* 此前 run-renderer 另有一份 formatTokens 只在这边带此边界护栏,两处已收敛
|
|
17
|
+
* 到本函数(run-renderer 的状态行 `📊 N` 也走这里)。
|
|
13
18
|
*/
|
|
14
|
-
function formatTokenK(n) {
|
|
15
|
-
if (n >=
|
|
19
|
+
export function formatTokenK(n) {
|
|
20
|
+
if (n >= 999_500) {
|
|
16
21
|
const m = n / 1_000_000;
|
|
17
22
|
const rounded = Math.round(m * 10) / 10;
|
|
18
23
|
// Drop unnecessary ".0" for whole numbers (2.0M → 2M)
|
|
@@ -24,122 +29,136 @@ function formatTokenK(n) {
|
|
|
24
29
|
}
|
|
25
30
|
return n.toString();
|
|
26
31
|
}
|
|
32
|
+
function cumulativeField(value) {
|
|
33
|
+
return value !== undefined ? ` · 累计 ${formatTokenK(value)}` : '';
|
|
34
|
+
}
|
|
35
|
+
function cumulativeCachePercent(usage) {
|
|
36
|
+
const cumInput = usage?.cumulativeInputTokens;
|
|
37
|
+
const cumCacheRead = usage?.cumulativeCacheReadTokens;
|
|
38
|
+
return cumInput !== undefined && cumCacheRead !== undefined && cumInput + cumCacheRead > 0
|
|
39
|
+
? Math.round((cumCacheRead / (cumInput + cumCacheRead)) * 100)
|
|
40
|
+
: null;
|
|
41
|
+
}
|
|
42
|
+
function cumulativeParts(usage) {
|
|
43
|
+
const cumCacheRead = usage?.cumulativeCacheReadTokens;
|
|
44
|
+
const percent = cumulativeCachePercent(usage);
|
|
45
|
+
const cacheRead = cumCacheRead !== undefined
|
|
46
|
+
? cumulativeField(cumCacheRead) + (percent !== null ? ` (${percent}%)` : '')
|
|
47
|
+
: '';
|
|
48
|
+
return {
|
|
49
|
+
input: cumulativeField(usage?.cumulativeInputTokens),
|
|
50
|
+
output: cumulativeField(usage?.cumulativeOutputTokens),
|
|
51
|
+
cacheRead,
|
|
52
|
+
cacheCreate: cumulativeField(usage?.cumulativeCacheCreationTokens),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
27
55
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
56
|
+
* 一致性护栏:本 run ≤ 累计 是硬不变量(累计 = 所有 run 之和,必 ≥ 本 run)。
|
|
57
|
+
* 违反说明来源 scope 混用(如 DeepSeek live result usage 是累积/超大 scope,
|
|
58
|
+
* 大于 jsonl 去重聚合的 session 累计)。只标记不修正——数字保持原样便于排查,
|
|
59
|
+
* 由数据源修复。两侧都有值时校验(避免旧数据/缺失字段误报)。
|
|
60
|
+
* 两个分支(real input/output / 10% 估计)共用,保证"本 run > 累计"在任意
|
|
61
|
+
* 路径都显式暴露。
|
|
32
62
|
*/
|
|
33
|
-
|
|
63
|
+
function cumulativeTotalSuffix(usage, totalTokens) {
|
|
64
|
+
const cum = usage?.cumulativeTotalTokens;
|
|
65
|
+
if (cum === undefined)
|
|
66
|
+
return '';
|
|
67
|
+
const guard = totalTokens > cum ? ' · ⚠️ 累计异常' : '';
|
|
68
|
+
return cumulativeField(cum) + guard;
|
|
69
|
+
}
|
|
70
|
+
function contextLine(length, usage) {
|
|
71
|
+
const ctxLimit = usage.contextLimit;
|
|
72
|
+
const ctxPercent = ctxLimit !== undefined && ctxLimit > 0 ? ` (${Math.round((length / ctxLimit) * 100)}%)` : '';
|
|
73
|
+
const preCompact = usage.compactPreContextLength !== undefined
|
|
74
|
+
? `(压缩前 ${formatTokenK(usage.compactPreContextLength)})`
|
|
75
|
+
: '';
|
|
76
|
+
return `Context - ${formatTokenK(length)}${ctxPercent}${preCompact}`;
|
|
77
|
+
}
|
|
78
|
+
function headerLines(usage, options) {
|
|
34
79
|
const lines = [];
|
|
35
|
-
// 1. Success 状态(通用格式,无具体结果值)
|
|
36
80
|
lines.push(options?.completedLabel ?? '✅ 已完成');
|
|
37
|
-
|
|
38
|
-
if (usage?.model) {
|
|
81
|
+
if (usage?.model)
|
|
39
82
|
lines.push(`Model - ${usage.model}`);
|
|
40
|
-
|
|
41
|
-
// 1.5. 可选的结果行(showResult 为 true 时显示)
|
|
42
|
-
if (options?.showResult && options?.result) {
|
|
83
|
+
if (options?.showResult && options?.result)
|
|
43
84
|
lines.push(`结果 - ${options.result}`);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (usage?.
|
|
47
|
-
const ctxLimit = usage.contextLimit;
|
|
48
|
-
const ctxPercent = ctxLimit !== undefined && ctxLimit > 0
|
|
49
|
-
? ` (${Math.round((usage.contextLength / ctxLimit) * 100)}%)`
|
|
50
|
-
: '';
|
|
51
|
-
const preCompact = usage.compactPreContextLength !== undefined
|
|
52
|
-
? `(压缩前 ${formatTokenK(usage.compactPreContextLength)})`
|
|
53
|
-
: '';
|
|
54
|
-
lines.push(`Context - ${formatTokenK(usage.contextLength)}${ctxPercent}${preCompact}`);
|
|
55
|
-
}
|
|
56
|
-
// 3. Compact 次数
|
|
57
|
-
if (usage?.compactCount) {
|
|
85
|
+
if (usage?.contextLength !== undefined)
|
|
86
|
+
lines.push(contextLine(usage.contextLength, usage));
|
|
87
|
+
if (usage?.compactCount)
|
|
58
88
|
lines.push(`Compact - ${usage.compactCount}次`);
|
|
89
|
+
return lines;
|
|
90
|
+
}
|
|
91
|
+
/** 命中率分母由调用方给:real 分支是 input+cacheRead,estimate 分支是 totalInput。 */
|
|
92
|
+
function makeTokenLines(input, output, cacheRead, cacheCreation, totalTokens, cachePercent, parts, usage) {
|
|
93
|
+
const lines = [
|
|
94
|
+
`Input token - ${formatTokenK(input)}${parts.input}`,
|
|
95
|
+
`Output token - ${formatTokenK(output)}${parts.output}`,
|
|
96
|
+
];
|
|
97
|
+
if (cacheCreation > 0) {
|
|
98
|
+
lines.push(`Cache create - ${formatTokenK(cacheCreation)}${parts.cacheCreate}`);
|
|
59
99
|
}
|
|
60
|
-
|
|
100
|
+
lines.push(`Cached token - ${formatTokenK(cacheRead)} (${cachePercent}%)${parts.cacheRead}`);
|
|
101
|
+
lines.push(`Total token - ${formatTokenK(totalTokens)}${cumulativeTotalSuffix(usage, totalTokens)}`);
|
|
102
|
+
return lines;
|
|
103
|
+
}
|
|
104
|
+
function realTokenLines(usage, realInput, realOutput, parts) {
|
|
105
|
+
// Unified ccusage-aligned formula:
|
|
106
|
+
// total = max(total_tokens, input + output + cacheRead + cacheCreation)
|
|
107
|
+
// cache% = cacheRead / (input + cacheRead)
|
|
108
|
+
// cacheRead/cacheCreation are never double-counted; total_tokens folds in any
|
|
109
|
+
// extra (e.g. opencode reasoning, which is separate from output) via max().
|
|
110
|
+
const cacheRead = usage.cacheReadTokens ?? 0;
|
|
111
|
+
const cacheCreation = usage.cacheCreationTokens ?? 0;
|
|
112
|
+
const sumParts = realInput + realOutput + cacheRead + cacheCreation;
|
|
113
|
+
const totalTokens = usage.totalTokens != null ? Math.max(usage.totalTokens, sumParts) : sumParts;
|
|
114
|
+
const promptTokens = realInput + cacheRead;
|
|
115
|
+
const cachePercent = promptTokens > 0 ? Math.round((cacheRead / promptTokens) * 100) : 0;
|
|
116
|
+
return makeTokenLines(realInput, realOutput, cacheRead, cacheCreation, totalTokens, cachePercent, parts, usage);
|
|
117
|
+
}
|
|
118
|
+
function estimateTokenLines(usage, parts) {
|
|
119
|
+
// Estimate path: when real input/output are absent,
|
|
120
|
+
// fall back to the 10% estimate based on contextLength.
|
|
61
121
|
const cacheRead = usage?.cacheReadTokens ?? 0;
|
|
62
122
|
const cacheCreation = usage?.cacheCreationTokens ?? 0;
|
|
123
|
+
const inputRef = usage?.contextLength ?? cacheRead;
|
|
124
|
+
if (inputRef <= 0 && cacheRead <= 0 && cacheCreation <= 0)
|
|
125
|
+
return [];
|
|
126
|
+
const totalInput = inputRef + cacheRead;
|
|
127
|
+
const outputEst = inputRef > 0 ? Math.round(inputRef * 0.1) : 0;
|
|
128
|
+
const cachePercent = totalInput > 0 ? Math.round((cacheRead / totalInput) * 100) : 0;
|
|
129
|
+
const totalTokens = totalInput + outputEst + cacheCreation;
|
|
130
|
+
return makeTokenLines(totalInput, outputEst, cacheRead, cacheCreation, totalTokens, cachePercent, parts, usage);
|
|
131
|
+
}
|
|
132
|
+
function tokenLines(usage, parts) {
|
|
63
133
|
const realInput = usage?.inputTokens;
|
|
64
134
|
const realOutput = usage?.outputTokens;
|
|
65
|
-
// Cumulative suffix (session-wide totals), appended to Input/Output lines.
|
|
66
|
-
const cumInputSuffix = usage?.cumulativeInputTokens !== undefined
|
|
67
|
-
? ` · 累计 ${formatTokenK(usage.cumulativeInputTokens)}`
|
|
68
|
-
: '';
|
|
69
|
-
const cumOutputSuffix = usage?.cumulativeOutputTokens !== undefined
|
|
70
|
-
? ` · 累计 ${formatTokenK(usage.cumulativeOutputTokens)}`
|
|
71
|
-
: '';
|
|
72
|
-
const cumTotalSuffix = usage?.cumulativeTotalTokens !== undefined
|
|
73
|
-
? ` · 累计 ${formatTokenK(usage.cumulativeTotalTokens)}`
|
|
74
|
-
: '';
|
|
75
|
-
// 一致性护栏:本 run ≤ 累计 是硬不变量(累计 = 所有 run 之和,必 ≥ 本 run)。
|
|
76
|
-
// 违反说明来源 scope 混用(如 DeepSeek live result usage 是累积/超大 scope,
|
|
77
|
-
// 大于 jsonl 去重聚合的 session 累计)。只标记不修正——数字保持原样便于排查,
|
|
78
|
-
// 由数据源修复。两侧都有值时校验(避免旧数据/缺失字段误报)。
|
|
79
|
-
// 两个分支(real input/output / 10% 估计)共用,保证"本 run > 累计"在任意
|
|
80
|
-
// 路径都显式暴露。
|
|
81
|
-
const withCumGuard = (totalTokens) => cumTotalSuffix +
|
|
82
|
-
(usage?.cumulativeTotalTokens !== undefined && totalTokens > usage.cumulativeTotalTokens
|
|
83
|
-
? ' · ⚠️ 累计异常'
|
|
84
|
-
: '');
|
|
85
|
-
// 计算累计 cache 命中率
|
|
86
|
-
const cumInput = usage?.cumulativeInputTokens;
|
|
87
|
-
const cumCacheRead = usage?.cumulativeCacheReadTokens;
|
|
88
|
-
const cumCachePercent = cumInput !== undefined && cumCacheRead !== undefined && cumInput + cumCacheRead > 0
|
|
89
|
-
? Math.round((cumCacheRead / (cumInput + cumCacheRead)) * 100)
|
|
90
|
-
: null;
|
|
91
|
-
// 构建累计 suffix
|
|
92
|
-
const cumCacheReadSuffix = cumCacheRead !== undefined
|
|
93
|
-
? ` · 累计 ${formatTokenK(cumCacheRead)}` +
|
|
94
|
-
(cumCachePercent !== null ? ` (${cumCachePercent}%)` : '')
|
|
95
|
-
: '';
|
|
96
|
-
const cumCacheCreateSuffix = usage?.cumulativeCacheCreationTokens !== undefined
|
|
97
|
-
? ` · 累计 ${formatTokenK(usage.cumulativeCacheCreationTokens)}`
|
|
98
|
-
: '';
|
|
99
135
|
if (realInput !== undefined && realOutput !== undefined) {
|
|
100
|
-
|
|
101
|
-
// total = max(total_tokens, input + output + cacheRead + cacheCreation)
|
|
102
|
-
// cache% = cacheRead / (input + cacheRead)
|
|
103
|
-
// cacheRead/cacheCreation are never double-counted; total_tokens folds in any
|
|
104
|
-
// extra (e.g. opencode reasoning, which is separate from output) via max().
|
|
105
|
-
const promptTokens = realInput + cacheRead;
|
|
106
|
-
const cachePercent = promptTokens > 0 ? Math.round((cacheRead / promptTokens) * 100) : 0;
|
|
107
|
-
const sumParts = realInput + realOutput + cacheRead + cacheCreation;
|
|
108
|
-
const totalTokens = usage?.totalTokens != null ? Math.max(usage.totalTokens, sumParts) : sumParts;
|
|
109
|
-
lines.push(`Input token - ${formatTokenK(realInput)}${cumInputSuffix}`);
|
|
110
|
-
lines.push(`Output token - ${formatTokenK(realOutput)}${cumOutputSuffix}`);
|
|
111
|
-
if (cacheCreation > 0) {
|
|
112
|
-
lines.push(`Cache create - ${formatTokenK(cacheCreation)}${cumCacheCreateSuffix}`);
|
|
113
|
-
}
|
|
114
|
-
lines.push(`Cached token - ${formatTokenK(cacheRead)} (${cachePercent}%)${cumCacheReadSuffix}`);
|
|
115
|
-
lines.push(`Total token - ${formatTokenK(totalTokens)}${withCumGuard(totalTokens)}`);
|
|
136
|
+
return realTokenLines(usage, realInput, realOutput, parts);
|
|
116
137
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (inputRef > 0 || cacheRead > 0 || cacheCreation > 0) {
|
|
122
|
-
const totalInput = inputRef + cacheRead;
|
|
123
|
-
const outputEst = inputRef > 0 ? Math.round(inputRef * 0.1) : 0;
|
|
124
|
-
const cachePercent = totalInput > 0 ? Math.round((cacheRead / totalInput) * 100) : 0;
|
|
125
|
-
const totalTokens = totalInput + outputEst + cacheCreation;
|
|
126
|
-
lines.push(`Input token - ${formatTokenK(totalInput)}${cumInputSuffix}`);
|
|
127
|
-
lines.push(`Output token - ${formatTokenK(outputEst)}${cumOutputSuffix}`);
|
|
128
|
-
if (cacheCreation > 0) {
|
|
129
|
-
lines.push(`Cache create - ${formatTokenK(cacheCreation)}${cumCacheCreateSuffix}`);
|
|
130
|
-
}
|
|
131
|
-
lines.push(`Cached token - ${formatTokenK(cacheRead)} (${cachePercent}%)${cumCacheReadSuffix}`);
|
|
132
|
-
lines.push(`Total token - ${formatTokenK(totalTokens)}${withCumGuard(totalTokens)}`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
// 8. 本次 run 花费(USD)
|
|
138
|
+
return estimateTokenLines(usage, parts);
|
|
139
|
+
}
|
|
140
|
+
function trailingLines(usage) {
|
|
141
|
+
const lines = [];
|
|
136
142
|
if (usage?.costUsd !== undefined) {
|
|
137
143
|
lines.push(`Cost - $${usage.costUsd.toFixed(4)}`);
|
|
138
144
|
}
|
|
139
|
-
// 9. 推理 token(> 0 才显示,格式对齐既有 token 行)
|
|
140
145
|
if (usage?.reasoningTokens !== undefined && usage.reasoningTokens > 0) {
|
|
141
146
|
lines.push(`Reasoning token - ${formatTokenK(usage.reasoningTokens)}`);
|
|
142
147
|
}
|
|
143
|
-
return lines
|
|
148
|
+
return lines;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Format usage stats (context length, compact count, cache tokens) into a display string.
|
|
152
|
+
* Each stat on its own line with blank lines between sections.
|
|
153
|
+
* @param usage - Usage stats object
|
|
154
|
+
* @param options - Optional display options (showResult adds result line)
|
|
155
|
+
*/
|
|
156
|
+
export function formatUsageStats(usage, options) {
|
|
157
|
+
const parts = cumulativeParts(usage);
|
|
158
|
+
return [
|
|
159
|
+
...headerLines(usage, options),
|
|
160
|
+
...tokenLines(usage, parts),
|
|
161
|
+
...trailingLines(usage),
|
|
162
|
+
].join('\n');
|
|
144
163
|
}
|
|
145
164
|
//# sourceMappingURL=usage-format.js.map
|
package/dist/cli.js
CHANGED
|
@@ -92,10 +92,17 @@ export async function bootstrap(deps) {
|
|
|
92
92
|
forwarders.set(sig, forwarder);
|
|
93
93
|
onSignal(sig, forwarder);
|
|
94
94
|
}
|
|
95
|
-
child.on('error', () => {
|
|
95
|
+
child.on('error', (err) => {
|
|
96
|
+
// bun 拉起失败(ENOENT/EACCES)会静默切 node 兜底,但错误本身必须留痕,
|
|
97
|
+
// 否则「为什么跑在了 node 上」无从排查。logger 此时尚未初始化,console
|
|
98
|
+
// 是规则允许的启动期例外。
|
|
99
|
+
console.error('[lark-remote] bun spawn failed, falling back to node:', err);
|
|
96
100
|
for (const [sig, forwarder] of forwarders)
|
|
97
101
|
offSignal(sig, forwarder);
|
|
98
|
-
void importEntry().catch(() =>
|
|
102
|
+
void importEntry().catch((importErr) => {
|
|
103
|
+
console.error('[lark-remote] node fallback import failed:', importErr);
|
|
104
|
+
exit(1);
|
|
105
|
+
});
|
|
99
106
|
});
|
|
100
107
|
child.on('exit', (code, signal) => {
|
|
101
108
|
if (signal) {
|
|
@@ -146,7 +153,12 @@ function main() {
|
|
|
146
153
|
killSelf: (sig) => process.kill(process.pid, sig),
|
|
147
154
|
exit: (code) => process.exit(code),
|
|
148
155
|
};
|
|
149
|
-
|
|
156
|
+
// bootstrap 是第一接触面:静默 exit(1) 会让安装损坏/依赖缺失表现为零输出
|
|
157
|
+
// 退出,必须把根因打到 stderr(logger 未初始化,console 为规则允许的例外)。
|
|
158
|
+
void bootstrap(deps).catch((err) => {
|
|
159
|
+
console.error('[lark-remote] bootstrap failed:', err);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
});
|
|
150
162
|
}
|
|
151
163
|
if (isDirectEntry()) {
|
|
152
164
|
main();
|
package/dist/clone.js
CHANGED
|
@@ -237,7 +237,7 @@ export class CloneSession {
|
|
|
237
237
|
// 图片发送失败不阻断流程:给出浏览器打开兜底。
|
|
238
238
|
await this.connector
|
|
239
239
|
.sendWithRetry(ctx.chatId, { text: `⚠️ 二维码图片发送失败,请在浏览器打开创建:${info.url}` }, { replyTo: ctx.messageId })
|
|
240
|
-
.catch(() =>
|
|
240
|
+
.catch((err) => getLogger().warn('[clone] fallback notice send failed:', err));
|
|
241
241
|
return;
|
|
242
242
|
}
|
|
243
243
|
finally {
|
|
@@ -336,7 +336,7 @@ export class CloneSession {
|
|
|
336
336
|
`新应用凭证(请妥善保管):App ID ${result.client_id}\n` +
|
|
337
337
|
'可修复本机问题后用 /clone 重新走一遍流程(会创建新应用)',
|
|
338
338
|
}, { replyTo: ctx.messageId })
|
|
339
|
-
.catch(() =>
|
|
339
|
+
.catch((sendErr) => getLogger().warn('[clone] finalize failure notice send failed:', sendErr));
|
|
340
340
|
}
|
|
341
341
|
finally {
|
|
342
342
|
this.state = 'idle';
|
|
@@ -33,33 +33,45 @@ export function displayWidth(text) {
|
|
|
33
33
|
total += charWidth(ch);
|
|
34
34
|
return total;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* 显示宽度为 2 的码点区间(闭区间,Map 的键 = 下界、值 = 上界)。
|
|
38
|
+
*
|
|
39
|
+
* 用 Map 而非对象字面量:对象字面量的 `constructor` / `toString` 等键会命中
|
|
40
|
+
* Object.prototype,查表会拿到非预期值;数字键的 Map 无此问题。顺序与原 if 链
|
|
41
|
+
* 一致(所有区间都返回同一宽度,遍历早停即等价),逐字保留:
|
|
42
|
+
* - [0x1100,0x115f] Hangul Jamo;U+2329 / U+232A 单独列为单码点区间;
|
|
43
|
+
* - CJK 统一表意文字 / 假名 / 全角标点 / 兼容表意 / 全角 / Hangul 音节;
|
|
44
|
+
* - 常见 emoji(0x1f300–0x1faff)与 Misc Symbols(0x2600–0x27bf);
|
|
45
|
+
* - Misc Technical 块(U+2300–U+23FF):⏱⏹⏳⏸⏺ 等计时器/播放器符号在
|
|
46
|
+
* 状态行里实际使用,手机端按 emoji 宽度渲染——按 1 计会系统性低估行宽,
|
|
47
|
+
* 窄屏余量比设计值更小。
|
|
48
|
+
*/
|
|
49
|
+
const WIDE_CODE_POINT_RANGES = new Map([
|
|
50
|
+
[0x1100, 0x115f],
|
|
51
|
+
[0x2329, 0x2329],
|
|
52
|
+
[0x232a, 0x232a],
|
|
53
|
+
[0x2e80, 0xa4cf],
|
|
54
|
+
[0xac00, 0xd7a3],
|
|
55
|
+
[0xf900, 0xfaff],
|
|
56
|
+
[0xfe30, 0xfe6f],
|
|
57
|
+
[0xff00, 0xff60],
|
|
58
|
+
[0xffe0, 0xffe6],
|
|
59
|
+
[0x1f300, 0x1faff],
|
|
60
|
+
[0x2600, 0x27bf],
|
|
61
|
+
[0x2300, 0x23ff],
|
|
62
|
+
]);
|
|
63
|
+
/** 码点是否落在任一「宽度 2」区间内(模块级命名函数,供 charWidth 查表)。 */
|
|
64
|
+
function isWideCodePoint(code) {
|
|
65
|
+
for (const [lo, hi] of WIDE_CODE_POINT_RANGES) {
|
|
66
|
+
if (code >= lo && code <= hi)
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
36
71
|
function charWidth(ch) {
|
|
37
72
|
const code = ch.codePointAt(0) ?? 0;
|
|
38
73
|
// CJK 统一表意文字、假名、全角标点、常见 emoji 都按两个半角算。
|
|
39
|
-
|
|
40
|
-
return 2;
|
|
41
|
-
if (code >= 0x2e80 && code <= 0xa4cf)
|
|
42
|
-
return 2;
|
|
43
|
-
if (code >= 0xac00 && code <= 0xd7a3)
|
|
44
|
-
return 2;
|
|
45
|
-
if (code >= 0xf900 && code <= 0xfaff)
|
|
46
|
-
return 2;
|
|
47
|
-
if (code >= 0xfe30 && code <= 0xfe6f)
|
|
48
|
-
return 2;
|
|
49
|
-
if (code >= 0xff00 && code <= 0xff60)
|
|
50
|
-
return 2;
|
|
51
|
-
if (code >= 0xffe0 && code <= 0xffe6)
|
|
52
|
-
return 2;
|
|
53
|
-
if (code >= 0x1f300 && code <= 0x1faff)
|
|
54
|
-
return 2;
|
|
55
|
-
if (code >= 0x2600 && code <= 0x27bf)
|
|
56
|
-
return 2;
|
|
57
|
-
// Misc Technical 块(U+2300–U+23FF):⏱⏹⏳⏸⏺ 等计时器/播放器符号在
|
|
58
|
-
// 状态行里实际使用,手机端按 emoji 宽度渲染——按 1 计会系统性低估行宽,
|
|
59
|
-
// 窄屏余量比设计值更小。
|
|
60
|
-
if (code >= 0x2300 && code <= 0x23ff)
|
|
61
|
-
return 2;
|
|
62
|
-
return 1;
|
|
74
|
+
return isWideCodePoint(code) ? 2 : 1;
|
|
63
75
|
}
|
|
64
76
|
/**
|
|
65
77
|
* 中间省略截断:保留开头与结尾,只在中间打 `…`。
|