@aipack-ai/cli 0.0.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/README.md +210 -0
- package/dist/chunk-DWPSBSRO.js +1402 -0
- package/dist/chunk-DWPSBSRO.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +8 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +179 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,1402 @@
|
|
|
1
|
+
// src/cli.ts
|
|
2
|
+
import chalk7 from "chalk";
|
|
3
|
+
|
|
4
|
+
// src/commands/approvals.ts
|
|
5
|
+
import path2 from "path";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { FileApprovalStore, createApprovalManager } from "@aipack-ai/agent";
|
|
8
|
+
|
|
9
|
+
// src/version.ts
|
|
10
|
+
import os from "os";
|
|
11
|
+
import path from "path";
|
|
12
|
+
|
|
13
|
+
// package.json
|
|
14
|
+
var package_default = {
|
|
15
|
+
name: "@aipack-ai/cli",
|
|
16
|
+
version: "0.0.1",
|
|
17
|
+
description: "aipack CLI - \u57FA\u4E8E @aipack-ai/agent \u7684\u7EC8\u7AEF AI \u52A9\u624B",
|
|
18
|
+
type: "module",
|
|
19
|
+
main: "./dist/index.js",
|
|
20
|
+
types: "./dist/index.d.ts",
|
|
21
|
+
bin: {
|
|
22
|
+
aipack: "dist/cli.js"
|
|
23
|
+
},
|
|
24
|
+
exports: {
|
|
25
|
+
".": {
|
|
26
|
+
types: "./dist/index.d.ts",
|
|
27
|
+
import: "./dist/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
files: [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
scripts: {
|
|
35
|
+
build: "tsup",
|
|
36
|
+
prebuild: "pnpm --filter @aipack-ai/agent build",
|
|
37
|
+
typecheck: "tsc --noEmit",
|
|
38
|
+
dev: "tsx src/cli.ts"
|
|
39
|
+
},
|
|
40
|
+
keywords: [
|
|
41
|
+
"cli",
|
|
42
|
+
"ai",
|
|
43
|
+
"agent",
|
|
44
|
+
"llm",
|
|
45
|
+
"aipack"
|
|
46
|
+
],
|
|
47
|
+
license: "MIT",
|
|
48
|
+
engines: {
|
|
49
|
+
node: ">=18.19.0"
|
|
50
|
+
},
|
|
51
|
+
dependencies: {
|
|
52
|
+
"@aipack-ai/agent": "workspace:*",
|
|
53
|
+
chalk: "^5.3.0"
|
|
54
|
+
},
|
|
55
|
+
devDependencies: {
|
|
56
|
+
"@types/node": "^20.12.0",
|
|
57
|
+
tsup: "^8.5.1",
|
|
58
|
+
tsx: "^4.16.0",
|
|
59
|
+
typescript: "^5.5.0"
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/version.ts
|
|
64
|
+
var APP_NAME = package_default.name.replace(/^@[^/]+\//, "");
|
|
65
|
+
var VERSION = package_default.version;
|
|
66
|
+
function defaultConfigDir() {
|
|
67
|
+
return process.env.AIPACK_CONFIG_DIR ?? path.join(os.homedir(), ".aipack");
|
|
68
|
+
}
|
|
69
|
+
function defaultSessionDir(cwd) {
|
|
70
|
+
return path.join(defaultConfigDir(), "cli-sessions", encodeDir(cwd));
|
|
71
|
+
}
|
|
72
|
+
function encodeDir(p) {
|
|
73
|
+
return p.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/commands/approvals.ts
|
|
77
|
+
function createStore() {
|
|
78
|
+
return new FileApprovalStore({
|
|
79
|
+
baseDir: path2.join(defaultConfigDir(), "approvals")
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
async function handleApprovalsCommand(args) {
|
|
83
|
+
const [sub, ...rest] = args;
|
|
84
|
+
if (!sub || sub === "help" || sub === "--help") {
|
|
85
|
+
printUsage();
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
const store = createStore();
|
|
89
|
+
if (sub === "list") {
|
|
90
|
+
const pending = await store.load();
|
|
91
|
+
if (pending.length === 0) {
|
|
92
|
+
console.log(chalk.dim("\uFF08\u65E0\u672A\u51B3\u5BA1\u6279\u5355\uFF09"));
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
for (const p of pending) {
|
|
96
|
+
printApproval(p);
|
|
97
|
+
}
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
if (sub === "approve" || sub === "deny") {
|
|
101
|
+
const id = rest[0];
|
|
102
|
+
if (!id) {
|
|
103
|
+
console.error(chalk.red(`\u7528\u6CD5: aipack approvals ${sub} <id>`));
|
|
104
|
+
return 1;
|
|
105
|
+
}
|
|
106
|
+
const manager = createApprovalManager({ store });
|
|
107
|
+
await manager.restore();
|
|
108
|
+
const ok = manager.resolve(id, sub === "approve");
|
|
109
|
+
manager.close();
|
|
110
|
+
if (!ok) {
|
|
111
|
+
console.error(chalk.yellow(`\u5BA1\u6279\u5355 ${id} \u4E0D\u5B58\u5728\u6216\u5DF2\u7ED3\u7B97`));
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
114
|
+
console.log(chalk.green(`\u5DF2${sub === "approve" ? "\u6279\u51C6" : "\u9A73\u56DE"} ${id}`));
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
printUsage();
|
|
118
|
+
console.error(chalk.red(`\u672A\u77E5\u5B50\u547D\u4EE4: ${sub}`));
|
|
119
|
+
return 1;
|
|
120
|
+
}
|
|
121
|
+
function printApproval(p) {
|
|
122
|
+
const created = new Date(p.createdAt).toLocaleString();
|
|
123
|
+
const expires = p.expiresAt ? chalk.dim(` \u8FC7\u671F ${new Date(p.expiresAt).toLocaleTimeString()}`) : "";
|
|
124
|
+
console.log(`${chalk.cyan(p.id)} ${chalk.bold(p.toolName)} ${chalk.dim(created)}${expires}`);
|
|
125
|
+
if (p.permissions.length > 0) {
|
|
126
|
+
console.log(chalk.dim(` \u80FD\u529B: ${p.permissions.join(", ")}`));
|
|
127
|
+
}
|
|
128
|
+
const argsStr = JSON.stringify(p.args);
|
|
129
|
+
if (argsStr && argsStr !== "{}") {
|
|
130
|
+
console.log(chalk.dim(` \u53C2\u6570: ${argsStr.length > 120 ? `${argsStr.slice(0, 120)}\u2026` : argsStr}`));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function printUsage() {
|
|
134
|
+
console.log(`\u7528\u6CD5:
|
|
135
|
+
aipack approvals list \u5217\u51FA\u672A\u51B3\u5BA1\u6279\u5355
|
|
136
|
+
aipack approvals approve <id> \u6279\u51C6
|
|
137
|
+
aipack approvals deny <id> \u9A73\u56DE`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/commands/models.ts
|
|
141
|
+
import chalk2 from "chalk";
|
|
142
|
+
import { getBuiltinProviders, getBuiltinModels, hasProviderConfigured } from "@aipack-ai/agent";
|
|
143
|
+
function listModels(search) {
|
|
144
|
+
const term = typeof search === "string" ? search.toLowerCase() : void 0;
|
|
145
|
+
let total = 0;
|
|
146
|
+
for (const provider of getBuiltinProviders()) {
|
|
147
|
+
const models = getBuiltinModels(provider.id);
|
|
148
|
+
const matched = term ? models.filter((m) => m.id.toLowerCase().includes(term) || m.name.toLowerCase().includes(term) || provider.id.includes(term)) : models;
|
|
149
|
+
if (matched.length === 0) continue;
|
|
150
|
+
const configured = hasProviderConfigured(provider.id);
|
|
151
|
+
const badge = configured ? chalk2.green("\u2713") : chalk2.dim("\xB7");
|
|
152
|
+
console.log(`${badge} ${chalk2.bold(provider.name)} ${chalk2.dim(`(${provider.id})`)}`);
|
|
153
|
+
for (const m of matched) {
|
|
154
|
+
const flags = [
|
|
155
|
+
m.reasoning ? chalk2.dim("\u63A8\u7406") : null,
|
|
156
|
+
`${m.contextWindow / 1e3 | 0}k`
|
|
157
|
+
].filter(Boolean).join(" ");
|
|
158
|
+
console.log(` ${m.id.padEnd(44)} ${chalk2.dim(flags)}`);
|
|
159
|
+
total++;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
console.log(chalk2.dim(`
|
|
163
|
+
\u5171 ${total} \u4E2A\u6A21\u578B\u3002\u2713 = \u5DF2\u914D\u7F6E API Key\uFF08${getEnvHint()}\uFF09`));
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
function getEnvHint() {
|
|
167
|
+
const provider = getBuiltinProviders().find((p) => hasProviderConfigured(p.id));
|
|
168
|
+
return provider ? `${provider.envVar} \u5DF2\u8BBE\u7F6E` : "\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55 API Key";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/args.ts
|
|
172
|
+
var VALID_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "max"];
|
|
173
|
+
function isValidThinkingLevel(level) {
|
|
174
|
+
return VALID_THINKING_LEVELS.includes(level);
|
|
175
|
+
}
|
|
176
|
+
function parseArgs(args) {
|
|
177
|
+
const result = {
|
|
178
|
+
messages: [],
|
|
179
|
+
fileArgs: [],
|
|
180
|
+
diagnostics: []
|
|
181
|
+
};
|
|
182
|
+
for (let i = 0; i < args.length; i++) {
|
|
183
|
+
const arg = args[i];
|
|
184
|
+
if (arg === "--help" || arg === "-h") {
|
|
185
|
+
result.help = true;
|
|
186
|
+
} else if (arg === "--version" || arg === "-v") {
|
|
187
|
+
result.version = true;
|
|
188
|
+
} else if (arg === "--mode" && i + 1 < args.length) {
|
|
189
|
+
const mode = args[++i];
|
|
190
|
+
if (mode === "text" || mode === "json") {
|
|
191
|
+
result.mode = mode;
|
|
192
|
+
} else {
|
|
193
|
+
result.diagnostics.push({ type: "error", message: `\u65E0\u6548\u7684 mode: ${mode}\uFF08\u53EF\u9009 text / json\uFF09` });
|
|
194
|
+
}
|
|
195
|
+
} else if (arg === "--continue" || arg === "-c") {
|
|
196
|
+
result.continue = true;
|
|
197
|
+
} else if (arg === "--resume" || arg === "-r") {
|
|
198
|
+
result.resume = true;
|
|
199
|
+
} else if (arg === "--provider" && i + 1 < args.length) {
|
|
200
|
+
result.provider = args[++i];
|
|
201
|
+
} else if (arg === "--model" && i + 1 < args.length) {
|
|
202
|
+
result.model = args[++i];
|
|
203
|
+
} else if (arg === "--api-key" && i + 1 < args.length) {
|
|
204
|
+
result.apiKey = args[++i];
|
|
205
|
+
} else if (arg === "--system-prompt" && i + 1 < args.length) {
|
|
206
|
+
result.systemPrompt = args[++i];
|
|
207
|
+
} else if (arg === "--append-system-prompt" && i + 1 < args.length) {
|
|
208
|
+
result.appendSystemPrompt = result.appendSystemPrompt ?? [];
|
|
209
|
+
result.appendSystemPrompt.push(args[++i]);
|
|
210
|
+
} else if (arg === "--thinking" && i + 1 < args.length) {
|
|
211
|
+
const level = args[++i];
|
|
212
|
+
if (isValidThinkingLevel(level)) {
|
|
213
|
+
result.thinking = level;
|
|
214
|
+
} else {
|
|
215
|
+
result.diagnostics.push({
|
|
216
|
+
type: "warning",
|
|
217
|
+
message: `\u65E0\u6548\u7684\u601D\u8003\u7EA7\u522B "${level}"\u3002\u53EF\u9009: ${VALID_THINKING_LEVELS.join(", ")}`
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
} else if (arg === "--name" || arg === "-n") {
|
|
221
|
+
if (i + 1 < args.length) {
|
|
222
|
+
result.name = args[++i];
|
|
223
|
+
} else {
|
|
224
|
+
result.diagnostics.push({ type: "error", message: "--name \u9700\u8981\u4E00\u4E2A\u503C" });
|
|
225
|
+
}
|
|
226
|
+
} else if (arg === "--no-session") {
|
|
227
|
+
result.noSession = true;
|
|
228
|
+
} else if (arg === "--session" && i + 1 < args.length) {
|
|
229
|
+
result.session = args[++i];
|
|
230
|
+
} else if (arg === "--session-dir" && i + 1 < args.length) {
|
|
231
|
+
result.sessionDir = args[++i];
|
|
232
|
+
} else if ((arg === "--tools" || arg === "-t") && i + 1 < args.length) {
|
|
233
|
+
result.tools = args[++i].split(",").map((s) => s.trim()).filter(Boolean);
|
|
234
|
+
} else if ((arg === "--exclude-tools" || arg === "-xt") && i + 1 < args.length) {
|
|
235
|
+
result.excludeTools = args[++i].split(",").map((s) => s.trim()).filter(Boolean);
|
|
236
|
+
} else if (arg === "--no-tools" || arg === "-nt") {
|
|
237
|
+
result.noTools = true;
|
|
238
|
+
} else if (arg === "--safe") {
|
|
239
|
+
result.safe = true;
|
|
240
|
+
} else if (arg === "--print" || arg === "-p") {
|
|
241
|
+
result.print = true;
|
|
242
|
+
const next = args[i + 1];
|
|
243
|
+
if (next !== void 0 && !next.startsWith("@") && !next.startsWith("-")) {
|
|
244
|
+
result.messages.push(next);
|
|
245
|
+
i++;
|
|
246
|
+
}
|
|
247
|
+
} else if (arg === "--list-models") {
|
|
248
|
+
const next = args[i + 1];
|
|
249
|
+
if (next !== void 0 && !next.startsWith("-") && !next.startsWith("@")) {
|
|
250
|
+
result.listModels = next;
|
|
251
|
+
i++;
|
|
252
|
+
} else {
|
|
253
|
+
result.listModels = true;
|
|
254
|
+
}
|
|
255
|
+
} else if (arg.startsWith("@")) {
|
|
256
|
+
result.fileArgs.push(arg.slice(1));
|
|
257
|
+
} else if (arg.startsWith("--")) {
|
|
258
|
+
result.diagnostics.push({ type: "error", message: `\u672A\u77E5\u9009\u9879: ${arg}` });
|
|
259
|
+
} else if (arg.startsWith("-") && arg !== "-") {
|
|
260
|
+
result.diagnostics.push({ type: "error", message: `\u672A\u77E5\u9009\u9879: ${arg}` });
|
|
261
|
+
} else {
|
|
262
|
+
result.messages.push(arg);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return result;
|
|
266
|
+
}
|
|
267
|
+
function printHelp() {
|
|
268
|
+
console.log(`${APP_NAME} ${VERSION} - \u57FA\u4E8E @aipack-ai/agent \u7684\u7EC8\u7AEF AI \u52A9\u624B
|
|
269
|
+
|
|
270
|
+
${"\u7528\u6CD5:"}
|
|
271
|
+
${APP_NAME} [\u9009\u9879] [@\u6587\u4EF6...] [\u6D88\u606F...]
|
|
272
|
+
|
|
273
|
+
${"\u5B50\u547D\u4EE4:"}
|
|
274
|
+
${APP_NAME} approvals list \u5217\u51FA\u672A\u51B3\u5BA1\u6279\u5355
|
|
275
|
+
${APP_NAME} approvals approve <id> \u6279\u51C6\u5BA1\u6279\u5355
|
|
276
|
+
${APP_NAME} approvals deny <id> \u9A73\u56DE\u5BA1\u6279\u5355
|
|
277
|
+
${APP_NAME} --list-models [\u641C\u7D22] \u5217\u51FA\u53EF\u7528\u6A21\u578B
|
|
278
|
+
|
|
279
|
+
${"\u6A21\u5F0F:"}
|
|
280
|
+
(\u9ED8\u8BA4) \u4EA4\u4E92\u6A21\u5F0F\uFF08REPL\uFF09
|
|
281
|
+
--print, -p [\u6D88\u606F] \u975E\u4EA4\u4E92\uFF1A\u5904\u7406\u4E00\u6B21\u63D0\u793A\u540E\u9000\u51FA\uFF08\u652F\u6301\u7BA1\u9053 stdin\uFF09
|
|
282
|
+
--mode json \u4EE5 JSON \u884C\u8F93\u51FA\u5168\u90E8\u6D41\u5F0F\u4E8B\u4EF6
|
|
283
|
+
|
|
284
|
+
${"\u6A21\u578B\u9009\u9879:"}
|
|
285
|
+
--provider <\u540D\u79F0> \u63D0\u4F9B\u5546\uFF08openai/deepseek/anthropic/google...\uFF09
|
|
286
|
+
--model <id> \u6A21\u578B ID\uFF0C\u652F\u6301 provider/id \u7EC4\u5408\u5199\u6CD5
|
|
287
|
+
--api-key <key> API Key\uFF08\u8986\u76D6\u73AF\u5883\u53D8\u91CF\uFF09
|
|
288
|
+
--thinking <\u7EA7\u522B> \u601D\u8003\u7EA7\u522B: off/minimal/low/medium/high/max
|
|
289
|
+
|
|
290
|
+
${"\u4F1A\u8BDD\u9009\u9879:"}
|
|
291
|
+
--continue, -c \u7EE7\u7EED\u5F53\u524D\u76EE\u5F55\u6700\u8FD1\u7684\u4F1A\u8BDD
|
|
292
|
+
--resume, -r \u6D4F\u89C8\u5E76\u9009\u62E9\u5386\u53F2\u4F1A\u8BDD
|
|
293
|
+
--session <\u540D\u79F0> \u4F7F\u7528\u6307\u5B9A\u4F1A\u8BDD
|
|
294
|
+
--name, -n <\u540D\u79F0> \u4E3A\u65B0\u4F1A\u8BDD\u547D\u540D
|
|
295
|
+
--session-dir <\u76EE\u5F55> \u81EA\u5B9A\u4E49\u4F1A\u8BDD\u5B58\u50A8\u76EE\u5F55
|
|
296
|
+
--no-session \u4E34\u65F6\u4F1A\u8BDD\uFF08\u4E0D\u6301\u4E45\u5316\uFF09
|
|
297
|
+
|
|
298
|
+
${"\u5DE5\u5177\u9009\u9879:"}
|
|
299
|
+
--tools, -t <\u5217\u8868> \u5DE5\u5177\u767D\u540D\u5355\uFF08\u9017\u53F7\u5206\u9694\uFF09
|
|
300
|
+
--exclude-tools, -xt <\u5217\u8868> \u5DE5\u5177\u9ED1\u540D\u5355\uFF08\u9017\u53F7\u5206\u9694\uFF09
|
|
301
|
+
--no-tools, -nt \u7981\u7528\u5168\u90E8\u5DE5\u5177
|
|
302
|
+
--safe \u4FDD\u5B88\u6A21\u5F0F\uFF1A\u5199\u6587\u4EF6/shell \u5168\u90E8\u4EBA\u5DE5\u786E\u8BA4
|
|
303
|
+
|
|
304
|
+
\u5185\u7F6E\u5DE5\u5177: read, write, edit, bash
|
|
305
|
+
\u9ED8\u8BA4\u6743\u9650: \u8BFB\u5199\u6587\u4EF6\u9759\u9ED8\u653E\u884C\uFF08\u5DE5\u4F5C\u533A\u8303\u56F4\u5185\uFF09\uFF1Bbash \u4EC5\u5371\u9669\u547D\u4EE4
|
|
306
|
+
\uFF08sudo\u3001rm -rf ~\u3001\u78C1\u76D8\u5199\u5165\u3001\u8FDC\u7A0B\u811A\u672C\u7BA1\u9053\u7B49\uFF09\u9700\u786E\u8BA4
|
|
307
|
+
|
|
308
|
+
${"\u5176\u4ED6:"}
|
|
309
|
+
--system-prompt <\u6587\u672C> \u66FF\u6362\u9ED8\u8BA4\u7CFB\u7EDF\u63D0\u793A\u8BCD
|
|
310
|
+
--append-system-prompt <\u6587\u672C> \u8FFD\u52A0\u7CFB\u7EDF\u63D0\u793A\u8BCD\uFF08\u53EF\u591A\u6B21\uFF09
|
|
311
|
+
--help, -h \u663E\u793A\u5E2E\u52A9
|
|
312
|
+
--version, -v \u663E\u793A\u7248\u672C
|
|
313
|
+
|
|
314
|
+
${"\u793A\u4F8B:"}
|
|
315
|
+
# \u4EA4\u4E92\u6A21\u5F0F
|
|
316
|
+
${APP_NAME}
|
|
317
|
+
|
|
318
|
+
# \u975E\u4EA4\u4E92\u5355\u6B21\u63D0\u95EE\uFF08\u652F\u6301\u7BA1\u9053\uFF09
|
|
319
|
+
cat README.md | ${APP_NAME} -p "\u603B\u7ED3\u8FD9\u6BB5\u6587\u672C"
|
|
320
|
+
|
|
321
|
+
# \u6307\u5B9A\u6A21\u578B
|
|
322
|
+
${APP_NAME} --provider deepseek --model deepseek-chat "\u4F60\u597D"
|
|
323
|
+
|
|
324
|
+
# provider/id \u7EC4\u5408\u5199\u6CD5
|
|
325
|
+
${APP_NAME} --model anthropic/claude-sonnet-4-20250514 "\u5E2E\u6211\u91CD\u6784\u4EE3\u7801"
|
|
326
|
+
|
|
327
|
+
# \u9644\u5E26\u6587\u4EF6\u4E0A\u4E0B\u6587
|
|
328
|
+
${APP_NAME} @package.json "\u5206\u6790\u8FD9\u4E2A\u6587\u4EF6\u7684\u4F9D\u8D56"
|
|
329
|
+
|
|
330
|
+
# \u7EE7\u7EED\u4E0A\u6B21\u4F1A\u8BDD
|
|
331
|
+
${APP_NAME} -c "\u6211\u4EEC\u521A\u624D\u804A\u5230\u54EA\u91CC\u4E86\uFF1F"
|
|
332
|
+
|
|
333
|
+
# \u53EA\u8BFB\u6A21\u5F0F
|
|
334
|
+
${APP_NAME} --tools read -p "\u5BA1\u67E5 src/ \u4E0B\u7684\u4EE3\u7801"
|
|
335
|
+
|
|
336
|
+
${"\u73AF\u5883\u53D8\u91CF:"}
|
|
337
|
+
OPENAI_API_KEY / DEEPSEEK_API_KEY / ANTHROPIC_API_KEY / GOOGLE_API_KEY ...
|
|
338
|
+
AIPACK_CONFIG_DIR \u914D\u7F6E\u76EE\u5F55\uFF08\u9ED8\u8BA4 ~/.aipack\uFF09
|
|
339
|
+
`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/builder.ts
|
|
343
|
+
import fs2 from "fs/promises";
|
|
344
|
+
import path4 from "path";
|
|
345
|
+
import { pathToFileURL } from "url";
|
|
346
|
+
import {
|
|
347
|
+
createRuntime,
|
|
348
|
+
adaptAiModel,
|
|
349
|
+
createStreamFnFromAi,
|
|
350
|
+
getBuiltinModel,
|
|
351
|
+
getBuiltinModels as getBuiltinModels2,
|
|
352
|
+
getBuiltinProviders as getBuiltinProviders2,
|
|
353
|
+
BUILTIN_PROVIDERS,
|
|
354
|
+
hasProviderConfigured as hasProviderConfigured2,
|
|
355
|
+
createFileSessionStorage,
|
|
356
|
+
createPermissionPolicy,
|
|
357
|
+
createApprovalManager as createApprovalManager2,
|
|
358
|
+
FileApprovalStore as FileApprovalStore2
|
|
359
|
+
} from "@aipack-ai/agent";
|
|
360
|
+
|
|
361
|
+
// src/tools.ts
|
|
362
|
+
import fs from "fs/promises";
|
|
363
|
+
import path3 from "path";
|
|
364
|
+
import { spawn } from "child_process";
|
|
365
|
+
var workspaceRoot = process.cwd();
|
|
366
|
+
function resolveInWorkspace(p) {
|
|
367
|
+
const abs = path3.resolve(workspaceRoot, p);
|
|
368
|
+
if (!abs.startsWith(workspaceRoot)) {
|
|
369
|
+
throw new Error(`\u8DEF\u5F84\u8D8A\u754C\uFF1A${p}\uFF08\u5DE5\u4F5C\u533A\uFF1A${workspaceRoot}\uFF09`);
|
|
370
|
+
}
|
|
371
|
+
return abs;
|
|
372
|
+
}
|
|
373
|
+
function textResult(text, details) {
|
|
374
|
+
return { content: [{ type: "text", text }], details: details ?? {} };
|
|
375
|
+
}
|
|
376
|
+
function errorResult(message) {
|
|
377
|
+
return {
|
|
378
|
+
content: [{ type: "text", text: `\u9519\u8BEF: ${message}` }],
|
|
379
|
+
details: { error: message }
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
var MAX_READ_CHARS = 1e5;
|
|
383
|
+
var readTool = {
|
|
384
|
+
name: "read",
|
|
385
|
+
description: "\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9\u3002\u8FD4\u56DE\u6587\u672C\u5185\u5BB9\uFF0C\u8D85\u957F\u81EA\u52A8\u622A\u65AD\u3002",
|
|
386
|
+
parameters: {
|
|
387
|
+
type: "object",
|
|
388
|
+
properties: {
|
|
389
|
+
file: { type: "string", description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u5DE5\u4F5C\u533A\uFF09" },
|
|
390
|
+
offset: { type: "number", description: "\u8D77\u59CB\u884C\u53F7\uFF08\u4ECE 1 \u5F00\u59CB\uFF0C\u53EF\u9009\uFF09" },
|
|
391
|
+
limit: { type: "number", description: "\u8BFB\u53D6\u884C\u6570\uFF08\u53EF\u9009\uFF09" }
|
|
392
|
+
},
|
|
393
|
+
required: ["file"]
|
|
394
|
+
},
|
|
395
|
+
permissions: ["fs:read"],
|
|
396
|
+
async execute(_id, rawArgs) {
|
|
397
|
+
const args = rawArgs;
|
|
398
|
+
try {
|
|
399
|
+
const abs = resolveInWorkspace(args.file);
|
|
400
|
+
let content = await fs.readFile(abs, "utf8");
|
|
401
|
+
if (args.offset !== void 0 || args.limit !== void 0) {
|
|
402
|
+
const lines = content.split("\n");
|
|
403
|
+
const start = Math.max(0, (args.offset ?? 1) - 1);
|
|
404
|
+
const end = args.limit !== void 0 ? start + args.limit : lines.length;
|
|
405
|
+
content = lines.slice(start, end).join("\n");
|
|
406
|
+
}
|
|
407
|
+
if (content.length > MAX_READ_CHARS) {
|
|
408
|
+
content = content.slice(0, MAX_READ_CHARS) + `
|
|
409
|
+
...[\u622A\u65AD\uFF0C\u5171 ${content.length} \u5B57\u7B26]`;
|
|
410
|
+
}
|
|
411
|
+
return textResult(content, { file: args.file, truncated: content.length >= MAX_READ_CHARS });
|
|
412
|
+
} catch (err) {
|
|
413
|
+
return errorResult(String(err instanceof Error ? err.message : err));
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
var writeTool = {
|
|
418
|
+
name: "write",
|
|
419
|
+
description: "\u5199\u5165\u6587\u4EF6\uFF08\u521B\u5EFA\u6216\u8986\u76D6\uFF09\u3002\u81EA\u52A8\u521B\u5EFA\u7236\u76EE\u5F55\u3002",
|
|
420
|
+
parameters: {
|
|
421
|
+
type: "object",
|
|
422
|
+
properties: {
|
|
423
|
+
file: { type: "string", description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u5DE5\u4F5C\u533A\uFF09" },
|
|
424
|
+
content: { type: "string", description: "\u5B8C\u6574\u6587\u4EF6\u5185\u5BB9" }
|
|
425
|
+
},
|
|
426
|
+
required: ["file", "content"]
|
|
427
|
+
},
|
|
428
|
+
permissions: ["fs:write"],
|
|
429
|
+
async execute(_id, rawArgs) {
|
|
430
|
+
const args = rawArgs;
|
|
431
|
+
try {
|
|
432
|
+
const abs = resolveInWorkspace(args.file);
|
|
433
|
+
await fs.mkdir(path3.dirname(abs), { recursive: true });
|
|
434
|
+
await fs.writeFile(abs, args.content, "utf8");
|
|
435
|
+
return textResult(`\u5DF2\u5199\u5165 ${args.file}\uFF08${args.content.length} \u5B57\u7B26\uFF09`, { file: args.file });
|
|
436
|
+
} catch (err) {
|
|
437
|
+
return errorResult(String(err instanceof Error ? err.message : err));
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
var editTool = {
|
|
442
|
+
name: "edit",
|
|
443
|
+
description: "\u7F16\u8F91\u6587\u4EF6\uFF1A\u5C06 oldString \u7684\u9996\u6B21\u51FA\u73B0\u66FF\u6362\u4E3A newString\u3002oldString \u5FC5\u987B\u552F\u4E00\u5339\u914D\u3002",
|
|
444
|
+
parameters: {
|
|
445
|
+
type: "object",
|
|
446
|
+
properties: {
|
|
447
|
+
file: { type: "string", description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u5DE5\u4F5C\u533A\uFF09" },
|
|
448
|
+
oldString: { type: "string", description: "\u88AB\u66FF\u6362\u7684\u6587\u672C\uFF08\u987B\u5728\u6587\u4EF6\u4E2D\u552F\u4E00\uFF09" },
|
|
449
|
+
newString: { type: "string", description: "\u66FF\u6362\u540E\u7684\u6587\u672C" }
|
|
450
|
+
},
|
|
451
|
+
required: ["file", "oldString", "newString"]
|
|
452
|
+
},
|
|
453
|
+
permissions: ["fs:write"],
|
|
454
|
+
async execute(_id, rawArgs) {
|
|
455
|
+
const args = rawArgs;
|
|
456
|
+
try {
|
|
457
|
+
const abs = resolveInWorkspace(args.file);
|
|
458
|
+
const content = await fs.readFile(abs, "utf8");
|
|
459
|
+
const first = content.indexOf(args.oldString);
|
|
460
|
+
if (first === -1) {
|
|
461
|
+
return errorResult(`\u5728 ${args.file} \u4E2D\u672A\u627E\u5230 oldString`);
|
|
462
|
+
}
|
|
463
|
+
if (content.indexOf(args.oldString, first + 1) !== -1) {
|
|
464
|
+
return errorResult(`oldString \u5728 ${args.file} \u4E2D\u51FA\u73B0\u591A\u6B21\uFF0C\u8BF7\u63D0\u4F9B\u66F4\u957F\u7684\u4E0A\u4E0B\u6587\u4F7F\u5176\u552F\u4E00`);
|
|
465
|
+
}
|
|
466
|
+
const updated = content.slice(0, first) + args.newString + content.slice(first + args.oldString.length);
|
|
467
|
+
await fs.writeFile(abs, updated, "utf8");
|
|
468
|
+
return textResult(`\u5DF2\u7F16\u8F91 ${args.file}`, { file: args.file });
|
|
469
|
+
} catch (err) {
|
|
470
|
+
return errorResult(String(err instanceof Error ? err.message : err));
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
function runShell(command, timeoutMs) {
|
|
475
|
+
return new Promise((resolve) => {
|
|
476
|
+
const child = spawn(command, {
|
|
477
|
+
shell: process.env.SHELL ?? "/bin/bash",
|
|
478
|
+
cwd: workspaceRoot,
|
|
479
|
+
env: { ...process.env, AIPACK_CLI: "true" },
|
|
480
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
481
|
+
});
|
|
482
|
+
let stdout = "";
|
|
483
|
+
let stderr = "";
|
|
484
|
+
let settled = false;
|
|
485
|
+
const timer = setTimeout(() => {
|
|
486
|
+
if (!settled) {
|
|
487
|
+
settled = true;
|
|
488
|
+
child.kill("SIGKILL");
|
|
489
|
+
resolve({ stdout, stderr: stderr + `
|
|
490
|
+
[\u547D\u4EE4\u8D85\u65F6\uFF08${timeoutMs}ms\uFF09\u88AB\u7EC8\u6B62]`, code: 124 });
|
|
491
|
+
}
|
|
492
|
+
}, timeoutMs);
|
|
493
|
+
child.stdout.on("data", (d) => {
|
|
494
|
+
stdout += d.toString();
|
|
495
|
+
});
|
|
496
|
+
child.stderr.on("data", (d) => {
|
|
497
|
+
stderr += d.toString();
|
|
498
|
+
});
|
|
499
|
+
child.on("close", (code) => {
|
|
500
|
+
if (!settled) {
|
|
501
|
+
settled = true;
|
|
502
|
+
clearTimeout(timer);
|
|
503
|
+
resolve({ stdout, stderr, code: code ?? 0 });
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
child.on("error", (err) => {
|
|
507
|
+
if (!settled) {
|
|
508
|
+
settled = true;
|
|
509
|
+
clearTimeout(timer);
|
|
510
|
+
resolve({ stdout, stderr: String(err), code: -1 });
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
var bashTool = {
|
|
516
|
+
name: "bash",
|
|
517
|
+
description: "\u5728\u5DE5\u4F5C\u533A\u6267\u884C shell \u5757\u547D\u4EE4\u5E76\u8FD4\u56DE\u8F93\u51FA\u3002\u9002\u7528\u4E8E\u6587\u4EF6\u641C\u7D22\u3001\u6784\u5EFA\u3001\u6D4B\u8BD5\u7B49\u64CD\u4F5C\u3002",
|
|
518
|
+
parameters: {
|
|
519
|
+
type: "object",
|
|
520
|
+
properties: {
|
|
521
|
+
command: { type: "string", description: "\u8981\u6267\u884C\u7684\u547D\u4EE4" },
|
|
522
|
+
timeoutMs: { type: "number", description: "\u8D85\u65F6\u6BEB\u79D2\u6570\uFF08\u9ED8\u8BA4 60000\uFF09" }
|
|
523
|
+
},
|
|
524
|
+
required: ["command"]
|
|
525
|
+
},
|
|
526
|
+
permissions: ["shell:exec"],
|
|
527
|
+
async execute(_id, rawArgs) {
|
|
528
|
+
const args = rawArgs;
|
|
529
|
+
const timeout = args.timeoutMs ?? 6e4;
|
|
530
|
+
try {
|
|
531
|
+
const { stdout, stderr, code } = await runShell(args.command, timeout);
|
|
532
|
+
const parts = [];
|
|
533
|
+
if (stdout.trim()) parts.push(`[stdout]
|
|
534
|
+
${stdout.slice(0, MAX_READ_CHARS)}`);
|
|
535
|
+
if (stderr.trim()) parts.push(`[stderr]
|
|
536
|
+
${stderr.slice(0, MAX_READ_CHARS)}`);
|
|
537
|
+
parts.push(`[\u9000\u51FA\u7801 ${code}]`);
|
|
538
|
+
return textResult(parts.join("\n"), { command: args.command, code });
|
|
539
|
+
} catch (err) {
|
|
540
|
+
return errorResult(String(err instanceof Error ? err.message : err));
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
var BUILTIN_TOOLS = [readTool, writeTool, editTool, bashTool];
|
|
545
|
+
function selectTools(options) {
|
|
546
|
+
if (options.noTools) return [];
|
|
547
|
+
let selected = [...BUILTIN_TOOLS];
|
|
548
|
+
if (options.tools && options.tools.length > 0) {
|
|
549
|
+
const allow = new Set(options.tools);
|
|
550
|
+
selected = selected.filter((t) => allow.has(t.name));
|
|
551
|
+
}
|
|
552
|
+
if (options.excludeTools && options.excludeTools.length > 0) {
|
|
553
|
+
const deny = new Set(options.excludeTools);
|
|
554
|
+
selected = selected.filter((t) => !deny.has(t.name));
|
|
555
|
+
}
|
|
556
|
+
return selected;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// src/builder.ts
|
|
560
|
+
async function loadConfig(cwd) {
|
|
561
|
+
for (const file of ["aipack.config.js", "aipack.config.mjs"]) {
|
|
562
|
+
const full = path4.join(cwd, file);
|
|
563
|
+
try {
|
|
564
|
+
await fs2.access(full);
|
|
565
|
+
const mod = await import(pathToFileURL(full).href);
|
|
566
|
+
const config = mod.default ?? mod;
|
|
567
|
+
return config ?? {};
|
|
568
|
+
} catch {
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return {};
|
|
572
|
+
}
|
|
573
|
+
function splitModelSpec(args) {
|
|
574
|
+
if (args.model && args.model.includes("/")) {
|
|
575
|
+
const idx = args.model.indexOf("/");
|
|
576
|
+
return { provider: args.model.slice(0, idx), modelId: args.model.slice(idx + 1) };
|
|
577
|
+
}
|
|
578
|
+
return { provider: args.provider, modelId: args.model };
|
|
579
|
+
}
|
|
580
|
+
function detectDefaultProvider() {
|
|
581
|
+
for (const p of getBuiltinProviders2()) {
|
|
582
|
+
if (hasProviderConfigured2(p.id)) {
|
|
583
|
+
const models = getBuiltinModels2(p.id);
|
|
584
|
+
if (models.length > 0) return { id: p.id, modelId: models[0].id };
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return { id: "openai", modelId: "gpt-4o-mini" };
|
|
588
|
+
}
|
|
589
|
+
function buildCustomModel(providerId, modelId) {
|
|
590
|
+
const meta = BUILTIN_PROVIDERS.find((p) => p.id === providerId);
|
|
591
|
+
const api = providerId === "anthropic" ? "anthropic-messages" : "openai-completions";
|
|
592
|
+
return {
|
|
593
|
+
id: modelId,
|
|
594
|
+
name: modelId,
|
|
595
|
+
provider: providerId,
|
|
596
|
+
api,
|
|
597
|
+
baseUrl: meta?.baseUrl,
|
|
598
|
+
reasoning: false,
|
|
599
|
+
input: ["text"],
|
|
600
|
+
contextWindow: 128e3,
|
|
601
|
+
maxTokens: 16384
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
function resolveModel(args) {
|
|
605
|
+
const spec = splitModelSpec(args);
|
|
606
|
+
const fallback = detectDefaultProvider();
|
|
607
|
+
const providerId = spec.provider ?? fallback.id;
|
|
608
|
+
const modelId = spec.modelId ?? fallback.modelId;
|
|
609
|
+
const builtin = getBuiltinModel(providerId, modelId);
|
|
610
|
+
if (builtin) return { aiModel: builtin, custom: false };
|
|
611
|
+
return { aiModel: buildCustomModel(providerId, modelId), custom: true };
|
|
612
|
+
}
|
|
613
|
+
async function listSessionsByRecency(storage) {
|
|
614
|
+
const keys = await storage.list();
|
|
615
|
+
const entries = await Promise.all(
|
|
616
|
+
keys.slice(0, 200).map(async (key) => {
|
|
617
|
+
const s = await storage.load(key);
|
|
618
|
+
return { key, updatedAt: s?.updatedAt ?? "" };
|
|
619
|
+
})
|
|
620
|
+
);
|
|
621
|
+
return entries.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)).map((e) => e.key);
|
|
622
|
+
}
|
|
623
|
+
async function resolveSessionKey(args, storage) {
|
|
624
|
+
if (args.noSession) return { sessionKey: `ephemeral-${Date.now().toString(36)}`, resumed: false };
|
|
625
|
+
if (args.session) return { sessionKey: sanitizeKey(args.session), resumed: true };
|
|
626
|
+
if (args.name) return { sessionKey: sanitizeKey(args.name), resumed: false };
|
|
627
|
+
if (args.continue && storage) {
|
|
628
|
+
const recent = await listSessionsByRecency(storage);
|
|
629
|
+
if (recent.length > 0) return { sessionKey: recent[0], resumed: true };
|
|
630
|
+
}
|
|
631
|
+
return { sessionKey: `s-${Date.now().toString(36)}`, resumed: false };
|
|
632
|
+
}
|
|
633
|
+
function sanitizeKey(name) {
|
|
634
|
+
return name.replace(/[^a-zA-Z0-9_\-:.]/g, "_");
|
|
635
|
+
}
|
|
636
|
+
var DEFAULT_APPROVAL_CAPABILITIES = ["fs:write", "shell:exec"];
|
|
637
|
+
async function buildRuntime(options) {
|
|
638
|
+
const { args, cwd } = options;
|
|
639
|
+
const config = await loadConfig(cwd);
|
|
640
|
+
const model = resolveModel(args);
|
|
641
|
+
const streamFn = createStreamFnFromAi(model.aiModel, {
|
|
642
|
+
apiKey: args.apiKey
|
|
643
|
+
});
|
|
644
|
+
const tools = selectTools({
|
|
645
|
+
tools: args.tools,
|
|
646
|
+
excludeTools: args.excludeTools,
|
|
647
|
+
noTools: args.noTools
|
|
648
|
+
});
|
|
649
|
+
const storage = args.noSession ? void 0 : createFileSessionStorage({
|
|
650
|
+
baseDir: args.sessionDir ?? defaultSessionDir(cwd)
|
|
651
|
+
});
|
|
652
|
+
const session = await resolveSessionKey(args, storage);
|
|
653
|
+
const approvalsEnabled = options.approvalsEnabled ?? config.approvals?.enabled === true;
|
|
654
|
+
const approvalCaps = new Set(config.approvals?.capabilities ?? DEFAULT_APPROVAL_CAPABILITIES);
|
|
655
|
+
let approvalManager;
|
|
656
|
+
if (approvalsEnabled) {
|
|
657
|
+
approvalManager = createApprovalManager2({
|
|
658
|
+
store: new FileApprovalStore2({
|
|
659
|
+
baseDir: path4.join(defaultConfigDir(), "approvals")
|
|
660
|
+
})
|
|
661
|
+
});
|
|
662
|
+
await approvalManager.restore();
|
|
663
|
+
}
|
|
664
|
+
const policy = buildPermissionPolicy({
|
|
665
|
+
config,
|
|
666
|
+
approvalCaps,
|
|
667
|
+
approvalsEnabled,
|
|
668
|
+
safe: args.safe === true,
|
|
669
|
+
confirmFn: options.confirmFn
|
|
670
|
+
});
|
|
671
|
+
const systemPrompt = buildSystemPrompt(args);
|
|
672
|
+
const runtime = await createRuntime({
|
|
673
|
+
model: adaptAiModel(model.aiModel),
|
|
674
|
+
streamFn,
|
|
675
|
+
systemPrompt,
|
|
676
|
+
tools,
|
|
677
|
+
workspace: cwd,
|
|
678
|
+
config: { cli: true, version: VERSION, cwd },
|
|
679
|
+
sessionStorage: storage,
|
|
680
|
+
thinkingLevel: args.thinking,
|
|
681
|
+
permissionPolicy: policy,
|
|
682
|
+
approvals: approvalManager,
|
|
683
|
+
maxTurns: 50,
|
|
684
|
+
compaction: { enabled: true }
|
|
685
|
+
});
|
|
686
|
+
return {
|
|
687
|
+
runtime,
|
|
688
|
+
sessionKey: session.sessionKey,
|
|
689
|
+
storage,
|
|
690
|
+
approvalManager,
|
|
691
|
+
model,
|
|
692
|
+
config
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
function buildPermissionPolicy(opts) {
|
|
696
|
+
const { config, approvalCaps, approvalsEnabled, safe, confirmFn } = opts;
|
|
697
|
+
const customRules = (config.permissionRules ?? []).map((r) => ({
|
|
698
|
+
name: `config:${r.permission ?? r.toolName ?? "*"}`,
|
|
699
|
+
toolName: r.toolName ? new RegExp(`^${r.toolName}$`) : void 0,
|
|
700
|
+
permission: r.permission,
|
|
701
|
+
decision: r.decision
|
|
702
|
+
}));
|
|
703
|
+
const highRiskRules = [...approvalCaps].flatMap((cap) => {
|
|
704
|
+
let decision;
|
|
705
|
+
if (approvalsEnabled) decision = "pending";
|
|
706
|
+
else if (safe) decision = "confirm";
|
|
707
|
+
else decision = cap === "fs:write" ? "allow" : "confirm";
|
|
708
|
+
return [{ name: `builtin:${cap}:${decision}`, permission: cap, decision }];
|
|
709
|
+
});
|
|
710
|
+
return createPermissionPolicy({
|
|
711
|
+
rules: [
|
|
712
|
+
...customRules,
|
|
713
|
+
...highRiskRules,
|
|
714
|
+
{ name: "builtin:read", permission: "fs:read", decision: "allow" },
|
|
715
|
+
// 未声明 permissions 的安全工具放行
|
|
716
|
+
{ name: "builtin:safe-tools", decision: "allow" }
|
|
717
|
+
],
|
|
718
|
+
confirmFn,
|
|
719
|
+
defaultDecision: "deny"
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
function buildSystemPrompt(args) {
|
|
723
|
+
const base = args.systemPrompt ?? [
|
|
724
|
+
"\u4F60\u662F aipack\uFF0C\u4E00\u4E2A\u7EC8\u7AEF\u91CC\u7684 AI \u7F16\u7A0B\u52A9\u624B\u3002",
|
|
725
|
+
"\u4F60\u53EF\u4EE5\u4F7F\u7528\u5DE5\u5177\u8BFB\u5199\u6587\u4EF6\u3001\u6267\u884C\u547D\u4EE4\u6765\u5B8C\u6210\u7528\u6237\u7684\u4EFB\u52A1\u3002",
|
|
726
|
+
"\u56DE\u7B54\u4FDD\u6301\u7B80\u6D01\u3001\u6280\u672F\u5316\uFF0C\u4F7F\u7528\u4E0E\u7528\u6237\u76F8\u540C\u7684\u8BED\u8A00\u3002"
|
|
727
|
+
].join("\n");
|
|
728
|
+
const appended = args.appendSystemPrompt ?? [];
|
|
729
|
+
return [base, ...appended].join("\n\n");
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// src/initial-message.ts
|
|
733
|
+
import fs3 from "fs/promises";
|
|
734
|
+
import path5 from "path";
|
|
735
|
+
var IMAGE_EXT = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"]);
|
|
736
|
+
async function buildInitialMessage(messages, fileArgs, stdinText) {
|
|
737
|
+
const parts = [];
|
|
738
|
+
const media = [];
|
|
739
|
+
for (const file of fileArgs) {
|
|
740
|
+
const abs = path5.resolve(process.cwd(), file);
|
|
741
|
+
const ext = path5.extname(abs).toLowerCase();
|
|
742
|
+
try {
|
|
743
|
+
if (IMAGE_EXT.has(ext)) {
|
|
744
|
+
const buf = await fs3.readFile(abs);
|
|
745
|
+
media.push(`data:image/${ext.slice(1)};base64,${buf.toString("base64")}`);
|
|
746
|
+
} else {
|
|
747
|
+
const content = await fs3.readFile(abs, "utf8");
|
|
748
|
+
parts.push(`--- \u6587\u4EF6: ${file} ---
|
|
749
|
+
${content}
|
|
750
|
+
--- \u7ED3\u675F ---`);
|
|
751
|
+
}
|
|
752
|
+
} catch (err) {
|
|
753
|
+
parts.push(`[\u6587\u4EF6 ${file} \u8BFB\u53D6\u5931\u8D25: ${err instanceof Error ? err.message : err}]`);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
if (stdinText && stdinText.trim()) {
|
|
757
|
+
parts.push(`--- stdin ---
|
|
758
|
+
${stdinText.trim()}
|
|
759
|
+
--- \u7ED3\u675F ---`);
|
|
760
|
+
}
|
|
761
|
+
if (messages.length > 0) {
|
|
762
|
+
parts.unshift(messages.join("\n"));
|
|
763
|
+
}
|
|
764
|
+
return { text: parts.join("\n\n"), media };
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// src/modes/print.ts
|
|
768
|
+
import chalk4 from "chalk";
|
|
769
|
+
|
|
770
|
+
// src/modes/render.ts
|
|
771
|
+
import chalk3 from "chalk";
|
|
772
|
+
var ChunkRenderer = class {
|
|
773
|
+
constructor(opts = {}) {
|
|
774
|
+
this.opts = opts;
|
|
775
|
+
}
|
|
776
|
+
opts;
|
|
777
|
+
thinkingShown = false;
|
|
778
|
+
inTool = false;
|
|
779
|
+
render(chunk) {
|
|
780
|
+
const err = this.opts.toolStream ?? process.stdout;
|
|
781
|
+
switch (chunk.type) {
|
|
782
|
+
case "text":
|
|
783
|
+
if (chunk.content) process.stdout.write(chunk.content);
|
|
784
|
+
break;
|
|
785
|
+
case "thinking": {
|
|
786
|
+
if (this.opts.showThinking && chunk.content) {
|
|
787
|
+
process.stdout.write(chalk3.dim(chunk.content));
|
|
788
|
+
} else if (!this.thinkingShown) {
|
|
789
|
+
this.thinkingShown = true;
|
|
790
|
+
err.write(chalk3.dim(" (\u601D\u8003\u4E2D...)\n"));
|
|
791
|
+
}
|
|
792
|
+
break;
|
|
793
|
+
}
|
|
794
|
+
case "tool_start":
|
|
795
|
+
this.inTool = true;
|
|
796
|
+
err.write(chalk3.cyan(`
|
|
797
|
+
\u2699 ${chunk.toolName} `));
|
|
798
|
+
break;
|
|
799
|
+
case "tool_end":
|
|
800
|
+
if (this.inTool) {
|
|
801
|
+
err.write(chunk.isError ? chalk3.red(" \u2717\n") : chalk3.green(" \u2713\n"));
|
|
802
|
+
this.inTool = false;
|
|
803
|
+
}
|
|
804
|
+
break;
|
|
805
|
+
case "error":
|
|
806
|
+
err.write(chalk3.red(`
|
|
807
|
+
\u9519\u8BEF: ${chunk.content ?? "\u672A\u77E5\u9519\u8BEF"}
|
|
808
|
+
`));
|
|
809
|
+
break;
|
|
810
|
+
case "done":
|
|
811
|
+
process.stdout.write("\n");
|
|
812
|
+
break;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
reset() {
|
|
816
|
+
this.thinkingShown = false;
|
|
817
|
+
this.inTool = false;
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
|
|
821
|
+
// src/modes/print.ts
|
|
822
|
+
async function runPrintMode(runtime, request) {
|
|
823
|
+
const renderer = new ChunkRenderer({ toolStream: process.stderr });
|
|
824
|
+
let error;
|
|
825
|
+
try {
|
|
826
|
+
for await (const chunk of runtime.stream(request)) {
|
|
827
|
+
if (chunk.type === "error") error = chunk.content;
|
|
828
|
+
renderer.render(chunk);
|
|
829
|
+
}
|
|
830
|
+
} catch (err) {
|
|
831
|
+
process.stderr.write(chalk4.red(`\u9519\u8BEF: ${err instanceof Error ? err.message : String(err)}
|
|
832
|
+
`));
|
|
833
|
+
process.exitCode = 1;
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
if (error) process.exitCode = 1;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// src/modes/json.ts
|
|
840
|
+
async function runJsonMode(runtime, request) {
|
|
841
|
+
let sawError = false;
|
|
842
|
+
try {
|
|
843
|
+
for await (const chunk of runtime.stream(request)) {
|
|
844
|
+
if (chunk.type === "error") sawError = true;
|
|
845
|
+
const line = JSON.stringify({
|
|
846
|
+
type: chunk.type,
|
|
847
|
+
content: chunk.content,
|
|
848
|
+
toolName: chunk.toolName,
|
|
849
|
+
toolCallId: chunk.toolCallId,
|
|
850
|
+
isError: chunk.isError,
|
|
851
|
+
timestamp: Date.now()
|
|
852
|
+
});
|
|
853
|
+
process.stdout.write(`${line}
|
|
854
|
+
`);
|
|
855
|
+
}
|
|
856
|
+
if (sawError) process.exitCode = 1;
|
|
857
|
+
} catch (err) {
|
|
858
|
+
process.stdout.write(`${JSON.stringify({
|
|
859
|
+
type: "error",
|
|
860
|
+
content: err instanceof Error ? err.message : String(err),
|
|
861
|
+
timestamp: Date.now()
|
|
862
|
+
})}
|
|
863
|
+
`);
|
|
864
|
+
process.exitCode = 1;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// src/modes/interactive.ts
|
|
869
|
+
import readline2 from "readline";
|
|
870
|
+
import chalk5 from "chalk";
|
|
871
|
+
import {
|
|
872
|
+
getBuiltinModels as getBuiltinModels3,
|
|
873
|
+
adaptAiModel as adaptAiModel2
|
|
874
|
+
} from "@aipack-ai/agent";
|
|
875
|
+
|
|
876
|
+
// src/prompt.ts
|
|
877
|
+
import readline from "readline";
|
|
878
|
+
function ask(question) {
|
|
879
|
+
return new Promise((resolve) => {
|
|
880
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
881
|
+
rl.question(question, (answer) => {
|
|
882
|
+
rl.close();
|
|
883
|
+
resolve(answer.trim());
|
|
884
|
+
});
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
function readStreamAll(stream) {
|
|
888
|
+
return new Promise((resolve) => {
|
|
889
|
+
let data = "";
|
|
890
|
+
stream.setEncoding?.("utf8");
|
|
891
|
+
stream.on("data", (chunk) => {
|
|
892
|
+
data += chunk;
|
|
893
|
+
});
|
|
894
|
+
stream.on("end", () => resolve(data));
|
|
895
|
+
stream.on("error", () => resolve(data));
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// src/modes/interactive.ts
|
|
900
|
+
var VALID_THINKING = /* @__PURE__ */ new Set(["off", "minimal", "low", "medium", "high", "max"]);
|
|
901
|
+
async function runInteractiveMode(opts) {
|
|
902
|
+
const { runtime, sessionKey, model, args, storage, approvalManager } = opts;
|
|
903
|
+
let activeKey = sessionKey;
|
|
904
|
+
if (args.resume && storage) {
|
|
905
|
+
const picked = await pickSessionInteractively(storage, sessionKey);
|
|
906
|
+
if (picked) activeKey = picked;
|
|
907
|
+
}
|
|
908
|
+
const renderer = new ChunkRenderer();
|
|
909
|
+
let busy = false;
|
|
910
|
+
let sigintCount = 0;
|
|
911
|
+
let finish;
|
|
912
|
+
const finished = new Promise((resolve) => {
|
|
913
|
+
finish = resolve;
|
|
914
|
+
});
|
|
915
|
+
function cleanup() {
|
|
916
|
+
finish();
|
|
917
|
+
}
|
|
918
|
+
let rl;
|
|
919
|
+
let recreating = false;
|
|
920
|
+
function setupRl() {
|
|
921
|
+
rl = readline2.createInterface({
|
|
922
|
+
input: process.stdin,
|
|
923
|
+
output: process.stdout,
|
|
924
|
+
prompt: chalk5.blue("aipack> ")
|
|
925
|
+
});
|
|
926
|
+
rl.on("SIGINT", () => {
|
|
927
|
+
if (busy) {
|
|
928
|
+
console.log(chalk5.yellow("\n\u4E2D\u65AD\u5F53\u524D\u8FD0\u884C..."));
|
|
929
|
+
runtime.abort(activeKey);
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
sigintCount++;
|
|
933
|
+
if (sigintCount >= 2) {
|
|
934
|
+
console.log(chalk5.dim("\n\u518D\u89C1"));
|
|
935
|
+
rl.close();
|
|
936
|
+
} else {
|
|
937
|
+
console.log(chalk5.dim("\uFF08\u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA\uFF09"));
|
|
938
|
+
rl.prompt();
|
|
939
|
+
setTimeout(() => {
|
|
940
|
+
sigintCount = 0;
|
|
941
|
+
}, 1500);
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
rl.on("close", () => {
|
|
945
|
+
if (!recreating) cleanup();
|
|
946
|
+
});
|
|
947
|
+
rl.on("line", onLine);
|
|
948
|
+
rl.prompt();
|
|
949
|
+
}
|
|
950
|
+
async function send(text) {
|
|
951
|
+
if (!text.trim()) return;
|
|
952
|
+
busy = true;
|
|
953
|
+
sigintCount = 0;
|
|
954
|
+
renderer.reset();
|
|
955
|
+
const request = {
|
|
956
|
+
message: text,
|
|
957
|
+
type: "message",
|
|
958
|
+
channel: "cli",
|
|
959
|
+
sessionKey: activeKey,
|
|
960
|
+
ephemeral: args.noSession
|
|
961
|
+
};
|
|
962
|
+
try {
|
|
963
|
+
for await (const chunk of runtime.stream(request)) {
|
|
964
|
+
renderer.render(chunk);
|
|
965
|
+
}
|
|
966
|
+
} catch (err) {
|
|
967
|
+
console.log(chalk5.red(`\u9519\u8BEF: ${err instanceof Error ? err.message : String(err)}`));
|
|
968
|
+
} finally {
|
|
969
|
+
busy = false;
|
|
970
|
+
rl.prompt();
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
async function handleCommand(line) {
|
|
974
|
+
const spaceIdx = line.indexOf(" ");
|
|
975
|
+
const cmd = (spaceIdx === -1 ? line : line.slice(0, spaceIdx)).toLowerCase();
|
|
976
|
+
const rest = spaceIdx === -1 ? "" : line.slice(spaceIdx + 1).trim();
|
|
977
|
+
switch (cmd) {
|
|
978
|
+
case "/help":
|
|
979
|
+
printSlashHelp();
|
|
980
|
+
break;
|
|
981
|
+
case "/quit":
|
|
982
|
+
case "/exit":
|
|
983
|
+
rl.close();
|
|
984
|
+
return;
|
|
985
|
+
case "/model": {
|
|
986
|
+
if (!rest) {
|
|
987
|
+
console.log(chalk5.dim(`\u5F53\u524D\u6A21\u578B: ${model.aiModel.provider}/${model.aiModel.id}`));
|
|
988
|
+
console.log(chalk5.dim("\u7528\u6CD5: /model provider/model-id\uFF08\u5982 /model deepseek/deepseek-chat\uFF09"));
|
|
989
|
+
break;
|
|
990
|
+
}
|
|
991
|
+
const slash = rest.indexOf("/");
|
|
992
|
+
const provider = slash === -1 ? model.aiModel.provider : rest.slice(0, slash);
|
|
993
|
+
const id = slash === -1 ? rest : rest.slice(slash + 1);
|
|
994
|
+
const found = getBuiltinModels3(provider).find((m) => m.id === id);
|
|
995
|
+
if (!found) {
|
|
996
|
+
console.log(chalk5.yellow(`\u672A\u627E\u5230\u5185\u7F6E\u6A21\u578B ${provider}/${id}\uFF0C\u53EF\u7528:`));
|
|
997
|
+
console.log(getBuiltinModels3(provider).map((m) => ` ${m.id}`).join("\n") || " (\u65E0)");
|
|
998
|
+
break;
|
|
999
|
+
}
|
|
1000
|
+
runtime.setModel(adaptAiModel2(found));
|
|
1001
|
+
model.aiModel = found;
|
|
1002
|
+
model.custom = false;
|
|
1003
|
+
console.log(chalk5.green(`\u5DF2\u5207\u6362\u5230 ${provider}/${id}`));
|
|
1004
|
+
break;
|
|
1005
|
+
}
|
|
1006
|
+
case "/thinking": {
|
|
1007
|
+
if (!VALID_THINKING.has(rest)) {
|
|
1008
|
+
console.log(chalk5.dim("\u7528\u6CD5: /thinking <off|minimal|low|medium|high|max>"));
|
|
1009
|
+
break;
|
|
1010
|
+
}
|
|
1011
|
+
runtime.setThinkingLevel(rest);
|
|
1012
|
+
console.log(chalk5.green(`\u601D\u8003\u7EA7\u522B: ${rest}`));
|
|
1013
|
+
break;
|
|
1014
|
+
}
|
|
1015
|
+
case "/system": {
|
|
1016
|
+
if (!rest) {
|
|
1017
|
+
console.log(chalk5.dim("\u7528\u6CD5: /system <\u65B0\u7684\u7CFB\u7EDF\u63D0\u793A\u8BCD>"));
|
|
1018
|
+
break;
|
|
1019
|
+
}
|
|
1020
|
+
runtime.setSystemPrompt(rest);
|
|
1021
|
+
console.log(chalk5.green("\u7CFB\u7EDF\u63D0\u793A\u8BCD\u5DF2\u66F4\u65B0"));
|
|
1022
|
+
break;
|
|
1023
|
+
}
|
|
1024
|
+
case "/session":
|
|
1025
|
+
console.log(`\u4F1A\u8BDD key: ${chalk5.cyan(activeKey)}`);
|
|
1026
|
+
console.log(`\u6D88\u606F\u6570: ${runtime.getMessages(activeKey).length}`);
|
|
1027
|
+
console.log(`\u6301\u4E45\u5316: ${args.noSession ? chalk5.yellow("\u5426\uFF08--no-session\uFF09") : chalk5.green(storage ? "\u662F" : "\u5426\uFF08\u65E0\u5B58\u50A8\uFF09")}`);
|
|
1028
|
+
break;
|
|
1029
|
+
case "/sessions": {
|
|
1030
|
+
if (!storage) {
|
|
1031
|
+
console.log(chalk5.yellow("\u5F53\u524D\u4E3A\u4E34\u65F6\u4F1A\u8BDD\uFF0C\u65E0\u6301\u4E45\u5316\u5B58\u50A8"));
|
|
1032
|
+
break;
|
|
1033
|
+
}
|
|
1034
|
+
const sessions = await listSessionsByRecency(storage);
|
|
1035
|
+
if (sessions.length === 0) {
|
|
1036
|
+
console.log(chalk5.dim("\uFF08\u65E0\u5386\u53F2\u4F1A\u8BDD\uFF09"));
|
|
1037
|
+
break;
|
|
1038
|
+
}
|
|
1039
|
+
console.log(sessions.slice(0, 10).map((s, i) => `${s === activeKey ? chalk5.green("\u25B8 ") : " "}${i + 1}. ${s}`).join("\n"));
|
|
1040
|
+
console.log(chalk5.dim("\u5207\u6362: \u9000\u51FA\u540E\u7528 aipack --session <key> \u6062\u590D"));
|
|
1041
|
+
break;
|
|
1042
|
+
}
|
|
1043
|
+
case "/clear":
|
|
1044
|
+
runtime.clearSession(activeKey);
|
|
1045
|
+
console.log(chalk5.green("\u4F1A\u8BDD\u5DF2\u6E05\u7A7A\uFF08\u4EC5\u5185\u5B58\uFF09"));
|
|
1046
|
+
break;
|
|
1047
|
+
case "/tools":
|
|
1048
|
+
console.log(chalk5.dim("\u5185\u7F6E\u5DE5\u5177\u901A\u8FC7 --tools/--exclude-tools \u914D\u7F6E\uFF1B\u5F53\u524D\u8FD0\u884C\u65F6\u672A\u63D0\u4F9B\u5217\u4E3E\u63A5\u53E3"));
|
|
1049
|
+
break;
|
|
1050
|
+
case "/approvals": {
|
|
1051
|
+
if (!approvalManager) {
|
|
1052
|
+
console.log(chalk5.yellow("\u5BA1\u6279\u672A\u542F\u7528\uFF08\u5728 aipack.config.js \u4E2D\u914D\u7F6E approvals.enabled: true\uFF09"));
|
|
1053
|
+
break;
|
|
1054
|
+
}
|
|
1055
|
+
const pending = approvalManager.list();
|
|
1056
|
+
if (pending.length === 0) {
|
|
1057
|
+
console.log(chalk5.dim("\uFF08\u65E0\u672A\u51B3\u5BA1\u6279\uFF09"));
|
|
1058
|
+
break;
|
|
1059
|
+
}
|
|
1060
|
+
for (const p of pending) {
|
|
1061
|
+
console.log(`${chalk5.cyan(p.id)} ${p.request.toolName} ${chalk5.dim(new Date(p.createdAt).toLocaleString())}${p.restored ? chalk5.yellow(" (\u5B64\u513F)") : ""}`);
|
|
1062
|
+
}
|
|
1063
|
+
break;
|
|
1064
|
+
}
|
|
1065
|
+
case "/approve":
|
|
1066
|
+
case "/deny": {
|
|
1067
|
+
if (!approvalManager) {
|
|
1068
|
+
console.log(chalk5.yellow("\u5BA1\u6279\u672A\u542F\u7528"));
|
|
1069
|
+
break;
|
|
1070
|
+
}
|
|
1071
|
+
if (!rest) {
|
|
1072
|
+
console.log(chalk5.dim(`\u7528\u6CD5: ${cmd} <\u5BA1\u6279\u5355 id>`));
|
|
1073
|
+
break;
|
|
1074
|
+
}
|
|
1075
|
+
const ok = approvalManager.resolve(rest, cmd === "/approve");
|
|
1076
|
+
console.log(ok ? chalk5.green("\u5DF2\u751F\u6548") : chalk5.yellow("\u5BA1\u6279\u5355\u4E0D\u5B58\u5728\u6216\u5DF2\u7ED3\u7B97"));
|
|
1077
|
+
break;
|
|
1078
|
+
}
|
|
1079
|
+
default:
|
|
1080
|
+
console.log(chalk5.yellow(`\u672A\u77E5\u547D\u4EE4: ${cmd}\uFF08/help \u67E5\u770B\u5168\u90E8\uFF09`));
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
function printSlashHelp() {
|
|
1084
|
+
console.log(`${chalk5.bold("\u547D\u4EE4:")}
|
|
1085
|
+
/model [provider/id] \u5207\u6362\u6A21\u578B\uFF08\u65E0\u53C2\u663E\u793A\u5F53\u524D\uFF09
|
|
1086
|
+
/thinking <\u7EA7\u522B> off/minimal/low/medium/high/max
|
|
1087
|
+
/system <\u6587\u672C> \u66FF\u6362\u7CFB\u7EDF\u63D0\u793A\u8BCD
|
|
1088
|
+
/session \u5F53\u524D\u4F1A\u8BDD\u4FE1\u606F
|
|
1089
|
+
/sessions \u5217\u51FA\u5386\u53F2\u4F1A\u8BDD
|
|
1090
|
+
/clear \u6E05\u7A7A\u5F53\u524D\u4F1A\u8BDD\uFF08\u4EC5\u5185\u5B58\uFF09
|
|
1091
|
+
/approvals \u672A\u51B3\u5BA1\u6279\u5355
|
|
1092
|
+
/approve <id> \u6279\u51C6
|
|
1093
|
+
/deny <id> \u9A73\u56DE
|
|
1094
|
+
/quit \u9000\u51FA\uFF08Ctrl+C \u53CC\u51FB\uFF09`);
|
|
1095
|
+
}
|
|
1096
|
+
async function onLine(line) {
|
|
1097
|
+
const text = line.trim();
|
|
1098
|
+
if (!text) {
|
|
1099
|
+
rl.prompt();
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
if (busy) {
|
|
1103
|
+
console.log(chalk5.dim("\uFF08\u8FD0\u884C\u4E2D\uFF0C\u8F93\u5165\u88AB\u5FFD\u7565\uFF1BCtrl+C \u4E2D\u65AD\uFF09"));
|
|
1104
|
+
rl.prompt();
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
if (text.startsWith("/")) {
|
|
1108
|
+
await handleCommand(text);
|
|
1109
|
+
rl.prompt();
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
await send(text);
|
|
1113
|
+
}
|
|
1114
|
+
if (opts.confirmRef) {
|
|
1115
|
+
opts.confirmRef.fn = async (req) => {
|
|
1116
|
+
recreating = true;
|
|
1117
|
+
rl.close();
|
|
1118
|
+
try {
|
|
1119
|
+
return await opts.baseConfirm(req);
|
|
1120
|
+
} finally {
|
|
1121
|
+
recreating = false;
|
|
1122
|
+
setupRl();
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
const modelLabel = `${model.aiModel.provider}/${model.aiModel.id}${model.custom ? chalk5.yellow(" (\u81EA\u5B9A\u4E49)") : ""}`;
|
|
1127
|
+
console.log(chalk5.bold(`${APP_NAME} ${VERSION}`) + chalk5.dim(` \u4F1A\u8BDD: ${activeKey}${args.noSession ? " (\u4E34\u65F6)" : ""}`));
|
|
1128
|
+
console.log(chalk5.dim(`\u6A21\u578B: ${modelLabel}`));
|
|
1129
|
+
console.log(chalk5.dim("\u8F93\u5165 /help \u67E5\u770B\u547D\u4EE4\uFF0CCtrl+C \u4E2D\u65AD\u8FD0\u884C\uFF0C\u8FDE\u6309\u4E24\u6B21\u9000\u51FA\n"));
|
|
1130
|
+
setupRl();
|
|
1131
|
+
if (opts.initialMessages && opts.initialMessages.length > 0) {
|
|
1132
|
+
await send(opts.initialMessages.join("\n"));
|
|
1133
|
+
}
|
|
1134
|
+
await finished;
|
|
1135
|
+
}
|
|
1136
|
+
async function pickSessionInteractively(storage, currentKey) {
|
|
1137
|
+
const sessions = await listSessionsByRecency(storage);
|
|
1138
|
+
if (sessions.length === 0) {
|
|
1139
|
+
console.log(chalk5.dim("\uFF08\u65E0\u5386\u53F2\u4F1A\u8BDD\uFF0C\u5C06\u65B0\u5EFA\uFF09"));
|
|
1140
|
+
return void 0;
|
|
1141
|
+
}
|
|
1142
|
+
console.log(chalk5.bold("\u9009\u62E9\u4F1A\u8BDD:"));
|
|
1143
|
+
sessions.slice(0, 15).forEach((s, i) => {
|
|
1144
|
+
console.log(`${s === currentKey ? chalk5.green("\u25B8 ") : " "}${i + 1}. ${s}`);
|
|
1145
|
+
});
|
|
1146
|
+
console.log(chalk5.dim(" n. \u65B0\u4F1A\u8BDD"));
|
|
1147
|
+
const answer = await ask("\u7F16\u53F7: ");
|
|
1148
|
+
if (answer === "" || answer.toLowerCase() === "n") return void 0;
|
|
1149
|
+
const idx = Number.parseInt(answer, 10);
|
|
1150
|
+
if (Number.isInteger(idx) && idx >= 1 && idx <= Math.min(sessions.length, 15)) {
|
|
1151
|
+
return sessions[idx - 1];
|
|
1152
|
+
}
|
|
1153
|
+
return void 0;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// src/select.ts
|
|
1157
|
+
import chalk6 from "chalk";
|
|
1158
|
+
async function select(question, options, defaultIndex = 0) {
|
|
1159
|
+
const stdin = process.stdin;
|
|
1160
|
+
const out = process.stdout;
|
|
1161
|
+
if (!stdin.isTTY || options.length === 0) return null;
|
|
1162
|
+
return new Promise((resolve) => {
|
|
1163
|
+
let selected = Math.min(Math.max(defaultIndex, 0), options.length - 1);
|
|
1164
|
+
const prevRaw = stdin.isRaw ?? false;
|
|
1165
|
+
stdin.setRawMode(true);
|
|
1166
|
+
stdin.setEncoding("utf8");
|
|
1167
|
+
stdin.resume();
|
|
1168
|
+
function render() {
|
|
1169
|
+
for (let i = 0; i < options.length; i++) {
|
|
1170
|
+
const opt = options[i];
|
|
1171
|
+
const line = i === selected ? `${chalk6.cyan("\u276F ")}${chalk6.bold(opt.label)}` : ` ${opt.label}`;
|
|
1172
|
+
out.write(`\x1B[2K\r${line}
|
|
1173
|
+
`);
|
|
1174
|
+
}
|
|
1175
|
+
out.write(`\x1B[${options.length}A`);
|
|
1176
|
+
}
|
|
1177
|
+
function finish(value) {
|
|
1178
|
+
stdin.removeListener("data", onData);
|
|
1179
|
+
try {
|
|
1180
|
+
stdin.setRawMode(prevRaw);
|
|
1181
|
+
} catch {
|
|
1182
|
+
}
|
|
1183
|
+
if (!prevRaw) stdin.pause();
|
|
1184
|
+
for (let i = 0; i < options.length; i++) out.write("\x1B[2K\r\n");
|
|
1185
|
+
out.write(`\x1B[${options.length}A`);
|
|
1186
|
+
const chosen = options.find((o) => o.value === value);
|
|
1187
|
+
const summary = chosen ? chalk6.green(`\u2713 ${chosen.label}`) : chalk6.dim("\u5DF2\u53D6\u6D88");
|
|
1188
|
+
out.write(`\x1B[2K\r ${summary}
|
|
1189
|
+
`);
|
|
1190
|
+
resolve(value);
|
|
1191
|
+
}
|
|
1192
|
+
function onData(chunk) {
|
|
1193
|
+
if (chunk.startsWith("\x1B[A") || chunk === "k") {
|
|
1194
|
+
selected = (selected - 1 + options.length) % options.length;
|
|
1195
|
+
} else if (chunk.startsWith("\x1B[B") || chunk === "j") {
|
|
1196
|
+
selected = (selected + 1) % options.length;
|
|
1197
|
+
} else if (chunk === "\r" || chunk === "\n") {
|
|
1198
|
+
finish(options[selected].value);
|
|
1199
|
+
return;
|
|
1200
|
+
} else if (chunk === "\x1B" || chunk === "\x1B\x1B" || chunk === "q" || chunk === "") {
|
|
1201
|
+
finish(null);
|
|
1202
|
+
return;
|
|
1203
|
+
} else if (/^[1-9]$/.test(chunk)) {
|
|
1204
|
+
const idx = Number(chunk) - 1;
|
|
1205
|
+
if (idx < options.length) {
|
|
1206
|
+
finish(options[idx].value);
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
return;
|
|
1210
|
+
} else {
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
render();
|
|
1214
|
+
}
|
|
1215
|
+
out.write(`${chalk6.yellow("?")} ${chalk6.bold(question)}
|
|
1216
|
+
`);
|
|
1217
|
+
render();
|
|
1218
|
+
stdin.on("data", onData);
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// src/confirm.ts
|
|
1223
|
+
var DANGEROUS_PATTERNS = [
|
|
1224
|
+
// 递归删除根/家目录(rm -rf /、rm -rf ~、rm -rf /* 等;正常子目录不受影响)
|
|
1225
|
+
[/rm\s+[^;|&]*[rf][^;|&]*\s+(\/|~|\$HOME)(\/?\*|\s|$)/, "\u9012\u5F52\u5220\u9664\u6839/\u5BB6\u76EE\u5F55"],
|
|
1226
|
+
[/\bsudo(\s|$)/, "\u63D0\u6743\u6267\u884C"],
|
|
1227
|
+
[/\bmkfs(\s|\.)/, "\u683C\u5F0F\u5316\u78C1\u76D8"],
|
|
1228
|
+
[/\bdd\s+[^;|]*of=\/dev\//, "\u5199\u5165\u78C1\u76D8\u8BBE\u5907"],
|
|
1229
|
+
[/>\s*\/dev\/(sd|disk|nvme)/, "\u8986\u5199\u78C1\u76D8\u8BBE\u5907"],
|
|
1230
|
+
[/(curl|wget)[^|;]*\|\s*(sudo\s+)?(ba|z)?sh/, "\u7BA1\u9053\u6267\u884C\u8FDC\u7A0B\u811A\u672C"],
|
|
1231
|
+
[/chmod\s+-R\s+777\s+\/(\s|$)/, "\u6839\u76EE\u5F55\u5F00\u653E\u5168\u90E8\u6743\u9650"],
|
|
1232
|
+
[/\b(shutdown|reboot|halt|poweroff)\b/, "\u5173\u673A/\u91CD\u542F"],
|
|
1233
|
+
[/:\s*\(\)\s*\{[^}]*\}\s*;\s*:/, "fork \u70B8\u5F39"]
|
|
1234
|
+
];
|
|
1235
|
+
function isDangerousCommand(command) {
|
|
1236
|
+
for (const [pattern, reason] of DANGEROUS_PATTERNS) {
|
|
1237
|
+
if (pattern.test(command)) return reason;
|
|
1238
|
+
}
|
|
1239
|
+
return null;
|
|
1240
|
+
}
|
|
1241
|
+
function summarizeArgs(req) {
|
|
1242
|
+
const args = req.args;
|
|
1243
|
+
if (!args || typeof args !== "object") return "";
|
|
1244
|
+
const cmd = args.command;
|
|
1245
|
+
if (typeof cmd === "string") return cmd.length > 60 ? `${cmd.slice(0, 60)}\u2026` : cmd;
|
|
1246
|
+
const file = args.file ?? args.path;
|
|
1247
|
+
if (typeof file === "string") return file;
|
|
1248
|
+
return "";
|
|
1249
|
+
}
|
|
1250
|
+
function createToolConfirmHandler(options = {}) {
|
|
1251
|
+
const autoApproveSafe = options.autoApproveSafe ?? true;
|
|
1252
|
+
const alwaysAllowed = /* @__PURE__ */ new Set();
|
|
1253
|
+
return async (req) => {
|
|
1254
|
+
if (!process.stdin.isTTY) return false;
|
|
1255
|
+
const key = req.permissions.length > 0 ? [...req.permissions].sort().join(",") : `tool:${req.toolName}`;
|
|
1256
|
+
if (alwaysAllowed.has(key)) return true;
|
|
1257
|
+
const args = req.args;
|
|
1258
|
+
const command = typeof args?.command === "string" ? args.command : "";
|
|
1259
|
+
if (autoApproveSafe && command && !isDangerousCommand(command)) {
|
|
1260
|
+
return true;
|
|
1261
|
+
}
|
|
1262
|
+
const dangerReason = command ? isDangerousCommand(command) : null;
|
|
1263
|
+
const summary = command || summarizeArgs(req);
|
|
1264
|
+
const question = dangerReason ? `\u5371\u9669\u547D\u4EE4\uFF08${dangerReason}\uFF09\uFF1A${summary}` : summary ? `\u5141\u8BB8\u6267\u884C ${req.toolName}\uFF1A${summary}` : `\u5141\u8BB8\u6267\u884C ${req.toolName}`;
|
|
1265
|
+
const choice = await select(question, [
|
|
1266
|
+
{ label: "\u5141\u8BB8", value: "once" },
|
|
1267
|
+
{ label: "\u603B\u662F\u5141\u8BB8\uFF08\u672C\u4F1A\u8BDD\uFF09", value: "always" },
|
|
1268
|
+
{ label: "\u62D2\u7EDD", value: "deny" }
|
|
1269
|
+
]);
|
|
1270
|
+
if (choice === "always") {
|
|
1271
|
+
alwaysAllowed.add(key);
|
|
1272
|
+
return true;
|
|
1273
|
+
}
|
|
1274
|
+
return choice === "once";
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// src/cli.ts
|
|
1279
|
+
import { createRequest } from "@aipack-ai/agent";
|
|
1280
|
+
async function main(argv) {
|
|
1281
|
+
if (argv[0] === "approvals" || argv[0] === "approval") {
|
|
1282
|
+
return handleApprovalsCommand(argv.slice(1));
|
|
1283
|
+
}
|
|
1284
|
+
const args = parseArgs(argv);
|
|
1285
|
+
if (args.help) {
|
|
1286
|
+
printHelp();
|
|
1287
|
+
return 0;
|
|
1288
|
+
}
|
|
1289
|
+
if (args.version) {
|
|
1290
|
+
console.log(VERSION);
|
|
1291
|
+
return 0;
|
|
1292
|
+
}
|
|
1293
|
+
const errors = args.diagnostics.filter((d) => d.type === "error");
|
|
1294
|
+
if (errors.length > 0) {
|
|
1295
|
+
for (const e of errors) console.error(chalk7.red(e.message));
|
|
1296
|
+
printHelp();
|
|
1297
|
+
return 1;
|
|
1298
|
+
}
|
|
1299
|
+
for (const w of args.diagnostics.filter((d) => d.type === "warning")) {
|
|
1300
|
+
console.error(chalk7.yellow(w.message));
|
|
1301
|
+
}
|
|
1302
|
+
if (args.listModels !== void 0) {
|
|
1303
|
+
return listModels(args.listModels);
|
|
1304
|
+
}
|
|
1305
|
+
let stdinText;
|
|
1306
|
+
if ((args.print || args.mode === "json") && !process.stdin.isTTY) {
|
|
1307
|
+
stdinText = await readStreamAll(process.stdin);
|
|
1308
|
+
}
|
|
1309
|
+
const initial = await buildInitialMessage(args.messages, args.fileArgs, stdinText);
|
|
1310
|
+
const isNonInteractive = args.print || args.mode === "json";
|
|
1311
|
+
if (isNonInteractive && !initial.text.trim() && initial.media.length === 0) {
|
|
1312
|
+
console.error(chalk7.red("\u975E\u4EA4\u4E92\u6A21\u5F0F\u9700\u8981\u63D0\u4F9B\u6D88\u606F\uFF08\u4F4D\u7F6E\u53C2\u6570\u3001@\u6587\u4EF6\u6216\u7BA1\u9053 stdin\uFF09"));
|
|
1313
|
+
return 1;
|
|
1314
|
+
}
|
|
1315
|
+
const toolConfirm = createToolConfirmHandler({ autoApproveSafe: !args.safe });
|
|
1316
|
+
const confirmRef = {
|
|
1317
|
+
fn: (req) => toolConfirm(req)
|
|
1318
|
+
};
|
|
1319
|
+
let built;
|
|
1320
|
+
try {
|
|
1321
|
+
built = await buildRuntime({
|
|
1322
|
+
args,
|
|
1323
|
+
cwd: process.cwd(),
|
|
1324
|
+
confirmFn: (req) => confirmRef.fn(req)
|
|
1325
|
+
});
|
|
1326
|
+
} catch (err) {
|
|
1327
|
+
console.error(chalk7.red(`\u521D\u59CB\u5316\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`));
|
|
1328
|
+
return 1;
|
|
1329
|
+
}
|
|
1330
|
+
try {
|
|
1331
|
+
if (args.mode === "json") {
|
|
1332
|
+
const request = createRequest(initial.text || "(\u7A7A)", {
|
|
1333
|
+
channel: "cli",
|
|
1334
|
+
sessionKey: built.sessionKey,
|
|
1335
|
+
ephemeral: args.noSession,
|
|
1336
|
+
media: initial.media
|
|
1337
|
+
});
|
|
1338
|
+
await runJsonMode(built.runtime, request);
|
|
1339
|
+
} else if (args.print) {
|
|
1340
|
+
const request = createRequest(initial.text || "(\u7A7A)", {
|
|
1341
|
+
channel: "cli",
|
|
1342
|
+
sessionKey: built.sessionKey,
|
|
1343
|
+
ephemeral: args.noSession,
|
|
1344
|
+
media: initial.media
|
|
1345
|
+
});
|
|
1346
|
+
await runPrintMode(built.runtime, request);
|
|
1347
|
+
} else {
|
|
1348
|
+
await runInteractiveMode({
|
|
1349
|
+
runtime: built.runtime,
|
|
1350
|
+
sessionKey: built.sessionKey,
|
|
1351
|
+
model: built.model,
|
|
1352
|
+
args,
|
|
1353
|
+
storage: built.storage,
|
|
1354
|
+
approvalManager: built.approvalManager,
|
|
1355
|
+
initialMessages: args.messages.length > 0 ? [initial.text] : [],
|
|
1356
|
+
confirmRef,
|
|
1357
|
+
baseConfirm: toolConfirm
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
} finally {
|
|
1361
|
+
built.approvalManager?.close();
|
|
1362
|
+
await built.runtime.close();
|
|
1363
|
+
}
|
|
1364
|
+
return process.exitCode === 1 ? 1 : 0;
|
|
1365
|
+
}
|
|
1366
|
+
if (process.argv[1] && /cli\.(js|mjs|ts)$/.test(process.argv[1])) {
|
|
1367
|
+
main(process.argv.slice(2)).then((code) => process.exit(code)).catch((err) => {
|
|
1368
|
+
console.error(chalk7.red(`\u81F4\u547D\u9519\u8BEF: ${err instanceof Error ? err.stack ?? err.message : String(err)}`));
|
|
1369
|
+
process.exit(1);
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
export {
|
|
1374
|
+
APP_NAME,
|
|
1375
|
+
VERSION,
|
|
1376
|
+
defaultConfigDir,
|
|
1377
|
+
defaultSessionDir,
|
|
1378
|
+
isValidThinkingLevel,
|
|
1379
|
+
parseArgs,
|
|
1380
|
+
printHelp,
|
|
1381
|
+
readTool,
|
|
1382
|
+
writeTool,
|
|
1383
|
+
editTool,
|
|
1384
|
+
bashTool,
|
|
1385
|
+
BUILTIN_TOOLS,
|
|
1386
|
+
selectTools,
|
|
1387
|
+
loadConfig,
|
|
1388
|
+
resolveModel,
|
|
1389
|
+
listSessionsByRecency,
|
|
1390
|
+
resolveSessionKey,
|
|
1391
|
+
buildRuntime,
|
|
1392
|
+
buildInitialMessage,
|
|
1393
|
+
runPrintMode,
|
|
1394
|
+
runJsonMode,
|
|
1395
|
+
runInteractiveMode,
|
|
1396
|
+
pickSessionInteractively,
|
|
1397
|
+
select,
|
|
1398
|
+
isDangerousCommand,
|
|
1399
|
+
createToolConfirmHandler,
|
|
1400
|
+
main
|
|
1401
|
+
};
|
|
1402
|
+
//# sourceMappingURL=chunk-DWPSBSRO.js.map
|