@tachu/extensions 1.0.0-beta.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 +77 -0
- package/LICENSE +201 -0
- package/README.md +825 -0
- package/README_ZH.md +815 -0
- package/dist/backends/file.d.ts +18 -0
- package/dist/backends/file.d.ts.map +1 -0
- package/dist/backends/file.js +69 -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 +57 -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 +53 -0
- package/dist/backends/web.js.map +1 -0
- package/dist/common/net.d.ts +31 -0
- package/dist/common/net.d.ts.map +1 -0
- package/dist/common/net.js +162 -0
- package/dist/common/net.js.map +1 -0
- package/dist/common/path.d.ts +18 -0
- package/dist/common/path.d.ts.map +1 -0
- package/dist/common/path.js +34 -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 +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -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 +70 -0
- package/dist/mcp/sse-adapter.d.ts.map +1 -0
- package/dist/mcp/sse-adapter.js +176 -0
- package/dist/mcp/sse-adapter.js.map +1 -0
- package/dist/mcp/stdio-adapter.d.ts +73 -0
- package/dist/mcp/stdio-adapter.d.ts.map +1 -0
- package/dist/mcp/stdio-adapter.js +178 -0
- package/dist/mcp/stdio-adapter.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 +54 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +298 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +4 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/mock.d.ts +38 -0
- package/dist/providers/mock.d.ts.map +1 -0
- package/dist/providers/mock.js +79 -0
- package/dist/providers/mock.js.map +1 -0
- package/dist/providers/openai.d.ts +61 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +299 -0
- package/dist/providers/openai.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/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 +190 -0
- package/dist/tools/apply-patch/executor.js.map +1 -0
- package/dist/tools/fetch-url/descriptor.md +38 -0
- package/dist/tools/fetch-url/executor.d.ts +20 -0
- package/dist/tools/fetch-url/executor.d.ts.map +1 -0
- package/dist/tools/fetch-url/executor.js +34 -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 +191 -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 +46 -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 +113 -0
- package/dist/tools/search-code/executor.js.map +1 -0
- package/dist/tools/shared.d.ts +21 -0
- package/dist/tools/shared.d.ts.map +1 -0
- package/dist/tools/shared.js +12 -0
- package/dist/tools/shared.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 +81 -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 +65 -0
- package/dist/vector/qdrant.d.ts.map +1 -0
- package/dist/vector/qdrant.js +176 -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;AAGrB;;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;CAqDtF"}
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
* 文件系统执行后端。
|
|
6
|
+
*/
|
|
7
|
+
export class FileBackend {
|
|
8
|
+
name = "file";
|
|
9
|
+
kind = "file";
|
|
10
|
+
traits = {
|
|
11
|
+
sideEffect: "write",
|
|
12
|
+
idempotent: false,
|
|
13
|
+
requiresApproval: true,
|
|
14
|
+
timeout: 30_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.operation || !payload.path) {
|
|
26
|
+
throw new ValidationError("VALIDATION_FILE_OPERATION", "file backend 缺少 operation/path");
|
|
27
|
+
}
|
|
28
|
+
switch (payload.operation) {
|
|
29
|
+
case "read": {
|
|
30
|
+
const data = await readFile(payload.path, payload.encoding ?? "utf8");
|
|
31
|
+
return { success: true, result: { content: data, traceId: context.traceId } };
|
|
32
|
+
}
|
|
33
|
+
case "write": {
|
|
34
|
+
if (typeof payload.content !== "string") {
|
|
35
|
+
throw new ValidationError("VALIDATION_FILE_OPERATION", "write 操作缺少 content");
|
|
36
|
+
}
|
|
37
|
+
if (payload.createDirs) {
|
|
38
|
+
await mkdir(dirname(payload.path), { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
await writeFile(payload.path, payload.content, payload.encoding ?? "utf8");
|
|
41
|
+
const fileStat = await stat(payload.path);
|
|
42
|
+
return { success: true, result: { bytesWritten: fileStat.size, traceId: context.traceId } };
|
|
43
|
+
}
|
|
44
|
+
case "delete": {
|
|
45
|
+
await rm(payload.path, { force: true, recursive: true });
|
|
46
|
+
return { success: true, result: { deleted: true, traceId: context.traceId } };
|
|
47
|
+
}
|
|
48
|
+
case "move": {
|
|
49
|
+
if (!payload.to) {
|
|
50
|
+
throw new ValidationError("VALIDATION_FILE_OPERATION", "move 操作缺少 to");
|
|
51
|
+
}
|
|
52
|
+
await mkdir(dirname(payload.to), { recursive: true });
|
|
53
|
+
await rename(payload.path, payload.to);
|
|
54
|
+
return { success: true, result: { moved: true, traceId: context.traceId } };
|
|
55
|
+
}
|
|
56
|
+
case "copy": {
|
|
57
|
+
if (!payload.to) {
|
|
58
|
+
throw new ValidationError("VALIDATION_FILE_OPERATION", "copy 操作缺少 to");
|
|
59
|
+
}
|
|
60
|
+
await mkdir(dirname(payload.to), { recursive: true });
|
|
61
|
+
await copyFile(payload.path, payload.to);
|
|
62
|
+
return { success: true, result: { copied: true, traceId: context.traceId } };
|
|
63
|
+
}
|
|
64
|
+
default:
|
|
65
|
+
throw new ValidationError("VALIDATION_FILE_OPERATION", `未知文件操作: ${payload.operation}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# 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;CA0CtF"}
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
const processRef = Bun.spawn({
|
|
29
|
+
cmd: [payload.command, ...(payload.args ?? [])],
|
|
30
|
+
...(payload.cwd ? { cwd: payload.cwd } : {}),
|
|
31
|
+
stdout: "pipe",
|
|
32
|
+
stderr: "pipe",
|
|
33
|
+
env: process.env,
|
|
34
|
+
});
|
|
35
|
+
const timeoutId = setTimeout(() => {
|
|
36
|
+
if (processRef.pid) {
|
|
37
|
+
void terminateProcess(processRef.pid);
|
|
38
|
+
}
|
|
39
|
+
}, payload.timeoutMs ?? this.traits.timeout);
|
|
40
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
41
|
+
readStreamWithLimit(processRef.stdout, OUTPUT_LIMIT_BYTES),
|
|
42
|
+
readStreamWithLimit(processRef.stderr, OUTPUT_LIMIT_BYTES),
|
|
43
|
+
processRef.exited,
|
|
44
|
+
]);
|
|
45
|
+
clearTimeout(timeoutId);
|
|
46
|
+
return {
|
|
47
|
+
success: exitCode === 0,
|
|
48
|
+
result: {
|
|
49
|
+
stdout: stdout.text,
|
|
50
|
+
stderr: stderr.text,
|
|
51
|
+
exitCode,
|
|
52
|
+
traceId: context.traceId,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/backends/terminal.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1E,MAAM,kBAAkB,GAAG,IAAI,GAAG,IAAI,CAAC;AAEvC;;GAEG;AACH,MAAM,OAAO,eAAe;IACjB,IAAI,GAAG,UAAU,CAAC;IAClB,IAAI,GAAG,UAAmB,CAAC;IAC3B,MAAM,GAAoB;QACjC,UAAU,EAAE,cAAc;QAC1B,UAAU,EAAE,KAAK;QACjB,gBAAgB,EAAE,IAAI;QACtB,OAAO,EAAE,MAAM;KAChB,CAAC;IAEF;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,KAAmB,EAAE,OAAyB;QAC1D,MAAM,OAAO,GAAG,KAAK,CAAC,OAKrB,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACrB,MAAM,IAAI,eAAe,CAAC,0BAA0B,EAAE,+BAA+B,CAAC,CAAC;QACzF,CAAC;QAED,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC;YAC3B,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;YAC/C,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5C,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,GAAG,EAAE,OAAO,CAAC,GAAG;SACjB,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAChC,IAAI,UAAU,CAAC,GAAG,EAAE,CAAC;gBACnB,KAAK,gBAAgB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACxC,CAAC;QACH,CAAC,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAE7C,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACnD,mBAAmB,CAAC,UAAU,CAAC,MAAM,EAAE,kBAAkB,CAAC;YAC1D,mBAAmB,CAAC,UAAU,CAAC,MAAM,EAAE,kBAAkB,CAAC;YAC1D,UAAU,CAAC,MAAM;SAClB,CAAC,CAAC;QACH,YAAY,CAAC,SAAS,CAAC,CAAC;QAExB,OAAO;YACL,OAAO,EAAE,QAAQ,KAAK,CAAC;YACvB,MAAM,EAAE;gBACN,MAAM,EAAE,MAAM,CAAC,IAAI;gBACnB,MAAM,EAAE,MAAM,CAAC,IAAI;gBACnB,QAAQ;gBACR,OAAO,EAAE,OAAO,CAAC,OAAO;aACzB;SACF,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -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;CAuCtF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
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
|
+
const timeout = withAbortTimeout(undefined, payload.timeoutMs ?? this.traits.timeout, "TIMEOUT_WEB_BACKEND");
|
|
30
|
+
try {
|
|
31
|
+
const response = await fetch(target, {
|
|
32
|
+
method: payload.method ?? "GET",
|
|
33
|
+
headers: payload.headers,
|
|
34
|
+
body: payload.body,
|
|
35
|
+
signal: timeout.signal,
|
|
36
|
+
});
|
|
37
|
+
const body = await readResponseBodyWithLimit(response, MAX_RESPONSE_BYTES);
|
|
38
|
+
return {
|
|
39
|
+
success: response.ok,
|
|
40
|
+
result: {
|
|
41
|
+
status: response.status,
|
|
42
|
+
body: body.body,
|
|
43
|
+
truncated: body.truncated,
|
|
44
|
+
traceId: context.traceId,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
timeout.cleanup();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# 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,MAAM,OAAO,GAAG,gBAAgB,CAC9B,SAAS,EACT,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,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 检查 URL 是否指向私网地址。
|
|
3
|
+
*
|
|
4
|
+
* @param input 待检查 URL
|
|
5
|
+
* @throws SafetyError 当 URL 指向私网/本机地址时抛出
|
|
6
|
+
*/
|
|
7
|
+
export declare const assertPublicUrl: (input: string) => Promise<URL>;
|
|
8
|
+
/**
|
|
9
|
+
* 组合外部取消信号与超时控制。
|
|
10
|
+
*
|
|
11
|
+
* @param signal 外部取消信号
|
|
12
|
+
* @param timeoutMs 超时时间(毫秒)
|
|
13
|
+
* @param timeoutCode 超时错误码
|
|
14
|
+
* @returns 合并信号与清理函数
|
|
15
|
+
*/
|
|
16
|
+
export declare const withAbortTimeout: (signal: AbortSignal | undefined, timeoutMs: number, timeoutCode?: string) => {
|
|
17
|
+
signal: AbortSignal;
|
|
18
|
+
cleanup: () => void;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* 读取响应体并按最大字节数截断。
|
|
22
|
+
*
|
|
23
|
+
* @param response Fetch 响应
|
|
24
|
+
* @param maxBytes 最大字节数
|
|
25
|
+
* @returns 文本内容和截断标识
|
|
26
|
+
*/
|
|
27
|
+
export declare const readResponseBodyWithLimit: (response: Response, maxBytes: number) => Promise<{
|
|
28
|
+
body: string;
|
|
29
|
+
truncated: boolean;
|
|
30
|
+
}>;
|
|
31
|
+
//# sourceMappingURL=net.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"net.d.ts","sourceRoot":"","sources":["../../src/common/net.ts"],"names":[],"mappings":"AA6CA;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAU,OAAO,MAAM,KAAG,OAAO,CAAC,GAAG,CA8ChE,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,162 @@
|
|
|
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 isPrivateIpv4 = (ip) => {
|
|
6
|
+
const [aRaw, bRaw] = ip.split(".");
|
|
7
|
+
const a = Number(aRaw);
|
|
8
|
+
const b = Number(bRaw);
|
|
9
|
+
if (Number.isNaN(a) || Number.isNaN(b)) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (a === 10 || a === 127) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
if (a === 192 && b === 168) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
if (a === 172 && b >= 16 && b <= 31) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (a === 169 && b === 254) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (a === 0) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
};
|
|
29
|
+
const isPrivateIpv6 = (ip) => {
|
|
30
|
+
const lower = ip.toLowerCase();
|
|
31
|
+
if (lower === "::1") {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
if (lower.startsWith("fc") || lower.startsWith("fd")) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
if (lower.startsWith("fe80")) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* 检查 URL 是否指向私网地址。
|
|
44
|
+
*
|
|
45
|
+
* @param input 待检查 URL
|
|
46
|
+
* @throws SafetyError 当 URL 指向私网/本机地址时抛出
|
|
47
|
+
*/
|
|
48
|
+
export const assertPublicUrl = async (input) => {
|
|
49
|
+
let parsed;
|
|
50
|
+
try {
|
|
51
|
+
parsed = new URL(input);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
throw new SafetyError("SAFETY_INVALID_URL", `URL 无效: ${input}`, { cause: error });
|
|
55
|
+
}
|
|
56
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
57
|
+
throw new SafetyError("SAFETY_PROTOCOL_NOT_ALLOWED", `协议不允许: ${parsed.protocol}`);
|
|
58
|
+
}
|
|
59
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
60
|
+
if (LOCAL_HOSTNAMES.has(hostname) || hostname.endsWith(".local")) {
|
|
61
|
+
throw new SafetyError("SAFETY_PRIVATE_NETWORK_BLOCKED", `已阻止私网地址: ${hostname}`, {
|
|
62
|
+
context: { hostname },
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const literalIpFamily = isIP(hostname);
|
|
66
|
+
if (literalIpFamily === 4 && isPrivateIpv4(hostname)) {
|
|
67
|
+
throw new SafetyError("SAFETY_PRIVATE_NETWORK_BLOCKED", `已阻止私网地址: ${hostname}`, {
|
|
68
|
+
context: { hostname },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (literalIpFamily === 6 && isPrivateIpv6(hostname)) {
|
|
72
|
+
throw new SafetyError("SAFETY_PRIVATE_NETWORK_BLOCKED", `已阻止私网地址: ${hostname}`, {
|
|
73
|
+
context: { hostname },
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (literalIpFamily === 0) {
|
|
77
|
+
const records = await lookup(hostname, { all: true });
|
|
78
|
+
for (const record of records) {
|
|
79
|
+
if ((record.family === 4 && isPrivateIpv4(record.address)) ||
|
|
80
|
+
(record.family === 6 && isPrivateIpv6(record.address))) {
|
|
81
|
+
throw new SafetyError("SAFETY_PRIVATE_NETWORK_BLOCKED", `已阻止私网地址: ${hostname}`, {
|
|
82
|
+
context: { hostname, resolved: records.map((item) => item.address) },
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return parsed;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* 组合外部取消信号与超时控制。
|
|
91
|
+
*
|
|
92
|
+
* @param signal 外部取消信号
|
|
93
|
+
* @param timeoutMs 超时时间(毫秒)
|
|
94
|
+
* @param timeoutCode 超时错误码
|
|
95
|
+
* @returns 合并信号与清理函数
|
|
96
|
+
*/
|
|
97
|
+
export const withAbortTimeout = (signal, timeoutMs, timeoutCode = "TIMEOUT_PROVIDER_REQUEST") => {
|
|
98
|
+
const controller = new AbortController();
|
|
99
|
+
let timeoutId;
|
|
100
|
+
const onAbort = () => {
|
|
101
|
+
controller.abort(signal?.reason ?? new Error("aborted"));
|
|
102
|
+
};
|
|
103
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
104
|
+
timeoutId = setTimeout(() => {
|
|
105
|
+
controller.abort(new TimeoutError(timeoutCode, `请求超时: ${timeoutMs}ms`, { retryable: true }));
|
|
106
|
+
}, timeoutMs);
|
|
107
|
+
return {
|
|
108
|
+
signal: controller.signal,
|
|
109
|
+
cleanup: () => {
|
|
110
|
+
if (timeoutId) {
|
|
111
|
+
clearTimeout(timeoutId);
|
|
112
|
+
}
|
|
113
|
+
signal?.removeEventListener("abort", onAbort);
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* 读取响应体并按最大字节数截断。
|
|
119
|
+
*
|
|
120
|
+
* @param response Fetch 响应
|
|
121
|
+
* @param maxBytes 最大字节数
|
|
122
|
+
* @returns 文本内容和截断标识
|
|
123
|
+
*/
|
|
124
|
+
export const readResponseBodyWithLimit = async (response, maxBytes) => {
|
|
125
|
+
const reader = response.body?.getReader();
|
|
126
|
+
if (!reader) {
|
|
127
|
+
return { body: "", truncated: false };
|
|
128
|
+
}
|
|
129
|
+
const chunks = [];
|
|
130
|
+
let total = 0;
|
|
131
|
+
let truncated = false;
|
|
132
|
+
while (true) {
|
|
133
|
+
const { done, value } = await reader.read();
|
|
134
|
+
if (done) {
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
if (!value) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const room = maxBytes - total;
|
|
141
|
+
if (room <= 0) {
|
|
142
|
+
truncated = true;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
if (value.byteLength > room) {
|
|
146
|
+
chunks.push(value.subarray(0, room));
|
|
147
|
+
total += room;
|
|
148
|
+
truncated = true;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
chunks.push(value);
|
|
152
|
+
total += value.byteLength;
|
|
153
|
+
}
|
|
154
|
+
const merged = new Uint8Array(total);
|
|
155
|
+
let offset = 0;
|
|
156
|
+
for (const chunk of chunks) {
|
|
157
|
+
merged.set(chunk, offset);
|
|
158
|
+
offset += chunk.byteLength;
|
|
159
|
+
}
|
|
160
|
+
return { body: new TextDecoder().decode(merged), truncated };
|
|
161
|
+
};
|
|
162
|
+
//# 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,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 将用户输入路径规范化到 workspaceRoot 下。
|
|
3
|
+
*
|
|
4
|
+
* @param workspaceRoot 工作区根目录
|
|
5
|
+
* @param targetPath 用户输入路径(可相对/绝对)
|
|
6
|
+
* @returns 规范化后的绝对路径
|
|
7
|
+
* @throws ValidationError 当路径逃逸工作区时抛出
|
|
8
|
+
*/
|
|
9
|
+
export declare const resolveWorkspacePath: (workspaceRoot: string, targetPath: string) => string;
|
|
10
|
+
/**
|
|
11
|
+
* 将绝对路径转换为相对工作区路径。
|
|
12
|
+
*
|
|
13
|
+
* @param workspaceRoot 工作区根目录
|
|
14
|
+
* @param absPath 绝对路径
|
|
15
|
+
* @returns 相对路径(POSIX 风格)
|
|
16
|
+
*/
|
|
17
|
+
export declare const toWorkspaceRelativePath: (workspaceRoot: string, absPath: string) => string;
|
|
18
|
+
//# sourceMappingURL=path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/common/path.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,eAAe,MAAM,EAAE,YAAY,MAAM,KAAG,MAWhF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GAAI,eAAe,MAAM,EAAE,SAAS,MAAM,KAAG,MAGhF,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { resolve, relative, isAbsolute } from "node:path";
|
|
2
|
+
import { ValidationError } from "@tachu/core";
|
|
3
|
+
/**
|
|
4
|
+
* 将用户输入路径规范化到 workspaceRoot 下。
|
|
5
|
+
*
|
|
6
|
+
* @param workspaceRoot 工作区根目录
|
|
7
|
+
* @param targetPath 用户输入路径(可相对/绝对)
|
|
8
|
+
* @returns 规范化后的绝对路径
|
|
9
|
+
* @throws ValidationError 当路径逃逸工作区时抛出
|
|
10
|
+
*/
|
|
11
|
+
export const resolveWorkspacePath = (workspaceRoot, targetPath) => {
|
|
12
|
+
const candidate = isAbsolute(targetPath)
|
|
13
|
+
? resolve(targetPath)
|
|
14
|
+
: resolve(workspaceRoot, targetPath);
|
|
15
|
+
const rel = relative(workspaceRoot, candidate);
|
|
16
|
+
if (rel.startsWith("..") || rel.includes(`..${"/"}`)) {
|
|
17
|
+
throw new ValidationError("VALIDATION_PATH_ESCAPE", `路径越界: ${targetPath}`, {
|
|
18
|
+
context: { workspaceRoot, targetPath, candidate },
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return candidate;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 将绝对路径转换为相对工作区路径。
|
|
25
|
+
*
|
|
26
|
+
* @param workspaceRoot 工作区根目录
|
|
27
|
+
* @param absPath 绝对路径
|
|
28
|
+
* @returns 相对路径(POSIX 风格)
|
|
29
|
+
*/
|
|
30
|
+
export const toWorkspaceRelativePath = (workspaceRoot, absPath) => {
|
|
31
|
+
const rel = relative(workspaceRoot, absPath).replaceAll("\\", "/");
|
|
32
|
+
return rel.length === 0 ? "." : rel;
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/common/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,aAAqB,EAAE,UAAkB,EAAU,EAAE;IACxF,MAAM,SAAS,GAAG,UAAU,CAAC,UAAU,CAAC;QACtC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;QACrB,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IAC/C,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,GAAG,EAAE,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,eAAe,CAAC,wBAAwB,EAAE,SAAS,UAAU,EAAE,EAAE;YACzE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE;SAClD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,aAAqB,EAAE,OAAe,EAAU,EAAE;IACxF,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACnE,OAAO,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AACtC,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 将流内容读取为字符串并限制最大字节数。
|
|
3
|
+
*
|
|
4
|
+
* @param stream 可读流
|
|
5
|
+
* @param maxBytes 最大字节数
|
|
6
|
+
* @returns 文本与截断标识
|
|
7
|
+
*/
|
|
8
|
+
export declare const readStreamWithLimit: (stream: ReadableStream<Uint8Array> | null | undefined, maxBytes: number) => Promise<{
|
|
9
|
+
text: string;
|
|
10
|
+
truncated: boolean;
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* 尝试优雅结束子进程,必要时强制杀死。
|
|
14
|
+
*
|
|
15
|
+
* @param pid 进程 PID
|
|
16
|
+
* @param graceMs SIGTERM 后等待时长
|
|
17
|
+
*/
|
|
18
|
+
export declare const terminateProcess: (pid: number, graceMs?: number) => Promise<void>;
|
|
19
|
+
//# sourceMappingURL=process.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/common/process.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,SAAS,EACrD,UAAU,MAAM,KACf,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAwC9C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAU,KAAK,MAAM,EAAE,gBAAc,KAAG,OAAO,CAAC,IAAI,CAYhF,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 将流内容读取为字符串并限制最大字节数。
|
|
3
|
+
*
|
|
4
|
+
* @param stream 可读流
|
|
5
|
+
* @param maxBytes 最大字节数
|
|
6
|
+
* @returns 文本与截断标识
|
|
7
|
+
*/
|
|
8
|
+
export const readStreamWithLimit = async (stream, maxBytes) => {
|
|
9
|
+
if (!stream) {
|
|
10
|
+
return { text: "", truncated: false };
|
|
11
|
+
}
|
|
12
|
+
const reader = stream.getReader();
|
|
13
|
+
const chunks = [];
|
|
14
|
+
let total = 0;
|
|
15
|
+
let truncated = false;
|
|
16
|
+
while (true) {
|
|
17
|
+
const { done, value } = await reader.read();
|
|
18
|
+
if (done) {
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
if (!value) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const room = maxBytes - total;
|
|
25
|
+
if (room <= 0) {
|
|
26
|
+
truncated = true;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
if (value.byteLength > room) {
|
|
30
|
+
chunks.push(value.subarray(0, room));
|
|
31
|
+
total += room;
|
|
32
|
+
truncated = true;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
chunks.push(value);
|
|
36
|
+
total += value.byteLength;
|
|
37
|
+
}
|
|
38
|
+
const merged = new Uint8Array(total);
|
|
39
|
+
let offset = 0;
|
|
40
|
+
for (const chunk of chunks) {
|
|
41
|
+
merged.set(chunk, offset);
|
|
42
|
+
offset += chunk.byteLength;
|
|
43
|
+
}
|
|
44
|
+
return { text: new TextDecoder().decode(merged), truncated };
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* 尝试优雅结束子进程,必要时强制杀死。
|
|
48
|
+
*
|
|
49
|
+
* @param pid 进程 PID
|
|
50
|
+
* @param graceMs SIGTERM 后等待时长
|
|
51
|
+
*/
|
|
52
|
+
export const terminateProcess = async (pid, graceMs = 3000) => {
|
|
53
|
+
try {
|
|
54
|
+
process.kill(pid, "SIGTERM");
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
await new Promise((resolve) => setTimeout(resolve, graceMs));
|
|
60
|
+
try {
|
|
61
|
+
process.kill(pid, "SIGKILL");
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// 进程已结束
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=process.js.map
|