lark-remote 0.4.4 → 0.5.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 +28 -0
- package/dist/binder.d.ts +11 -10
- package/dist/binder.js +17 -16
- package/dist/bridge/approval-coordinator.d.ts +1 -1
- package/dist/bridge/approval-coordinator.js +3 -3
- package/dist/bridge/inbound-media.d.ts +1 -27
- package/dist/bridge/inbound-media.js +23 -21
- package/dist/bridge/index.d.ts +57 -57
- package/dist/bridge/index.js +136 -129
- package/dist/bridge/queue-manager.d.ts +45 -38
- package/dist/bridge/queue-manager.js +136 -136
- package/dist/card/approval-render.d.ts +1 -1
- package/dist/card/approval-render.js +6 -6
- package/dist/card/bash-card-session.d.ts +3 -3
- package/dist/card/bash-card-session.js +3 -3
- package/dist/card/bash-renderer.js +5 -5
- package/dist/card/card-session.d.ts +4 -4
- package/dist/card/card-session.js +6 -6
- package/dist/card/card-shared.d.ts +1 -1
- package/dist/card/card-shared.js +2 -2
- package/dist/card/run-card-session.js +2 -2
- package/dist/card/run-renderer.d.ts +1 -1
- package/dist/card/run-renderer.js +22 -22
- package/dist/card/run-state.d.ts +2 -2
- package/dist/card/run-state.js +3 -3
- package/dist/card/tool-render.d.ts +1 -1
- package/dist/card/tool-render.js +6 -6
- package/dist/cli.js +5 -6
- package/dist/clone.d.ts +6 -9
- package/dist/clone.js +12 -8
- package/dist/common/clamp.d.ts +2 -0
- package/dist/common/clamp.js +5 -0
- package/dist/common/fs.d.ts +4 -3
- package/dist/common/fs.js +5 -4
- package/dist/common/truncate.d.ts +1 -1
- package/dist/common/truncate.js +2 -2
- package/dist/common/ttl-cache.d.ts +2 -2
- package/dist/common/ttl-cache.js +2 -2
- package/dist/config/codex-config.d.ts +9 -9
- package/dist/config/codex-config.js +18 -18
- package/dist/config/dir.d.ts +4 -5
- package/dist/config/dir.js +12 -10
- package/dist/config/index.d.ts +3 -3
- package/dist/config/index.js +6 -6
- package/dist/config/opencode-config.js +3 -3
- package/dist/config/wizard.js +2 -2
- package/dist/connector/index.d.ts +5 -7
- package/dist/connector/index.js +29 -16
- package/dist/inbound/event.d.ts +1 -1
- package/dist/inbound/event.js +1 -1
- package/dist/inbound/placeholder.d.ts +2 -2
- package/dist/inbound/placeholder.js +2 -2
- package/dist/inbound/receipt.d.ts +1 -3
- package/dist/inbound/receipt.js +2 -2
- package/dist/inbound/turn-assembler.d.ts +1 -3
- package/dist/inbound/turn-assembler.js +2 -2
- package/dist/index.js +38 -30
- package/dist/instance-lock.js +8 -7
- package/dist/logger/index.d.ts +1 -1
- package/dist/logger/index.js +4 -4
- package/dist/persistence/atomic-write.d.ts +1 -1
- package/dist/persistence/atomic-write.js +9 -5
- package/dist/platform/agent-stopper.d.ts +2 -2
- package/dist/platform/identity.d.ts +7 -1
- package/dist/platform/identity.js +2 -2
- package/dist/platform/path.d.ts +6 -0
- package/dist/platform/path.js +14 -8
- package/dist/platform/select.d.ts +1 -1
- package/dist/platform/select.js +1 -1
- package/dist/platform/shell.d.ts +2 -3
- package/dist/platform/spawn.d.ts +2 -2
- package/dist/platform/spawn.js +2 -2
- package/dist/platform/terminator-posix.js +6 -6
- package/dist/platform/terminator-win32.js +3 -3
- package/dist/platform/terminator.d.ts +3 -3
- package/dist/platform/terminator.js +2 -2
- package/dist/platform/types.d.ts +1 -1
- package/dist/restart.d.ts +1 -1
- package/dist/restart.js +5 -3
- package/dist/router/card-helpers.d.ts +1 -3
- package/dist/router/card-helpers.js +2 -2
- package/dist/router/config/codex.d.ts +1 -1
- package/dist/router/config/codex.js +3 -3
- package/dist/router/config/common/model-patch.d.ts +1 -2
- package/dist/router/config/common/model-patch.js +1 -2
- package/dist/router/config/opencode.js +2 -2
- package/dist/router/config/pi.js +1 -1
- package/dist/router/index.d.ts +30 -30
- package/dist/router/index.js +103 -84
- package/dist/router/order-exec-dispatch.d.ts +3 -10
- package/dist/router/order-exec-dispatch.js +5 -5
- package/dist/router/{utils.d.ts → usage-format.d.ts} +2 -4
- package/dist/router/{utils.js → usage-format.js} +3 -8
- package/dist/runner/bash/runner.d.ts +2 -2
- package/dist/runner/bash/runner.js +6 -6
- package/dist/runner/claude/runner.d.ts +1 -1
- package/dist/runner/claude/runner.js +2 -3
- package/dist/runner/claude/session.d.ts +4 -6
- package/dist/runner/claude/session.js +21 -23
- package/dist/runner/codex/app-server/runner.js +3 -3
- package/dist/runner/codex/app-server/translator.d.ts +2 -2
- package/dist/runner/codex/app-server/translator.js +5 -5
- package/dist/runner/common/acp/{protocol-helpers.d.ts → acp-approval.d.ts} +4 -10
- package/dist/runner/common/acp/{protocol-helpers.js → acp-approval.js} +7 -6
- package/dist/runner/common/acp/base-acp-runner.d.ts +1 -1
- package/dist/runner/common/acp/base-acp-runner.js +2 -2
- package/dist/runner/common/acp/base-translator.d.ts +3 -3
- package/dist/runner/common/acp/base-translator.js +4 -4
- package/dist/runner/common/acp/protocol-types.js +1 -1
- package/dist/runner/common/connection-based-runner.d.ts +5 -5
- package/dist/runner/common/connection-based-runner.js +5 -5
- package/dist/runner/common/jsonl-stream.d.ts +2 -2
- package/dist/runner/common/jsonl-stream.js +12 -12
- package/dist/runner/common/jsonrpc/client.d.ts +1 -1
- package/dist/runner/common/jsonrpc/client.js +1 -1
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +13 -13
- package/dist/runner/common/jsonrpc/connection-manager.js +37 -37
- package/dist/runner/common/jsonrpc/transport.d.ts +1 -1
- package/dist/runner/common/jsonrpc/transport.js +2 -2
- package/dist/runner/common/{usage.d.ts → result-usage.d.ts} +1 -1
- package/dist/runner/common/{usage.js → result-usage.js} +2 -2
- package/dist/runner/common/spawning-runner.d.ts +23 -10
- package/dist/runner/common/spawning-runner.js +48 -22
- package/dist/runner/common/{runner-utils.d.ts → synthetic-events.d.ts} +4 -4
- package/dist/runner/common/{runner-utils.js → synthetic-events.js} +5 -5
- package/dist/runner/dsh/client.d.ts +1 -1
- package/dist/runner/dsh/client.js +1 -1
- package/dist/runner/dsh/runner.js +3 -3
- package/dist/runner/kimi/acp/runner.d.ts +4 -4
- package/dist/runner/kimi/acp/runner.js +10 -10
- package/dist/runner/kimi/acp/translator.d.ts +2 -2
- package/dist/runner/kimi/acp/translator.js +5 -5
- package/dist/runner/opencode/acp/runner.d.ts +4 -4
- package/dist/runner/opencode/acp/runner.js +4 -4
- package/dist/runner/opencode/acp/translator.js +2 -2
- package/dist/runner/pi/rpc/runner.d.ts +1 -1
- package/dist/runner/pi/rpc/runner.js +2 -2
- package/dist/runner/pi/rpc/translator.d.ts +1 -2
- package/dist/runner/pi/rpc/translator.js +4 -6
- package/dist/runner/registry.d.ts +3 -3
- package/dist/runner/registry.js +3 -3
- package/dist/runner/types.d.ts +1 -1
- package/dist/session/claude/session-index.d.ts +13 -1
- package/dist/session/claude/session-index.js +21 -6
- package/dist/session/claude/session-reader.d.ts +1 -1
- package/dist/session/claude/session-reader.js +1 -1
- package/dist/session/claude/sessions.js +10 -10
- package/dist/session/codex/rollout-reader.d.ts +4 -4
- package/dist/session/codex/rollout-reader.js +14 -14
- package/dist/session/common/two-pass.js +2 -2
- package/dist/session/common/usage-accumulator.d.ts +2 -2
- package/dist/session/common/usage-accumulator.js +2 -2
- package/dist/session/dsh/sessions.d.ts +1 -1
- package/dist/session/dsh/sessions.js +3 -3
- package/dist/session/kimi/sessions.d.ts +2 -6
- package/dist/session/kimi/sessions.js +13 -19
- package/dist/session/opencode/sessions.d.ts +3 -3
- package/dist/session/opencode/sessions.js +15 -13
- package/dist/session/pi/sessions.js +13 -12
- package/dist/session/registry.d.ts +1 -1
- package/dist/session/registry.js +1 -1
- package/dist/session/session-store.d.ts +3 -4
- package/dist/session/session-store.js +3 -5
- package/dist/update/index.d.ts +1 -1
- package/dist/update/index.js +1 -1
- package/dist/update/version-check.d.ts +5 -3
- package/dist/update/version-check.js +10 -8
- package/dist/workspace/index.d.ts +4 -4
- package/dist/workspace/index.js +5 -5
- package/package.json +1 -1
package/dist/logger/index.js
CHANGED
|
@@ -21,7 +21,7 @@ function todayStr(d) {
|
|
|
21
21
|
return `${y}-${m}-${day}`;
|
|
22
22
|
}
|
|
23
23
|
function timestampStr(d) {
|
|
24
|
-
//
|
|
24
|
+
// local ISO 8601 with milliseconds and explicit offset, e.g.
|
|
25
25
|
// 2026-08-03T01:30:00.000+08:00. Previously this used toISOString() (UTC)
|
|
26
26
|
// while the daily directory boundary (todayStr) used local time — after
|
|
27
27
|
// local midnight in a +08:00 zone the new day's directory held lines
|
|
@@ -83,7 +83,7 @@ export class Logger {
|
|
|
83
83
|
fs.mkdirSync(dayDir, { recursive: true });
|
|
84
84
|
this.currentFile = path.join(dayDir, `${this.prefix}-${this.pid}.log`);
|
|
85
85
|
this.currentDate = dateStr;
|
|
86
|
-
//
|
|
86
|
+
// prune daily log directories older than the retention window so
|
|
87
87
|
// the logs directory does not grow without bound (single-day size cap is
|
|
88
88
|
// out of scope; bounded retention by day is the requested guard). Wrapped
|
|
89
89
|
// defensively so cleanup failure never blocks logging.
|
|
@@ -91,7 +91,7 @@ export class Logger {
|
|
|
91
91
|
return this.currentFile;
|
|
92
92
|
}
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
94
|
+
* remove daily subdirectories older than the retention window.
|
|
95
95
|
* Best-effort — any error is swallowed (logging must never throw).
|
|
96
96
|
*/
|
|
97
97
|
pruneOldDirs(currentDateStr) {
|
|
@@ -125,7 +125,7 @@ export class Logger {
|
|
|
125
125
|
const message = format(...args);
|
|
126
126
|
const line = `${ts} [${LEVEL_UPPER[level]}] ${message}\n`;
|
|
127
127
|
const file = this.ensureFile(date);
|
|
128
|
-
//
|
|
128
|
+
// never let a disk error (EACCES / ENOSPC / EROFS) escape write().
|
|
129
129
|
// If logging itself throws — especially inside an uncaughtException handler
|
|
130
130
|
// that calls logger.error(...) — a re-throw would abort Node and skip
|
|
131
131
|
// instanceLock.release(), leaking the singleton lock. Logging must never
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Atomically write content (string or binary Buffer) to a file via tmp + rename.
|
|
3
3
|
* Falls back to copy+unlink when rename fails with EXDEV (cross-device).
|
|
4
|
-
* rename 目标被占用(win32 EPERM/EBUSY
|
|
4
|
+
* rename 目标被占用(win32 EPERM/EBUSY)时短暂退避重试;重试耗尽后
|
|
5
5
|
* 保留 tmp 现场并告警,再抛出原错误。
|
|
6
6
|
*/
|
|
7
7
|
export declare function atomicWrite(filePath: string, content: string | Buffer, encoding?: BufferEncoding): void;
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { bestEffortUnlink, withBusyRetry } from '../common/fs.js';
|
|
4
4
|
import { getLogger } from '../logger/index.js';
|
|
5
5
|
/**
|
|
6
6
|
* Atomically write content (string or binary Buffer) to a file via tmp + rename.
|
|
7
7
|
* Falls back to copy+unlink when rename fails with EXDEV (cross-device).
|
|
8
|
-
* rename 目标被占用(win32 EPERM/EBUSY
|
|
8
|
+
* rename 目标被占用(win32 EPERM/EBUSY)时短暂退避重试;重试耗尽后
|
|
9
9
|
* 保留 tmp 现场并告警,再抛出原错误。
|
|
10
10
|
*/
|
|
11
11
|
export function atomicWrite(filePath, content, encoding = 'utf-8') {
|
|
12
12
|
const dir = path.dirname(filePath);
|
|
13
13
|
fs.mkdirSync(dir, { recursive: true });
|
|
14
|
-
|
|
14
|
+
// tmp 命名:pid-ts-rand 后缀,避免固定
|
|
15
|
+
// `.tmp` 名在异常残留后被下一次写入复用/误判;必须与目标同目录(rename 原子性)
|
|
16
|
+
const tmpPath = path.join(dir, `.${path.basename(filePath)}.tmp-${process.pid}-${Date.now()}-${Math.random()
|
|
17
|
+
.toString(36)
|
|
18
|
+
.slice(2, 10)}`);
|
|
15
19
|
// Buffer 内容忽略 encoding(writeFileSync 对 Buffer 自动以二进制写出)。
|
|
16
20
|
fs.writeFileSync(tmpPath, content, encoding);
|
|
17
21
|
// fsync the tmp file before rename so its data is durable on disk;
|
|
@@ -63,7 +67,7 @@ export function atomicWrite(filePath, content, encoding = 'utf-8') {
|
|
|
63
67
|
// errors we must not leak the tmp file — except when the scene is
|
|
64
68
|
// deliberately preserved after busy-retry exhaustion.
|
|
65
69
|
if (!preserveTmpScene) {
|
|
66
|
-
|
|
70
|
+
bestEffortUnlink(tmpPath);
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
}
|
|
@@ -86,7 +90,7 @@ export function atomicMoveFile(srcPath, destPath) {
|
|
|
86
90
|
catch (err) {
|
|
87
91
|
if (err.code === 'EXDEV') {
|
|
88
92
|
fs.copyFileSync(srcPath, destPath);
|
|
89
|
-
|
|
93
|
+
bestEffortUnlink(srcPath);
|
|
90
94
|
}
|
|
91
95
|
else {
|
|
92
96
|
throw err;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 每 agent
|
|
2
|
+
* 每 agent 的协议停止通道注册表。
|
|
3
3
|
*
|
|
4
4
|
* Windows 没有可拦截的跨进程 SIGTERM,优雅停止只能靠协议通道(claude 的 stdin
|
|
5
5
|
* 控制通道、codex 的 turn interrupt、ACP 的 session/cancel…)。「无通道」是
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 不会来的退出事件。
|
|
8
8
|
*
|
|
9
9
|
* **键 = agent + pid,不是 agent 单维**。两条理由:
|
|
10
|
-
* 1. 同一 agent 可以同时有多个存活子进程(每个
|
|
10
|
+
* 1. 同一 agent 可以同时有多个存活子进程(每个 cwd 一条长驻连接,外加
|
|
11
11
|
* claude 的进程代际更替)。按 agent 单键索引时后注册的连接会**覆盖**前一个
|
|
12
12
|
* 的通道,Terminator 便拿着 A 工作区的通道去停 B 工作区的进程;
|
|
13
13
|
* 2. Terminator 手上只有 `proc`,只有 pid 能把「这条通道」与「正要停的那个
|
|
@@ -12,7 +12,13 @@ export interface QueryIdentityOptions {
|
|
|
12
12
|
export interface VerifyPidIdentityOptions extends QueryIdentityOptions {
|
|
13
13
|
/** 期望的二进制名(basename,如 'claude'/'node');win32/darwin 大小写不敏感 */
|
|
14
14
|
expectedBinary: string;
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* pid 文件记录的 CreationDate;缺省则退化为仅命令行匹配。
|
|
17
|
+
* 现状:**无生产写入方**(pid 文件/锁文件都不记 CreationDate),仅测试
|
|
18
|
+
* 使用——接线需 win32 写侧在正常启动路径多一次 PowerShell 查询自身
|
|
19
|
+
* CreationDate,与 instance-lock「CIM 只在锁已存在路径查询」的约束冲突,
|
|
20
|
+
* 故保留该字段但暂不接线。
|
|
21
|
+
*/
|
|
16
22
|
expectedCreationDate?: string;
|
|
17
23
|
/** 匹配强度;默认 'executable'(见 {@link IdentityMatchMode}) */
|
|
18
24
|
matchMode?: IdentityMatchMode;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* pid
|
|
2
|
+
* pid 身份验证。
|
|
3
3
|
*
|
|
4
4
|
* POSIX 现状用 `ps -o command=` 读命令行,Windows 没有 ps,等价物是 CIM:
|
|
5
5
|
* PowerShell `Get-CimInstance Win32_Process` 取 CommandLine,并额外取
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - 只在查询失败/身份不匹配时返回 null / false,绝不抛给调用方——调用方
|
|
10
10
|
* (killOrphan、实例锁陈旧判定)一律「只有身份匹配才动手」;
|
|
11
11
|
* - win32 每次查询要拉起 PowerShell(冷启动约 1s),批量扫 pid 文件的场景
|
|
12
|
-
* (killOrphan
|
|
12
|
+
* (killOrphan)应按需调用,真机矩阵需实测整体耗时;
|
|
13
13
|
* - platform 显式注入,win32 语义可在任意宿主上单测。
|
|
14
14
|
*/
|
|
15
15
|
import { execFileSync, spawn } from 'node:child_process';
|
package/dist/platform/path.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export interface PathOptions {
|
|
2
2
|
platform?: NodeJS.Platform;
|
|
3
3
|
}
|
|
4
|
+
/**
|
|
5
|
+
* 路径的「判等键」形态:把 samePath 的平台归一规则单源化,供 Map/Set 键控
|
|
6
|
+
* 场景使用(按 cwd 建倒排索引时不能每次查询做 O(n) 两两比较)。不变量:
|
|
7
|
+
* `samePath(a, b)` 当且仅当 `pathKey(a) === pathKey(b)`。
|
|
8
|
+
*/
|
|
9
|
+
export declare function pathKey(p: string, opts?: PathOptions): string;
|
|
4
10
|
export declare function samePath(a: string, b: string, opts?: PathOptions): boolean;
|
|
5
11
|
/**
|
|
6
12
|
* cwd → 会话项目目录名(Claude/pi 的 on-disk 编码,lossy N-to-N)。
|
package/dist/platform/path.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PathKit —
|
|
2
|
+
* PathKit — 跨平台路径语义。
|
|
3
3
|
*
|
|
4
4
|
* 所有 cwd 相等性判断、会话目录编码、pid 文件名后缀、spawn cwd 前统一走本模块。
|
|
5
5
|
* 语义映射:
|
|
@@ -14,14 +14,18 @@ import { currentPlatform, isWin32 } from './select.js';
|
|
|
14
14
|
function resolveOptions(opts) {
|
|
15
15
|
return { platform: opts?.platform ?? currentPlatform };
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* 路径的「判等键」形态:把 samePath 的平台归一规则单源化,供 Map/Set 键控
|
|
19
|
+
* 场景使用(按 cwd 建倒排索引时不能每次查询做 O(n) 两两比较)。不变量:
|
|
20
|
+
* `samePath(a, b)` 当且仅当 `pathKey(a) === pathKey(b)`。
|
|
21
|
+
*/
|
|
22
|
+
export function pathKey(p, opts) {
|
|
18
23
|
const { platform } = resolveOptions(opts);
|
|
19
24
|
if (platform === 'linux')
|
|
20
|
-
return
|
|
25
|
+
return p;
|
|
21
26
|
if (isWin32(platform)) {
|
|
22
27
|
// win32:\ 与 / 都是分隔符,且路径大小写不敏感
|
|
23
|
-
|
|
24
|
-
return norm(a).toLowerCase() === norm(b).toLowerCase();
|
|
28
|
+
return p.replaceAll('\\', '/').replace(/\/+$/, '').toLowerCase();
|
|
25
29
|
}
|
|
26
30
|
// darwin:默认按大小写不敏感处理(macOS 默认文件系统 APFS 不敏感);
|
|
27
31
|
// 反斜杠是合法文件名字符,不动。分隔符只有 /。
|
|
@@ -29,9 +33,11 @@ export function samePath(a, b, opts) {
|
|
|
29
33
|
// 此处会判等(cwd 守卫假阳性)——影响面小(默认卷不敏感),如需精确需
|
|
30
34
|
// 逐目录 stat 探测卷大小写属性,暂不做。
|
|
31
35
|
// 尾部分隔符必须与 win32 同口径忽略,否则 `/cd ~/repo/` 与 `/cd ~/repo`
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
// 会被判成两个目录,迁移后直接表现为会话目录错配。
|
|
37
|
+
return p.replace(/\/+$/, '').toLowerCase();
|
|
38
|
+
}
|
|
39
|
+
export function samePath(a, b, opts) {
|
|
40
|
+
return pathKey(a, opts) === pathKey(b, opts);
|
|
35
41
|
}
|
|
36
42
|
/**
|
|
37
43
|
* cwd → 会话项目目录名(Claude/pi 的 on-disk 编码,lossy N-to-N)。
|
package/dist/platform/select.js
CHANGED
package/dist/platform/shell.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ShellBackend:把 `!` 命令与 kimi ACP terminal 的 shell 依赖收进 seam
|
|
3
|
-
* (design.md §7.2)。
|
|
2
|
+
* ShellBackend:把 `!` 命令与 kimi ACP terminal 的 shell 依赖收进 seam。
|
|
4
3
|
*
|
|
5
4
|
* - posix:`bash -c <command>`(与现状 BashProcessRunner 同语义);
|
|
6
5
|
* - win32:默认 Git Bash(`bash.exe -c <command>`),powershell / cmd 仅作为
|
|
7
|
-
*
|
|
6
|
+
* 显式用户选择;
|
|
8
7
|
* - 首版不做命令翻译器:Git Bash 缺失时抛 {@link ShellUnavailableError},
|
|
9
8
|
* 由调用方转成明确错误卡(提示需要 Git Bash)。
|
|
10
9
|
*
|
package/dist/platform/spawn.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare function spawnProcessSync(command: string, args?: readonly string
|
|
|
14
14
|
/**
|
|
15
15
|
* env 覆盖合并:先按不区分大小写删除 base 中的同名旧键,再设新值。
|
|
16
16
|
* Windows env 键大小写不敏感,`{...process.env, PATH:x}` 会产生 `PATH`/`Path`
|
|
17
|
-
*
|
|
17
|
+
* 双键。注意 posix 上并非普通 spread:base 中大小写不同名的键(如
|
|
18
18
|
* `Path`)也会被删除,而非与 `PATH` 并存——可接受(posix 语义键为全大写
|
|
19
19
|
* `PATH`,大小写变体本身就不是语义键),但语义与 spread 不同,勿混淆。
|
|
20
20
|
*/
|
|
@@ -30,7 +30,7 @@ export declare function mergeProcessEnv(base?: NodeJS.ProcessEnv, overrides?: No
|
|
|
30
30
|
* Windows 上 `DETACHED_PROCESS` 下 cmd.exe 另建控制台并重绑标准句柄,于是
|
|
31
31
|
* 子进程(npm 安装的 agent 全是 `.cmd` 垫片)的 stdout/stderr 永不抵达父进程,
|
|
32
32
|
* 表现为 agent 全程静默、turn 超时。实测 `windowsHide: true` 不能改善,
|
|
33
|
-
* 因此
|
|
33
|
+
* 因此 「保留 detached + windowsHide」的假设据此修正为「win32 不
|
|
34
34
|
* detached」。调用方应同时传 `windowsHide: true`,避免 cmd.exe 闪控制台窗口。
|
|
35
35
|
*/
|
|
36
36
|
export declare function useDetachedProcessGroup(platform?: NodeJS.Platform): boolean;
|
package/dist/platform/spawn.js
CHANGED
|
@@ -32,7 +32,7 @@ export function spawnProcessSync(command, args = [], options = {}) {
|
|
|
32
32
|
/**
|
|
33
33
|
* env 覆盖合并:先按不区分大小写删除 base 中的同名旧键,再设新值。
|
|
34
34
|
* Windows env 键大小写不敏感,`{...process.env, PATH:x}` 会产生 `PATH`/`Path`
|
|
35
|
-
*
|
|
35
|
+
* 双键。注意 posix 上并非普通 spread:base 中大小写不同名的键(如
|
|
36
36
|
* `Path`)也会被删除,而非与 `PATH` 并存——可接受(posix 语义键为全大写
|
|
37
37
|
* `PATH`,大小写变体本身就不是语义键),但语义与 spread 不同,勿混淆。
|
|
38
38
|
*/
|
|
@@ -61,7 +61,7 @@ const COMMAND_NOT_FOUND_PATTERN = /is not recognized as an internal or external
|
|
|
61
61
|
* Windows 上 `DETACHED_PROCESS` 下 cmd.exe 另建控制台并重绑标准句柄,于是
|
|
62
62
|
* 子进程(npm 安装的 agent 全是 `.cmd` 垫片)的 stdout/stderr 永不抵达父进程,
|
|
63
63
|
* 表现为 agent 全程静默、turn 超时。实测 `windowsHide: true` 不能改善,
|
|
64
|
-
* 因此
|
|
64
|
+
* 因此 「保留 detached + windowsHide」的假设据此修正为「win32 不
|
|
65
65
|
* detached」。调用方应同时传 `windowsHide: true`,避免 cmd.exe 闪控制台窗口。
|
|
66
66
|
*/
|
|
67
67
|
export function useDetachedProcessGroup(platform = currentPlatform) {
|
|
@@ -41,22 +41,22 @@ async function stopPosix(proc, opts, log) {
|
|
|
41
41
|
}
|
|
42
42
|
// 负 PID = 整个进程组:shell 包装层与孙进程一并带走
|
|
43
43
|
const pgid = -pid;
|
|
44
|
-
log('debug', `[
|
|
44
|
+
log('debug', `[terminator] sending SIGTERM to pgid=${pgid} immediate=${opts.immediate}`);
|
|
45
45
|
const termErr = signalGroup(pgid, 'SIGTERM');
|
|
46
46
|
if (termErr) {
|
|
47
|
-
log('warn', `[
|
|
47
|
+
log('warn', `[terminator] ${termErr}`);
|
|
48
48
|
return { requested: false, via: 'cooperative', error: termErr };
|
|
49
49
|
}
|
|
50
50
|
if (opts.immediate) {
|
|
51
51
|
// 不等待:SIGKILL 紧随其后;进程已死时 kill 抛 ESRCH,忽略即可
|
|
52
52
|
const killErr = signalGroup(pgid, 'SIGKILL');
|
|
53
53
|
if (killErr) {
|
|
54
|
-
log('warn', `[
|
|
54
|
+
log('warn', `[terminator] ${killErr}`);
|
|
55
55
|
return { requested: false, via: 'taskkill', error: killErr };
|
|
56
56
|
}
|
|
57
57
|
return { requested: true, via: 'taskkill' };
|
|
58
58
|
}
|
|
59
|
-
// 与 exit 事件竞速等 grace;用 once 避免重复 stop()
|
|
59
|
+
// 与 exit 事件竞速等 grace;用 once 避免重复 stop() 累积监听器
|
|
60
60
|
let graceTimer;
|
|
61
61
|
const exited = await Promise.race([
|
|
62
62
|
new Promise((resolve) => {
|
|
@@ -71,10 +71,10 @@ async function stopPosix(proc, opts, log) {
|
|
|
71
71
|
if (graceTimer)
|
|
72
72
|
clearTimeout(graceTimer);
|
|
73
73
|
if (!exited) {
|
|
74
|
-
log('info', `[
|
|
74
|
+
log('info', `[terminator] process group ${pgid} did not exit within grace period, sending SIGKILL`);
|
|
75
75
|
const killErr = signalGroup(pgid, 'SIGKILL');
|
|
76
76
|
if (killErr) {
|
|
77
|
-
log('warn', `[
|
|
77
|
+
log('warn', `[terminator] ${killErr}`);
|
|
78
78
|
return { requested: false, via: 'taskkill', error: killErr };
|
|
79
79
|
}
|
|
80
80
|
return { requested: true, via: 'taskkill' };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* win32 终止器:协议停止通道 → grace 等待 → taskkill
|
|
2
|
+
* win32 终止器:协议停止通道 → grace 等待 → taskkill 树杀。
|
|
3
3
|
*
|
|
4
4
|
* 状态机:
|
|
5
5
|
* 1. 进程已退出 → nothing(via='already-exited')
|
|
@@ -74,7 +74,7 @@ export function createWin32Terminator(deps) {
|
|
|
74
74
|
return { requested: true, via: 'taskkill' };
|
|
75
75
|
}
|
|
76
76
|
const agent = deps.agent;
|
|
77
|
-
// 按 agent + pid 查:同一 agent 可能有多条长驻连接(每
|
|
77
|
+
// 按 agent + pid 查:同一 agent 可能有多条长驻连接(每 cwd 一条),
|
|
78
78
|
// 只有 pid 能把通道归属钉到手上这个 proc 上。
|
|
79
79
|
const stopper = agent === undefined ? undefined : stoppers.get(agent, proc.pid);
|
|
80
80
|
if (!stopper) {
|
|
@@ -99,7 +99,7 @@ export function createWin32Terminator(deps) {
|
|
|
99
99
|
return { requested: true, via: 'taskkill' };
|
|
100
100
|
},
|
|
101
101
|
cleanupOnExit(proc) {
|
|
102
|
-
//
|
|
102
|
+
// 真机验证项(§9.2 矩阵):本方法在 process.on('exit') 里调用,而
|
|
103
103
|
// killTree 依赖 libuv uv_spawn 在 exit handler 内同步发起 taskkill——
|
|
104
104
|
// 大概率可用(uv_spawn 本身同步),但与 posix 路径(同步 process.kill)
|
|
105
105
|
// 可靠性不等价;失败表现为 win32 上 lark-remote 退出后 agent 变孤儿。
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 平台终止器 seam
|
|
2
|
+
* 平台终止器 seam 的接口层。
|
|
3
3
|
*
|
|
4
4
|
* Windows 没有可拦截的跨进程 SIGTERM、负 PID 无效,因此把「进程组 + 信号」
|
|
5
5
|
* 换成「协议停止通道 + 进程树杀」。本文件只放接口与平台分发,实现见
|
|
@@ -14,7 +14,7 @@ export type { AgentStopper } from './agent-stopper.js';
|
|
|
14
14
|
* 进程终止器。
|
|
15
15
|
*
|
|
16
16
|
* `stop` 的 `immediate` 语义与原 `process-stopper` 的 `stop({immediate})` 对齐。
|
|
17
|
-
*
|
|
17
|
+
* 声明返回 `Promise<void>`,这里收窄为 {@link TerminateResult}:降级
|
|
18
18
|
* 必须可观测(优雅段被跳过、走了树杀、进程早已退出),否则 win32 上「等一个
|
|
19
19
|
* 不会来的退出事件」这类退化无法被发现。
|
|
20
20
|
*/
|
|
@@ -39,6 +39,6 @@ export interface TerminatorDeps {
|
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
41
|
* 唯一的终止器选择入口:posix 走组杀,win32 走「协议通道 + taskkill 树杀」。
|
|
42
|
-
* 其余模块一律拿这里的 Terminator,禁止散落平台 if
|
|
42
|
+
* 其余模块一律拿这里的 Terminator,禁止散落平台 if。
|
|
43
43
|
*/
|
|
44
44
|
export declare function createTerminator(deps: TerminatorDeps): Terminator;
|
|
@@ -17,12 +17,12 @@ function defaultTerminatorLog(level, message) {
|
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* 唯一的终止器选择入口:posix 走组杀,win32 走「协议通道 + taskkill 树杀」。
|
|
20
|
-
* 其余模块一律拿这里的 Terminator,禁止散落平台 if
|
|
20
|
+
* 其余模块一律拿这里的 Terminator,禁止散落平台 if。
|
|
21
21
|
*/
|
|
22
22
|
export function createTerminator(deps) {
|
|
23
23
|
const platform = deps.platform ?? currentPlatform;
|
|
24
24
|
// 默认日志出口必须真的接上:win32 的「无协议通道 → 直接树杀」「协议通道抛错
|
|
25
|
-
// → 转树杀」以及 posix 的 grace
|
|
25
|
+
// → 转树杀」以及 posix 的 grace 超时都是设计要求的可观测降级信号,
|
|
26
26
|
// 缺省 undefined 会让这些信号全部静默消失。
|
|
27
27
|
const log = deps.log ?? defaultTerminatorLog;
|
|
28
28
|
if (isWin32(platform)) {
|
package/dist/platform/types.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface LaunchSpec {
|
|
|
11
11
|
/** 可执行文件路径(posix 绝对路径 / win32 含 .cmd 垫片路径) */
|
|
12
12
|
file: string;
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
14
|
+
/** 终止结果(win32 状态机产出;可观测降级用)。 */
|
|
15
15
|
export interface TerminateResult {
|
|
16
16
|
/** 进程是否已确认被请求终止(不代表已退出,退出由 exit 事件驱动) */
|
|
17
17
|
requested: boolean;
|
package/dist/restart.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export declare const RESTART_WAIT_PID_ENV = "LARK_REMOTE_RESTART_WAIT_PID";
|
|
|
16
16
|
* dir) propagate. Caller picks throw-vs-degrade semantics.
|
|
17
17
|
*
|
|
18
18
|
* Two disciplines live here so every caller inherits them for free:
|
|
19
|
-
* - windowsHide: win32 detached children would flash a console window
|
|
19
|
+
* - windowsHide: win32 detached children would flash a console window;
|
|
20
20
|
* - the 'error' handler is attached BEFORE the pid check — a failed spawn
|
|
21
21
|
* emits 'error' asynchronously (ENOENT/EACCES), and the synchronous pid
|
|
22
22
|
* check throws/returns first, so attaching after would leave the 'error'
|
package/dist/restart.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawnProcess } from './platform/spawn.js';
|
|
1
|
+
import { mergeProcessEnv, spawnProcess } from './platform/spawn.js';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { getLogger } from './logger/index.js';
|
|
@@ -23,7 +23,7 @@ const POLL_MS = 100;
|
|
|
23
23
|
* dir) propagate. Caller picks throw-vs-degrade semantics.
|
|
24
24
|
*
|
|
25
25
|
* Two disciplines live here so every caller inherits them for free:
|
|
26
|
-
* - windowsHide: win32 detached children would flash a console window
|
|
26
|
+
* - windowsHide: win32 detached children would flash a console window;
|
|
27
27
|
* - the 'error' handler is attached BEFORE the pid check — a failed spawn
|
|
28
28
|
* emits 'error' asynchronously (ENOENT/EACCES), and the synchronous pid
|
|
29
29
|
* check throws/returns first, so attaching after would leave the 'error'
|
|
@@ -58,7 +58,9 @@ export function spawnDetachedBridge(args, logFilePath, opts) {
|
|
|
58
58
|
*/
|
|
59
59
|
export function spawnReplacementBridge(logsDir) {
|
|
60
60
|
const pid = spawnDetachedBridge(process.argv.slice(1), path.join(logsDir, 'restart-child.log'), {
|
|
61
|
-
env
|
|
61
|
+
// env 注入统一走 mergeProcessEnv:Windows env 键大小写不敏感,
|
|
62
|
+
// 覆盖注入必须先按不区分大小写删旧键,避免双键
|
|
63
|
+
env: mergeProcessEnv(process.env, { [RESTART_WAIT_PID_ENV]: String(process.pid) }),
|
|
62
64
|
});
|
|
63
65
|
if (pid === null) {
|
|
64
66
|
throw new Error('spawn replacement lark-remote failed: no pid');
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { markdownDiv } from '../card/collapsible.js';
|
|
10
10
|
import type { AgentSessionContentEvent } from '../runner/index.js';
|
|
11
|
-
import { formatUsageStats } from './
|
|
11
|
+
import { formatUsageStats } from './usage-format.js';
|
|
12
12
|
type SessionUsageLike = Parameters<typeof formatUsageStats>[0];
|
|
13
13
|
/**
|
|
14
14
|
* Build a collapsible panel for a single session content event.
|
|
@@ -28,8 +28,6 @@ type SessionUsageLike = Parameters<typeof formatUsageStats>[0];
|
|
|
28
28
|
*/
|
|
29
29
|
export declare function sessionEventPanel(ev: AgentSessionContentEvent, index: number, totalEvents: number, tailExpandedCount?: number, agentKind?: string): object;
|
|
30
30
|
export { markdownDiv };
|
|
31
|
-
/** 分页卡「跳转页码」输入框的组件 name(仅用于结构断言与可读性)。 */
|
|
32
|
-
export declare const PAGE_JUMP_INPUT_NAME = "pageInput";
|
|
33
31
|
/** 页码输入非法时的统一提示文案(/ls /ws /resume /active /order 共用)。 */
|
|
34
32
|
export declare const PAGE_JUMP_INVALID_HINT = "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u9875\u7801\uFF08\u6B63\u6574\u6570\uFF09";
|
|
35
33
|
/**
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { agentDisplayName } from '../card/card-shared.js';
|
|
10
10
|
import { collapsibleMarkdownPanel, markdownDiv } from '../card/collapsible.js';
|
|
11
11
|
import { formatTimestamp } from '../card/time.js';
|
|
12
|
-
import { formatUsageStats } from './
|
|
12
|
+
import { formatUsageStats } from './usage-format.js';
|
|
13
13
|
/**
|
|
14
14
|
* Build a collapsible panel for a single session content event.
|
|
15
15
|
*
|
|
@@ -64,7 +64,7 @@ function eventLabel(ev, agentKind) {
|
|
|
64
64
|
}
|
|
65
65
|
export { markdownDiv };
|
|
66
66
|
/** 分页卡「跳转页码」输入框的组件 name(仅用于结构断言与可读性)。 */
|
|
67
|
-
|
|
67
|
+
const PAGE_JUMP_INPUT_NAME = 'pageInput';
|
|
68
68
|
/** 页码输入非法时的统一提示文案(/ls /ws /resume /active /order 共用)。 */
|
|
69
69
|
export const PAGE_JUMP_INVALID_HINT = '请输入有效的页码(正整数)';
|
|
70
70
|
/**
|
|
@@ -16,7 +16,7 @@ export declare class CodexConfigBuilder implements AgentConfigCardBuilder {
|
|
|
16
16
|
* - 否则优先 supported_reasoning_levels 中位 (len-1)/2;
|
|
17
17
|
* - 列表为空才用声明 default_reasoning_level;
|
|
18
18
|
* - 两者皆无 → value=undefined(清空档位 = 不传 effort;router setNestedValue
|
|
19
|
-
* 对 undefined
|
|
19
|
+
* 对 undefined 做键删除)。
|
|
20
20
|
*/
|
|
21
21
|
private effortPatchForModel;
|
|
22
22
|
}
|
|
@@ -41,7 +41,7 @@ export class CodexConfigBuilder {
|
|
|
41
41
|
key: 'agents.codex.reasoningEffort',
|
|
42
42
|
label: '推理强度',
|
|
43
43
|
type: 'select',
|
|
44
|
-
//
|
|
44
|
+
// 档位按目录实际声明透传:codex 支持 none/Custom,不再按标准枚举过滤
|
|
45
45
|
options: getReasoningEffortOptions(currentCodexModel),
|
|
46
46
|
currentValue: currentReasoningEffort,
|
|
47
47
|
});
|
|
@@ -81,7 +81,7 @@ export class CodexConfigBuilder {
|
|
|
81
81
|
if (key === 'agents.codex.modelProvider' && typeof value === 'string') {
|
|
82
82
|
const codexCfg = loadCodexConfig();
|
|
83
83
|
const currentModel = config.agents?.codex?.model;
|
|
84
|
-
const modelPatch = resetModelPatch(
|
|
84
|
+
const modelPatch = resetModelPatch('agents.codex.model', currentModel, codexCfg.modelOptions(value));
|
|
85
85
|
if (modelPatch) {
|
|
86
86
|
patches.push(modelPatch);
|
|
87
87
|
const replacedModel = modelPatch.value;
|
|
@@ -108,7 +108,7 @@ export class CodexConfigBuilder {
|
|
|
108
108
|
* - 否则优先 supported_reasoning_levels 中位 (len-1)/2;
|
|
109
109
|
* - 列表为空才用声明 default_reasoning_level;
|
|
110
110
|
* - 两者皆无 → value=undefined(清空档位 = 不传 effort;router setNestedValue
|
|
111
|
-
* 对 undefined
|
|
111
|
+
* 对 undefined 做键删除)。
|
|
112
112
|
*/
|
|
113
113
|
effortPatchForModel(config, newModel) {
|
|
114
114
|
const currentReasoningEffort = config.agents?.codex?.reasoningEffort;
|
|
@@ -10,13 +10,12 @@
|
|
|
10
10
|
* model is not offered by the new provider (and the new provider has models),
|
|
11
11
|
* patch the model to the provider's first option.
|
|
12
12
|
*
|
|
13
|
-
* @param providerKey The field key that changed, e.g. 'agents.codex.modelProvider'.
|
|
14
13
|
* @param modelKey The model field to reset, e.g. 'agents.codex.model'.
|
|
15
14
|
* @param currentModel The currently-configured model value (may be undefined).
|
|
16
15
|
* @param newModelOptions Models offered by the newly-selected provider.
|
|
17
16
|
* @returns A `{ key, value }` patch, or null when no reset is needed.
|
|
18
17
|
*/
|
|
19
|
-
export declare function resetModelPatch(
|
|
18
|
+
export declare function resetModelPatch(modelKey: string, currentModel: string | undefined, newModelOptions: string[]): {
|
|
20
19
|
key: string;
|
|
21
20
|
value: unknown;
|
|
22
21
|
} | null;
|
|
@@ -10,13 +10,12 @@
|
|
|
10
10
|
* model is not offered by the new provider (and the new provider has models),
|
|
11
11
|
* patch the model to the provider's first option.
|
|
12
12
|
*
|
|
13
|
-
* @param providerKey The field key that changed, e.g. 'agents.codex.modelProvider'.
|
|
14
13
|
* @param modelKey The model field to reset, e.g. 'agents.codex.model'.
|
|
15
14
|
* @param currentModel The currently-configured model value (may be undefined).
|
|
16
15
|
* @param newModelOptions Models offered by the newly-selected provider.
|
|
17
16
|
* @returns A `{ key, value }` patch, or null when no reset is needed.
|
|
18
17
|
*/
|
|
19
|
-
export function resetModelPatch(
|
|
18
|
+
export function resetModelPatch(modelKey, currentModel, newModelOptions) {
|
|
20
19
|
const currentModelIsValid = newModelOptions.some((m) => m === currentModel);
|
|
21
20
|
if (!currentModelIsValid && newModelOptions.length > 0) {
|
|
22
21
|
return { key: modelKey, value: newModelOptions[0] };
|
|
@@ -29,7 +29,7 @@ export class OpencodeConfigBuilder {
|
|
|
29
29
|
type: 'select',
|
|
30
30
|
options: modelOptions,
|
|
31
31
|
});
|
|
32
|
-
//
|
|
32
|
+
// mode select(build/plan,默认 build)——opencode 无运行期审批档位,
|
|
33
33
|
// mode 即审批粒度档位(build 逐项审批 / plan 规划模式)。
|
|
34
34
|
const currentMode = displayConfig.agents?.opencode?.mode ?? 'build';
|
|
35
35
|
fields.push({
|
|
@@ -58,7 +58,7 @@ export class OpencodeConfigBuilder {
|
|
|
58
58
|
if (key === 'agents.opencode.providerID' && typeof value === 'string') {
|
|
59
59
|
const opencodeCfg = loadOpencodeConfig();
|
|
60
60
|
const currentModel = config.agents?.opencode?.modelID;
|
|
61
|
-
const modelPatch = resetModelPatch(
|
|
61
|
+
const modelPatch = resetModelPatch('agents.opencode.modelID', currentModel, opencodeCfg.modelOptions(value));
|
|
62
62
|
if (modelPatch)
|
|
63
63
|
patches.push(modelPatch);
|
|
64
64
|
}
|
package/dist/router/config/pi.js
CHANGED
|
@@ -39,7 +39,7 @@ export class PiConfigBuilder {
|
|
|
39
39
|
// provider 变更时,重置 model 为新 provider 的首个模型
|
|
40
40
|
if (key === 'agents.pi.provider' && typeof value === 'string') {
|
|
41
41
|
const currentModel = config.agents?.pi?.model;
|
|
42
|
-
const modelPatch = resetModelPatch(
|
|
42
|
+
const modelPatch = resetModelPatch('agents.pi.model', currentModel, getPiModelOptions(value));
|
|
43
43
|
if (modelPatch)
|
|
44
44
|
patches.push(modelPatch);
|
|
45
45
|
}
|