opencode-feishu-plugin 0.2.13 → 0.2.14
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.en.md +18 -0
- package/README.md +18 -0
- package/dist/index.js +461 -22
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -22,6 +22,7 @@ Connect [OpenCode](https://opencode.ai) to Feishu/Lark: **one Feishu topic = one
|
|
|
22
22
|
| 📊 **Real-time visible** | "Thinking" receipt → live tool-call cards → streaming text updates; footer shows the current model |
|
|
23
23
|
| ⏹ **Controllable** | Every reply card has a "force stop" button; a watchdog auto-interrupts stuck sessions; native queue with `/steer` `/now` to cut in |
|
|
24
24
|
| 📎 **Images / files** | Images and files sent in Feishu are downloaded and attached to the session, so vision/file-capable models can see and read them |
|
|
25
|
+
| 🧭 **One-sentence session** | Send a task in the main chat — the AI judges intent, finds the right working directory, and creates + starts the session in one tap |
|
|
25
26
|
| 🖱 **Main-window menu** | Two fixed quick buttons above the chat input — "New session" and "Session list" (bot custom menu) — one tap to fire them |
|
|
26
27
|
| 🚫 **No ports** | Full long-connection; no inbound port needed on the server |
|
|
27
28
|
|
|
@@ -127,6 +128,22 @@ After configuring the menu (setup step 6), two quick buttons stay pinned **above
|
|
|
127
128
|
- After changing the menu, **re-publish the app version** (per the console: takes effect within ~5 minutes after publishing);
|
|
128
129
|
- Message the bot at least once before using the menu (the plugin remembers the p2p chat from messages; any normal usage satisfies this).
|
|
129
130
|
|
|
131
|
+
### One-sentence session creation (AI finds the directory)
|
|
132
|
+
|
|
133
|
+
When you send plain text in the main chat, the AI first judges the **intent** and picks the best-matching working directory from candidates (recent dirs + local session dirs), then replies with a proposal card:
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
You: fix the zlib download bug
|
|
137
|
+
→ 🆕 Suggested new session
|
|
138
|
+
Task: fix download bug | Dir: `/Users/code/zlib`
|
|
139
|
+
[✅ Create & send] [❌ Cancel]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
"Create & send" = create the session + auto-open a topic + send your original message in to start working (permission defaults to "Editable"; adjust later with `/perm`).
|
|
143
|
+
|
|
144
|
+
- Chat/unrecognized/dir-not-in-candidates → falls back to the console hint card, **never creates a wrong session**; the model's path must match a candidate (anti-hallucination);
|
|
145
|
+
- Proposal cards are valid for 1 hour; set `quickNew: false` to go back to the console-only mode.
|
|
146
|
+
|
|
130
147
|
### Inside a topic (work)
|
|
131
148
|
|
|
132
149
|
A topic = a session; sending plain text is giving the AI a command.
|
|
@@ -174,6 +191,7 @@ When the agent asks via `question` or other form tools, the form becomes a Feish
|
|
|
174
191
|
| `logFile` | string \| boolean | — | `true` = write `<configDir>/plugins/feishu.log`; recommended in server mode |
|
|
175
192
|
| `approvalTtlMs` | number | `600000` | Approval token / card validity |
|
|
176
193
|
| `staleExecutionMs` | number | `300000` | Watchdog threshold (0–60 min; **0 = disabled**; sessions waiting on a form / pending approval are never killed) |
|
|
194
|
+
| `quickNew` | boolean | `true` | Main-chat "one-sentence session": AI judges intent + finds the directory, proposal card creates in one tap; `false` = console-only mode |
|
|
177
195
|
| `gatewayLocation` | string | — | Only start the gateway at this location (or its subdirectories); empty = any location |
|
|
178
196
|
|
|
179
197
|
Full config (including `cardMaxTables`, `topicStatus*`, `resumeSummary*`, `keepalive*`, `gatewayMatchGraceMs`) → [docs/advanced.en.md](./docs/advanced.en.md#full-configuration).
|
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
| 📊 **实时可见** | 「思考中」回执 → 工具调用实时上卡 → 文本流式更新,页脚显示当前模型 |
|
|
23
23
|
| ⏹ **可控可停** | 每张回复卡带「强制停止」;看门狗自动中断卡死会话;忙时原生排队,`/steer` `/now` 可插队 |
|
|
24
24
|
| 📎 **图片 / 文件** | 飞书里的图片 / 文件自动下载并挂进会话,支持视觉 / 文件的模型直接看图、读文件 |
|
|
25
|
+
| 🧭 **一句话建会话** | 主聊天流发任务文本,AI 自动判断意图、找好工作目录,一键建会话并开始处理 |
|
|
25
26
|
| 🖱 **主窗口菜单** | 机器人输入框上固定「新建会话」「会话列表」快捷按钮(机器人自定义菜单),一点即用 |
|
|
26
27
|
| 🚫 **无端口** | 全程长连接,服务器无需开放任何入站端口 |
|
|
27
28
|
|
|
@@ -127,6 +128,22 @@ tail -f ~/.config/opencode/plugins/feishu.log # 应看到「飞书长连接已
|
|
|
127
128
|
- 菜单配置修改后需**重新发布版本**(官方说明:发版成功后约 5 分钟内生效);
|
|
128
129
|
- 首次使用菜单前,先给机器人发过至少一条消息(插件据消息记住单聊会话;正常使用必然满足)。
|
|
129
130
|
|
|
131
|
+
### 一句话建会话(AI 找目录)
|
|
132
|
+
|
|
133
|
+
主聊天流发普通文本时,AI 会先判断**意图**,并从候选目录(最近使用 + 本机会话目录)里挑出最匹配的工作目录,回一张建议卡:
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
你:帮我修一下 zlib 的下载 bug
|
|
137
|
+
→ 🆕 建议新建会话
|
|
138
|
+
任务:修下载 bug | 目录:`/Users/code/zlib`
|
|
139
|
+
[✅ 创建并发送] [❌ 取消]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
点「创建并发送」= 建会话 + 自动开话题 + 把原消息发进去开始处理(权限默认「可编辑」,进话题后可用 `/perm` 调整)。
|
|
143
|
+
|
|
144
|
+
- 闲聊 / 无法识别 / 目录不在候选中 → 回退为管理台提示卡,**不会误建会话**;模型给出的路径必须命中候选清单(防幻觉);
|
|
145
|
+
- 建议卡有效期 1 小时;不想要此功能可设 `quickNew: false` 回到纯管理台模式。
|
|
146
|
+
|
|
130
147
|
### 话题内(干活)
|
|
131
148
|
|
|
132
149
|
一个话题 = 一个会话,发普通文本就是给 AI 下指令。
|
|
@@ -174,6 +191,7 @@ agent 反问时表单会变成飞书卡片,**点按钮或在话题里直接发
|
|
|
174
191
|
| `logFile` | string \| boolean | — | `true` = 写 `<configDir>/plugins/feishu.log`,服务模式建议开启 |
|
|
175
192
|
| `approvalTtlMs` | number | `600000` | 审批 token / 卡片有效期 |
|
|
176
193
|
| `staleExecutionMs` | number | `300000` | 看门狗阈值(0–60 分钟;**0 = 关闭看门狗**;待答表单 / 未决审批期间不判卡死) |
|
|
194
|
+
| `quickNew` | boolean | `true` | 主聊天流「一句话建会话」:AI 判断意图 + 找目录,建议卡一键创建;`false` = 回到纯管理台模式 |
|
|
177
195
|
| `gatewayLocation` | string | — | 只在该 location(及其子目录)启动网关;留空 = 任意 location 生效 |
|
|
178
196
|
|
|
179
197
|
完整配置(含 `cardMaxTables`、`topicStatus*`、`resumeSummary*`、`keepalive*`、`gatewayMatchGraceMs` 等进阶项)见 [docs/advanced.md](./docs/advanced.md#完整配置项)。
|
package/dist/index.js
CHANGED
|
@@ -144037,6 +144037,7 @@ function resolveConfig2(raw, env = process.env, deps = {}) {
|
|
|
144037
144037
|
);
|
|
144038
144038
|
const attachmentTimeoutMs = clamp4(asNumber(merged.attachmentTimeoutMs, 3e4), 5e3, 12e4);
|
|
144039
144039
|
const attachmentsDirRaw = asString(merged.attachmentsDir).trim();
|
|
144040
|
+
const quickNew = asBoolean(merged.quickNew, true);
|
|
144040
144041
|
const keepaliveIntervalMs = clamp4(
|
|
144041
144042
|
asNumber(merged.keepaliveIntervalMs, 20 * 60 * 1e3),
|
|
144042
144043
|
5 * 60 * 1e3,
|
|
@@ -144101,7 +144102,8 @@ function resolveConfig2(raw, env = process.env, deps = {}) {
|
|
|
144101
144102
|
acceptAttachments,
|
|
144102
144103
|
attachmentMaxBytes,
|
|
144103
144104
|
attachmentTimeoutMs,
|
|
144104
|
-
...attachmentsDirRaw ? { attachmentsDir: attachmentsDirRaw } : {}
|
|
144105
|
+
...attachmentsDirRaw ? { attachmentsDir: attachmentsDirRaw } : {},
|
|
144106
|
+
quickNew
|
|
144105
144107
|
};
|
|
144106
144108
|
}
|
|
144107
144109
|
function loadConfigFile(env, deps, warnings) {
|
|
@@ -146078,6 +146080,14 @@ function withAssistantID(state, assistantMessageID) {
|
|
|
146078
146080
|
if (!assistantMessageID || assistantMessageID === state.assistantMessageID) return state;
|
|
146079
146081
|
return { ...state, assistantMessageID };
|
|
146080
146082
|
}
|
|
146083
|
+
function lastPrefixTextIndex(blocks, text) {
|
|
146084
|
+
for (let i = blocks.length - 1; i >= 0; i -= 1) {
|
|
146085
|
+
const block = blocks[i];
|
|
146086
|
+
if (block.kind !== "text") continue;
|
|
146087
|
+
if (block.content && text.startsWith(block.content)) return i;
|
|
146088
|
+
}
|
|
146089
|
+
return -1;
|
|
146090
|
+
}
|
|
146081
146091
|
function patchTool(state, id2, update2) {
|
|
146082
146092
|
let found = false;
|
|
146083
146093
|
const blocks = state.blocks.map((b) => {
|
|
@@ -146118,21 +146128,62 @@ function reduce6(state, event) {
|
|
|
146118
146128
|
if (!event.delta) return state;
|
|
146119
146129
|
const base2 = stepTransition(state, event.assistantMessageID);
|
|
146120
146130
|
const last = base2[base2.length - 1];
|
|
146121
|
-
const blocks = last && last.kind === "text" && last.streaming ? [...base2.slice(0, -1), { ...last, content: last.content + event.delta }] : [...base2, { kind: "text", content: event.delta, streaming: true }];
|
|
146131
|
+
const blocks = last && last.kind === "text" && last.streaming ? [...base2.slice(0, -1), { ...last, content: last.content + event.delta, ...event.assistantMessageID ? { msg: event.assistantMessageID } : {} }] : [...base2, { kind: "text", content: event.delta, streaming: true, ...event.assistantMessageID ? { msg: event.assistantMessageID } : {} }];
|
|
146122
146132
|
const next = withAssistantID(state, event.assistantMessageID);
|
|
146123
146133
|
return { ...next, blocks, footer: "streaming", terminal: "running" };
|
|
146124
146134
|
}
|
|
146125
146135
|
case "text.ended": {
|
|
146126
146136
|
const blocks = state.blocks;
|
|
146127
|
-
const
|
|
146128
|
-
|
|
146129
|
-
if (
|
|
146130
|
-
|
|
146131
|
-
|
|
146132
|
-
|
|
146133
|
-
|
|
146134
|
-
|
|
146137
|
+
const endedText = event.text;
|
|
146138
|
+
const endedMsg = event.assistantMessageID;
|
|
146139
|
+
if (!endedMsg) {
|
|
146140
|
+
const last = blocks[blocks.length - 1];
|
|
146141
|
+
let nextBlocks2;
|
|
146142
|
+
if (last && last.kind === "text" && last.streaming) {
|
|
146143
|
+
nextBlocks2 = [...blocks.slice(0, -1), { ...last, content: endedText ?? last.content, streaming: false }];
|
|
146144
|
+
} else if (endedText) {
|
|
146145
|
+
const idx = lastPrefixTextIndex(blocks, endedText);
|
|
146146
|
+
nextBlocks2 = idx >= 0 ? blocks.map((b, i) => i === idx && b.kind === "text" ? { ...b, content: endedText, streaming: false } : b) : [...closeStreamingText(blocks), { kind: "text", content: endedText, streaming: false }];
|
|
146147
|
+
} else {
|
|
146148
|
+
nextBlocks2 = closeStreamingText(blocks);
|
|
146149
|
+
}
|
|
146150
|
+
return { ...state, blocks: nextBlocks2, footer: "streaming" };
|
|
146135
146151
|
}
|
|
146152
|
+
let targetIdx = -1;
|
|
146153
|
+
for (let i = blocks.length - 1; i >= 0; i -= 1) {
|
|
146154
|
+
const b = blocks[i];
|
|
146155
|
+
if (b.kind === "text" && b.msg === endedMsg) {
|
|
146156
|
+
targetIdx = i;
|
|
146157
|
+
break;
|
|
146158
|
+
}
|
|
146159
|
+
}
|
|
146160
|
+
const tail = blocks[blocks.length - 1];
|
|
146161
|
+
if (targetIdx < 0 && tail && tail.kind === "text" && tail.streaming) targetIdx = blocks.length - 1;
|
|
146162
|
+
if (targetIdx < 0) {
|
|
146163
|
+
if (!endedText) return { ...state, blocks: closeStreamingText(blocks), footer: "streaming" };
|
|
146164
|
+
return {
|
|
146165
|
+
...state,
|
|
146166
|
+
blocks: [
|
|
146167
|
+
...closeStreamingText(blocks),
|
|
146168
|
+
{ kind: "text", content: endedText, streaming: false, msg: endedMsg }
|
|
146169
|
+
],
|
|
146170
|
+
footer: "streaming"
|
|
146171
|
+
};
|
|
146172
|
+
}
|
|
146173
|
+
const target = blocks[targetIdx];
|
|
146174
|
+
let earlierLen = 0;
|
|
146175
|
+
for (let i = 0; i < targetIdx; i += 1) {
|
|
146176
|
+
const b = blocks[i];
|
|
146177
|
+
if (b.kind === "text" && b.msg === endedMsg) earlierLen += b.content.length;
|
|
146178
|
+
}
|
|
146179
|
+
let content = target.kind === "text" ? target.content : "";
|
|
146180
|
+
if (endedText !== void 0 && endedText.length >= earlierLen) {
|
|
146181
|
+
const suffix = endedText.slice(earlierLen);
|
|
146182
|
+
if (suffix.length >= content.length) content = suffix;
|
|
146183
|
+
}
|
|
146184
|
+
const nextBlocks = blocks.map(
|
|
146185
|
+
(b, i) => i === targetIdx && b.kind === "text" ? { ...b, content, streaming: false } : b
|
|
146186
|
+
);
|
|
146136
146187
|
return { ...state, blocks: nextBlocks, footer: "streaming" };
|
|
146137
146188
|
}
|
|
146138
146189
|
case "tool.input.started": {
|
|
@@ -146830,6 +146881,15 @@ function createRunController(deps) {
|
|
|
146830
146881
|
}
|
|
146831
146882
|
if (!card) return;
|
|
146832
146883
|
update2(card, event, false);
|
|
146884
|
+
if (isActivityEvent(event.type)) {
|
|
146885
|
+
deps.log.debug("\u8FD0\u884C\u5361\u5757\u7ED3\u6784", {
|
|
146886
|
+
sessionID,
|
|
146887
|
+
event: event.type,
|
|
146888
|
+
blocks: card.state.blocks.map(
|
|
146889
|
+
(b) => b.kind === "text" ? `T${b.streaming ? "*" : ""}${b.content.length}` : `tool:${b.tool.name}`
|
|
146890
|
+
)
|
|
146891
|
+
});
|
|
146892
|
+
}
|
|
146833
146893
|
},
|
|
146834
146894
|
hasActive(sessionID) {
|
|
146835
146895
|
return sessions.get(sessionID)?.active !== void 0;
|
|
@@ -148502,6 +148562,128 @@ function parsePage(value3) {
|
|
|
148502
148562
|
const parsed = typeof value3 === "number" ? value3 : typeof value3 === "string" ? Number.parseInt(value3, 10) : Number.NaN;
|
|
148503
148563
|
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0;
|
|
148504
148564
|
}
|
|
148565
|
+
|
|
148566
|
+
// src/feishu/quick-new-cards.ts
|
|
148567
|
+
function button3(text, type, value3) {
|
|
148568
|
+
return {
|
|
148569
|
+
tag: "button",
|
|
148570
|
+
text: { tag: "plain_text", content: text },
|
|
148571
|
+
type,
|
|
148572
|
+
behaviors: [{ type: "callback", value: value3 }]
|
|
148573
|
+
};
|
|
148574
|
+
}
|
|
148575
|
+
function buildQuickNewThinkingCard() {
|
|
148576
|
+
return {
|
|
148577
|
+
schema: "2.0",
|
|
148578
|
+
config: { update_multi: true },
|
|
148579
|
+
header: { title: { tag: "plain_text", content: "\u{1F914} \u6B63\u5728\u8BC6\u522B\u610F\u56FE\u4E0E\u76EE\u5F55\u2026" }, template: "blue" },
|
|
148580
|
+
body: {
|
|
148581
|
+
elements: [
|
|
148582
|
+
{ tag: "markdown", content: "\u6B63\u5728\u5224\u65AD\u8FD9\u6761\u6D88\u606F\u662F\u5426\u9700\u8981\u65B0\u5EFA\u4F1A\u8BDD\uFF0C\u4EE5\u53CA\u6700\u5408\u9002\u7684\u5DE5\u4F5C\u76EE\u5F55\u2026" }
|
|
148583
|
+
]
|
|
148584
|
+
}
|
|
148585
|
+
};
|
|
148586
|
+
}
|
|
148587
|
+
function buildQuickNewProposalCard(input) {
|
|
148588
|
+
const preview = input.textPreview.trim().replace(/\n+/g, " ").slice(0, 120);
|
|
148589
|
+
const lines = [
|
|
148590
|
+
`**\u4EFB\u52A1**\uFF1A${input.title}`,
|
|
148591
|
+
`**\u76EE\u5F55**\uFF1A\`${input.directory}\``,
|
|
148592
|
+
...input.reason ? [`**\u4F9D\u636E**\uFF1A${input.reason}`] : [],
|
|
148593
|
+
"",
|
|
148594
|
+
`\u539F\u6D88\u606F\uFF1A${preview}`
|
|
148595
|
+
];
|
|
148596
|
+
const elements = [
|
|
148597
|
+
{ tag: "markdown", content: truncateCardContent(lines.join("\n")) },
|
|
148598
|
+
{
|
|
148599
|
+
tag: "markdown",
|
|
148600
|
+
content: "\u70B9\u300C\u521B\u5EFA\u5E76\u53D1\u9001\u300D= \u5EFA\u4F1A\u8BDD + \u81EA\u52A8\u5F00\u8BDD\u9898 + \u628A\u539F\u6D88\u606F\u53D1\u8FDB\u53BB\u5F00\u59CB\u5904\u7406\u3002",
|
|
148601
|
+
text_size: "notation"
|
|
148602
|
+
},
|
|
148603
|
+
button3("\u2705 \u521B\u5EFA\u5E76\u53D1\u9001", "primary", { cmd: "quicknew", op: "create", id: input.id }),
|
|
148604
|
+
button3("\u274C \u53D6\u6D88", "default", { cmd: "quicknew", op: "cancel", id: input.id })
|
|
148605
|
+
];
|
|
148606
|
+
return {
|
|
148607
|
+
schema: "2.0",
|
|
148608
|
+
config: { update_multi: true },
|
|
148609
|
+
header: { title: { tag: "plain_text", content: "\u{1F195} \u5EFA\u8BAE\u65B0\u5EFA\u4F1A\u8BDD" }, template: "turquoise" },
|
|
148610
|
+
body: { elements }
|
|
148611
|
+
};
|
|
148612
|
+
}
|
|
148613
|
+
function buildQuickNewResolvedCard(title, note2, template2 = "grey") {
|
|
148614
|
+
return {
|
|
148615
|
+
schema: "2.0",
|
|
148616
|
+
config: { update_multi: true },
|
|
148617
|
+
header: { title: { tag: "plain_text", content: title }, template: template2 },
|
|
148618
|
+
body: { elements: [{ tag: "markdown", content: truncateCardContent(note2) }] }
|
|
148619
|
+
};
|
|
148620
|
+
}
|
|
148621
|
+
function parseQuickNewActionValue(raw) {
|
|
148622
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
148623
|
+
const value3 = raw;
|
|
148624
|
+
if (value3.cmd !== "quicknew") return void 0;
|
|
148625
|
+
const op = value3.op === "create" || value3.op === "cancel" ? value3.op : void 0;
|
|
148626
|
+
const id2 = typeof value3.id === "string" ? value3.id.trim() : "";
|
|
148627
|
+
if (!op || !id2) return void 0;
|
|
148628
|
+
return { op, id: id2 };
|
|
148629
|
+
}
|
|
148630
|
+
|
|
148631
|
+
// src/session/quick-new.ts
|
|
148632
|
+
var QUICK_NEW_INSTRUCTION = [
|
|
148633
|
+
"\u4F60\u662F\u98DE\u4E66 AI \u52A9\u624B\u300C\u7BA1\u7406\u53F0\u300D\u7684\u610F\u56FE\u8BC6\u522B\u5668\u3002\u7528\u6237\u5728\u7BA1\u7406\u53F0\uFF08\u8FD8\u6CA1\u6709\u4F1A\u8BDD\uFF09\u53D1\u6765\u4E00\u6761\u6D88\u606F\u3002",
|
|
148634
|
+
"\u8BF7\u5224\u65AD\u5B83\u662F\u4E0D\u662F\u4E00\u4E2A\u9700\u8981\u65B0\u5EFA\u4F1A\u8BDD\u53BB\u6267\u884C\u7684\u5F00\u53D1/\u64CD\u4F5C\u4EFB\u52A1\uFF0C\u5E76\u4ECE\u5019\u9009\u76EE\u5F55\u4E2D\u9009\u51FA\u6700\u5408\u9002\u7684\u5DE5\u4F5C\u76EE\u5F55\u3002",
|
|
148635
|
+
"\u53EA\u8F93\u51FA\u4E00\u4E2A JSON \u5BF9\u8C61\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\uFF1A",
|
|
148636
|
+
'{"intent":"task|chat","dir":"<\u7EDD\u5BF9\u8DEF\u5F84\u6216\u7A7A\u5B57\u7B26\u4E32>","title":"<\u4E0D\u8D85\u8FC720\u5B57\u7684\u4F1A\u8BDD\u6807\u9898>","reason":"<\u4E00\u53E5\u8BDD\u7406\u7531>"}',
|
|
148637
|
+
"\u89C4\u5219\uFF1A",
|
|
148638
|
+
"- \u95F2\u804A\u3001\u95EE\u5019\u3001\u8BE2\u95EE\u7528\u6CD5 \u2192 intent=chat\uFF0Cdir \u7559\u7A7A\u3002",
|
|
148639
|
+
"- \u660E\u786E\u7684\u5F00\u53D1/\u64CD\u4F5C\u4EFB\u52A1\uFF08\u6539 bug\u3001\u5199\u4EE3\u7801\u3001\u6574\u7406\u6587\u4EF6\u3001\u8DD1\u811A\u672C\u7B49\uFF09\u2192 intent=task\u3002",
|
|
148640
|
+
"- dir \u53EA\u80FD\u4ECE\u5019\u9009\u76EE\u5F55\u4E2D\u9009\u62E9\uFF08\u8BED\u4E49\u6700\u5339\u914D\u7684\u90A3\u4E2A\uFF09\uFF1B\u627E\u4E0D\u5230\u5408\u9002\u7684\u5C31\u7559\u7A7A\u5B57\u7B26\u4E32\u3002"
|
|
148641
|
+
].join("\n");
|
|
148642
|
+
function buildQuickNewPrompt(text, candidates) {
|
|
148643
|
+
const list = candidates.slice(0, 40).map((c) => `- ${c.path}${c.label ? `\uFF08${c.label}\uFF09` : ""}`).join("\n");
|
|
148644
|
+
return [
|
|
148645
|
+
QUICK_NEW_INSTRUCTION,
|
|
148646
|
+
"",
|
|
148647
|
+
"\u5019\u9009\u76EE\u5F55\uFF1A",
|
|
148648
|
+
list || "\uFF08\u65E0\uFF09",
|
|
148649
|
+
"",
|
|
148650
|
+
"\u7528\u6237\u6D88\u606F\uFF1A",
|
|
148651
|
+
text.slice(0, 2e3)
|
|
148652
|
+
].join("\n");
|
|
148653
|
+
}
|
|
148654
|
+
function parseQuickNewDecision(raw) {
|
|
148655
|
+
if (!raw) return void 0;
|
|
148656
|
+
const text = raw.trim();
|
|
148657
|
+
const start2 = text.indexOf("{");
|
|
148658
|
+
const end = text.lastIndexOf("}");
|
|
148659
|
+
if (start2 < 0 || end <= start2) return void 0;
|
|
148660
|
+
let parsed;
|
|
148661
|
+
try {
|
|
148662
|
+
parsed = JSON.parse(text.slice(start2, end + 1));
|
|
148663
|
+
} catch {
|
|
148664
|
+
return void 0;
|
|
148665
|
+
}
|
|
148666
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
|
|
148667
|
+
const obj = parsed;
|
|
148668
|
+
const intent = obj.intent === "task" ? "task" : obj.intent === "chat" ? "chat" : void 0;
|
|
148669
|
+
if (!intent) return void 0;
|
|
148670
|
+
const dir = typeof obj.dir === "string" ? obj.dir.trim() : typeof obj.directory === "string" ? obj.directory.trim() : "";
|
|
148671
|
+
const title = typeof obj.title === "string" ? obj.title.trim().slice(0, 30) : "";
|
|
148672
|
+
const reason = typeof obj.reason === "string" ? obj.reason.trim().slice(0, 200) : "";
|
|
148673
|
+
return {
|
|
148674
|
+
intent,
|
|
148675
|
+
...dir ? { directory: dir } : {},
|
|
148676
|
+
...title ? { title } : {},
|
|
148677
|
+
...reason ? { reason } : {}
|
|
148678
|
+
};
|
|
148679
|
+
}
|
|
148680
|
+
function matchCandidateDirectory(directory, candidates) {
|
|
148681
|
+
if (!directory) return void 0;
|
|
148682
|
+
const normalize2 = (p) => p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
|
|
148683
|
+
const target = normalize2(directory);
|
|
148684
|
+
const hit = candidates.find((c) => normalize2(c.path) === target);
|
|
148685
|
+
return hit?.path;
|
|
148686
|
+
}
|
|
148505
148687
|
var SYSTEM_DIRS = [
|
|
148506
148688
|
"/etc",
|
|
148507
148689
|
"/usr",
|
|
@@ -149253,7 +149435,7 @@ var SETUP_FORM_FIELDS = {
|
|
|
149253
149435
|
var SETUP_FORM_MAX_MODELS = 15;
|
|
149254
149436
|
var SETUP_FORM_DEFAULT_PERM = "edit";
|
|
149255
149437
|
var SETUP_FORM_DIR_CUSTOM = "__custom__";
|
|
149256
|
-
function
|
|
149438
|
+
function button4(text, type, value3) {
|
|
149257
149439
|
return {
|
|
149258
149440
|
tag: "button",
|
|
149259
149441
|
text: { tag: "plain_text", content: text },
|
|
@@ -149296,28 +149478,28 @@ function buildModelCard(input) {
|
|
|
149296
149478
|
if (input.page <= 0 && recent.length > 0) {
|
|
149297
149479
|
for (const model of recent) {
|
|
149298
149480
|
const isCurrent = input.current && refKey(input.current) === refKey(model);
|
|
149299
|
-
elements.push(
|
|
149481
|
+
elements.push(button4(`\u{1F9E0} ${shorten(modelLabel(model), 30)}`, isCurrent ? "primary" : "default", modelValue(model, input.sid)));
|
|
149300
149482
|
}
|
|
149301
149483
|
if (input.models.length > recent.length) {
|
|
149302
|
-
elements.push(
|
|
149484
|
+
elements.push(button4("\u66F4\u591A \u25B8", "default", moreValue(1, input.sid)));
|
|
149303
149485
|
}
|
|
149304
149486
|
} else {
|
|
149305
149487
|
const pageNum = input.page <= 0 ? 1 : input.page;
|
|
149306
149488
|
const slice = input.models.slice((pageNum - 1) * pageSize, pageNum * pageSize);
|
|
149307
149489
|
if (slice.length === 0 && recent.length > 0) {
|
|
149308
149490
|
for (const model of recent) {
|
|
149309
|
-
elements.push(
|
|
149491
|
+
elements.push(button4(`\u{1F9E0} ${shorten(modelLabel(model), 30)}`, "default", modelValue(model, input.sid)));
|
|
149310
149492
|
}
|
|
149311
149493
|
} else {
|
|
149312
149494
|
for (const model of slice) {
|
|
149313
149495
|
const isCurrent = input.current && refKey(input.current) === refKey(model);
|
|
149314
149496
|
elements.push(
|
|
149315
|
-
|
|
149497
|
+
button4(`\u{1F9E0} ${shorten(modelLabel(model), 30)}`, isCurrent ? "primary" : "default", modelValue(model, input.sid))
|
|
149316
149498
|
);
|
|
149317
149499
|
}
|
|
149318
|
-
if (pageNum > 1) elements.push(
|
|
149500
|
+
if (pageNum > 1) elements.push(button4("\u25C2 \u4E0A\u4E00\u9875", "default", moreValue(pageNum - 1, input.sid)));
|
|
149319
149501
|
if (pageNum * pageSize < input.models.length) {
|
|
149320
|
-
elements.push(
|
|
149502
|
+
elements.push(button4("\u4E0B\u4E00\u9875 \u25B8", "default", moreValue(pageNum + 1, input.sid)));
|
|
149321
149503
|
}
|
|
149322
149504
|
}
|
|
149323
149505
|
}
|
|
@@ -149331,7 +149513,7 @@ function buildPermCard(input) {
|
|
|
149331
149513
|
}
|
|
149332
149514
|
const elements = [{ tag: "markdown", content: truncateCardContent(lines.join("\n")) }];
|
|
149333
149515
|
for (const info of PERMISSION_PRESETS) {
|
|
149334
|
-
elements.push(
|
|
149516
|
+
elements.push(button4(presetLabel(info.id), input.current === info.id ? "primary" : "default", permValue(info.id, input.sid)));
|
|
149335
149517
|
}
|
|
149336
149518
|
return headerCard("\u{1F510} \u9009\u62E9\u6743\u9650\u6863\u4F4D", "orange", elements);
|
|
149337
149519
|
}
|
|
@@ -149347,8 +149529,8 @@ function buildConfirmCard(input) {
|
|
|
149347
149529
|
];
|
|
149348
149530
|
return headerCard("\u2705 \u786E\u8BA4\u521B\u5EFA\u4F1A\u8BDD", "green", [
|
|
149349
149531
|
{ tag: "markdown", content: truncateCardContent(lines.join("\n")) },
|
|
149350
|
-
|
|
149351
|
-
|
|
149532
|
+
button4("\u2705 \u521B\u5EFA", "primary", { wizard: "confirm" }),
|
|
149533
|
+
button4("\u2716\uFE0F \u53D6\u6D88", "default", { wizard: "cancel" })
|
|
149352
149534
|
]);
|
|
149353
149535
|
}
|
|
149354
149536
|
function buildSetupDoneCard(title, lines, template2 = "grey") {
|
|
@@ -150908,6 +151090,7 @@ async function routeEvent(event, deps) {
|
|
|
150908
151090
|
break;
|
|
150909
151091
|
case "session.text.started": {
|
|
150910
151092
|
const data = event.data;
|
|
151093
|
+
deps.log.debug("text.started", { sessionID: data.sessionID, msg: data.assistantMessageID });
|
|
150911
151094
|
deps.applyRun(data.sessionID, {
|
|
150912
151095
|
type: "text.started",
|
|
150913
151096
|
...data.assistantMessageID ? { assistantMessageID: data.assistantMessageID } : {}
|
|
@@ -150916,6 +151099,11 @@ async function routeEvent(event, deps) {
|
|
|
150916
151099
|
}
|
|
150917
151100
|
case "session.text.delta": {
|
|
150918
151101
|
const data = event.data;
|
|
151102
|
+
deps.log.debug("text.delta", {
|
|
151103
|
+
sessionID: data.sessionID,
|
|
151104
|
+
msg: data.assistantMessageID,
|
|
151105
|
+
len: typeof data.delta === "string" ? data.delta.length : 0
|
|
151106
|
+
});
|
|
150919
151107
|
deps.applyRun(data.sessionID, {
|
|
150920
151108
|
type: "text.delta",
|
|
150921
151109
|
delta: data.delta,
|
|
@@ -150925,6 +151113,12 @@ async function routeEvent(event, deps) {
|
|
|
150925
151113
|
}
|
|
150926
151114
|
case "session.text.ended": {
|
|
150927
151115
|
const data = event.data;
|
|
151116
|
+
deps.log.debug("text.ended", {
|
|
151117
|
+
sessionID: data.sessionID,
|
|
151118
|
+
msg: data.assistantMessageID,
|
|
151119
|
+
len: typeof data.text === "string" ? data.text.length : -1,
|
|
151120
|
+
hasText: typeof data.text === "string"
|
|
151121
|
+
});
|
|
150928
151122
|
deps.applyRun(data.sessionID, {
|
|
150929
151123
|
type: "text.ended",
|
|
150930
151124
|
...data.text ? { text: data.text } : {},
|
|
@@ -151326,7 +151520,8 @@ function routeCardAction(action, deps) {
|
|
|
151326
151520
|
return deps.handleCompact(action);
|
|
151327
151521
|
}
|
|
151328
151522
|
const hasForm = action.formValue !== void 0;
|
|
151329
|
-
const routed = hasForm || isSetupFormAction(value3) || Boolean(parseSessionCardValue(value3)) || Boolean(parseSetupCardValue(value3))
|
|
151523
|
+
const routed = hasForm || isSetupFormAction(value3) || Boolean(parseSessionCardValue(value3)) || Boolean(parseSetupCardValue(value3)) || // 「一句话建会话」建议卡按钮(issue #2)。
|
|
151524
|
+
Boolean(parseQuickNewActionValue(value3));
|
|
151330
151525
|
deps.log.debug("\u5361\u7247\u56DE\u8C03\u8DEF\u7531", {
|
|
151331
151526
|
hasForm,
|
|
151332
151527
|
hasValue: value3 !== void 0,
|
|
@@ -152088,6 +152283,10 @@ async function start(ctx, config, log2, logSink) {
|
|
|
152088
152283
|
rootKnown: Boolean(rootLink)
|
|
152089
152284
|
});
|
|
152090
152285
|
if (decision.kind === "main-hint") {
|
|
152286
|
+
if (config.quickNew) {
|
|
152287
|
+
await handleQuickNew(message);
|
|
152288
|
+
return;
|
|
152289
|
+
}
|
|
152091
152290
|
const res = await sender.sendCard(message.chatId, buildConsoleHintCard());
|
|
152092
152291
|
if (!res.ok) log2.warn("\u7BA1\u7406\u53F0\u63D0\u793A\u5361\u53D1\u9001\u5931\u8D25", { error: res.error ?? "unknown" });
|
|
152093
152292
|
return;
|
|
@@ -152183,6 +152382,242 @@ async function start(ctx, config, log2, logSink) {
|
|
|
152183
152382
|
log2.info("\u673A\u5668\u4EBA\u83DC\u5355\u70B9\u51FB \u2192 \u6267\u884C\u547D\u4EE4", { eventKey: click.eventKey, cmd: entry.cmd, label: entry.label });
|
|
152184
152383
|
await handleMessage(synthetic);
|
|
152185
152384
|
}
|
|
152385
|
+
const QUICK_NEW_KEY_PREFIX = "feishu:v2:quicknew:";
|
|
152386
|
+
const QUICK_NEW_TTL_MS = 60 * 60 * 1e3;
|
|
152387
|
+
function parseQuickNewEntry(raw) {
|
|
152388
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
152389
|
+
const v = raw;
|
|
152390
|
+
const text = typeof v.text === "string" ? v.text : "";
|
|
152391
|
+
const dir = typeof v.dir === "string" ? v.dir : "";
|
|
152392
|
+
const chatId = typeof v.chatId === "string" ? v.chatId : "";
|
|
152393
|
+
const openId = typeof v.openId === "string" ? v.openId : "";
|
|
152394
|
+
const anchorMessageId = typeof v.anchorMessageId === "string" ? v.anchorMessageId : "";
|
|
152395
|
+
if (!text || !dir || !chatId || !openId || !anchorMessageId) return void 0;
|
|
152396
|
+
return {
|
|
152397
|
+
text,
|
|
152398
|
+
title: typeof v.title === "string" ? v.title : "",
|
|
152399
|
+
dir,
|
|
152400
|
+
chatId,
|
|
152401
|
+
openId,
|
|
152402
|
+
anchorMessageId,
|
|
152403
|
+
createdAt: typeof v.createdAt === "number" ? v.createdAt : 0
|
|
152404
|
+
};
|
|
152405
|
+
}
|
|
152406
|
+
async function handleQuickNew(message) {
|
|
152407
|
+
const hint = async () => {
|
|
152408
|
+
const res = await sender.sendCard(message.chatId, buildConsoleHintCard());
|
|
152409
|
+
if (!res.ok) log2.warn("\u7BA1\u7406\u53F0\u63D0\u793A\u5361\u53D1\u9001\u5931\u8D25", { error: res.error ?? "unknown" });
|
|
152410
|
+
};
|
|
152411
|
+
try {
|
|
152412
|
+
const candidates = [];
|
|
152413
|
+
const seen = /* @__PURE__ */ new Set();
|
|
152414
|
+
for (const dir2 of await recent.listDirs()) {
|
|
152415
|
+
if (!seen.has(dir2)) {
|
|
152416
|
+
seen.add(dir2);
|
|
152417
|
+
candidates.push({ path: dir2 });
|
|
152418
|
+
}
|
|
152419
|
+
}
|
|
152420
|
+
let entries3 = [];
|
|
152421
|
+
try {
|
|
152422
|
+
const raw = await listAllSessionsRaw() ?? await listSessionsOverHttp({}, { log: log2 });
|
|
152423
|
+
entries3 = normalizeSessionList(raw) ?? [];
|
|
152424
|
+
} catch (err2) {
|
|
152425
|
+
log2.debug("quick-new \u4F1A\u8BDD\u5217\u8868\u8BFB\u53D6\u5931\u8D25", { error: errorMessage(err2) });
|
|
152426
|
+
}
|
|
152427
|
+
for (const entry2 of entries3) {
|
|
152428
|
+
if (entry2.directory && !seen.has(entry2.directory)) {
|
|
152429
|
+
seen.add(entry2.directory);
|
|
152430
|
+
candidates.push({ path: entry2.directory, ...entry2.title ? { label: entry2.title } : {} });
|
|
152431
|
+
}
|
|
152432
|
+
}
|
|
152433
|
+
const routingSessionID = entries3[0]?.sessionID;
|
|
152434
|
+
if (candidates.length === 0 || !routingSessionID) {
|
|
152435
|
+
log2.debug("quick-new \u65E0\u5019\u9009\u76EE\u5F55\u6216\u4F1A\u8BDD\uFF0C\u56DE\u9000\u63D0\u793A\u5361", {
|
|
152436
|
+
candidates: candidates.length,
|
|
152437
|
+
hasRouting: Boolean(routingSessionID)
|
|
152438
|
+
});
|
|
152439
|
+
await hint();
|
|
152440
|
+
return;
|
|
152441
|
+
}
|
|
152442
|
+
const ack = await sender.sendCard(message.chatId, buildQuickNewThinkingCard());
|
|
152443
|
+
if (!ack.ok || !ack.messageId) {
|
|
152444
|
+
await hint();
|
|
152445
|
+
return;
|
|
152446
|
+
}
|
|
152447
|
+
const generateText = async (prompt, requestOptions) => {
|
|
152448
|
+
const api = ctx.generate.text;
|
|
152449
|
+
if (typeof api !== "function") throw new Error("generate.text unavailable");
|
|
152450
|
+
return api({ prompt }, requestOptions);
|
|
152451
|
+
};
|
|
152452
|
+
const outcome = await quickGenerateWithSession(
|
|
152453
|
+
{ log: log2, generateText },
|
|
152454
|
+
{ prompt: buildQuickNewPrompt(message.text, candidates), sessionID: routingSessionID }
|
|
152455
|
+
);
|
|
152456
|
+
const decision = parseQuickNewDecision(extractGeneratedText(outcome.result));
|
|
152457
|
+
const dir = decision?.intent === "task" ? matchCandidateDirectory(decision.directory, candidates) : void 0;
|
|
152458
|
+
const validation = dir ? validateDir(dir) : void 0;
|
|
152459
|
+
if (!decision || decision.intent !== "task" || !dir || !validation?.ok) {
|
|
152460
|
+
log2.debug("quick-new \u5224\u5B9A\u4E3A\u975E\u4EFB\u52A1\u6216\u672A\u5339\u914D\u5230\u76EE\u5F55", {
|
|
152461
|
+
intent: decision?.intent,
|
|
152462
|
+
dir: decision?.directory,
|
|
152463
|
+
reason: decision?.reason
|
|
152464
|
+
});
|
|
152465
|
+
await sender.patchCard(ack.messageId, buildConsoleHintCard());
|
|
152466
|
+
return;
|
|
152467
|
+
}
|
|
152468
|
+
const id2 = `qn_${message.messageId}`;
|
|
152469
|
+
const title = decision.title || topicTitle(message.text);
|
|
152470
|
+
const entry = {
|
|
152471
|
+
text: message.text,
|
|
152472
|
+
title,
|
|
152473
|
+
dir: validation.path,
|
|
152474
|
+
chatId: message.chatId,
|
|
152475
|
+
openId: message.senderOpenId,
|
|
152476
|
+
anchorMessageId: message.messageId,
|
|
152477
|
+
createdAt: Date.now()
|
|
152478
|
+
};
|
|
152479
|
+
await ctx.storage.set(
|
|
152480
|
+
QUICK_NEW_KEY_PREFIX + id2,
|
|
152481
|
+
entry
|
|
152482
|
+
);
|
|
152483
|
+
await sender.patchCard(
|
|
152484
|
+
ack.messageId,
|
|
152485
|
+
buildQuickNewProposalCard({
|
|
152486
|
+
id: id2,
|
|
152487
|
+
title,
|
|
152488
|
+
directory: validation.path,
|
|
152489
|
+
textPreview: message.text,
|
|
152490
|
+
...decision.reason ? { reason: decision.reason } : {}
|
|
152491
|
+
})
|
|
152492
|
+
);
|
|
152493
|
+
log2.info("quick-new \u5EFA\u8BAE\u5361\u5DF2\u53D1\u9001", { dir: validation.path, title, reason: decision.reason });
|
|
152494
|
+
} catch (err2) {
|
|
152495
|
+
log2.warn("quick-new \u5904\u7406\u5931\u8D25\uFF0C\u56DE\u9000\u7BA1\u7406\u53F0\u63D0\u793A", { error: errorMessage(err2) });
|
|
152496
|
+
await hint();
|
|
152497
|
+
}
|
|
152498
|
+
}
|
|
152499
|
+
async function handleQuickNewCardAction(action, value3) {
|
|
152500
|
+
if (!owner.isAllowed(action.operatorOpenId)) {
|
|
152501
|
+
return { toast: { type: "error", content: "\u65E0\u64CD\u4F5C\u6743\u9650" } };
|
|
152502
|
+
}
|
|
152503
|
+
const key = QUICK_NEW_KEY_PREFIX + value3.id;
|
|
152504
|
+
let raw;
|
|
152505
|
+
try {
|
|
152506
|
+
raw = await ctx.storage.get(key);
|
|
152507
|
+
} catch {
|
|
152508
|
+
raw = void 0;
|
|
152509
|
+
}
|
|
152510
|
+
const entry = parseQuickNewEntry(raw);
|
|
152511
|
+
if (!entry || Date.now() - entry.createdAt > QUICK_NEW_TTL_MS) {
|
|
152512
|
+
try {
|
|
152513
|
+
await ctx.storage.remove(key);
|
|
152514
|
+
} catch {
|
|
152515
|
+
}
|
|
152516
|
+
if (action.messageId) {
|
|
152517
|
+
await sender.patchCard(
|
|
152518
|
+
action.messageId,
|
|
152519
|
+
buildQuickNewResolvedCard("\u231B\uFE0F \u5EFA\u8BAE\u5DF2\u8FC7\u671F", "\u8BF7\u91CD\u65B0\u53D1\u9001\u6D88\u606F\u3002", "orange")
|
|
152520
|
+
);
|
|
152521
|
+
}
|
|
152522
|
+
return { toast: { type: "warning", content: "\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u53D1\u6D88\u606F" } };
|
|
152523
|
+
}
|
|
152524
|
+
if (value3.op === "cancel") {
|
|
152525
|
+
try {
|
|
152526
|
+
await ctx.storage.remove(key);
|
|
152527
|
+
} catch {
|
|
152528
|
+
}
|
|
152529
|
+
if (action.messageId) {
|
|
152530
|
+
await sender.patchCard(action.messageId, buildQuickNewResolvedCard("\u274C \u5DF2\u53D6\u6D88", "\u672A\u521B\u5EFA\u4F1A\u8BDD\u3002"));
|
|
152531
|
+
}
|
|
152532
|
+
return { toast: { type: "info", content: "\u5DF2\u53D6\u6D88" } };
|
|
152533
|
+
}
|
|
152534
|
+
const validation = validateDir(entry.dir);
|
|
152535
|
+
if (!validation.ok) {
|
|
152536
|
+
if (action.messageId) {
|
|
152537
|
+
await sender.patchCard(
|
|
152538
|
+
action.messageId,
|
|
152539
|
+
buildQuickNewResolvedCard("\u26A0\uFE0F \u76EE\u5F55\u4E0D\u53EF\u7528", `${entry.dir}\uFF1A${validation.message}`, "red")
|
|
152540
|
+
);
|
|
152541
|
+
}
|
|
152542
|
+
return { toast: { type: "error", content: "\u76EE\u5F55\u4E0D\u53EF\u7528" } };
|
|
152543
|
+
}
|
|
152544
|
+
try {
|
|
152545
|
+
await ctx.storage.remove(key);
|
|
152546
|
+
} catch {
|
|
152547
|
+
}
|
|
152548
|
+
const perm = "edit";
|
|
152549
|
+
const title = entry.title || topicTitle(entry.text);
|
|
152550
|
+
void (async () => {
|
|
152551
|
+
const created = await createSessionInternal({
|
|
152552
|
+
title,
|
|
152553
|
+
chatId: entry.chatId,
|
|
152554
|
+
openId: entry.openId,
|
|
152555
|
+
setActive: false,
|
|
152556
|
+
directory: validation.path,
|
|
152557
|
+
permissions: presetToRuleset(perm),
|
|
152558
|
+
perm,
|
|
152559
|
+
gateMode: presetGateMode(perm)
|
|
152560
|
+
});
|
|
152561
|
+
await recent.addDir(validation.path);
|
|
152562
|
+
const readyCard = buildSessionReadyCard({
|
|
152563
|
+
title,
|
|
152564
|
+
sessionID: created.id,
|
|
152565
|
+
dir: validation.path,
|
|
152566
|
+
perm: presetLabel(perm)
|
|
152567
|
+
});
|
|
152568
|
+
const res = await sender.replyCard(entry.anchorMessageId, readyCard, { replyInThread: true });
|
|
152569
|
+
if (res.ok && res.messageId) {
|
|
152570
|
+
await sessionMap.bindRoot(entry.anchorMessageId, created.id);
|
|
152571
|
+
const meta = res.threadId ? void 0 : await sender.getMessageMeta(res.messageId);
|
|
152572
|
+
const threadId = res.threadId ?? meta?.threadId;
|
|
152573
|
+
if (threadId) {
|
|
152574
|
+
await sessionMap.bindThread(threadId, created.id, entry.chatId, entry.openId, entry.anchorMessageId);
|
|
152575
|
+
} else {
|
|
152576
|
+
log2.warn("quick-new \u5F00\u8BDD\u9898\u540E\u672A\u8BFB\u5230 thread_id", { sessionID: created.id });
|
|
152577
|
+
}
|
|
152578
|
+
await sessionMap.setRootCard(created.id, {
|
|
152579
|
+
style: "created",
|
|
152580
|
+
sessionID: created.id,
|
|
152581
|
+
title,
|
|
152582
|
+
dir: validation.path,
|
|
152583
|
+
perm: presetLabel(perm)
|
|
152584
|
+
});
|
|
152585
|
+
} else {
|
|
152586
|
+
log2.warn("quick-new \u5F00\u8BDD\u9898\u5931\u8D25", { sessionID: created.id, error: res.error ?? "unknown" });
|
|
152587
|
+
}
|
|
152588
|
+
await runInSession(
|
|
152589
|
+
{
|
|
152590
|
+
eventId: `quicknew_${value3.id}`,
|
|
152591
|
+
messageId: entry.anchorMessageId,
|
|
152592
|
+
chatId: entry.chatId,
|
|
152593
|
+
text: entry.text,
|
|
152594
|
+
senderOpenId: entry.openId
|
|
152595
|
+
},
|
|
152596
|
+
created.id,
|
|
152597
|
+
entry.anchorMessageId
|
|
152598
|
+
);
|
|
152599
|
+
if (action.messageId) {
|
|
152600
|
+
await sender.patchCard(
|
|
152601
|
+
action.messageId,
|
|
152602
|
+
buildQuickNewResolvedCard(
|
|
152603
|
+
"\u2705 \u5DF2\u521B\u5EFA",
|
|
152604
|
+
`\u76EE\u5F55\uFF1A\`${validation.path}\`
|
|
152605
|
+
\u5DF2\u5F00\u8BDD\u9898\u5E76\u5F00\u59CB\u5904\u7406\u4F60\u7684\u6D88\u606F\u3002`,
|
|
152606
|
+
"green"
|
|
152607
|
+
)
|
|
152608
|
+
);
|
|
152609
|
+
}
|
|
152610
|
+
})().catch((err2) => {
|
|
152611
|
+
log2.warn("quick-new \u5EFA\u4F1A\u8BDD\u5931\u8D25", { error: errorMessage(err2) });
|
|
152612
|
+
if (action.messageId) {
|
|
152613
|
+
void sender.patchCard(
|
|
152614
|
+
action.messageId,
|
|
152615
|
+
buildQuickNewResolvedCard("\u26A0\uFE0F \u521B\u5EFA\u5931\u8D25", errorMessage(err2).slice(0, 160), "red")
|
|
152616
|
+
);
|
|
152617
|
+
}
|
|
152618
|
+
});
|
|
152619
|
+
return { toast: { type: "success", content: "\u5DF2\u521B\u5EFA\uFF0C\u6B63\u5728\u6253\u5F00\u8BDD\u9898\u2026" } };
|
|
152620
|
+
}
|
|
152186
152621
|
async function runInActiveSession(message) {
|
|
152187
152622
|
let active = await sessionMap.getActive(message.chatId);
|
|
152188
152623
|
if (!active) {
|
|
@@ -152257,7 +152692,11 @@ ${downloadedAttachmentPrompt(message.attachment, outcome)}`;
|
|
|
152257
152692
|
handleForm: (a) => formRelay.handleCardAction(a),
|
|
152258
152693
|
handleStop: (a) => stop.handleCardAction(a),
|
|
152259
152694
|
handleCompact: (a) => compact2.handleCardAction(a),
|
|
152260
|
-
handleCommands: (a) =>
|
|
152695
|
+
handleCommands: (a) => {
|
|
152696
|
+
const quick = parseQuickNewActionValue(a.rawValue);
|
|
152697
|
+
if (quick) return handleQuickNewCardAction(a, quick);
|
|
152698
|
+
return commands.handleCardAction(a);
|
|
152699
|
+
},
|
|
152261
152700
|
handleApprovals: (a) => approvals.handleCardAction(a)
|
|
152262
152701
|
})
|
|
152263
152702
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-feishu-plugin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.14",
|
|
4
4
|
"description": "Feishu/Lark plugin for OpenCode V2 — single-user topics as sessions, in-card permission approvals, streaming cards. Minimal permissions, no public endpoint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|