@modelzen/feishu-codex-bridge 0.1.0 → 0.1.1
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 +20 -21
- package/dist/cli.js +69 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,33 +32,17 @@
|
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
35
|
-
## ⚠️ 安全须知(务必先读)
|
|
36
|
-
|
|
37
|
-
本机器人调用 Codex 时固定使用 **`approvalPolicy: "never"` + `sandbox: "danger-full-access"`** —— 即 **无任何人工审批、对磁盘完全访问**。这意味着:
|
|
38
|
-
|
|
39
|
-
> **任何能在项目群里给机器人发消息的人,都能在你这台机器上、以你的身份、在该项目目录里执行任意命令(读写文件、联网、运行脚本)。**
|
|
40
|
-
|
|
41
|
-
因此:
|
|
42
|
-
|
|
43
|
-
- 只把**你信任的人**拉进项目群;
|
|
44
|
-
- 在**你自己掌控的机器/账号**上运行,最好是隔离的开发机或容器;
|
|
45
|
-
- 绑定的项目目录里不要放你不愿被读写的敏感数据;
|
|
46
|
-
- 它不是多租户托管服务,是给你(和你信任的小团队)自用的桥。
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
35
|
## 📦 前置条件
|
|
51
36
|
|
|
52
37
|
| 依赖 | 说明 | 获取方式 |
|
|
53
38
|
|------|------|----------|
|
|
54
39
|
| **Node.js ≥ 20** | 运行时 | <https://nodejs.org> 或 `nvm install 20` |
|
|
55
|
-
| **Codex CLI** | 后端,bridge 会 spawn `codex app-server` | `npm i -g @openai/codex
|
|
56
|
-
| **Codex 已登录** | app-server 需要 `~/.codex/auth.json` |
|
|
57
|
-
| **飞书 / Lark 账号** |
|
|
58
|
-
| **lark-cli
|
|
40
|
+
| **Codex CLI** | 后端,bridge 会 spawn `codex app-server` | `npm i -g @openai/codex`,或装 Codex.app,或用 `CODEX_BIN` 指向已有二进制 |
|
|
41
|
+
| **Codex 已登录** | app-server 需要 `~/.codex/auth.json` | `codex login` |
|
|
42
|
+
| **飞书 / Lark 账号** | 租户需允许「扫码创建应用」(个人/开发者租户一般可以) | 首次 `run` 时扫码创建 |
|
|
43
|
+
| **lark-cli**(可选) | 仅「文档评论回复」需读文档正文时用到;不装也能跑,只是读不到正文 | `lark-cli auth login`,确保在 PATH 上 |
|
|
59
44
|
|
|
60
|
-
>
|
|
61
|
-
> ⚠️ `lark-cli` 以**你的用户身份**登录,所以 Codex 只应用它来**读**;prompt 已明确禁止 Codex 用它发评论(否则评论会署成你本人,而不是机器人)。
|
|
45
|
+
> 收发消息、回卡片、发评论回复均走 `@larksuiteoapi/node-sdk` 长连接,**不依赖** `lark-cli`。⚠️ `lark-cli` 以**你的身份**登录,仅供 Codex **读**文档;prompt 已禁止用它发评论(否则评论会署你本人)。
|
|
62
46
|
|
|
63
47
|
---
|
|
64
48
|
|
|
@@ -239,6 +223,21 @@ src/
|
|
|
239
223
|
|
|
240
224
|
---
|
|
241
225
|
|
|
226
|
+
## ⚠️ 安全须知
|
|
227
|
+
|
|
228
|
+
本机器人调用 Codex 时固定使用 **`approvalPolicy: "never"` + `sandbox: "danger-full-access"`** —— 即 **无任何人工审批、对磁盘完全访问**。这意味着:
|
|
229
|
+
|
|
230
|
+
> **任何能在项目群里给机器人发消息的人,都能在你这台机器上、以你的身份、在该项目目录里执行任意命令(读写文件、联网、运行脚本)。**
|
|
231
|
+
|
|
232
|
+
因此:
|
|
233
|
+
|
|
234
|
+
- 只把**你信任的人**拉进项目群;
|
|
235
|
+
- 在**你自己掌控的机器/账号**上运行,最好是隔离的开发机或容器;
|
|
236
|
+
- 绑定的项目目录里不要放你不愿被读写的敏感数据;
|
|
237
|
+
- 它不是多租户托管服务,是给你(和你信任的小团队)自用的桥。
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
242
241
|
## ❓ 故障排查
|
|
243
242
|
|
|
244
243
|
| 现象 | 排查 |
|
package/dist/cli.js
CHANGED
|
@@ -2358,9 +2358,14 @@ var RC = {
|
|
|
2358
2358
|
};
|
|
2359
2359
|
var REASONING_MAX = 1500;
|
|
2360
2360
|
var COLLAPSE_TOOL_THRESHOLD = 3;
|
|
2361
|
+
var PROCESS_BODY_BUDGET = 22e3;
|
|
2361
2362
|
function buildRunCard(rc) {
|
|
2362
2363
|
const state = rc.rs;
|
|
2363
2364
|
const running = state.terminal === "running";
|
|
2365
|
+
const elements = running ? renderRunning(state, rc) : renderTerminal(state, rc);
|
|
2366
|
+
return card(elements, { streaming: running, summary: summaryText(state) });
|
|
2367
|
+
}
|
|
2368
|
+
function renderRunning(state, rc) {
|
|
2364
2369
|
const elements = [];
|
|
2365
2370
|
const reasoning = reasoningContent(state);
|
|
2366
2371
|
if (reasoning) elements.push(reasoningPanel(reasoning, state.reasoningActive));
|
|
@@ -2369,23 +2374,79 @@ function buildRunCard(rc) {
|
|
|
2369
2374
|
if (group.kind === "text") {
|
|
2370
2375
|
if (group.content.trim()) elements.push(md(group.content));
|
|
2371
2376
|
} else {
|
|
2372
|
-
elements.push(...renderToolGroup(group.tools,
|
|
2377
|
+
elements.push(...renderToolGroup(group.tools, false));
|
|
2373
2378
|
}
|
|
2374
2379
|
}
|
|
2380
|
+
if (state.footer) elements.push(footerStatus(state.footer));
|
|
2381
|
+
if (rc.cardKey) elements.push(actions([button("\u23F9 \u7EC8\u6B62", { a: RC.stop, m: rc.cardKey }, "danger")]));
|
|
2382
|
+
return elements;
|
|
2383
|
+
}
|
|
2384
|
+
function renderTerminal(state, rc) {
|
|
2385
|
+
const elements = [];
|
|
2386
|
+
const answerIdx = lastTextIndex(state.blocks);
|
|
2387
|
+
const answer = answerIdx >= 0 ? state.blocks[answerIdx].content.trim() : "";
|
|
2388
|
+
const processBlocks = state.blocks.filter((_, i) => i !== answerIdx);
|
|
2389
|
+
const blocks = rc.showTools === false ? processBlocks.filter((b) => b.kind !== "tool") : processBlocks;
|
|
2390
|
+
const reasoning = reasoningContent(state);
|
|
2391
|
+
const processEls = buildProcessBody(reasoning, blocks);
|
|
2392
|
+
if (processEls.length > 0) {
|
|
2393
|
+
const toolCount = blocks.reduce((n, b) => b.kind === "tool" ? n + 1 : n, 0);
|
|
2394
|
+
elements.push(
|
|
2395
|
+
collapsiblePanelEl({
|
|
2396
|
+
title: processTitle(Boolean(reasoning), toolCount),
|
|
2397
|
+
expanded: false,
|
|
2398
|
+
border: "grey",
|
|
2399
|
+
elements: processEls
|
|
2400
|
+
})
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
if (answer) elements.push(md(answer));
|
|
2375
2404
|
if (state.terminal === "interrupted") {
|
|
2376
2405
|
elements.push(noteMd("_\u23F9 \u5DF2\u88AB\u4E2D\u65AD_"));
|
|
2377
2406
|
} else if (state.terminal === "idle_timeout") {
|
|
2378
2407
|
elements.push(noteMd(`_\u23F1 ${state.idleTimeoutMinutes ?? 0} \u5206\u949F\u65E0\u54CD\u5E94\uFF0C\u5DF2\u81EA\u52A8\u7EC8\u6B62_`));
|
|
2379
2408
|
} else if (state.terminal === "error" && state.errorMsg) {
|
|
2380
2409
|
elements.push(noteMd(`\u26A0\uFE0F agent \u5931\u8D25\uFF1A${state.errorMsg}`));
|
|
2381
|
-
} else if (state.terminal === "done" &&
|
|
2410
|
+
} else if (state.terminal === "done" && !answer) {
|
|
2382
2411
|
elements.push(noteMd("_\uFF08\u672A\u8FD4\u56DE\u5185\u5BB9\uFF09_"));
|
|
2383
2412
|
}
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2413
|
+
return elements;
|
|
2414
|
+
}
|
|
2415
|
+
function lastTextIndex(blocks) {
|
|
2416
|
+
for (let i = blocks.length - 1; i >= 0; i--) {
|
|
2417
|
+
const b = blocks[i];
|
|
2418
|
+
if (b && b.kind === "text" && b.content.trim()) return i;
|
|
2387
2419
|
}
|
|
2388
|
-
return
|
|
2420
|
+
return -1;
|
|
2421
|
+
}
|
|
2422
|
+
function buildProcessBody(reasoning, blocks) {
|
|
2423
|
+
const rich = processElements(reasoning, blocks, false);
|
|
2424
|
+
if (estimateSize2(rich) <= PROCESS_BODY_BUDGET) return rich;
|
|
2425
|
+
return processElements(reasoning, blocks, true);
|
|
2426
|
+
}
|
|
2427
|
+
function processElements(reasoning, blocks, compactTools) {
|
|
2428
|
+
const out = [];
|
|
2429
|
+
if (reasoning) out.push(reasoningPanel(reasoning, false));
|
|
2430
|
+
for (const group of groupBlocks(blocks)) {
|
|
2431
|
+
if (group.kind === "text") {
|
|
2432
|
+
if (group.content.trim()) out.push(md(group.content));
|
|
2433
|
+
} else {
|
|
2434
|
+
out.push(...renderToolGroup(group.tools, true, compactTools));
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
return out;
|
|
2438
|
+
}
|
|
2439
|
+
function processTitle(hasReasoning, toolCount) {
|
|
2440
|
+
const parts = [];
|
|
2441
|
+
if (hasReasoning) parts.push("\u{1F9E0} \u601D\u8003");
|
|
2442
|
+
if (toolCount > 0) parts.push(`\u{1F9F0} ${toolCount} \u4E2A\u5DE5\u5177\u8C03\u7528`);
|
|
2443
|
+
const detail = parts.length > 0 ? `\uFF1A${parts.join(" \xB7 ")}` : "";
|
|
2444
|
+
return `\u{1F5C2} **\u8FC7\u7A0B${detail}**\uFF08\u70B9\u51FB\u5C55\u5F00\uFF09`;
|
|
2445
|
+
}
|
|
2446
|
+
function estimateSize2(els) {
|
|
2447
|
+
let n = 0;
|
|
2448
|
+
for (const el of els) n += JSON.stringify(el).length;
|
|
2449
|
+
return n;
|
|
2389
2450
|
}
|
|
2390
2451
|
function buildRunCardPlain(rc) {
|
|
2391
2452
|
return buildRunCard({ ...rc, cardKey: void 0 });
|
|
@@ -2405,8 +2466,9 @@ function* groupBlocks(blocks) {
|
|
|
2405
2466
|
}
|
|
2406
2467
|
if (toolBuf.length > 0) yield { kind: "tools", tools: toolBuf };
|
|
2407
2468
|
}
|
|
2408
|
-
function renderToolGroup(tools, finalized) {
|
|
2469
|
+
function renderToolGroup(tools, finalized, compact = false) {
|
|
2409
2470
|
if (tools.length === 0) return [];
|
|
2471
|
+
if (compact) return [collapsedToolSummary(tools, true)];
|
|
2410
2472
|
if (tools.length < COLLAPSE_TOOL_THRESHOLD) {
|
|
2411
2473
|
return tools.map((t) => toolPanel(t, false));
|
|
2412
2474
|
}
|