lark-remote 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -1
- package/README.en.md +9 -1
- package/README.md +14 -2
- package/dist/bridge/approval-coordinator.d.ts +71 -34
- package/dist/bridge/approval-coordinator.js +225 -41
- package/dist/bridge/inbound-media.d.ts +62 -0
- package/dist/bridge/inbound-media.js +282 -0
- package/dist/bridge/index.d.ts +97 -13
- package/dist/bridge/index.js +227 -87
- package/dist/bridge/queue-manager.d.ts +2 -1
- package/dist/bridge/queue-manager.js +1 -0
- package/dist/card/approval-render.js +294 -36
- package/dist/card/bash-card-session.d.ts +2 -24
- package/dist/card/bash-card-session.js +6 -73
- package/dist/card/bash-renderer.js +2 -3
- package/dist/card/card-budget.js +29 -75
- package/dist/card/card-session.d.ts +48 -0
- package/dist/card/card-session.js +86 -0
- package/dist/card/card-shared.d.ts +7 -16
- package/dist/card/card-shared.js +7 -30
- package/dist/card/run-card-session.d.ts +2 -55
- package/dist/card/run-card-session.js +6 -104
- package/dist/card/run-renderer.d.ts +7 -0
- package/dist/card/run-renderer.js +116 -238
- package/dist/card/run-state.d.ts +13 -0
- package/dist/card/run-state.js +25 -16
- package/dist/card/text-truncate.d.ts +13 -12
- package/dist/card/text-truncate.js +13 -63
- package/dist/common/fs.d.ts +8 -0
- package/dist/common/fs.js +17 -0
- package/dist/common/guards.d.ts +0 -20
- package/dist/common/guards.js +0 -43
- package/dist/common/sleep.d.ts +2 -0
- package/dist/common/sleep.js +5 -0
- package/dist/common/truncate.d.ts +39 -0
- package/dist/common/truncate.js +92 -0
- package/dist/config/codex-config.d.ts +1 -1
- package/dist/config/codex-config.js +1 -1
- package/dist/config/index.d.ts +112 -24
- package/dist/config/index.js +139 -37
- package/dist/connector/index.d.ts +69 -2
- package/dist/connector/index.js +107 -1
- package/dist/index.js +150 -118
- package/dist/instance-lock.js +4 -12
- package/dist/order/alias-resolve.d.ts +13 -0
- package/dist/order/alias-resolve.js +22 -0
- package/dist/order/alias-store.d.ts +38 -0
- package/dist/order/alias-store.js +106 -0
- package/dist/persistence/atomic-write.d.ts +8 -2
- package/dist/persistence/atomic-write.js +23 -7
- package/dist/restart.js +2 -1
- package/dist/router/card-action-payload.d.ts +30 -0
- package/dist/router/card-action-payload.js +39 -0
- package/dist/router/card-helpers.d.ts +63 -0
- package/dist/router/card-helpers.js +146 -0
- package/dist/router/config/claude.js +30 -1
- package/dist/router/config/codex.js +29 -50
- package/dist/router/config/common/model-patch.d.ts +22 -0
- package/dist/router/config/common/model-patch.js +26 -0
- package/dist/router/config/common/render.js +5 -4
- package/dist/router/config/dsh.d.ts +51 -0
- package/dist/router/config/dsh.js +179 -0
- package/dist/router/config/index.d.ts +12 -0
- package/dist/router/config/index.js +28 -1
- package/dist/router/config/kimi.d.ts +4 -1
- package/dist/router/config/kimi.js +27 -0
- package/dist/router/config/opencode.js +18 -5
- package/dist/router/config/pi.js +4 -5
- package/dist/router/config/types.d.ts +17 -2
- package/dist/router/index.d.ts +76 -22
- package/dist/router/index.js +479 -548
- package/dist/router/utils.d.ts +2 -0
- package/dist/router/utils.js +16 -2
- package/dist/runner/agent-choices-common.d.ts +16 -0
- package/dist/runner/agent-choices-common.js +34 -0
- package/dist/runner/claude/runner.d.ts +47 -10
- package/dist/runner/claude/runner.js +117 -46
- package/dist/runner/claude/session.d.ts +166 -0
- package/dist/runner/claude/session.js +768 -0
- package/dist/runner/codex/app-server/protocol-types.d.ts +66 -39
- package/dist/runner/codex/app-server/protocol-types.js +2 -13
- package/dist/runner/codex/app-server/runner.d.ts +52 -59
- package/dist/runner/codex/app-server/runner.js +216 -338
- package/dist/runner/codex/app-server/translator.d.ts +8 -21
- package/dist/runner/codex/app-server/translator.js +45 -18
- package/dist/runner/codex/index.d.ts +0 -2
- package/dist/runner/codex/index.js +0 -2
- package/dist/runner/common/acp/protocol-helpers.d.ts +14 -0
- package/dist/runner/common/acp/protocol-helpers.js +21 -0
- package/dist/runner/common/acp/protocol-types.d.ts +271 -0
- package/dist/runner/common/acp/protocol-types.js +77 -0
- package/dist/runner/common/connection-based-runner.d.ts +111 -0
- package/dist/runner/common/connection-based-runner.js +261 -0
- package/dist/runner/{codex/app-server → common/jsonrpc}/client.d.ts +14 -7
- package/dist/runner/{codex/app-server → common/jsonrpc}/client.js +27 -18
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +125 -0
- package/dist/runner/{codex/app-server → common/jsonrpc}/connection-manager.js +63 -28
- package/dist/runner/{codex/app-server → common/jsonrpc}/transport.d.ts +5 -4
- package/dist/runner/{codex/app-server → common/jsonrpc}/transport.js +18 -19
- package/dist/runner/common/jsonrpc/types.d.ts +32 -0
- package/dist/runner/common/jsonrpc/types.js +9 -0
- package/dist/runner/common/process-stopper.d.ts +2 -1
- package/dist/runner/common/process-stopper.js +1 -0
- package/dist/runner/common/runner-utils.d.ts +6 -13
- package/dist/runner/common/runner-utils.js +6 -47
- package/dist/runner/common/spawning-runner.d.ts +24 -70
- package/dist/runner/common/spawning-runner.js +98 -152
- package/dist/runner/dsh/client.d.ts +95 -0
- package/dist/runner/dsh/client.js +418 -0
- package/dist/runner/dsh/fake-dsh-server.d.ts +60 -0
- package/dist/runner/dsh/fake-dsh-server.js +156 -0
- package/dist/runner/dsh/index.d.ts +3 -0
- package/dist/runner/dsh/index.js +4 -0
- package/dist/runner/dsh/runner.d.ts +57 -0
- package/dist/runner/dsh/runner.js +191 -0
- package/dist/runner/dsh/translator.d.ts +28 -0
- package/dist/runner/dsh/translator.js +98 -0
- package/dist/runner/dsh/types.d.ts +155 -0
- package/dist/runner/dsh/types.js +14 -0
- package/dist/runner/index.d.ts +5 -4
- package/dist/runner/index.js +5 -4
- package/dist/runner/kimi/acp/runner.d.ts +106 -0
- package/dist/runner/kimi/acp/runner.js +516 -0
- package/dist/runner/kimi/acp/translator.d.ts +138 -0
- package/dist/runner/kimi/acp/translator.js +441 -0
- package/dist/runner/kimi/index.d.ts +1 -1
- package/dist/runner/kimi/index.js +1 -1
- package/dist/runner/opencode/acp/index.d.ts +1 -0
- package/dist/runner/opencode/acp/index.js +2 -0
- package/dist/runner/opencode/acp/runner.d.ts +121 -0
- package/dist/runner/opencode/acp/runner.js +429 -0
- package/dist/runner/opencode/acp/translator.d.ts +119 -0
- package/dist/runner/opencode/acp/translator.js +345 -0
- package/dist/runner/opencode/index.d.ts +1 -1
- package/dist/runner/opencode/index.js +1 -1
- package/dist/runner/pi/index.d.ts +1 -1
- package/dist/runner/pi/index.js +1 -1
- package/dist/runner/pi/rpc/client.d.ts +52 -0
- package/dist/runner/pi/rpc/client.js +114 -0
- package/dist/runner/pi/rpc/index.d.ts +4 -0
- package/dist/runner/pi/rpc/index.js +4 -0
- package/dist/runner/pi/rpc/protocol-types.d.ts +127 -0
- package/dist/runner/pi/rpc/protocol-types.js +21 -0
- package/dist/runner/pi/rpc/runner.d.ts +74 -0
- package/dist/runner/pi/rpc/runner.js +190 -0
- package/dist/runner/pi/rpc/translator.d.ts +55 -0
- package/dist/runner/pi/rpc/translator.js +258 -0
- package/dist/runner/probe.js +6 -1
- package/dist/runner/question-common.d.ts +26 -0
- package/dist/runner/question-common.js +42 -0
- package/dist/runner/registry.d.ts +7 -8
- package/dist/runner/registry.js +3 -4
- package/dist/runner/resolve-agent-choices.js +18 -59
- package/dist/runner/sync-agent-choices.js +16 -67
- package/dist/runner/types.d.ts +97 -14
- package/dist/session/claude/session-index.js +2 -3
- package/dist/session/claude/session-reader.js +3 -6
- package/dist/session/claude/sessions.js +43 -28
- package/dist/session/codex/rollout-reader.d.ts +2 -2
- package/dist/session/codex/rollout-reader.js +14 -17
- package/dist/session/codex/sessions.d.ts +1 -1
- package/dist/session/codex/sessions.js +2 -2
- package/dist/session/common/content-blocks.d.ts +7 -0
- package/dist/session/common/content-blocks.js +13 -9
- package/dist/session/common/jsonl.d.ts +8 -25
- package/dist/session/common/jsonl.js +126 -261
- package/dist/session/common/pagination.d.ts +28 -0
- package/dist/session/common/pagination.js +33 -0
- package/dist/session/dsh/index.d.ts +1 -0
- package/dist/session/dsh/index.js +2 -0
- package/dist/session/dsh/sessions.d.ts +39 -0
- package/dist/session/dsh/sessions.js +237 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/session/kimi/sessions.d.ts +34 -1
- package/dist/session/kimi/sessions.js +208 -98
- package/dist/session/opencode/sessions.d.ts +17 -3
- package/dist/session/opencode/sessions.js +29 -29
- package/dist/session/pi/sessions.d.ts +5 -1
- package/dist/session/pi/sessions.js +12 -16
- package/dist/session/session-store.d.ts +11 -1
- package/dist/session/session-store.js +75 -178
- package/dist/update/index.d.ts +2 -2
- package/dist/update/index.js +2 -2
- package/dist/update/install.d.ts +20 -2
- package/dist/update/install.js +51 -7
- package/dist/workspace/index.d.ts +4 -0
- package/dist/workspace/index.js +8 -1
- package/package.json +2 -1
- package/dist/runner/codex/app-server/connection-manager.d.ts +0 -56
- package/dist/runner/codex/argv.d.ts +0 -33
- package/dist/runner/codex/argv.js +0 -40
- package/dist/runner/codex/factory.d.ts +0 -29
- package/dist/runner/codex/factory.js +0 -39
- package/dist/runner/codex/jsonl.d.ts +0 -37
- package/dist/runner/codex/jsonl.js +0 -221
- package/dist/runner/codex/runner.d.ts +0 -66
- package/dist/runner/codex/runner.js +0 -129
- package/dist/runner/common/exec-translator.d.ts +0 -73
- package/dist/runner/common/exec-translator.js +0 -105
- package/dist/runner/kimi/jsonl.d.ts +0 -28
- package/dist/runner/kimi/jsonl.js +0 -143
- package/dist/runner/kimi/runner.d.ts +0 -49
- package/dist/runner/kimi/runner.js +0 -105
- package/dist/runner/opencode/argv.d.ts +0 -32
- package/dist/runner/opencode/argv.js +0 -35
- package/dist/runner/opencode/jsonl.d.ts +0 -68
- package/dist/runner/opencode/jsonl.js +0 -295
- package/dist/runner/opencode/runner.d.ts +0 -61
- package/dist/runner/opencode/runner.js +0 -93
- package/dist/runner/pi/jsonl.d.ts +0 -128
- package/dist/runner/pi/jsonl.js +0 -249
- package/dist/runner/pi/runner.d.ts +0 -36
- package/dist/runner/pi/runner.js +0 -76
- package/dist/session/common/catch-up.d.ts +0 -12
- package/dist/session/common/catch-up.js +0 -31
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 一次别名展开。
|
|
3
|
+
*
|
|
4
|
+
* - 仅匹配消息开头的 `$name`(`^$name`),`$name` 后接空格或行尾,全词精确匹配;
|
|
5
|
+
* - 名称必须 `[A-Za-z_][A-Za-z0-9_]*`(数字开头的 `$500` 永不匹配);
|
|
6
|
+
* - 未知别名返回 undefined(原样透传给 agent,不报错);
|
|
7
|
+
* - 单次不递归:展开结果不再二次展开,防死循环;
|
|
8
|
+
* - `!` / `/` 开头消息天然不匹配(正则要求 `$` 开头),bash 的 `$PATH` 不受影响。
|
|
9
|
+
*
|
|
10
|
+
* @returns 展开后的完整消息;无匹配时返回 undefined。
|
|
11
|
+
*/
|
|
12
|
+
export function resolveAlias(message, aliases) {
|
|
13
|
+
const match = /^\$([A-Za-z_][A-Za-z0-9_]*)(?=$|[ \t])/.exec(message);
|
|
14
|
+
if (!match)
|
|
15
|
+
return undefined;
|
|
16
|
+
const entry = aliases.find((a) => a.name === match[1]);
|
|
17
|
+
if (!entry)
|
|
18
|
+
return undefined;
|
|
19
|
+
// lookahead 不消费空格:match[0] 仅含 $name,参数(含前导空格)原样拼接在别名文本后。
|
|
20
|
+
return entry.text + message.slice(match[0].length);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=alias-resolve.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare const MAX_ALIASES = 50;
|
|
2
|
+
export declare const MAX_ALIAS_NAME_LENGTH = 20;
|
|
3
|
+
export declare const MAX_ALIAS_TEXT_LENGTH = 200;
|
|
4
|
+
/**
|
|
5
|
+
* 别名名称规则:`[A-Za-z_][A-Za-z0-9_]*`,≤20 字符,不能数字开头
|
|
6
|
+
* —— `$500`、`$3d` 这类正常文本永远不匹配,不会误展开。
|
|
7
|
+
*/
|
|
8
|
+
export declare const ALIAS_NAME_PATTERN: RegExp;
|
|
9
|
+
export interface AliasEntry {
|
|
10
|
+
name: string;
|
|
11
|
+
text: string;
|
|
12
|
+
createdAt: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 全局别名存储。
|
|
16
|
+
*
|
|
17
|
+
* 独立于 OrderStore(aliases.json),避免破坏 orders.json 向后兼容。
|
|
18
|
+
* 原子写(tmp + rename)+ 损坏恢复,复用 OrderStore/WorkspaceStore 同一模式。
|
|
19
|
+
*/
|
|
20
|
+
export declare class AliasStore {
|
|
21
|
+
private filePath;
|
|
22
|
+
private data;
|
|
23
|
+
constructor(filePath?: string);
|
|
24
|
+
/** Reload data from disk (useful when another process may have written). */
|
|
25
|
+
reload(): void;
|
|
26
|
+
private load;
|
|
27
|
+
private persist;
|
|
28
|
+
has(name: string): boolean;
|
|
29
|
+
get(name: string): AliasEntry | undefined;
|
|
30
|
+
list(): AliasEntry[];
|
|
31
|
+
/**
|
|
32
|
+
* 注册/更新别名(同名覆盖文本,保留 createdAt)。返回写入后的条目。
|
|
33
|
+
* 名称/文本/数量超限时抛错,由调用方提示用户。
|
|
34
|
+
*/
|
|
35
|
+
set(name: string, text: string): AliasEntry;
|
|
36
|
+
/** 删除别名;不存在返回 false。 */
|
|
37
|
+
remove(name: string): boolean;
|
|
38
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import { atomicWriteJson } from '../persistence/atomic-write.js';
|
|
4
|
+
import { loadJsonFile } from '../persistence/load-json-file.js';
|
|
5
|
+
export const MAX_ALIASES = 50;
|
|
6
|
+
export const MAX_ALIAS_NAME_LENGTH = 20;
|
|
7
|
+
export const MAX_ALIAS_TEXT_LENGTH = 200;
|
|
8
|
+
/**
|
|
9
|
+
* 别名名称规则:`[A-Za-z_][A-Za-z0-9_]*`,≤20 字符,不能数字开头
|
|
10
|
+
* —— `$500`、`$3d` 这类正常文本永远不匹配,不会误展开。
|
|
11
|
+
*/
|
|
12
|
+
export const ALIAS_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
13
|
+
/**
|
|
14
|
+
* 全局别名存储。
|
|
15
|
+
*
|
|
16
|
+
* 独立于 OrderStore(aliases.json),避免破坏 orders.json 向后兼容。
|
|
17
|
+
* 原子写(tmp + rename)+ 损坏恢复,复用 OrderStore/WorkspaceStore 同一模式。
|
|
18
|
+
*/
|
|
19
|
+
export class AliasStore {
|
|
20
|
+
filePath;
|
|
21
|
+
data;
|
|
22
|
+
constructor(filePath) {
|
|
23
|
+
this.filePath = filePath ?? path.join(os.homedir(), '.lark-remote', 'aliases.json');
|
|
24
|
+
this.data = [];
|
|
25
|
+
this.load();
|
|
26
|
+
}
|
|
27
|
+
/** Reload data from disk (useful when another process may have written). */
|
|
28
|
+
reload() {
|
|
29
|
+
this.load();
|
|
30
|
+
}
|
|
31
|
+
load() {
|
|
32
|
+
const parsed = loadJsonFile(this.filePath, undefined);
|
|
33
|
+
if (!parsed || !Array.isArray(parsed)) {
|
|
34
|
+
this.data = [];
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const valid = [];
|
|
38
|
+
for (const e of parsed) {
|
|
39
|
+
if (e &&
|
|
40
|
+
typeof e.name === 'string' &&
|
|
41
|
+
typeof e.text === 'string' &&
|
|
42
|
+
typeof e.createdAt === 'string' &&
|
|
43
|
+
ALIAS_NAME_PATTERN.test(e.name) &&
|
|
44
|
+
e.name.length <= MAX_ALIAS_NAME_LENGTH) {
|
|
45
|
+
valid.push({
|
|
46
|
+
name: e.name,
|
|
47
|
+
text: e.text.slice(0, MAX_ALIAS_TEXT_LENGTH),
|
|
48
|
+
createdAt: e.createdAt,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
this.data = valid;
|
|
53
|
+
}
|
|
54
|
+
persist() {
|
|
55
|
+
atomicWriteJson(this.filePath, this.data);
|
|
56
|
+
}
|
|
57
|
+
has(name) {
|
|
58
|
+
return this.data.some((a) => a.name === name);
|
|
59
|
+
}
|
|
60
|
+
get(name) {
|
|
61
|
+
return this.data.find((a) => a.name === name);
|
|
62
|
+
}
|
|
63
|
+
list() {
|
|
64
|
+
return [...this.data];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 注册/更新别名(同名覆盖文本,保留 createdAt)。返回写入后的条目。
|
|
68
|
+
* 名称/文本/数量超限时抛错,由调用方提示用户。
|
|
69
|
+
*/
|
|
70
|
+
set(name, text) {
|
|
71
|
+
if (!ALIAS_NAME_PATTERN.test(name) || name.length > MAX_ALIAS_NAME_LENGTH) {
|
|
72
|
+
throw new Error(`别名名称只能包含字母/数字/下划线,不能数字开头,且不超过 ${MAX_ALIAS_NAME_LENGTH} 字符`);
|
|
73
|
+
}
|
|
74
|
+
if (text.length > MAX_ALIAS_TEXT_LENGTH) {
|
|
75
|
+
throw new Error(`别名文本超过 ${MAX_ALIAS_TEXT_LENGTH} 字符限制`);
|
|
76
|
+
}
|
|
77
|
+
if (!this.has(name) && this.data.length >= MAX_ALIASES) {
|
|
78
|
+
throw new Error(`别名数量已达上限 ${MAX_ALIASES}`);
|
|
79
|
+
}
|
|
80
|
+
const existing = this.get(name);
|
|
81
|
+
const entry = {
|
|
82
|
+
name,
|
|
83
|
+
text,
|
|
84
|
+
createdAt: existing?.createdAt ?? new Date().toISOString(),
|
|
85
|
+
};
|
|
86
|
+
const idx = this.data.findIndex((a) => a.name === name);
|
|
87
|
+
if (idx >= 0) {
|
|
88
|
+
this.data[idx] = entry;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
this.data.push(entry);
|
|
92
|
+
}
|
|
93
|
+
this.persist();
|
|
94
|
+
return entry;
|
|
95
|
+
}
|
|
96
|
+
/** 删除别名;不存在返回 false。 */
|
|
97
|
+
remove(name) {
|
|
98
|
+
const before = this.data.length;
|
|
99
|
+
this.data = this.data.filter((a) => a.name !== name);
|
|
100
|
+
if (this.data.length === before)
|
|
101
|
+
return false;
|
|
102
|
+
this.persist();
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=alias-store.js.map
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Atomically write string
|
|
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
4
|
*/
|
|
5
|
-
export declare function atomicWrite(filePath: string, content: string, encoding?: BufferEncoding): void;
|
|
5
|
+
export declare function atomicWrite(filePath: string, content: string | Buffer, encoding?: BufferEncoding): void;
|
|
6
6
|
/**
|
|
7
7
|
* Atomically write JSON data to a file via tmp + rename.
|
|
8
8
|
* Ensures a crash mid-write cannot leave a truncated file.
|
|
9
9
|
*/
|
|
10
10
|
export declare function atomicWriteJson(filePath: string, data: unknown): void;
|
|
11
|
+
/**
|
|
12
|
+
* 把已写好的文件原子移动到目标位置(rename 同设备即原子;EXDEV 跨设备
|
|
13
|
+
* 回退 copy + unlink)。失败时抛出,由调用方负责清理源文件(best-effort)。
|
|
14
|
+
* 用于入站媒体:下载已流式写入临时文件,落盘只需一次 move,不再复制进内存。
|
|
15
|
+
*/
|
|
16
|
+
export declare function atomicMoveFile(srcPath: string, destPath: string): void;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { silentlyUnlink } from '../common/fs.js';
|
|
3
4
|
/**
|
|
4
|
-
* Atomically write string
|
|
5
|
+
* Atomically write content (string or binary Buffer) to a file via tmp + rename.
|
|
5
6
|
* Falls back to copy+unlink when rename fails with EXDEV (cross-device).
|
|
6
7
|
*/
|
|
7
8
|
export function atomicWrite(filePath, content, encoding = 'utf-8') {
|
|
8
9
|
const dir = path.dirname(filePath);
|
|
9
10
|
fs.mkdirSync(dir, { recursive: true });
|
|
10
11
|
const tmpPath = filePath + '.tmp';
|
|
12
|
+
// Buffer 内容忽略 encoding(writeFileSync 对 Buffer 自动以二进制写出)。
|
|
11
13
|
fs.writeFileSync(tmpPath, content, encoding);
|
|
12
14
|
// fsync the tmp file before rename so its data is durable on disk;
|
|
13
15
|
// rename itself is only a metadata change, so without fsync a crash after
|
|
@@ -47,12 +49,7 @@ export function atomicWrite(filePath, content, encoding = 'utf-8') {
|
|
|
47
49
|
// Best-effort cleanup: on success the tmp was renamed away (ENOENT here,
|
|
48
50
|
// silently ignored); on EXDEV it was copied and now needs removal; on other
|
|
49
51
|
// errors we must not leak the tmp file.
|
|
50
|
-
|
|
51
|
-
fs.unlinkSync(tmpPath);
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
/* already moved or never created */
|
|
55
|
-
}
|
|
52
|
+
silentlyUnlink(tmpPath);
|
|
56
53
|
}
|
|
57
54
|
}
|
|
58
55
|
/**
|
|
@@ -62,4 +59,23 @@ export function atomicWrite(filePath, content, encoding = 'utf-8') {
|
|
|
62
59
|
export function atomicWriteJson(filePath, data) {
|
|
63
60
|
atomicWrite(filePath, JSON.stringify(data, null, 2));
|
|
64
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* 把已写好的文件原子移动到目标位置(rename 同设备即原子;EXDEV 跨设备
|
|
64
|
+
* 回退 copy + unlink)。失败时抛出,由调用方负责清理源文件(best-effort)。
|
|
65
|
+
* 用于入站媒体:下载已流式写入临时文件,落盘只需一次 move,不再复制进内存。
|
|
66
|
+
*/
|
|
67
|
+
export function atomicMoveFile(srcPath, destPath) {
|
|
68
|
+
try {
|
|
69
|
+
fs.renameSync(srcPath, destPath);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
if (err.code === 'EXDEV') {
|
|
73
|
+
fs.copyFileSync(srcPath, destPath);
|
|
74
|
+
silentlyUnlink(srcPath);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
65
81
|
//# sourceMappingURL=atomic-write.js.map
|
package/dist/restart.js
CHANGED
|
@@ -2,6 +2,7 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { getLogger } from './logger/index.js';
|
|
5
|
+
import { sleep } from './common/sleep.js';
|
|
5
6
|
/**
|
|
6
7
|
* Self-restart without an external watchdog/cron: the only process that can
|
|
7
8
|
* reliably start the successor is the dying bridge itself. The old process
|
|
@@ -66,7 +67,7 @@ export async function waitForPreviousInstance() {
|
|
|
66
67
|
getLogger().info(`[restart] previous instance pid=${pid} exited, continuing startup`);
|
|
67
68
|
return;
|
|
68
69
|
}
|
|
69
|
-
await
|
|
70
|
+
await sleep(POLL_MS);
|
|
70
71
|
}
|
|
71
72
|
getLogger().warn(`[restart] previous instance pid=${pid} still alive after ${WAIT_TIMEOUT_MS}ms, proceeding anyway`);
|
|
72
73
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card action payload assembly (entry layer).
|
|
3
|
+
*
|
|
4
|
+
* Feishu card callbacks arrive as a normalized `CardActionEvent` whose
|
|
5
|
+
* `action` only carries `value/tag/name/option/formValue` — the
|
|
6
|
+
* `@larksuite/channel` normalizer drops everything else (e.g. `input_value`,
|
|
7
|
+
* multi-select `options`). This module rebuilds the full payload that the
|
|
8
|
+
* router consumes, merging the button's behavior `value` with component
|
|
9
|
+
* out-of-band fields.
|
|
10
|
+
*
|
|
11
|
+
* Extracted from src/index.ts so the entry transform is behavior-testable
|
|
12
|
+
* (the main() wiring itself cannot be injected, see index-wiring.test.ts).
|
|
13
|
+
*/
|
|
14
|
+
import type { CardActionEvent } from '@larksuite/channel';
|
|
15
|
+
import type { CardActionPayload } from './index.js';
|
|
16
|
+
/**
|
|
17
|
+
* Build the full card action payload for router.handleCardAction.
|
|
18
|
+
*
|
|
19
|
+
* - `option`/`formValue`: select/form components deliver the user's choice
|
|
20
|
+
* out-of-band in `action.option`/`action.formValue`; button callbacks carry
|
|
21
|
+
* it in the behavior `value` instead (and `action.option` is absent). The
|
|
22
|
+
* behavior-value field must survive for buttons, so the component field
|
|
23
|
+
* only wins when it is actually present.
|
|
24
|
+
* - `inputValue`: CardKit 2.0 input submit-icon callbacks deliver the text in
|
|
25
|
+
* the raw event (`action.raw.action.input_value`); the SDK normalizer drops
|
|
26
|
+
* `action.action.input_value`.
|
|
27
|
+
* - `options`: reserved for multi-select components (future agent support);
|
|
28
|
+
* the SDK normalizer drops `action.options`, so it is read from raw.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildCardActionFullValue(actionValue: CardActionPayload, action: CardActionEvent): CardActionPayload;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card action payload assembly (entry layer).
|
|
3
|
+
*
|
|
4
|
+
* Feishu card callbacks arrive as a normalized `CardActionEvent` whose
|
|
5
|
+
* `action` only carries `value/tag/name/option/formValue` — the
|
|
6
|
+
* `@larksuite/channel` normalizer drops everything else (e.g. `input_value`,
|
|
7
|
+
* multi-select `options`). This module rebuilds the full payload that the
|
|
8
|
+
* router consumes, merging the button's behavior `value` with component
|
|
9
|
+
* out-of-band fields.
|
|
10
|
+
*
|
|
11
|
+
* Extracted from src/index.ts so the entry transform is behavior-testable
|
|
12
|
+
* (the main() wiring itself cannot be injected, see index-wiring.test.ts).
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Build the full card action payload for router.handleCardAction.
|
|
16
|
+
*
|
|
17
|
+
* - `option`/`formValue`: select/form components deliver the user's choice
|
|
18
|
+
* out-of-band in `action.option`/`action.formValue`; button callbacks carry
|
|
19
|
+
* it in the behavior `value` instead (and `action.option` is absent). The
|
|
20
|
+
* behavior-value field must survive for buttons, so the component field
|
|
21
|
+
* only wins when it is actually present.
|
|
22
|
+
* - `inputValue`: CardKit 2.0 input submit-icon callbacks deliver the text in
|
|
23
|
+
* the raw event (`action.raw.action.input_value`); the SDK normalizer drops
|
|
24
|
+
* `action.action.input_value`.
|
|
25
|
+
* - `options`: reserved for multi-select components (future agent support);
|
|
26
|
+
* the SDK normalizer drops `action.options`, so it is read from raw.
|
|
27
|
+
*/
|
|
28
|
+
export function buildCardActionFullValue(actionValue, action) {
|
|
29
|
+
return {
|
|
30
|
+
...actionValue,
|
|
31
|
+
option: action.action.option ?? actionValue.option,
|
|
32
|
+
formValue: action.action.formValue ?? actionValue.formValue,
|
|
33
|
+
options: action.raw?.action?.options ??
|
|
34
|
+
actionValue.options,
|
|
35
|
+
inputValue: action.raw?.action?.input_value ??
|
|
36
|
+
action.action.input_value,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=card-action-payload.js.map
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { markdownDiv } from '../card/collapsible.js';
|
|
10
10
|
import type { AgentSessionContentEvent } from '../runner/index.js';
|
|
11
|
+
import { formatUsageStats } from './utils.js';
|
|
12
|
+
type SessionUsageLike = Parameters<typeof formatUsageStats>[0];
|
|
11
13
|
/**
|
|
12
14
|
* Build a collapsible panel for a single session content event.
|
|
13
15
|
*
|
|
@@ -26,3 +28,64 @@ import type { AgentSessionContentEvent } from '../runner/index.js';
|
|
|
26
28
|
*/
|
|
27
29
|
export declare function sessionEventPanel(ev: AgentSessionContentEvent, index: number, totalEvents: number, tailExpandedCount?: number, agentKind?: string): object;
|
|
28
30
|
export { markdownDiv };
|
|
31
|
+
/**
|
|
32
|
+
* Build a CardKit 2.0 pagination bar (prev-button / page-label / next-button
|
|
33
|
+
* columns). Shared by /ls, /ws, /resume, /active, /order.
|
|
34
|
+
*
|
|
35
|
+
* Returns the `column_set` element (plus caller appends an `hr`). Each caller
|
|
36
|
+
* supplies its own callback cmd + extra value fields and label text (semantic
|
|
37
|
+
* differences are preserved, not silently unified).
|
|
38
|
+
*
|
|
39
|
+
* @param opts.cmd The pagination callback command (e.g. 'ls.page').
|
|
40
|
+
* @param opts.offset Current page offset.
|
|
41
|
+
* @param opts.pageSize Items per page.
|
|
42
|
+
* @param opts.total Total item count.
|
|
43
|
+
* @param opts.extra Extra callback value fields (e.g. path, agent, pageSize).
|
|
44
|
+
* @param opts.label Page-label markdown text (e.g. `**第 1/3 页**(共 30 项)`).
|
|
45
|
+
* @param opts.prevText / opts.nextText Button labels (default '⬅ 上一页' / '下一页 ➡').
|
|
46
|
+
*/
|
|
47
|
+
export declare function paginationBar(opts: {
|
|
48
|
+
cmd: string;
|
|
49
|
+
offset: number;
|
|
50
|
+
pageSize: number;
|
|
51
|
+
total: number;
|
|
52
|
+
extra?: Record<string, unknown>;
|
|
53
|
+
label: string;
|
|
54
|
+
prevText?: string;
|
|
55
|
+
nextText?: string;
|
|
56
|
+
}): object;
|
|
57
|
+
/**
|
|
58
|
+
* Build a session-history resume card (shared by auto-restore, /resume <id>,
|
|
59
|
+
* config-switch resume, and run-completion notification).
|
|
60
|
+
*
|
|
61
|
+
* Encapsulates the common structure: header (cwd + sessionId + displayTitle/
|
|
62
|
+
* recap sections) → optional hidden-count indicator → folded event panels (or
|
|
63
|
+
* empty placeholder) → usage → trailing-hr removal → action column_set → card
|
|
64
|
+
* shell. Callers pass their specific header text, card title, usage result
|
|
65
|
+
* label, placeholders, and already-built action buttons via opts.
|
|
66
|
+
*/
|
|
67
|
+
export declare function buildSessionHistoryCard(state: {
|
|
68
|
+
sessionId: string;
|
|
69
|
+
cwd: string;
|
|
70
|
+
displayTitle?: string;
|
|
71
|
+
aiTitle?: string;
|
|
72
|
+
recap?: string;
|
|
73
|
+
events: AgentSessionContentEvent[];
|
|
74
|
+
usage?: SessionUsageLike;
|
|
75
|
+
}, opts: {
|
|
76
|
+
agentKind: string;
|
|
77
|
+
/** Header first line, e.g. `📂 \`${cwd}\`\n已恢复最近会话: **id**`. */
|
|
78
|
+
headerText: string;
|
|
79
|
+
/** Card header title content. */
|
|
80
|
+
title: string;
|
|
81
|
+
/** Optional result label passed to formatUsageStats (showResult:true). */
|
|
82
|
+
usageResult?: string;
|
|
83
|
+
/** Empty-events placeholder (some callers show a hint instead of nothing). */
|
|
84
|
+
emptyPlaceholder?: string;
|
|
85
|
+
/** Hidden earlier-events count (auto-restore only). */
|
|
86
|
+
hiddenCount?: number;
|
|
87
|
+
/** Action buttons to render (already built, protocol-specific). */
|
|
88
|
+
actions?: object[];
|
|
89
|
+
/** Optional card header template (e.g. 'green' for completion cards). */
|
|
90
|
+
headerTemplate?: string;
|
|
91
|
+
}): object;
|
|
@@ -9,6 +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 './utils.js';
|
|
12
13
|
/**
|
|
13
14
|
* Build a collapsible panel for a single session content event.
|
|
14
15
|
*
|
|
@@ -62,4 +63,149 @@ function eventLabel(ev, agentKind) {
|
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
export { markdownDiv };
|
|
66
|
+
/**
|
|
67
|
+
* Build a CardKit 2.0 pagination bar (prev-button / page-label / next-button
|
|
68
|
+
* columns). Shared by /ls, /ws, /resume, /active, /order.
|
|
69
|
+
*
|
|
70
|
+
* Returns the `column_set` element (plus caller appends an `hr`). Each caller
|
|
71
|
+
* supplies its own callback cmd + extra value fields and label text (semantic
|
|
72
|
+
* differences are preserved, not silently unified).
|
|
73
|
+
*
|
|
74
|
+
* @param opts.cmd The pagination callback command (e.g. 'ls.page').
|
|
75
|
+
* @param opts.offset Current page offset.
|
|
76
|
+
* @param opts.pageSize Items per page.
|
|
77
|
+
* @param opts.total Total item count.
|
|
78
|
+
* @param opts.extra Extra callback value fields (e.g. path, agent, pageSize).
|
|
79
|
+
* @param opts.label Page-label markdown text (e.g. `**第 1/3 页**(共 30 项)`).
|
|
80
|
+
* @param opts.prevText / opts.nextText Button labels (default '⬅ 上一页' / '下一页 ➡').
|
|
81
|
+
*/
|
|
82
|
+
export function paginationBar(opts) {
|
|
83
|
+
const hasPrev = opts.offset > 0;
|
|
84
|
+
const hasNext = opts.offset + opts.pageSize < opts.total;
|
|
85
|
+
const prevText = opts.prevText ?? '⬅ 上一页';
|
|
86
|
+
const nextText = opts.nextText ?? '下一页 ➡';
|
|
87
|
+
const pageColumns = [];
|
|
88
|
+
if (hasPrev) {
|
|
89
|
+
pageColumns.push({
|
|
90
|
+
tag: 'column',
|
|
91
|
+
width: 'auto',
|
|
92
|
+
vertical_align: 'center',
|
|
93
|
+
elements: [
|
|
94
|
+
{
|
|
95
|
+
tag: 'button',
|
|
96
|
+
text: { tag: 'plain_text', content: prevText },
|
|
97
|
+
type: 'default',
|
|
98
|
+
size: 'small',
|
|
99
|
+
behaviors: [
|
|
100
|
+
{
|
|
101
|
+
type: 'callback',
|
|
102
|
+
value: { cmd: opts.cmd, offset: opts.offset - opts.pageSize, ...opts.extra },
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
pageColumns.push({
|
|
110
|
+
tag: 'column',
|
|
111
|
+
width: 'weighted',
|
|
112
|
+
weight: 1,
|
|
113
|
+
vertical_align: 'center',
|
|
114
|
+
elements: [{ tag: 'div', text: { tag: 'lark_md', content: opts.label } }],
|
|
115
|
+
});
|
|
116
|
+
if (hasNext) {
|
|
117
|
+
pageColumns.push({
|
|
118
|
+
tag: 'column',
|
|
119
|
+
width: 'auto',
|
|
120
|
+
vertical_align: 'center',
|
|
121
|
+
elements: [
|
|
122
|
+
{
|
|
123
|
+
tag: 'button',
|
|
124
|
+
text: { tag: 'plain_text', content: nextText },
|
|
125
|
+
type: 'default',
|
|
126
|
+
size: 'small',
|
|
127
|
+
behaviors: [
|
|
128
|
+
{
|
|
129
|
+
type: 'callback',
|
|
130
|
+
value: { cmd: opts.cmd, offset: opts.offset + opts.pageSize, ...opts.extra },
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return { tag: 'column_set', columns: pageColumns };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Build a session-history resume card (shared by auto-restore, /resume <id>,
|
|
141
|
+
* config-switch resume, and run-completion notification).
|
|
142
|
+
*
|
|
143
|
+
* Encapsulates the common structure: header (cwd + sessionId + displayTitle/
|
|
144
|
+
* recap sections) → optional hidden-count indicator → folded event panels (or
|
|
145
|
+
* empty placeholder) → usage → trailing-hr removal → action column_set → card
|
|
146
|
+
* shell. Callers pass their specific header text, card title, usage result
|
|
147
|
+
* label, placeholders, and already-built action buttons via opts.
|
|
148
|
+
*/
|
|
149
|
+
export function buildSessionHistoryCard(state, opts) {
|
|
150
|
+
const { displayTitle, aiTitle, recap, events, usage } = state;
|
|
151
|
+
let header = opts.headerText;
|
|
152
|
+
const sections = [];
|
|
153
|
+
// Reader 层不再截断 displayTitle/summary;统一在消费侧这里截到 200 字符。
|
|
154
|
+
if (displayTitle) {
|
|
155
|
+
const label = aiTitle ? 'AI 标题' : '最近输入';
|
|
156
|
+
const preview = displayTitle.length > 200 ? displayTitle.slice(0, 197) + '...' : displayTitle;
|
|
157
|
+
sections.push(`🏷️ **${label}**\n${preview}`);
|
|
158
|
+
}
|
|
159
|
+
if (recap) {
|
|
160
|
+
const recapPreview = recap.length > 200 ? recap.slice(0, 197) + '...' : recap;
|
|
161
|
+
sections.push(`📝 **Recap**\n${recapPreview}`);
|
|
162
|
+
}
|
|
163
|
+
if (sections.length > 0) {
|
|
164
|
+
header += '\n\n' + sections.join('\n\n──\n\n');
|
|
165
|
+
}
|
|
166
|
+
const elements = [markdownDiv(header), { tag: 'hr' }];
|
|
167
|
+
if (opts.hiddenCount && opts.hiddenCount > 0) {
|
|
168
|
+
elements.push(markdownDiv(`📜 还有 ${opts.hiddenCount} 个更早的事件未显示`));
|
|
169
|
+
}
|
|
170
|
+
if (events.length === 0 && opts.emptyPlaceholder) {
|
|
171
|
+
elements.push(markdownDiv(opts.emptyPlaceholder));
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
events.forEach((ev, i) => {
|
|
175
|
+
elements.push(sessionEventPanel(ev, i, events.length, 2, opts.agentKind));
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
if (usage) {
|
|
179
|
+
const usageStr = opts.usageResult
|
|
180
|
+
? formatUsageStats(usage, { showResult: true, result: opts.usageResult })
|
|
181
|
+
: formatUsageStats(usage);
|
|
182
|
+
elements.push(markdownDiv(usageStr));
|
|
183
|
+
}
|
|
184
|
+
// Remove trailing hr
|
|
185
|
+
if (elements.length > 0 && elements[elements.length - 1].tag === 'hr') {
|
|
186
|
+
elements.pop();
|
|
187
|
+
}
|
|
188
|
+
if (opts.actions && opts.actions.length > 0) {
|
|
189
|
+
elements.push({
|
|
190
|
+
tag: 'column_set',
|
|
191
|
+
columns: opts.actions.map((btn) => ({
|
|
192
|
+
tag: 'column',
|
|
193
|
+
width: 'auto',
|
|
194
|
+
elements: [btn],
|
|
195
|
+
})),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
schema: '2.0',
|
|
200
|
+
config: { wide_screen_mode: true },
|
|
201
|
+
header: {
|
|
202
|
+
...(opts.headerTemplate ? { template: opts.headerTemplate } : {}),
|
|
203
|
+
title: {
|
|
204
|
+
tag: 'plain_text',
|
|
205
|
+
content: opts.title,
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
body: { elements },
|
|
209
|
+
};
|
|
210
|
+
}
|
|
65
211
|
//# sourceMappingURL=card-helpers.js.map
|
|
@@ -4,7 +4,24 @@
|
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
-
import { MODEL_ID_TO_ALIAS, MODEL_ALIAS_TO_ID, getModelOptionsFromSettings, CLAUDE_EFFORTS, } from '../../config/index.js';
|
|
7
|
+
import { MODEL_ID_TO_ALIAS, MODEL_ALIAS_TO_ID, getModelOptionsFromSettings, CLAUDE_EFFORTS, CLAUDE_PERMISSION_MODES, } from '../../config/index.js';
|
|
8
|
+
/** Claude 官方 --permission-mode 中文注释(value 走 CLAUDE_PERMISSION_MODES SSOT)。 */
|
|
9
|
+
const PERMISSION_MODE_LABELS = {
|
|
10
|
+
default: '默认(敏感操作逐项询问)',
|
|
11
|
+
acceptEdits: '自动接受文件编辑',
|
|
12
|
+
auto: '自动放行低风险操作',
|
|
13
|
+
bypassPermissions: '跳过所有权限检查',
|
|
14
|
+
dontAsk: '仅预先批准的工具',
|
|
15
|
+
plan: '计划模式(只读探索)',
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* 下拉选项排除 manual:manual 是 default 的别名(claude CLI 等价),
|
|
19
|
+
* 只保留 default 一个,避免下拉出现两个等价项。schema 仍接受 manual(旧配置向后兼容)。
|
|
20
|
+
*/
|
|
21
|
+
const PERMISSION_MODE_OPTIONS = CLAUDE_PERMISSION_MODES.filter((m) => m !== 'manual').map((m) => ({
|
|
22
|
+
text: `${m}(${PERMISSION_MODE_LABELS[m] ?? ''})`,
|
|
23
|
+
value: m,
|
|
24
|
+
}));
|
|
8
25
|
export class ClaudeConfigBuilder {
|
|
9
26
|
/** Find Claude settings.json path (for dynamic model reading). */
|
|
10
27
|
findSettingsPath() {
|
|
@@ -61,6 +78,18 @@ export class ClaudeConfigBuilder {
|
|
|
61
78
|
options: CLAUDE_EFFORTS,
|
|
62
79
|
currentValue: currentEffort,
|
|
63
80
|
});
|
|
81
|
+
// 权限模式(Claude 官方 --permission-mode 枚举;bypassPermissions 为 schema 默认,
|
|
82
|
+
// 保持旧行为无审批卡,配置为其他值后激活交互式审批)
|
|
83
|
+
const currentPermissionMode = displayConfig.claude?.permissionMode ?? 'bypassPermissions';
|
|
84
|
+
// manual 是 default 的别名:旧配置按 default 显示(下拉无 manual 项,避免空选)
|
|
85
|
+
const displayedPermissionMode = currentPermissionMode === 'manual' ? 'default' : currentPermissionMode;
|
|
86
|
+
fields.push({
|
|
87
|
+
key: 'claude.permissionMode',
|
|
88
|
+
label: '权限模式',
|
|
89
|
+
type: 'select',
|
|
90
|
+
options: PERMISSION_MODE_OPTIONS,
|
|
91
|
+
currentValue: displayedPermissionMode,
|
|
92
|
+
});
|
|
64
93
|
return fields;
|
|
65
94
|
}
|
|
66
95
|
handleFieldChange(key, _value, _config) {
|