aws-runtime-bridge 1.7.49 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/AcodeSdkAdapter.d.ts +104 -0
- package/dist/adapter/AcodeSdkAdapter.d.ts.map +1 -0
- package/dist/adapter/AcodeSdkAdapter.js +644 -0
- package/dist/adapter/AcodeSdkAdapter.test.d.ts +2 -0
- package/dist/adapter/AcodeSdkAdapter.test.d.ts.map +1 -0
- package/dist/adapter/AcodeSdkAdapter.test.js +1280 -0
- package/dist/adapter/AdapterRegistry.d.ts +1 -1
- package/dist/adapter/AdapterRegistry.d.ts.map +1 -1
- package/dist/adapter/AdapterRegistry.js +2 -0
- package/dist/adapter/AdapterRegistry.test.js +10 -1
- package/dist/adapter/ClaudeSdkAdapter.test.js +31 -8
- package/dist/adapter/CodexSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/CodexSdkAdapter.js +2 -0
- package/dist/adapter/OpencodeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/OpencodeSdkAdapter.js +2 -0
- package/dist/adapter/SdkProviderSpi.d.ts.map +1 -1
- package/dist/adapter/SdkProviderSpi.js +10 -1
- package/dist/adapter/adapter.test.js +2 -2
- package/dist/adapter/idle-poll-loop.d.ts +4 -4
- package/dist/adapter/idle-poll-loop.d.ts.map +1 -1
- package/dist/adapter/idle-poll-loop.js +3 -3
- package/dist/adapter/idle-poll-loop.test.js +15 -1
- package/dist/adapter/index.d.ts +1 -0
- package/dist/adapter/index.d.ts.map +1 -1
- package/dist/adapter/index.js +1 -0
- package/dist/adapter/types.d.ts +25 -6
- package/dist/adapter/types.d.ts.map +1 -1
- package/dist/adapter/types.js +221 -110
- package/dist/adapter/types.test.js +10 -3
- package/dist/adapters/cc-switch/index.d.ts +2 -0
- package/dist/adapters/cc-switch/index.d.ts.map +1 -1
- package/dist/adapters/cc-switch/index.js +6 -0
- package/dist/adapters/cc-switch/mcp-acode.d.ts +17 -0
- package/dist/adapters/cc-switch/mcp-acode.d.ts.map +1 -0
- package/dist/adapters/cc-switch/mcp-acode.js +71 -0
- package/dist/adapters/cc-switch/mcp-acode.test.d.ts +2 -0
- package/dist/adapters/cc-switch/mcp-acode.test.d.ts.map +1 -0
- package/dist/adapters/cc-switch/mcp-acode.test.js +80 -0
- package/dist/adapters/cc-switch/mcp-claude.d.ts.map +1 -1
- package/dist/adapters/cc-switch/mcp-claude.js +1 -0
- package/dist/adapters/cc-switch/mcp-claudecode.d.ts.map +1 -1
- package/dist/adapters/cc-switch/mcp-claudecode.js +1 -0
- package/dist/adapters/cc-switch/mcp-claudecode.test.js +1 -0
- package/dist/adapters/cc-switch/mcp-codex.d.ts.map +1 -1
- package/dist/adapters/cc-switch/mcp-codex.js +1 -0
- package/dist/adapters/cc-switch/mcp-codex.test.js +1 -0
- package/dist/adapters/cc-switch/mcp-opencode.d.ts.map +1 -1
- package/dist/adapters/cc-switch/mcp-opencode.js +1 -0
- package/dist/adapters/cc-switch/mcp-opencode.test.js +1 -0
- package/dist/adapters/cc-switch/skill-acode.d.ts +11 -0
- package/dist/adapters/cc-switch/skill-acode.d.ts.map +1 -0
- package/dist/adapters/cc-switch/skill-acode.js +12 -0
- package/dist/adapters/cc-switch/skill-directory.test.js +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -0
- package/dist/index.js +2 -0
- package/dist/routes/ai-sources.js +3 -2
- package/dist/routes/instance.d.ts.map +1 -1
- package/dist/routes/instance.js +7 -8
- package/dist/routes/instance.test.js +2 -1
- package/dist/routes/mcp.js +2 -2
- package/dist/routes/mcp.test.js +2 -1
- package/dist/routes/runtime-binding.d.ts +3 -2
- package/dist/routes/runtime-binding.d.ts.map +1 -1
- package/dist/routes/runtime-binding.js +11 -58
- package/dist/routes/runtime-binding.test.js +4 -80
- package/dist/routes/sessions.d.ts +5 -0
- package/dist/routes/sessions.d.ts.map +1 -1
- package/dist/routes/sessions.js +36 -16
- package/dist/routes/sessions.test.js +13 -0
- package/dist/routes/skills.js +2 -2
- package/dist/routes/skills.test.js +2 -1
- package/dist/routes/terminal.d.ts +27 -7
- package/dist/routes/terminal.d.ts.map +1 -1
- package/dist/routes/terminal.js +439 -171
- package/dist/routes/terminal.test.js +385 -197
- package/dist/services/acode-package.d.ts +8 -0
- package/dist/services/acode-package.d.ts.map +1 -0
- package/dist/services/acode-package.js +82 -0
- package/dist/services/ai-source-apply.d.ts +4 -0
- package/dist/services/ai-source-apply.d.ts.map +1 -1
- package/dist/services/ai-source-apply.js +8 -0
- package/dist/services/ai-source-apply.test.js +8 -1
- package/dist/services/cc-switch-sdk.d.ts.map +1 -1
- package/dist/services/cc-switch-sdk.js +9 -1
- package/dist/services/instance-init-service.d.ts.map +1 -1
- package/dist/services/instance-init-service.js +3 -2
- package/dist/services/instance-init-service.test.js +6 -5
- package/dist/services/instance-service.d.ts.map +1 -1
- package/dist/services/instance-service.js +1 -0
- package/dist/services/instance-state.js +1 -1
- package/dist/services/instance-state.test.js +4 -4
- package/dist/services/mcp-launch-binding-queue.d.ts +1 -0
- package/dist/services/mcp-launch-binding-queue.d.ts.map +1 -1
- package/dist/services/mcp-launch-binding-queue.js +8 -2
- package/dist/services/mcp-launch-binding-queue.test.js +47 -4
- package/dist/services/session-output.d.ts +10 -6
- package/dist/services/session-output.d.ts.map +1 -1
- package/dist/services/session-output.js +29 -14
- package/dist/services/session-output.test.js +93 -39
- package/dist/services/tool-installer.d.ts.map +1 -1
- package/dist/services/tool-installer.js +10 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/file-utils.d.ts.map +1 -1
- package/dist/utils/file-utils.js +1 -0
- package/dist/utils/mcp-utils.d.ts.map +1 -1
- package/dist/utils/mcp-utils.js +4 -1
- package/dist/utils/validation.js +1 -1
- package/node_modules/@cc-switch/sdk/dist/constants.d.ts.map +1 -1
- package/node_modules/@cc-switch/sdk/dist/constants.js +7 -0
- package/node_modules/@cc-switch/sdk/dist/schemas.d.ts.map +1 -1
- package/node_modules/@cc-switch/sdk/dist/schemas.js +1 -0
- package/node_modules/@cc-switch/sdk/dist/sdk.d.ts.map +1 -1
- package/node_modules/@cc-switch/sdk/dist/sdk.js +7 -3
- package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.d.ts +6 -0
- package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.d.ts.map +1 -1
- package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.js +27 -1
- package/node_modules/@cc-switch/sdk/dist/types.d.ts +4 -1
- package/node_modules/@cc-switch/sdk/dist/types.d.ts.map +1 -1
- package/package/acode/README.md +88 -0
- package/package/acode/dist/built-in-file-tools.d.ts +16 -0
- package/package/acode/dist/built-in-file-tools.d.ts.map +1 -0
- package/package/acode/dist/built-in-file-tools.js +569 -0
- package/package/acode/dist/cli.d.ts +3 -0
- package/package/acode/dist/cli.d.ts.map +1 -0
- package/package/acode/dist/cli.js +37 -0
- package/package/acode/dist/commands/commandLoader.d.ts +7 -0
- package/package/acode/dist/commands/commandLoader.d.ts.map +1 -0
- package/package/acode/dist/commands/commandLoader.js +33 -0
- package/package/acode/dist/commands/commandParser.d.ts +7 -0
- package/package/acode/dist/commands/commandParser.d.ts.map +1 -0
- package/package/acode/dist/commands/commandParser.js +77 -0
- package/package/acode/dist/commands/commandRegistry.d.ts +13 -0
- package/package/acode/dist/commands/commandRegistry.d.ts.map +1 -0
- package/package/acode/dist/commands/commandRegistry.js +30 -0
- package/package/acode/dist/config.d.ts +4 -0
- package/package/acode/dist/config.d.ts.map +1 -0
- package/package/acode/dist/config.js +80 -0
- package/package/acode/dist/hooks/hookBus.d.ts +19 -0
- package/package/acode/dist/hooks/hookBus.d.ts.map +1 -0
- package/package/acode/dist/hooks/hookBus.js +40 -0
- package/package/acode/dist/index.d.ts +8 -0
- package/package/acode/dist/index.d.ts.map +1 -0
- package/package/acode/dist/index.js +7 -0
- package/package/acode/dist/runtime.d.ts +42 -0
- package/package/acode/dist/runtime.d.ts.map +1 -0
- package/package/acode/dist/runtime.js +1283 -0
- package/package/acode/dist/types.d.ts +112 -0
- package/package/acode/dist/types.d.ts.map +1 -0
- package/package/acode/dist/types.js +1 -0
- package/package/acode/package.json +24 -0
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +4 -4
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/agent-client.js +9 -6
- package/package/aws-client-agent-mcp/dist/agent-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/agent-client.test.js +21 -0
- package/package/aws-client-agent-mcp/dist/agent-client.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +1 -17
- package/package/aws-client-agent-mcp/dist/http-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.js +7 -113
- package/package/aws-client-agent-mcp/dist/http-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.test.js +22 -2
- package/package/aws-client-agent-mcp/dist/http-client.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +0 -5
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.js +3 -24
- package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js +90 -5
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +1 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.js +31 -3
- package/package/aws-client-agent-mcp/dist/status-reporter.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/types.d.ts +4 -1
- package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/types.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +0 -13
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.js +5 -100
- package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js +43 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +1 -1
- package/package/cc-switch-sdk/dist/constants.d.ts.map +1 -1
- package/package/cc-switch-sdk/dist/constants.js +7 -0
- package/package/cc-switch-sdk/dist/schemas.d.ts.map +1 -1
- package/package/cc-switch-sdk/dist/schemas.js +1 -0
- package/package/cc-switch-sdk/dist/sdk.d.ts.map +1 -1
- package/package/cc-switch-sdk/dist/sdk.js +7 -3
- package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts +6 -0
- package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts.map +1 -1
- package/package/cc-switch-sdk/dist/services/ai-config-service.js +27 -1
- package/package/cc-switch-sdk/dist/types.d.ts +4 -1
- package/package/cc-switch-sdk/dist/types.d.ts.map +1 -1
- package/package.json +8 -4
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { promises as fs } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
function toDisplayPath(targetPath) {
|
|
6
|
+
return targetPath.replace(/\\/g, "/");
|
|
7
|
+
}
|
|
8
|
+
async function pathExists(targetPath) {
|
|
9
|
+
try {
|
|
10
|
+
await fs.access(targetPath);
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function countOccurrences(content, search) {
|
|
18
|
+
if (!search) {
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
let count = 0;
|
|
22
|
+
let index = content.indexOf(search);
|
|
23
|
+
while (index >= 0) {
|
|
24
|
+
count += 1;
|
|
25
|
+
index = content.indexOf(search, index + search.length);
|
|
26
|
+
}
|
|
27
|
+
return count;
|
|
28
|
+
}
|
|
29
|
+
function readStringArg(args, names, label) {
|
|
30
|
+
for (const name of names) {
|
|
31
|
+
const value = args[name];
|
|
32
|
+
if (typeof value === "string") {
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`${label} is required`);
|
|
37
|
+
}
|
|
38
|
+
function readOptionalStringArg(args, names) {
|
|
39
|
+
for (const name of names) {
|
|
40
|
+
const value = args[name];
|
|
41
|
+
if (typeof value === "string" && value.trim()) {
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
function readOptionalPositiveIntegerArg(args, names, fallback) {
|
|
48
|
+
for (const name of names) {
|
|
49
|
+
const value = args[name];
|
|
50
|
+
const numeric = typeof value === "number"
|
|
51
|
+
? value
|
|
52
|
+
: typeof value === "string"
|
|
53
|
+
? Number(value)
|
|
54
|
+
: NaN;
|
|
55
|
+
if (Number.isFinite(numeric) && numeric > 0) {
|
|
56
|
+
return Math.floor(numeric);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return fallback;
|
|
60
|
+
}
|
|
61
|
+
function ensureInsideWorkspace(workspaceRoot, targetPath) {
|
|
62
|
+
const resolvedTargetPath = path.resolve(targetPath);
|
|
63
|
+
const relativePath = path.relative(workspaceRoot, resolvedTargetPath);
|
|
64
|
+
if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
|
|
65
|
+
throw new Error(`Target path is outside workspace: ${resolvedTargetPath}`);
|
|
66
|
+
}
|
|
67
|
+
return resolvedTargetPath;
|
|
68
|
+
}
|
|
69
|
+
async function resolveWorkspaceDirectory(workingDirectory, rawCwd) {
|
|
70
|
+
const requestedRoot = path.resolve(String(workingDirectory || "").trim());
|
|
71
|
+
const workspaceRoot = await fs.realpath(requestedRoot);
|
|
72
|
+
const workspaceStat = await fs.stat(workspaceRoot);
|
|
73
|
+
if (!workspaceStat.isDirectory()) {
|
|
74
|
+
throw new Error(`Workspace path is not a directory: ${requestedRoot}`);
|
|
75
|
+
}
|
|
76
|
+
if (!rawCwd?.trim()) {
|
|
77
|
+
return { workspaceRoot, cwd: workspaceRoot };
|
|
78
|
+
}
|
|
79
|
+
const requestedCwd = rawCwd.trim();
|
|
80
|
+
const candidatePath = path.isAbsolute(requestedCwd)
|
|
81
|
+
? path.resolve(requestedCwd)
|
|
82
|
+
: path.resolve(requestedRoot, requestedCwd);
|
|
83
|
+
const requestedRelativePath = path.relative(requestedRoot, candidatePath);
|
|
84
|
+
if (requestedRelativePath.startsWith("..") ||
|
|
85
|
+
path.isAbsolute(requestedRelativePath)) {
|
|
86
|
+
throw new Error(`Command cwd is outside workspace: ${candidatePath}`);
|
|
87
|
+
}
|
|
88
|
+
const realCwd = ensureInsideWorkspace(workspaceRoot, await fs.realpath(candidatePath));
|
|
89
|
+
const cwdStat = await fs.stat(realCwd);
|
|
90
|
+
if (!cwdStat.isDirectory()) {
|
|
91
|
+
throw new Error(`Command cwd is not a directory: ${realCwd}`);
|
|
92
|
+
}
|
|
93
|
+
return { workspaceRoot, cwd: realCwd };
|
|
94
|
+
}
|
|
95
|
+
async function ensureWritableTarget(workspaceRoot, targetPath) {
|
|
96
|
+
const realParentPath = await fs.realpath(path.dirname(targetPath));
|
|
97
|
+
ensureInsideWorkspace(workspaceRoot, realParentPath);
|
|
98
|
+
try {
|
|
99
|
+
const targetLinkStat = await fs.lstat(targetPath);
|
|
100
|
+
if (targetLinkStat.isSymbolicLink()) {
|
|
101
|
+
throw new Error(`Refusing to write through symbolic link: ${targetPath}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
const errorCode = typeof error === "object" && error !== null
|
|
106
|
+
? Reflect.get(error, "code")
|
|
107
|
+
: undefined;
|
|
108
|
+
if (errorCode !== "ENOENT") {
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 解析内置文件工具目标路径。
|
|
115
|
+
* 主流程:同时校验请求路径与 realpath 工作区边界,防止绝对路径、`..` 与符号链接目录穿透。
|
|
116
|
+
*/
|
|
117
|
+
async function resolveWorkspaceTarget(workingDirectory, rawFilePath) {
|
|
118
|
+
const requestedRoot = path.resolve(String(workingDirectory || "").trim());
|
|
119
|
+
const workspaceRoot = await fs.realpath(requestedRoot);
|
|
120
|
+
const workspaceStat = await fs.stat(workspaceRoot);
|
|
121
|
+
if (!workspaceStat.isDirectory()) {
|
|
122
|
+
throw new Error(`Workspace path is not a directory: ${requestedRoot}`);
|
|
123
|
+
}
|
|
124
|
+
const normalizedFilePath = rawFilePath.trim();
|
|
125
|
+
if (!normalizedFilePath) {
|
|
126
|
+
throw new Error("file_path is required");
|
|
127
|
+
}
|
|
128
|
+
const requestedPath = path.isAbsolute(normalizedFilePath)
|
|
129
|
+
? path.resolve(normalizedFilePath)
|
|
130
|
+
: path.resolve(requestedRoot, normalizedFilePath);
|
|
131
|
+
const requestedRelativePath = path.relative(requestedRoot, requestedPath);
|
|
132
|
+
if (requestedRelativePath.startsWith("..") ||
|
|
133
|
+
path.isAbsolute(requestedRelativePath)) {
|
|
134
|
+
throw new Error(`Target path is outside workspace: ${requestedPath}`);
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
workspaceRoot,
|
|
138
|
+
requestedPath,
|
|
139
|
+
targetPath: ensureInsideWorkspace(workspaceRoot, path.join(workspaceRoot, requestedRelativePath)),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* 原子写入完整文件内容。
|
|
144
|
+
* 主流程:创建父目录 -> 校验 realpath 边界和符号链接 -> 写同目录临时文件 -> rename 覆盖目标。
|
|
145
|
+
*/
|
|
146
|
+
async function writeFileAtomically(workingDirectory, filePath, content) {
|
|
147
|
+
const target = await resolveWorkspaceTarget(workingDirectory, filePath);
|
|
148
|
+
const parentPath = path.dirname(target.targetPath);
|
|
149
|
+
const createdParentDirectory = !(await pathExists(parentPath));
|
|
150
|
+
await fs.mkdir(parentPath, { recursive: true });
|
|
151
|
+
await ensureWritableTarget(target.workspaceRoot, target.targetPath);
|
|
152
|
+
const temporaryPath = path.join(parentPath, `.${path.basename(target.targetPath)}.${process.pid}.${randomUUID()}.tmp`);
|
|
153
|
+
try {
|
|
154
|
+
await fs.writeFile(temporaryPath, content, "utf-8");
|
|
155
|
+
await fs.rename(temporaryPath, target.targetPath);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
await fs.rm(temporaryPath, { force: true });
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
workspaceRoot: target.workspaceRoot,
|
|
163
|
+
targetPath: target.targetPath,
|
|
164
|
+
bytes: Buffer.byteLength(content, "utf-8"),
|
|
165
|
+
createdParentDirectory,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
async function readFileInsideWorkspace(workingDirectory, filePath) {
|
|
169
|
+
const target = await resolveWorkspaceTarget(workingDirectory, filePath);
|
|
170
|
+
const realTargetPath = ensureInsideWorkspace(target.workspaceRoot, await fs.realpath(target.targetPath));
|
|
171
|
+
const stat = await fs.stat(realTargetPath);
|
|
172
|
+
if (!stat.isFile()) {
|
|
173
|
+
throw new Error(`Path is not a file: ${realTargetPath}`);
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
workspaceRoot: target.workspaceRoot,
|
|
177
|
+
targetPath: realTargetPath,
|
|
178
|
+
content: await fs.readFile(realTargetPath, "utf-8"),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const DEFAULT_COMMAND_TIMEOUT_MS = 30_000;
|
|
182
|
+
const MAX_COMMAND_TIMEOUT_MS = 10 * 60_000;
|
|
183
|
+
const DEFAULT_COMMAND_INACTIVITY_TIMEOUT_MS = 60_000;
|
|
184
|
+
const MAX_COMMAND_INACTIVITY_TIMEOUT_MS = 10 * 60_000;
|
|
185
|
+
const MAX_COMMAND_OUTPUT_BYTES = 128 * 1024;
|
|
186
|
+
const COMMAND_OUTPUT_SPILLOVER_LINE_LIMIT = 100;
|
|
187
|
+
function padDatePart(value) {
|
|
188
|
+
return String(value).padStart(2, "0");
|
|
189
|
+
}
|
|
190
|
+
function countOutputLines(output) {
|
|
191
|
+
if (!output) {
|
|
192
|
+
return 0;
|
|
193
|
+
}
|
|
194
|
+
const normalized = output.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
195
|
+
const withoutTrailingLineBreak = normalized.endsWith("\n")
|
|
196
|
+
? normalized.slice(0, -1)
|
|
197
|
+
: normalized;
|
|
198
|
+
return withoutTrailingLineBreak
|
|
199
|
+
? withoutTrailingLineBreak.split("\n").length
|
|
200
|
+
: 1;
|
|
201
|
+
}
|
|
202
|
+
function buildCommandOutputSpilloverPath(workspaceRoot, now = new Date()) {
|
|
203
|
+
const year = now.getFullYear();
|
|
204
|
+
const month = padDatePart(now.getMonth() + 1);
|
|
205
|
+
const day = padDatePart(now.getDate());
|
|
206
|
+
const hour = padDatePart(now.getHours());
|
|
207
|
+
const minute = padDatePart(now.getMinutes());
|
|
208
|
+
const timestamp = `${year}${month}${day}-${hour}${minute}${padDatePart(now.getSeconds())}-${String(now.getMilliseconds()).padStart(3, "0")}-${process.pid}-${randomUUID().slice(0, 8)}`;
|
|
209
|
+
return path.join(workspaceRoot, ".agentswork", "acode", "cmd-dist", `${year}-${month}-${day}`, hour, minute, `${timestamp}-cmd.out`);
|
|
210
|
+
}
|
|
211
|
+
async function writeCommandOutputSpilloverFile(workspaceRoot, content) {
|
|
212
|
+
const targetPath = buildCommandOutputSpilloverPath(workspaceRoot);
|
|
213
|
+
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
214
|
+
await fs.writeFile(targetPath, content, "utf-8");
|
|
215
|
+
return targetPath;
|
|
216
|
+
}
|
|
217
|
+
function buildCommandOutputSpilloverContent(input) {
|
|
218
|
+
return [
|
|
219
|
+
`Command: ${input.commandText}`,
|
|
220
|
+
`CWD: ${input.cwd}`,
|
|
221
|
+
`Started: ${input.startedAt.toISOString()}`,
|
|
222
|
+
`Ended: ${input.endedAt.toISOString()}`,
|
|
223
|
+
`Exit code: ${input.exitCode ?? "null"}`,
|
|
224
|
+
`Signal: ${input.signal ?? "null"}`,
|
|
225
|
+
`Timed out: ${input.timedOut}`,
|
|
226
|
+
`Timed out by inactivity: ${input.inactivityTimedOut}`,
|
|
227
|
+
"",
|
|
228
|
+
"--- stdout ---",
|
|
229
|
+
input.stdout,
|
|
230
|
+
"",
|
|
231
|
+
"--- stderr ---",
|
|
232
|
+
input.stderr,
|
|
233
|
+
].join("\n");
|
|
234
|
+
}
|
|
235
|
+
function formatCommandOutputSpilloverStdout(outputFilePath, outputLineCount) {
|
|
236
|
+
return [
|
|
237
|
+
`[ACode] 命令输出超过 ${COMMAND_OUTPUT_SPILLOVER_LINE_LIMIT} 行(共 ${outputLineCount} 行),完整输出已写入: ${toDisplayPath(outputFilePath)}`,
|
|
238
|
+
"[ACode] Agent 可自行读取该文件,或在该文件中搜索需要的内容。",
|
|
239
|
+
"",
|
|
240
|
+
].join("\n");
|
|
241
|
+
}
|
|
242
|
+
function truncateOutput(value) {
|
|
243
|
+
const bytes = Buffer.byteLength(value, "utf-8");
|
|
244
|
+
if (bytes <= MAX_COMMAND_OUTPUT_BYTES) {
|
|
245
|
+
return { text: value, truncated: false };
|
|
246
|
+
}
|
|
247
|
+
const buffer = Buffer.from(value, "utf-8");
|
|
248
|
+
return {
|
|
249
|
+
text: buffer.subarray(0, MAX_COMMAND_OUTPUT_BYTES).toString("utf-8") +
|
|
250
|
+
`\n...[truncated after ${MAX_COMMAND_OUTPUT_BYTES} bytes]`,
|
|
251
|
+
truncated: true,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
function shellExecutable() {
|
|
255
|
+
if (process.platform === "win32") {
|
|
256
|
+
return {
|
|
257
|
+
command: process.env.ComSpec || "cmd.exe",
|
|
258
|
+
argsPrefix: ["/d", "/s", "/c"],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return { command: process.env.SHELL || "/bin/sh", argsPrefix: ["-lc"] };
|
|
262
|
+
}
|
|
263
|
+
function runShellCommand(commandText, workspaceRoot, cwd, timeoutMs, inactivityTimeoutMs) {
|
|
264
|
+
return new Promise((resolve, reject) => {
|
|
265
|
+
const shell = shellExecutable();
|
|
266
|
+
const startedAt = new Date();
|
|
267
|
+
const child = spawn(shell.command, [...shell.argsPrefix, commandText], {
|
|
268
|
+
cwd,
|
|
269
|
+
env: process.env,
|
|
270
|
+
windowsHide: true,
|
|
271
|
+
});
|
|
272
|
+
let stdout = "";
|
|
273
|
+
let stderr = "";
|
|
274
|
+
let timedOut = false;
|
|
275
|
+
let inactivityTimedOut = false;
|
|
276
|
+
let killEscalationTimer;
|
|
277
|
+
let inactivityTimer;
|
|
278
|
+
const escalateKill = () => {
|
|
279
|
+
killEscalationTimer = setTimeout(() => {
|
|
280
|
+
if (!child.killed) {
|
|
281
|
+
child.kill("SIGKILL");
|
|
282
|
+
}
|
|
283
|
+
}, 1000);
|
|
284
|
+
killEscalationTimer.unref?.();
|
|
285
|
+
};
|
|
286
|
+
const timer = setTimeout(() => {
|
|
287
|
+
timedOut = true;
|
|
288
|
+
child.kill("SIGTERM");
|
|
289
|
+
escalateKill();
|
|
290
|
+
}, timeoutMs);
|
|
291
|
+
timer.unref?.();
|
|
292
|
+
const resetInactivityTimer = () => {
|
|
293
|
+
if (inactivityTimer) {
|
|
294
|
+
clearTimeout(inactivityTimer);
|
|
295
|
+
}
|
|
296
|
+
inactivityTimer = setTimeout(() => {
|
|
297
|
+
inactivityTimedOut = true;
|
|
298
|
+
timedOut = true;
|
|
299
|
+
child.kill("SIGTERM");
|
|
300
|
+
escalateKill();
|
|
301
|
+
}, inactivityTimeoutMs);
|
|
302
|
+
inactivityTimer.unref?.();
|
|
303
|
+
};
|
|
304
|
+
resetInactivityTimer();
|
|
305
|
+
child.stdout?.setEncoding("utf-8");
|
|
306
|
+
child.stderr?.setEncoding("utf-8");
|
|
307
|
+
child.stdout?.on("data", (chunk) => {
|
|
308
|
+
stdout += chunk;
|
|
309
|
+
resetInactivityTimer();
|
|
310
|
+
});
|
|
311
|
+
child.stderr?.on("data", (chunk) => {
|
|
312
|
+
stderr += chunk;
|
|
313
|
+
resetInactivityTimer();
|
|
314
|
+
});
|
|
315
|
+
child.on("error", (error) => {
|
|
316
|
+
clearTimeout(timer);
|
|
317
|
+
if (inactivityTimer) {
|
|
318
|
+
clearTimeout(inactivityTimer);
|
|
319
|
+
}
|
|
320
|
+
if (killEscalationTimer) {
|
|
321
|
+
clearTimeout(killEscalationTimer);
|
|
322
|
+
}
|
|
323
|
+
reject(error);
|
|
324
|
+
});
|
|
325
|
+
child.on("close", async (exitCode, signal) => {
|
|
326
|
+
clearTimeout(timer);
|
|
327
|
+
if (inactivityTimer) {
|
|
328
|
+
clearTimeout(inactivityTimer);
|
|
329
|
+
}
|
|
330
|
+
if (killEscalationTimer) {
|
|
331
|
+
clearTimeout(killEscalationTimer);
|
|
332
|
+
}
|
|
333
|
+
const endedAt = new Date();
|
|
334
|
+
const outputLineCount = countOutputLines(`${stdout}${stderr}`);
|
|
335
|
+
try {
|
|
336
|
+
if (outputLineCount > COMMAND_OUTPUT_SPILLOVER_LINE_LIMIT) {
|
|
337
|
+
const outputFilePath = await writeCommandOutputSpilloverFile(workspaceRoot, buildCommandOutputSpilloverContent({
|
|
338
|
+
commandText,
|
|
339
|
+
cwd,
|
|
340
|
+
startedAt,
|
|
341
|
+
endedAt,
|
|
342
|
+
exitCode,
|
|
343
|
+
signal,
|
|
344
|
+
timedOut,
|
|
345
|
+
inactivityTimedOut,
|
|
346
|
+
stdout,
|
|
347
|
+
stderr,
|
|
348
|
+
}));
|
|
349
|
+
resolve({
|
|
350
|
+
exitCode,
|
|
351
|
+
signal,
|
|
352
|
+
stdout: formatCommandOutputSpilloverStdout(outputFilePath, outputLineCount),
|
|
353
|
+
stderr: "",
|
|
354
|
+
durationMs: endedAt.getTime() - startedAt.getTime(),
|
|
355
|
+
timedOut,
|
|
356
|
+
inactivityTimedOut,
|
|
357
|
+
outputSpilled: true,
|
|
358
|
+
outputFilePath: toDisplayPath(outputFilePath),
|
|
359
|
+
outputLineCount,
|
|
360
|
+
});
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const truncatedStdout = truncateOutput(stdout);
|
|
364
|
+
const truncatedStderr = truncateOutput(stderr);
|
|
365
|
+
resolve({
|
|
366
|
+
exitCode,
|
|
367
|
+
signal,
|
|
368
|
+
stdout: truncatedStdout.text,
|
|
369
|
+
stderr: truncatedStderr.text,
|
|
370
|
+
durationMs: endedAt.getTime() - startedAt.getTime(),
|
|
371
|
+
timedOut,
|
|
372
|
+
inactivityTimedOut,
|
|
373
|
+
outputSpilled: false,
|
|
374
|
+
outputLineCount,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
reject(error);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
function writeFileTool(workingDirectory) {
|
|
384
|
+
return {
|
|
385
|
+
kind: "builtin",
|
|
386
|
+
toolName: "write_file",
|
|
387
|
+
exposedName: "write_file",
|
|
388
|
+
description: "Create or overwrite a UTF-8 text file inside the current workspace. Parent directories are created automatically.",
|
|
389
|
+
inputSchema: {
|
|
390
|
+
type: "object",
|
|
391
|
+
properties: {
|
|
392
|
+
file_path: {
|
|
393
|
+
type: "string",
|
|
394
|
+
description: "Workspace-relative file path to write.",
|
|
395
|
+
},
|
|
396
|
+
content: {
|
|
397
|
+
type: "string",
|
|
398
|
+
description: "Complete UTF-8 file content.",
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
required: ["file_path", "content"],
|
|
402
|
+
additionalProperties: false,
|
|
403
|
+
},
|
|
404
|
+
async execute(args) {
|
|
405
|
+
const filePath = readStringArg(args, ["file_path", "filePath"], "file_path");
|
|
406
|
+
const content = readStringArg(args, ["content"], "content");
|
|
407
|
+
const result = await writeFileAtomically(workingDirectory, filePath, content);
|
|
408
|
+
return {
|
|
409
|
+
ok: true,
|
|
410
|
+
tool: "write_file",
|
|
411
|
+
workspacePath: toDisplayPath(result.workspaceRoot),
|
|
412
|
+
filePath: toDisplayPath(result.targetPath),
|
|
413
|
+
bytes: result.bytes,
|
|
414
|
+
createdParentDirectory: result.createdParentDirectory,
|
|
415
|
+
};
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
function editFileTool(workingDirectory) {
|
|
420
|
+
return {
|
|
421
|
+
kind: "builtin",
|
|
422
|
+
toolName: "edit_file",
|
|
423
|
+
exposedName: "edit_file",
|
|
424
|
+
description: "Replace exactly one occurrence of old_string with new_string in a UTF-8 workspace text file.",
|
|
425
|
+
inputSchema: {
|
|
426
|
+
type: "object",
|
|
427
|
+
properties: {
|
|
428
|
+
file_path: {
|
|
429
|
+
type: "string",
|
|
430
|
+
description: "Workspace-relative file path to edit.",
|
|
431
|
+
},
|
|
432
|
+
old_string: {
|
|
433
|
+
type: "string",
|
|
434
|
+
description: "Existing text to replace. Must occur exactly once.",
|
|
435
|
+
},
|
|
436
|
+
new_string: { type: "string", description: "Replacement text." },
|
|
437
|
+
},
|
|
438
|
+
required: ["file_path", "old_string", "new_string"],
|
|
439
|
+
additionalProperties: false,
|
|
440
|
+
},
|
|
441
|
+
async execute(args) {
|
|
442
|
+
const filePath = readStringArg(args, ["file_path", "filePath"], "file_path");
|
|
443
|
+
const oldString = readStringArg(args, ["old_string", "oldString"], "old_string");
|
|
444
|
+
const newString = readStringArg(args, ["new_string", "newString"], "new_string");
|
|
445
|
+
if (!oldString) {
|
|
446
|
+
throw new Error("old_string must not be empty");
|
|
447
|
+
}
|
|
448
|
+
const current = await readFileInsideWorkspace(workingDirectory, filePath);
|
|
449
|
+
const occurrences = countOccurrences(current.content, oldString);
|
|
450
|
+
if (occurrences === 0) {
|
|
451
|
+
throw new Error("old_string was not found in file");
|
|
452
|
+
}
|
|
453
|
+
if (occurrences > 1) {
|
|
454
|
+
throw new Error(`old_string occurs ${occurrences} times; provide a unique match`);
|
|
455
|
+
}
|
|
456
|
+
const updatedContent = current.content.replace(oldString, newString);
|
|
457
|
+
const result = await writeFileAtomically(workingDirectory, filePath, updatedContent);
|
|
458
|
+
return {
|
|
459
|
+
ok: true,
|
|
460
|
+
tool: "edit_file",
|
|
461
|
+
workspacePath: toDisplayPath(result.workspaceRoot),
|
|
462
|
+
filePath: toDisplayPath(result.targetPath),
|
|
463
|
+
replacements: 1,
|
|
464
|
+
bytes: result.bytes,
|
|
465
|
+
};
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
function readFileTool(workingDirectory) {
|
|
470
|
+
return {
|
|
471
|
+
kind: "builtin",
|
|
472
|
+
toolName: "read_file",
|
|
473
|
+
exposedName: "read_file",
|
|
474
|
+
description: "Read a UTF-8 text file inside the current workspace.",
|
|
475
|
+
inputSchema: {
|
|
476
|
+
type: "object",
|
|
477
|
+
properties: {
|
|
478
|
+
file_path: {
|
|
479
|
+
type: "string",
|
|
480
|
+
description: "Workspace-relative file path to read.",
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
required: ["file_path"],
|
|
484
|
+
additionalProperties: false,
|
|
485
|
+
},
|
|
486
|
+
async execute(args) {
|
|
487
|
+
const filePath = readStringArg(args, ["file_path", "filePath"], "file_path");
|
|
488
|
+
const result = await readFileInsideWorkspace(workingDirectory, filePath);
|
|
489
|
+
return {
|
|
490
|
+
ok: true,
|
|
491
|
+
tool: "read_file",
|
|
492
|
+
workspacePath: toDisplayPath(result.workspaceRoot),
|
|
493
|
+
filePath: toDisplayPath(result.targetPath),
|
|
494
|
+
content: result.content,
|
|
495
|
+
};
|
|
496
|
+
},
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
function bashTool(workingDirectory) {
|
|
500
|
+
return {
|
|
501
|
+
kind: "builtin",
|
|
502
|
+
toolName: "Bash",
|
|
503
|
+
exposedName: "Bash",
|
|
504
|
+
description: "Execute a shell command in the current workspace. Use for local diagnostics, builds, tests, and OS commands. The command runs with a bounded timeout and returns stdout, stderr, and exit code.",
|
|
505
|
+
inputSchema: {
|
|
506
|
+
type: "object",
|
|
507
|
+
properties: {
|
|
508
|
+
command: { type: "string", description: "Shell command to execute." },
|
|
509
|
+
cwd: {
|
|
510
|
+
type: "string",
|
|
511
|
+
description: "Optional workspace-relative working directory. Defaults to the workspace root.",
|
|
512
|
+
},
|
|
513
|
+
timeout_ms: {
|
|
514
|
+
type: "number",
|
|
515
|
+
description: "Optional timeout in milliseconds. Defaults to 30000 and is capped at 600000.",
|
|
516
|
+
},
|
|
517
|
+
inactivity_timeout_ms: {
|
|
518
|
+
type: "number",
|
|
519
|
+
description: "Optional no-output timeout in milliseconds. Defaults to 60000 and is capped at 600000.",
|
|
520
|
+
},
|
|
521
|
+
description: {
|
|
522
|
+
type: "string",
|
|
523
|
+
description: "Optional short reason for running the command.",
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
required: ["command"],
|
|
527
|
+
additionalProperties: false,
|
|
528
|
+
},
|
|
529
|
+
async execute(args) {
|
|
530
|
+
const command = readStringArg(args, ["command"], "command").trim();
|
|
531
|
+
if (!command) {
|
|
532
|
+
throw new Error("command must not be empty");
|
|
533
|
+
}
|
|
534
|
+
const requestedTimeoutMs = readOptionalPositiveIntegerArg(args, ["timeout_ms", "timeoutMs"], DEFAULT_COMMAND_TIMEOUT_MS);
|
|
535
|
+
const timeoutMs = Math.min(requestedTimeoutMs, MAX_COMMAND_TIMEOUT_MS);
|
|
536
|
+
const requestedInactivityTimeoutMs = readOptionalPositiveIntegerArg(args, ["inactivity_timeout_ms", "inactivityTimeoutMs"], DEFAULT_COMMAND_INACTIVITY_TIMEOUT_MS);
|
|
537
|
+
const inactivityTimeoutMs = Math.min(requestedInactivityTimeoutMs, MAX_COMMAND_INACTIVITY_TIMEOUT_MS);
|
|
538
|
+
const cwdArg = readOptionalStringArg(args, ["cwd"]);
|
|
539
|
+
const resolved = await resolveWorkspaceDirectory(workingDirectory, cwdArg);
|
|
540
|
+
const result = await runShellCommand(command, resolved.workspaceRoot, resolved.cwd, timeoutMs, inactivityTimeoutMs);
|
|
541
|
+
return {
|
|
542
|
+
ok: result.exitCode === 0 && !result.timedOut,
|
|
543
|
+
tool: "Bash",
|
|
544
|
+
command,
|
|
545
|
+
cwd: toDisplayPath(resolved.cwd),
|
|
546
|
+
exitCode: result.exitCode,
|
|
547
|
+
signal: result.signal,
|
|
548
|
+
timedOut: result.timedOut,
|
|
549
|
+
inactivityTimedOut: result.inactivityTimedOut,
|
|
550
|
+
timeoutMs,
|
|
551
|
+
inactivityTimeoutMs,
|
|
552
|
+
durationMs: result.durationMs,
|
|
553
|
+
outputSpilled: result.outputSpilled,
|
|
554
|
+
outputFilePath: result.outputFilePath,
|
|
555
|
+
outputLineCount: result.outputLineCount,
|
|
556
|
+
stdout: result.stdout,
|
|
557
|
+
stderr: result.stderr,
|
|
558
|
+
};
|
|
559
|
+
},
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
export function createBuiltInFileTools(workingDirectory) {
|
|
563
|
+
return [
|
|
564
|
+
readFileTool(workingDirectory),
|
|
565
|
+
writeFileTool(workingDirectory),
|
|
566
|
+
editFileTool(workingDirectory),
|
|
567
|
+
bashTool(workingDirectory),
|
|
568
|
+
];
|
|
569
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { ACodeRuntime } from './runtime.js';
|
|
5
|
+
function getFlagValue(args, name) {
|
|
6
|
+
const index = args.indexOf(name);
|
|
7
|
+
if (index >= 0) {
|
|
8
|
+
return args[index + 1];
|
|
9
|
+
}
|
|
10
|
+
const prefixed = args.find((arg) => arg.startsWith(`${name}=`));
|
|
11
|
+
return prefixed ? prefixed.slice(name.length + 1) : undefined;
|
|
12
|
+
}
|
|
13
|
+
async function main() {
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const cwd = path.resolve(getFlagValue(args, '--cwd') || process.cwd());
|
|
16
|
+
const configPath = getFlagValue(args, '--config');
|
|
17
|
+
const skillsDir = getFlagValue(args, '--skills-dir');
|
|
18
|
+
const prompt = getFlagValue(args, '--prompt') || args.filter((arg) => !arg.startsWith('--')).join(' ');
|
|
19
|
+
const runtime = new ACodeRuntime({
|
|
20
|
+
sessionId: getFlagValue(args, '--session-id') || randomUUID(),
|
|
21
|
+
workingDirectory: cwd,
|
|
22
|
+
configPath,
|
|
23
|
+
skillsDir,
|
|
24
|
+
initialPrompt: prompt,
|
|
25
|
+
model: getFlagValue(args, '--model'),
|
|
26
|
+
});
|
|
27
|
+
runtime.on('event', (event) => {
|
|
28
|
+
process.stdout.write(`${JSON.stringify(event)}\n`);
|
|
29
|
+
});
|
|
30
|
+
await runtime.start();
|
|
31
|
+
await runtime.stop();
|
|
32
|
+
}
|
|
33
|
+
main().catch((error) => {
|
|
34
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
35
|
+
process.stderr.write(`[acode] ${message}\n`);
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ACodeCommandSpec } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* 从命令目录加载 Markdown slash commands。
|
|
4
|
+
* 主流程:跳过不存在目录与隐藏文件 -> 解析 `.md` 文件 -> 按文件名稳定排序。
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadAcodeCommands(commandDirs: readonly string[]): Promise<ACodeCommandSpec[]>;
|
|
7
|
+
//# sourceMappingURL=commandLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commandLoader.d.ts","sourceRoot":"","sources":["../../src/commands/commandLoader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAYpD;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAgBnG"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parseAcodeCommandMarkdown } from './commandParser.js';
|
|
4
|
+
async function pathExists(targetPath) {
|
|
5
|
+
try {
|
|
6
|
+
await fs.access(targetPath);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 从命令目录加载 Markdown slash commands。
|
|
15
|
+
* 主流程:跳过不存在目录与隐藏文件 -> 解析 `.md` 文件 -> 按文件名稳定排序。
|
|
16
|
+
*/
|
|
17
|
+
export async function loadAcodeCommands(commandDirs) {
|
|
18
|
+
const commands = [];
|
|
19
|
+
for (const commandDir of commandDirs) {
|
|
20
|
+
if (!(await pathExists(commandDir))) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const entries = await fs.readdir(commandDir, { withFileTypes: true });
|
|
24
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
25
|
+
if (!entry.isFile() || entry.name.startsWith('.') || path.extname(entry.name).toLowerCase() !== '.md') {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const sourcePath = path.join(commandDir, entry.name);
|
|
29
|
+
commands.push(parseAcodeCommandMarkdown(sourcePath, await fs.readFile(sourcePath, 'utf-8')));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return commands;
|
|
33
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ACodeCommandSpec } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* 解析 Markdown slash command。
|
|
4
|
+
* 主流程:读取可选 YAML frontmatter -> 使用正文作为 prompt 模板 -> 缺省命令名取文件名。
|
|
5
|
+
*/
|
|
6
|
+
export declare function parseAcodeCommandMarkdown(sourcePath: string, rawMarkdown: string): ACodeCommandSpec;
|
|
7
|
+
//# sourceMappingURL=commandParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commandParser.d.ts","sourceRoot":"","sources":["../../src/commands/commandParser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AA6DpD;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAuBnG"}
|