@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.
@@ -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