lark-remote 0.1.0
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 +52 -0
- package/LICENSE +21 -0
- package/README.en.md +162 -0
- package/README.md +161 -0
- package/dist/binder.d.ts +57 -0
- package/dist/binder.js +105 -0
- package/dist/bridge/index.d.ts +338 -0
- package/dist/bridge/index.js +1281 -0
- package/dist/bridge/queue-manager.d.ts +277 -0
- package/dist/bridge/queue-manager.js +729 -0
- package/dist/card/bash-card-session.d.ts +78 -0
- package/dist/card/bash-card-session.js +174 -0
- package/dist/card/bash-renderer.d.ts +24 -0
- package/dist/card/bash-renderer.js +187 -0
- package/dist/card/card-budget.d.ts +31 -0
- package/dist/card/card-budget.js +280 -0
- package/dist/card/card-session.d.ts +83 -0
- package/dist/card/card-session.js +185 -0
- package/dist/card/card-shared.d.ts +28 -0
- package/dist/card/card-shared.js +99 -0
- package/dist/card/collapsible.d.ts +26 -0
- package/dist/card/collapsible.js +87 -0
- package/dist/card/run-card-session.d.ts +78 -0
- package/dist/card/run-card-session.js +165 -0
- package/dist/card/run-renderer.d.ts +66 -0
- package/dist/card/run-renderer.js +841 -0
- package/dist/card/run-state.d.ts +103 -0
- package/dist/card/run-state.js +313 -0
- package/dist/card/text-truncate.d.ts +56 -0
- package/dist/card/text-truncate.js +184 -0
- package/dist/card/time.d.ts +1 -0
- package/dist/card/time.js +14 -0
- package/dist/card/tool-render.d.ts +25 -0
- package/dist/card/tool-render.js +176 -0
- package/dist/cli.d.ts +28 -0
- package/dist/cli.js +89 -0
- package/dist/common/guards.d.ts +37 -0
- package/dist/common/guards.js +65 -0
- package/dist/config/codex-config.d.ts +113 -0
- package/dist/config/codex-config.js +428 -0
- package/dist/config/dir.d.ts +17 -0
- package/dist/config/dir.js +81 -0
- package/dist/config/index.d.ts +219 -0
- package/dist/config/index.js +388 -0
- package/dist/config/kimi-config.d.ts +33 -0
- package/dist/config/kimi-config.js +111 -0
- package/dist/config/opencode-config.d.ts +20 -0
- package/dist/config/opencode-config.js +124 -0
- package/dist/config/pi-config.d.ts +49 -0
- package/dist/config/pi-config.js +151 -0
- package/dist/config/wizard.d.ts +12 -0
- package/dist/config/wizard.js +84 -0
- package/dist/connector/file-limits.d.ts +7 -0
- package/dist/connector/file-limits.js +8 -0
- package/dist/connector/index.d.ts +86 -0
- package/dist/connector/index.js +340 -0
- package/dist/error-classification.d.ts +15 -0
- package/dist/error-classification.js +24 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +580 -0
- package/dist/instance-lock.d.ts +30 -0
- package/dist/instance-lock.js +184 -0
- package/dist/logger/index.d.ts +57 -0
- package/dist/logger/index.js +178 -0
- package/dist/order/index.d.ts +27 -0
- package/dist/order/index.js +90 -0
- package/dist/persistence/atomic-write.d.ts +10 -0
- package/dist/persistence/atomic-write.js +65 -0
- package/dist/persistence/load-json-file.d.ts +9 -0
- package/dist/persistence/load-json-file.js +23 -0
- package/dist/restart.d.ts +24 -0
- package/dist/restart.js +73 -0
- package/dist/router/card-helpers.d.ts +28 -0
- package/dist/router/card-helpers.js +65 -0
- package/dist/router/config/claude.d.ts +14 -0
- package/dist/router/config/claude.js +72 -0
- package/dist/router/config/codex.d.ts +22 -0
- package/dist/router/config/codex.js +100 -0
- package/dist/router/config/common/render.d.ts +16 -0
- package/dist/router/config/common/render.js +158 -0
- package/dist/router/config/index.d.ts +18 -0
- package/dist/router/config/index.js +38 -0
- package/dist/router/config/kimi.d.ts +21 -0
- package/dist/router/config/kimi.js +63 -0
- package/dist/router/config/opencode.d.ts +12 -0
- package/dist/router/config/opencode.js +44 -0
- package/dist/router/config/pi.d.ts +12 -0
- package/dist/router/config/pi.js +50 -0
- package/dist/router/config/types.d.ts +43 -0
- package/dist/router/config/types.js +8 -0
- package/dist/router/index.d.ts +318 -0
- package/dist/router/index.js +2715 -0
- package/dist/router/order-exec-dispatch.d.ts +30 -0
- package/dist/router/order-exec-dispatch.js +48 -0
- package/dist/router/utils.d.ts +44 -0
- package/dist/router/utils.js +116 -0
- package/dist/runner/bash/index.d.ts +4 -0
- package/dist/runner/bash/index.js +5 -0
- package/dist/runner/bash/runner.d.ts +55 -0
- package/dist/runner/bash/runner.js +213 -0
- package/dist/runner/claude/index.d.ts +4 -0
- package/dist/runner/claude/index.js +5 -0
- package/dist/runner/claude/runner.d.ts +29 -0
- package/dist/runner/claude/runner.js +83 -0
- package/dist/runner/codex/argv.d.ts +33 -0
- package/dist/runner/codex/argv.js +40 -0
- package/dist/runner/codex/index.d.ts +1 -0
- package/dist/runner/codex/index.js +2 -0
- package/dist/runner/codex/jsonl.d.ts +37 -0
- package/dist/runner/codex/jsonl.js +221 -0
- package/dist/runner/codex/runner.d.ts +65 -0
- package/dist/runner/codex/runner.js +124 -0
- package/dist/runner/common/exec-translator.d.ts +73 -0
- package/dist/runner/common/exec-translator.js +105 -0
- package/dist/runner/common/jsonl-stream.d.ts +39 -0
- package/dist/runner/common/jsonl-stream.js +203 -0
- package/dist/runner/common/process-stopper.d.ts +28 -0
- package/dist/runner/common/process-stopper.js +79 -0
- package/dist/runner/common/runner-utils.d.ts +21 -0
- package/dist/runner/common/runner-utils.js +65 -0
- package/dist/runner/common/spawn-heartbeat.d.ts +33 -0
- package/dist/runner/common/spawn-heartbeat.js +51 -0
- package/dist/runner/common/spawning-runner.d.ts +321 -0
- package/dist/runner/common/spawning-runner.js +634 -0
- package/dist/runner/common/usage.d.ts +31 -0
- package/dist/runner/common/usage.js +29 -0
- package/dist/runner/index.d.ts +19 -0
- package/dist/runner/index.js +24 -0
- package/dist/runner/kimi/index.d.ts +1 -0
- package/dist/runner/kimi/index.js +2 -0
- package/dist/runner/kimi/jsonl.d.ts +28 -0
- package/dist/runner/kimi/jsonl.js +143 -0
- package/dist/runner/kimi/runner.d.ts +50 -0
- package/dist/runner/kimi/runner.js +105 -0
- package/dist/runner/opencode/argv.d.ts +32 -0
- package/dist/runner/opencode/argv.js +35 -0
- package/dist/runner/opencode/index.d.ts +1 -0
- package/dist/runner/opencode/index.js +2 -0
- package/dist/runner/opencode/jsonl.d.ts +68 -0
- package/dist/runner/opencode/jsonl.js +295 -0
- package/dist/runner/opencode/runner.d.ts +63 -0
- package/dist/runner/opencode/runner.js +93 -0
- package/dist/runner/pi/index.d.ts +1 -0
- package/dist/runner/pi/index.js +2 -0
- package/dist/runner/pi/jsonl.d.ts +128 -0
- package/dist/runner/pi/jsonl.js +249 -0
- package/dist/runner/pi/runner.d.ts +37 -0
- package/dist/runner/pi/runner.js +76 -0
- package/dist/runner/registry.d.ts +54 -0
- package/dist/runner/registry.js +75 -0
- package/dist/runner/resolve-agent-choices.d.ts +7 -0
- package/dist/runner/resolve-agent-choices.js +76 -0
- package/dist/runner/sync-agent-choices.d.ts +6 -0
- package/dist/runner/sync-agent-choices.js +73 -0
- package/dist/runner/types.d.ts +250 -0
- package/dist/runner/types.js +8 -0
- package/dist/session/claude/index.d.ts +1 -0
- package/dist/session/claude/index.js +2 -0
- package/dist/session/claude/session-reader.d.ts +34 -0
- package/dist/session/claude/session-reader.js +46 -0
- package/dist/session/claude/sessions.d.ts +53 -0
- package/dist/session/claude/sessions.js +528 -0
- package/dist/session/codex/index.d.ts +1 -0
- package/dist/session/codex/index.js +2 -0
- package/dist/session/codex/rollout-reader.d.ts +88 -0
- package/dist/session/codex/rollout-reader.js +473 -0
- package/dist/session/codex/sessions.d.ts +21 -0
- package/dist/session/codex/sessions.js +40 -0
- package/dist/session/common/catch-up.d.ts +12 -0
- package/dist/session/common/catch-up.js +31 -0
- package/dist/session/common/constants.d.ts +11 -0
- package/dist/session/common/constants.js +12 -0
- package/dist/session/common/content-blocks.d.ts +37 -0
- package/dist/session/common/content-blocks.js +68 -0
- package/dist/session/common/jsonl.d.ts +77 -0
- package/dist/session/common/jsonl.js +351 -0
- package/dist/session/common/usage-accumulator.d.ts +58 -0
- package/dist/session/common/usage-accumulator.js +72 -0
- package/dist/session/index.d.ts +7 -0
- package/dist/session/index.js +11 -0
- package/dist/session/kimi/index.d.ts +1 -0
- package/dist/session/kimi/index.js +2 -0
- package/dist/session/kimi/sessions.d.ts +81 -0
- package/dist/session/kimi/sessions.js +527 -0
- package/dist/session/opencode/index.d.ts +1 -0
- package/dist/session/opencode/index.js +2 -0
- package/dist/session/opencode/sessions.d.ts +81 -0
- package/dist/session/opencode/sessions.js +388 -0
- package/dist/session/pi/index.d.ts +1 -0
- package/dist/session/pi/index.js +2 -0
- package/dist/session/pi/sessions.d.ts +27 -0
- package/dist/session/pi/sessions.js +399 -0
- package/dist/session/registry.d.ts +12 -0
- package/dist/session/registry.js +20 -0
- package/dist/session/session-store.d.ts +86 -0
- package/dist/session/session-store.js +450 -0
- package/dist/startup-contact.d.ts +18 -0
- package/dist/startup-contact.js +51 -0
- package/dist/workspace/index.d.ts +19 -0
- package/dist/workspace/index.js +53 -0
- package/package.json +93 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { CommandRouter } from './index.js';
|
|
2
|
+
import type { Bridge } from '../bridge/index.js';
|
|
3
|
+
/** Context shared by all card-action dispatch paths (file-local; callers pass it structurally). */
|
|
4
|
+
interface DispatchContext {
|
|
5
|
+
userId: string;
|
|
6
|
+
chatId: string;
|
|
7
|
+
messageId: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Dispatch an `order.exec` card action as an equivalent queued user message
|
|
11
|
+
* (Plan A): resolve the order text at the enqueue boundary, then enqueue it
|
|
12
|
+
* through `router.handle(orderText)` — the same path a hand-typed message
|
|
13
|
+
* takes. The real text flows as the queue `messagePreview` (→ queue card
|
|
14
|
+
* display + edit default_value) and as the forwarded content; a unique
|
|
15
|
+
* internal key is used as the queue dedup key instead of the Feishu card
|
|
16
|
+
* messageId (one order card is clickable N times, so the card messageId is
|
|
17
|
+
* 1:N with enqueue actions and must not be reused).
|
|
18
|
+
*
|
|
19
|
+
* Extracted from `src/index.ts` so the enqueue-time contract is unit-testable
|
|
20
|
+
* without wiring up the full `main()` graph. Returns a short status string
|
|
21
|
+
* for the caller to log; performs its own error feedback via `bridge.sendResult`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function dispatchOrderExecForQueue(args: {
|
|
24
|
+
router: CommandRouter;
|
|
25
|
+
bridge: Bridge;
|
|
26
|
+
workspace: string;
|
|
27
|
+
orderId: string | undefined;
|
|
28
|
+
ctx: DispatchContext;
|
|
29
|
+
}): Promise<'enqueued' | 'missing-id' | 'not-found'>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatch an `order.exec` card action as an equivalent queued user message
|
|
3
|
+
* (Plan A): resolve the order text at the enqueue boundary, then enqueue it
|
|
4
|
+
* through `router.handle(orderText)` — the same path a hand-typed message
|
|
5
|
+
* takes. The real text flows as the queue `messagePreview` (→ queue card
|
|
6
|
+
* display + edit default_value) and as the forwarded content; a unique
|
|
7
|
+
* internal key is used as the queue dedup key instead of the Feishu card
|
|
8
|
+
* messageId (one order card is clickable N times, so the card messageId is
|
|
9
|
+
* 1:N with enqueue actions and must not be reused).
|
|
10
|
+
*
|
|
11
|
+
* Extracted from `src/index.ts` so the enqueue-time contract is unit-testable
|
|
12
|
+
* without wiring up the full `main()` graph. Returns a short status string
|
|
13
|
+
* for the caller to log; performs its own error feedback via `bridge.sendResult`.
|
|
14
|
+
*/
|
|
15
|
+
export async function dispatchOrderExecForQueue(args) {
|
|
16
|
+
const { router, bridge, workspace, orderId, ctx } = args;
|
|
17
|
+
if (!orderId) {
|
|
18
|
+
await bridge.sendResult({ text: '卡片 payload 缺少必要信息' }, ctx);
|
|
19
|
+
return 'missing-id';
|
|
20
|
+
}
|
|
21
|
+
const resolved = router.resolveOrderExecForQueue(orderId);
|
|
22
|
+
if (!resolved) {
|
|
23
|
+
await bridge.sendResult({ text: '指令不存在或已被删除' }, ctx);
|
|
24
|
+
return 'not-found';
|
|
25
|
+
}
|
|
26
|
+
// Step4/D4: 入队时刻(T0)快照 agent+session,随任务闭包带到 T1 执行时刻,
|
|
27
|
+
// 避免排队期间 /new、/config 改写 live 状态导致语义漂移。唯一捕获点
|
|
28
|
+
// Bridge.currentBinding。
|
|
29
|
+
const binding = bridge.currentBinding(ctx.userId);
|
|
30
|
+
bridge.enqueue(workspace, async () => {
|
|
31
|
+
await router.handle(resolved.orderText, ctx, { cwdOverride: workspace, binding });
|
|
32
|
+
}, {
|
|
33
|
+
taskMeta: {
|
|
34
|
+
userId: ctx.userId,
|
|
35
|
+
chatId: ctx.chatId,
|
|
36
|
+
messageId: resolved.internalKey,
|
|
37
|
+
messagePreview: resolved.orderText.slice(0, 3000),
|
|
38
|
+
// The internal key is NOT a valid Feishu message id, so the queue
|
|
39
|
+
// status card must reply to the real Feishu card message id instead.
|
|
40
|
+
feishuReplyTo: ctx.messageId,
|
|
41
|
+
// D3/Step4:binding 随 taskMeta 存进 QueuedTask,供替换闭包
|
|
42
|
+
// (queue.edit/queue.immediate)复用,不重新快照。
|
|
43
|
+
binding,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
return 'enqueued';
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=order-exec-dispatch.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure utility functions extracted from router/index.ts.
|
|
3
|
+
* Path-safety helpers, display formatting, and validation — no Router state.
|
|
4
|
+
*/
|
|
5
|
+
import type { ActiveRunSnapshot } from '../bridge/index.js';
|
|
6
|
+
export interface SessionDisplayUsage {
|
|
7
|
+
contextLength?: number;
|
|
8
|
+
compactCount?: number;
|
|
9
|
+
cacheReadTokens?: number;
|
|
10
|
+
cacheCreationTokens?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function activeRunUsage(activeRun: ActiveRunSnapshot | undefined): SessionDisplayUsage | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Format usage stats (context length, compact count, cache tokens) into a display string.
|
|
15
|
+
* Each stat on its own line with blank lines between sections.
|
|
16
|
+
* @param usage - Usage stats object
|
|
17
|
+
* @param options - Optional display options (showResult adds result line)
|
|
18
|
+
*/
|
|
19
|
+
export declare function formatUsageStats(usage?: {
|
|
20
|
+
contextLength?: number;
|
|
21
|
+
compactCount?: number;
|
|
22
|
+
cacheReadTokens?: number;
|
|
23
|
+
cacheCreationTokens?: number;
|
|
24
|
+
/** Agent-declared total; when present the display uses max(total, sum of parts). */
|
|
25
|
+
totalTokens?: number;
|
|
26
|
+
/** Real input tokens from the agent's result event. When present, used
|
|
27
|
+
* directly for the "Input token" line instead of contextLength+cache. */
|
|
28
|
+
inputTokens?: number;
|
|
29
|
+
/** Real output tokens from the agent's result event. When present, used
|
|
30
|
+
* directly instead of the 10% estimate. */
|
|
31
|
+
outputTokens?: number;
|
|
32
|
+
/** Session-cumulative total/input/output (all runs), from session jsonl. When
|
|
33
|
+
* present, appended as "· 累计 X" on the Total/Input/Output lines. */
|
|
34
|
+
cumulativeTotalTokens?: number;
|
|
35
|
+
cumulativeInputTokens?: number;
|
|
36
|
+
cumulativeOutputTokens?: number;
|
|
37
|
+
/** Session-cumulative cache read tokens (all runs). */
|
|
38
|
+
cumulativeCacheReadTokens?: number;
|
|
39
|
+
/** Session-cumulative cache creation tokens (all runs). */
|
|
40
|
+
cumulativeCacheCreationTokens?: number;
|
|
41
|
+
}, options?: {
|
|
42
|
+
showResult?: boolean;
|
|
43
|
+
result?: string;
|
|
44
|
+
}): string;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure utility functions extracted from router/index.ts.
|
|
3
|
+
* Path-safety helpers, display formatting, and validation — no Router state.
|
|
4
|
+
*/
|
|
5
|
+
export function activeRunUsage(activeRun) {
|
|
6
|
+
if (!activeRun || activeRun.contextLength === undefined)
|
|
7
|
+
return undefined;
|
|
8
|
+
return {
|
|
9
|
+
contextLength: activeRun.contextLength,
|
|
10
|
+
compactCount: activeRun.compactCount,
|
|
11
|
+
cacheReadTokens: activeRun.cacheReadTokens,
|
|
12
|
+
cacheCreationTokens: activeRun.cacheCreationTokens,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
// --- Extracted helpers for refactoring ---
|
|
16
|
+
/**
|
|
17
|
+
* Format number to K unit string, rounding to the nearest integer K
|
|
18
|
+
* (e.g., 120000 → 120K, 1500 → 2K, 1950 → 2K). Values < 1000 are returned as-is.
|
|
19
|
+
*/
|
|
20
|
+
function formatTokenK(n) {
|
|
21
|
+
if (n >= 1000) {
|
|
22
|
+
const k = Math.round(n / 1000);
|
|
23
|
+
return `${k}K`;
|
|
24
|
+
}
|
|
25
|
+
return n.toString();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Format usage stats (context length, compact count, cache tokens) into a display string.
|
|
29
|
+
* Each stat on its own line with blank lines between sections.
|
|
30
|
+
* @param usage - Usage stats object
|
|
31
|
+
* @param options - Optional display options (showResult adds result line)
|
|
32
|
+
*/
|
|
33
|
+
export function formatUsageStats(usage, options) {
|
|
34
|
+
const lines = [];
|
|
35
|
+
// 1. Success 状态(通用格式,无具体结果值)
|
|
36
|
+
lines.push('✅ 已完成');
|
|
37
|
+
// 1.5. 可选的结果行(showResult 为 true 时显示)
|
|
38
|
+
if (options?.showResult && options?.result) {
|
|
39
|
+
lines.push(`结果 - ${options.result}`);
|
|
40
|
+
}
|
|
41
|
+
// 2. Context 长度
|
|
42
|
+
if (usage?.contextLength !== undefined) {
|
|
43
|
+
lines.push(`Context - ${formatTokenK(usage.contextLength)}`);
|
|
44
|
+
}
|
|
45
|
+
// 3. Compact 次数
|
|
46
|
+
if (usage?.compactCount) {
|
|
47
|
+
lines.push(`Compact - ${usage.compactCount}次`);
|
|
48
|
+
}
|
|
49
|
+
// 4-7. Token 统计
|
|
50
|
+
const cacheRead = usage?.cacheReadTokens ?? 0;
|
|
51
|
+
const cacheCreation = usage?.cacheCreationTokens ?? 0;
|
|
52
|
+
const realInput = usage?.inputTokens;
|
|
53
|
+
const realOutput = usage?.outputTokens;
|
|
54
|
+
// Cumulative suffix (session-wide totals), appended to Input/Output lines.
|
|
55
|
+
const cumInputSuffix = usage?.cumulativeInputTokens !== undefined
|
|
56
|
+
? ` · 累计 ${formatTokenK(usage.cumulativeInputTokens)}`
|
|
57
|
+
: '';
|
|
58
|
+
const cumOutputSuffix = usage?.cumulativeOutputTokens !== undefined
|
|
59
|
+
? ` · 累计 ${formatTokenK(usage.cumulativeOutputTokens)}`
|
|
60
|
+
: '';
|
|
61
|
+
const cumTotalSuffix = usage?.cumulativeTotalTokens !== undefined
|
|
62
|
+
? ` · 累计 ${formatTokenK(usage.cumulativeTotalTokens)}`
|
|
63
|
+
: '';
|
|
64
|
+
// 计算累计 cache 命中率
|
|
65
|
+
const cumInput = usage?.cumulativeInputTokens;
|
|
66
|
+
const cumCacheRead = usage?.cumulativeCacheReadTokens;
|
|
67
|
+
const cumCachePercent = cumInput !== undefined && cumCacheRead !== undefined && cumInput + cumCacheRead > 0
|
|
68
|
+
? Math.round((cumCacheRead / (cumInput + cumCacheRead)) * 100)
|
|
69
|
+
: null;
|
|
70
|
+
// 构建累计 suffix
|
|
71
|
+
const cumCacheReadSuffix = cumCacheRead !== undefined
|
|
72
|
+
? ` · 累计 ${formatTokenK(cumCacheRead)}` +
|
|
73
|
+
(cumCachePercent !== null ? ` (${cumCachePercent}%)` : '')
|
|
74
|
+
: '';
|
|
75
|
+
const cumCacheCreateSuffix = usage?.cumulativeCacheCreationTokens !== undefined
|
|
76
|
+
? ` · 累计 ${formatTokenK(usage.cumulativeCacheCreationTokens)}`
|
|
77
|
+
: '';
|
|
78
|
+
if (realInput !== undefined && realOutput !== undefined) {
|
|
79
|
+
// Unified ccusage-aligned formula:
|
|
80
|
+
// total = max(total_tokens, input + output + cacheRead + cacheCreation)
|
|
81
|
+
// cache% = cacheRead / (input + cacheRead)
|
|
82
|
+
// cacheRead/cacheCreation are never double-counted; total_tokens folds in any
|
|
83
|
+
// extra (e.g. opencode reasoning, which is separate from output) via max().
|
|
84
|
+
const promptTokens = realInput + cacheRead;
|
|
85
|
+
const cachePercent = promptTokens > 0 ? Math.round((cacheRead / promptTokens) * 100) : 0;
|
|
86
|
+
const sumParts = realInput + realOutput + cacheRead + cacheCreation;
|
|
87
|
+
const totalTokens = usage?.totalTokens != null ? Math.max(usage.totalTokens, sumParts) : sumParts;
|
|
88
|
+
lines.push(`Input token - ${formatTokenK(realInput)}${cumInputSuffix}`);
|
|
89
|
+
lines.push(`Output token - ${formatTokenK(realOutput)}${cumOutputSuffix}`);
|
|
90
|
+
if (cacheCreation > 0) {
|
|
91
|
+
lines.push(`Cache create - ${formatTokenK(cacheCreation)}${cumCacheCreateSuffix}`);
|
|
92
|
+
}
|
|
93
|
+
lines.push(`Cached token - ${formatTokenK(cacheRead)} (${cachePercent}%)${cumCacheReadSuffix}`);
|
|
94
|
+
lines.push(`Total token - ${formatTokenK(totalTokens)}${cumTotalSuffix}`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Estimate path: when real input/output are absent,
|
|
98
|
+
// fall back to the 10% estimate based on contextLength.
|
|
99
|
+
const inputRef = usage?.contextLength ?? cacheRead;
|
|
100
|
+
if (inputRef > 0 || cacheRead > 0 || cacheCreation > 0) {
|
|
101
|
+
const totalInput = inputRef + cacheRead;
|
|
102
|
+
const outputEst = inputRef > 0 ? Math.round(inputRef * 0.1) : 0;
|
|
103
|
+
const cachePercent = totalInput > 0 ? Math.round((cacheRead / totalInput) * 100) : 0;
|
|
104
|
+
const totalTokens = totalInput + outputEst + cacheCreation;
|
|
105
|
+
lines.push(`Input token - ${formatTokenK(totalInput)}${cumInputSuffix}`);
|
|
106
|
+
lines.push(`Output token - ${formatTokenK(outputEst)}${cumOutputSuffix}`);
|
|
107
|
+
if (cacheCreation > 0) {
|
|
108
|
+
lines.push(`Cache create - ${formatTokenK(cacheCreation)}${cumCacheCreateSuffix}`);
|
|
109
|
+
}
|
|
110
|
+
lines.push(`Cached token - ${formatTokenK(cacheRead)} (${cachePercent}%)${cumCacheReadSuffix}`);
|
|
111
|
+
lines.push(`Total token - ${formatTokenK(totalTokens)}${cumTotalSuffix}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return lines.join('\n');
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
interface BashOutputEvent {
|
|
2
|
+
type: 'stdout' | 'stderr' | 'exit';
|
|
3
|
+
content: string;
|
|
4
|
+
exitCode?: number;
|
|
5
|
+
}
|
|
6
|
+
export interface BashRunner {
|
|
7
|
+
run(command: string, opts: {
|
|
8
|
+
cwd: string;
|
|
9
|
+
}): AsyncGenerator<BashOutputEvent>;
|
|
10
|
+
stop(opts?: {
|
|
11
|
+
immediate?: boolean;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
get isRunning(): boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Lightweight bash executor for `!` commands.
|
|
17
|
+
* Spawns `bash -c "<command>"` and yields stdout/stderr/exit as events.
|
|
18
|
+
*
|
|
19
|
+
* Output streaming is event-driven: the run loop races the next stdout/stderr
|
|
20
|
+
* `data` event against process `exit`/`error`, so it wakes instantly on either
|
|
21
|
+
* (no `setTimeout` busy-wait polling). This drops exit latency from ~100ms
|
|
22
|
+
* (the previous poll granularity) to ~0 and avoids periodic CPU wakeups while
|
|
23
|
+
* a long command produces no output. Each stream has a single `data` handler
|
|
24
|
+
* that logs, enqueues, and wakes the loop.
|
|
25
|
+
*/
|
|
26
|
+
export declare class BashProcessRunner implements BashRunner {
|
|
27
|
+
private currentProcess;
|
|
28
|
+
/**
|
|
29
|
+
* 背压高水位/低水位(P1-4 层④,参照 jsonl-stream.ts 的 pauseThreshold/
|
|
30
|
+
* resumeThreshold)。stdout/stderr 队列超过高水位时 pause 流(内存有界),
|
|
31
|
+
* 消费者 drain 到低水位后 resume。构造可注入以便测试用小水位。
|
|
32
|
+
*/
|
|
33
|
+
private readonly queueHighWater;
|
|
34
|
+
private readonly queueLowWater;
|
|
35
|
+
private stopper;
|
|
36
|
+
constructor(opts?: {
|
|
37
|
+
stopGraceMs?: number;
|
|
38
|
+
queueHighWater?: number;
|
|
39
|
+
queueLowWater?: number;
|
|
40
|
+
});
|
|
41
|
+
get isRunning(): boolean;
|
|
42
|
+
run(command: string, opts: {
|
|
43
|
+
cwd: string;
|
|
44
|
+
}): AsyncGenerator<BashOutputEvent>;
|
|
45
|
+
/**
|
|
46
|
+
* P1-22: called by the process-level exit dispatcher on SIGINT/SIGTERM/exit.
|
|
47
|
+
* Kills the whole process group (leader + background children) via
|
|
48
|
+
* ProcessStopper, matching agent-runner cleanupOnExit semantics.
|
|
49
|
+
*/
|
|
50
|
+
cleanupOnExit(): void;
|
|
51
|
+
stop(opts?: {
|
|
52
|
+
immediate?: boolean;
|
|
53
|
+
}): Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { getLogger } from '../../logger/index.js';
|
|
3
|
+
import { ProcessStopper } from '../common/process-stopper.js';
|
|
4
|
+
import { registerExitCleanup, unregisterExitCleanup } from '../common/spawning-runner.js';
|
|
5
|
+
/**
|
|
6
|
+
* Lightweight bash executor for `!` commands.
|
|
7
|
+
* Spawns `bash -c "<command>"` and yields stdout/stderr/exit as events.
|
|
8
|
+
*
|
|
9
|
+
* Output streaming is event-driven: the run loop races the next stdout/stderr
|
|
10
|
+
* `data` event against process `exit`/`error`, so it wakes instantly on either
|
|
11
|
+
* (no `setTimeout` busy-wait polling). This drops exit latency from ~100ms
|
|
12
|
+
* (the previous poll granularity) to ~0 and avoids periodic CPU wakeups while
|
|
13
|
+
* a long command produces no output. Each stream has a single `data` handler
|
|
14
|
+
* that logs, enqueues, and wakes the loop.
|
|
15
|
+
*/
|
|
16
|
+
export class BashProcessRunner {
|
|
17
|
+
currentProcess = null;
|
|
18
|
+
/**
|
|
19
|
+
* 背压高水位/低水位(P1-4 层④,参照 jsonl-stream.ts 的 pauseThreshold/
|
|
20
|
+
* resumeThreshold)。stdout/stderr 队列超过高水位时 pause 流(内存有界),
|
|
21
|
+
* 消费者 drain 到低水位后 resume。构造可注入以便测试用小水位。
|
|
22
|
+
*/
|
|
23
|
+
queueHighWater;
|
|
24
|
+
queueLowWater;
|
|
25
|
+
stopper;
|
|
26
|
+
constructor(opts) {
|
|
27
|
+
const stopGraceMs = opts?.stopGraceMs ?? 1000;
|
|
28
|
+
this.queueHighWater = opts?.queueHighWater ?? 32;
|
|
29
|
+
this.queueLowWater = opts?.queueLowWater ?? 16;
|
|
30
|
+
if (this.queueLowWater > this.queueHighWater) {
|
|
31
|
+
this.queueLowWater = this.queueHighWater;
|
|
32
|
+
}
|
|
33
|
+
this.stopper = new ProcessStopper({ graceMs: stopGraceMs });
|
|
34
|
+
}
|
|
35
|
+
get isRunning() {
|
|
36
|
+
return (this.currentProcess !== null &&
|
|
37
|
+
this.currentProcess.exitCode === null &&
|
|
38
|
+
this.currentProcess.signalCode === null);
|
|
39
|
+
}
|
|
40
|
+
async *run(command, opts) {
|
|
41
|
+
if (this.isRunning) {
|
|
42
|
+
getLogger().warn('[bash-runner] run() called while already running');
|
|
43
|
+
throw new Error('bash process already running');
|
|
44
|
+
}
|
|
45
|
+
getLogger().debug(`[bash-runner] spawning command="${command}" cwd=${opts.cwd}`);
|
|
46
|
+
const proc = spawn('bash', ['-c', command], {
|
|
47
|
+
cwd: opts.cwd,
|
|
48
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
49
|
+
// Spawn in new process group so we can kill the entire group
|
|
50
|
+
detached: true,
|
|
51
|
+
});
|
|
52
|
+
this.currentProcess = proc;
|
|
53
|
+
getLogger().info(`[bash-runner] spawn pid=${proc.pid} command="${command.slice(0, 50)}..." cwd=${opts.cwd}`);
|
|
54
|
+
// P1-22: register with the process-level exit dispatcher while the process
|
|
55
|
+
// is alive — bridge exit/restart (SIGINT/SIGTERM/exit) then kills the whole
|
|
56
|
+
// group via cleanupOnExit instead of orphaning `!` processes.
|
|
57
|
+
registerExitCleanup(this);
|
|
58
|
+
// Single handler per stream: log + enqueue + wake the run loop. The
|
|
59
|
+
// previous implementation registered two `data` listeners per stream (one
|
|
60
|
+
// for logging, one for queueing) -- both fired on the same chunk.
|
|
61
|
+
const stdoutQueue = [];
|
|
62
|
+
const stderrQueue = [];
|
|
63
|
+
// 背压状态(P1-4 层④):队列超高水位 → pause 对应流;drain 到低水位 → resume。
|
|
64
|
+
let stdoutPaused = false;
|
|
65
|
+
let stderrPaused = false;
|
|
66
|
+
const maybePauseStdout = () => {
|
|
67
|
+
if (!stdoutPaused && stdoutQueue.length > this.queueHighWater) {
|
|
68
|
+
stdoutPaused = true;
|
|
69
|
+
proc.stdout.pause();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const maybeResumeStdout = () => {
|
|
73
|
+
if (stdoutPaused && stdoutQueue.length <= this.queueLowWater) {
|
|
74
|
+
stdoutPaused = false;
|
|
75
|
+
proc.stdout.resume();
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const maybePauseStderr = () => {
|
|
79
|
+
if (!stderrPaused && stderrQueue.length > this.queueHighWater) {
|
|
80
|
+
stderrPaused = true;
|
|
81
|
+
proc.stderr.pause();
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const maybeResumeStderr = () => {
|
|
85
|
+
if (stderrPaused && stderrQueue.length <= this.queueLowWater) {
|
|
86
|
+
stderrPaused = false;
|
|
87
|
+
proc.stderr.resume();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
// Resolved by a `data` event to signal "more output may be available,
|
|
91
|
+
// re-check the queues". Held as a field so either stream's handler can
|
|
92
|
+
// resolve the single in-flight waiter. Cleared on resolve and on loop exit.
|
|
93
|
+
let dataWaiter = null;
|
|
94
|
+
const notifyData = () => {
|
|
95
|
+
if (dataWaiter) {
|
|
96
|
+
const resolve = dataWaiter;
|
|
97
|
+
dataWaiter = null;
|
|
98
|
+
resolve();
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
proc.stdout?.on('data', (chunk) => {
|
|
102
|
+
const text = chunk.toString('utf-8');
|
|
103
|
+
getLogger().debug(`[bash-runner] stdout: ${text.slice(0, 100)}`);
|
|
104
|
+
stdoutQueue.push(text);
|
|
105
|
+
maybePauseStdout();
|
|
106
|
+
notifyData();
|
|
107
|
+
});
|
|
108
|
+
proc.stderr?.on('data', (chunk) => {
|
|
109
|
+
const text = chunk.toString('utf-8');
|
|
110
|
+
getLogger().debug(`[bash-runner] stderr: ${text.slice(0, 100)}`);
|
|
111
|
+
stderrQueue.push(text);
|
|
112
|
+
maybePauseStderr();
|
|
113
|
+
notifyData();
|
|
114
|
+
});
|
|
115
|
+
// Use the `exit` event (not `close`): `close` waits for ALL child processes
|
|
116
|
+
// including background daemons (`nohup &`), which can take forever. `exit`
|
|
117
|
+
// fires as soon as bash itself exits. A SIGKILL from /stop sets `signal`
|
|
118
|
+
// while `code` stays null, so callers distinguish signal-kill from exit.
|
|
119
|
+
// A spawn failure (e.g. bash missing) emits `error` and never `exit`;
|
|
120
|
+
// resolving completion with code=1 here lets the loop terminate instead of
|
|
121
|
+
// spinning forever on a permanently-null exitCode check (the old poll loop
|
|
122
|
+
// had the same latent hang on a missing bash binary).
|
|
123
|
+
let completionResult = null;
|
|
124
|
+
const completion = new Promise((resolve) => {
|
|
125
|
+
proc.once('exit', (code, signal) => {
|
|
126
|
+
completionResult = { code, signal };
|
|
127
|
+
resolve();
|
|
128
|
+
});
|
|
129
|
+
proc.once('error', (err) => {
|
|
130
|
+
getLogger().error('[bash-runner] process error:', err.message);
|
|
131
|
+
completionResult = { code: 1, signal: null };
|
|
132
|
+
resolve();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
try {
|
|
136
|
+
while (completionResult === null) {
|
|
137
|
+
// Drain queued chunks before waiting, so output is streamed
|
|
138
|
+
// incrementally rather than batched at exit.
|
|
139
|
+
while (stdoutQueue.length > 0) {
|
|
140
|
+
yield { type: 'stdout', content: stdoutQueue.shift() };
|
|
141
|
+
maybeResumeStdout();
|
|
142
|
+
}
|
|
143
|
+
while (stderrQueue.length > 0) {
|
|
144
|
+
yield { type: 'stderr', content: stderrQueue.shift() };
|
|
145
|
+
maybeResumeStderr();
|
|
146
|
+
}
|
|
147
|
+
if (completionResult === null) {
|
|
148
|
+
// Wait for the next chunk OR process exit/error -- no fixed-interval
|
|
149
|
+
// polling. Racing `completion` guarantees the loop wakes on exit even
|
|
150
|
+
// when the command produces no output (e.g. `true`, `nohup &`), which
|
|
151
|
+
// the previous setTimeout(100) poll only caught after a full tick.
|
|
152
|
+
const nextData = new Promise((resolve) => {
|
|
153
|
+
dataWaiter = resolve;
|
|
154
|
+
});
|
|
155
|
+
await Promise.race([nextData, completion]);
|
|
156
|
+
dataWaiter = null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Drain any chunks that arrived between the last drain and exit. (Data
|
|
160
|
+
// emitted after the exit yield is not delivered -- matching the prior
|
|
161
|
+
// contract; the bridge accumulates output/stderr from pre-exit events.)
|
|
162
|
+
while (stdoutQueue.length > 0) {
|
|
163
|
+
yield { type: 'stdout', content: stdoutQueue.shift() };
|
|
164
|
+
maybeResumeStdout();
|
|
165
|
+
}
|
|
166
|
+
while (stderrQueue.length > 0) {
|
|
167
|
+
yield { type: 'stderr', content: stderrQueue.shift() };
|
|
168
|
+
maybeResumeStderr();
|
|
169
|
+
}
|
|
170
|
+
const { code, signal } = completionResult;
|
|
171
|
+
getLogger().info(`[bash-runner] process exited with code=${code} signal=${signal}`);
|
|
172
|
+
// A SIGKILL'd process (e.g. from /stop) has exitCode=null and a signal
|
|
173
|
+
// set. Reporting `code ?? 0` would falsely claim success; use -1 to
|
|
174
|
+
// signal abnormal termination by signal.
|
|
175
|
+
yield { type: 'exit', content: '', exitCode: code ?? (signal ? -1 : 0) };
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
dataWaiter = null;
|
|
179
|
+
this.currentProcess = null;
|
|
180
|
+
// Run finished — remove from the exit dispatcher so dead runners are not
|
|
181
|
+
// retained (mirror agent-runner slot eviction; keeps the Set bounded to
|
|
182
|
+
// active bash runs only).
|
|
183
|
+
unregisterExitCleanup(this);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* P1-22: called by the process-level exit dispatcher on SIGINT/SIGTERM/exit.
|
|
188
|
+
* Kills the whole process group (leader + background children) via
|
|
189
|
+
* ProcessStopper, matching agent-runner cleanupOnExit semantics.
|
|
190
|
+
*/
|
|
191
|
+
cleanupOnExit() {
|
|
192
|
+
const proc = this.currentProcess;
|
|
193
|
+
if (!proc || proc.exitCode !== null || proc.signalCode !== null)
|
|
194
|
+
return;
|
|
195
|
+
try {
|
|
196
|
+
void this.stopper.stop(proc, { immediate: true });
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
// fire-and-forget: nothing else to do at process exit
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async stop(opts) {
|
|
203
|
+
const proc = this.currentProcess;
|
|
204
|
+
if (!proc || proc.exitCode !== null || proc.signalCode !== null) {
|
|
205
|
+
this.currentProcess = null;
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
// Delegate to ProcessStopper for unified stop semantics
|
|
209
|
+
await this.stopper.stop(proc, { immediate: opts?.immediate });
|
|
210
|
+
this.currentProcess = null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type AgentEvent, type SpawnOptions, type AgentSessionReader, type AgentRunner as IAgentRunner, type AgentStatusInfo } from '../types.js';
|
|
2
|
+
import { SpawningRunner } from '../common/spawning-runner.js';
|
|
3
|
+
/**
|
|
4
|
+
* Claude Agent Runner
|
|
5
|
+
*
|
|
6
|
+
* 使用 Claude Code CLI (claude) 作为后端 AI 代理
|
|
7
|
+
*/
|
|
8
|
+
export declare class ClaudeRunner extends SpawningRunner implements IAgentRunner {
|
|
9
|
+
readonly kind: "claude";
|
|
10
|
+
readonly sessionReader: AgentSessionReader;
|
|
11
|
+
private defaultModel;
|
|
12
|
+
private defaultEffort;
|
|
13
|
+
private defaultSettings?;
|
|
14
|
+
constructor(opts: {
|
|
15
|
+
binary?: string;
|
|
16
|
+
model?: string;
|
|
17
|
+
effort?: string;
|
|
18
|
+
settings?: string;
|
|
19
|
+
stopGraceMs?: number;
|
|
20
|
+
pidDir?: string;
|
|
21
|
+
workspace: string;
|
|
22
|
+
spawnHeartbeatMs?: number;
|
|
23
|
+
sessionReader?: AgentSessionReader;
|
|
24
|
+
});
|
|
25
|
+
get pid(): number | undefined;
|
|
26
|
+
getStatusInfo(): AgentStatusInfo;
|
|
27
|
+
protected buildArgv(opts: SpawnOptions): string[];
|
|
28
|
+
protected translate(rawEvent: unknown, _ctx: unknown): AgentEvent | AgentEvent[] | null;
|
|
29
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Runner 实现
|
|
3
|
+
*
|
|
4
|
+
* 从 runner/index.ts 提取的 ClaudeRunner 类
|
|
5
|
+
*/
|
|
6
|
+
import { ClaudeSessionReader } from '../../session/claude/index.js';
|
|
7
|
+
import { SpawningRunner } from '../common/spawning-runner.js';
|
|
8
|
+
import { MODEL_ID_TO_ALIAS } from '../../config/index.js';
|
|
9
|
+
/**
|
|
10
|
+
* Claude Agent Runner
|
|
11
|
+
*
|
|
12
|
+
* 使用 Claude Code CLI (claude) 作为后端 AI 代理
|
|
13
|
+
*/
|
|
14
|
+
export class ClaudeRunner extends SpawningRunner {
|
|
15
|
+
kind = 'claude';
|
|
16
|
+
sessionReader;
|
|
17
|
+
defaultModel;
|
|
18
|
+
defaultEffort;
|
|
19
|
+
defaultSettings;
|
|
20
|
+
constructor(opts) {
|
|
21
|
+
super({
|
|
22
|
+
binary: opts.binary ?? 'claude',
|
|
23
|
+
pidDir: opts.pidDir,
|
|
24
|
+
workspace: opts.workspace,
|
|
25
|
+
stopGraceMs: opts.stopGraceMs,
|
|
26
|
+
spawnHeartbeatMs: opts.spawnHeartbeatMs,
|
|
27
|
+
pidFilePrefix: 'claude',
|
|
28
|
+
logTag: 'claude-runner',
|
|
29
|
+
});
|
|
30
|
+
this.defaultModel = opts.model ?? 'claude-opus-4-8';
|
|
31
|
+
this.defaultEffort = opts.effort ?? 'medium';
|
|
32
|
+
this.defaultSettings = opts.settings;
|
|
33
|
+
this.sessionReader = opts.sessionReader ?? new ClaudeSessionReader();
|
|
34
|
+
}
|
|
35
|
+
get pid() {
|
|
36
|
+
return this.currentProcess?.pid;
|
|
37
|
+
}
|
|
38
|
+
getStatusInfo() {
|
|
39
|
+
const alias = MODEL_ID_TO_ALIAS[this.defaultModel] ?? this.defaultModel;
|
|
40
|
+
const isHaiku = this.defaultModel.includes('haiku');
|
|
41
|
+
return {
|
|
42
|
+
kind: 'claude',
|
|
43
|
+
model: alias,
|
|
44
|
+
reasoning: isHaiku ? 'off' : this.defaultEffort,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
buildArgv(opts) {
|
|
48
|
+
const args = [
|
|
49
|
+
'-p',
|
|
50
|
+
this.currentMessage,
|
|
51
|
+
'--output-format',
|
|
52
|
+
'stream-json',
|
|
53
|
+
'--verbose',
|
|
54
|
+
// Hardcoded: Feishu chat doesn't support interactive approval.
|
|
55
|
+
'--permission-mode',
|
|
56
|
+
'bypassPermissions',
|
|
57
|
+
];
|
|
58
|
+
if (opts.sessionId) {
|
|
59
|
+
args.push('--resume', opts.sessionId);
|
|
60
|
+
}
|
|
61
|
+
if (opts.model) {
|
|
62
|
+
args.push('--model', opts.model);
|
|
63
|
+
}
|
|
64
|
+
const effort = opts.effort ?? this.defaultEffort;
|
|
65
|
+
if (effort) {
|
|
66
|
+
args.push('--effort', effort);
|
|
67
|
+
}
|
|
68
|
+
const settings = opts.settings ?? this.defaultSettings;
|
|
69
|
+
if (settings) {
|
|
70
|
+
args.push('--settings', settings);
|
|
71
|
+
}
|
|
72
|
+
return args;
|
|
73
|
+
}
|
|
74
|
+
translate(rawEvent, _ctx) {
|
|
75
|
+
const event = rawEvent;
|
|
76
|
+
// 统一方案:所有 runner 自己生成 timestamp
|
|
77
|
+
if (!event.timestamp) {
|
|
78
|
+
event.timestamp = new Date().toISOString();
|
|
79
|
+
}
|
|
80
|
+
return event;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=runner.js.map
|