@tachu/extensions 1.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +119 -0
- package/LICENSE +201 -0
- package/README.md +1104 -0
- package/README_ZH.md +1082 -0
- package/dist/backends/file.d.ts +18 -0
- package/dist/backends/file.d.ts.map +1 -0
- package/dist/backends/file.js +85 -0
- package/dist/backends/file.js.map +1 -0
- package/dist/backends/index.d.ts +4 -0
- package/dist/backends/index.d.ts.map +1 -0
- package/dist/backends/index.js +4 -0
- package/dist/backends/index.js.map +1 -0
- package/dist/backends/terminal.d.ts +18 -0
- package/dist/backends/terminal.d.ts.map +1 -0
- package/dist/backends/terminal.js +81 -0
- package/dist/backends/terminal.js.map +1 -0
- package/dist/backends/web.d.ts +18 -0
- package/dist/backends/web.d.ts.map +1 -0
- package/dist/backends/web.js +55 -0
- package/dist/backends/web.js.map +1 -0
- package/dist/common/net.d.ts +39 -0
- package/dist/common/net.d.ts.map +1 -0
- package/dist/common/net.js +177 -0
- package/dist/common/net.js.map +1 -0
- package/dist/common/path.d.ts +51 -0
- package/dist/common/path.d.ts.map +1 -0
- package/dist/common/path.js +76 -0
- package/dist/common/path.js.map +1 -0
- package/dist/common/process.d.ts +19 -0
- package/dist/common/process.d.ts.map +1 -0
- package/dist/common/process.js +67 -0
- package/dist/common/process.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +3 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/sse-adapter.d.ts +82 -0
- package/dist/mcp/sse-adapter.d.ts.map +1 -0
- package/dist/mcp/sse-adapter.js +201 -0
- package/dist/mcp/sse-adapter.js.map +1 -0
- package/dist/mcp/stdio-adapter.d.ts +85 -0
- package/dist/mcp/stdio-adapter.d.ts.map +1 -0
- package/dist/mcp/stdio-adapter.js +203 -0
- package/dist/mcp/stdio-adapter.js.map +1 -0
- package/dist/memory/fs-memory-system.d.ts +147 -0
- package/dist/memory/fs-memory-system.d.ts.map +1 -0
- package/dist/memory/fs-memory-system.js +266 -0
- package/dist/memory/fs-memory-system.js.map +1 -0
- package/dist/memory/index.d.ts +2 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +2 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/observability/index.d.ts +3 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +3 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/jsonl-emitter.d.ts +58 -0
- package/dist/observability/jsonl-emitter.d.ts.map +1 -0
- package/dist/observability/jsonl-emitter.js +96 -0
- package/dist/observability/jsonl-emitter.js.map +1 -0
- package/dist/observability/otel-emitter.d.ts +52 -0
- package/dist/observability/otel-emitter.d.ts.map +1 -0
- package/dist/observability/otel-emitter.js +143 -0
- package/dist/observability/otel-emitter.js.map +1 -0
- package/dist/providers/anthropic.d.ts +73 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +521 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +5 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/mock.d.ts +81 -0
- package/dist/providers/mock.d.ts.map +1 -0
- package/dist/providers/mock.js +160 -0
- package/dist/providers/mock.js.map +1 -0
- package/dist/providers/openai.d.ts +95 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +529 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/qwen.d.ts +145 -0
- package/dist/providers/qwen.d.ts.map +1 -0
- package/dist/providers/qwen.js +669 -0
- package/dist/providers/qwen.js.map +1 -0
- package/dist/rules/index.d.ts +9 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +15 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/no-hallucination.md +11 -0
- package/dist/rules/no-sensitive-output.md +11 -0
- package/dist/rules/prefer-concise-response.md +11 -0
- package/dist/rules/require-tool-verification.md +11 -0
- package/dist/safety/default-gate.d.ts +112 -0
- package/dist/safety/default-gate.d.ts.map +1 -0
- package/dist/safety/default-gate.js +188 -0
- package/dist/safety/default-gate.js.map +1 -0
- package/dist/safety/index.d.ts +2 -0
- package/dist/safety/index.d.ts.map +1 -0
- package/dist/safety/index.js +2 -0
- package/dist/safety/index.js.map +1 -0
- package/dist/tools/_shared/web-client.d.ts +18 -0
- package/dist/tools/_shared/web-client.d.ts.map +1 -0
- package/dist/tools/_shared/web-client.js +46 -0
- package/dist/tools/_shared/web-client.js.map +1 -0
- package/dist/tools/apply-patch/descriptor.md +27 -0
- package/dist/tools/apply-patch/executor.d.ts +19 -0
- package/dist/tools/apply-patch/executor.d.ts.map +1 -0
- package/dist/tools/apply-patch/executor.js +193 -0
- package/dist/tools/apply-patch/executor.js.map +1 -0
- package/dist/tools/fetch-url/descriptor.md +44 -0
- package/dist/tools/fetch-url/executor.d.ts +28 -0
- package/dist/tools/fetch-url/executor.d.ts.map +1 -0
- package/dist/tools/fetch-url/executor.js +115 -0
- package/dist/tools/fetch-url/executor.js.map +1 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +286 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-dir/descriptor.md +29 -0
- package/dist/tools/list-dir/executor.d.ts +22 -0
- package/dist/tools/list-dir/executor.d.ts.map +1 -0
- package/dist/tools/list-dir/executor.js +48 -0
- package/dist/tools/list-dir/executor.js.map +1 -0
- package/dist/tools/read-file/descriptor.md +28 -0
- package/dist/tools/read-file/executor.d.ts +15 -0
- package/dist/tools/read-file/executor.d.ts.map +1 -0
- package/dist/tools/read-file/executor.js +22 -0
- package/dist/tools/read-file/executor.js.map +1 -0
- package/dist/tools/run-shell/descriptor.md +39 -0
- package/dist/tools/run-shell/executor.d.ts +20 -0
- package/dist/tools/run-shell/executor.d.ts.map +1 -0
- package/dist/tools/run-shell/executor.js +76 -0
- package/dist/tools/run-shell/executor.js.map +1 -0
- package/dist/tools/search-code/descriptor.md +31 -0
- package/dist/tools/search-code/executor.d.ts +23 -0
- package/dist/tools/search-code/executor.d.ts.map +1 -0
- package/dist/tools/search-code/executor.js +122 -0
- package/dist/tools/search-code/executor.js.map +1 -0
- package/dist/tools/shared.d.ts +47 -0
- package/dist/tools/shared.d.ts.map +1 -0
- package/dist/tools/shared.js +27 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/tools/web-fetch/descriptor.md +198 -0
- package/dist/tools/web-fetch/errors.d.ts +32 -0
- package/dist/tools/web-fetch/errors.d.ts.map +1 -0
- package/dist/tools/web-fetch/errors.js +91 -0
- package/dist/tools/web-fetch/errors.js.map +1 -0
- package/dist/tools/web-fetch/executor.d.ts +10 -0
- package/dist/tools/web-fetch/executor.d.ts.map +1 -0
- package/dist/tools/web-fetch/executor.js +191 -0
- package/dist/tools/web-fetch/executor.js.map +1 -0
- package/dist/tools/web-fetch/index.d.ts +4 -0
- package/dist/tools/web-fetch/index.d.ts.map +1 -0
- package/dist/tools/web-fetch/index.js +3 -0
- package/dist/tools/web-fetch/index.js.map +1 -0
- package/dist/tools/web-fetch/types.d.ts +157 -0
- package/dist/tools/web-fetch/types.d.ts.map +1 -0
- package/dist/tools/web-fetch/types.js +7 -0
- package/dist/tools/web-fetch/types.js.map +1 -0
- package/dist/tools/web-search/descriptor.md +89 -0
- package/dist/tools/web-search/errors.d.ts +33 -0
- package/dist/tools/web-search/errors.d.ts.map +1 -0
- package/dist/tools/web-search/errors.js +45 -0
- package/dist/tools/web-search/errors.js.map +1 -0
- package/dist/tools/web-search/executor.d.ts +10 -0
- package/dist/tools/web-search/executor.d.ts.map +1 -0
- package/dist/tools/web-search/executor.js +185 -0
- package/dist/tools/web-search/executor.js.map +1 -0
- package/dist/tools/web-search/index.d.ts +4 -0
- package/dist/tools/web-search/index.d.ts.map +1 -0
- package/dist/tools/web-search/index.js +3 -0
- package/dist/tools/web-search/index.js.map +1 -0
- package/dist/tools/web-search/types.d.ts +86 -0
- package/dist/tools/web-search/types.d.ts.map +1 -0
- package/dist/tools/web-search/types.js +7 -0
- package/dist/tools/web-search/types.js.map +1 -0
- package/dist/tools/write-file/descriptor.md +30 -0
- package/dist/tools/write-file/executor.d.ts +16 -0
- package/dist/tools/write-file/executor.d.ts.map +1 -0
- package/dist/tools/write-file/executor.js +18 -0
- package/dist/tools/write-file/executor.js.map +1 -0
- package/dist/transformers/document-to-text.d.ts +23 -0
- package/dist/transformers/document-to-text.d.ts.map +1 -0
- package/dist/transformers/document-to-text.js +69 -0
- package/dist/transformers/document-to-text.js.map +1 -0
- package/dist/transformers/image-to-text.d.ts +38 -0
- package/dist/transformers/image-to-text.d.ts.map +1 -0
- package/dist/transformers/image-to-text.js +82 -0
- package/dist/transformers/image-to-text.js.map +1 -0
- package/dist/transformers/index.d.ts +3 -0
- package/dist/transformers/index.d.ts.map +1 -0
- package/dist/transformers/index.js +3 -0
- package/dist/transformers/index.js.map +1 -0
- package/dist/vector/index.d.ts +3 -0
- package/dist/vector/index.d.ts.map +1 -0
- package/dist/vector/index.js +3 -0
- package/dist/vector/index.js.map +1 -0
- package/dist/vector/local-fs.d.ts +76 -0
- package/dist/vector/local-fs.d.ts.map +1 -0
- package/dist/vector/local-fs.js +153 -0
- package/dist/vector/local-fs.js.map +1 -0
- package/dist/vector/qdrant.d.ts +85 -0
- package/dist/vector/qdrant.d.ts.map +1 -0
- package/dist/vector/qdrant.js +208 -0
- package/dist/vector/qdrant.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BackendInput, BackendOutput, ExecutionBackend, ExecutionContext, ExecutionTraits } from "@tachu/core";
|
|
2
|
+
/**
|
|
3
|
+
* 文件系统执行后端。
|
|
4
|
+
*/
|
|
5
|
+
export declare class FileBackend implements ExecutionBackend {
|
|
6
|
+
readonly name = "file";
|
|
7
|
+
readonly kind: "file";
|
|
8
|
+
readonly traits: ExecutionTraits;
|
|
9
|
+
/**
|
|
10
|
+
* 执行文件操作。
|
|
11
|
+
*
|
|
12
|
+
* @param input 后端输入
|
|
13
|
+
* @param context 执行上下文
|
|
14
|
+
* @returns 后端输出
|
|
15
|
+
*/
|
|
16
|
+
execute(input: BackendInput, context: ExecutionContext): Promise<BackendOutput>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/backends/file.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EAChB,MAAM,aAAa,CAAC;AAerB;;GAEG;AACH,qBAAa,WAAY,YAAW,gBAAgB;IAClD,QAAQ,CAAC,IAAI,UAAU;IACvB,QAAQ,CAAC,IAAI,EAAG,MAAM,CAAU;IAChC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAK9B;IAEF;;;;;;OAMG;IACG,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC;CA0DtF"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { copyFile, mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { ValidationError } from "@tachu/core";
|
|
4
|
+
/**
|
|
5
|
+
* 如果 `context.abortSignal` 已触发,就立即抛出;否则 no-op。
|
|
6
|
+
*
|
|
7
|
+
* D1-LOW-11:Node fs 的 Promises API 并非每个方法都支持 `signal`,改用显式检查
|
|
8
|
+
* 在 backend 入口以及每个分支前完成"响应外部取消"的语义。
|
|
9
|
+
*/
|
|
10
|
+
const assertNotAborted = (context) => {
|
|
11
|
+
if (context.abortSignal?.aborted) {
|
|
12
|
+
throw context.abortSignal.reason ?? new Error("file backend aborted");
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* 文件系统执行后端。
|
|
17
|
+
*/
|
|
18
|
+
export class FileBackend {
|
|
19
|
+
name = "file";
|
|
20
|
+
kind = "file";
|
|
21
|
+
traits = {
|
|
22
|
+
sideEffect: "write",
|
|
23
|
+
idempotent: false,
|
|
24
|
+
requiresApproval: true,
|
|
25
|
+
timeout: 30_000,
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* 执行文件操作。
|
|
29
|
+
*
|
|
30
|
+
* @param input 后端输入
|
|
31
|
+
* @param context 执行上下文
|
|
32
|
+
* @returns 后端输出
|
|
33
|
+
*/
|
|
34
|
+
async execute(input, context) {
|
|
35
|
+
const payload = input.payload;
|
|
36
|
+
if (!payload.operation || !payload.path) {
|
|
37
|
+
throw new ValidationError("VALIDATION_FILE_OPERATION", "file backend 缺少 operation/path");
|
|
38
|
+
}
|
|
39
|
+
assertNotAborted(context);
|
|
40
|
+
switch (payload.operation) {
|
|
41
|
+
case "read": {
|
|
42
|
+
const data = await readFile(payload.path, payload.encoding ?? "utf8");
|
|
43
|
+
assertNotAborted(context);
|
|
44
|
+
return { success: true, result: { content: data, traceId: context.traceId } };
|
|
45
|
+
}
|
|
46
|
+
case "write": {
|
|
47
|
+
if (typeof payload.content !== "string") {
|
|
48
|
+
throw new ValidationError("VALIDATION_FILE_OPERATION", "write 操作缺少 content");
|
|
49
|
+
}
|
|
50
|
+
if (payload.createDirs) {
|
|
51
|
+
await mkdir(dirname(payload.path), { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
assertNotAborted(context);
|
|
54
|
+
await writeFile(payload.path, payload.content, payload.encoding ?? "utf8");
|
|
55
|
+
const fileStat = await stat(payload.path);
|
|
56
|
+
return { success: true, result: { bytesWritten: fileStat.size, traceId: context.traceId } };
|
|
57
|
+
}
|
|
58
|
+
case "delete": {
|
|
59
|
+
await rm(payload.path, { force: true, recursive: true });
|
|
60
|
+
return { success: true, result: { deleted: true, traceId: context.traceId } };
|
|
61
|
+
}
|
|
62
|
+
case "move": {
|
|
63
|
+
if (!payload.to) {
|
|
64
|
+
throw new ValidationError("VALIDATION_FILE_OPERATION", "move 操作缺少 to");
|
|
65
|
+
}
|
|
66
|
+
await mkdir(dirname(payload.to), { recursive: true });
|
|
67
|
+
assertNotAborted(context);
|
|
68
|
+
await rename(payload.path, payload.to);
|
|
69
|
+
return { success: true, result: { moved: true, traceId: context.traceId } };
|
|
70
|
+
}
|
|
71
|
+
case "copy": {
|
|
72
|
+
if (!payload.to) {
|
|
73
|
+
throw new ValidationError("VALIDATION_FILE_OPERATION", "copy 操作缺少 to");
|
|
74
|
+
}
|
|
75
|
+
await mkdir(dirname(payload.to), { recursive: true });
|
|
76
|
+
assertNotAborted(context);
|
|
77
|
+
await copyFile(payload.path, payload.to);
|
|
78
|
+
return { success: true, result: { copied: true, traceId: context.traceId } };
|
|
79
|
+
}
|
|
80
|
+
default:
|
|
81
|
+
throw new ValidationError("VALIDATION_FILE_OPERATION", `未知文件操作: ${payload.operation}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/backends/file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backends/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backends/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BackendInput, BackendOutput, ExecutionBackend, ExecutionTraits, ExecutionContext } from "@tachu/core";
|
|
2
|
+
/**
|
|
3
|
+
* 终端执行后端。
|
|
4
|
+
*/
|
|
5
|
+
export declare class TerminalBackend implements ExecutionBackend {
|
|
6
|
+
readonly name = "terminal";
|
|
7
|
+
readonly kind: "terminal";
|
|
8
|
+
readonly traits: ExecutionTraits;
|
|
9
|
+
/**
|
|
10
|
+
* 执行终端命令。
|
|
11
|
+
*
|
|
12
|
+
* @param input 后端输入
|
|
13
|
+
* @param context 执行上下文
|
|
14
|
+
* @returns 后端输出
|
|
15
|
+
*/
|
|
16
|
+
execute(input: BackendInput, context: ExecutionContext): Promise<BackendOutput>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=terminal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/backends/terminal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAMrB;;GAEG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IACtD,QAAQ,CAAC,IAAI,cAAc;IAC3B,QAAQ,CAAC,IAAI,EAAG,UAAU,CAAU;IACpC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAK9B;IAEF;;;;;;OAMG;IACG,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC;CAqEtF"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ValidationError } from "@tachu/core";
|
|
2
|
+
import { readStreamWithLimit, terminateProcess } from "../common/process";
|
|
3
|
+
const OUTPUT_LIMIT_BYTES = 1024 * 1024;
|
|
4
|
+
/**
|
|
5
|
+
* 终端执行后端。
|
|
6
|
+
*/
|
|
7
|
+
export class TerminalBackend {
|
|
8
|
+
name = "terminal";
|
|
9
|
+
kind = "terminal";
|
|
10
|
+
traits = {
|
|
11
|
+
sideEffect: "irreversible",
|
|
12
|
+
idempotent: false,
|
|
13
|
+
requiresApproval: true,
|
|
14
|
+
timeout: 60_000,
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* 执行终端命令。
|
|
18
|
+
*
|
|
19
|
+
* @param input 后端输入
|
|
20
|
+
* @param context 执行上下文
|
|
21
|
+
* @returns 后端输出
|
|
22
|
+
*/
|
|
23
|
+
async execute(input, context) {
|
|
24
|
+
const payload = input.payload;
|
|
25
|
+
if (!payload.command) {
|
|
26
|
+
throw new ValidationError("VALIDATION_EMPTY_COMMAND", "terminal backend command 不能为空");
|
|
27
|
+
}
|
|
28
|
+
// D1-LOW-11:尊重宿主注入的 `context.abortSignal`。即便主干传入一个已取消的
|
|
29
|
+
// 信号,也直接返回失败,不再产生真实子进程,避免 "先 spawn 再 kill" 的开销。
|
|
30
|
+
if (context.abortSignal?.aborted) {
|
|
31
|
+
const reason = context.abortSignal.reason instanceof Error
|
|
32
|
+
? context.abortSignal.reason.message
|
|
33
|
+
: String(context.abortSignal.reason ?? "aborted");
|
|
34
|
+
return {
|
|
35
|
+
success: false,
|
|
36
|
+
result: {
|
|
37
|
+
stdout: "",
|
|
38
|
+
stderr: `terminal backend aborted before spawn: ${reason}`,
|
|
39
|
+
exitCode: -1,
|
|
40
|
+
traceId: context.traceId,
|
|
41
|
+
aborted: true,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const processRef = Bun.spawn({
|
|
46
|
+
cmd: [payload.command, ...(payload.args ?? [])],
|
|
47
|
+
...(payload.cwd ? { cwd: payload.cwd } : {}),
|
|
48
|
+
stdout: "pipe",
|
|
49
|
+
stderr: "pipe",
|
|
50
|
+
env: process.env,
|
|
51
|
+
});
|
|
52
|
+
const onExternalAbort = () => {
|
|
53
|
+
if (processRef.pid) {
|
|
54
|
+
void terminateProcess(processRef.pid);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
context.abortSignal?.addEventListener("abort", onExternalAbort, { once: true });
|
|
58
|
+
const timeoutId = setTimeout(() => {
|
|
59
|
+
if (processRef.pid) {
|
|
60
|
+
void terminateProcess(processRef.pid);
|
|
61
|
+
}
|
|
62
|
+
}, payload.timeoutMs ?? this.traits.timeout);
|
|
63
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
64
|
+
readStreamWithLimit(processRef.stdout, OUTPUT_LIMIT_BYTES),
|
|
65
|
+
readStreamWithLimit(processRef.stderr, OUTPUT_LIMIT_BYTES),
|
|
66
|
+
processRef.exited,
|
|
67
|
+
]);
|
|
68
|
+
clearTimeout(timeoutId);
|
|
69
|
+
context.abortSignal?.removeEventListener("abort", onExternalAbort);
|
|
70
|
+
return {
|
|
71
|
+
success: exitCode === 0,
|
|
72
|
+
result: {
|
|
73
|
+
stdout: stdout.text,
|
|
74
|
+
stderr: stderr.text,
|
|
75
|
+
exitCode,
|
|
76
|
+
traceId: context.traceId,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/backends/terminal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BackendInput, BackendOutput, ExecutionBackend, ExecutionContext, ExecutionTraits } from "@tachu/core";
|
|
2
|
+
/**
|
|
3
|
+
* Web 请求执行后端。
|
|
4
|
+
*/
|
|
5
|
+
export declare class WebBackend implements ExecutionBackend {
|
|
6
|
+
readonly name = "web";
|
|
7
|
+
readonly kind: "web";
|
|
8
|
+
readonly traits: ExecutionTraits;
|
|
9
|
+
/**
|
|
10
|
+
* 执行 HTTP 请求。
|
|
11
|
+
*
|
|
12
|
+
* @param input 后端输入
|
|
13
|
+
* @param context 执行上下文
|
|
14
|
+
* @returns 后端输出
|
|
15
|
+
*/
|
|
16
|
+
execute(input: BackendInput, context: ExecutionContext): Promise<BackendOutput>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=web.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/backends/web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EAChB,MAAM,aAAa,CAAC;AAMrB;;GAEG;AACH,qBAAa,UAAW,YAAW,gBAAgB;IACjD,QAAQ,CAAC,IAAI,SAAS;IACtB,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAK9B;IAEF;;;;;;OAMG;IACG,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC;CAyCtF"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ValidationError } from "@tachu/core";
|
|
2
|
+
import { assertPublicUrl, readResponseBodyWithLimit, withAbortTimeout } from "../common/net";
|
|
3
|
+
const MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
4
|
+
/**
|
|
5
|
+
* Web 请求执行后端。
|
|
6
|
+
*/
|
|
7
|
+
export class WebBackend {
|
|
8
|
+
name = "web";
|
|
9
|
+
kind = "web";
|
|
10
|
+
traits = {
|
|
11
|
+
sideEffect: "readonly",
|
|
12
|
+
idempotent: false,
|
|
13
|
+
requiresApproval: false,
|
|
14
|
+
timeout: 30_000,
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* 执行 HTTP 请求。
|
|
18
|
+
*
|
|
19
|
+
* @param input 后端输入
|
|
20
|
+
* @param context 执行上下文
|
|
21
|
+
* @returns 后端输出
|
|
22
|
+
*/
|
|
23
|
+
async execute(input, context) {
|
|
24
|
+
const payload = input.payload;
|
|
25
|
+
if (!payload.url) {
|
|
26
|
+
throw new ValidationError("VALIDATION_INVALID_URL", "web backend 缺少 url");
|
|
27
|
+
}
|
|
28
|
+
const target = await assertPublicUrl(payload.url);
|
|
29
|
+
// D1-LOW-11:把宿主注入的 `context.abortSignal` 与本地超时信号合并,外部取消
|
|
30
|
+
// 能立即中断下游 fetch 请求,不必等到本 backend 的内建超时到期。
|
|
31
|
+
const timeout = withAbortTimeout(context.abortSignal, payload.timeoutMs ?? this.traits.timeout, "TIMEOUT_WEB_BACKEND");
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetch(target, {
|
|
34
|
+
method: payload.method ?? "GET",
|
|
35
|
+
headers: payload.headers,
|
|
36
|
+
body: payload.body,
|
|
37
|
+
signal: timeout.signal,
|
|
38
|
+
});
|
|
39
|
+
const body = await readResponseBodyWithLimit(response, MAX_RESPONSE_BYTES);
|
|
40
|
+
return {
|
|
41
|
+
success: response.ok,
|
|
42
|
+
result: {
|
|
43
|
+
status: response.status,
|
|
44
|
+
body: body.body,
|
|
45
|
+
truncated: body.truncated,
|
|
46
|
+
traceId: context.traceId,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
timeout.cleanup();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=web.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/backends/web.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE7F,MAAM,kBAAkB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3C;;GAEG;AACH,MAAM,OAAO,UAAU;IACZ,IAAI,GAAG,KAAK,CAAC;IACb,IAAI,GAAG,KAAc,CAAC;IACtB,MAAM,GAAoB;QACjC,UAAU,EAAE,UAAU;QACtB,UAAU,EAAE,KAAK;QACjB,gBAAgB,EAAE,KAAK;QACvB,OAAO,EAAE,MAAM;KAChB,CAAC;IAEF;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,KAAmB,EAAE,OAAyB;QAC1D,MAAM,OAAO,GAAG,KAAK,CAAC,OAMrB,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACjB,MAAM,IAAI,eAAe,CAAC,wBAAwB,EAAE,oBAAoB,CAAC,CAAC;QAC5E,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAClD,wDAAwD;QACxD,0CAA0C;QAC1C,MAAM,OAAO,GAAG,gBAAgB,CAC9B,OAAO,CAAC,WAAW,EACnB,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EACxC,qBAAqB,CACtB,CAAC;QACF,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE;gBACnC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;gBAC/B,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,yBAAyB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;YAC3E,OAAO;gBACL,OAAO,EAAE,QAAQ,CAAC,EAAE;gBACpB,MAAM,EAAE;oBACN,MAAM,EAAE,QAAQ,CAAC,MAAM;oBACvB,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,OAAO,EAAE,OAAO,CAAC,OAAO;iBACzB;aACF,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置网络安全开关。仅供集成测试在启动本地 HTTP 服务时临时放行 127.0.0.1 / localhost。
|
|
3
|
+
*
|
|
4
|
+
* 生产代码不应调用本函数。调用后请在用例结束时显式还原为 `false`,避免污染其它测试。
|
|
5
|
+
*/
|
|
6
|
+
export declare const configureNetSafety: (opts: {
|
|
7
|
+
allowLoopbackForTests?: boolean;
|
|
8
|
+
}) => void;
|
|
9
|
+
/**
|
|
10
|
+
* 检查 URL 是否指向私网地址。
|
|
11
|
+
*
|
|
12
|
+
* @param input 待检查 URL
|
|
13
|
+
* @throws SafetyError 当 URL 指向私网/本机地址时抛出
|
|
14
|
+
*/
|
|
15
|
+
export declare const assertPublicUrl: (input: string) => Promise<URL>;
|
|
16
|
+
/**
|
|
17
|
+
* 组合外部取消信号与超时控制。
|
|
18
|
+
*
|
|
19
|
+
* @param signal 外部取消信号
|
|
20
|
+
* @param timeoutMs 超时时间(毫秒)
|
|
21
|
+
* @param timeoutCode 超时错误码
|
|
22
|
+
* @returns 合并信号与清理函数
|
|
23
|
+
*/
|
|
24
|
+
export declare const withAbortTimeout: (signal: AbortSignal | undefined, timeoutMs: number, timeoutCode?: string) => {
|
|
25
|
+
signal: AbortSignal;
|
|
26
|
+
cleanup: () => void;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* 读取响应体并按最大字节数截断。
|
|
30
|
+
*
|
|
31
|
+
* @param response Fetch 响应
|
|
32
|
+
* @param maxBytes 最大字节数
|
|
33
|
+
* @returns 文本内容和截断标识
|
|
34
|
+
*/
|
|
35
|
+
export declare const readResponseBodyWithLimit: (response: Response, maxBytes: number) => Promise<{
|
|
36
|
+
body: string;
|
|
37
|
+
truncated: boolean;
|
|
38
|
+
}>;
|
|
39
|
+
//# sourceMappingURL=net.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"net.d.ts","sourceRoot":"","sources":["../../src/common/net.ts"],"names":[],"mappings":"AAiBA;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM;IACvC,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,KAAG,IAIH,CAAC;AAyCF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAU,OAAO,MAAM,KAAG,OAAO,CAAC,GAAG,CAgDhE,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,WAAW,GAAG,SAAS,EAC/B,WAAW,MAAM,EACjB,oBAAwC,KACvC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAsB5C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GACpC,UAAU,QAAQ,EAClB,UAAU,MAAM,KACf,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAwC9C,CAAC"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { lookup } from "node:dns/promises";
|
|
2
|
+
import { isIP } from "node:net";
|
|
3
|
+
import { SafetyError, TimeoutError } from "@tachu/core";
|
|
4
|
+
const LOCAL_HOSTNAMES = new Set(["localhost", "localhost.localdomain"]);
|
|
5
|
+
const netSafety = {
|
|
6
|
+
allowLoopbackForTests: false,
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* 配置网络安全开关。仅供集成测试在启动本地 HTTP 服务时临时放行 127.0.0.1 / localhost。
|
|
10
|
+
*
|
|
11
|
+
* 生产代码不应调用本函数。调用后请在用例结束时显式还原为 `false`,避免污染其它测试。
|
|
12
|
+
*/
|
|
13
|
+
export const configureNetSafety = (opts) => {
|
|
14
|
+
if (typeof opts.allowLoopbackForTests === "boolean") {
|
|
15
|
+
netSafety.allowLoopbackForTests = opts.allowLoopbackForTests;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const isPrivateIpv4 = (ip) => {
|
|
19
|
+
const [aRaw, bRaw] = ip.split(".");
|
|
20
|
+
const a = Number(aRaw);
|
|
21
|
+
const b = Number(bRaw);
|
|
22
|
+
if (Number.isNaN(a) || Number.isNaN(b)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
if (a === 10 || a === 127) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
if (a === 192 && b === 168) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
if (a === 172 && b >= 16 && b <= 31) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
if (a === 169 && b === 254) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
if (a === 0) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
};
|
|
42
|
+
const isPrivateIpv6 = (ip) => {
|
|
43
|
+
const lower = ip.toLowerCase();
|
|
44
|
+
if (lower === "::1") {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (lower.startsWith("fc") || lower.startsWith("fd")) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
if (lower.startsWith("fe80")) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* 检查 URL 是否指向私网地址。
|
|
57
|
+
*
|
|
58
|
+
* @param input 待检查 URL
|
|
59
|
+
* @throws SafetyError 当 URL 指向私网/本机地址时抛出
|
|
60
|
+
*/
|
|
61
|
+
export const assertPublicUrl = async (input) => {
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = new URL(input);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw new SafetyError("SAFETY_INVALID_URL", `URL 无效: ${input}`, { cause: error });
|
|
68
|
+
}
|
|
69
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
70
|
+
throw new SafetyError("SAFETY_PROTOCOL_NOT_ALLOWED", `协议不允许: ${parsed.protocol}`);
|
|
71
|
+
}
|
|
72
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
73
|
+
if (!netSafety.allowLoopbackForTests) {
|
|
74
|
+
if (LOCAL_HOSTNAMES.has(hostname) || hostname.endsWith(".local")) {
|
|
75
|
+
throw new SafetyError("SAFETY_PRIVATE_NETWORK_BLOCKED", `已阻止私网地址: ${hostname}`, {
|
|
76
|
+
context: { hostname },
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const literalIpFamily = isIP(hostname);
|
|
80
|
+
if (literalIpFamily === 4 && isPrivateIpv4(hostname)) {
|
|
81
|
+
throw new SafetyError("SAFETY_PRIVATE_NETWORK_BLOCKED", `已阻止私网地址: ${hostname}`, {
|
|
82
|
+
context: { hostname },
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (literalIpFamily === 6 && isPrivateIpv6(hostname)) {
|
|
86
|
+
throw new SafetyError("SAFETY_PRIVATE_NETWORK_BLOCKED", `已阻止私网地址: ${hostname}`, {
|
|
87
|
+
context: { hostname },
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (literalIpFamily === 0) {
|
|
91
|
+
const records = await lookup(hostname, { all: true });
|
|
92
|
+
for (const record of records) {
|
|
93
|
+
if ((record.family === 4 && isPrivateIpv4(record.address)) ||
|
|
94
|
+
(record.family === 6 && isPrivateIpv6(record.address))) {
|
|
95
|
+
throw new SafetyError("SAFETY_PRIVATE_NETWORK_BLOCKED", `已阻止私网地址: ${hostname}`, {
|
|
96
|
+
context: { hostname, resolved: records.map((item) => item.address) },
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return parsed;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* 组合外部取消信号与超时控制。
|
|
106
|
+
*
|
|
107
|
+
* @param signal 外部取消信号
|
|
108
|
+
* @param timeoutMs 超时时间(毫秒)
|
|
109
|
+
* @param timeoutCode 超时错误码
|
|
110
|
+
* @returns 合并信号与清理函数
|
|
111
|
+
*/
|
|
112
|
+
export const withAbortTimeout = (signal, timeoutMs, timeoutCode = "TIMEOUT_PROVIDER_REQUEST") => {
|
|
113
|
+
const controller = new AbortController();
|
|
114
|
+
let timeoutId;
|
|
115
|
+
const onAbort = () => {
|
|
116
|
+
controller.abort(signal?.reason ?? new Error("aborted"));
|
|
117
|
+
};
|
|
118
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
119
|
+
timeoutId = setTimeout(() => {
|
|
120
|
+
controller.abort(new TimeoutError(timeoutCode, `请求超时: ${timeoutMs}ms`, { retryable: true }));
|
|
121
|
+
}, timeoutMs);
|
|
122
|
+
return {
|
|
123
|
+
signal: controller.signal,
|
|
124
|
+
cleanup: () => {
|
|
125
|
+
if (timeoutId) {
|
|
126
|
+
clearTimeout(timeoutId);
|
|
127
|
+
}
|
|
128
|
+
signal?.removeEventListener("abort", onAbort);
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* 读取响应体并按最大字节数截断。
|
|
134
|
+
*
|
|
135
|
+
* @param response Fetch 响应
|
|
136
|
+
* @param maxBytes 最大字节数
|
|
137
|
+
* @returns 文本内容和截断标识
|
|
138
|
+
*/
|
|
139
|
+
export const readResponseBodyWithLimit = async (response, maxBytes) => {
|
|
140
|
+
const reader = response.body?.getReader();
|
|
141
|
+
if (!reader) {
|
|
142
|
+
return { body: "", truncated: false };
|
|
143
|
+
}
|
|
144
|
+
const chunks = [];
|
|
145
|
+
let total = 0;
|
|
146
|
+
let truncated = false;
|
|
147
|
+
while (true) {
|
|
148
|
+
const { done, value } = await reader.read();
|
|
149
|
+
if (done) {
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
if (!value) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const room = maxBytes - total;
|
|
156
|
+
if (room <= 0) {
|
|
157
|
+
truncated = true;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
if (value.byteLength > room) {
|
|
161
|
+
chunks.push(value.subarray(0, room));
|
|
162
|
+
total += room;
|
|
163
|
+
truncated = true;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
chunks.push(value);
|
|
167
|
+
total += value.byteLength;
|
|
168
|
+
}
|
|
169
|
+
const merged = new Uint8Array(total);
|
|
170
|
+
let offset = 0;
|
|
171
|
+
for (const chunk of chunks) {
|
|
172
|
+
merged.set(chunk, offset);
|
|
173
|
+
offset += chunk.byteLength;
|
|
174
|
+
}
|
|
175
|
+
return { body: new TextDecoder().decode(merged), truncated };
|
|
176
|
+
};
|
|
177
|
+
//# sourceMappingURL=net.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"net.js","sourceRoot":"","sources":["../../src/common/net.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 解析一条工具输入路径的可选上下文。
|
|
3
|
+
*/
|
|
4
|
+
export interface ResolveAllowedPathOptions {
|
|
5
|
+
/**
|
|
6
|
+
* 允许访问的根目录列表(绝对路径)。相对路径会以第一个根为基准展开。
|
|
7
|
+
* 至少包含一个;通常是 `[workspaceRoot, ...extraRoots]`。
|
|
8
|
+
*/
|
|
9
|
+
allowedRoots: readonly string[];
|
|
10
|
+
/**
|
|
11
|
+
* 本次调用是否已经通过用户审批(见 `@tachu/core` 的 `onBeforeToolCall`)。
|
|
12
|
+
*
|
|
13
|
+
* 一旦为 `true`,路径校验会直接放行:语义上用户已经在审批提示里看过
|
|
14
|
+
* `argumentsPreview`(包括任何路径字段)并显式同意。这条通道只用于"本次
|
|
15
|
+
* 工具调用",不会污染后续调用的沙箱策略。
|
|
16
|
+
*
|
|
17
|
+
* 审批未触发或被拒绝时必须保持 `false`。
|
|
18
|
+
*/
|
|
19
|
+
sandboxWaived?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 把用户输入路径规范化到允许的根目录集合之一。
|
|
23
|
+
*
|
|
24
|
+
* 沙箱层级自上而下:
|
|
25
|
+
* 1. `sandboxWaived === true` → 本次调用豁免,直接返回 `resolve(...)` 后的绝对路径;
|
|
26
|
+
* 2. 否则检查候选路径是否落在任意 `allowedRoots` 之下;全部不满足时抛出
|
|
27
|
+
* `ValidationError(VALIDATION_PATH_ESCAPE)`。
|
|
28
|
+
*
|
|
29
|
+
* 该函数对"同时传入了相对路径与绝对路径"的语义:相对路径基于
|
|
30
|
+
* `allowedRoots[0]` 展开(通常就是 workspaceRoot),因为模型给出相对路径时
|
|
31
|
+
* 默认意图"相对工作区";绝对路径则直接 resolve 后再做白名单判定。
|
|
32
|
+
*
|
|
33
|
+
* @throws ValidationError 当沙箱未豁免且候选路径不在任何允许的根下
|
|
34
|
+
*/
|
|
35
|
+
export declare const resolveAllowedPath: (targetPath: string, options: ResolveAllowedPathOptions) => string;
|
|
36
|
+
/**
|
|
37
|
+
* 旧签名兼容层:仅允许 `workspaceRoot` 一个根,不走审批豁免。
|
|
38
|
+
*
|
|
39
|
+
* @deprecated 新代码请改用 {@link resolveAllowedPath},通过 `ToolExecutionContext.allowedRoots`
|
|
40
|
+
* 和 `ToolExecutionContext.sandboxWaived` 传递完整的白名单/审批状态。
|
|
41
|
+
*/
|
|
42
|
+
export declare const resolveWorkspacePath: (workspaceRoot: string, targetPath: string) => string;
|
|
43
|
+
/**
|
|
44
|
+
* 将绝对路径转换为相对工作区路径。
|
|
45
|
+
*
|
|
46
|
+
* @param workspaceRoot 工作区根目录
|
|
47
|
+
* @param absPath 绝对路径
|
|
48
|
+
* @returns 相对路径(POSIX 风格)
|
|
49
|
+
*/
|
|
50
|
+
export declare const toWorkspaceRelativePath: (workspaceRoot: string, absPath: string) => string;
|
|
51
|
+
//# sourceMappingURL=path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/common/path.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAID;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,GAC7B,YAAY,MAAM,EAClB,SAAS,yBAAyB,KACjC,MAsCF,CAAC;AAUF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,eAAe,MAAM,EAAE,YAAY,MAAM,KAAG,MAEhF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GAAI,eAAe,MAAM,EAAE,SAAS,MAAM,KAAG,MAGhF,CAAC"}
|