@modelzen/feishu-codex-bridge 0.6.3 → 0.6.4
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 +441 -54
- 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
|
}
|
|
@@ -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;
|
|
@@ -4166,6 +4186,8 @@ var ClaudeAgentThread = class {
|
|
|
4166
4186
|
...toSdkEffort(cfg.effort) ? { effort: toSdkEffort(cfg.effort) } : {},
|
|
4167
4187
|
...cfg.resume ? { resume: cfg.sessionId } : { sessionId: cfg.sessionId },
|
|
4168
4188
|
...cfg.systemPromptAppend ? { systemPrompt: { type: "preset", preset: "claude_code", append: cfg.systemPromptAppend } } : {},
|
|
4189
|
+
...cfg.settingSources ? { settingSources: cfg.settingSources } : {},
|
|
4190
|
+
...cfg.env ? { env: cfg.env } : {},
|
|
4169
4191
|
// permission tier (permissionMode / sandbox / canUseTool / disallowedTools)
|
|
4170
4192
|
...cfg.permission,
|
|
4171
4193
|
stderr: (d) => {
|
|
@@ -4449,6 +4471,13 @@ var ClaudeAgentThread = class {
|
|
|
4449
4471
|
|
|
4450
4472
|
// src/agent/claude-agent/backend.ts
|
|
4451
4473
|
var SDK_PKG = "@anthropic-ai/claude-agent-sdk";
|
|
4474
|
+
var BRIDGE_SETTING_SOURCES = ["user", "project"];
|
|
4475
|
+
function bridgeClaudeEnv() {
|
|
4476
|
+
const base = {};
|
|
4477
|
+
for (const [k2, v] of Object.entries(process.env)) if (typeof v === "string") base[k2] = v;
|
|
4478
|
+
base.FEISHU_CODEX_BRIDGE = "1";
|
|
4479
|
+
return base;
|
|
4480
|
+
}
|
|
4452
4481
|
var sdkPromise;
|
|
4453
4482
|
function loadSdk() {
|
|
4454
4483
|
sdkPromise ??= loadBackendDep(SDK_PKG);
|
|
@@ -4531,6 +4560,8 @@ var ClaudeAgentBackend = class {
|
|
|
4531
4560
|
effort: opts.effort,
|
|
4532
4561
|
permission: permissionOptions(opts.mode, opts.network, opts.cwd),
|
|
4533
4562
|
systemPromptAppend: BRIDGE_DEVELOPER_INSTRUCTIONS,
|
|
4563
|
+
settingSources: BRIDGE_SETTING_SOURCES,
|
|
4564
|
+
env: bridgeClaudeEnv(),
|
|
4534
4565
|
query: sdk.query
|
|
4535
4566
|
});
|
|
4536
4567
|
}
|
|
@@ -4544,6 +4575,8 @@ var ClaudeAgentBackend = class {
|
|
|
4544
4575
|
effort: opts.effort,
|
|
4545
4576
|
permission: permissionOptions(opts.mode, opts.network, opts.cwd),
|
|
4546
4577
|
systemPromptAppend: BRIDGE_DEVELOPER_INSTRUCTIONS,
|
|
4578
|
+
settingSources: BRIDGE_SETTING_SOURCES,
|
|
4579
|
+
env: bridgeClaudeEnv(),
|
|
4547
4580
|
query: sdk.query
|
|
4548
4581
|
});
|
|
4549
4582
|
}
|
|
@@ -5633,6 +5666,8 @@ var DM = {
|
|
|
5633
5666
|
joinGroupSubmit: "dm.joinGroup.submit",
|
|
5634
5667
|
projects: "dm.projects",
|
|
5635
5668
|
settings: "dm.settings",
|
|
5669
|
+
// ☕ 咖啡一下(离开接管):从全局设置卡进入的二级卡片(仿云文档评论那样的子卡入口)
|
|
5670
|
+
coffeeSettings: "dm.coffee.settings",
|
|
5636
5671
|
doctor: "dm.doctor",
|
|
5637
5672
|
reconnect: "dm.reconnect",
|
|
5638
5673
|
update: "dm.update",
|
|
@@ -5675,9 +5710,16 @@ var DM = {
|
|
|
5675
5710
|
modelDefaultSubmit: "dm.proj.modelDefault.submit",
|
|
5676
5711
|
// 🔐 权限:codex 沙箱档位(管理员档 + 普通用户档)+ 联网,做成下拉表单(选+提交)
|
|
5677
5712
|
permission: "dm.proj.perm",
|
|
5678
|
-
permissionSubmit: "dm.proj.perm.submit"
|
|
5713
|
+
permissionSubmit: "dm.proj.perm.submit",
|
|
5679
5714
|
// 🧠 后端 backend/backendSubmit 已移除:后端改为「创建时选定、运行时固定、不支持切换」,
|
|
5680
5715
|
// 新建卡选后端见 newProjectSubmit;项目设置卡只读展示。
|
|
5716
|
+
// 📝 云文档评论 @bot 全局设置:后端按钮(级联)→模型/强度下拉表单提交;提示词在卡内编辑。
|
|
5717
|
+
commentSettings: "dm.comment.settings",
|
|
5718
|
+
commentSetBackend: "dm.comment.setBackend",
|
|
5719
|
+
commentSubmit: "dm.comment.submit",
|
|
5720
|
+
commentEditPrompt: "dm.comment.editPrompt",
|
|
5721
|
+
commentPromptSubmit: "dm.comment.promptSubmit",
|
|
5722
|
+
commentResetPrompt: "dm.comment.resetPrompt"
|
|
5681
5723
|
};
|
|
5682
5724
|
var GS = {
|
|
5683
5725
|
setNoMention: "gs.noMention",
|
|
@@ -6149,7 +6191,7 @@ function settingItem(name, desc, actionId, current, opts) {
|
|
|
6149
6191
|
actions(opts.map((o) => button(o.label, { a: actionId, v: o.value }, o.value === current ? "primary" : "default")))
|
|
6150
6192
|
];
|
|
6151
6193
|
}
|
|
6152
|
-
function buildSettingsCard(cfg
|
|
6194
|
+
function buildSettingsCard(cfg) {
|
|
6153
6195
|
const watchdogSec = cfg.preferences?.runIdleTimeoutSeconds ?? 120;
|
|
6154
6196
|
return card(
|
|
6155
6197
|
[
|
|
@@ -6207,13 +6249,169 @@ function buildSettingsCard(cfg, localAgents = []) {
|
|
|
6207
6249
|
{ label: "20", value: "20" }
|
|
6208
6250
|
]
|
|
6209
6251
|
),
|
|
6210
|
-
|
|
6252
|
+
hr(),
|
|
6253
|
+
settingSection("\u2615 \u5496\u5561\u4E00\u4E0B"),
|
|
6254
|
+
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"),
|
|
6255
|
+
actions([button("\u8BBE\u7F6E\u5496\u5561\u4E00\u4E0B / \u901A\u77E5 / \u4FDD\u6D3B / hooks", { a: DM.coffeeSettings }, "primary")]),
|
|
6256
|
+
hr(),
|
|
6257
|
+
settingSection("\u{1F4DD} \u4E91\u6587\u6863\u8BC4\u8BBA"),
|
|
6258
|
+
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"),
|
|
6259
|
+
actions([button("\u8BBE\u7F6E\u540E\u7AEF / \u6A21\u578B / \u5F3A\u5EA6 / \u63D0\u793A\u8BCD", { a: DM.commentSettings }, "primary")]),
|
|
6211
6260
|
hr(),
|
|
6212
6261
|
actions([button("\u{1F46E} \u7BA1\u7406\u5458", { a: DM.admins }), button("\u2B05\uFE0F \u83DC\u5355", { a: DM.menu })])
|
|
6213
6262
|
],
|
|
6214
6263
|
{ header: { title: "\u2699\uFE0F \u5168\u5C40\u8BBE\u7F6E", template: "blue" } }
|
|
6215
6264
|
);
|
|
6216
6265
|
}
|
|
6266
|
+
function buildCoffeeSettingsCard(section) {
|
|
6267
|
+
return card(
|
|
6268
|
+
[
|
|
6269
|
+
...section.slice(1),
|
|
6270
|
+
// 去掉为「内联进主卡」而加的开头 hr()
|
|
6271
|
+
hr(),
|
|
6272
|
+
actions([button("\u2B05\uFE0F \u8FD4\u56DE\u8BBE\u7F6E", { a: DM.settings })])
|
|
6273
|
+
],
|
|
6274
|
+
{ header: { title: "\u2615 \u5496\u5561\u4E00\u4E0B", template: "blue" } }
|
|
6275
|
+
);
|
|
6276
|
+
}
|
|
6277
|
+
var LARK_CLI_DOC_URL = "https://bytedance.larkoffice.com/wiki/ILuTww7Xcimb6GkhH0mcK2f4nS7";
|
|
6278
|
+
function buildCommentSettingsCard(cfg, backendOptions, models, notice) {
|
|
6279
|
+
const comments = cfg.preferences?.comments ?? {};
|
|
6280
|
+
const visible = models.filter((m) => !m.hidden);
|
|
6281
|
+
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;
|
|
6282
|
+
const explicit = comments.model ? visible.find((m) => m.id === comments.model) : void 0;
|
|
6283
|
+
const curModel = explicit ?? visible.find((m) => m.isDefault) ?? visible[0];
|
|
6284
|
+
const unionEfforts = EFFORT_ORDER.filter((e) => visible.some((m) => (m.supportedEfforts ?? []).includes(e)));
|
|
6285
|
+
const curEffort = comments.effort && (curModel?.supportedEfforts ?? []).includes(comments.effort) ? comments.effort : curModel?.defaultEffort;
|
|
6286
|
+
const canPickModel = visible.length > 1;
|
|
6287
|
+
const canPickEffort = unionEfforts.length > 0;
|
|
6288
|
+
const els = [
|
|
6289
|
+
...notice ? [md(notice)] : [],
|
|
6290
|
+
md("**\u{1F4DD} \u4E91\u6587\u6863\u8BC4\u8BBA @bot**"),
|
|
6291
|
+
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"),
|
|
6292
|
+
hr()
|
|
6293
|
+
];
|
|
6294
|
+
if (backendOptions.length > 1) {
|
|
6295
|
+
els.push(
|
|
6296
|
+
md("\u{1F9E0} **\u540E\u7AEF**"),
|
|
6297
|
+
actions(
|
|
6298
|
+
backendOptions.map(
|
|
6299
|
+
(b) => button(b.label, { a: DM.commentSetBackend, v: b.id }, b.id === curBackend ? "primary" : "default")
|
|
6300
|
+
)
|
|
6301
|
+
)
|
|
6302
|
+
);
|
|
6303
|
+
} else {
|
|
6304
|
+
els.push(md(`\u{1F9E0} **\u540E\u7AEF**\uFF1A${backendOptions[0]?.label ?? curBackend}`));
|
|
6305
|
+
}
|
|
6306
|
+
if (canPickModel || canPickEffort) {
|
|
6307
|
+
const formEls = [];
|
|
6308
|
+
if (canPickModel) {
|
|
6309
|
+
formEls.push(
|
|
6310
|
+
md("\u{1F916} **\u6A21\u578B**"),
|
|
6311
|
+
selectMenu({
|
|
6312
|
+
name: "model",
|
|
6313
|
+
placeholder: "\u9009\u62E9\u6A21\u578B",
|
|
6314
|
+
options: visible.map((m) => ({ label: m.displayName, value: m.id })),
|
|
6315
|
+
initial: curModel?.id
|
|
6316
|
+
})
|
|
6317
|
+
);
|
|
6318
|
+
} else {
|
|
6319
|
+
formEls.push(md(`\u{1F916} **\u6A21\u578B**\uFF1A${curModel?.displayName ?? "\u540E\u7AEF\u9ED8\u8BA4"}\uFF08\u8BE5\u540E\u7AEF\u4EC5\u4E00\u4E2A\u6A21\u578B\uFF09`));
|
|
6320
|
+
}
|
|
6321
|
+
if (canPickEffort) {
|
|
6322
|
+
formEls.push(
|
|
6323
|
+
md("\u{1F39A} **\u63A8\u7406\u5F3A\u5EA6**"),
|
|
6324
|
+
selectMenu({
|
|
6325
|
+
name: "effort",
|
|
6326
|
+
placeholder: "\u9009\u62E9\u63A8\u7406\u5F3A\u5EA6",
|
|
6327
|
+
options: unionEfforts.map((e) => ({ label: EFFORT_LABEL[e], value: e })),
|
|
6328
|
+
initial: curEffort
|
|
6329
|
+
})
|
|
6330
|
+
);
|
|
6331
|
+
}
|
|
6332
|
+
formEls.push(actions([submitButton("\u2705 \u4FDD\u5B58\u6A21\u578B / \u5F3A\u5EA6", { a: DM.commentSubmit }, "primary", "submit_comment")]));
|
|
6333
|
+
els.push(form("comment_model_effort", formEls));
|
|
6334
|
+
} else {
|
|
6335
|
+
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"));
|
|
6336
|
+
}
|
|
6337
|
+
els.push(
|
|
6338
|
+
hr(),
|
|
6339
|
+
md("\u270D\uFE0F **\u63D0\u793A\u8BCD**"),
|
|
6340
|
+
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"),
|
|
6341
|
+
actions([button("\u7F16\u8F91\u63D0\u793A\u8BCD", { a: DM.commentEditPrompt }, "primary")]),
|
|
6342
|
+
hr(),
|
|
6343
|
+
md("\u{1F4CE} **\u914D\u5408\u98DE\u4E66 CLI**"),
|
|
6344
|
+
note(
|
|
6345
|
+
`\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`
|
|
6346
|
+
),
|
|
6347
|
+
hr(),
|
|
6348
|
+
actions([button("\u2B05\uFE0F \u8FD4\u56DE\u8BBE\u7F6E", { a: DM.settings })])
|
|
6349
|
+
);
|
|
6350
|
+
return card(els, { header: { title: "\u{1F4DD} \u6587\u6863\u8BC4\u8BBA\u8BBE\u7F6E", template: "blue" } });
|
|
6351
|
+
}
|
|
6352
|
+
function buildCommentPromptCard(currentPrompt, notice, masterFile) {
|
|
6353
|
+
return card(
|
|
6354
|
+
[
|
|
6355
|
+
...notice ? [md(notice)] : [],
|
|
6356
|
+
md("**\u270D\uFE0F \u8BC4\u8BBA\u63D0\u793A\u8BCD**"),
|
|
6357
|
+
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"),
|
|
6358
|
+
md(
|
|
6359
|
+
[
|
|
6360
|
+
"**\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",
|
|
6361
|
+
"- `{docUrl}` \u6587\u6863\u94FE\u63A5",
|
|
6362
|
+
"- `{fileToken}` \u6587\u6863 token\uFF08\u94FE\u63A5\u91CC\u7C7B\u578B\u540E\u90A3\u6BB5\uFF09",
|
|
6363
|
+
"- `{fileType}` \u6587\u6863\u7C7B\u578B\uFF0C\u53D6\u503C\uFF1A",
|
|
6364
|
+
" - `doc`/`docx`\uFF08\u98DE\u4E66\u4E91\u6587\u6863\uFF09",
|
|
6365
|
+
" - `sheet`\uFF08\u98DE\u4E66\u8868\u683C\uFF09",
|
|
6366
|
+
" - `bitable`\uFF08\u591A\u7EF4\u8868\u683C\uFF09"
|
|
6367
|
+
].join("\n")
|
|
6368
|
+
),
|
|
6369
|
+
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"),
|
|
6370
|
+
form("comment_prompt", [
|
|
6371
|
+
input({
|
|
6372
|
+
name: "prompt",
|
|
6373
|
+
label: "\u63D0\u793A\u8BCD\u5185\u5BB9",
|
|
6374
|
+
value: currentPrompt,
|
|
6375
|
+
required: true,
|
|
6376
|
+
inputType: "multiline_text",
|
|
6377
|
+
rows: 12,
|
|
6378
|
+
width: "fill",
|
|
6379
|
+
maxLength: 1e3
|
|
6380
|
+
// Feishu input hard cap (range 1–1000); longer prompts → edit master file
|
|
6381
|
+
}),
|
|
6382
|
+
// 两个都是 form 提交按钮(普通 button 在 form 内不保证触发):保存读输入框内容落盘;
|
|
6383
|
+
// 重置忽略输入框、直接把内置默认写回 master 并同步(handler 端处理)。
|
|
6384
|
+
actions([
|
|
6385
|
+
submitButton("\u2705 \u4FDD\u5B58\u63D0\u793A\u8BCD", { a: DM.commentPromptSubmit }, "primary", "submit_prompt"),
|
|
6386
|
+
submitButton("\u21A9\uFE0F \u91CD\u7F6E\u4E3A\u9ED8\u8BA4", { a: DM.commentResetPrompt }, "default", "reset_prompt")
|
|
6387
|
+
])
|
|
6388
|
+
]),
|
|
6389
|
+
hr(),
|
|
6390
|
+
md("**\u{1F4E8} \u6BCF\u8F6E\u8BC4\u8BBA @\u6211\uFF0C\u6211\u4F1A\u6536\u5230\u4E0B\u9762\u6D88\u606F\uFF1A**"),
|
|
6391
|
+
md(
|
|
6392
|
+
[
|
|
6393
|
+
"\u6211\u5728\u98DE\u4E66\u4E91\u6587\u6863\u7684\u8BC4\u8BBA\u91CC @\u4E86\u4F60\u3002\u6587\u6863\u4FE1\u606F\uFF1A",
|
|
6394
|
+
"- \u94FE\u63A5\uFF1A{docUrl}",
|
|
6395
|
+
"- file_token\uFF1A{fileToken}",
|
|
6396
|
+
"- \u7C7B\u578B\uFF1A{fileType}",
|
|
6397
|
+
"- \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",
|
|
6398
|
+
"",
|
|
6399
|
+
// 空行结束列表,否则下一行被并进「评论范围」那个 bullet
|
|
6400
|
+
"\u7528\u6237\u9009\u4E2D\u7684\u539F\u6587\uFF1A\uFF08\u4EC5\u884C\u5185\u8BC4\u8BBA\u65F6\u9644\u4E0A\uFF09",
|
|
6401
|
+
"> \u2026\u2026\u88AB\u8BC4\u8BBA\u7684\u90A3\u6BB5\u6587\u5B57\u2026\u2026",
|
|
6402
|
+
"",
|
|
6403
|
+
// 空行结束引用,否则「用户的问题」被并进引用块
|
|
6404
|
+
"\u7528\u6237\u7684\u95EE\u9898\uFF1A\u2026\u2026\u8BC4\u8BBA\u6B63\u6587\u2026\u2026"
|
|
6405
|
+
].join("\n")
|
|
6406
|
+
),
|
|
6407
|
+
note(
|
|
6408
|
+
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"
|
|
6409
|
+
),
|
|
6410
|
+
actions([button("\u2B05\uFE0F \u8FD4\u56DE", { a: DM.commentSettings })])
|
|
6411
|
+
],
|
|
6412
|
+
{ header: { title: "\u270D\uFE0F \u7F16\u8F91\u63D0\u793A\u8BCD", template: "blue" }, widthMode: "fill" }
|
|
6413
|
+
);
|
|
6414
|
+
}
|
|
6217
6415
|
function buildWatchdogCustomCard(cfg) {
|
|
6218
6416
|
const cur = cfg.preferences?.runIdleTimeoutSeconds ?? 120;
|
|
6219
6417
|
return card(
|
|
@@ -10409,7 +10607,9 @@ function weaveSender(text, sender) {
|
|
|
10409
10607
|
|
|
10410
10608
|
// src/bot/comments.ts
|
|
10411
10609
|
init_logger();
|
|
10412
|
-
|
|
10610
|
+
import { mkdir as mkdir14, readdir as readdir4, readFile as readFile12, writeFile as writeFile11 } from "fs/promises";
|
|
10611
|
+
import { dirname as dirname12, join as join19 } from "path";
|
|
10612
|
+
var SUPPORTED_FILE_TYPES = /* @__PURE__ */ new Set(["doc", "docx", "sheet", "bitable"]);
|
|
10413
10613
|
var REPLY_MAX_CHARS = 2e3;
|
|
10414
10614
|
function apiErrCode(err) {
|
|
10415
10615
|
return err?.response?.data?.code;
|
|
@@ -10504,10 +10704,80 @@ var DOC_HOSTS = {
|
|
|
10504
10704
|
feishu: "feishu.cn",
|
|
10505
10705
|
lark: "larksuite.com"
|
|
10506
10706
|
};
|
|
10707
|
+
var DOC_URL_PATH = {
|
|
10708
|
+
doc: "docs",
|
|
10709
|
+
docx: "docx",
|
|
10710
|
+
sheet: "sheets",
|
|
10711
|
+
bitable: "base"
|
|
10712
|
+
};
|
|
10713
|
+
function buildDocUrl(tenant, fileType, fileToken) {
|
|
10714
|
+
const seg = DOC_URL_PATH[fileType] ?? fileType;
|
|
10715
|
+
return `https://${DOC_HOSTS[tenant]}/${seg}/${fileToken}`;
|
|
10716
|
+
}
|
|
10717
|
+
var DEFAULT_COMMENT_INSTRUCTIONS = `# \u98DE\u4E66\u4E91\u6587\u6863\u8BC4\u8BBA\u52A9\u624B
|
|
10718
|
+
|
|
10719
|
+
\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
|
|
10720
|
+
|
|
10721
|
+
## \u600E\u4E48\u505A
|
|
10722
|
+
- \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
|
|
10723
|
+
- \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
|
|
10724
|
+
- \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
|
|
10725
|
+
|
|
10726
|
+
## \u5FC5\u987B\u9075\u5B88
|
|
10727
|
+
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
|
|
10728
|
+
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
|
|
10729
|
+
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
|
|
10730
|
+
`;
|
|
10731
|
+
function commentSessionKey(fileToken, commentId) {
|
|
10732
|
+
return `doc:${fileToken}:${commentId}`;
|
|
10733
|
+
}
|
|
10734
|
+
function commentCwd(projectsRoot, fileType, fileToken) {
|
|
10735
|
+
return join19(projectsRoot, `comment-${fileType}-${fileToken}`);
|
|
10736
|
+
}
|
|
10737
|
+
async function loadCommentInstructions(masterFile) {
|
|
10738
|
+
try {
|
|
10739
|
+
const existing = await readFile12(masterFile, "utf8");
|
|
10740
|
+
return existing.trim() ? existing : DEFAULT_COMMENT_INSTRUCTIONS;
|
|
10741
|
+
} catch {
|
|
10742
|
+
}
|
|
10743
|
+
await mkdir14(dirname12(masterFile), { recursive: true }).catch(() => void 0);
|
|
10744
|
+
await writeFile11(masterFile, DEFAULT_COMMENT_INSTRUCTIONS, "utf8").catch(() => void 0);
|
|
10745
|
+
return DEFAULT_COMMENT_INSTRUCTIONS;
|
|
10746
|
+
}
|
|
10747
|
+
async function syncCommentInstructions(cwd, instructions) {
|
|
10748
|
+
await mkdir14(cwd, { recursive: true });
|
|
10749
|
+
await Promise.all([
|
|
10750
|
+
writeFile11(join19(cwd, "AGENTS.md"), instructions, "utf8"),
|
|
10751
|
+
writeFile11(join19(cwd, "CLAUDE.md"), instructions, "utf8")
|
|
10752
|
+
]);
|
|
10753
|
+
}
|
|
10754
|
+
async function saveCommentInstructions(masterFile, content) {
|
|
10755
|
+
await mkdir14(dirname12(masterFile), { recursive: true });
|
|
10756
|
+
await writeFile11(masterFile, content, "utf8");
|
|
10757
|
+
}
|
|
10758
|
+
function renderCommentInstructions(template, tenant, fileType, fileToken) {
|
|
10759
|
+
const docUrl = buildDocUrl(tenant, fileType, fileToken);
|
|
10760
|
+
return template.replace(/\{fileType\}/g, fileType).replace(/\{fileToken\}/g, fileToken).replace(/\{docUrl\}/g, docUrl);
|
|
10761
|
+
}
|
|
10762
|
+
async function syncAllCommentInstructions(projectsRoot, rawTemplate, tenant) {
|
|
10763
|
+
const entries = await readdir4(projectsRoot, { withFileTypes: true }).catch(() => []);
|
|
10764
|
+
let synced = 0;
|
|
10765
|
+
for (const e of entries) {
|
|
10766
|
+
if (!e.isDirectory() || !e.name.startsWith("comment-")) continue;
|
|
10767
|
+
const m = /^comment-([a-z]+)-(.+)$/.exec(e.name);
|
|
10768
|
+
const fileType = m?.[1];
|
|
10769
|
+
const fileToken = m?.[2];
|
|
10770
|
+
if (!fileType || !fileToken) continue;
|
|
10771
|
+
const rendered = renderCommentInstructions(rawTemplate, tenant, fileType, fileToken);
|
|
10772
|
+
await syncCommentInstructions(join19(projectsRoot, e.name), rendered).catch(() => void 0);
|
|
10773
|
+
synced++;
|
|
10774
|
+
}
|
|
10775
|
+
return synced;
|
|
10776
|
+
}
|
|
10507
10777
|
function buildCommentPrompt(target, ctx, tenant) {
|
|
10508
|
-
const docUrl =
|
|
10778
|
+
const docUrl = buildDocUrl(tenant, target.fileType, target.fileToken);
|
|
10509
10779
|
const parts = [];
|
|
10510
|
-
parts.push("\u6211\u5728\u98DE\u4E66\u4E91\u6587\u6863\u7684\u8BC4\u8BBA\u91CC
|
|
10780
|
+
parts.push("\u6211\u5728\u98DE\u4E66\u4E91\u6587\u6863\u7684\u8BC4\u8BBA\u91CC @\u4E86\u4F60\u3002\u6587\u6863\u4FE1\u606F\uFF1A");
|
|
10511
10781
|
parts.push(`- \u94FE\u63A5\uFF1A${docUrl}`);
|
|
10512
10782
|
parts.push(`- file_token\uFF1A${target.fileToken}`);
|
|
10513
10783
|
parts.push(`- \u7C7B\u578B\uFF1A${target.fileType}`);
|
|
@@ -10519,20 +10789,6 @@ function buildCommentPrompt(target, ctx, tenant) {
|
|
|
10519
10789
|
}
|
|
10520
10790
|
parts.push("");
|
|
10521
10791
|
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
10792
|
return parts.join("\n");
|
|
10537
10793
|
}
|
|
10538
10794
|
function stripMarkdown(s) {
|
|
@@ -10912,6 +11168,7 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
10912
11168
|
}
|
|
10913
11169
|
const active = /* @__PURE__ */ new Map();
|
|
10914
11170
|
const docLocks = /* @__PURE__ */ new Map();
|
|
11171
|
+
const commentInstrUsed = /* @__PURE__ */ new Map();
|
|
10915
11172
|
const sema = new Semaphore(getMaxConcurrentRuns(cfg));
|
|
10916
11173
|
const currentIdleMs = () => getRunIdleTimeoutMs(cfg) ?? 0;
|
|
10917
11174
|
const resumePending = /* @__PURE__ */ new Map();
|
|
@@ -11760,10 +12017,13 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
11760
12017
|
if (opts?.render !== false) void patch(evt, renderSettings);
|
|
11761
12018
|
}
|
|
11762
12019
|
let cliHookStatuses;
|
|
11763
|
-
|
|
12020
|
+
function renderSettings() {
|
|
12021
|
+
return buildSettingsCard(cfg);
|
|
12022
|
+
}
|
|
12023
|
+
async function renderCoffeeSettings(refreshHooks = false) {
|
|
11764
12024
|
if (refreshHooks || !cliHookStatuses) cliHookStatuses = await inspectCliBridgeHooks();
|
|
11765
12025
|
const cliPrefs = getCliBridgePreferences(cfg);
|
|
11766
|
-
const
|
|
12026
|
+
const section = cliBridgeSettingsSection({
|
|
11767
12027
|
enabled: cliPrefs.enabled,
|
|
11768
12028
|
statuses: cliHookStatuses,
|
|
11769
12029
|
canEnable: canEnableCliBridge(cfg),
|
|
@@ -11771,7 +12031,25 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
11771
12031
|
agents: cliPrefs.agents,
|
|
11772
12032
|
keepAwake: cliPrefs.keepAwake.enabled
|
|
11773
12033
|
});
|
|
11774
|
-
return
|
|
12034
|
+
return buildCoffeeSettingsCard(section);
|
|
12035
|
+
}
|
|
12036
|
+
function commentBackendOptions() {
|
|
12037
|
+
return visibleCatalog().filter((e) => e.id === DEFAULT_BACKEND_ID || isBackendEntryInstalled(e)).map((e) => ({ id: e.id, label: e.displayName }));
|
|
12038
|
+
}
|
|
12039
|
+
async function renderCommentSettings(notice) {
|
|
12040
|
+
const comments = getCommentsConfig(cfg);
|
|
12041
|
+
const models = await listModels(backendFor(comments.backend)).catch(() => []);
|
|
12042
|
+
return buildCommentSettingsCard(cfg, commentBackendOptions(), models, notice);
|
|
12043
|
+
}
|
|
12044
|
+
function applyCommentsPref(evt, mut) {
|
|
12045
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12046
|
+
const prefs = { ...cfg.preferences ?? {} };
|
|
12047
|
+
const comments = { ...prefs.comments ?? {} };
|
|
12048
|
+
mut(comments);
|
|
12049
|
+
prefs.comments = comments;
|
|
12050
|
+
cfg.preferences = prefs;
|
|
12051
|
+
void saveConfig(cfg).catch((err) => log.fail("console", err, { phase: "save-config" }));
|
|
12052
|
+
void patch(evt, () => renderCommentSettings());
|
|
11775
12053
|
}
|
|
11776
12054
|
const freshMenu = (evt) => {
|
|
11777
12055
|
patch(evt, buildDmMenuCard({ webConsoleUrl: webConsoleUrl(), version: bridgeVersion() }));
|
|
@@ -11920,7 +12198,9 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
11920
12198
|
const page = typeof value.p === "number" ? value.p : Number(value.p) || 0;
|
|
11921
12199
|
patch(evt, () => renderProjectList(page));
|
|
11922
12200
|
}).on(DM.settings, async ({ evt }) => {
|
|
11923
|
-
if (dmAdmin(evt.operator?.openId)) await patch(evt,
|
|
12201
|
+
if (dmAdmin(evt.operator?.openId)) await patch(evt, renderSettings);
|
|
12202
|
+
}).on(DM.coffeeSettings, async ({ evt }) => {
|
|
12203
|
+
if (dmAdmin(evt.operator?.openId)) await patch(evt, () => renderCoffeeSettings(true));
|
|
11924
12204
|
}).on(CLI.toggleEnabled, async ({ evt, value }) => {
|
|
11925
12205
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
11926
12206
|
const enabled = value.v === "on";
|
|
@@ -11940,7 +12220,7 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
11940
12220
|
} catch (err) {
|
|
11941
12221
|
log.fail("cli-bridge", err, { phase: enabled ? "enable" : "disable" });
|
|
11942
12222
|
}
|
|
11943
|
-
void patch(evt,
|
|
12223
|
+
void patch(evt, renderCoffeeSettings);
|
|
11944
12224
|
}).on(CLI.repairHooks, async ({ evt }) => {
|
|
11945
12225
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
11946
12226
|
try {
|
|
@@ -11951,13 +12231,14 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
11951
12231
|
} catch (err) {
|
|
11952
12232
|
log.fail("cli-bridge", err, { phase: "repair-hooks" });
|
|
11953
12233
|
}
|
|
11954
|
-
await patch(evt, () =>
|
|
12234
|
+
await patch(evt, () => renderCoffeeSettings(true));
|
|
11955
12235
|
}).on(CLI.setNotifyScope, ({ evt, value }) => {
|
|
11956
12236
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
11957
12237
|
const scope = value.v === "bound_projects" || value.v === "none" ? value.v : "all";
|
|
11958
12238
|
applyPref(evt, (p) => {
|
|
11959
12239
|
p.cliBridge = { ...p.cliBridge ?? {}, notifyScope: scope };
|
|
11960
|
-
});
|
|
12240
|
+
}, { render: false });
|
|
12241
|
+
void patch(evt, renderCoffeeSettings);
|
|
11961
12242
|
}).on(CLI.toggleAgent, ({ evt, value }) => {
|
|
11962
12243
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
11963
12244
|
const agent = value.agent === "codex" ? "codex" : "claude";
|
|
@@ -11965,14 +12246,16 @@ function createOrchestrator(channel, cfg, fallbackCwd, cliBridge) {
|
|
|
11965
12246
|
applyPref(evt, (p) => {
|
|
11966
12247
|
const cur = p.cliBridge ?? {};
|
|
11967
12248
|
p.cliBridge = { ...cur, agents: { ...cur.agents ?? {}, [agent]: on } };
|
|
11968
|
-
});
|
|
12249
|
+
}, { render: false });
|
|
12250
|
+
void patch(evt, renderCoffeeSettings);
|
|
11969
12251
|
}).on(CLI.toggleKeepAwake, ({ evt, value }) => {
|
|
11970
12252
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
11971
12253
|
const on = value.v === "on";
|
|
11972
12254
|
applyPref(evt, (p) => {
|
|
11973
12255
|
const cur = p.cliBridge ?? {};
|
|
11974
12256
|
p.cliBridge = { ...cur, keepAwake: { ...cur.keepAwake ?? {}, enabled: on } };
|
|
11975
|
-
});
|
|
12257
|
+
}, { render: false });
|
|
12258
|
+
void patch(evt, renderCoffeeSettings);
|
|
11976
12259
|
}).on(DM.doctor, async ({ evt }) => {
|
|
11977
12260
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
11978
12261
|
await sendManagedCard(channel, evt.chatId, buildDoctorCard(await buildDoctorInfo()), evt.messageId).catch(
|
|
@@ -12114,6 +12397,78 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
12114
12397
|
}).on(DM.setConcurrency, ({ evt, value }) => {
|
|
12115
12398
|
const n = Number(value.v);
|
|
12116
12399
|
if (Number.isFinite(n)) applyPref(evt, (p) => p.maxConcurrentRuns = n);
|
|
12400
|
+
}).on(DM.commentSettings, ({ evt }) => {
|
|
12401
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12402
|
+
void patch(evt, () => renderCommentSettings());
|
|
12403
|
+
}).on(DM.commentSetBackend, ({ evt, value }) => {
|
|
12404
|
+
const v = typeof value.v === "string" ? value.v : void 0;
|
|
12405
|
+
applyCommentsPref(evt, (c) => {
|
|
12406
|
+
c.backend = v && backendIds().includes(v) ? v : void 0;
|
|
12407
|
+
c.model = void 0;
|
|
12408
|
+
c.effort = void 0;
|
|
12409
|
+
});
|
|
12410
|
+
}).on(DM.commentSubmit, ({ evt, formValue }) => {
|
|
12411
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12412
|
+
const modelId = selectValue(formValue, "model");
|
|
12413
|
+
const effort = asEffort(selectValue(formValue, "effort"));
|
|
12414
|
+
applyCommentsPref(evt, (c) => {
|
|
12415
|
+
if (modelId) c.model = modelId;
|
|
12416
|
+
if (effort) c.effort = effort;
|
|
12417
|
+
});
|
|
12418
|
+
}).on(DM.commentEditPrompt, ({ evt }) => {
|
|
12419
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12420
|
+
void patch(
|
|
12421
|
+
evt,
|
|
12422
|
+
async () => buildCommentPromptCard(
|
|
12423
|
+
await loadCommentInstructions(paths.commentInstructionsFile),
|
|
12424
|
+
void 0,
|
|
12425
|
+
paths.commentInstructionsFile
|
|
12426
|
+
)
|
|
12427
|
+
);
|
|
12428
|
+
}).on(DM.commentPromptSubmit, ({ evt, formValue }) => {
|
|
12429
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12430
|
+
const content = typeof formValue?.prompt === "string" ? formValue.prompt : "";
|
|
12431
|
+
void (async () => {
|
|
12432
|
+
if (!content.trim()) {
|
|
12433
|
+
const cur = await loadCommentInstructions(paths.commentInstructionsFile);
|
|
12434
|
+
await patch(evt, buildCommentPromptCard(cur, "\u26A0\uFE0F \u63D0\u793A\u8BCD\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u672A\u4FDD\u5B58\u3002", paths.commentInstructionsFile));
|
|
12435
|
+
return;
|
|
12436
|
+
}
|
|
12437
|
+
await saveCommentInstructions(paths.commentInstructionsFile, content).catch(
|
|
12438
|
+
(err) => log.fail("console", err, { phase: "save-comment-prompt" })
|
|
12439
|
+
);
|
|
12440
|
+
const n = await syncAllCommentInstructions(paths.commentsRootDir, content, cfg.accounts.app.tenant).catch(
|
|
12441
|
+
() => 0
|
|
12442
|
+
);
|
|
12443
|
+
await patch(
|
|
12444
|
+
evt,
|
|
12445
|
+
() => buildCommentPromptCard(
|
|
12446
|
+
content,
|
|
12447
|
+
`\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`,
|
|
12448
|
+
paths.commentInstructionsFile
|
|
12449
|
+
)
|
|
12450
|
+
);
|
|
12451
|
+
})();
|
|
12452
|
+
}).on(DM.commentResetPrompt, ({ evt }) => {
|
|
12453
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
12454
|
+
void (async () => {
|
|
12455
|
+
await saveCommentInstructions(paths.commentInstructionsFile, DEFAULT_COMMENT_INSTRUCTIONS).catch(
|
|
12456
|
+
(err) => log.fail("console", err, { phase: "reset-comment-prompt" })
|
|
12457
|
+
);
|
|
12458
|
+
const n = await syncAllCommentInstructions(
|
|
12459
|
+
paths.commentsRootDir,
|
|
12460
|
+
DEFAULT_COMMENT_INSTRUCTIONS,
|
|
12461
|
+
cfg.accounts.app.tenant
|
|
12462
|
+
).catch(() => 0);
|
|
12463
|
+
await patch(
|
|
12464
|
+
evt,
|
|
12465
|
+
() => buildCommentPromptCard(
|
|
12466
|
+
DEFAULT_COMMENT_INSTRUCTIONS,
|
|
12467
|
+
`\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`,
|
|
12468
|
+
paths.commentInstructionsFile
|
|
12469
|
+
)
|
|
12470
|
+
);
|
|
12471
|
+
})();
|
|
12117
12472
|
}).on(GS.setNoMention, ({ evt, value }) => {
|
|
12118
12473
|
if (!isAdmin(cfg, evt.operator?.openId ?? "")) return;
|
|
12119
12474
|
const on = value.v === "on";
|
|
@@ -12932,18 +13287,39 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
12932
13287
|
if (!evt.mentionedBot) return log.info("comment", "skip", { reason: "not-mentioned" });
|
|
12933
13288
|
if (!SUPPORTED_FILE_TYPES.has(evt.fileType))
|
|
12934
13289
|
return log.info("comment", "skip", { reason: "unsupported-fileType", fileType: evt.fileType });
|
|
13290
|
+
const operatorId = evt.operator?.openId ?? "";
|
|
13291
|
+
if (!isAdmin(cfg, operatorId))
|
|
13292
|
+
return log.info("comment", "skip", { reason: "not-admin", sender: operatorId.slice(-6) || "(unknown)" });
|
|
12935
13293
|
const resolved = await resolveComment(channel, evt);
|
|
12936
13294
|
if (!resolved) return log.info("comment", "skip", { reason: "no-target-or-empty" });
|
|
12937
13295
|
const { target, ctx } = resolved;
|
|
12938
13296
|
log.info("comment", "parsed", { isWhole: ctx.isWhole, hasQuote: Boolean(ctx.quote) });
|
|
12939
|
-
const
|
|
12940
|
-
const
|
|
13297
|
+
const sessionKey = commentSessionKey(target.fileToken, evt.commentId);
|
|
13298
|
+
const cwd = commentCwd(paths.commentsRootDir, target.fileType, target.fileToken);
|
|
13299
|
+
const rawInstructions = await loadCommentInstructions(paths.commentInstructionsFile);
|
|
13300
|
+
const instructions = renderCommentInstructions(
|
|
13301
|
+
rawInstructions,
|
|
13302
|
+
cfg.accounts.app.tenant,
|
|
13303
|
+
target.fileType,
|
|
13304
|
+
target.fileToken
|
|
13305
|
+
);
|
|
13306
|
+
let synced = true;
|
|
13307
|
+
await syncCommentInstructions(cwd, instructions).catch((err) => {
|
|
13308
|
+
synced = false;
|
|
13309
|
+
log.warn("comment", "sync-instructions-failed", { err: err instanceof Error ? err.message : String(err) });
|
|
13310
|
+
});
|
|
13311
|
+
const facts = buildCommentPrompt(target, ctx, cfg.accounts.app.tenant);
|
|
13312
|
+
const prompt = synced ? facts : `${instructions}
|
|
13313
|
+
|
|
13314
|
+
---
|
|
13315
|
+
|
|
13316
|
+
${facts}`;
|
|
12941
13317
|
const reacted = ctx.targetReplyId ? await addCommentReaction(channel, target, ctx.targetReplyId) : false;
|
|
12942
13318
|
try {
|
|
12943
13319
|
await withDocLock(sessionKey, async () => {
|
|
12944
13320
|
const release2 = await sema.acquire();
|
|
12945
13321
|
try {
|
|
12946
|
-
const thread = await resolveDocThread(sessionKey, ctx.question);
|
|
13322
|
+
const thread = await resolveDocThread(sessionKey, cwd, instructions, ctx.question);
|
|
12947
13323
|
const rec = await getSession(sessionKey);
|
|
12948
13324
|
const run = thread.runStreamed({ text: prompt }, { model: rec?.model, effort: rec?.effort });
|
|
12949
13325
|
let state = initialState;
|
|
@@ -12957,6 +13333,7 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
12957
13333
|
if (tid) void thread.abort(tid).catch(() => void 0);
|
|
12958
13334
|
void thread.close().catch(() => void 0);
|
|
12959
13335
|
sessions.delete(sessionKey);
|
|
13336
|
+
commentInstrUsed.delete(sessionKey);
|
|
12960
13337
|
} else {
|
|
12961
13338
|
touchSession(sessionKey);
|
|
12962
13339
|
await patchSession(sessionKey, { updatedAt: Date.now() });
|
|
@@ -12994,12 +13371,15 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
12994
13371
|
});
|
|
12995
13372
|
return run;
|
|
12996
13373
|
}
|
|
12997
|
-
async function resolveDocThread(sessionKey, question) {
|
|
13374
|
+
async function resolveDocThread(sessionKey, cwd, instructions, question) {
|
|
12998
13375
|
const live = sessions.get(sessionKey);
|
|
12999
13376
|
if (live) {
|
|
13000
|
-
|
|
13377
|
+
const alive = live.isAlive();
|
|
13378
|
+
if (alive && commentInstrUsed.get(sessionKey) === instructions) return live;
|
|
13379
|
+
if (alive && commentInstrUsed.get(sessionKey) !== instructions) void live.close().catch(() => void 0);
|
|
13001
13380
|
sessions.delete(sessionKey);
|
|
13002
|
-
|
|
13381
|
+
commentInstrUsed.delete(sessionKey);
|
|
13382
|
+
log.info("agent", alive ? "comment-instr-changed-evict" : "dead-thread-evict", { sessionKey });
|
|
13003
13383
|
}
|
|
13004
13384
|
const rec = await getSession(sessionKey);
|
|
13005
13385
|
if (rec) {
|
|
@@ -13011,20 +13391,27 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
13011
13391
|
effort: rec.effort
|
|
13012
13392
|
});
|
|
13013
13393
|
trackSession(sessionKey, resumed);
|
|
13394
|
+
commentInstrUsed.set(sessionKey, instructions);
|
|
13014
13395
|
return resumed;
|
|
13015
13396
|
} catch (err) {
|
|
13016
13397
|
log.fail("agent", err, { phase: "comment-resume", sessionKey });
|
|
13017
13398
|
}
|
|
13018
13399
|
}
|
|
13019
|
-
const
|
|
13020
|
-
const
|
|
13400
|
+
const comments = getCommentsConfig(cfg);
|
|
13401
|
+
const be = backendFor(comments.backend);
|
|
13402
|
+
const { model, effort } = pickDefault(await listModels(be), {
|
|
13403
|
+
model: comments.model,
|
|
13404
|
+
effort: comments.effort
|
|
13405
|
+
});
|
|
13406
|
+
const fresh = await be.startThread({ cwd, model, effort });
|
|
13021
13407
|
trackSession(sessionKey, fresh);
|
|
13408
|
+
commentInstrUsed.set(sessionKey, instructions);
|
|
13022
13409
|
await upsertSession({
|
|
13023
13410
|
threadId: sessionKey,
|
|
13024
13411
|
chatId: sessionKey,
|
|
13025
|
-
cwd
|
|
13412
|
+
cwd,
|
|
13026
13413
|
sessionId: fresh.sessionId,
|
|
13027
|
-
backend:
|
|
13414
|
+
backend: be.id,
|
|
13028
13415
|
model,
|
|
13029
13416
|
effort,
|
|
13030
13417
|
summary: question.slice(0, 80),
|
|
@@ -13426,7 +13813,7 @@ function createAdminIpcResponder(execute, send) {
|
|
|
13426
13813
|
// src/admin/service.ts
|
|
13427
13814
|
init_bots();
|
|
13428
13815
|
init_paths();
|
|
13429
|
-
import { readFile as
|
|
13816
|
+
import { readFile as readFile13, rm as rm7 } from "fs/promises";
|
|
13430
13817
|
init_schema();
|
|
13431
13818
|
init_store();
|
|
13432
13819
|
init_schema();
|
|
@@ -13498,8 +13885,8 @@ init_logger();
|
|
|
13498
13885
|
|
|
13499
13886
|
// src/admin/host.ts
|
|
13500
13887
|
init_paths();
|
|
13501
|
-
import { readdir as
|
|
13502
|
-
import { dirname as
|
|
13888
|
+
import { readdir as readdir5, stat as stat4 } from "fs/promises";
|
|
13889
|
+
import { dirname as dirname13, join as join21, resolve as resolve8 } from "path";
|
|
13503
13890
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
13504
13891
|
import { arch, platform as platform2, release } from "os";
|
|
13505
13892
|
function toDaemonStatus(opts) {
|
|
@@ -13523,12 +13910,12 @@ async function dirBytes(dir) {
|
|
|
13523
13910
|
let total = 0;
|
|
13524
13911
|
let entries;
|
|
13525
13912
|
try {
|
|
13526
|
-
entries = await
|
|
13913
|
+
entries = await readdir5(dir);
|
|
13527
13914
|
} catch {
|
|
13528
13915
|
return 0;
|
|
13529
13916
|
}
|
|
13530
13917
|
for (const name of entries) {
|
|
13531
|
-
const full =
|
|
13918
|
+
const full = join21(dir, name);
|
|
13532
13919
|
try {
|
|
13533
13920
|
const st = await stat4(full);
|
|
13534
13921
|
if (st.isDirectory()) total += await dirBytes(full);
|
|
@@ -13538,7 +13925,7 @@ async function dirBytes(dir) {
|
|
|
13538
13925
|
}
|
|
13539
13926
|
return total;
|
|
13540
13927
|
}
|
|
13541
|
-
async function collectHostDoctor(logsDir2 =
|
|
13928
|
+
async function collectHostDoctor(logsDir2 = join21(paths.appDir, "logs")) {
|
|
13542
13929
|
return {
|
|
13543
13930
|
node: process.version,
|
|
13544
13931
|
platform: platform2(),
|
|
@@ -13551,7 +13938,7 @@ async function collectHostDoctor(logsDir2 = join20(paths.appDir, "logs")) {
|
|
|
13551
13938
|
};
|
|
13552
13939
|
}
|
|
13553
13940
|
function resolveCliBinPath3() {
|
|
13554
|
-
const distDir =
|
|
13941
|
+
const distDir = dirname13(fileURLToPath5(import.meta.url));
|
|
13555
13942
|
return resolve8(distDir, "..", "bin", "feishu-codex-bridge.mjs");
|
|
13556
13943
|
}
|
|
13557
13944
|
function buildDaemonControlCommand(action, binPath = resolveCliBinPath3(), nodePath = process.execPath) {
|
|
@@ -13599,7 +13986,7 @@ function createAdminService(deps = {}) {
|
|
|
13599
13986
|
}
|
|
13600
13987
|
async function lockFileRunState(botId) {
|
|
13601
13988
|
try {
|
|
13602
|
-
const raw = await
|
|
13989
|
+
const raw = await readFile13(botPaths(botId).processesFile, "utf8");
|
|
13603
13990
|
const rec = JSON.parse(raw);
|
|
13604
13991
|
if (typeof rec.pid === "number" && isAlive2(rec.pid)) {
|
|
13605
13992
|
return { running: true, pid: rec.pid, startedAt: rec.startedAt };
|
|
@@ -14127,7 +14514,7 @@ import { createServer } from "http";
|
|
|
14127
14514
|
import { randomUUID as randomUUID7, timingSafeEqual } from "crypto";
|
|
14128
14515
|
import { mkdirSync as mkdirSync3, watch } from "fs";
|
|
14129
14516
|
import { open as open2, stat as stat5 } from "fs/promises";
|
|
14130
|
-
import { join as
|
|
14517
|
+
import { join as join22 } from "path";
|
|
14131
14518
|
|
|
14132
14519
|
// src/web/ui.ts
|
|
14133
14520
|
var UI_PURE_JS = `
|
|
@@ -16802,7 +17189,7 @@ var SSE_INITIAL_TAIL_BYTES = 16 * 1024;
|
|
|
16802
17189
|
function createWebServer(opts) {
|
|
16803
17190
|
const token = opts.token ?? randomUUID7();
|
|
16804
17191
|
const html = opts.html ?? UI_HTML;
|
|
16805
|
-
const logDir = opts.logDir ??
|
|
17192
|
+
const logDir = opts.logDir ?? join22(paths.appDir, "logs");
|
|
16806
17193
|
const sseCleanups = /* @__PURE__ */ new Set();
|
|
16807
17194
|
const installJobs = /* @__PURE__ */ new Map();
|
|
16808
17195
|
let qrSession = null;
|
|
@@ -17440,7 +17827,7 @@ function isLoopbackOrigin(origin) {
|
|
|
17440
17827
|
}
|
|
17441
17828
|
}
|
|
17442
17829
|
function todayLogFile(dir) {
|
|
17443
|
-
return
|
|
17830
|
+
return join22(dir, `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.log`);
|
|
17444
17831
|
}
|
|
17445
17832
|
function clampInt(raw, min, max, fallback) {
|
|
17446
17833
|
const n = Number(raw);
|
|
@@ -17643,7 +18030,7 @@ async function runSupervisor(bots) {
|
|
|
17643
18030
|
// src/core/single-instance.ts
|
|
17644
18031
|
init_paths();
|
|
17645
18032
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, statSync as statSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
17646
|
-
import { dirname as
|
|
18033
|
+
import { dirname as dirname14 } from "path";
|
|
17647
18034
|
var CLAIM_ATTEMPTS = 5;
|
|
17648
18035
|
var PID_REUSE_SLACK_MS = 15e3;
|
|
17649
18036
|
var EMPTY_LOCK_STALE_MS = 2e3;
|
|
@@ -17689,7 +18076,7 @@ function isLiveHolder(rec, appId) {
|
|
|
17689
18076
|
return true;
|
|
17690
18077
|
}
|
|
17691
18078
|
function acquireSingleInstanceLock(appId, file = paths.processesFile) {
|
|
17692
|
-
mkdirSync4(
|
|
18079
|
+
mkdirSync4(dirname14(file), { recursive: true });
|
|
17693
18080
|
const record = { pid: process.pid, appId, startedAt: Date.now() };
|
|
17694
18081
|
for (let attempt = 0; attempt < CLAIM_ATTEMPTS; attempt++) {
|
|
17695
18082
|
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
|