@24klynx/tools 0.1.2 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.d.mts +84 -14
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +528 -116
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { ToolError } from "@24klynx/core";
|
|
2
|
-
import { mkdir, readFile, readdir, stat
|
|
1
|
+
import { NotImplementedError, ToolError } from "@24klynx/core";
|
|
2
|
+
import { mkdir, readFile, readdir, stat } from "node:fs/promises";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, writeFileSync } from "node:fs";
|
|
3
4
|
import { dirname, join, relative, resolve } from "node:path";
|
|
4
5
|
import { exec, execFile, spawn } from "node:child_process";
|
|
5
6
|
import { promisify } from "node:util";
|
|
6
|
-
import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { randomUUID } from "node:crypto";
|
|
8
|
-
import { homedir } from "node:os";
|
|
8
|
+
import { homedir, tmpdir } from "node:os";
|
|
9
9
|
//#region src/availability.ts
|
|
10
10
|
function evalAlways() {
|
|
11
11
|
return { available: true };
|
|
@@ -166,7 +166,7 @@ function createToolRegistry() {
|
|
|
166
166
|
*
|
|
167
167
|
* Limits: 2000 lines per call, binary files return MIME + base64.
|
|
168
168
|
*/
|
|
169
|
-
const descriptor$
|
|
169
|
+
const descriptor$38 = {
|
|
170
170
|
name: "read_file",
|
|
171
171
|
description: "读取文件内容。支持 offset/limit 分页。二进制和图片文件以 base64 编码数据加 MIME 类型返回。",
|
|
172
172
|
inputSchema: {
|
|
@@ -233,13 +233,23 @@ const TEXT_EXTENSIONS = new Set([
|
|
|
233
233
|
".proto",
|
|
234
234
|
".vue",
|
|
235
235
|
".svelte",
|
|
236
|
-
".astro"
|
|
236
|
+
".astro",
|
|
237
|
+
""
|
|
238
|
+
]);
|
|
239
|
+
/** Common files without extensions that should be treated as text. */
|
|
240
|
+
const TEXT_FILENAMES = new Set([
|
|
241
|
+
"dockerfile",
|
|
242
|
+
"makefile",
|
|
243
|
+
"license",
|
|
244
|
+
"changelog"
|
|
237
245
|
]);
|
|
238
246
|
function isTextExtension(filePath) {
|
|
239
247
|
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
240
|
-
|
|
248
|
+
if (TEXT_EXTENSIONS.has(ext)) return true;
|
|
249
|
+
const basename = filePath.slice(filePath.lastIndexOf("/") + 1).toLowerCase();
|
|
250
|
+
return TEXT_FILENAMES.has(basename);
|
|
241
251
|
}
|
|
242
|
-
const handler$
|
|
252
|
+
const handler$31 = { async handle(invocation, signal) {
|
|
243
253
|
const { file_path, offset, limit } = invocation.payload;
|
|
244
254
|
const startTime = Date.now();
|
|
245
255
|
if (signal.aborted) return {
|
|
@@ -337,7 +347,7 @@ function guessMime(filePath) {
|
|
|
337
347
|
* Creates parent directories automatically. Overwrites existing files
|
|
338
348
|
* without asking (permission pipeline handles safety).
|
|
339
349
|
*/
|
|
340
|
-
const descriptor$
|
|
350
|
+
const descriptor$37 = {
|
|
341
351
|
name: "write_file",
|
|
342
352
|
description: "创建新文件或完全覆盖已有文件。父目录不存在时会自动创建。",
|
|
343
353
|
inputSchema: {
|
|
@@ -360,7 +370,7 @@ const descriptor$32 = {
|
|
|
360
370
|
executor: "core:write_file",
|
|
361
371
|
owner: "core"
|
|
362
372
|
};
|
|
363
|
-
const handler$
|
|
373
|
+
const handler$30 = { async handle(invocation, signal) {
|
|
364
374
|
const { file_path, content } = invocation.payload;
|
|
365
375
|
const startTime = Date.now();
|
|
366
376
|
if (signal.aborted) return {
|
|
@@ -370,7 +380,9 @@ const handler$29 = { async handle(invocation, signal) {
|
|
|
370
380
|
};
|
|
371
381
|
try {
|
|
372
382
|
await mkdir(dirname(file_path), { recursive: true });
|
|
373
|
-
|
|
383
|
+
const tmpPath = file_path + ".tmp";
|
|
384
|
+
writeFileSync(tmpPath, content, "utf-8");
|
|
385
|
+
renameSync(tmpPath, file_path);
|
|
374
386
|
return {
|
|
375
387
|
success: true,
|
|
376
388
|
content: `已写入 ${content.length} 字节到 ${file_path}`,
|
|
@@ -393,7 +405,7 @@ const handler$29 = { async handle(invocation, signal) {
|
|
|
393
405
|
* This is the primary editing tool — LLMs should use it instead of write_file
|
|
394
406
|
* for targeted changes.
|
|
395
407
|
*/
|
|
396
|
-
const descriptor$
|
|
408
|
+
const descriptor$36 = {
|
|
397
409
|
name: "edit_file",
|
|
398
410
|
description: "在文件中执行精确字符串替换。old_string 必须在文件中精确匹配(包括空格和缩进),除非设置 replace_all 为 true。",
|
|
399
411
|
inputSchema: {
|
|
@@ -428,7 +440,7 @@ const descriptor$31 = {
|
|
|
428
440
|
executor: "core:edit_file",
|
|
429
441
|
owner: "core"
|
|
430
442
|
};
|
|
431
|
-
const handler$
|
|
443
|
+
const handler$29 = { async handle(invocation, signal) {
|
|
432
444
|
const { file_path, old_string, new_string, replace_all } = invocation.payload;
|
|
433
445
|
const startTime = Date.now();
|
|
434
446
|
if (signal.aborted) return {
|
|
@@ -454,7 +466,10 @@ const handler$28 = { async handle(invocation, signal) {
|
|
|
454
466
|
content: `old_string 在 ${file_path} 中出现了 ${count} 次。请设置 replace_all: true 替换所有匹配项,或提供更多上下文使匹配唯一。`,
|
|
455
467
|
metadata: { durationMs: Date.now() - startTime }
|
|
456
468
|
};
|
|
457
|
-
|
|
469
|
+
const updated = replace_all ? original.replaceAll(old_string, new_string) : original.replace(old_string, new_string);
|
|
470
|
+
const tmpPath = file_path + ".tmp";
|
|
471
|
+
writeFileSync(tmpPath, updated, "utf-8");
|
|
472
|
+
renameSync(tmpPath, file_path);
|
|
458
473
|
return {
|
|
459
474
|
success: true,
|
|
460
475
|
content: `已在 ${file_path} 中替换 ${replace_all ? count : 1} 处`,
|
|
@@ -491,7 +506,7 @@ function countOccurrences$1(haystack, needle) {
|
|
|
491
506
|
*
|
|
492
507
|
* Returns sorted absolute paths. Default excludes: node_modules, .git, dist, build.
|
|
493
508
|
*/
|
|
494
|
-
const descriptor$
|
|
509
|
+
const descriptor$35 = {
|
|
495
510
|
name: "glob",
|
|
496
511
|
description: "用 glob 模式匹配文件。返回排序后的绝对路径。支持 ** 递归匹配、* 通配符。",
|
|
497
512
|
inputSchema: {
|
|
@@ -526,7 +541,7 @@ const DEFAULT_EXCLUDE = new Set([
|
|
|
526
541
|
"venv"
|
|
527
542
|
]);
|
|
528
543
|
const MAX_RESULTS = 1e4;
|
|
529
|
-
const handler$
|
|
544
|
+
const handler$28 = { async handle(invocation, signal) {
|
|
530
545
|
const { pattern, path: rootPath } = invocation.payload;
|
|
531
546
|
const startTime = Date.now();
|
|
532
547
|
const base = rootPath ? resolve(rootPath) : process.cwd();
|
|
@@ -620,13 +635,13 @@ function matchSegment(name, seg) {
|
|
|
620
635
|
//#endregion
|
|
621
636
|
//#region src/builtin/files/grep.ts
|
|
622
637
|
/**
|
|
623
|
-
* grep —
|
|
638
|
+
* grep — 基于 ripgrep 的正则内容搜索工具。
|
|
624
639
|
*
|
|
625
|
-
*
|
|
626
|
-
*
|
|
640
|
+
* ripgrep (rg) 为必需依赖。未安装时给出各平台安装命令。
|
|
641
|
+
* 输出有默认行数(250)和字节数(50KB)限制,防止 LLM 上下文溢出。
|
|
627
642
|
*/
|
|
628
643
|
promisify(execFile);
|
|
629
|
-
const descriptor$
|
|
644
|
+
const descriptor$34 = {
|
|
630
645
|
name: "grep",
|
|
631
646
|
description: "使用正则表达式搜索文件内容。返回匹配行及可选的上下文。优先使用 ripgrep。",
|
|
632
647
|
inputSchema: {
|
|
@@ -750,7 +765,7 @@ function buildGrepResult(collected, limit, byteTruncated, startTime) {
|
|
|
750
765
|
/**
|
|
751
766
|
* Classify an error thrown during ripgrep execution and return the
|
|
752
767
|
* appropriate {@link ToolResult}, or `null` if the caller should
|
|
753
|
-
* fall through to the
|
|
768
|
+
* fall through to return the "rg not installed" error.
|
|
754
769
|
*/
|
|
755
770
|
function classifyRgError(err, signal, startTime) {
|
|
756
771
|
if (err.code === "ENOENT") return null;
|
|
@@ -776,7 +791,7 @@ function classifyRgError(err, signal, startTime) {
|
|
|
776
791
|
metadata: { durationMs: Date.now() - startTime }
|
|
777
792
|
};
|
|
778
793
|
}
|
|
779
|
-
const handler$
|
|
794
|
+
const handler$27 = { async handle(invocation, signal) {
|
|
780
795
|
const { pattern, path, glob, output_mode, "-n": showLineNum, "-i": caseInsensitive, "-C": context, head_limit } = invocation.payload;
|
|
781
796
|
const startTime = Date.now();
|
|
782
797
|
if (signal.aborted) return {
|
|
@@ -807,7 +822,7 @@ const handler$26 = { async handle(invocation, signal) {
|
|
|
807
822
|
}
|
|
808
823
|
return {
|
|
809
824
|
success: false,
|
|
810
|
-
content: "ripgrep (rg)
|
|
825
|
+
content: "ripgrep (rg) 未安装,grep 功能依赖 ripgrep。安装方式:Windows: choco install ripgrep 或 winget install BurntSushi.ripgrep.MSVC、macOS: brew install ripgrep、Linux: apt install ripgrep 或 dnf install ripgrep。详见: https://github.com/BurntSushi/ripgrep",
|
|
811
826
|
metadata: { durationMs: Date.now() - startTime }
|
|
812
827
|
};
|
|
813
828
|
} };
|
|
@@ -954,7 +969,7 @@ function isUrlAllowed(url) {
|
|
|
954
969
|
reason: `域名 "${host}" 不在允许列表中。如需访问请联系用户确认。`
|
|
955
970
|
};
|
|
956
971
|
}
|
|
957
|
-
const descriptor$
|
|
972
|
+
const descriptor$33 = {
|
|
958
973
|
name: "web_fetch",
|
|
959
974
|
description: "从 URL 获取内容并以文本形式返回。HTTP 自动升级为 HTTPS。跨域重定向会返回给调用方而非自动跟随。每个 URL 的结果缓存 15 分钟。HTML 内容会转换为纯文本。",
|
|
960
975
|
inputSchema: {
|
|
@@ -1089,7 +1104,7 @@ function buildFetchError(err, startTime) {
|
|
|
1089
1104
|
metadata: { durationMs: Date.now() - startTime }
|
|
1090
1105
|
};
|
|
1091
1106
|
}
|
|
1092
|
-
const handler$
|
|
1107
|
+
const handler$26 = { async handle(invocation, signal) {
|
|
1093
1108
|
const { url, maxChars } = invocation.payload;
|
|
1094
1109
|
const charLimit = maxChars ?? 5e4;
|
|
1095
1110
|
const startTime = Date.now();
|
|
@@ -1162,7 +1177,7 @@ const handler$25 = { async handle(invocation, signal) {
|
|
|
1162
1177
|
} };
|
|
1163
1178
|
//#endregion
|
|
1164
1179
|
//#region src/builtin/files/web-search.ts
|
|
1165
|
-
const descriptor$
|
|
1180
|
+
const descriptor$32 = {
|
|
1166
1181
|
name: "web_search",
|
|
1167
1182
|
description: "搜索网页并返回包含标题、URL 和摘要的结果。支持域名白名单和黑名单过滤。",
|
|
1168
1183
|
inputSchema: {
|
|
@@ -1286,7 +1301,7 @@ function cacheKey(query, allowed, blocked) {
|
|
|
1286
1301
|
if (blocked?.length) parts.push("block:" + blocked.sort().join(","));
|
|
1287
1302
|
return parts.join("|");
|
|
1288
1303
|
}
|
|
1289
|
-
const handler$
|
|
1304
|
+
const handler$25 = { async handle(invocation, signal) {
|
|
1290
1305
|
const { query, allowed_domains, blocked_domains } = invocation.payload;
|
|
1291
1306
|
const startTime = Date.now();
|
|
1292
1307
|
if (signal.aborted) return {
|
|
@@ -1348,7 +1363,7 @@ const handler$24 = { async handle(invocation, signal) {
|
|
|
1348
1363
|
} };
|
|
1349
1364
|
//#endregion
|
|
1350
1365
|
//#region src/builtin/files/todo-write.ts
|
|
1351
|
-
const descriptor$
|
|
1366
|
+
const descriptor$31 = {
|
|
1352
1367
|
name: "todo_write",
|
|
1353
1368
|
description: "创建和更新任务列表以跟踪进度。每次调用会替换整个列表。每项任务必须包含 content、status 和 activeForm。",
|
|
1354
1369
|
inputSchema: {
|
|
@@ -1392,7 +1407,7 @@ const descriptor$26 = {
|
|
|
1392
1407
|
executor: "core:todo_write",
|
|
1393
1408
|
owner: "core"
|
|
1394
1409
|
};
|
|
1395
|
-
const handler$
|
|
1410
|
+
const handler$24 = { async handle(invocation, _signal) {
|
|
1396
1411
|
const { todos } = invocation.payload;
|
|
1397
1412
|
const startTime = Date.now();
|
|
1398
1413
|
if (!Array.isArray(todos)) return {
|
|
@@ -1432,6 +1447,117 @@ const handler$23 = { async handle(invocation, _signal) {
|
|
|
1432
1447
|
};
|
|
1433
1448
|
} };
|
|
1434
1449
|
//#endregion
|
|
1450
|
+
//#region src/builtin/files/bash.ts
|
|
1451
|
+
/**
|
|
1452
|
+
* bash — 跨平台执行 Shell 命令。
|
|
1453
|
+
*
|
|
1454
|
+
* Unix (Linux/macOS) 上使用 `bash -c` 执行,
|
|
1455
|
+
* Windows 上使用 `cmd /c` 执行(PowerShell 由独立的 powershell 工具处理)。
|
|
1456
|
+
* 默认 30 秒超时,stdout/stderr 各截断至 10KB。
|
|
1457
|
+
*/
|
|
1458
|
+
/** stdout/stderr 最大输出长度(字节),超出部分截断并标记。 */
|
|
1459
|
+
const MAX_OUTPUT_LENGTH = 1e4;
|
|
1460
|
+
const descriptor$30 = {
|
|
1461
|
+
name: "bash",
|
|
1462
|
+
description: "执行 Shell 命令。在 Unix (Linux/macOS) 上使用 bash -c,在 Windows 上使用 cmd /c。返回 stdout 和 stderr。默认 30 秒超时,输出超出 10KB 会被截断。",
|
|
1463
|
+
inputSchema: {
|
|
1464
|
+
type: "object",
|
|
1465
|
+
properties: {
|
|
1466
|
+
command: {
|
|
1467
|
+
type: "string",
|
|
1468
|
+
description: "要执行的 Shell 命令"
|
|
1469
|
+
},
|
|
1470
|
+
timeout: {
|
|
1471
|
+
type: "number",
|
|
1472
|
+
description: "超时时间(毫秒),默认 30000",
|
|
1473
|
+
default: 3e4
|
|
1474
|
+
}
|
|
1475
|
+
},
|
|
1476
|
+
required: ["command"]
|
|
1477
|
+
},
|
|
1478
|
+
kind: "ExecutesCode",
|
|
1479
|
+
safety: "RequiresApproval",
|
|
1480
|
+
availability: { type: "always" },
|
|
1481
|
+
executor: "core:bash",
|
|
1482
|
+
owner: "core"
|
|
1483
|
+
};
|
|
1484
|
+
const handler$23 = { async handle(invocation, signal) {
|
|
1485
|
+
const { command, timeout } = invocation.payload;
|
|
1486
|
+
const startTime = Date.now();
|
|
1487
|
+
if (signal.aborted) return {
|
|
1488
|
+
success: false,
|
|
1489
|
+
content: "操作已取消",
|
|
1490
|
+
metadata: { durationMs: Date.now() - startTime }
|
|
1491
|
+
};
|
|
1492
|
+
if (!command || command.trim().length === 0) return {
|
|
1493
|
+
success: false,
|
|
1494
|
+
content: "请输入要执行的命令。",
|
|
1495
|
+
metadata: { durationMs: Date.now() - startTime }
|
|
1496
|
+
};
|
|
1497
|
+
const timeoutMs = timeout ?? 3e4;
|
|
1498
|
+
const shellCmd = buildShellCommand(command);
|
|
1499
|
+
return new Promise((resolve) => {
|
|
1500
|
+
let aborted = false;
|
|
1501
|
+
const onAbort = () => {
|
|
1502
|
+
aborted = true;
|
|
1503
|
+
};
|
|
1504
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1505
|
+
exec(shellCmd, {
|
|
1506
|
+
timeout: timeoutMs,
|
|
1507
|
+
windowsHide: true
|
|
1508
|
+
}, (err, stdout, stderr) => {
|
|
1509
|
+
signal.removeEventListener("abort", onAbort);
|
|
1510
|
+
if (aborted) {
|
|
1511
|
+
resolve({
|
|
1512
|
+
success: false,
|
|
1513
|
+
content: "操作已取消",
|
|
1514
|
+
metadata: { durationMs: Date.now() - startTime }
|
|
1515
|
+
});
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
if (err) {
|
|
1519
|
+
resolve({
|
|
1520
|
+
success: false,
|
|
1521
|
+
content: err.code === "ETIMEDOUT" || err.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" ? `命令执行超时(${timeoutMs}ms)` : `命令执行失败 (exit ${err.code ?? "unknown"}): ${err.message}\n\nstderr:\n${stderr}`,
|
|
1522
|
+
metadata: { durationMs: Date.now() - startTime }
|
|
1523
|
+
});
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
const truncatedStdout = truncate(stdout);
|
|
1527
|
+
const truncatedStderr = truncate(stderr);
|
|
1528
|
+
const parts = [];
|
|
1529
|
+
if (truncatedStdout.text) parts.push(`stdout:\n${truncatedStdout.text}`);
|
|
1530
|
+
if (truncatedStderr.text) parts.push(`stderr:\n${truncatedStderr.text}`);
|
|
1531
|
+
const output = parts.join("\n\n") || "(无输出)";
|
|
1532
|
+
const wasTruncated = truncatedStdout.truncated || truncatedStderr.truncated;
|
|
1533
|
+
resolve({
|
|
1534
|
+
success: true,
|
|
1535
|
+
content: output,
|
|
1536
|
+
metadata: {
|
|
1537
|
+
durationMs: Date.now() - startTime,
|
|
1538
|
+
truncated: wasTruncated
|
|
1539
|
+
}
|
|
1540
|
+
});
|
|
1541
|
+
});
|
|
1542
|
+
});
|
|
1543
|
+
} };
|
|
1544
|
+
/** 根据平台构建 Shell 命令字符串。 */
|
|
1545
|
+
function buildShellCommand(command) {
|
|
1546
|
+
if (process.platform === "win32") return `cmd /c "${command.replace(/"/g, "\\\"")}"`;
|
|
1547
|
+
return `bash -c '${command.replace(/'/g, `'\\''`)}'`;
|
|
1548
|
+
}
|
|
1549
|
+
/** 截断超长输出,末尾追加截断提示。 */
|
|
1550
|
+
function truncate(raw) {
|
|
1551
|
+
if (raw.length <= MAX_OUTPUT_LENGTH) return {
|
|
1552
|
+
text: raw,
|
|
1553
|
+
truncated: false
|
|
1554
|
+
};
|
|
1555
|
+
return {
|
|
1556
|
+
text: raw.slice(0, MAX_OUTPUT_LENGTH) + `\n...(截断,原 ${raw.length} 字节,仅显示前 ${MAX_OUTPUT_LENGTH} 字节)`,
|
|
1557
|
+
truncated: true
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
//#endregion
|
|
1435
1561
|
//#region src/builtin/files/powershell.ts
|
|
1436
1562
|
/**
|
|
1437
1563
|
* powershell — 在 Windows 上执行 PowerShell 命令。
|
|
@@ -1439,7 +1565,7 @@ const handler$23 = { async handle(invocation, _signal) {
|
|
|
1439
1565
|
* 仅在 Windows 平台可用。使用 child_process.exec 执行,
|
|
1440
1566
|
* 默认 30 秒超时。返回 stdout 和 stderr。
|
|
1441
1567
|
*/
|
|
1442
|
-
const descriptor$
|
|
1568
|
+
const descriptor$29 = {
|
|
1443
1569
|
name: "powershell",
|
|
1444
1570
|
description: "在 Windows 上执行 PowerShell 命令。返回 stdout 和 stderr。仅 Windows 平台可用。命令以 -NoProfile 模式运行。",
|
|
1445
1571
|
inputSchema: {
|
|
@@ -1484,6 +1610,16 @@ const handler$22 = { async handle(invocation, signal) {
|
|
|
1484
1610
|
content: "请输入要执行的 PowerShell 命令。",
|
|
1485
1611
|
metadata: { durationMs: Date.now() - startTime }
|
|
1486
1612
|
};
|
|
1613
|
+
if (/\$\(/.test(command)) return {
|
|
1614
|
+
success: false,
|
|
1615
|
+
content: "命令包含不安全的子表达式 `$(`。请使用安全的替代方式。",
|
|
1616
|
+
metadata: { durationMs: Date.now() - startTime }
|
|
1617
|
+
};
|
|
1618
|
+
if (/`/.test(command)) return {
|
|
1619
|
+
success: false,
|
|
1620
|
+
content: "命令包含不安全的转义符 `` ` ``。请使用安全的替代方式。",
|
|
1621
|
+
metadata: { durationMs: Date.now() - startTime }
|
|
1622
|
+
};
|
|
1487
1623
|
const timeoutMs = timeout ?? 3e4;
|
|
1488
1624
|
const escapedCommand = command.replace(/"/g, "\\\"");
|
|
1489
1625
|
return new Promise((resolve) => {
|
|
@@ -1535,7 +1671,7 @@ const handler$22 = { async handle(invocation, signal) {
|
|
|
1535
1671
|
* .ipynb 文件结构为 JSON: { cells: [{ cell_type, source, ... }], ... }
|
|
1536
1672
|
* source 字段可以是字符串或字符串数组,统一按字符串数组处理。
|
|
1537
1673
|
*/
|
|
1538
|
-
const descriptor$
|
|
1674
|
+
const descriptor$28 = {
|
|
1539
1675
|
name: "NotebookEditTool",
|
|
1540
1676
|
description: "编辑 Jupyter notebook (.ipynb) 文件中的单元格。支持插入、替换、删除和列出代码与 Markdown 单元格。",
|
|
1541
1677
|
inputSchema: {
|
|
@@ -1740,7 +1876,7 @@ const handler$21 = { async handle(invocation, signal) {
|
|
|
1740
1876
|
} };
|
|
1741
1877
|
//#endregion
|
|
1742
1878
|
//#region src/builtin/files/secret-scan.ts
|
|
1743
|
-
const descriptor$
|
|
1879
|
+
const descriptor$27 = {
|
|
1744
1880
|
name: "SecretScanner",
|
|
1745
1881
|
description: "扫描文件内容以查找潜在的密钥、令牌和凭证。在写入操作前使用此工具检查敏感信息。检测模式包括:私钥、GitHub token、OpenAI key、Google API key、JWT、通用 API key、密码赋值。",
|
|
1746
1882
|
inputSchema: {
|
|
@@ -1862,7 +1998,7 @@ const handler$20 = { async handle(invocation, signal) {
|
|
|
1862
1998
|
* 此工具默认隐藏(需设置 flag "lsp_enabled" 才会暴露)。
|
|
1863
1999
|
* 当 IDE 连接后将提供完整的 LSP 集成,目前提供接口契约层。
|
|
1864
2000
|
*/
|
|
1865
|
-
const descriptor$
|
|
2001
|
+
const descriptor$26 = {
|
|
1866
2002
|
name: "LSPTool",
|
|
1867
2003
|
description: "查询语言服务器协议(LSP)以获取诊断、类型信息、跳转定义等。支持 diagnostics、hover、definition、references、format 等常见 LSP 操作。",
|
|
1868
2004
|
inputSchema: {
|
|
@@ -2020,7 +2156,7 @@ function countOccurrences(str, char) {
|
|
|
2020
2156
|
*/
|
|
2021
2157
|
/** 活跃的 REPL 会话映射,key 为 session UUID。 */
|
|
2022
2158
|
const activeSessions = /* @__PURE__ */ new Map();
|
|
2023
|
-
const descriptor$
|
|
2159
|
+
const descriptor$25 = {
|
|
2024
2160
|
name: "REPLTool",
|
|
2025
2161
|
description: "在持久化 REPL 会话中执行代码。启动运行时(Node.js/Python/Bash)并保持状态在多次调用之间。支持 start(启动)、eval(执行)、stop(停止)、status(状态)四种操作。",
|
|
2026
2162
|
inputSchema: {
|
|
@@ -2243,7 +2379,7 @@ function handleStatus$1() {
|
|
|
2243
2379
|
}
|
|
2244
2380
|
//#endregion
|
|
2245
2381
|
//#region src/builtin/agent/agent.ts
|
|
2246
|
-
const descriptor$
|
|
2382
|
+
const descriptor$24 = {
|
|
2247
2383
|
name: "agent",
|
|
2248
2384
|
description: "启动子 Agent 自主处理复杂多步骤任务。每种子 Agent 类型拥有不同能力和可用工具。子 Agent 将最终结果返回给父 Agent。",
|
|
2249
2385
|
inputSchema: {
|
|
@@ -2314,7 +2450,7 @@ const handler$17 = { async handle(invocation, signal) {
|
|
|
2314
2450
|
} };
|
|
2315
2451
|
//#endregion
|
|
2316
2452
|
//#region src/builtin/agent/task.ts
|
|
2317
|
-
const descriptor$
|
|
2453
|
+
const descriptor$23 = {
|
|
2318
2454
|
name: "task",
|
|
2319
2455
|
description: "管理后台任务——创建、列表、获取、更新或停止任务。任务持久化到 SQLite,会话重启后仍存在。适用于跨多个会话的长时间运行操作。",
|
|
2320
2456
|
inputSchema: {
|
|
@@ -2532,7 +2668,7 @@ function mockDispatch(action, taskId, taskType, taskPayload, taskStatus, startTi
|
|
|
2532
2668
|
const handler$16 = buildHandler(null);
|
|
2533
2669
|
//#endregion
|
|
2534
2670
|
//#region src/builtin/agent/team-create.ts
|
|
2535
|
-
const descriptor$
|
|
2671
|
+
const descriptor$22 = {
|
|
2536
2672
|
name: "team_create",
|
|
2537
2673
|
description: "创建多 Agent 协作团队。每个成员拥有独立的角色、工具集和模型。团队通过可配置的策略协作完成工作(默认:共识模式)。",
|
|
2538
2674
|
inputSchema: {
|
|
@@ -2589,30 +2725,16 @@ const descriptor$18 = {
|
|
|
2589
2725
|
};
|
|
2590
2726
|
const handler$15 = { async handle(invocation, signal) {
|
|
2591
2727
|
const { name, members, strategy } = invocation.payload;
|
|
2592
|
-
const startTime = Date.now();
|
|
2593
2728
|
if (signal.aborted) return {
|
|
2594
2729
|
success: false,
|
|
2595
2730
|
content: "团队创建已取消",
|
|
2596
|
-
metadata: { durationMs:
|
|
2597
|
-
};
|
|
2598
|
-
const strat = strategy ?? "consensus";
|
|
2599
|
-
const memberList = members.map((m) => ` - ${m.name} (${m.role})${m.model ? ` [${m.model}]` : ""}`).join("\n");
|
|
2600
|
-
return {
|
|
2601
|
-
success: true,
|
|
2602
|
-
content: [
|
|
2603
|
-
`团队已创建:${name}`,
|
|
2604
|
-
`策略:${strat}`,
|
|
2605
|
-
`成员(${members.length} 人):`,
|
|
2606
|
-
memberList,
|
|
2607
|
-
"",
|
|
2608
|
-
"[Phase 4 集成:通过 @24klynx/agent 子 Agent 通道实现真实团队编排]"
|
|
2609
|
-
].join("\n"),
|
|
2610
|
-
metadata: { durationMs: Date.now() - startTime }
|
|
2731
|
+
metadata: { durationMs: 0 }
|
|
2611
2732
|
};
|
|
2733
|
+
throw new NotImplementedError(`团队 "${name ?? "未命名"}" 创建失败:团队协作引擎将在 Phase 4 实现。当前不支持多 Agent 团队编排(${members?.length ?? 0} 名成员,策略:${strategy ?? "consensus"})。`);
|
|
2612
2734
|
} };
|
|
2613
2735
|
//#endregion
|
|
2614
2736
|
//#region src/builtin/agent/team-delete.ts
|
|
2615
|
-
const descriptor$
|
|
2737
|
+
const descriptor$21 = {
|
|
2616
2738
|
name: "team_delete",
|
|
2617
2739
|
description: "删除多 Agent 协作团队。移除团队前会终止所有正在执行的成员任务。",
|
|
2618
2740
|
inputSchema: {
|
|
@@ -2634,25 +2756,16 @@ const descriptor$17 = {
|
|
|
2634
2756
|
};
|
|
2635
2757
|
const handler$14 = { async handle(invocation, signal) {
|
|
2636
2758
|
const { team_id } = invocation.payload;
|
|
2637
|
-
const startTime = Date.now();
|
|
2638
2759
|
if (signal.aborted) return {
|
|
2639
2760
|
success: false,
|
|
2640
2761
|
content: "团队删除已取消",
|
|
2641
|
-
metadata: { durationMs:
|
|
2642
|
-
};
|
|
2643
|
-
return {
|
|
2644
|
-
success: true,
|
|
2645
|
-
content: [
|
|
2646
|
-
`团队已删除:${team_id}`,
|
|
2647
|
-
"所有成员任务已终止。",
|
|
2648
|
-
"[Phase 4 集成:通过 @24klynx/agent 实现真实团队生命周期管理]"
|
|
2649
|
-
].join("\n"),
|
|
2650
|
-
metadata: { durationMs: Date.now() - startTime }
|
|
2762
|
+
metadata: { durationMs: 0 }
|
|
2651
2763
|
};
|
|
2764
|
+
throw new NotImplementedError(`团队 "${team_id}" 删除失败:团队协作引擎将在 Phase 4 实现。当前不支持多 Agent 团队生命周期管理。`);
|
|
2652
2765
|
} };
|
|
2653
2766
|
//#endregion
|
|
2654
2767
|
//#region src/builtin/agent/verify-plan.ts
|
|
2655
|
-
const descriptor$
|
|
2768
|
+
const descriptor$20 = {
|
|
2656
2769
|
name: "verify_plan_execution",
|
|
2657
2770
|
description: "验证计划执行是否匹配其规范。逐一检查每个检查点的预期输出与实际输出是否一致。在实施计划后使用此工具,确保无遗漏。",
|
|
2658
2771
|
inputSchema: {
|
|
@@ -2730,7 +2843,7 @@ const handler$13 = { async handle(invocation, signal) {
|
|
|
2730
2843
|
} };
|
|
2731
2844
|
//#endregion
|
|
2732
2845
|
//#region src/builtin/agent/remote-trigger.ts
|
|
2733
|
-
const descriptor$
|
|
2846
|
+
const descriptor$19 = {
|
|
2734
2847
|
name: "remote_trigger",
|
|
2735
2848
|
description: "通过发送 HTTP 请求(webhook / CI 回调)触发远程任务。支持 GET 和 POST 方法,可自定义请求头和请求体。默认超时 30 秒。",
|
|
2736
2849
|
inputSchema: {
|
|
@@ -2915,7 +3028,7 @@ function isValidCron(cron) {
|
|
|
2915
3028
|
});
|
|
2916
3029
|
}
|
|
2917
3030
|
init$1();
|
|
2918
|
-
const descriptor$
|
|
3031
|
+
const descriptor$18 = {
|
|
2919
3032
|
name: "ScheduleCronTool",
|
|
2920
3033
|
description: "使用 cron 语法安排定时任务。任务在指定时间或间隔触发。支持一次性提醒和循环计划。标准 cron 格式:分 时 日 月 周。也支持 @hourly、@daily、@weekly 别名。",
|
|
2921
3034
|
inputSchema: {
|
|
@@ -3060,7 +3173,7 @@ function persist() {
|
|
|
3060
3173
|
writeFileSync(STORAGE_FILE, JSON.stringify(Array.from(workflows.values()), null, 2));
|
|
3061
3174
|
}
|
|
3062
3175
|
init();
|
|
3063
|
-
const descriptor$
|
|
3176
|
+
const descriptor$17 = {
|
|
3064
3177
|
name: "WorkflowTool",
|
|
3065
3178
|
description: "定义并执行多步骤工作流。每个步骤可以是一个工具调用,步骤之间可以有依赖关系。支持 define(定义)、run(运行)、status(查看状态)、list(列出所有)四种操作。",
|
|
3066
3179
|
inputSchema: {
|
|
@@ -3167,7 +3280,13 @@ function handleDefine(name, steps) {
|
|
|
3167
3280
|
content: `工作流已定义。ID:${id}\n名称:${name}\n步骤:\n${steps.map((s, i) => ` ${i + 1}. ${s.name} — 调用 ${s.toolName}`).join("\n")}`
|
|
3168
3281
|
};
|
|
3169
3282
|
}
|
|
3170
|
-
/**
|
|
3283
|
+
/**
|
|
3284
|
+
* 运行工作流:返回步骤指令供 Agent 循环按序执行。
|
|
3285
|
+
*
|
|
3286
|
+
* 本工具是"指令生成器"而非"执行引擎"——
|
|
3287
|
+
* 它输出清晰的步骤文本,由 Agent 循环(loop.ts)负责依次调用各步骤指定的工具。
|
|
3288
|
+
* 这样设计是为了让每一步的工具调用都经过权限检查和用户审批流程。
|
|
3289
|
+
*/
|
|
3171
3290
|
function handleRun(workflowId) {
|
|
3172
3291
|
if (!workflowId) return {
|
|
3173
3292
|
success: false,
|
|
@@ -3235,7 +3354,7 @@ function handleList() {
|
|
|
3235
3354
|
}
|
|
3236
3355
|
//#endregion
|
|
3237
3356
|
//#region src/builtin/agent/tungsten.ts
|
|
3238
|
-
const descriptor$
|
|
3357
|
+
const descriptor$16 = {
|
|
3239
3358
|
name: "TungstenTool",
|
|
3240
3359
|
description: "重型复合工具:并行调度多个子 Agent 处理复杂任务的各个部分,然后综合结果。支持 parallel(并行)、pipeline(流水线)、debate(辩论)三种策略。适用于代码审查、多模块重构、全面调研等大规模任务。",
|
|
3241
3360
|
inputSchema: {
|
|
@@ -3343,7 +3462,14 @@ function decomposeTask(task, maxAgents) {
|
|
|
3343
3462
|
suggestedAgentRole: "主执行 Agent"
|
|
3344
3463
|
}];
|
|
3345
3464
|
}
|
|
3346
|
-
/**
|
|
3465
|
+
/**
|
|
3466
|
+
* 根据策略生成执行计划文本。
|
|
3467
|
+
*
|
|
3468
|
+
* 本工具是"任务规划器"而非"执行引擎"——
|
|
3469
|
+
* 它将复杂任务拆解为子任务并输出结构化指令,
|
|
3470
|
+
* 实际的子 Agent spawn 由 Agent 工具(agent.ts)负责。
|
|
3471
|
+
* Agent 循环读取此计划后逐一创建子 Agent 并收集结果。
|
|
3472
|
+
*/
|
|
3347
3473
|
function buildExecutionPlan(task, subtasks, maxAgents, strategy) {
|
|
3348
3474
|
const planLines = [
|
|
3349
3475
|
"═══════════════════════════════════════",
|
|
@@ -3394,7 +3520,7 @@ function strategyLabel(strategy) {
|
|
|
3394
3520
|
}
|
|
3395
3521
|
//#endregion
|
|
3396
3522
|
//#region src/builtin/mode/enter-plan-mode.ts
|
|
3397
|
-
const descriptor$
|
|
3523
|
+
const descriptor$15 = {
|
|
3398
3524
|
name: "enter_plan_mode",
|
|
3399
3525
|
description: "进入计划模式 — 切换为只读模式,Agent 只能读取文件和输出设计方案。所有写入/编辑/执行工具将自动被拒绝。在进行重大架构变更前使用此模式,以便在实施前获得用户对方案的认可。",
|
|
3400
3526
|
inputSchema: {
|
|
@@ -3428,7 +3554,7 @@ const handler$8 = { async handle(_invocation, signal) {
|
|
|
3428
3554
|
} };
|
|
3429
3555
|
//#endregion
|
|
3430
3556
|
//#region src/builtin/mode/exit-plan-mode.ts
|
|
3431
|
-
const descriptor$
|
|
3557
|
+
const descriptor$14 = {
|
|
3432
3558
|
name: "exit_plan_mode",
|
|
3433
3559
|
description: "退出计划模式,返回普通模式(写入工具重新启用)。计划方案将在执行前展示给用户审批。",
|
|
3434
3560
|
inputSchema: {
|
|
@@ -3461,7 +3587,7 @@ const handler$7 = { async handle(_invocation, signal) {
|
|
|
3461
3587
|
} };
|
|
3462
3588
|
//#endregion
|
|
3463
3589
|
//#region src/builtin/mode/enter-worktree.ts
|
|
3464
|
-
const descriptor$
|
|
3590
|
+
const descriptor$13 = {
|
|
3465
3591
|
name: "enter_worktree",
|
|
3466
3592
|
description: "创建新的 git worktree 或进入已有 worktree 进行隔离工作。Worktree 存放在 .claude/worktrees/ 目录下。使用 name 参数创建新 worktree,使用 path 参数进入已有 worktree。",
|
|
3467
3593
|
inputSchema: {
|
|
@@ -3525,7 +3651,7 @@ const handler$6 = { async handle(invocation, signal) {
|
|
|
3525
3651
|
} };
|
|
3526
3652
|
//#endregion
|
|
3527
3653
|
//#region src/builtin/mode/exit-worktree.ts
|
|
3528
|
-
const descriptor$
|
|
3654
|
+
const descriptor$12 = {
|
|
3529
3655
|
name: "exit_worktree",
|
|
3530
3656
|
description: "退出 git worktree 并返回原始目录。使用 'keep' 保留 worktree,或使用 'remove' 删除(如有未提交变更需搭配 discard_changes)。",
|
|
3531
3657
|
inputSchema: {
|
|
@@ -3587,7 +3713,7 @@ const handler$5 = { async handle(invocation, signal) {
|
|
|
3587
3713
|
* - list: 列出所有配置项
|
|
3588
3714
|
* - path: 返回配置文件路径
|
|
3589
3715
|
*/
|
|
3590
|
-
const descriptor$
|
|
3716
|
+
const descriptor$11 = {
|
|
3591
3717
|
name: "ConfigTool",
|
|
3592
3718
|
description: "读取或修改 Lynx 配置。可以查看当前配置值或设置新的配置项。支持四种操作:get(获取单个配置项)、set(设置配置项)、list(列出所有配置项)、path(查看配置文件路径)。",
|
|
3593
3719
|
inputSchema: {
|
|
@@ -3722,7 +3848,7 @@ const handler$4 = { async handle(invocation, signal) {
|
|
|
3722
3848
|
} };
|
|
3723
3849
|
//#endregion
|
|
3724
3850
|
//#region src/builtin/interact/ask-user-question.ts
|
|
3725
|
-
const descriptor$
|
|
3851
|
+
const descriptor$10 = {
|
|
3726
3852
|
name: "ask_user_question",
|
|
3727
3853
|
description: "向用户提出一个或多个澄清性问题并等待回答。支持单选、多选以及自由文本的「其他」选项。当您被一个只有用户才能做决定的问题卡住时使用。",
|
|
3728
3854
|
inputSchema: {
|
|
@@ -3809,7 +3935,7 @@ const handler$3 = { async handle(invocation, signal) {
|
|
|
3809
3935
|
} };
|
|
3810
3936
|
//#endregion
|
|
3811
3937
|
//#region src/builtin/interact/brief.ts
|
|
3812
|
-
const descriptor$
|
|
3938
|
+
const descriptor$9 = {
|
|
3813
3939
|
name: "brief",
|
|
3814
3940
|
description: "生成当前对话上下文的结构化摘要简报。适用于进度检查点、会话间交接、或向用户提供状态更新。",
|
|
3815
3941
|
inputSchema: {
|
|
@@ -3869,7 +3995,7 @@ const handler$2 = { async handle(invocation, signal) {
|
|
|
3869
3995
|
} };
|
|
3870
3996
|
//#endregion
|
|
3871
3997
|
//#region src/builtin/interact/synthetic-output.ts
|
|
3872
|
-
const descriptor$
|
|
3998
|
+
const descriptor$8 = {
|
|
3873
3999
|
name: "synthetic_output",
|
|
3874
4000
|
description: "生成符合 JSON Schema 的结构化输出。用于在子 Agent 和父 Agent 之间传递带类型的数据。如果输出无法通过 schema 校验,LLM 会自动重试。",
|
|
3875
4001
|
inputSchema: {
|
|
@@ -3913,7 +4039,7 @@ const handler$1 = { async handle(invocation, signal) {
|
|
|
3913
4039
|
} };
|
|
3914
4040
|
//#endregion
|
|
3915
4041
|
//#region src/builtin/interact/sleep.ts
|
|
3916
|
-
const descriptor$
|
|
4042
|
+
const descriptor$7 = {
|
|
3917
4043
|
name: "sleep",
|
|
3918
4044
|
description: "暂停执行指定秒数。用于速率限制退避、等待外部事件或轮询间隔。最长 300 秒。必须提供原因。",
|
|
3919
4045
|
inputSchema: {
|
|
@@ -3972,7 +4098,7 @@ const handler = { async handle(invocation, signal) {
|
|
|
3972
4098
|
} };
|
|
3973
4099
|
//#endregion
|
|
3974
4100
|
//#region src/builtin/memory/memory-write.ts
|
|
3975
|
-
const descriptor$
|
|
4101
|
+
const descriptor$3 = {
|
|
3976
4102
|
name: "memory_write",
|
|
3977
4103
|
description: "管理持久化记忆。支持以下操作:create/update — 写入或更新一条记忆;delete — 按名称删除;search — 全文搜索记忆(按相关度排序);compact — 去重压缩相似条目(Jaccard 相似度 > 0.8);forget — 按 glob 模式批量删除匹配的记忆;export — 导出全部记忆为 JSON 字符串;import — 从 JSON 字符串批量导入记忆(跳过同名条目);merge — 合并另一个 memory 目录中的记忆文件。当用户要求记住某事、搜索记忆、清理重复记忆或迁移记忆时使用。",
|
|
3978
4104
|
inputSchema: {
|
|
@@ -4190,9 +4316,291 @@ function createMemoryWriteHandler(memoryManager) {
|
|
|
4190
4316
|
} };
|
|
4191
4317
|
}
|
|
4192
4318
|
//#endregion
|
|
4193
|
-
//#region src/builtin/
|
|
4319
|
+
//#region src/builtin/files/tool-search.ts
|
|
4320
|
+
const descriptor$6 = {
|
|
4321
|
+
name: "ToolSearchTool",
|
|
4322
|
+
description: "按名称或描述搜索可用的工具。返回匹配工具的摘要信息,包括工具名、用途和安全等级。",
|
|
4323
|
+
inputSchema: {
|
|
4324
|
+
type: "object",
|
|
4325
|
+
properties: { query: {
|
|
4326
|
+
type: "string",
|
|
4327
|
+
description: "搜索关键词(大小写不敏感)"
|
|
4328
|
+
} },
|
|
4329
|
+
required: ["query"]
|
|
4330
|
+
},
|
|
4331
|
+
kind: "ReadOnly",
|
|
4332
|
+
safety: "Safe",
|
|
4333
|
+
availability: { type: "always" },
|
|
4334
|
+
executor: "core:tool_search",
|
|
4335
|
+
owner: "core"
|
|
4336
|
+
};
|
|
4337
|
+
/**
|
|
4338
|
+
* 创建 ToolSearchTool 的 handler。
|
|
4339
|
+
*
|
|
4340
|
+
* 工厂函数接收获取所有工具描述符的函数,返回 handler。
|
|
4341
|
+
* 这样可以在不引入循环依赖的情况下注入工具列表。
|
|
4342
|
+
*
|
|
4343
|
+
* @param getAllTools — 返回所有已注册 ToolDescriptor 的函数
|
|
4344
|
+
*/
|
|
4345
|
+
function createToolSearchHandler(getAllTools) {
|
|
4346
|
+
return { async handle(invocation, signal) {
|
|
4347
|
+
const { query } = invocation.payload;
|
|
4348
|
+
const startTime = Date.now();
|
|
4349
|
+
if (signal.aborted) return {
|
|
4350
|
+
success: false,
|
|
4351
|
+
content: "操作已取消",
|
|
4352
|
+
metadata: { durationMs: Date.now() - startTime }
|
|
4353
|
+
};
|
|
4354
|
+
if (!query || query.trim().length === 0) return {
|
|
4355
|
+
success: false,
|
|
4356
|
+
content: "请输入搜索关键词。",
|
|
4357
|
+
metadata: { durationMs: Date.now() - startTime }
|
|
4358
|
+
};
|
|
4359
|
+
const lowerQuery = query.trim().toLowerCase();
|
|
4360
|
+
const matches = getAllTools().filter((tool) => {
|
|
4361
|
+
const nameLower = tool.name.toLowerCase();
|
|
4362
|
+
const descLower = tool.description.toLowerCase();
|
|
4363
|
+
return nameLower.includes(lowerQuery) || descLower.includes(lowerQuery);
|
|
4364
|
+
});
|
|
4365
|
+
if (matches.length === 0) return {
|
|
4366
|
+
success: true,
|
|
4367
|
+
content: `未找到与 "${query.trim()}" 匹配的工具。`,
|
|
4368
|
+
metadata: { durationMs: Date.now() - startTime }
|
|
4369
|
+
};
|
|
4370
|
+
const lines = matches.map((tool) => {
|
|
4371
|
+
const safetyLabel = safetyToLabel(tool.safety);
|
|
4372
|
+
return `- **${tool.name}** [${safetyLabel}]\n ${tool.description}`;
|
|
4373
|
+
});
|
|
4374
|
+
return {
|
|
4375
|
+
success: true,
|
|
4376
|
+
content: `找到 ${matches.length} 个匹配的工具:\n\n${lines.join("\n\n")}`,
|
|
4377
|
+
metadata: { durationMs: Date.now() - startTime }
|
|
4378
|
+
};
|
|
4379
|
+
} };
|
|
4380
|
+
}
|
|
4381
|
+
/** 将 safety 枚举值转为中文标签。 */
|
|
4382
|
+
function safetyToLabel(safety) {
|
|
4383
|
+
switch (safety) {
|
|
4384
|
+
case "Safe": return "安全";
|
|
4385
|
+
case "WorkspaceSafe": return "工作区安全";
|
|
4386
|
+
case "RequiresApproval": return "需审批";
|
|
4387
|
+
case "Dangerous": return "危险";
|
|
4388
|
+
default: return safety;
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
//#endregion
|
|
4392
|
+
//#region src/builtin/mcp/call-tool.ts
|
|
4194
4393
|
/** 工具描述符 — 注册到 ToolRegistry 的静态元数据。 */
|
|
4394
|
+
const descriptor$2 = {
|
|
4395
|
+
name: "mcp__call_tool",
|
|
4396
|
+
description: "直接调用已连接的 MCP 服务器的工具。使用 serverName 和 toolName 指定目标工具。",
|
|
4397
|
+
inputSchema: {
|
|
4398
|
+
type: "object",
|
|
4399
|
+
properties: {
|
|
4400
|
+
serverName: {
|
|
4401
|
+
type: "string",
|
|
4402
|
+
description: "MCP 服务器名称"
|
|
4403
|
+
},
|
|
4404
|
+
toolName: {
|
|
4405
|
+
type: "string",
|
|
4406
|
+
description: "原始工具名(非命名空间格式)"
|
|
4407
|
+
},
|
|
4408
|
+
arguments: {
|
|
4409
|
+
type: "object",
|
|
4410
|
+
description: "传递给工具的参数"
|
|
4411
|
+
}
|
|
4412
|
+
},
|
|
4413
|
+
required: ["serverName", "toolName"]
|
|
4414
|
+
},
|
|
4415
|
+
kind: "ExecutesCode",
|
|
4416
|
+
safety: "RequiresApproval",
|
|
4417
|
+
availability: { type: "always" },
|
|
4418
|
+
executor: "core:mcp_call_tool",
|
|
4419
|
+
owner: "core"
|
|
4420
|
+
};
|
|
4421
|
+
/**
|
|
4422
|
+
* 创建 mcp__call_tool 的 handler,绑定到给定的 McpManager。
|
|
4423
|
+
*
|
|
4424
|
+
* 工厂模式 — McpManager 在 bootstrap 时注入,
|
|
4425
|
+
* 因此 lynx-tools 无需依赖 lynx-agent。
|
|
4426
|
+
*/
|
|
4427
|
+
function createMcpCallToolHandler(mcpManager) {
|
|
4428
|
+
return { async handle(invocation, signal) {
|
|
4429
|
+
const { serverName, toolName, arguments: toolArgs } = invocation.payload;
|
|
4430
|
+
if (!serverName || typeof serverName !== "string") return {
|
|
4431
|
+
success: false,
|
|
4432
|
+
content: "缺少必需参数 serverName"
|
|
4433
|
+
};
|
|
4434
|
+
if (!toolName || typeof toolName !== "string") return {
|
|
4435
|
+
success: false,
|
|
4436
|
+
content: "缺少必需参数 toolName"
|
|
4437
|
+
};
|
|
4438
|
+
const namespacedName = `mcp__${serverName}__${toolName}`;
|
|
4439
|
+
if (signal.aborted) return {
|
|
4440
|
+
success: false,
|
|
4441
|
+
content: "操作已取消"
|
|
4442
|
+
};
|
|
4443
|
+
try {
|
|
4444
|
+
const result = await mcpManager.callTool(namespacedName, toolArgs ?? {});
|
|
4445
|
+
if (result.isError) return {
|
|
4446
|
+
success: false,
|
|
4447
|
+
content: result.content
|
|
4448
|
+
};
|
|
4449
|
+
return {
|
|
4450
|
+
success: true,
|
|
4451
|
+
content: result.content
|
|
4452
|
+
};
|
|
4453
|
+
} catch (err) {
|
|
4454
|
+
return {
|
|
4455
|
+
success: false,
|
|
4456
|
+
content: `MCP 工具调用失败:${err.message}`
|
|
4457
|
+
};
|
|
4458
|
+
}
|
|
4459
|
+
} };
|
|
4460
|
+
}
|
|
4461
|
+
//#endregion
|
|
4462
|
+
//#region src/builtin/mcp/list-resources.ts
|
|
4463
|
+
/** 工具描述符。 */
|
|
4195
4464
|
const descriptor = {
|
|
4465
|
+
name: "ListMcpResourcesTool",
|
|
4466
|
+
description: "列出已连接 MCP 服务器提供的资源。可指定 server 名称进行过滤。",
|
|
4467
|
+
inputSchema: {
|
|
4468
|
+
type: "object",
|
|
4469
|
+
properties: { server: {
|
|
4470
|
+
type: "string",
|
|
4471
|
+
description: "过滤特定 MCP 服务器(可选)"
|
|
4472
|
+
} }
|
|
4473
|
+
},
|
|
4474
|
+
kind: "ReadOnly",
|
|
4475
|
+
safety: "Safe",
|
|
4476
|
+
availability: { type: "always" },
|
|
4477
|
+
executor: "core:mcp_list_resources",
|
|
4478
|
+
owner: "core"
|
|
4479
|
+
};
|
|
4480
|
+
/**
|
|
4481
|
+
* 创建 ListMcpResourcesTool 的 handler,绑定到给定的 McpManager。
|
|
4482
|
+
*
|
|
4483
|
+
* 工厂模式 — McpManager 在 bootstrap 时注入。
|
|
4484
|
+
*/
|
|
4485
|
+
function createListMcpResourcesHandler(mcpManager) {
|
|
4486
|
+
return { async handle(invocation, _signal) {
|
|
4487
|
+
const { server } = invocation.payload;
|
|
4488
|
+
if (!mcpManager.hasResourceCapability()) return {
|
|
4489
|
+
success: true,
|
|
4490
|
+
content: "当前没有已连接且支持 resources 协议的 MCP 服务器。"
|
|
4491
|
+
};
|
|
4492
|
+
let resources = mcpManager.getAllResources();
|
|
4493
|
+
if (server && typeof server === "string") {
|
|
4494
|
+
resources = resources.filter((r) => r.server === server);
|
|
4495
|
+
if (resources.length === 0) return {
|
|
4496
|
+
success: true,
|
|
4497
|
+
content: `服务器 "${server}" 未提供任何资源,或未连接。`
|
|
4498
|
+
};
|
|
4499
|
+
}
|
|
4500
|
+
if (resources.length === 0) return {
|
|
4501
|
+
success: true,
|
|
4502
|
+
content: "已连接的 MCP 服务器未提供任何资源。"
|
|
4503
|
+
};
|
|
4504
|
+
const lines = resources.map((r) => {
|
|
4505
|
+
const parts = [`- **${r.name}**`];
|
|
4506
|
+
parts.push(`(server: ${r.server})`);
|
|
4507
|
+
parts.push(`uri: \`${r.uri}\``);
|
|
4508
|
+
if (r.mimeType) parts.push(`mimeType: ${r.mimeType}`);
|
|
4509
|
+
if (r.description) parts.push(`${r.description}`);
|
|
4510
|
+
return parts.join(" \n ");
|
|
4511
|
+
});
|
|
4512
|
+
return {
|
|
4513
|
+
success: true,
|
|
4514
|
+
content: `已连接 MCP 服务器资源列表(共 ${resources.length} 项):\n\n${lines.join("\n")}`
|
|
4515
|
+
};
|
|
4516
|
+
} };
|
|
4517
|
+
}
|
|
4518
|
+
//#endregion
|
|
4519
|
+
//#region src/builtin/mcp/read-resource.ts
|
|
4520
|
+
/**
|
|
4521
|
+
* ReadMcpResourceTool — 通过 URI 读取 MCP 资源的内容。
|
|
4522
|
+
*
|
|
4523
|
+
* 文本内容直接返回,二进制内容保存到临时文件并返回文件路径。
|
|
4524
|
+
*/
|
|
4525
|
+
/** 工具描述符。 */
|
|
4526
|
+
const descriptor$4 = {
|
|
4527
|
+
name: "ReadMcpResourceTool",
|
|
4528
|
+
description: "通过 URI 读取 MCP 资源的内容。二进制资源将保存到临时文件并返回文件路径。",
|
|
4529
|
+
inputSchema: {
|
|
4530
|
+
type: "object",
|
|
4531
|
+
properties: {
|
|
4532
|
+
server: {
|
|
4533
|
+
type: "string",
|
|
4534
|
+
description: "MCP 服务器名称"
|
|
4535
|
+
},
|
|
4536
|
+
uri: {
|
|
4537
|
+
type: "string",
|
|
4538
|
+
description: "资源 URI"
|
|
4539
|
+
}
|
|
4540
|
+
},
|
|
4541
|
+
required: ["server", "uri"]
|
|
4542
|
+
},
|
|
4543
|
+
kind: "ReadOnly",
|
|
4544
|
+
safety: "Safe",
|
|
4545
|
+
availability: { type: "always" },
|
|
4546
|
+
executor: "core:mcp_read_resource",
|
|
4547
|
+
owner: "core"
|
|
4548
|
+
};
|
|
4549
|
+
/**
|
|
4550
|
+
* 创建 ReadMcpResourceTool 的 handler,绑定到给定的 McpManager。
|
|
4551
|
+
*
|
|
4552
|
+
* 工厂模式 — McpManager 在 bootstrap 时注入。
|
|
4553
|
+
*/
|
|
4554
|
+
function createReadMcpResourceHandler(mcpManager) {
|
|
4555
|
+
return { async handle(invocation, _signal) {
|
|
4556
|
+
const { server, uri } = invocation.payload;
|
|
4557
|
+
if (!server || typeof server !== "string") return {
|
|
4558
|
+
success: false,
|
|
4559
|
+
content: "缺少必需参数 server"
|
|
4560
|
+
};
|
|
4561
|
+
if (!uri || typeof uri !== "string") return {
|
|
4562
|
+
success: false,
|
|
4563
|
+
content: "缺少必需参数 uri"
|
|
4564
|
+
};
|
|
4565
|
+
try {
|
|
4566
|
+
const contents = (await mcpManager.readResource(server, uri)).contents;
|
|
4567
|
+
if (!contents || contents.length === 0) return {
|
|
4568
|
+
success: true,
|
|
4569
|
+
content: `资源 "${uri}" 返回了空内容。`
|
|
4570
|
+
};
|
|
4571
|
+
const textContents = [];
|
|
4572
|
+
const binaryContents = [];
|
|
4573
|
+
for (const item of contents) {
|
|
4574
|
+
const c = item;
|
|
4575
|
+
if (c.text !== void 0) textContents.push(c.text);
|
|
4576
|
+
else if (c.blob !== void 0) {
|
|
4577
|
+
const tmpDir = join(tmpdir(), "lynx-mcp-resources");
|
|
4578
|
+
mkdirSync(tmpDir, { recursive: true });
|
|
4579
|
+
const filePath = join(tmpDir, uri.replace(/[^a-zA-Z0-9._-]/g, "_").slice(-64) || "resource");
|
|
4580
|
+
writeFileSync(filePath, Buffer.from(c.blob, "base64"));
|
|
4581
|
+
const mimeLabel = c.mimeType ? ` (${c.mimeType})` : "";
|
|
4582
|
+
binaryContents.push(`二进制内容${mimeLabel}已保存到:${filePath}`);
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
const parts = [];
|
|
4586
|
+
if (textContents.length > 0) parts.push(textContents.join("\n---\n"));
|
|
4587
|
+
if (binaryContents.length > 0) parts.push(binaryContents.join("\n"));
|
|
4588
|
+
return {
|
|
4589
|
+
success: true,
|
|
4590
|
+
content: parts.join("\n\n") || "(空内容)"
|
|
4591
|
+
};
|
|
4592
|
+
} catch (err) {
|
|
4593
|
+
return {
|
|
4594
|
+
success: false,
|
|
4595
|
+
content: `读取 MCP 资源失败:${err.message}`
|
|
4596
|
+
};
|
|
4597
|
+
}
|
|
4598
|
+
} };
|
|
4599
|
+
}
|
|
4600
|
+
//#endregion
|
|
4601
|
+
//#region src/builtin/mcp/mcp-auth.ts
|
|
4602
|
+
/** 工具描述符 — 注册到 ToolRegistry 的静态元数据。 */
|
|
4603
|
+
const descriptor$1 = {
|
|
4196
4604
|
name: "McpAuthTool",
|
|
4197
4605
|
description: "触发 MCP 服务器的重新认证流程(OAuth 或 XAA)。用于在工具调用失败并返回认证错误时。",
|
|
4198
4606
|
inputSchema: {
|
|
@@ -4240,7 +4648,7 @@ function createMcpAuthHandler(mcpOps) {
|
|
|
4240
4648
|
}
|
|
4241
4649
|
//#endregion
|
|
4242
4650
|
//#region src/builtin/interact/send-message.ts
|
|
4243
|
-
const descriptor$
|
|
4651
|
+
const descriptor$5 = {
|
|
4244
4652
|
name: "SendMessageTool",
|
|
4245
4653
|
description: "通过配置的消息通道发送消息(飞书等)。用于向用户发送通知或进度摘要。需要提前配置消息通道。",
|
|
4246
4654
|
inputSchema: {
|
|
@@ -4309,127 +4717,131 @@ function createSendMessageHandler(sender) {
|
|
|
4309
4717
|
/** All built‑in (descriptor, handler) pairs in registration order. */
|
|
4310
4718
|
const BUILTIN_TOOLS = [
|
|
4311
4719
|
{
|
|
4312
|
-
descriptor: descriptor$
|
|
4720
|
+
descriptor: descriptor$38,
|
|
4721
|
+
handler: handler$31
|
|
4722
|
+
},
|
|
4723
|
+
{
|
|
4724
|
+
descriptor: descriptor$37,
|
|
4313
4725
|
handler: handler$30
|
|
4314
4726
|
},
|
|
4315
4727
|
{
|
|
4316
|
-
descriptor: descriptor$
|
|
4728
|
+
descriptor: descriptor$36,
|
|
4317
4729
|
handler: handler$29
|
|
4318
4730
|
},
|
|
4319
4731
|
{
|
|
4320
|
-
descriptor: descriptor$
|
|
4732
|
+
descriptor: descriptor$35,
|
|
4321
4733
|
handler: handler$28
|
|
4322
4734
|
},
|
|
4323
4735
|
{
|
|
4324
|
-
descriptor: descriptor$
|
|
4736
|
+
descriptor: descriptor$34,
|
|
4325
4737
|
handler: handler$27
|
|
4326
4738
|
},
|
|
4327
4739
|
{
|
|
4328
|
-
descriptor: descriptor$
|
|
4740
|
+
descriptor: descriptor$33,
|
|
4329
4741
|
handler: handler$26
|
|
4330
4742
|
},
|
|
4331
4743
|
{
|
|
4332
|
-
descriptor: descriptor$
|
|
4744
|
+
descriptor: descriptor$32,
|
|
4333
4745
|
handler: handler$25
|
|
4334
4746
|
},
|
|
4335
4747
|
{
|
|
4336
|
-
descriptor: descriptor$
|
|
4748
|
+
descriptor: descriptor$31,
|
|
4337
4749
|
handler: handler$24
|
|
4338
4750
|
},
|
|
4339
4751
|
{
|
|
4340
|
-
descriptor: descriptor$
|
|
4752
|
+
descriptor: descriptor$30,
|
|
4341
4753
|
handler: handler$23
|
|
4342
4754
|
},
|
|
4343
4755
|
{
|
|
4344
|
-
descriptor: descriptor$
|
|
4756
|
+
descriptor: descriptor$29,
|
|
4345
4757
|
handler: handler$22
|
|
4346
4758
|
},
|
|
4347
4759
|
{
|
|
4348
|
-
descriptor: descriptor$
|
|
4760
|
+
descriptor: descriptor$28,
|
|
4349
4761
|
handler: handler$21
|
|
4350
4762
|
},
|
|
4351
4763
|
{
|
|
4352
|
-
descriptor: descriptor$
|
|
4764
|
+
descriptor: descriptor$27,
|
|
4353
4765
|
handler: handler$20
|
|
4354
4766
|
},
|
|
4355
4767
|
{
|
|
4356
|
-
descriptor: descriptor$
|
|
4768
|
+
descriptor: descriptor$26,
|
|
4357
4769
|
handler: handler$19
|
|
4358
4770
|
},
|
|
4359
4771
|
{
|
|
4360
|
-
descriptor: descriptor$
|
|
4772
|
+
descriptor: descriptor$25,
|
|
4361
4773
|
handler: handler$18
|
|
4362
4774
|
},
|
|
4363
4775
|
{
|
|
4364
|
-
descriptor: descriptor$
|
|
4776
|
+
descriptor: descriptor$24,
|
|
4365
4777
|
handler: handler$17
|
|
4366
4778
|
},
|
|
4367
4779
|
{
|
|
4368
|
-
descriptor: descriptor$
|
|
4780
|
+
descriptor: descriptor$23,
|
|
4369
4781
|
handler: handler$16
|
|
4370
4782
|
},
|
|
4371
4783
|
{
|
|
4372
|
-
descriptor: descriptor$
|
|
4784
|
+
descriptor: descriptor$22,
|
|
4373
4785
|
handler: handler$15
|
|
4374
4786
|
},
|
|
4375
4787
|
{
|
|
4376
|
-
descriptor: descriptor$
|
|
4788
|
+
descriptor: descriptor$21,
|
|
4377
4789
|
handler: handler$14
|
|
4378
4790
|
},
|
|
4379
4791
|
{
|
|
4380
|
-
descriptor: descriptor$
|
|
4792
|
+
descriptor: descriptor$20,
|
|
4381
4793
|
handler: handler$13
|
|
4382
4794
|
},
|
|
4383
4795
|
{
|
|
4384
|
-
descriptor: descriptor$
|
|
4796
|
+
descriptor: descriptor$19,
|
|
4385
4797
|
handler: handler$12
|
|
4386
4798
|
},
|
|
4387
4799
|
{
|
|
4388
|
-
descriptor: descriptor$
|
|
4800
|
+
descriptor: descriptor$18,
|
|
4389
4801
|
handler: handler$11
|
|
4390
4802
|
},
|
|
4391
4803
|
{
|
|
4392
|
-
descriptor: descriptor$
|
|
4804
|
+
descriptor: descriptor$17,
|
|
4393
4805
|
handler: handler$10
|
|
4394
4806
|
},
|
|
4395
4807
|
{
|
|
4396
|
-
descriptor: descriptor$
|
|
4808
|
+
descriptor: descriptor$16,
|
|
4397
4809
|
handler: handler$9
|
|
4398
4810
|
},
|
|
4399
4811
|
{
|
|
4400
|
-
descriptor: descriptor$
|
|
4812
|
+
descriptor: descriptor$15,
|
|
4401
4813
|
handler: handler$8
|
|
4402
4814
|
},
|
|
4403
4815
|
{
|
|
4404
|
-
descriptor: descriptor$
|
|
4816
|
+
descriptor: descriptor$14,
|
|
4405
4817
|
handler: handler$7
|
|
4406
4818
|
},
|
|
4407
4819
|
{
|
|
4408
|
-
descriptor: descriptor$
|
|
4820
|
+
descriptor: descriptor$13,
|
|
4409
4821
|
handler: handler$6
|
|
4410
4822
|
},
|
|
4411
4823
|
{
|
|
4412
|
-
descriptor: descriptor$
|
|
4824
|
+
descriptor: descriptor$12,
|
|
4413
4825
|
handler: handler$5
|
|
4414
4826
|
},
|
|
4415
4827
|
{
|
|
4416
|
-
descriptor: descriptor$
|
|
4828
|
+
descriptor: descriptor$11,
|
|
4417
4829
|
handler: handler$4
|
|
4418
4830
|
},
|
|
4419
4831
|
{
|
|
4420
|
-
descriptor: descriptor$
|
|
4832
|
+
descriptor: descriptor$10,
|
|
4421
4833
|
handler: handler$3
|
|
4422
4834
|
},
|
|
4423
4835
|
{
|
|
4424
|
-
descriptor: descriptor$
|
|
4836
|
+
descriptor: descriptor$9,
|
|
4425
4837
|
handler: handler$2
|
|
4426
4838
|
},
|
|
4427
4839
|
{
|
|
4428
|
-
descriptor: descriptor$
|
|
4840
|
+
descriptor: descriptor$8,
|
|
4429
4841
|
handler: handler$1
|
|
4430
4842
|
},
|
|
4431
4843
|
{
|
|
4432
|
-
descriptor: descriptor$
|
|
4844
|
+
descriptor: descriptor$7,
|
|
4433
4845
|
handler
|
|
4434
4846
|
}
|
|
4435
4847
|
];
|
|
@@ -5002,6 +5414,6 @@ function redactSecrets(text) {
|
|
|
5002
5414
|
return result;
|
|
5003
5415
|
}
|
|
5004
5416
|
//#endregion
|
|
5005
|
-
export { SAFETY_LABELS, buildToolPlan, classifySensitivity as classifyPath, createMcpAuthHandler, createMemoryWriteHandler, createSendMessageHandler, createTaskHandler, createToolRegistry, escalateForSensitivePaths, evaluateAvailability, injectTaskManager, isBlockedCommand, isHeadlessSafe, isTrustedCommand, isPathSafe as isWithinWorkspace, listBuiltinDescriptors, descriptor as
|
|
5417
|
+
export { SAFETY_LABELS, buildToolPlan, classifySensitivity as classifyPath, createListMcpResourcesHandler, createMcpAuthHandler, createMcpCallToolHandler, createMemoryWriteHandler, createReadMcpResourceHandler, createSendMessageHandler, createTaskHandler, createToolRegistry, createToolSearchHandler, escalateForSensitivePaths, evaluateAvailability, injectTaskManager, isBlockedCommand, isHeadlessSafe, isTrustedCommand, isPathSafe as isWithinWorkspace, listBuiltinDescriptors, descriptor as listMcpResourcesDescriptor, descriptor$1 as mcpAuthDescriptor, descriptor$2 as mcpCallToolDescriptor, descriptor$3 as memoryWriteDescriptor, needsApproval, descriptor$4 as readMcpResourceDescriptor, redactSecrets, registerBuiltinTools, sanitizeUnicode, descriptor$5 as sendMessageDescriptor, descriptor$6 as toolSearchDescriptor };
|
|
5006
5418
|
|
|
5007
5419
|
//# sourceMappingURL=index.mjs.map
|