@jesonliu/lark-claudecode-bridge 0.3.0
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/LICENSE +21 -0
- package/README.md +183 -0
- package/config.example.yaml +42 -0
- package/deploy/com.lark-claudecode-bridge.plist +9 -0
- package/deploy/lark-claudecode-bridge.service +11 -0
- package/deploy/windows-start.bat +2 -0
- package/dist/access/access-control.d.ts +43 -0
- package/dist/access/access-control.js +88 -0
- package/dist/access/access-control.js.map +1 -0
- package/dist/bin/lcb.d.ts +2 -0
- package/dist/bin/lcb.js +191 -0
- package/dist/bin/lcb.js.map +1 -0
- package/dist/cli/app-manager.d.ts +28 -0
- package/dist/cli/app-manager.js +99 -0
- package/dist/cli/app-manager.js.map +1 -0
- package/dist/cli/asker.d.ts +7 -0
- package/dist/cli/asker.js +41 -0
- package/dist/cli/asker.js.map +1 -0
- package/dist/cli/setup-wizard.d.ts +28 -0
- package/dist/cli/setup-wizard.js +114 -0
- package/dist/cli/setup-wizard.js.map +1 -0
- package/dist/cli/stdin-pairing.d.ts +7 -0
- package/dist/cli/stdin-pairing.js +19 -0
- package/dist/cli/stdin-pairing.js.map +1 -0
- package/dist/cli/ws-manager.d.ts +11 -0
- package/dist/cli/ws-manager.js +67 -0
- package/dist/cli/ws-manager.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +169 -0
- package/dist/config.js.map +1 -0
- package/dist/executor/claude-executor.d.ts +28 -0
- package/dist/executor/claude-executor.js +84 -0
- package/dist/executor/claude-executor.js.map +1 -0
- package/dist/executor/notify-server.d.ts +34 -0
- package/dist/executor/notify-server.js +193 -0
- package/dist/executor/notify-server.js.map +1 -0
- package/dist/executor/output-collector.d.ts +5 -0
- package/dist/executor/output-collector.js +17 -0
- package/dist/executor/output-collector.js.map +1 -0
- package/dist/executor/permission-gate.d.ts +20 -0
- package/dist/executor/permission-gate.js +66 -0
- package/dist/executor/permission-gate.js.map +1 -0
- package/dist/gateway/card-builder.d.ts +15 -0
- package/dist/gateway/card-builder.js +60 -0
- package/dist/gateway/card-builder.js.map +1 -0
- package/dist/gateway/feishu-gateway.d.ts +164 -0
- package/dist/gateway/feishu-gateway.js +249 -0
- package/dist/gateway/feishu-gateway.js.map +1 -0
- package/dist/gateway/progress-card.d.ts +34 -0
- package/dist/gateway/progress-card.js +106 -0
- package/dist/gateway/progress-card.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +433 -0
- package/dist/index.js.map +1 -0
- package/dist/session/channel.d.ts +18 -0
- package/dist/session/channel.js +44 -0
- package/dist/session/channel.js.map +1 -0
- package/dist/session/commands.d.ts +24 -0
- package/dist/session/commands.js +87 -0
- package/dist/session/commands.js.map +1 -0
- package/dist/session/session-store.d.ts +42 -0
- package/dist/session/session-store.js +85 -0
- package/dist/session/session-store.js.map +1 -0
- package/dist/session/triggers.d.ts +12 -0
- package/dist/session/triggers.js +34 -0
- package/dist/session/triggers.js.map +1 -0
- package/dist/transcript/transcript-writer.d.ts +85 -0
- package/dist/transcript/transcript-writer.js +64 -0
- package/dist/transcript/transcript-writer.js.map +1 -0
- package/dist/types.d.ts +105 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +41 -0
package/dist/config.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { parse } from 'yaml';
|
|
5
|
+
/** LCB_CONFIG_DIR 环境变量可覆盖配置根目录(多租户分进程场景用;正常用户无须设置) */
|
|
6
|
+
export const CONFIG_DIR = join(process.env.LCB_CONFIG_DIR ?? homedir(), '.lark-claudecode-bridge');
|
|
7
|
+
export const CONFIG_PATH = join(CONFIG_DIR, 'config.yaml');
|
|
8
|
+
/** 触发词规则校验:match/rewrite 必须非空(错误消息带定位),顺序保持(按序首个命中生效) */
|
|
9
|
+
function normalizeTriggers(raw, where) {
|
|
10
|
+
if (raw === undefined || raw === null)
|
|
11
|
+
return undefined;
|
|
12
|
+
if (!Array.isArray(raw))
|
|
13
|
+
throw new Error(`${where} 的 triggers 必须为数组`);
|
|
14
|
+
return raw.map((t, j) => {
|
|
15
|
+
const match = typeof t?.match === 'string' ? t.match.trim() : '';
|
|
16
|
+
const rewrite = typeof t?.rewrite === 'string' ? t.rewrite.trim() : '';
|
|
17
|
+
if (!match)
|
|
18
|
+
throw new Error(`${where} 的 triggers[${j}].match 不能为空`);
|
|
19
|
+
if (!rewrite)
|
|
20
|
+
throw new Error(`${where} 的 triggers[${j}].rewrite 不能为空`);
|
|
21
|
+
// 无占位符合法但通常系笔误:用户输入会整个丢弃,warn 提示
|
|
22
|
+
if (!rewrite.includes('{text}') && !rewrite.includes('{args}')) {
|
|
23
|
+
console.warn(`[配置] ${where} 的 triggers[${j}].rewrite 不含 {text} / {args} 占位符,用户输入将不会传入(若非有意请修正)`);
|
|
24
|
+
}
|
|
25
|
+
return { match, rewrite };
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/** 插件引用校验:name/path 必须非空;路径不存在仅 warn(运行期外部目录可能挂载较晚,硬抛会阻断整个桥启动) */
|
|
29
|
+
function normalizePlugins(raw, where) {
|
|
30
|
+
if (raw === undefined || raw === null)
|
|
31
|
+
return undefined;
|
|
32
|
+
if (!Array.isArray(raw))
|
|
33
|
+
throw new Error(`${where} 的 plugins 必须为数组`);
|
|
34
|
+
return raw.map((p, j) => {
|
|
35
|
+
const name = typeof p?.name === 'string' ? p.name.trim() : '';
|
|
36
|
+
const path = typeof p?.path === 'string' ? p.path.trim() : '';
|
|
37
|
+
if (!name)
|
|
38
|
+
throw new Error(`${where} 的 plugins[${j}].name 不能为空`);
|
|
39
|
+
if (!path)
|
|
40
|
+
throw new Error(`${where} 的 plugins[${j}].path 不能为空`);
|
|
41
|
+
if (!existsSync(path)) {
|
|
42
|
+
console.warn(`[配置] ${where} 的 plugins[${j}](${name}) 路径当前不存在:${path}(若启动时仍缺失,将由 Claude Code 会话侧报错)`);
|
|
43
|
+
}
|
|
44
|
+
return { name, path };
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 归一化应用列表:doc.apps 数组优先(并存旧 feishu 时 warn 忽略);
|
|
49
|
+
* 无 apps 有 feishu → 归一化为单元素数组(只在内存归一,不回写文件,保住用户手写注释)。
|
|
50
|
+
* 每项逐字段校验,错误消息带 apps[i](name) 定位;appId 重复抛错(同一应用建两条长连接是配置错误)。
|
|
51
|
+
*/
|
|
52
|
+
function normalizeApps(doc, workspaceNames) {
|
|
53
|
+
const rawApps = [];
|
|
54
|
+
const isLegacy = !Array.isArray(doc.apps);
|
|
55
|
+
if (!isLegacy) {
|
|
56
|
+
rawApps.push(...doc.apps);
|
|
57
|
+
if (doc.feishu != null)
|
|
58
|
+
console.warn('[配置] 检测到 apps 与旧 feishu 并存,已忽略 feishu(apps 优先)');
|
|
59
|
+
}
|
|
60
|
+
else if (doc.feishu != null && typeof doc.feishu === 'object') {
|
|
61
|
+
rawApps.push(doc.feishu);
|
|
62
|
+
}
|
|
63
|
+
if (rawApps.length === 0)
|
|
64
|
+
throw new Error('配置缺少 apps(或旧版 feishu)应用凭证,请先运行 lcb setup');
|
|
65
|
+
const seenAppIds = new Set();
|
|
66
|
+
const apps = rawApps.map((raw, i) => {
|
|
67
|
+
const appId = typeof raw.app_id === 'string' ? raw.app_id.trim() : '';
|
|
68
|
+
const name = (typeof raw.name === 'string' && raw.name.trim()) || appId;
|
|
69
|
+
const where = `apps[${i}]${name ? `(${name})` : ''}`;
|
|
70
|
+
if (!appId)
|
|
71
|
+
throw new Error(`${where} 缺少 app_id,请检查 config.yaml`);
|
|
72
|
+
if (!raw.app_secret)
|
|
73
|
+
throw new Error(`${where} 缺少 app_secret,请检查 config.yaml`);
|
|
74
|
+
if (seenAppIds.has(appId)) {
|
|
75
|
+
throw new Error(`${where} 的 app_id "${appId}" 重复:同一应用建两条长连接会导致事件错乱,请检查 config.yaml`);
|
|
76
|
+
}
|
|
77
|
+
seenAppIds.add(appId);
|
|
78
|
+
if (raw.default_workspace && !workspaceNames.includes(raw.default_workspace)) {
|
|
79
|
+
throw new Error(`${where} 的 default_workspace "${raw.default_workspace}" 不在 workspaces 列表中`);
|
|
80
|
+
}
|
|
81
|
+
let concurrency;
|
|
82
|
+
if (raw.concurrency !== undefined && raw.concurrency !== null) {
|
|
83
|
+
// 非法值必须在此处抛错:NaN/0 透传给 Semaphore 会导致任务永远拿不到许可(死锁)
|
|
84
|
+
concurrency = Number(raw.concurrency);
|
|
85
|
+
if (!Number.isFinite(concurrency) || concurrency < 1 || concurrency > 100) {
|
|
86
|
+
throw new Error(`${where} 的 concurrency 必须为 1-100 的数字(当前值:${String(raw.concurrency)}),请检查 config.yaml`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Claude Code 数据目录:显式配置优先;旧 feishu 迁移的 app 沿用系统默认 ~/.claude(保存量会话 resume);
|
|
90
|
+
// apps 里新配置的 app 缺省独立目录——每个机器人一套会话存储/设置/插件,物理隔离互不共享
|
|
91
|
+
const claudeConfigDir = typeof raw.claude_config_dir === 'string' && raw.claude_config_dir.trim()
|
|
92
|
+
? raw.claude_config_dir
|
|
93
|
+
: isLegacy ? join(homedir(), '.claude') : join(CONFIG_DIR, 'claude', appId);
|
|
94
|
+
return {
|
|
95
|
+
name,
|
|
96
|
+
appId,
|
|
97
|
+
appSecret: raw.app_secret,
|
|
98
|
+
domain: raw.domain === 'lark' ? 'lark' : 'feishu',
|
|
99
|
+
defaultWorkspace: raw.default_workspace,
|
|
100
|
+
concurrency,
|
|
101
|
+
appendSystemPrompt: raw.append_system_prompt,
|
|
102
|
+
claudeConfigDir,
|
|
103
|
+
env: raw.env && typeof raw.env === 'object' ? raw.env : undefined,
|
|
104
|
+
triggers: normalizeTriggers(raw.triggers, where),
|
|
105
|
+
plugins: normalizePlugins(raw.plugins, where),
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
// name 重复仅 warn:纯显示用途,不影响路由与隔离
|
|
109
|
+
const names = new Set();
|
|
110
|
+
for (const a of apps) {
|
|
111
|
+
if (names.has(a.name))
|
|
112
|
+
console.warn(`[配置] 应用名称 "${a.name}" 重复,仅影响显示,不影响功能`);
|
|
113
|
+
names.add(a.name);
|
|
114
|
+
}
|
|
115
|
+
return apps;
|
|
116
|
+
}
|
|
117
|
+
export function loadConfig(path = CONFIG_PATH) {
|
|
118
|
+
let raw;
|
|
119
|
+
try {
|
|
120
|
+
raw = readFileSync(path, 'utf8');
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
throw new Error(`找不到配置文件 ${path},请先运行 lcb setup`);
|
|
124
|
+
}
|
|
125
|
+
let doc;
|
|
126
|
+
try {
|
|
127
|
+
doc = parse(raw);
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
throw new Error(`配置文件 YAML 语法错误:${e.message}`);
|
|
131
|
+
}
|
|
132
|
+
const workspaces = (doc.workspaces ?? []);
|
|
133
|
+
if (workspaces.length === 0)
|
|
134
|
+
throw new Error('配置至少需要一个 workspaces 条目');
|
|
135
|
+
const defaults = (doc.defaults ?? {});
|
|
136
|
+
if (!workspaces.some((w) => w.name === defaults.workspace)) {
|
|
137
|
+
throw new Error(`defaults.workspace "${defaults.workspace}" 不在 workspaces 列表中`);
|
|
138
|
+
}
|
|
139
|
+
// 非法值必须在此处抛错:NaN/0 透传给 Semaphore 会导致任务永远拿不到许可(死锁)
|
|
140
|
+
const concurrency = Number(doc.concurrency ?? 3);
|
|
141
|
+
if (!Number.isFinite(concurrency) || concurrency < 1 || concurrency > 100) {
|
|
142
|
+
throw new Error(`concurrency 必须为 1-100 的数字(当前值:${String(doc.concurrency)}),请检查 config.yaml`);
|
|
143
|
+
}
|
|
144
|
+
const apps = normalizeApps(doc, workspaces.map((w) => w.name));
|
|
145
|
+
const transcriptsRaw = (doc.transcripts ?? {});
|
|
146
|
+
const retentionDays = Number(transcriptsRaw.retention_days ?? 0);
|
|
147
|
+
return {
|
|
148
|
+
apps,
|
|
149
|
+
workspaces,
|
|
150
|
+
defaults: { workspace: defaults.workspace },
|
|
151
|
+
concurrency,
|
|
152
|
+
// 缺省/0/非法 = 永久保留,不默认删用户数据
|
|
153
|
+
transcripts: Number.isFinite(retentionDays) && retentionDays > 0 ? { retentionDays } : undefined,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/** 热重载比较:任一 app 的凭证/名称/工作区/并发/配置目录/人格变化即视为需重启的变更(顺序敏感) */
|
|
157
|
+
export function sameApps(a, b) {
|
|
158
|
+
if (a.length !== b.length)
|
|
159
|
+
return false;
|
|
160
|
+
return a.every((x, i) => {
|
|
161
|
+
const y = b[i];
|
|
162
|
+
return x.appId === y.appId && x.appSecret === y.appSecret && x.name === y.name
|
|
163
|
+
&& x.domain === y.domain && x.defaultWorkspace === y.defaultWorkspace
|
|
164
|
+
&& x.concurrency === y.concurrency && x.claudeConfigDir === y.claudeConfigDir
|
|
165
|
+
&& x.appendSystemPrompt === y.appendSystemPrompt
|
|
166
|
+
&& JSON.stringify(x.env ?? {}) === JSON.stringify(y.env ?? {});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type McpServerConfig } from '@anthropic-ai/claude-agent-sdk';
|
|
2
|
+
import type { ProgressEvent, TaskOutcome } from '../types.js';
|
|
3
|
+
export interface ExecutorCallbacks {
|
|
4
|
+
onProgress(event: ProgressEvent): Promise<void> | void;
|
|
5
|
+
}
|
|
6
|
+
export interface RunTaskOptions {
|
|
7
|
+
cwd: string;
|
|
8
|
+
resumeSessionId?: string;
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
/** per-app 环境变量:至少含 CLAUDE_CONFIG_DIR(每个机器人独立的 Claude Code 数据目录);可覆盖模型凭证等 */
|
|
11
|
+
env?: Record<string, string | undefined>;
|
|
12
|
+
/** per-app 人格补充,直通 SDK appendSystemPrompt */
|
|
13
|
+
appendSystemPrompt?: string;
|
|
14
|
+
canUseTool?: (toolName: string, input: Record<string, unknown>) => Promise<{
|
|
15
|
+
behavior: 'allow';
|
|
16
|
+
message?: string;
|
|
17
|
+
} | {
|
|
18
|
+
behavior: 'deny';
|
|
19
|
+
message: string;
|
|
20
|
+
}>;
|
|
21
|
+
/** 额外注入的 MCP server(如 lcb-notify 进程内通知工具),直通 Options.mcpServers */
|
|
22
|
+
mcpServers?: Record<string, McpServerConfig>;
|
|
23
|
+
/** 显式加载的本地插件目录(含 .claude-plugin/plugin.json),映射为 SDK 的 {type:'local', path} */
|
|
24
|
+
plugins?: Array<{
|
|
25
|
+
path: string;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
export declare function runTask(prompt: string, opts: RunTaskOptions, cb: ExecutorCallbacks): Promise<TaskOutcome>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Claude 执行器:封装 Agent SDK 的 query(),把流消息转成进度事件并收集产出
|
|
2
|
+
// 注意:不覆盖/清理任何 ANTHROPIC_* 环境变量,凭证与代理配置透传 process.env
|
|
3
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
4
|
+
import { OutputCollector } from './output-collector.js';
|
|
5
|
+
// SDK 选项只收 abortController(无 signal 项),把外部 signal 的中止转发给它
|
|
6
|
+
function bridgeSignal(signal) {
|
|
7
|
+
const controller = new AbortController();
|
|
8
|
+
if (signal.aborted) {
|
|
9
|
+
controller.abort();
|
|
10
|
+
return controller;
|
|
11
|
+
}
|
|
12
|
+
signal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
13
|
+
return controller;
|
|
14
|
+
}
|
|
15
|
+
function summarizeToolInput(input) {
|
|
16
|
+
if (typeof input.command === 'string')
|
|
17
|
+
return input.command;
|
|
18
|
+
if (typeof input.file_path === 'string')
|
|
19
|
+
return input.file_path;
|
|
20
|
+
if (typeof input.pattern === 'string')
|
|
21
|
+
return input.pattern;
|
|
22
|
+
return JSON.stringify(input).slice(0, 100);
|
|
23
|
+
}
|
|
24
|
+
export async function runTask(prompt, opts, cb) {
|
|
25
|
+
const collector = new OutputCollector();
|
|
26
|
+
const options = {
|
|
27
|
+
cwd: opts.cwd,
|
|
28
|
+
settingSources: ['user', 'project'],
|
|
29
|
+
permissionMode: 'default',
|
|
30
|
+
includePartialMessages: false,
|
|
31
|
+
...(opts.resumeSessionId ? { resume: opts.resumeSessionId } : {}),
|
|
32
|
+
...(opts.canUseTool ? { canUseTool: opts.canUseTool } : {}),
|
|
33
|
+
...(opts.signal ? { abortController: bridgeSignal(opts.signal) } : {}),
|
|
34
|
+
...(opts.env ? { env: opts.env } : {}),
|
|
35
|
+
...(opts.appendSystemPrompt ? { appendSystemPrompt: opts.appendSystemPrompt } : {}),
|
|
36
|
+
...(opts.mcpServers ? { mcpServers: opts.mcpServers } : {}),
|
|
37
|
+
...(opts.plugins ? { plugins: opts.plugins.map((p) => ({ type: 'local', path: p.path })) } : {}),
|
|
38
|
+
};
|
|
39
|
+
const q = query({ prompt, options });
|
|
40
|
+
let finalText = '';
|
|
41
|
+
let sessionId = opts.resumeSessionId ?? '';
|
|
42
|
+
let turns = 0;
|
|
43
|
+
const toolNames = new Map(); // tool_use_id → 工具名(tool_result 块本身不带 name)
|
|
44
|
+
for await (const message of q) {
|
|
45
|
+
switch (message.type) {
|
|
46
|
+
case 'assistant': {
|
|
47
|
+
turns++;
|
|
48
|
+
for (const block of message.message.content) {
|
|
49
|
+
if (block.type === 'text') {
|
|
50
|
+
await cb.onProgress({ kind: 'text', content: block.text });
|
|
51
|
+
}
|
|
52
|
+
else if (block.type === 'tool_use') {
|
|
53
|
+
const input = (block.input ?? {});
|
|
54
|
+
toolNames.set(block.id, block.name);
|
|
55
|
+
collector.track(block.name, input);
|
|
56
|
+
await cb.onProgress({ kind: 'tool-start', content: `${block.name}: ${summarizeToolInput(input)}` });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case 'user': {
|
|
62
|
+
const content = message.message.content;
|
|
63
|
+
if (!Array.isArray(content))
|
|
64
|
+
break;
|
|
65
|
+
for (const block of content) {
|
|
66
|
+
if (block.type === 'tool_result') {
|
|
67
|
+
const name = toolNames.get(block.tool_use_id) ?? '';
|
|
68
|
+
await cb.onProgress({ kind: 'tool-result', content: name, ok: !block.is_error });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case 'result': {
|
|
74
|
+
if (message.subtype === 'success')
|
|
75
|
+
finalText = message.result;
|
|
76
|
+
if ('session_id' in message && typeof message.session_id === 'string')
|
|
77
|
+
sessionId = message.session_id;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return { sessionId, finalText, producedFiles: collector.files(), turns };
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=claude-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-executor.js","sourceRoot":"","sources":["../../src/executor/claude-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type McpSdkServerConfigWithInstance, type SdkMcpToolDefinition } from '@anthropic-ai/claude-agent-sdk';
|
|
2
|
+
export declare const NOTIFY_SERVER_NAME = "lcb-notify";
|
|
3
|
+
/** canUseTool 收到的工具全名形态:mcp__lcb-notify__send_text */
|
|
4
|
+
export declare const NOTIFY_TOOL_PREFIX = "mcp__lcb-notify__";
|
|
5
|
+
/** 发送能力抽象:executeTask 闭包内绑定当前任务的 chatId 实现(含降级路径) */
|
|
6
|
+
export interface NotifySender {
|
|
7
|
+
sendText(markdown: string): Promise<void>;
|
|
8
|
+
sendImage(path: string, caption?: string): Promise<void>;
|
|
9
|
+
sendFile(path: string, note?: string): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 超长文本分块:按段落(\n\n+)贪心聚合,单段超长退化为按行聚合,行本身仍超长硬切兜底。
|
|
13
|
+
* 空白文本返回 [](调用方据此跳过发送)。块边界只可能落在段落/行/字符处,不打断 markdown 结构的概率最高。
|
|
14
|
+
*/
|
|
15
|
+
export declare function chunkText(text: string, maxChars?: number): string[];
|
|
16
|
+
/**
|
|
17
|
+
* 构建工具定义数组。handler 全部捕获异常返回 isError 中文原因,绝不抛出——
|
|
18
|
+
* 模型可感知失败并决定重试,不会炸掉整条 query 流。
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildNotifyTools(sender: NotifySender): SdkMcpToolDefinition<any>[];
|
|
21
|
+
/** 组装为进程内 server 配置:每任务一个实例,随 query 的 Options.mcpServers 注入 */
|
|
22
|
+
export declare function createNotifyServer(sender: NotifySender): McpSdkServerConfigWithInstance;
|
|
23
|
+
/**
|
|
24
|
+
* 由 gateway 发送能力 + chatId 组装 NotifySender(executeTask 每任务构造)。
|
|
25
|
+
* sendImageWithCaption 可选:缺省(旧 gateway / 测试 mock)时图片降级为
|
|
26
|
+
* 「caption 文本卡 + 图片消息」两条;sentPaths 记录已推送路径供任务收尾去重。
|
|
27
|
+
*/
|
|
28
|
+
export declare function createGatewaySender(args: {
|
|
29
|
+
chatId: string;
|
|
30
|
+
sentPaths: Set<string>;
|
|
31
|
+
sendText: (chatId: string, markdown: string) => Promise<unknown>;
|
|
32
|
+
sendImageWithCaption?: (chatId: string, path: string, caption?: string) => Promise<unknown>;
|
|
33
|
+
sendFileTo: (chatId: string, path: string) => Promise<unknown>;
|
|
34
|
+
}): NotifySender;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// 进程内通知服务:把「实时发给用户看」的能力以 MCP 工具形式注入 Claude 会话
|
|
2
|
+
// (lcb-notify 三件套:send_text / send_image / send_file)。由 executeTask 按任务
|
|
3
|
+
// 构造——chatId 在闭包内硬绑定,模型无法选择接收者,只能发到当前任务发起的聊天,
|
|
4
|
+
// 因此权限闸对该前缀直通(见 permission-gate.ts)。
|
|
5
|
+
import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
|
|
6
|
+
import { stat } from 'node:fs/promises';
|
|
7
|
+
import { basename, extname, resolve } from 'node:path';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
export const NOTIFY_SERVER_NAME = 'lcb-notify';
|
|
10
|
+
/** canUseTool 收到的工具全名形态:mcp__lcb-notify__send_text */
|
|
11
|
+
export const NOTIFY_TOOL_PREFIX = `mcp__${NOTIFY_SERVER_NAME}__`;
|
|
12
|
+
// 飞书 interactive 卡片 JSON 上限约 30KB,3000 个 CJK 字符 UTF-8 约 9KB,留足卡片包壳余量
|
|
13
|
+
const CHUNK_MAX_CHARS = 3000;
|
|
14
|
+
// 单块/单条失败重试一次的退避间隔(飞书偶发限流 / 网络抖动)
|
|
15
|
+
const RETRY_DELAY_MS = 800;
|
|
16
|
+
// 与 feishu-gateway 的 IMAGE_EXT 保持同一口径(此处独立声明,避免 executor 反向依赖 gateway)
|
|
17
|
+
const IMAGE_EXT = new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif']);
|
|
18
|
+
/**
|
|
19
|
+
* 超长文本分块:按段落(\n\n+)贪心聚合,单段超长退化为按行聚合,行本身仍超长硬切兜底。
|
|
20
|
+
* 空白文本返回 [](调用方据此跳过发送)。块边界只可能落在段落/行/字符处,不打断 markdown 结构的概率最高。
|
|
21
|
+
*/
|
|
22
|
+
export function chunkText(text, maxChars = CHUNK_MAX_CHARS) {
|
|
23
|
+
const src = text.trim();
|
|
24
|
+
if (!src)
|
|
25
|
+
return [];
|
|
26
|
+
const chunks = [];
|
|
27
|
+
let cur = '';
|
|
28
|
+
const flush = () => {
|
|
29
|
+
const t = cur.trim();
|
|
30
|
+
if (t)
|
|
31
|
+
chunks.push(t);
|
|
32
|
+
cur = '';
|
|
33
|
+
};
|
|
34
|
+
for (const para of src.split(/\n\n+/)) {
|
|
35
|
+
if (para.length > maxChars) {
|
|
36
|
+
// 超长段落:按行继续聚合(代码块/长表行常见此形态)
|
|
37
|
+
for (const line of para.split('\n')) {
|
|
38
|
+
if (line.length > maxChars) {
|
|
39
|
+
// 行本身超长(无换行的超长串):收尾当前块后按 maxChars 硬切
|
|
40
|
+
flush();
|
|
41
|
+
for (let i = 0; i < line.length; i += maxChars)
|
|
42
|
+
chunks.push(line.slice(i, i + maxChars));
|
|
43
|
+
}
|
|
44
|
+
else if (cur.length + line.length + 1 > maxChars) {
|
|
45
|
+
flush();
|
|
46
|
+
cur = line;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
cur = cur ? `${cur}\n${line}` : line;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (cur.length + para.length + 2 > maxChars) {
|
|
54
|
+
flush();
|
|
55
|
+
cur = para;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
cur = cur ? `${cur}\n\n${para}` : para;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
flush();
|
|
62
|
+
return chunks;
|
|
63
|
+
}
|
|
64
|
+
function sleep(ms) {
|
|
65
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
66
|
+
}
|
|
67
|
+
/** 带一次重试的发送:第二次失败向上抛,由工具 handler 捕获转为 isError 结果 */
|
|
68
|
+
async function sendWithRetry(fn) {
|
|
69
|
+
try {
|
|
70
|
+
await fn();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
await sleep(RETRY_DELAY_MS);
|
|
74
|
+
await fn();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function errText(e) {
|
|
78
|
+
return e instanceof Error ? e.message : String(e);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 构建工具定义数组。handler 全部捕获异常返回 isError 中文原因,绝不抛出——
|
|
82
|
+
* 模型可感知失败并决定重试,不会炸掉整条 query 流。
|
|
83
|
+
*/
|
|
84
|
+
export function buildNotifyTools(sender) {
|
|
85
|
+
const ok = (text) => ({ content: [{ type: 'text', text }] });
|
|
86
|
+
const fail = (text) => ({ content: [{ type: 'text', text }], isError: true });
|
|
87
|
+
// 用 SDK 的 tool() 辅助函数构建:handler 参数类型由 zod schema 推断,无需手写泛型
|
|
88
|
+
return [
|
|
89
|
+
tool('send_text', '向用户实时发送 markdown 文本(自动分块:超长内容拆成多张卡片,每块标注序号 i/N)。用于把生成的文案、说明、结论推给用户查阅。', {
|
|
90
|
+
text: z.string().min(1).describe('要发送的完整文本。必须传全文,不要截断或摘要'),
|
|
91
|
+
title: z.string().optional().describe('可选标题,显示在每块头部,如「文案初稿」「定稿全文」'),
|
|
92
|
+
}, async (args) => {
|
|
93
|
+
try {
|
|
94
|
+
const chunks = chunkText(args.text);
|
|
95
|
+
if (!chunks.length)
|
|
96
|
+
return fail('文本为空白,未发送');
|
|
97
|
+
const title = args.title ?? '消息';
|
|
98
|
+
const failures = [];
|
|
99
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
100
|
+
const payload = `**${title}(${i + 1}/${chunks.length})**\n\n${chunks[i]}`;
|
|
101
|
+
try {
|
|
102
|
+
await sendWithRetry(() => sender.sendText(payload));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
failures.push(i + 1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (failures.length) {
|
|
109
|
+
return fail(`已发送 ${chunks.length - failures.length}/${chunks.length} 块,第 ${failures.join('、')} 块失败(可重新发送失败部分)`);
|
|
110
|
+
}
|
|
111
|
+
return ok(`已发送 ${chunks.length} 块文本卡片(${title})`);
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
return fail(`文本发送失败:${errText(e)}`);
|
|
115
|
+
}
|
|
116
|
+
}),
|
|
117
|
+
tool('send_image', '把本地图片文件实时发送给用户,caption 显示在图片上方(如「【图 2/5】02-cover.png|质检 8.5/10」)。', {
|
|
118
|
+
path: z.string().describe('本地图片文件的绝对路径'),
|
|
119
|
+
caption: z.string().optional().describe('图片说明(编号/文件名/质检分等),展示在图片上方'),
|
|
120
|
+
}, async (args) => {
|
|
121
|
+
const p = resolve(args.path);
|
|
122
|
+
try {
|
|
123
|
+
const st = await stat(p).catch(() => null);
|
|
124
|
+
if (!st?.isFile())
|
|
125
|
+
return fail(`文件不存在:${p}`);
|
|
126
|
+
if (!IMAGE_EXT.has(extname(p).toLowerCase())) {
|
|
127
|
+
return fail(`不是图片文件(支持 ${[...IMAGE_EXT].join(' / ')}):${p}`);
|
|
128
|
+
}
|
|
129
|
+
await sendWithRetry(() => sender.sendImage(p, args.caption));
|
|
130
|
+
return ok(args.caption ? `图片已发送:${basename(p)}(${args.caption})` : `图片已发送:${basename(p)}`);
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
return fail(`图片发送失败:${basename(p)}(${errText(e)})`);
|
|
134
|
+
}
|
|
135
|
+
}),
|
|
136
|
+
tool('send_file', '把本地文件作为附件实时发送给用户(如 article.md / images-prompt.md),可附一条说明文字。', {
|
|
137
|
+
path: z.string().describe('本地文件的绝对路径'),
|
|
138
|
+
note: z.string().optional().describe('随文件发送的说明文字(飞书文件消息本身不带说明)'),
|
|
139
|
+
}, async (args) => {
|
|
140
|
+
const p = resolve(args.path);
|
|
141
|
+
try {
|
|
142
|
+
const st = await stat(p).catch(() => null);
|
|
143
|
+
if (!st?.isFile())
|
|
144
|
+
return fail(`文件不存在:${p}`);
|
|
145
|
+
// note 由 sender 实现负责随文件发出(飞书文件消息本身不带说明字段)
|
|
146
|
+
await sendWithRetry(() => sender.sendFile(p, args.note));
|
|
147
|
+
return ok(`文件已发送:${basename(p)}`);
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
return fail(`文件发送失败:${basename(p)}(${errText(e)})`);
|
|
151
|
+
}
|
|
152
|
+
}),
|
|
153
|
+
];
|
|
154
|
+
}
|
|
155
|
+
/** 组装为进程内 server 配置:每任务一个实例,随 query 的 Options.mcpServers 注入 */
|
|
156
|
+
export function createNotifyServer(sender) {
|
|
157
|
+
return createSdkMcpServer({
|
|
158
|
+
name: NOTIFY_SERVER_NAME,
|
|
159
|
+
version: '0.1.0',
|
|
160
|
+
instructions: '内容生产实时推送工具:把文案 / 图片 / 文件即时发给当前对话的用户查阅。发送失败会返回错误信息,可重试;工具仅在飞书桥接环境存在。',
|
|
161
|
+
tools: buildNotifyTools(sender),
|
|
162
|
+
// 工具始终进模型上下文(不被 tool search 延迟加载)——skill / agent 提示词直接引用工具名,必须首轮可见
|
|
163
|
+
alwaysLoad: true,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* 由 gateway 发送能力 + chatId 组装 NotifySender(executeTask 每任务构造)。
|
|
168
|
+
* sendImageWithCaption 可选:缺省(旧 gateway / 测试 mock)时图片降级为
|
|
169
|
+
* 「caption 文本卡 + 图片消息」两条;sentPaths 记录已推送路径供任务收尾去重。
|
|
170
|
+
*/
|
|
171
|
+
export function createGatewaySender(args) {
|
|
172
|
+
const { chatId, sentPaths } = args;
|
|
173
|
+
return {
|
|
174
|
+
sendText: async (md) => { await args.sendText(chatId, md); },
|
|
175
|
+
sendImage: async (p, caption) => {
|
|
176
|
+
sentPaths.add(resolve(p));
|
|
177
|
+
if (args.sendImageWithCaption) {
|
|
178
|
+
await args.sendImageWithCaption(chatId, p, caption);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (caption)
|
|
182
|
+
await args.sendText(chatId, caption);
|
|
183
|
+
await args.sendFileTo(chatId, p);
|
|
184
|
+
},
|
|
185
|
+
sendFile: async (p, note) => {
|
|
186
|
+
sentPaths.add(resolve(p));
|
|
187
|
+
if (note)
|
|
188
|
+
await args.sendText(chatId, note);
|
|
189
|
+
await args.sendFileTo(chatId, p);
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=notify-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify-server.js","sourceRoot":"","sources":["../../src/executor/notify-server.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,wEAAwE;AACxE,8CAA8C;AAC9C,qCAAqC;AACrC,OAAO,EAAE,kBAAkB,EAAE,IAAI,EAAkE,MAAM,gCAAgC,CAAC;AAC1I,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,kBAAkB,GAAG,YAAY,CAAC;AAC/C,sDAAsD;AACtD,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,kBAAkB,IAAI,CAAC;AASjE,qEAAqE;AACrE,MAAM,eAAe,GAAG,IAAI,CAAC;AAC7B,kCAAkC;AAClC,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,uEAAuE;AACvE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAEtE;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,QAAQ,GAAG,eAAe;IAChE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACxB,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,GAAG,GAAG,EAAE,CAAC;IACX,CAAC,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,IAAI,IAAI,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;YAC3B,4BAA4B;YAC5B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpC,IAAI,IAAI,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;oBAC3B,qCAAqC;oBACrC,KAAK,EAAE,CAAC;oBACR,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,QAAQ;wBAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;gBAC3F,CAAC;qBAAM,IAAI,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,QAAQ,EAAE,CAAC;oBACnD,KAAK,EAAE,CAAC;oBACR,GAAG,GAAG,IAAI,CAAC;gBACb,CAAC;qBAAM,CAAC;oBACN,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;gBACvC,CAAC;YACH,CAAC;QACH,CAAC;aAAM,IAAI,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,QAAQ,EAAE,CAAC;YACnD,KAAK,EAAE,CAAC;YACR,GAAG,GAAG,IAAI,CAAC;QACb,CAAC;aAAM,CAAC;YACN,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACzC,CAAC;IACH,CAAC;IACD,KAAK,EAAE,CAAC;IACR,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,oDAAoD;AACpD,KAAK,UAAU,aAAa,CAAC,EAAuB;IAClD,IAAI,CAAC;QACH,MAAM,EAAE,EAAE,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,KAAK,CAAC,cAAc,CAAC,CAAC;QAC5B,MAAM,EAAE,EAAE,CAAC;IACb,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,CAAU;IACzB,OAAO,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAoB;IACnD,MAAM,EAAE,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/F,2DAA2D;IAC3D,OAAO;QACL,IAAI,CAAC,WAAW,EAAE,uEAAuE,EAAE;YACzF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,wBAAwB,CAAC;YAC1D,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;SACpE,EAAE,KAAK,EAAE,IAAsC,EAAE,EAAE;YAClD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpC,IAAI,CAAC,MAAM,CAAC,MAAM;oBAAE,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC;gBAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC;gBACjC,MAAM,QAAQ,GAAa,EAAE,CAAC;gBAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACvC,MAAM,OAAO,GAAG,KAAK,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,UAAU,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC1E,IAAI,CAAC;wBACH,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;oBACtD,CAAC;oBAAC,MAAM,CAAC;wBACP,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;oBACvB,CAAC;gBACH,CAAC;gBACD,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;oBACpB,OAAO,IAAI,CAAC,OAAO,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,QAAQ,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;gBAClH,CAAC;gBACD,OAAO,EAAE,CAAC,OAAO,MAAM,CAAC,MAAM,UAAU,KAAK,GAAG,CAAC,CAAC;YACpD,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,IAAI,CAAC,UAAU,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACtC,CAAC;QACH,CAAC,CAAC;QACF,IAAI,CAAC,YAAY,EAAE,mEAAmE,EAAE;YACtF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;YACxC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;SACrE,EAAE,KAAK,EAAE,IAAwC,EAAE,EAAE;YACpD,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;gBAC3C,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE;oBAAE,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBAC7C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;oBAC7C,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAC/D,CAAC;gBACD,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;gBAC7D,OAAO,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC7F,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,IAAI,CAAC,UAAU,QAAQ,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACtD,CAAC;QACH,CAAC,CAAC;QACF,IAAI,CAAC,WAAW,EAAE,6DAA6D,EAAE;YAC/E,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;YACtC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;SACjE,EAAE,KAAK,EAAE,IAAqC,EAAE,EAAE;YACjD,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;gBAC3C,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE;oBAAE,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBAC7C,0CAA0C;gBAC1C,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBACzD,OAAO,EAAE,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACpC,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,IAAI,CAAC,UAAU,QAAQ,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACtD,CAAC;QACH,CAAC,CAAC;KACH,CAAC;AACJ,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,kBAAkB,CAAC,MAAoB;IACrD,OAAO,kBAAkB,CAAC;QACxB,IAAI,EAAE,kBAAkB;QACxB,OAAO,EAAE,OAAO;QAChB,YAAY,EAAE,qEAAqE;QACnF,KAAK,EAAE,gBAAgB,CAAC,MAAM,CAAC;QAC/B,mEAAmE;QACnE,UAAU,EAAE,IAAI;KACjB,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAMnC;IACC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IACnC,OAAO;QACL,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5D,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE;YAC9B,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;gBAC9B,MAAM,IAAI,CAAC,oBAAoB,CAAC,MAAM,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;gBACpD,OAAO;YACT,CAAC;YACD,IAAI,OAAO;gBAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAClD,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACnC,CAAC;QACD,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;YAC1B,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,IAAI;gBAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC5C,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACnC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// 产出收集器:跟踪任务过程中写文件工具产出的文件路径(去重)
|
|
2
|
+
const WRITE_TOOLS = new Set(['Write', 'Edit', 'NotebookEdit']);
|
|
3
|
+
export class OutputCollector {
|
|
4
|
+
seen = new Set();
|
|
5
|
+
track(toolName, input) {
|
|
6
|
+
if (!WRITE_TOOLS.has(toolName))
|
|
7
|
+
return;
|
|
8
|
+
// NotebookEdit 用 notebook_path,Write/Edit 用 file_path
|
|
9
|
+
const p = input.file_path ?? input.notebook_path;
|
|
10
|
+
if (typeof p === 'string' && p)
|
|
11
|
+
this.seen.add(p);
|
|
12
|
+
}
|
|
13
|
+
files() {
|
|
14
|
+
return [...this.seen];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=output-collector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-collector.js","sourceRoot":"","sources":["../../src/executor/output-collector.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;AAE/D,MAAM,OAAO,eAAe;IAClB,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAEjC,KAAK,CAAC,QAAgB,EAAE,KAA8B;QACpD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO;QACvC,sDAAsD;QACtD,MAAM,CAAC,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,aAAa,CAAC;QACjD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACnD,CAAC;IAED,KAAK;QACH,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;CACF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ConfirmationRequest, PermissionDecision } from '../types.js';
|
|
2
|
+
export declare const READ_ONLY_TOOLS: ReadonlySet<string>;
|
|
3
|
+
export type GateDecision = {
|
|
4
|
+
behavior: 'allow';
|
|
5
|
+
message?: string;
|
|
6
|
+
} | {
|
|
7
|
+
behavior: 'deny';
|
|
8
|
+
message: string;
|
|
9
|
+
};
|
|
10
|
+
export declare class PermissionGate {
|
|
11
|
+
private opts;
|
|
12
|
+
private remembered;
|
|
13
|
+
constructor(opts: {
|
|
14
|
+
ask: (req: ConfirmationRequest) => Promise<PermissionDecision>;
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
});
|
|
17
|
+
decide(toolName: string, input: Record<string, unknown>, workspaceName: string): Promise<GateDecision>;
|
|
18
|
+
rememberSession(toolName: string): void;
|
|
19
|
+
reset(): void;
|
|
20
|
+
}
|