@modelzen/feishu-codex-bridge 0.6.3 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +559 -85
- package/dist/index.d.ts +8 -0
- package/dist/index.js +12 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ feishu-codex-bridge web # 2. 打开本机网页控制台
|
|
|
50
50
|
- **免 @ + 自主目标**:话题 / 单会话群里可直接说话不必每次 @;`/goal <目标>` 让它自主多轮干到完成。
|
|
51
51
|
- **多模态**:消息里直接发图片(读图)、发文件附件(下载到本地交给 agent 打开分析)。
|
|
52
52
|
- **☕ 咖啡一下(反向桥)**:离开电脑时,把你本机正在跑的 Claude Code / Codex CLI 的「需要审批 / 提问 / 任务完成」接管到飞书私聊 —— 在手机上点确认 / 回答它就继续,机器保持不睡。
|
|
53
|
-
- **文档评论回复**:在飞书云文档(doc / docx / sheet /
|
|
53
|
+
- **文档评论回复**:在飞书云文档(doc / docx / sheet / bitable 多维表格,含 wiki)的评论里 @ 机器人,它读评论、跑 agent、把答案回到同一条评论线程。
|
|
54
54
|
- **双控制台**:私聊机器人弹交互菜单(新建项目 / 设置 / 用量 / 诊断 / 重连);网页控制台还能管后台服务、看实时日志、扫码加机器人。
|
|
55
55
|
- **多飞书机器人**:一台机器注册多个机器人、可同时连接,各自项目 / 会话独立。
|
|
56
56
|
- **三档权限沙箱**:每个项目可设「只读 / 读写 / 完全访问」,由 OS 沙箱强制(macOS / 原生 Windows)。
|
package/dist/cli.js
CHANGED
|
@@ -57,6 +57,18 @@ var init_paths = __esm({
|
|
|
57
57
|
get processesFile() {
|
|
58
58
|
return join(currentBotDir, "processes.json");
|
|
59
59
|
},
|
|
60
|
+
/** 云文档评论 @bot 的可编辑提示词 master 文件(当前 bot)。用户直接编辑这一份,
|
|
61
|
+
* 桥在每条评论运行前把它同步进该文档的评论工作目录(AGENTS.md / CLAUDE.md)。
|
|
62
|
+
* 首次缺失时由 bot/comments.ts 用内置默认模板自动落地。 */
|
|
63
|
+
get commentInstructionsFile() {
|
|
64
|
+
return join(currentBotDir, "comment-instructions.md");
|
|
65
|
+
},
|
|
66
|
+
/** 评论工作目录根(**当前 bot**):每个被评论文档一个 `comment-<type>-<token>` 子目录,
|
|
67
|
+
* 放同步进去的 AGENTS.md / CLAUDE.md。放 per-bot 目录下,与其它 bot 隔离——否则
|
|
68
|
+
* 编辑提示词时的「全量同步」会越界改到别的 bot 的评论目录。 */
|
|
69
|
+
get commentsRootDir() {
|
|
70
|
+
return join(currentBotDir, "comments");
|
|
71
|
+
},
|
|
60
72
|
/**
|
|
61
73
|
* Local CLI hook IPC endpoint for the current bot. macOS/Linux use a Unix
|
|
62
74
|
* domain socket file; Windows has none, so Node maps a `\\.\pipe\…` path to a
|
|
@@ -218,6 +230,9 @@ function resolveCliBridgeTarget(cfg) {
|
|
|
218
230
|
function canEnableCliBridge(cfg) {
|
|
219
231
|
return resolveCliBridgeTarget(cfg) ? { ok: true } : { ok: false, reason: "missing_owner" };
|
|
220
232
|
}
|
|
233
|
+
function getCommentsConfig(cfg) {
|
|
234
|
+
return cfg.preferences?.comments ?? {};
|
|
235
|
+
}
|
|
221
236
|
var RUN_IDLE_TIMEOUT_MIN_SEC, RUN_IDLE_TIMEOUT_MAX_SEC;
|
|
222
237
|
var init_schema = __esm({
|
|
223
238
|
"src/config/schema.ts"() {
|
|
@@ -630,6 +645,7 @@ var init_logger = __esm({
|
|
|
630
645
|
function card(elements, opts = {}) {
|
|
631
646
|
const config = { update_multi: true };
|
|
632
647
|
if (opts.forward === false) config.enable_forward = false;
|
|
648
|
+
if (opts.widthMode) config.width_mode = opts.widthMode;
|
|
633
649
|
if (opts.streaming) {
|
|
634
650
|
config.streaming_mode = true;
|
|
635
651
|
config.streaming_config = {
|
|
@@ -772,9 +788,13 @@ function input(opts) {
|
|
|
772
788
|
return {
|
|
773
789
|
tag: "input",
|
|
774
790
|
name: opts.name,
|
|
791
|
+
...opts.inputType ? { input_type: opts.inputType } : {},
|
|
792
|
+
...opts.rows ? { rows: opts.rows, auto_resize: true } : {},
|
|
793
|
+
...opts.width !== void 0 ? { width: opts.width } : {},
|
|
775
794
|
...opts.label ? { label: { tag: "plain_text", content: opts.label } } : {},
|
|
776
795
|
...opts.placeholder ? { placeholder: { tag: "plain_text", content: opts.placeholder } } : {},
|
|
777
796
|
...opts.value ? { default_value: opts.value } : {},
|
|
797
|
+
...opts.maxLength ? { max_length: opts.maxLength } : {},
|
|
778
798
|
required: Boolean(opts.required)
|
|
779
799
|
};
|
|
780
800
|
}
|
|
@@ -1086,7 +1106,7 @@ ${lines || "\uFF08\u65E0\uFF09"}`));
|
|
|
1086
1106
|
return card(elements, { forward: false });
|
|
1087
1107
|
}
|
|
1088
1108
|
function buildCliBridgeTaskCompletionCard(input2) {
|
|
1089
|
-
const verb = input2.replyDoneAt ? "\u2705 \u5DF2\
|
|
1109
|
+
const verb = input2.replyDoneAt ? "\u2705 \u5DF2\u6536\u5DE5" : input2.status === "failed" ? "\u274C \u4EFB\u52A1\u5931\u8D25" : pickCopy(COPY.completion, input2.id || input2.cwd);
|
|
1090
1110
|
const elements = [
|
|
1091
1111
|
titleEl(verb),
|
|
1092
1112
|
metaLine(input2.source, input2.cwd)
|
|
@@ -1103,7 +1123,7 @@ ${codeBlock(chunk, "text")}`));
|
|
|
1103
1123
|
}
|
|
1104
1124
|
if (input2.replyEnabled) {
|
|
1105
1125
|
const expiresAt = input2.replyExpiresAt ? `\uFF08\u6709\u6548\u671F\u81F3 ${new Date(input2.replyExpiresAt).toLocaleString("zh-CN")}\uFF09` : "";
|
|
1106
|
-
elements.push(actions([button("\
|
|
1126
|
+
elements.push(actions([button("\u2705 \u6536\u5DE5", { a: CLI.taskCompletionDone, id: input2.id }, "primary")]));
|
|
1107
1127
|
elements.push(note(pickCopy(COPY.footerReply, input2.id || input2.cwd) + expiresAt));
|
|
1108
1128
|
} else if (input2.replyDoneAt) {
|
|
1109
1129
|
elements.push(actions([disabledButton("\u2705 \u5DF2\u5B8C\u6210", "primary")]));
|
|
@@ -1170,8 +1190,8 @@ var init_cards2 = __esm({
|
|
|
1170
1190
|
"\u4EBA\u5728\u684C\u524D\u6211\u5C31\u95ED\u5634,\u4E00\u5207\u56DE\u5F52\u7EC8\u7AEF\u3002"
|
|
1171
1191
|
],
|
|
1172
1192
|
footerReply: [
|
|
1173
|
-
"\u{1F4AC} \u76F4\u63A5\u56DE\u590D\u8FD9\u6761\u6D88\u606F,\u5C31\u80FD\u63A5\u7740\u652F\u4F7F\u5B83\u5E72\u6D3B;\u6216\u70B9\u300C\
|
|
1174
|
-
"\u{1F4AC} \u56DE\u6211\u4E00\u53E5\u8BDD,\u5B83\u7ACB\u523B\u63A5\u7740\u8DD1;\u4E0D\u60F3\u7EE7\u7EED\u5C31\u70B9\u300C\
|
|
1193
|
+
"\u{1F4AC} \u76F4\u63A5\u56DE\u590D\u8FD9\u6761\u6D88\u606F,\u5C31\u80FD\u63A5\u7740\u652F\u4F7F\u5B83\u5E72\u6D3B;\u6216\u70B9\u300C\u6536\u5DE5\u300D\u8BA9\u5B83\u9000\u51FA\u3002",
|
|
1194
|
+
"\u{1F4AC} \u56DE\u6211\u4E00\u53E5\u8BDD,\u5B83\u7ACB\u523B\u63A5\u7740\u8DD1;\u4E0D\u60F3\u7EE7\u7EED\u5C31\u70B9\u300C\u6536\u5DE5\u300D\u3002"
|
|
1175
1195
|
]
|
|
1176
1196
|
};
|
|
1177
1197
|
}
|
|
@@ -1995,13 +2015,13 @@ __export(cli_bridge_exports, {
|
|
|
1995
2015
|
selectCliBridgeHookBot: () => selectCliBridgeHookBot,
|
|
1996
2016
|
shouldStartCliBridge: () => shouldStartCliBridge
|
|
1997
2017
|
});
|
|
1998
|
-
import { join as
|
|
2018
|
+
import { join as join20 } from "path";
|
|
1999
2019
|
async function selectCliBridgeHookBot(reg, opts = {}) {
|
|
2000
2020
|
const requested = opts.requested?.trim();
|
|
2001
2021
|
if (requested) {
|
|
2002
2022
|
return findBot(reg, requested) ?? { name: requested, appId: requested, tenant: "feishu", createdAt: 0 };
|
|
2003
2023
|
}
|
|
2004
|
-
const loadConfigForBot = opts.loadConfigForBot ?? ((appId) => loadConfig(
|
|
2024
|
+
const loadConfigForBot = opts.loadConfigForBot ?? ((appId) => loadConfig(join20(botDir(appId), "config.json")));
|
|
2005
2025
|
const current = currentBot(reg);
|
|
2006
2026
|
const active = activeBots(reg);
|
|
2007
2027
|
const candidates = active.length > 0 ? active : current ? [current] : reg.bots;
|
|
@@ -3106,14 +3126,19 @@ function mapNotification(n, ctx) {
|
|
|
3106
3126
|
function mapItemStart(item, ctx) {
|
|
3107
3127
|
switch (item.type) {
|
|
3108
3128
|
case "commandExecution":
|
|
3109
|
-
return { type: "tool_use", itemId: item.id, title: item.command, detail: String(item.cwd) };
|
|
3129
|
+
return { type: "tool_use", itemId: item.id, title: item.command, detail: String(item.cwd), kind: "command" };
|
|
3110
3130
|
case "fileChange":
|
|
3111
|
-
return { type: "tool_use", itemId: item.id, title: fileChangeTitle(item.changes, ctx?.cwd) };
|
|
3131
|
+
return { type: "tool_use", itemId: item.id, title: fileChangeTitle(item.changes, ctx?.cwd), kind: "file" };
|
|
3112
3132
|
case "webSearch":
|
|
3113
|
-
return {
|
|
3133
|
+
return {
|
|
3134
|
+
type: "tool_use",
|
|
3135
|
+
itemId: item.id,
|
|
3136
|
+
title: item.query ? `\u8054\u7F51\u641C\u7D22\uFF1A${item.query}` : "\u8054\u7F51\u641C\u7D22",
|
|
3137
|
+
kind: "search"
|
|
3138
|
+
};
|
|
3114
3139
|
case "mcpToolCall":
|
|
3115
3140
|
case "dynamicToolCall":
|
|
3116
|
-
return { type: "tool_use", itemId: item.id, title: "\u5DE5\u5177\u8C03\u7528" };
|
|
3141
|
+
return { type: "tool_use", itemId: item.id, title: "\u5DE5\u5177\u8C03\u7528", kind: "tool" };
|
|
3117
3142
|
default:
|
|
3118
3143
|
return null;
|
|
3119
3144
|
}
|
|
@@ -3783,7 +3808,8 @@ function createTurnMapper(ctx = {}) {
|
|
|
3783
3808
|
type: "tool_use",
|
|
3784
3809
|
itemId: String(b.id ?? `tool${++blockSeq}`),
|
|
3785
3810
|
title: toolTitle(b.name ?? "\u5DE5\u5177", b.input ?? {}, ctx.cwd),
|
|
3786
|
-
detail: toolDetail(b.name ?? "", b.input ?? {})
|
|
3811
|
+
detail: toolDetail(b.name ?? "", b.input ?? {}),
|
|
3812
|
+
kind: toolKind(b.name ?? "")
|
|
3787
3813
|
});
|
|
3788
3814
|
}
|
|
3789
3815
|
}
|
|
@@ -3902,6 +3928,26 @@ function toolTitle(name, input2, cwd) {
|
|
|
3902
3928
|
return name || "\u5DE5\u5177\u8C03\u7528";
|
|
3903
3929
|
}
|
|
3904
3930
|
}
|
|
3931
|
+
function toolKind(name) {
|
|
3932
|
+
switch (name) {
|
|
3933
|
+
case "Bash":
|
|
3934
|
+
case "BashOutput":
|
|
3935
|
+
return "command";
|
|
3936
|
+
case "Read":
|
|
3937
|
+
case "Write":
|
|
3938
|
+
case "Edit":
|
|
3939
|
+
case "MultiEdit":
|
|
3940
|
+
case "NotebookEdit":
|
|
3941
|
+
return "file";
|
|
3942
|
+
case "Glob":
|
|
3943
|
+
case "Grep":
|
|
3944
|
+
case "WebFetch":
|
|
3945
|
+
case "WebSearch":
|
|
3946
|
+
return "search";
|
|
3947
|
+
default:
|
|
3948
|
+
return "tool";
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3905
3951
|
function toolDetail(name, input2) {
|
|
3906
3952
|
if (name === "Bash" && typeof input2.description === "string" && input2.description) {
|
|
3907
3953
|
return input2.description;
|
|
@@ -4166,6 +4212,8 @@ var ClaudeAgentThread = class {
|
|
|
4166
4212
|
...toSdkEffort(cfg.effort) ? { effort: toSdkEffort(cfg.effort) } : {},
|
|
4167
4213
|
...cfg.resume ? { resume: cfg.sessionId } : { sessionId: cfg.sessionId },
|
|
4168
4214
|
...cfg.systemPromptAppend ? { systemPrompt: { type: "preset", preset: "claude_code", append: cfg.systemPromptAppend } } : {},
|
|
4215
|
+
...cfg.settingSources ? { settingSources: cfg.settingSources } : {},
|
|
4216
|
+
...cfg.env ? { env: cfg.env } : {},
|
|
4169
4217
|
// permission tier (permissionMode / sandbox / canUseTool / disallowedTools)
|
|
4170
4218
|
...cfg.permission,
|
|
4171
4219
|
stderr: (d) => {
|
|
@@ -4449,6 +4497,13 @@ var ClaudeAgentThread = class {
|
|
|
4449
4497
|
|
|
4450
4498
|
// src/agent/claude-agent/backend.ts
|
|
4451
4499
|
var SDK_PKG = "@anthropic-ai/claude-agent-sdk";
|
|
4500
|
+
var BRIDGE_SETTING_SOURCES = ["user", "project"];
|
|
4501
|
+
function bridgeClaudeEnv() {
|
|
4502
|
+
const base = {};
|
|
4503
|
+
for (const [k2, v] of Object.entries(process.env)) if (typeof v === "string") base[k2] = v;
|
|
4504
|
+
base.FEISHU_CODEX_BRIDGE = "1";
|
|
4505
|
+
return base;
|
|
4506
|
+
}
|
|
4452
4507
|
var sdkPromise;
|
|
4453
4508
|
function loadSdk() {
|
|
4454
4509
|
sdkPromise ??= loadBackendDep(SDK_PKG);
|
|
@@ -4531,6 +4586,8 @@ var ClaudeAgentBackend = class {
|
|
|
4531
4586
|
effort: opts.effort,
|
|
4532
4587
|
permission: permissionOptions(opts.mode, opts.network, opts.cwd),
|
|
4533
4588
|
systemPromptAppend: BRIDGE_DEVELOPER_INSTRUCTIONS,
|
|
4589
|
+
settingSources: BRIDGE_SETTING_SOURCES,
|
|
4590
|
+
env: bridgeClaudeEnv(),
|
|
4534
4591
|
query: sdk.query
|
|
4535
4592
|
});
|
|
4536
4593
|
}
|
|
@@ -4544,6 +4601,8 @@ var ClaudeAgentBackend = class {
|
|
|
4544
4601
|
effort: opts.effort,
|
|
4545
4602
|
permission: permissionOptions(opts.mode, opts.network, opts.cwd),
|
|
4546
4603
|
systemPromptAppend: BRIDGE_DEVELOPER_INSTRUCTIONS,
|
|
4604
|
+
settingSources: BRIDGE_SETTING_SOURCES,
|
|
4605
|
+
env: bridgeClaudeEnv(),
|
|
4547
4606
|
query: sdk.query
|
|
4548
4607
|
});
|
|
4549
4608
|
}
|
|
@@ -5633,6 +5692,8 @@ var DM = {
|
|
|
5633
5692
|
joinGroupSubmit: "dm.joinGroup.submit",
|
|
5634
5693
|
projects: "dm.projects",
|
|
5635
5694
|
settings: "dm.settings",
|
|
5695
|
+
// ☕ 咖啡一下(离开接管):从全局设置卡进入的二级卡片(仿云文档评论那样的子卡入口)
|
|
5696
|
+
coffeeSettings: "dm.coffee.settings",
|
|
5636
5697
|
doctor: "dm.doctor",
|
|
5637
5698
|
reconnect: "dm.reconnect",
|
|
5638
5699
|
update: "dm.update",
|
|
@@ -5675,9 +5736,16 @@ var DM = {
|
|
|
5675
5736
|
modelDefaultSubmit: "dm.proj.modelDefault.submit",
|
|
5676
5737
|
// 🔐 权限:codex 沙箱档位(管理员档 + 普通用户档)+ 联网,做成下拉表单(选+提交)
|
|
5677
5738
|
permission: "dm.proj.perm",
|
|
5678
|
-
permissionSubmit: "dm.proj.perm.submit"
|
|
5739
|
+
permissionSubmit: "dm.proj.perm.submit",
|
|
5679
5740
|
// 🧠 后端 backend/backendSubmit 已移除:后端改为「创建时选定、运行时固定、不支持切换」,
|
|
5680
5741
|
// 新建卡选后端见 newProjectSubmit;项目设置卡只读展示。
|
|
5742
|
+
// 📝 云文档评论 @bot 全局设置:后端按钮(级联)→模型/强度下拉表单提交;提示词在卡内编辑。
|
|
5743
|
+
commentSettings: "dm.comment.settings",
|
|
5744
|
+
commentSetBackend: "dm.comment.setBackend",
|
|
5745
|
+
commentSubmit: "dm.comment.submit",
|
|
5746
|
+
commentEditPrompt: "dm.comment.editPrompt",
|
|
5747
|
+
commentPromptSubmit: "dm.comment.promptSubmit",
|
|
5748
|
+
commentResetPrompt: "dm.comment.resetPrompt"
|
|
5681
5749
|
};
|
|
5682
5750
|
var GS = {
|
|
5683
5751
|
setNoMention: "gs.noMention",
|
|
@@ -6149,7 +6217,7 @@ function settingItem(name, desc, actionId, current, opts) {
|
|
|
6149
6217
|
actions(opts.map((o) => button(o.label, { a: actionId, v: o.value }, o.value === current ? "primary" : "default")))
|
|
6150
6218
|
];
|
|
6151
6219
|
}
|
|
6152
|
-
function buildSettingsCard(cfg
|
|
6220
|
+
function buildSettingsCard(cfg) {
|
|
6153
6221
|
const watchdogSec = cfg.preferences?.runIdleTimeoutSeconds ?? 120;
|
|
6154
6222
|
return card(
|
|
6155
6223
|
[
|
|
@@ -6207,13 +6275,169 @@ function buildSettingsCard(cfg, localAgents = []) {
|
|
|
6207
6275
|
{ label: "20", value: "20" }
|
|
6208
6276
|
]
|
|
6209
6277
|
),
|
|
6210
|
-
|
|
6278
|
+
hr(),
|
|
6279
|
+
settingSection("\u2615 \u5496\u5561\u4E00\u4E0B"),
|
|
6280
|
+
note("\u53BB\u5012\u676F\u5496\u5561\u7684\u5DE5\u592B\uFF0C\u6211\u66FF\u4F60\u76EF\u7740\u672C\u673A\u7684 Claude Code / Codex\u2014\u2014\u5B83\u8981\u5BA1\u6279\u3001\u8981\u95EE\u4F60\u3001\u6216\u8DD1\u5B8C\u4E86\uFF0C\u90FD\u63A8\u5230\u8FD9\u4E2A\u79C1\u804A\u3002\u542B\u901A\u77E5\u8303\u56F4\u3001\u8F6C\u53D1\u540E\u7AEF\u3001\u79BB\u5F00\u4FDD\u6D3B\u3001hooks \u4FEE\u590D\u3002"),
|
|
6281
|
+
actions([button("\u8BBE\u7F6E\u5496\u5561\u4E00\u4E0B / \u901A\u77E5 / \u4FDD\u6D3B / hooks", { a: DM.coffeeSettings }, "primary")]),
|
|
6282
|
+
hr(),
|
|
6283
|
+
settingSection("\u{1F4DD} \u4E91\u6587\u6863\u8BC4\u8BBA"),
|
|
6284
|
+
note("\u5728\u98DE\u4E66\u4E91\u6587\u6863\uFF08\u6587\u6863 / \u8868\u683C / \u591A\u7EF4\u8868\u683C\uFF0C\u542B wiki\uFF09\u7684\u8BC4\u8BBA\u91CC @\u6211\uFF0C\u6211\u8BFB\u8BC4\u8BBA\u3001\u8DD1 agent\u3001\u628A\u7B54\u6848\u8D34\u56DE\u8BC4\u8BBA\u3002\u53EF\u8BBE\u7F6E\u8BC4\u8BBA\u54CD\u5E94\u7528\u7684\u540E\u7AEF agent / \u6A21\u578B / \u63A8\u7406\u5F3A\u5EA6\uFF0C\u4EE5\u53CA\u81EA\u5B9A\u4E49\u63D0\u793A\u8BCD\uFF08\u5168\u5C40\uFF0C\u4E0D\u5206\u9879\u76EE\uFF1B\u4EC5\u7BA1\u7406\u5458\u53EF @\uFF09\u3002"),
|
|
6285
|
+
actions([button("\u8BBE\u7F6E\u540E\u7AEF / \u6A21\u578B / \u5F3A\u5EA6 / \u63D0\u793A\u8BCD", { a: DM.commentSettings }, "primary")]),
|
|
6211
6286
|
hr(),
|
|
6212
6287
|
actions([button("\u{1F46E} \u7BA1\u7406\u5458", { a: DM.admins }), button("\u2B05\uFE0F \u83DC\u5355", { a: DM.menu })])
|
|
6213
6288
|
],
|
|
6214
6289
|
{ header: { title: "\u2699\uFE0F \u5168\u5C40\u8BBE\u7F6E", template: "blue" } }
|
|
6215
6290
|
);
|
|
6216
6291
|
}
|
|
6292
|
+
function buildCoffeeSettingsCard(section) {
|
|
6293
|
+
return card(
|
|
6294
|
+
[
|
|
6295
|
+
...section.slice(1),
|
|
6296
|
+
// 去掉为「内联进主卡」而加的开头 hr()
|
|
6297
|
+
hr(),
|
|
6298
|
+
actions([button("\u2B05\uFE0F \u8FD4\u56DE\u8BBE\u7F6E", { a: DM.settings })])
|
|
6299
|
+
],
|
|
6300
|
+
{ header: { title: "\u2615 \u5496\u5561\u4E00\u4E0B", template: "blue" } }
|
|
6301
|
+
);
|
|
6302
|
+
}
|
|
6303
|
+
var LARK_CLI_DOC_URL = "https://bytedance.larkoffice.com/wiki/ILuTww7Xcimb6GkhH0mcK2f4nS7";
|
|
6304
|
+
function buildCommentSettingsCard(cfg, backendOptions, models, notice) {
|
|
6305
|
+
const comments = cfg.preferences?.comments ?? {};
|
|
6306
|
+
const visible = models.filter((m) => !m.hidden);
|
|
6307
|
+
const curBackend = comments.backend && backendOptions.some((b) => b.id === comments.backend) ? comments.backend : backendOptions.find((b) => b.id === DEFAULT_BACKEND_ID)?.id ?? backendOptions[0]?.id ?? DEFAULT_BACKEND_ID;
|
|
6308
|
+
const explicit = comments.model ? visible.find((m) => m.id === comments.model) : void 0;
|
|
6309
|
+
const curModel = explicit ?? visible.find((m) => m.isDefault) ?? visible[0];
|
|
6310
|
+
const unionEfforts = EFFORT_ORDER.filter((e) => visible.some((m) => (m.supportedEfforts ?? []).includes(e)));
|
|
6311
|
+
const curEffort = comments.effort && (curModel?.supportedEfforts ?? []).includes(comments.effort) ? comments.effort : curModel?.defaultEffort;
|
|
6312
|
+
const canPickModel = visible.length > 1;
|
|
6313
|
+
const canPickEffort = unionEfforts.length > 0;
|
|
6314
|
+
const els = [
|
|
6315
|
+
...notice ? [md(notice)] : [],
|
|
6316
|
+
md("**\u{1F4DD} \u4E91\u6587\u6863\u8BC4\u8BBA @bot**"),
|
|
6317
|
+
note("\u8BC4\u8BBA\u91CC @\u6211\u65F6\u7528\u7684\u540E\u7AEF / \u6A21\u578B / \u63A8\u7406\u5F3A\u5EA6\u3002\u53EA\u5F71\u54CD\u4E4B\u540E\u65B0\u5EFA\u7684\u8BC4\u8BBA\u3002"),
|
|
6318
|
+
hr()
|
|
6319
|
+
];
|
|
6320
|
+
if (backendOptions.length > 1) {
|
|
6321
|
+
els.push(
|
|
6322
|
+
md("\u{1F9E0} **\u540E\u7AEF**"),
|
|
6323
|
+
actions(
|
|
6324
|
+
backendOptions.map(
|
|
6325
|
+
(b) => button(b.label, { a: DM.commentSetBackend, v: b.id }, b.id === curBackend ? "primary" : "default")
|
|
6326
|
+
)
|
|
6327
|
+
)
|
|
6328
|
+
);
|
|
6329
|
+
} else {
|
|
6330
|
+
els.push(md(`\u{1F9E0} **\u540E\u7AEF**\uFF1A${backendOptions[0]?.label ?? curBackend}`));
|
|
6331
|
+
}
|
|
6332
|
+
if (canPickModel || canPickEffort) {
|
|
6333
|
+
const formEls = [];
|
|
6334
|
+
if (canPickModel) {
|
|
6335
|
+
formEls.push(
|
|
6336
|
+
md("\u{1F916} **\u6A21\u578B**"),
|
|
6337
|
+
selectMenu({
|
|
6338
|
+
name: "model",
|
|
6339
|
+
placeholder: "\u9009\u62E9\u6A21\u578B",
|
|
6340
|
+
options: visible.map((m) => ({ label: m.displayName, value: m.id })),
|
|
6341
|
+
initial: curModel?.id
|
|
6342
|
+
})
|
|
6343
|
+
);
|
|
6344
|
+
} else {
|
|
6345
|
+
formEls.push(md(`\u{1F916} **\u6A21\u578B**\uFF1A${curModel?.displayName ?? "\u540E\u7AEF\u9ED8\u8BA4"}\uFF08\u8BE5\u540E\u7AEF\u4EC5\u4E00\u4E2A\u6A21\u578B\uFF09`));
|
|
6346
|
+
}
|
|
6347
|
+
if (canPickEffort) {
|
|
6348
|
+
formEls.push(
|
|
6349
|
+
md("\u{1F39A} **\u63A8\u7406\u5F3A\u5EA6**"),
|
|
6350
|
+
selectMenu({
|
|
6351
|
+
name: "effort",
|
|
6352
|
+
placeholder: "\u9009\u62E9\u63A8\u7406\u5F3A\u5EA6",
|
|
6353
|
+
options: unionEfforts.map((e) => ({ label: EFFORT_LABEL[e], value: e })),
|
|
6354
|
+
initial: curEffort
|
|
6355
|
+
})
|
|
6356
|
+
);
|
|
6357
|
+
}
|
|
6358
|
+
formEls.push(actions([submitButton("\u2705 \u4FDD\u5B58\u6A21\u578B / \u5F3A\u5EA6", { a: DM.commentSubmit }, "primary", "submit_comment")]));
|
|
6359
|
+
els.push(form("comment_model_effort", formEls));
|
|
6360
|
+
} else {
|
|
6361
|
+
els.push(note("\u8BE5\u540E\u7AEF\u53EA\u6709\u4E00\u4E2A\u6A21\u578B\u4E14\u4E0D\u8C03\u63A8\u7406\u5F3A\u5EA6\uFF0C\u65E0\u9700\u8BBE\u7F6E\u3002"));
|
|
6362
|
+
}
|
|
6363
|
+
els.push(
|
|
6364
|
+
hr(),
|
|
6365
|
+
md("\u270D\uFE0F **\u63D0\u793A\u8BCD**"),
|
|
6366
|
+
note("\u8BC4\u8BBA @\u6211 \u65F6\u6211\u7684\u89D2\u8272\u4E0E\u56DE\u590D\u89C4\u5219\uFF08\u542B\u600E\u4E48\u8BFB / \u6539\u6587\u6863\uFF09\u3002\u70B9\u5F00\u53EF\u76F4\u63A5\u5728\u5361\u91CC\u7F16\u8F91\u3002"),
|
|
6367
|
+
actions([button("\u7F16\u8F91\u63D0\u793A\u8BCD", { a: DM.commentEditPrompt }, "primary")]),
|
|
6368
|
+
hr(),
|
|
6369
|
+
md("\u{1F4CE} **\u914D\u5408\u98DE\u4E66 CLI**"),
|
|
6370
|
+
note(
|
|
6371
|
+
`\u8BC4\u8BBA\u91CC\u8981**\u8BFB / \u6539\u6587\u6863**\uFF0C\u9760\u98DE\u4E66 CLI\uFF08lark-cli\uFF09\uFF1A\u88C5\u597D\u5E76\u767B\u5F55\u540E\u5373\u53EF\uFF08\u7528\u4F60\u81EA\u5DF1\u7684\u8EAB\u4EFD\u8BFB\u5199\u3001\u5BF9\u81EA\u5DF1\u7684\u6587\u6863\u6709\u6743\u9650\uFF09\u3002Tips\uFF1A\u98DE\u4E66 CLI \u53EF\u4E0E\u672C\u673A\u5668\u4EBA\u590D\u7528\u540C\u4E00\u4E2A App\u3002\u5B89\u88C5\u4E0E\u7528\u6CD5\u89C1 [\u98DE\u4E66 CLI \u6587\u6863](${LARK_CLI_DOC_URL})\u3002`
|
|
6372
|
+
),
|
|
6373
|
+
hr(),
|
|
6374
|
+
actions([button("\u2B05\uFE0F \u8FD4\u56DE\u8BBE\u7F6E", { a: DM.settings })])
|
|
6375
|
+
);
|
|
6376
|
+
return card(els, { header: { title: "\u{1F4DD} \u6587\u6863\u8BC4\u8BBA\u8BBE\u7F6E", template: "blue" } });
|
|
6377
|
+
}
|
|
6378
|
+
function buildCommentPromptCard(currentPrompt, notice, masterFile) {
|
|
6379
|
+
return card(
|
|
6380
|
+
[
|
|
6381
|
+
...notice ? [md(notice)] : [],
|
|
6382
|
+
md("**\u270D\uFE0F \u8BC4\u8BBA\u63D0\u793A\u8BCD**"),
|
|
6383
|
+
note("\u8BC4\u8BBA @\u6211 \u65F6\u6211\u7684\u56FA\u5B9A\u4EBA\u8BBE\u4E0E\u56DE\u590D\u89C4\u5219\u2014\u2014\u4FDD\u5B58\u540E\u4F1A\u540C\u6B65\u5230\u6240\u6709\u6587\u6863\uFF08\u542B\u5386\u53F2\uFF09\uFF0C\u4E0B\u4E00\u6761\u8BC4\u8BBA\u751F\u6548\u3002"),
|
|
6384
|
+
md(
|
|
6385
|
+
[
|
|
6386
|
+
"**\u53EF\u7528\u53D8\u91CF**\uFF08\u540C\u6B65\u5230\u6BCF\u7BC7\u6587\u6863\u65F6\u81EA\u52A8\u66FF\u6362\u6210\u8BE5\u6587\u6863\u81EA\u5DF1\u7684\u503C\uFF09\uFF1A",
|
|
6387
|
+
"- `{docUrl}` \u6587\u6863\u94FE\u63A5",
|
|
6388
|
+
"- `{fileToken}` \u6587\u6863 token\uFF08\u94FE\u63A5\u91CC\u7C7B\u578B\u540E\u90A3\u6BB5\uFF09",
|
|
6389
|
+
"- `{fileType}` \u6587\u6863\u7C7B\u578B\uFF0C\u53D6\u503C\uFF1A",
|
|
6390
|
+
" - `doc`/`docx`\uFF08\u98DE\u4E66\u4E91\u6587\u6863\uFF09",
|
|
6391
|
+
" - `sheet`\uFF08\u98DE\u4E66\u8868\u683C\uFF09",
|
|
6392
|
+
" - `bitable`\uFF08\u591A\u7EF4\u8868\u683C\uFF09"
|
|
6393
|
+
].join("\n")
|
|
6394
|
+
),
|
|
6395
|
+
note("\u8BC4\u8BBA\u7684\u9009\u4E2D\u539F\u6587\u3001\u7528\u6237\u95EE\u9898\u6BCF\u8F6E\u4F1A\u81EA\u52A8\u7ED9\u6211\uFF0C\u65E0\u9700\u5199\u8FDB\u63D0\u793A\u8BCD\u3002"),
|
|
6396
|
+
form("comment_prompt", [
|
|
6397
|
+
input({
|
|
6398
|
+
name: "prompt",
|
|
6399
|
+
label: "\u63D0\u793A\u8BCD\u5185\u5BB9",
|
|
6400
|
+
value: currentPrompt,
|
|
6401
|
+
required: true,
|
|
6402
|
+
inputType: "multiline_text",
|
|
6403
|
+
rows: 12,
|
|
6404
|
+
width: "fill",
|
|
6405
|
+
maxLength: 1e3
|
|
6406
|
+
// Feishu input hard cap (range 1–1000); longer prompts → edit master file
|
|
6407
|
+
}),
|
|
6408
|
+
// 两个都是 form 提交按钮(普通 button 在 form 内不保证触发):保存读输入框内容落盘;
|
|
6409
|
+
// 重置忽略输入框、直接把内置默认写回 master 并同步(handler 端处理)。
|
|
6410
|
+
actions([
|
|
6411
|
+
submitButton("\u2705 \u4FDD\u5B58\u63D0\u793A\u8BCD", { a: DM.commentPromptSubmit }, "primary", "submit_prompt"),
|
|
6412
|
+
submitButton("\u21A9\uFE0F \u91CD\u7F6E\u4E3A\u9ED8\u8BA4", { a: DM.commentResetPrompt }, "default", "reset_prompt")
|
|
6413
|
+
])
|
|
6414
|
+
]),
|
|
6415
|
+
hr(),
|
|
6416
|
+
md("**\u{1F4E8} \u6BCF\u8F6E\u8BC4\u8BBA @\u6211\uFF0C\u6211\u4F1A\u6536\u5230\u4E0B\u9762\u6D88\u606F\uFF1A**"),
|
|
6417
|
+
md(
|
|
6418
|
+
[
|
|
6419
|
+
"\u6211\u5728\u98DE\u4E66\u4E91\u6587\u6863\u7684\u8BC4\u8BBA\u91CC @\u4E86\u4F60\u3002\u6587\u6863\u4FE1\u606F\uFF1A",
|
|
6420
|
+
"- \u94FE\u63A5\uFF1A{docUrl}",
|
|
6421
|
+
"- file_token\uFF1A{fileToken}",
|
|
6422
|
+
"- \u7C7B\u578B\uFF1A{fileType}",
|
|
6423
|
+
"- \u8BC4\u8BBA\u8303\u56F4\uFF1A\u884C\u5185\u8BC4\u8BBA\uFF08\u9488\u5BF9\u9009\u4E2D\u6587\u5B57\uFF09 / \u5168\u6587\u8BC4\u8BBA\uFF08\u9488\u5BF9\u6574\u7BC7\uFF09",
|
|
6424
|
+
"",
|
|
6425
|
+
// 空行结束列表,否则下一行被并进「评论范围」那个 bullet
|
|
6426
|
+
"\u7528\u6237\u9009\u4E2D\u7684\u539F\u6587\uFF1A\uFF08\u4EC5\u884C\u5185\u8BC4\u8BBA\u65F6\u9644\u4E0A\uFF09",
|
|
6427
|
+
"> \u2026\u2026\u88AB\u8BC4\u8BBA\u7684\u90A3\u6BB5\u6587\u5B57\u2026\u2026",
|
|
6428
|
+
"",
|
|
6429
|
+
// 空行结束引用,否则「用户的问题」被并进引用块
|
|
6430
|
+
"\u7528\u6237\u7684\u95EE\u9898\uFF1A\u2026\u2026\u8BC4\u8BBA\u6B63\u6587\u2026\u2026"
|
|
6431
|
+
].join("\n")
|
|
6432
|
+
),
|
|
6433
|
+
note(
|
|
6434
|
+
masterFile ? `\u63D0\u793A\u8BCD\u4E5F\u53EF\u76F4\u63A5\u7F16\u8F91 ${masterFile}\uFF08\u6539\u6587\u4EF6\u540E\u65B0\u5185\u5BB9\u5728\u6BCF\u7BC7\u6587\u6863\u7684\u4E0B\u4E00\u6761\u8BC4\u8BBA\u65F6\u751F\u6548\uFF09\u3002` : "\u63D0\u793A\u8BCD\u4E5F\u53EF\u76F4\u63A5\u7F16\u8F91 bot \u76EE\u5F55\u4E0B\u7684 comment-instructions.md\u3002"
|
|
6435
|
+
),
|
|
6436
|
+
actions([button("\u2B05\uFE0F \u8FD4\u56DE", { a: DM.commentSettings })])
|
|
6437
|
+
],
|
|
6438
|
+
{ header: { title: "\u270D\uFE0F \u7F16\u8F91\u63D0\u793A\u8BCD", template: "blue" }, widthMode: "fill" }
|
|
6439
|
+
);
|
|
6440
|
+
}
|
|
6217
6441
|
function buildWatchdogCustomCard(cfg) {
|
|
6218
6442
|
const cur = cfg.preferences?.runIdleTimeoutSeconds ?? 120;
|
|
6219
6443
|
return card(
|
|
@@ -6809,6 +7033,7 @@ function reduce(state, evt) {
|
|
|
6809
7033
|
id: evt.itemId,
|
|
6810
7034
|
title: evt.title,
|
|
6811
7035
|
detail: evt.detail,
|
|
7036
|
+
kind: evt.kind,
|
|
6812
7037
|
status: "running"
|
|
6813
7038
|
};
|
|
6814
7039
|
return {
|
|
@@ -7151,25 +7376,61 @@ function renderCleanBody(bodyMarkdown, images) {
|
|
|
7151
7376
|
}
|
|
7152
7377
|
|
|
7153
7378
|
// src/card/tool-render.ts
|
|
7154
|
-
var HEADER_TITLE_MAX =
|
|
7379
|
+
var HEADER_TITLE_MAX = 120;
|
|
7155
7380
|
var OUTPUT_MAX = 1200;
|
|
7381
|
+
var CMD_BLOCK_MAX = 1e3;
|
|
7382
|
+
var SUMMARY_LINE_MAX = 400;
|
|
7156
7383
|
var BODY_TOTAL_MAX = 2500;
|
|
7384
|
+
function statusIcon(status) {
|
|
7385
|
+
return status === "done" ? "\u2705" : status === "error" ? "\u274C" : "\u23F3";
|
|
7386
|
+
}
|
|
7387
|
+
function kindGlyph(kind) {
|
|
7388
|
+
switch (kind) {
|
|
7389
|
+
case "command":
|
|
7390
|
+
return "\u{1F527}";
|
|
7391
|
+
case "file":
|
|
7392
|
+
return "\u{1F4C4}";
|
|
7393
|
+
case "search":
|
|
7394
|
+
return "\u{1F50D}";
|
|
7395
|
+
default:
|
|
7396
|
+
return "";
|
|
7397
|
+
}
|
|
7398
|
+
}
|
|
7399
|
+
function leadGlyphs(tool) {
|
|
7400
|
+
const glyph = kindGlyph(tool.kind);
|
|
7401
|
+
return glyph ? `${statusIcon(tool.status)} ${glyph}` : statusIcon(tool.status);
|
|
7402
|
+
}
|
|
7157
7403
|
function toolHeaderText(tool) {
|
|
7158
|
-
|
|
7159
|
-
|
|
7404
|
+
return `${leadGlyphs(tool)} **${escapeInline2(truncate3(tool.title, HEADER_TITLE_MAX))}**`;
|
|
7405
|
+
}
|
|
7406
|
+
function toolSummaryLine(tool) {
|
|
7407
|
+
return `- ${leadGlyphs(tool)} **${escapeInline2(truncate3(tool.title, SUMMARY_LINE_MAX))}**`;
|
|
7160
7408
|
}
|
|
7161
7409
|
function toolBodyMd(tool) {
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
const label = tool.status === "error" ? "Error" : "Output";
|
|
7166
|
-
const block = tool.output.startsWith("```") ? tool.output : bashBlock(tool.output);
|
|
7167
|
-
const body = `**${label}**
|
|
7168
|
-
${block}`;
|
|
7410
|
+
const lead = invocationMd(tool);
|
|
7411
|
+
const outputPart = tool.output ? outputBlock(tool) : tool.status === "running" ? "_\u8FD0\u884C\u4E2D\u2026_" : tool.kind === "search" ? "_\uFF08\u641C\u7D22\u7ED3\u679C\u5DF2\u7528\u4E8E\u4F5C\u7B54\uFF0C\u4E0D\u5355\u72EC\u56DE\u4F20\uFF09_" : "";
|
|
7412
|
+
const body = [lead, outputPart].filter(Boolean).join("\n\n");
|
|
7169
7413
|
if (body.length <= BODY_TOTAL_MAX) return body;
|
|
7170
7414
|
return `${body.slice(0, BODY_TOTAL_MAX)}\u2026
|
|
7171
7415
|
|
|
7172
|
-
_\uFF08\
|
|
7416
|
+
_\uFF08\u5185\u5BB9\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD\uFF09_`;
|
|
7417
|
+
}
|
|
7418
|
+
function invocationMd(tool) {
|
|
7419
|
+
if (tool.kind !== "command") return "";
|
|
7420
|
+
const cmd = tool.title.trim();
|
|
7421
|
+
if (!cmd) return "";
|
|
7422
|
+
const detail = tool.detail ? `
|
|
7423
|
+
\`${escapeInline2(tool.detail)}\`` : "";
|
|
7424
|
+
return `**\u547D\u4EE4**
|
|
7425
|
+
\`\`\`bash
|
|
7426
|
+
${truncate3(cmd, CMD_BLOCK_MAX)}
|
|
7427
|
+
\`\`\`${detail}`;
|
|
7428
|
+
}
|
|
7429
|
+
function outputBlock(tool) {
|
|
7430
|
+
const label = tool.status === "error" ? "Error" : "Output";
|
|
7431
|
+
const block = tool.output.startsWith("```") ? tool.output : bashBlock(tool.output);
|
|
7432
|
+
return `**${label}**
|
|
7433
|
+
${block}`;
|
|
7173
7434
|
}
|
|
7174
7435
|
function bashBlock(output) {
|
|
7175
7436
|
const note2 = output.length > OUTPUT_MAX ? `
|
|
@@ -7270,6 +7531,8 @@ var CONTROLS_EID = "controls";
|
|
|
7270
7531
|
var REASONING_MAX = 1500;
|
|
7271
7532
|
var COLLAPSE_TOOL_THRESHOLD = 3;
|
|
7272
7533
|
var PROCESS_BODY_BUDGET = 22e3;
|
|
7534
|
+
var PROCESS_COMPONENT_BUDGET = 120;
|
|
7535
|
+
var SUMMARY_BODY_MAX = 6e3;
|
|
7273
7536
|
function buildRunCard(rc) {
|
|
7274
7537
|
const state = rc.rs;
|
|
7275
7538
|
const running = state.terminal === "running";
|
|
@@ -7331,7 +7594,7 @@ function renderTerminal(state, rc) {
|
|
|
7331
7594
|
const toolCount = blocks.reduce((n, b) => b.kind === "tool" ? n + 1 : n, 0);
|
|
7332
7595
|
elements.push(
|
|
7333
7596
|
collapsiblePanelEl({
|
|
7334
|
-
title: processTitle(Boolean(reasoning), toolCount),
|
|
7597
|
+
title: processTitle(Boolean(reasoning), toolCount, state.terminal),
|
|
7335
7598
|
expanded: false,
|
|
7336
7599
|
border: "grey",
|
|
7337
7600
|
elements: processEls
|
|
@@ -7379,7 +7642,9 @@ function lastTextIndex(blocks) {
|
|
|
7379
7642
|
}
|
|
7380
7643
|
function buildProcessBody(reasoning, blocks) {
|
|
7381
7644
|
const rich = processElements(reasoning, blocks, false);
|
|
7382
|
-
if (estimateSize2(rich) <= PROCESS_BODY_BUDGET)
|
|
7645
|
+
if (estimateSize2(rich) <= PROCESS_BODY_BUDGET && estimateComponents(rich) <= PROCESS_COMPONENT_BUDGET) {
|
|
7646
|
+
return rich;
|
|
7647
|
+
}
|
|
7383
7648
|
return processElements(reasoning, blocks, true);
|
|
7384
7649
|
}
|
|
7385
7650
|
function processElements(reasoning, blocks, compactTools) {
|
|
@@ -7394,18 +7659,25 @@ function processElements(reasoning, blocks, compactTools) {
|
|
|
7394
7659
|
}
|
|
7395
7660
|
return out;
|
|
7396
7661
|
}
|
|
7397
|
-
function processTitle(hasReasoning, toolCount) {
|
|
7662
|
+
function processTitle(hasReasoning, toolCount, terminal) {
|
|
7663
|
+
const icon = terminal === "interrupted" ? "\u23F9" : terminal === "idle_timeout" ? "\u23F1" : terminal === "error" ? "\u26A0\uFE0F" : "\u2705";
|
|
7664
|
+
const noun = terminal === "interrupted" ? "\u4E2D\u65AD\u524D\u7684\u8FC7\u7A0B" : terminal === "idle_timeout" ? "\u8D85\u65F6\u524D\u7684\u8FC7\u7A0B" : terminal === "error" ? "\u51FA\u9519\u524D\u7684\u8FC7\u7A0B" : "\u672C\u8F6E\u8FC7\u7A0B";
|
|
7398
7665
|
const parts = [];
|
|
7399
7666
|
if (hasReasoning) parts.push("\u{1F9E0} \u601D\u8003");
|
|
7400
|
-
if (toolCount > 0) parts.push(`\u{1F9F0} ${toolCount} \u4E2A\u5DE5\u5177
|
|
7401
|
-
const detail = parts.length > 0 ?
|
|
7402
|
-
return
|
|
7667
|
+
if (toolCount > 0) parts.push(`\u{1F9F0} ${toolCount} \u4E2A\u5DE5\u5177`);
|
|
7668
|
+
const detail = parts.length > 0 ? ` \xB7 ${parts.join(" \xB7 ")}` : "";
|
|
7669
|
+
return `${icon} **${noun}**${detail}\uFF08\u70B9\u51FB\u5C55\u5F00\uFF09`;
|
|
7403
7670
|
}
|
|
7404
7671
|
function estimateSize2(els) {
|
|
7405
7672
|
let n = 0;
|
|
7406
7673
|
for (const el of els) n += JSON.stringify(el).length;
|
|
7407
7674
|
return n;
|
|
7408
7675
|
}
|
|
7676
|
+
function estimateComponents(els) {
|
|
7677
|
+
let n = 0;
|
|
7678
|
+
for (const el of els) n += el.tag === "collapsible_panel" ? 3 : 1;
|
|
7679
|
+
return n;
|
|
7680
|
+
}
|
|
7409
7681
|
function buildRunCardPlain(rc) {
|
|
7410
7682
|
return buildRunCard({ ...rc, cardKey: void 0 });
|
|
7411
7683
|
}
|
|
@@ -7440,11 +7712,11 @@ function* groupBlocks(blocks) {
|
|
|
7440
7712
|
}
|
|
7441
7713
|
function renderToolGroup(tools, finalized, compact = false) {
|
|
7442
7714
|
if (tools.length === 0) return [];
|
|
7443
|
-
if (compact) return [collapsedToolSummary(tools,
|
|
7715
|
+
if (compact) return [collapsedToolSummary(tools, finalized)];
|
|
7716
|
+
if (finalized) return tools.map((t) => toolPanel(t, false));
|
|
7444
7717
|
if (tools.length < COLLAPSE_TOOL_THRESHOLD) {
|
|
7445
7718
|
return tools.map((t) => toolPanel(t, false));
|
|
7446
7719
|
}
|
|
7447
|
-
if (finalized) return [collapsedToolSummary(tools, true)];
|
|
7448
7720
|
const prior = tools.slice(0, -1);
|
|
7449
7721
|
const latest = tools[tools.length - 1];
|
|
7450
7722
|
const out = [];
|
|
@@ -7454,7 +7726,7 @@ function renderToolGroup(tools, finalized, compact = false) {
|
|
|
7454
7726
|
}
|
|
7455
7727
|
function reasoningPanel(content, active) {
|
|
7456
7728
|
return collapsiblePanel({
|
|
7457
|
-
title: active ? "\u{1F9E0} **\u601D\u8003\
|
|
7729
|
+
title: active ? "\u{1F9E0} **\u6B63\u5728\u601D\u8003\u2026**" : "\u{1F9E0} **\u601D\u8003\u8FC7\u7A0B**\uFF08\u70B9\u51FB\u5C55\u5F00\uFF09",
|
|
7458
7730
|
expanded: active,
|
|
7459
7731
|
border: "grey",
|
|
7460
7732
|
body: truncate4(content, REASONING_MAX)
|
|
@@ -7470,11 +7742,24 @@ function toolPanel(tool, expanded) {
|
|
|
7470
7742
|
}
|
|
7471
7743
|
function collapsedToolSummary(tools, finalized) {
|
|
7472
7744
|
const suffix = finalized ? "\uFF08\u5DF2\u7ED3\u675F\uFF09" : "";
|
|
7745
|
+
const lines = tools.map(toolSummaryLine);
|
|
7746
|
+
let body = lines.join("\n");
|
|
7747
|
+
if (body.length > SUMMARY_BODY_MAX) {
|
|
7748
|
+
let kept = 0;
|
|
7749
|
+
let acc = 0;
|
|
7750
|
+
for (const line of lines) {
|
|
7751
|
+
if (acc + line.length + 1 > SUMMARY_BODY_MAX) break;
|
|
7752
|
+
acc += line.length + 1;
|
|
7753
|
+
kept++;
|
|
7754
|
+
}
|
|
7755
|
+
body = `${lines.slice(0, kept).join("\n")}
|
|
7756
|
+
- _\u2026\u8FD8\u6709 ${tools.length - kept} \u4E2A\u547D\u4EE4\u672A\u663E\u793A_`;
|
|
7757
|
+
}
|
|
7473
7758
|
return collapsiblePanel({
|
|
7474
|
-
title: `\
|
|
7759
|
+
title: `\u{1F9F0} **${tools.length} \u4E2A\u5DE5\u5177\u8C03\u7528${suffix}**`,
|
|
7475
7760
|
expanded: false,
|
|
7476
7761
|
border: "blue",
|
|
7477
|
-
body
|
|
7762
|
+
body
|
|
7478
7763
|
});
|
|
7479
7764
|
}
|
|
7480
7765
|
function footerStatusText(status) {
|
|
@@ -10409,7 +10694,9 @@ function weaveSender(text, sender) {
|
|
|
10409
10694
|
|
|
10410
10695
|
// src/bot/comments.ts
|
|
10411
10696
|
init_logger();
|
|
10412
|
-
|
|
10697
|
+
import { mkdir as mkdir14, readdir as readdir4, readFile as readFile12, writeFile as writeFile11 } from "fs/promises";
|
|
10698
|
+
import { dirname as dirname12, join as join19 } from "path";
|
|
10699
|
+
var SUPPORTED_FILE_TYPES = /* @__PURE__ */ new Set(["doc", "docx", "sheet", "bitable"]);
|
|
10413
10700
|
var REPLY_MAX_CHARS = 2e3;
|
|
10414
10701
|
function apiErrCode(err) {
|
|
10415
10702
|
return err?.response?.data?.code;
|
|
@@ -10504,10 +10791,80 @@ var DOC_HOSTS = {
|
|
|
10504
10791
|
feishu: "feishu.cn",
|
|
10505
10792
|
lark: "larksuite.com"
|
|
10506
10793
|
};
|
|
10794
|
+
var DOC_URL_PATH = {
|
|
10795
|
+
doc: "docs",
|
|
10796
|
+
docx: "docx",
|
|
10797
|
+
sheet: "sheets",
|
|
10798
|
+
bitable: "base"
|
|
10799
|
+
};
|
|
10800
|
+
function buildDocUrl(tenant, fileType, fileToken) {
|
|
10801
|
+
const seg = DOC_URL_PATH[fileType] ?? fileType;
|
|
10802
|
+
return `https://${DOC_HOSTS[tenant]}/${seg}/${fileToken}`;
|
|
10803
|
+
}
|
|
10804
|
+
var DEFAULT_COMMENT_INSTRUCTIONS = `# \u98DE\u4E66\u4E91\u6587\u6863\u8BC4\u8BBA\u52A9\u624B
|
|
10805
|
+
|
|
10806
|
+
\u4F60\u88AB @ \u5728\u4E00\u7BC7\u98DE\u4E66\u4E91\u6587\u6863\uFF08{docUrl}\uFF0C\u7C7B\u578B {fileType}\uFF0Cfile_token\uFF1A{fileToken}\uFF09\u7684\u8BC4\u8BBA\u91CC\u3002\u7CFB\u7EDF\u4F1A\u628A\u4F60\u8FD9\u4E00\u8F6E\u7684\u6700\u7EC8\u56DE\u590D\u81EA\u52A8\u8D34\u56DE\u8FD9\u6761\u8BC4\u8BBA\u3002\u6BCF\u8F6E\u8FD8\u4F1A\u7ED9\u4F60\u8BC4\u8BBA\u8303\u56F4\u548C\uFF08\u884C\u5185\u8BC4\u8BBA\u65F6\uFF09\u9009\u4E2D\u7684\u539F\u6587\u3002
|
|
10807
|
+
|
|
10808
|
+
## \u600E\u4E48\u505A
|
|
10809
|
+
- \u9700\u8981\u770B\u6B63\u6587\uFF0C\u6216\u7528\u6237\u8981\u6C42"\u5E2E\u6211\u6539 / \u4F18\u5316 / \u91CD\u5199 / \u7EC6\u5316\u8FD9\u6BB5"\u65F6\uFF0C\u7528 lark-cli \u8BFB / \u6539\u8FD9\u7BC7\u6587\u6863\uFF08{fileToken}\uFF09\u3002
|
|
10810
|
+
- \u6539\u6587\u6863\u65F6\u7EDD\u4E0D\u8981\u5220\u9664\u6216\u6574\u6BB5\u66FF\u6362"\u88AB\u8BC4\u8BBA\u7684\u539F\u6587"\uFF08\u672C\u8F6E\u7ED9\u4F60\u7684\u9009\u4E2D\u539F\u6587\uFF09\u2014\u2014\u98DE\u4E66\u8BC4\u8BBA\u951A\u5B9A\u5728\u8FD9\u6BB5\u6587\u5B57\u4E0A\uFF0C\u5220\u4E86\u5B83\u8BC4\u8BBA\u4F1A\u53D8\u6210"\u539F\u6587\u5DF2\u88AB\u5220\u9664"\u3001\u79FB\u8FDB\u5386\u53F2\u8BC4\u8BBA\uFF0C\u6B63\u6587\u5C31\u770B\u4E0D\u5230\u4E86\u3002\u8981\u4FDD\u7559\u8FD9\u6BB5\u539F\u6587\uFF0C\u5728\u5B83\u4E4B\u540E\u6216\u5468\u56F4\u589E\u6539\uFF0C\u53EA\u52A8\u8FD9\u4E00\u5904\u3002
|
|
10811
|
+
- \u6539\u5B8C\u53EA\u56DE\u4E00\u53E5\u7B80\u77ED\u8BF4\u660E\uFF1B\u7528\u6237\u53EA\u662F\u63D0\u95EE\u5C31\u76F4\u63A5\u7B54\uFF0C\u4E0D\u6539\u4EFB\u4F55\u4E1C\u897F\u3002
|
|
10812
|
+
|
|
10813
|
+
## \u5FC5\u987B\u9075\u5B88
|
|
10814
|
+
1. \u4E0D\u8981\u81EA\u5DF1\u53D1\u8868 / \u56DE\u590D / \u89E3\u51B3 / \u5220\u9664\u4EFB\u4F55\u98DE\u4E66\u8BC4\u8BBA\u2014\u2014\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u628A\u4F60\u7684\u6700\u7EC8\u56DE\u590D\u8D34\u4E0A\u53BB\u3002
|
|
10815
|
+
2. \u53EA\u8F93\u51FA\u6700\u7EC8\u7B54\u6848\u672C\u8EAB\uFF0C\u4E0D\u8981\u590D\u8FF0\u5206\u6790\u8FC7\u7A0B\u6216"\u6211\u73B0\u5728\u53BB\u2026"\u3002
|
|
10816
|
+
3. \u5168\u7A0B\u7EAF\u6587\u672C\uFF1A\u4E0D\u8981\u4EFB\u4F55 markdown \u6807\u8BB0\uFF08** __ # - * > \u53CD\u5F15\u53F7 \u4E4B\u7C7B\uFF09\u3001\u4E0D\u8981\u4EE3\u7801\u5757\u3001\u4E0D\u8981\u8868\u683C\u2014\u2014\u8BC4\u8BBA\u6846\u4E0D\u6E32\u67D3\uFF0C\u4F1A\u539F\u6837\u663E\u793A\u8FD9\u4E9B\u7B26\u53F7\u3002\u8BED\u6C14\u7B80\u6D01\u76F4\u63A5\u3002
|
|
10817
|
+
`;
|
|
10818
|
+
function commentSessionKey(fileToken, commentId) {
|
|
10819
|
+
return `doc:${fileToken}:${commentId}`;
|
|
10820
|
+
}
|
|
10821
|
+
function commentCwd(projectsRoot, fileType, fileToken) {
|
|
10822
|
+
return join19(projectsRoot, `comment-${fileType}-${fileToken}`);
|
|
10823
|
+
}
|
|
10824
|
+
async function loadCommentInstructions(masterFile) {
|
|
10825
|
+
try {
|
|
10826
|
+
const existing = await readFile12(masterFile, "utf8");
|
|
10827
|
+
return existing.trim() ? existing : DEFAULT_COMMENT_INSTRUCTIONS;
|
|
10828
|
+
} catch {
|
|
10829
|
+
}
|
|
10830
|
+
await mkdir14(dirname12(masterFile), { recursive: true }).catch(() => void 0);
|
|
10831
|
+
await writeFile11(masterFile, DEFAULT_COMMENT_INSTRUCTIONS, "utf8").catch(() => void 0);
|
|
10832
|
+
return DEFAULT_COMMENT_INSTRUCTIONS;
|
|
10833
|
+
}
|
|
10834
|
+
async function syncCommentInstructions(cwd, instructions) {
|
|
10835
|
+
await mkdir14(cwd, { recursive: true });
|
|
10836
|
+
await Promise.all([
|
|
10837
|
+
writeFile11(join19(cwd, "AGENTS.md"), instructions, "utf8"),
|
|
10838
|
+
writeFile11(join19(cwd, "CLAUDE.md"), instructions, "utf8")
|
|
10839
|
+
]);
|
|
10840
|
+
}
|
|
10841
|
+
async function saveCommentInstructions(masterFile, content) {
|
|
10842
|
+
await mkdir14(dirname12(masterFile), { recursive: true });
|
|
10843
|
+
await writeFile11(masterFile, content, "utf8");
|
|
10844
|
+
}
|
|
10845
|
+
function renderCommentInstructions(template, tenant, fileType, fileToken) {
|
|
10846
|
+
const docUrl = buildDocUrl(tenant, fileType, fileToken);
|
|
10847
|
+
return template.replace(/\{fileType\}/g, fileType).replace(/\{fileToken\}/g, fileToken).replace(/\{docUrl\}/g, docUrl);
|
|
10848
|
+
}
|
|
10849
|
+
async function syncAllCommentInstructions(projectsRoot, rawTemplate, tenant) {
|
|
10850
|
+
const entries = await readdir4(projectsRoot, { withFileTypes: true }).catch(() => []);
|
|
10851
|
+
let synced = 0;
|
|
10852
|
+
for (const e of entries) {
|
|
10853
|
+
if (!e.isDirectory() || !e.name.startsWith("comment-")) continue;
|
|
10854
|
+
const m = /^comment-([a-z]+)-(.+)$/.exec(e.name);
|
|
10855
|
+
const fileType = m?.[1];
|
|
10856
|
+
const fileToken = m?.[2];
|
|
10857
|
+
if (!fileType || !fileToken) continue;
|
|
10858
|
+
const rendered = renderCommentInstructions(rawTemplate, tenant, fileType, fileToken);
|
|
10859
|
+
await syncCommentInstructions(join19(projectsRoot, e.name), rendered).catch(() => void 0);
|
|
10860
|
+
synced++;
|
|
10861
|
+
}
|
|
10862
|
+
return synced;
|
|
10863
|
+
}
|
|
10507
10864
|
function buildCommentPrompt(target, ctx, tenant) {
|
|
10508
|
-
const docUrl =
|
|
10865
|
+
const docUrl = buildDocUrl(tenant, target.fileType, target.fileToken);
|
|
10509
10866
|
const parts = [];
|
|
10510
|
-
parts.push("\u6211\u5728\u98DE\u4E66\u4E91\u6587\u6863\u7684\u8BC4\u8BBA\u91CC
|
|
10867
|
+
parts.push("\u6211\u5728\u98DE\u4E66\u4E91\u6587\u6863\u7684\u8BC4\u8BBA\u91CC @\u4E86\u4F60\u3002\u6587\u6863\u4FE1\u606F\uFF1A");
|
|
10511
10868
|
parts.push(`- \u94FE\u63A5\uFF1A${docUrl}`);
|
|
10512
10869
|
parts.push(`- file_token\uFF1A${target.fileToken}`);
|
|
10513
10870
|
parts.push(`- \u7C7B\u578B\uFF1A${target.fileType}`);
|
|
@@ -10519,20 +10876,6 @@ function buildCommentPrompt(target, ctx, tenant) {
|
|
|
10519
10876
|
}
|
|
10520
10877
|
parts.push("");
|
|
10521
10878
|
parts.push(`\u7528\u6237\u7684\u95EE\u9898\uFF1A${ctx.question}`);
|
|
10522
|
-
parts.push("");
|
|
10523
|
-
parts.push(
|
|
10524
|
-
`\u5982\u679C\u56DE\u7B54\u9700\u8981\u6587\u6863\u6B63\u6587\u5185\u5BB9\uFF0C\u53EF\u7528 lark-cli \u53EA\u8BFB\u5730\u83B7\u53D6\uFF08\u4EC5\u7528\u4E8E\u8BFB\u53D6\uFF0C\u4E0D\u8981\u7528\u5B83\u5199\u4EFB\u4F55\u4E1C\u897F\uFF09\uFF1A
|
|
10525
|
-
lark-cli docs +fetch --doc ${target.fileToken} --api-version v2`
|
|
10526
|
-
);
|
|
10527
|
-
parts.push("");
|
|
10528
|
-
parts.push("\u3010\u975E\u5E38\u91CD\u8981\uFF0C\u52A1\u5FC5\u9075\u5B88\u3011");
|
|
10529
|
-
parts.push(
|
|
10530
|
-
"1. \u4E0D\u8981\u81EA\u5DF1\u53BB\u53D1\u8868 / \u56DE\u590D / \u4FEE\u6539\u4EFB\u4F55\u98DE\u4E66\u8BC4\u8BBA\u6216\u6587\u6863\uFF08\u4E5F\u4E0D\u8981\u7528 lark-cli \u6216\u4EFB\u4F55\u5DE5\u5177\u53BB\u53D1\u8BC4\u8BBA\uFF09\u2014\u2014\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u628A\u4F60\u4E0B\u9762\u7ED9\u51FA\u7684\u6700\u7EC8\u56DE\u590D\u53D1\u5230\u8FD9\u6761\u8BC4\u8BBA\u91CC\uFF0C\u4F60\u53EA\u7BA1\u628A\u7B54\u6848\u5199\u51FA\u6765\u3002"
|
|
10531
|
-
);
|
|
10532
|
-
parts.push("2. \u53EA\u8F93\u51FA\u8981\u53D1\u7ED9\u7528\u6237\u7684\u300C\u6700\u7EC8\u7B54\u6848\u300D\u672C\u8EAB\uFF0C\u4E0D\u8981\u590D\u8FF0\u5206\u6790\u8FC7\u7A0B\u3001\u6B65\u9AA4\u3001\u6216\u300C\u6211\u73B0\u5728\u53BB\u2026\u300D\u8FD9\u7C7B\u8BF4\u660E\u3002");
|
|
10533
|
-
parts.push(
|
|
10534
|
-
"3. \u7528\u7EAF\u6587\u672C\uFF0C\u4E0D\u8981\u7528 markdown \u6807\u8BB0\uFF08\u4E0D\u8981 ** __ # - * > ` \u4E4B\u7C7B\uFF09\uFF0C\u4E0D\u8981\u4EE3\u7801\u5757\uFF1B\u8BC4\u8BBA\u6846\u4E0D\u6E32\u67D3 markdown\uFF0C\u4F1A\u539F\u6837\u663E\u793A\u8FD9\u4E9B\u7B26\u53F7\u3002\u56DE\u7B54\u7B80\u6D01\u76F4\u63A5\u3002"
|
|
10535
|
-
);
|
|
10536
10879
|
return parts.join("\n");
|
|
10537
10880
|
}
|
|
10538
10881
|
function stripMarkdown(s) {
|
|
@@ -10912,6 +11255,7 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
10912
11255
|
}
|
|
10913
11256
|
const active = /* @__PURE__ */ new Map();
|
|
10914
11257
|
const docLocks = /* @__PURE__ */ new Map();
|
|
11258
|
+
const commentInstrUsed = /* @__PURE__ */ new Map();
|
|
10915
11259
|
const sema = new Semaphore(getMaxConcurrentRuns(cfg));
|
|
10916
11260
|
const currentIdleMs = () => getRunIdleTimeoutMs(cfg) ?? 0;
|
|
10917
11261
|
const resumePending = /* @__PURE__ */ new Map();
|
|
@@ -11760,10 +12104,13 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
11760
12104
|
if (opts?.render !== false) void patch(evt, renderSettings);
|
|
11761
12105
|
}
|
|
11762
12106
|
let cliHookStatuses;
|
|
11763
|
-
|
|
12107
|
+
function renderSettings() {
|
|
12108
|
+
return buildSettingsCard(cfg);
|
|
12109
|
+
}
|
|
12110
|
+
async function renderCoffeeSettings(refreshHooks = false) {
|
|
11764
12111
|
if (refreshHooks || !cliHookStatuses) cliHookStatuses = await inspectCliBridgeHooks();
|
|
11765
12112
|
const cliPrefs = getCliBridgePreferences(cfg);
|
|
11766
|
-
const
|
|
12113
|
+
const section = cliBridgeSettingsSection({
|
|
11767
12114
|
enabled: cliPrefs.enabled,
|
|
11768
12115
|
statuses: cliHookStatuses,
|
|
11769
12116
|
canEnable: canEnableCliBridge(cfg),
|
|
@@ -11771,7 +12118,25 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
11771
12118
|
agents: cliPrefs.agents,
|
|
11772
12119
|
keepAwake: cliPrefs.keepAwake.enabled
|
|
11773
12120
|
});
|
|
11774
|
-
return
|
|
12121
|
+
return buildCoffeeSettingsCard(section);
|
|
12122
|
+
}
|
|
12123
|
+
function commentBackendOptions() {
|
|
12124
|
+
return visibleCatalog().filter((e) => e.id === DEFAULT_BACKEND_ID || isBackendEntryInstalled(e)).map((e) => ({ id: e.id, label: e.displayName }));
|
|
12125
|
+
}
|
|
12126
|
+
async function renderCommentSettings(notice) {
|
|
12127
|
+
const comments = getCommentsConfig(cfg);
|
|
12128
|
+
const models = await listModels(backendFor(comments.backend)).catch(() => []);
|
|
12129
|
+
return buildCommentSettingsCard(cfg, commentBackendOptions(), models, notice);
|
|
12130
|
+
}
|
|
12131
|
+
function applyCommentsPref(evt, mut) {
|
|
12132
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12133
|
+
const prefs = { ...cfg.preferences ?? {} };
|
|
12134
|
+
const comments = { ...prefs.comments ?? {} };
|
|
12135
|
+
mut(comments);
|
|
12136
|
+
prefs.comments = comments;
|
|
12137
|
+
cfg.preferences = prefs;
|
|
12138
|
+
void saveConfig(cfg).catch((err) => log.fail("console", err, { phase: "save-config" }));
|
|
12139
|
+
void patch(evt, () => renderCommentSettings());
|
|
11775
12140
|
}
|
|
11776
12141
|
const freshMenu = (evt) => {
|
|
11777
12142
|
patch(evt, buildDmMenuCard({ webConsoleUrl: webConsoleUrl(), version: bridgeVersion() }));
|
|
@@ -11920,7 +12285,9 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
11920
12285
|
const page = typeof value.p === "number" ? value.p : Number(value.p) || 0;
|
|
11921
12286
|
patch(evt, () => renderProjectList(page));
|
|
11922
12287
|
}).on(DM.settings, async ({ evt }) => {
|
|
11923
|
-
if (dmAdmin(evt.operator?.openId)) await patch(evt,
|
|
12288
|
+
if (dmAdmin(evt.operator?.openId)) await patch(evt, renderSettings);
|
|
12289
|
+
}).on(DM.coffeeSettings, async ({ evt }) => {
|
|
12290
|
+
if (dmAdmin(evt.operator?.openId)) await patch(evt, () => renderCoffeeSettings(true));
|
|
11924
12291
|
}).on(CLI.toggleEnabled, async ({ evt, value }) => {
|
|
11925
12292
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
11926
12293
|
const enabled = value.v === "on";
|
|
@@ -11940,7 +12307,7 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
11940
12307
|
} catch (err) {
|
|
11941
12308
|
log.fail("cli-bridge", err, { phase: enabled ? "enable" : "disable" });
|
|
11942
12309
|
}
|
|
11943
|
-
void patch(evt,
|
|
12310
|
+
void patch(evt, renderCoffeeSettings);
|
|
11944
12311
|
}).on(CLI.repairHooks, async ({ evt }) => {
|
|
11945
12312
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
11946
12313
|
try {
|
|
@@ -11951,13 +12318,14 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
11951
12318
|
} catch (err) {
|
|
11952
12319
|
log.fail("cli-bridge", err, { phase: "repair-hooks" });
|
|
11953
12320
|
}
|
|
11954
|
-
await patch(evt, () =>
|
|
12321
|
+
await patch(evt, () => renderCoffeeSettings(true));
|
|
11955
12322
|
}).on(CLI.setNotifyScope, ({ evt, value }) => {
|
|
11956
12323
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
11957
12324
|
const scope = value.v === "bound_projects" || value.v === "none" ? value.v : "all";
|
|
11958
12325
|
applyPref(evt, (p) => {
|
|
11959
12326
|
p.cliBridge = { ...p.cliBridge ?? {}, notifyScope: scope };
|
|
11960
|
-
});
|
|
12327
|
+
}, { render: false });
|
|
12328
|
+
void patch(evt, renderCoffeeSettings);
|
|
11961
12329
|
}).on(CLI.toggleAgent, ({ evt, value }) => {
|
|
11962
12330
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
11963
12331
|
const agent = value.agent === "codex" ? "codex" : "claude";
|
|
@@ -11965,14 +12333,16 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
11965
12333
|
applyPref(evt, (p) => {
|
|
11966
12334
|
const cur = p.cliBridge ?? {};
|
|
11967
12335
|
p.cliBridge = { ...cur, agents: { ...cur.agents ?? {}, [agent]: on } };
|
|
11968
|
-
});
|
|
12336
|
+
}, { render: false });
|
|
12337
|
+
void patch(evt, renderCoffeeSettings);
|
|
11969
12338
|
}).on(CLI.toggleKeepAwake, ({ evt, value }) => {
|
|
11970
12339
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
11971
12340
|
const on = value.v === "on";
|
|
11972
12341
|
applyPref(evt, (p) => {
|
|
11973
12342
|
const cur = p.cliBridge ?? {};
|
|
11974
12343
|
p.cliBridge = { ...cur, keepAwake: { ...cur.keepAwake ?? {}, enabled: on } };
|
|
11975
|
-
});
|
|
12344
|
+
}, { render: false });
|
|
12345
|
+
void patch(evt, renderCoffeeSettings);
|
|
11976
12346
|
}).on(DM.doctor, async ({ evt }) => {
|
|
11977
12347
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
11978
12348
|
await sendManagedCard(channel, evt.chatId, buildDoctorCard(await buildDoctorInfo()), evt.messageId).catch(
|
|
@@ -12114,6 +12484,78 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
12114
12484
|
}).on(DM.setConcurrency, ({ evt, value }) => {
|
|
12115
12485
|
const n = Number(value.v);
|
|
12116
12486
|
if (Number.isFinite(n)) applyPref(evt, (p) => p.maxConcurrentRuns = n);
|
|
12487
|
+
}).on(DM.commentSettings, ({ evt }) => {
|
|
12488
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12489
|
+
void patch(evt, () => renderCommentSettings());
|
|
12490
|
+
}).on(DM.commentSetBackend, ({ evt, value }) => {
|
|
12491
|
+
const v = typeof value.v === "string" ? value.v : void 0;
|
|
12492
|
+
applyCommentsPref(evt, (c) => {
|
|
12493
|
+
c.backend = v && backendIds().includes(v) ? v : void 0;
|
|
12494
|
+
c.model = void 0;
|
|
12495
|
+
c.effort = void 0;
|
|
12496
|
+
});
|
|
12497
|
+
}).on(DM.commentSubmit, ({ evt, formValue }) => {
|
|
12498
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12499
|
+
const modelId = selectValue(formValue, "model");
|
|
12500
|
+
const effort = asEffort(selectValue(formValue, "effort"));
|
|
12501
|
+
applyCommentsPref(evt, (c) => {
|
|
12502
|
+
if (modelId) c.model = modelId;
|
|
12503
|
+
if (effort) c.effort = effort;
|
|
12504
|
+
});
|
|
12505
|
+
}).on(DM.commentEditPrompt, ({ evt }) => {
|
|
12506
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12507
|
+
void patch(
|
|
12508
|
+
evt,
|
|
12509
|
+
async () => buildCommentPromptCard(
|
|
12510
|
+
await loadCommentInstructions(paths.commentInstructionsFile),
|
|
12511
|
+
void 0,
|
|
12512
|
+
paths.commentInstructionsFile
|
|
12513
|
+
)
|
|
12514
|
+
);
|
|
12515
|
+
}).on(DM.commentPromptSubmit, ({ evt, formValue }) => {
|
|
12516
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12517
|
+
const content = typeof formValue?.prompt === "string" ? formValue.prompt : "";
|
|
12518
|
+
void (async () => {
|
|
12519
|
+
if (!content.trim()) {
|
|
12520
|
+
const cur = await loadCommentInstructions(paths.commentInstructionsFile);
|
|
12521
|
+
await patch(evt, buildCommentPromptCard(cur, "\u26A0\uFE0F \u63D0\u793A\u8BCD\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u672A\u4FDD\u5B58\u3002", paths.commentInstructionsFile));
|
|
12522
|
+
return;
|
|
12523
|
+
}
|
|
12524
|
+
await saveCommentInstructions(paths.commentInstructionsFile, content).catch(
|
|
12525
|
+
(err) => log.fail("console", err, { phase: "save-comment-prompt" })
|
|
12526
|
+
);
|
|
12527
|
+
const n = await syncAllCommentInstructions(paths.commentsRootDir, content, cfg.accounts.app.tenant).catch(
|
|
12528
|
+
() => 0
|
|
12529
|
+
);
|
|
12530
|
+
await patch(
|
|
12531
|
+
evt,
|
|
12532
|
+
() => buildCommentPromptCard(
|
|
12533
|
+
content,
|
|
12534
|
+
`\u2705 \u63D0\u793A\u8BCD\u5DF2\u4FDD\u5B58\uFF0C\u5DF2\u540C\u6B65\u5230 ${n} \u4E2A\u6587\u6863\uFF08\u542B\u5386\u53F2\uFF09\uFF0C\u4E0B\u4E00\u6761\u8BC4\u8BBA\u751F\u6548\u3002`,
|
|
12535
|
+
paths.commentInstructionsFile
|
|
12536
|
+
)
|
|
12537
|
+
);
|
|
12538
|
+
})();
|
|
12539
|
+
}).on(DM.commentResetPrompt, ({ evt }) => {
|
|
12540
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12541
|
+
void (async () => {
|
|
12542
|
+
await saveCommentInstructions(paths.commentInstructionsFile, DEFAULT_COMMENT_INSTRUCTIONS).catch(
|
|
12543
|
+
(err) => log.fail("console", err, { phase: "reset-comment-prompt" })
|
|
12544
|
+
);
|
|
12545
|
+
const n = await syncAllCommentInstructions(
|
|
12546
|
+
paths.commentsRootDir,
|
|
12547
|
+
DEFAULT_COMMENT_INSTRUCTIONS,
|
|
12548
|
+
cfg.accounts.app.tenant
|
|
12549
|
+
).catch(() => 0);
|
|
12550
|
+
await patch(
|
|
12551
|
+
evt,
|
|
12552
|
+
() => buildCommentPromptCard(
|
|
12553
|
+
DEFAULT_COMMENT_INSTRUCTIONS,
|
|
12554
|
+
`\u21A9\uFE0F \u5DF2\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u63D0\u793A\u8BCD\uFF0C\u5DF2\u540C\u6B65\u5230 ${n} \u4E2A\u6587\u6863\uFF08\u542B\u5386\u53F2\uFF09\uFF0C\u4E0B\u4E00\u6761\u8BC4\u8BBA\u751F\u6548\u3002`,
|
|
12555
|
+
paths.commentInstructionsFile
|
|
12556
|
+
)
|
|
12557
|
+
);
|
|
12558
|
+
})();
|
|
12117
12559
|
}).on(GS.setNoMention, ({ evt, value }) => {
|
|
12118
12560
|
if (!isAdmin(cfg, evt.operator?.openId ?? "")) return;
|
|
12119
12561
|
const on = value.v === "on";
|
|
@@ -12932,18 +13374,39 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
12932
13374
|
if (!evt.mentionedBot) return log.info("comment", "skip", { reason: "not-mentioned" });
|
|
12933
13375
|
if (!SUPPORTED_FILE_TYPES.has(evt.fileType))
|
|
12934
13376
|
return log.info("comment", "skip", { reason: "unsupported-fileType", fileType: evt.fileType });
|
|
13377
|
+
const operatorId = evt.operator?.openId ?? "";
|
|
13378
|
+
if (!isAdmin(cfg, operatorId))
|
|
13379
|
+
return log.info("comment", "skip", { reason: "not-admin", sender: operatorId.slice(-6) || "(unknown)" });
|
|
12935
13380
|
const resolved = await resolveComment(channel, evt);
|
|
12936
13381
|
if (!resolved) return log.info("comment", "skip", { reason: "no-target-or-empty" });
|
|
12937
13382
|
const { target, ctx } = resolved;
|
|
12938
13383
|
log.info("comment", "parsed", { isWhole: ctx.isWhole, hasQuote: Boolean(ctx.quote) });
|
|
12939
|
-
const
|
|
12940
|
-
const
|
|
13384
|
+
const sessionKey = commentSessionKey(target.fileToken, evt.commentId);
|
|
13385
|
+
const cwd = commentCwd(paths.commentsRootDir, target.fileType, target.fileToken);
|
|
13386
|
+
const rawInstructions = await loadCommentInstructions(paths.commentInstructionsFile);
|
|
13387
|
+
const instructions = renderCommentInstructions(
|
|
13388
|
+
rawInstructions,
|
|
13389
|
+
cfg.accounts.app.tenant,
|
|
13390
|
+
target.fileType,
|
|
13391
|
+
target.fileToken
|
|
13392
|
+
);
|
|
13393
|
+
let synced = true;
|
|
13394
|
+
await syncCommentInstructions(cwd, instructions).catch((err) => {
|
|
13395
|
+
synced = false;
|
|
13396
|
+
log.warn("comment", "sync-instructions-failed", { err: err instanceof Error ? err.message : String(err) });
|
|
13397
|
+
});
|
|
13398
|
+
const facts = buildCommentPrompt(target, ctx, cfg.accounts.app.tenant);
|
|
13399
|
+
const prompt = synced ? facts : `${instructions}
|
|
13400
|
+
|
|
13401
|
+
---
|
|
13402
|
+
|
|
13403
|
+
${facts}`;
|
|
12941
13404
|
const reacted = ctx.targetReplyId ? await addCommentReaction(channel, target, ctx.targetReplyId) : false;
|
|
12942
13405
|
try {
|
|
12943
13406
|
await withDocLock(sessionKey, async () => {
|
|
12944
13407
|
const release2 = await sema.acquire();
|
|
12945
13408
|
try {
|
|
12946
|
-
const thread = await resolveDocThread(sessionKey, ctx.question);
|
|
13409
|
+
const thread = await resolveDocThread(sessionKey, cwd, instructions, ctx.question);
|
|
12947
13410
|
const rec = await getSession(sessionKey);
|
|
12948
13411
|
const run = thread.runStreamed({ text: prompt }, { model: rec?.model, effort: rec?.effort });
|
|
12949
13412
|
let state = initialState;
|
|
@@ -12957,6 +13420,7 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
12957
13420
|
if (tid) void thread.abort(tid).catch(() => void 0);
|
|
12958
13421
|
void thread.close().catch(() => void 0);
|
|
12959
13422
|
sessions.delete(sessionKey);
|
|
13423
|
+
commentInstrUsed.delete(sessionKey);
|
|
12960
13424
|
} else {
|
|
12961
13425
|
touchSession(sessionKey);
|
|
12962
13426
|
await patchSession(sessionKey, { updatedAt: Date.now() });
|
|
@@ -12994,12 +13458,15 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
12994
13458
|
});
|
|
12995
13459
|
return run;
|
|
12996
13460
|
}
|
|
12997
|
-
async function resolveDocThread(sessionKey, question) {
|
|
13461
|
+
async function resolveDocThread(sessionKey, cwd, instructions, question) {
|
|
12998
13462
|
const live = sessions.get(sessionKey);
|
|
12999
13463
|
if (live) {
|
|
13000
|
-
|
|
13464
|
+
const alive = live.isAlive();
|
|
13465
|
+
if (alive && commentInstrUsed.get(sessionKey) === instructions) return live;
|
|
13466
|
+
if (alive && commentInstrUsed.get(sessionKey) !== instructions) void live.close().catch(() => void 0);
|
|
13001
13467
|
sessions.delete(sessionKey);
|
|
13002
|
-
|
|
13468
|
+
commentInstrUsed.delete(sessionKey);
|
|
13469
|
+
log.info("agent", alive ? "comment-instr-changed-evict" : "dead-thread-evict", { sessionKey });
|
|
13003
13470
|
}
|
|
13004
13471
|
const rec = await getSession(sessionKey);
|
|
13005
13472
|
if (rec) {
|
|
@@ -13011,20 +13478,27 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
13011
13478
|
effort: rec.effort
|
|
13012
13479
|
});
|
|
13013
13480
|
trackSession(sessionKey, resumed);
|
|
13481
|
+
commentInstrUsed.set(sessionKey, instructions);
|
|
13014
13482
|
return resumed;
|
|
13015
13483
|
} catch (err) {
|
|
13016
13484
|
log.fail("agent", err, { phase: "comment-resume", sessionKey });
|
|
13017
13485
|
}
|
|
13018
13486
|
}
|
|
13019
|
-
const
|
|
13020
|
-
const
|
|
13487
|
+
const comments = getCommentsConfig(cfg);
|
|
13488
|
+
const be = backendFor(comments.backend);
|
|
13489
|
+
const { model, effort } = pickDefault(await listModels(be), {
|
|
13490
|
+
model: comments.model,
|
|
13491
|
+
effort: comments.effort
|
|
13492
|
+
});
|
|
13493
|
+
const fresh = await be.startThread({ cwd, model, effort });
|
|
13021
13494
|
trackSession(sessionKey, fresh);
|
|
13495
|
+
commentInstrUsed.set(sessionKey, instructions);
|
|
13022
13496
|
await upsertSession({
|
|
13023
13497
|
threadId: sessionKey,
|
|
13024
13498
|
chatId: sessionKey,
|
|
13025
|
-
cwd
|
|
13499
|
+
cwd,
|
|
13026
13500
|
sessionId: fresh.sessionId,
|
|
13027
|
-
backend:
|
|
13501
|
+
backend: be.id,
|
|
13028
13502
|
model,
|
|
13029
13503
|
effort,
|
|
13030
13504
|
summary: question.slice(0, 80),
|
|
@@ -13426,7 +13900,7 @@ function createAdminIpcResponder(execute, send) {
|
|
|
13426
13900
|
// src/admin/service.ts
|
|
13427
13901
|
init_bots();
|
|
13428
13902
|
init_paths();
|
|
13429
|
-
import { readFile as
|
|
13903
|
+
import { readFile as readFile13, rm as rm7 } from "fs/promises";
|
|
13430
13904
|
init_schema();
|
|
13431
13905
|
init_store();
|
|
13432
13906
|
init_schema();
|
|
@@ -13498,8 +13972,8 @@ init_logger();
|
|
|
13498
13972
|
|
|
13499
13973
|
// src/admin/host.ts
|
|
13500
13974
|
init_paths();
|
|
13501
|
-
import { readdir as
|
|
13502
|
-
import { dirname as
|
|
13975
|
+
import { readdir as readdir5, stat as stat4 } from "fs/promises";
|
|
13976
|
+
import { dirname as dirname13, join as join21, resolve as resolve8 } from "path";
|
|
13503
13977
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
13504
13978
|
import { arch, platform as platform2, release } from "os";
|
|
13505
13979
|
function toDaemonStatus(opts) {
|
|
@@ -13523,12 +13997,12 @@ async function dirBytes(dir) {
|
|
|
13523
13997
|
let total = 0;
|
|
13524
13998
|
let entries;
|
|
13525
13999
|
try {
|
|
13526
|
-
entries = await
|
|
14000
|
+
entries = await readdir5(dir);
|
|
13527
14001
|
} catch {
|
|
13528
14002
|
return 0;
|
|
13529
14003
|
}
|
|
13530
14004
|
for (const name of entries) {
|
|
13531
|
-
const full =
|
|
14005
|
+
const full = join21(dir, name);
|
|
13532
14006
|
try {
|
|
13533
14007
|
const st = await stat4(full);
|
|
13534
14008
|
if (st.isDirectory()) total += await dirBytes(full);
|
|
@@ -13538,7 +14012,7 @@ async function dirBytes(dir) {
|
|
|
13538
14012
|
}
|
|
13539
14013
|
return total;
|
|
13540
14014
|
}
|
|
13541
|
-
async function collectHostDoctor(logsDir2 =
|
|
14015
|
+
async function collectHostDoctor(logsDir2 = join21(paths.appDir, "logs")) {
|
|
13542
14016
|
return {
|
|
13543
14017
|
node: process.version,
|
|
13544
14018
|
platform: platform2(),
|
|
@@ -13551,7 +14025,7 @@ async function collectHostDoctor(logsDir2 = join20(paths.appDir, "logs")) {
|
|
|
13551
14025
|
};
|
|
13552
14026
|
}
|
|
13553
14027
|
function resolveCliBinPath3() {
|
|
13554
|
-
const distDir =
|
|
14028
|
+
const distDir = dirname13(fileURLToPath5(import.meta.url));
|
|
13555
14029
|
return resolve8(distDir, "..", "bin", "feishu-codex-bridge.mjs");
|
|
13556
14030
|
}
|
|
13557
14031
|
function buildDaemonControlCommand(action, binPath = resolveCliBinPath3(), nodePath = process.execPath) {
|
|
@@ -13599,7 +14073,7 @@ function createAdminService(deps = {}) {
|
|
|
13599
14073
|
}
|
|
13600
14074
|
async function lockFileRunState(botId) {
|
|
13601
14075
|
try {
|
|
13602
|
-
const raw = await
|
|
14076
|
+
const raw = await readFile13(botPaths(botId).processesFile, "utf8");
|
|
13603
14077
|
const rec = JSON.parse(raw);
|
|
13604
14078
|
if (typeof rec.pid === "number" && isAlive2(rec.pid)) {
|
|
13605
14079
|
return { running: true, pid: rec.pid, startedAt: rec.startedAt };
|
|
@@ -14127,7 +14601,7 @@ import { createServer } from "http";
|
|
|
14127
14601
|
import { randomUUID as randomUUID7, timingSafeEqual } from "crypto";
|
|
14128
14602
|
import { mkdirSync as mkdirSync3, watch } from "fs";
|
|
14129
14603
|
import { open as open2, stat as stat5 } from "fs/promises";
|
|
14130
|
-
import { join as
|
|
14604
|
+
import { join as join22 } from "path";
|
|
14131
14605
|
|
|
14132
14606
|
// src/web/ui.ts
|
|
14133
14607
|
var UI_PURE_JS = `
|
|
@@ -16802,7 +17276,7 @@ var SSE_INITIAL_TAIL_BYTES = 16 * 1024;
|
|
|
16802
17276
|
function createWebServer(opts) {
|
|
16803
17277
|
const token = opts.token ?? randomUUID7();
|
|
16804
17278
|
const html = opts.html ?? UI_HTML;
|
|
16805
|
-
const logDir = opts.logDir ??
|
|
17279
|
+
const logDir = opts.logDir ?? join22(paths.appDir, "logs");
|
|
16806
17280
|
const sseCleanups = /* @__PURE__ */ new Set();
|
|
16807
17281
|
const installJobs = /* @__PURE__ */ new Map();
|
|
16808
17282
|
let qrSession = null;
|
|
@@ -17440,7 +17914,7 @@ function isLoopbackOrigin(origin) {
|
|
|
17440
17914
|
}
|
|
17441
17915
|
}
|
|
17442
17916
|
function todayLogFile(dir) {
|
|
17443
|
-
return
|
|
17917
|
+
return join22(dir, `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.log`);
|
|
17444
17918
|
}
|
|
17445
17919
|
function clampInt(raw, min, max, fallback) {
|
|
17446
17920
|
const n = Number(raw);
|
|
@@ -17643,7 +18117,7 @@ async function runSupervisor(bots) {
|
|
|
17643
18117
|
// src/core/single-instance.ts
|
|
17644
18118
|
init_paths();
|
|
17645
18119
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, statSync as statSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
17646
|
-
import { dirname as
|
|
18120
|
+
import { dirname as dirname14 } from "path";
|
|
17647
18121
|
var CLAIM_ATTEMPTS = 5;
|
|
17648
18122
|
var PID_REUSE_SLACK_MS = 15e3;
|
|
17649
18123
|
var EMPTY_LOCK_STALE_MS = 2e3;
|
|
@@ -17689,7 +18163,7 @@ function isLiveHolder(rec, appId) {
|
|
|
17689
18163
|
return true;
|
|
17690
18164
|
}
|
|
17691
18165
|
function acquireSingleInstanceLock(appId, file = paths.processesFile) {
|
|
17692
|
-
mkdirSync4(
|
|
18166
|
+
mkdirSync4(dirname14(file), { recursive: true });
|
|
17693
18167
|
const record = { pid: process.pid, appId, startedAt: Date.now() };
|
|
17694
18168
|
for (let attempt = 0; attempt < CLAIM_ATTEMPTS; attempt++) {
|
|
17695
18169
|
try {
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,14 @@ declare const paths: {
|
|
|
26
26
|
readonly projectsFile: string;
|
|
27
27
|
/** 在跑的 start 进程注册中心(同 App 冲突检测;当前 bot) */
|
|
28
28
|
readonly processesFile: string;
|
|
29
|
+
/** 云文档评论 @bot 的可编辑提示词 master 文件(当前 bot)。用户直接编辑这一份,
|
|
30
|
+
* 桥在每条评论运行前把它同步进该文档的评论工作目录(AGENTS.md / CLAUDE.md)。
|
|
31
|
+
* 首次缺失时由 bot/comments.ts 用内置默认模板自动落地。 */
|
|
32
|
+
readonly commentInstructionsFile: string;
|
|
33
|
+
/** 评论工作目录根(**当前 bot**):每个被评论文档一个 `comment-<type>-<token>` 子目录,
|
|
34
|
+
* 放同步进去的 AGENTS.md / CLAUDE.md。放 per-bot 目录下,与其它 bot 隔离——否则
|
|
35
|
+
* 编辑提示词时的「全量同步」会越界改到别的 bot 的评论目录。 */
|
|
36
|
+
readonly commentsRootDir: string;
|
|
29
37
|
/**
|
|
30
38
|
* Local CLI hook IPC endpoint for the current bot. macOS/Linux use a Unix
|
|
31
39
|
* domain socket file; Windows has none, so Node maps a `\\.\pipe\…` path to a
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,18 @@ var paths = {
|
|
|
34
34
|
get processesFile() {
|
|
35
35
|
return join(currentBotDir, "processes.json");
|
|
36
36
|
},
|
|
37
|
+
/** 云文档评论 @bot 的可编辑提示词 master 文件(当前 bot)。用户直接编辑这一份,
|
|
38
|
+
* 桥在每条评论运行前把它同步进该文档的评论工作目录(AGENTS.md / CLAUDE.md)。
|
|
39
|
+
* 首次缺失时由 bot/comments.ts 用内置默认模板自动落地。 */
|
|
40
|
+
get commentInstructionsFile() {
|
|
41
|
+
return join(currentBotDir, "comment-instructions.md");
|
|
42
|
+
},
|
|
43
|
+
/** 评论工作目录根(**当前 bot**):每个被评论文档一个 `comment-<type>-<token>` 子目录,
|
|
44
|
+
* 放同步进去的 AGENTS.md / CLAUDE.md。放 per-bot 目录下,与其它 bot 隔离——否则
|
|
45
|
+
* 编辑提示词时的「全量同步」会越界改到别的 bot 的评论目录。 */
|
|
46
|
+
get commentsRootDir() {
|
|
47
|
+
return join(currentBotDir, "comments");
|
|
48
|
+
},
|
|
37
49
|
/**
|
|
38
50
|
* Local CLI hook IPC endpoint for the current bot. macOS/Linux use a Unix
|
|
39
51
|
* domain socket file; Windows has none, so Node maps a `\\.\pipe\…` path to a
|