jinzd-ai-cli 0.4.263 → 0.4.265
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 +59 -37
- package/README.zh-CN.md +63 -41
- package/dist/{batch-AN47AYCZ.js → batch-C7SHXRQ2.js} +2 -2
- package/dist/{chunk-VT6NRDD2.js → chunk-23SJVOQD.js} +6 -2
- package/dist/{chunk-QMB7QYZC.js → chunk-2MR7EP24.js} +1 -1
- package/dist/chunk-5C76XDDF.js +78 -0
- package/dist/chunk-6YZEZFX5.js +40 -0
- package/dist/{chunk-TL5MD5XU.js → chunk-AUEX6RNY.js} +17 -1
- package/dist/{chunk-MTZ6XHJD.js → chunk-CVI3LSGR.js} +2 -2
- package/dist/{chunk-B27MLWFJ.js → chunk-E3JRGJCJ.js} +4 -4
- package/dist/{chunk-MJICIIBW.js → chunk-IFTUBTDA.js} +1 -1
- package/dist/{chunk-VUZAKIZ5.js → chunk-LZBD2VGJ.js} +1 -1
- package/dist/{chunk-3RHY2HF7.js → chunk-M2RKBTVZ.js} +1 -1
- package/dist/{chunk-7YCA2BD3.js → chunk-NJLPA6S4.js} +1 -1
- package/dist/{chunk-GMZTY7DA.js → chunk-NOS3QV7G.js} +13 -8
- package/dist/{chunk-GKI7B4BD.js → chunk-TJBG4F2R.js} +24 -95
- package/dist/{chunk-QG4NG7KJ.js → chunk-V4D7YD24.js} +9 -2
- package/dist/{chunk-3ARPAVNV.js → chunk-VOIOUJXI.js} +11 -3
- package/dist/{chunk-WM4VTGAF.js → chunk-X6NWK2YK.js} +1 -1
- package/dist/{chunk-VKBGOFEO.js → chunk-Y7KJPNQI.js} +1 -1
- package/dist/{chunk-XST4N2JM.js → chunk-YDTYBUAB.js} +1 -1
- package/dist/{chunk-5K2LFD7D.js → chunk-YXYOLPDB.js} +1 -1
- package/dist/{ci-JTO7LN5S.js → ci-TDXLI3OM.js} +6 -5
- package/dist/{ci-format-2VGHOLBA.js → ci-format-AFX2LP4Y.js} +2 -2
- package/dist/{constants-HLSJL5HQ.js → constants-66TQEDGO.js} +1 -1
- package/dist/{doctor-cli-JML4FGH4.js → doctor-cli-GMCOGCDR.js} +5 -5
- package/dist/electron-server.js +57 -12
- package/dist/{hub-6EYFGJRT.js → hub-2YXETHPY.js} +8 -4
- package/dist/{hub-server-QGUSMN22.js → hub-server-RF6LURXY.js} +3 -1
- package/dist/index.js +123 -546
- package/dist/{persistent-memory-MPBPSZDN.js → persistent-memory-2DKCETB7.js} +2 -2
- package/dist/{persistent-memory-ANIMO5CF.js → persistent-memory-WTZO5CQ7.js} +2 -2
- package/dist/{pr-XE7NGT6U.js → pr-Y2LDG3R7.js} +15 -10
- package/dist/{run-tests-TF7UPUWW.js → run-tests-FIUGDIDL.js} +2 -2
- package/dist/{run-tests-ME5KED57.js → run-tests-PWTNSUVP.js} +2 -2
- package/dist/{server-VKA4FR2Z.js → server-OZ4RFDFH.js} +25 -15
- package/dist/{server-BIYJEJK3.js → server-ZCG5QD6P.js} +6 -5
- package/dist/setup-wizard-S55ICNHH.js +483 -0
- package/dist/{task-orchestrator-2RAKLFWN.js → task-orchestrator-Q2L6JEUX.js} +10 -7
- package/dist/{usage-5F5Q3GGR.js → usage-SDL5Z4CS.js} +3 -3
- package/package.json +1 -1
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
theme
|
|
4
|
+
} from "./chunk-5C76XDDF.js";
|
|
5
|
+
|
|
6
|
+
// src/repl/setup-wizard.ts
|
|
7
|
+
import { password, select, input, confirm } from "@inquirer/prompts";
|
|
8
|
+
var PROVIDERS = [
|
|
9
|
+
{ value: "claude", name: "Claude (Anthropic)" },
|
|
10
|
+
{ value: "gemini", name: "Gemini (Google)" },
|
|
11
|
+
{ value: "deepseek", name: "DeepSeek" },
|
|
12
|
+
{ value: "zhipu", name: "Zhipu (GLM)" },
|
|
13
|
+
{ value: "kimi", name: "Kimi (Moonshot AI)" },
|
|
14
|
+
{ value: "minimax", name: "MiniMax (\u6D77\u87BA)" },
|
|
15
|
+
{ value: "qwen", name: "Qwen (Alibaba Cloud)" },
|
|
16
|
+
{ value: "openai", name: "OpenAI" },
|
|
17
|
+
{ value: "openrouter", name: "OpenRouter" },
|
|
18
|
+
{ value: "ollama", name: "Ollama (Local)" }
|
|
19
|
+
];
|
|
20
|
+
var NO_KEY_PROVIDERS = /* @__PURE__ */ new Set(["ollama"]);
|
|
21
|
+
var OLLAMA_DEFAULT_HOST = "http://localhost:11434";
|
|
22
|
+
function maskKey(key) {
|
|
23
|
+
if (key.length <= 10) return "****";
|
|
24
|
+
return key.slice(0, 6) + "****" + key.slice(-4);
|
|
25
|
+
}
|
|
26
|
+
function errorMessage(err) {
|
|
27
|
+
if (err instanceof Error && err.message) return err.message;
|
|
28
|
+
return String(err ?? "unknown error");
|
|
29
|
+
}
|
|
30
|
+
var SetupWizard = class {
|
|
31
|
+
constructor(config) {
|
|
32
|
+
this.config = config;
|
|
33
|
+
}
|
|
34
|
+
config;
|
|
35
|
+
async runFirstRun() {
|
|
36
|
+
console.log(theme.heading("\nWelcome to ai-cli!\n"));
|
|
37
|
+
console.log("Let's get to know you first, then set up your AI provider.\n");
|
|
38
|
+
try {
|
|
39
|
+
await this.setupProfile();
|
|
40
|
+
console.log();
|
|
41
|
+
const providerId = await select({
|
|
42
|
+
message: "Which AI provider do you want to set up first?",
|
|
43
|
+
choices: PROVIDERS
|
|
44
|
+
});
|
|
45
|
+
await this.setupProvider(providerId);
|
|
46
|
+
this.config.set("defaultProvider", providerId);
|
|
47
|
+
this.config.save();
|
|
48
|
+
const nickname = this.config.get("userProfile")?.nickname;
|
|
49
|
+
const greeting = nickname ? `Setup complete, ${nickname}!` : "Setup complete!";
|
|
50
|
+
console.log(theme.success(`
|
|
51
|
+
${greeting} Starting ai-cli...
|
|
52
|
+
`));
|
|
53
|
+
return true;
|
|
54
|
+
} catch (err) {
|
|
55
|
+
const message = errorMessage(err);
|
|
56
|
+
const canceled = /user force closed|interrupted|cancel|aborted/i.test(message);
|
|
57
|
+
if (canceled) {
|
|
58
|
+
console.log(theme.warning("\nSetup canceled. Run aicli config to try again.\n"));
|
|
59
|
+
} else {
|
|
60
|
+
console.log(theme.error(`
|
|
61
|
+
First-run setup failed: ${message}`));
|
|
62
|
+
console.log(theme.dim(`Config directory: ${this.config.getConfigDir()}`));
|
|
63
|
+
console.log(theme.dim("Run aicli config to retry, or aicli doctor to inspect provider/config health.\n"));
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async runFull() {
|
|
69
|
+
console.log(theme.heading("\nai-cli Configuration\n"));
|
|
70
|
+
let running = true;
|
|
71
|
+
while (running) {
|
|
72
|
+
const currentProxy = this.config.get("proxy") ?? "";
|
|
73
|
+
const proxyStatus = currentProxy ? theme.success(`[${currentProxy}]`) : theme.dim("[not configured]");
|
|
74
|
+
const googleKeyStatus = this.config.getApiKey("google-search") ? theme.success("[configured]") : theme.dim("[not configured]");
|
|
75
|
+
const profileName = this.config.get("userProfile")?.nickname || this.config.get("userProfile")?.name;
|
|
76
|
+
const profileStatus = profileName ? theme.success(`[${profileName}]`) : theme.dim("[not configured]");
|
|
77
|
+
const yoloOn = this.config.get("alwaysYolo") === true;
|
|
78
|
+
const mcpOff = this.config.get("mcpEnabled") === false;
|
|
79
|
+
const behaviorFlags = [];
|
|
80
|
+
if (yoloOn) behaviorFlags.push("YOLO on");
|
|
81
|
+
if (mcpOff) behaviorFlags.push("MCP off");
|
|
82
|
+
const behaviorStatus = behaviorFlags.length ? theme.warning(`[${behaviorFlags.join(", ")}]`) : theme.dim("[default]");
|
|
83
|
+
const action = await select({
|
|
84
|
+
message: "What would you like to configure?",
|
|
85
|
+
choices: [
|
|
86
|
+
{ value: "profile", name: `Edit your profile (identity) ${profileStatus}` },
|
|
87
|
+
{ value: "apikey", name: "Manage API key for a provider" },
|
|
88
|
+
{ value: "default", name: "Change default provider" },
|
|
89
|
+
{ value: "proxy", name: `Configure proxy (HTTP/HTTPS) ${proxyStatus}` },
|
|
90
|
+
{ value: "google", name: `Configure Google Search (API Key + CX) ${googleKeyStatus}` },
|
|
91
|
+
{ value: "behavior", name: `Behavior toggles (YOLO, MCP, ...) ${behaviorStatus}` },
|
|
92
|
+
{ value: "preset", name: "Apply task preset (safe review, workspace coding, local-only, team web)" },
|
|
93
|
+
{ value: "done", name: "Done" }
|
|
94
|
+
]
|
|
95
|
+
});
|
|
96
|
+
if (action === "profile") {
|
|
97
|
+
await this.setupProfile();
|
|
98
|
+
} else if (action === "proxy") {
|
|
99
|
+
await this.setupProxy();
|
|
100
|
+
} else if (action === "google") {
|
|
101
|
+
await this.setupGoogleSearch();
|
|
102
|
+
} else if (action === "behavior") {
|
|
103
|
+
await this.setupBehavior();
|
|
104
|
+
} else if (action === "preset") {
|
|
105
|
+
await this.setupPreset();
|
|
106
|
+
} else if (action === "apikey") {
|
|
107
|
+
const choicesWithStatus = PROVIDERS.map((p) => {
|
|
108
|
+
if (NO_KEY_PROVIDERS.has(p.value)) {
|
|
109
|
+
return { value: p.value, name: `${p.name} ${theme.dim("[no API key needed]")}` };
|
|
110
|
+
}
|
|
111
|
+
const existingKey = this.config.getApiKey(p.value);
|
|
112
|
+
const status = existingKey ? theme.success(`[${maskKey(existingKey)}]`) : theme.dim("[not configured]");
|
|
113
|
+
return { value: p.value, name: `${p.name} ${status}` };
|
|
114
|
+
});
|
|
115
|
+
const providerId = await select({
|
|
116
|
+
message: "Select provider:",
|
|
117
|
+
choices: choicesWithStatus
|
|
118
|
+
});
|
|
119
|
+
await this.setupProvider(providerId);
|
|
120
|
+
} else if (action === "default") {
|
|
121
|
+
const providerId = await select({
|
|
122
|
+
message: "Select default provider:",
|
|
123
|
+
choices: PROVIDERS
|
|
124
|
+
});
|
|
125
|
+
this.config.set("defaultProvider", providerId);
|
|
126
|
+
this.config.save();
|
|
127
|
+
console.log(theme.success(`Default provider set to: ${providerId}
|
|
128
|
+
`));
|
|
129
|
+
} else {
|
|
130
|
+
running = false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async setupPreset() {
|
|
135
|
+
const preset = await select({
|
|
136
|
+
message: "Choose a task-oriented preset:",
|
|
137
|
+
choices: [
|
|
138
|
+
{ value: "safe-review", name: "Safe review only - read-only tools, network confirm" },
|
|
139
|
+
{ value: "workspace-coding", name: "Workspace coding - write in workspace with confirmations" },
|
|
140
|
+
{ value: "local-ollama", name: "Local-only Ollama - local provider, external network denied" },
|
|
141
|
+
{ value: "team-web", name: "Team Web UI - workspace coding plus auth reminder" }
|
|
142
|
+
]
|
|
143
|
+
});
|
|
144
|
+
const currentNetwork = this.config.get("networkPolicy");
|
|
145
|
+
if (preset === "safe-review") {
|
|
146
|
+
this.config.set("defaultPermissionProfile", "read-only");
|
|
147
|
+
this.config.set("alwaysYolo", false);
|
|
148
|
+
this.config.set("networkPolicy", { ...currentNetwork, enabled: true, defaultAction: "confirm", tools: { ...currentNetwork.tools, shell: "confirm", web_fetch: "confirm", web_search: "confirm", google_search: "confirm", mcp: "confirm" } });
|
|
149
|
+
console.log(theme.success("Preset applied: Safe review only. Writes require denial/confirmation according to the read-only profile."));
|
|
150
|
+
} else if (preset === "workspace-coding") {
|
|
151
|
+
this.config.set("defaultPermissionProfile", "workspace-write");
|
|
152
|
+
this.config.set("alwaysYolo", false);
|
|
153
|
+
this.config.set("networkPolicy", { ...currentNetwork, enabled: true, defaultAction: "confirm", tools: { ...currentNetwork.tools, shell: "confirm", web_fetch: "confirm", web_search: "confirm", google_search: "confirm", mcp: "confirm" } });
|
|
154
|
+
console.log(theme.success("Preset applied: Workspace coding. File writes stay inside the workspace and sensitive actions still ask."));
|
|
155
|
+
} else if (preset === "local-ollama") {
|
|
156
|
+
this.config.set("defaultProvider", "ollama");
|
|
157
|
+
this.config.set("defaultPermissionProfile", "workspace-write");
|
|
158
|
+
this.config.set("alwaysYolo", false);
|
|
159
|
+
this.config.set("networkPolicy", { ...currentNetwork, enabled: true, defaultAction: "deny", allowPrivateNetwork: true, tools: { ...currentNetwork.tools, shell: "confirm", web_fetch: "deny", web_search: "deny", google_search: "deny", mcp: "confirm" } });
|
|
160
|
+
console.log(theme.success("Preset applied: Local-only Ollama. External web/search is denied; private network is allowed for local services."));
|
|
161
|
+
} else {
|
|
162
|
+
this.config.set("defaultPermissionProfile", "workspace-write");
|
|
163
|
+
this.config.set("alwaysYolo", false);
|
|
164
|
+
this.config.set("networkPolicy", { ...currentNetwork, enabled: true, defaultAction: "confirm", tools: { ...currentNetwork.tools, shell: "confirm", web_fetch: "confirm", web_search: "confirm", google_search: "confirm", mcp: "confirm" } });
|
|
165
|
+
console.log(theme.warning("Preset applied: Team Web UI. Start Web with auth enabled and put it behind your normal TLS/reverse proxy boundary."));
|
|
166
|
+
}
|
|
167
|
+
this.config.save();
|
|
168
|
+
console.log(theme.dim("Review changes with aicli doctor or aicli config.\n"));
|
|
169
|
+
}
|
|
170
|
+
async setupProvider(providerId) {
|
|
171
|
+
if (NO_KEY_PROVIDERS.has(providerId)) {
|
|
172
|
+
await this.setupOllama();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const provider = PROVIDERS.find((p) => p.value === providerId);
|
|
176
|
+
const displayName = provider?.name ?? providerId;
|
|
177
|
+
const existingKey = this.config.getApiKey(providerId);
|
|
178
|
+
console.log(`
|
|
179
|
+
Managing ${displayName} API Key`);
|
|
180
|
+
if (existingKey) {
|
|
181
|
+
console.log(theme.dim(` Current: ${maskKey(existingKey)}`));
|
|
182
|
+
} else {
|
|
183
|
+
console.log(theme.dim(" Current: (not configured)"));
|
|
184
|
+
}
|
|
185
|
+
const choices = existingKey ? [
|
|
186
|
+
{ value: "keep", name: "Keep current key" },
|
|
187
|
+
{ value: "fingerprint", name: `Copy masked fingerprint (${maskKey(existingKey)})` },
|
|
188
|
+
{ value: "change", name: "Update key" }
|
|
189
|
+
] : [{ value: "change", name: "Enter key" }, { value: "skip", name: "Skip" }];
|
|
190
|
+
const action = await select({
|
|
191
|
+
message: "Action:",
|
|
192
|
+
choices
|
|
193
|
+
});
|
|
194
|
+
if (action === "fingerprint" && existingKey) {
|
|
195
|
+
console.log(theme.dim(`
|
|
196
|
+
Fingerprint: ${maskKey(existingKey)}`));
|
|
197
|
+
console.log(theme.dim(" Full secrets are not printed. Use Update key to replace it.\n"));
|
|
198
|
+
return;
|
|
199
|
+
} else if (action === "keep" || action === "skip") {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const newKey = await password({
|
|
203
|
+
message: `Enter ${displayName} API key:`,
|
|
204
|
+
validate: (val) => val.length > 0 || "API key cannot be empty"
|
|
205
|
+
});
|
|
206
|
+
this.config.setApiKey(providerId, newKey);
|
|
207
|
+
console.log(theme.success(`API key saved for ${displayName}: ${maskKey(newKey)}
|
|
208
|
+
`));
|
|
209
|
+
}
|
|
210
|
+
/** Ollama 专用配置:检测连通性、发现本地模型、可选自定义地址 */
|
|
211
|
+
async setupOllama() {
|
|
212
|
+
console.log(theme.heading("\n\u{1F999} Ollama (Local) Configuration"));
|
|
213
|
+
console.log(theme.dim(" Ollama runs AI models locally \u2014 no API key needed."));
|
|
214
|
+
console.log(theme.dim(" Install: https://ollama.com\n"));
|
|
215
|
+
const customBaseUrls = this.config.get("customBaseUrls") ?? {};
|
|
216
|
+
const currentHost = customBaseUrls["ollama"] ? customBaseUrls["ollama"].replace(/\/v1\/?$/, "") : OLLAMA_DEFAULT_HOST;
|
|
217
|
+
const ollamaHost = await input({
|
|
218
|
+
message: "Ollama server address:",
|
|
219
|
+
default: currentHost,
|
|
220
|
+
validate: (val) => {
|
|
221
|
+
if (!val) return "Address cannot be empty";
|
|
222
|
+
if (!val.startsWith("http://") && !val.startsWith("https://")) {
|
|
223
|
+
return "Must start with http:// or https://";
|
|
224
|
+
}
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
const host = ollamaHost.replace(/\/+$/, "");
|
|
229
|
+
console.log(theme.dim(`
|
|
230
|
+
Connecting to ${host}...`));
|
|
231
|
+
let models = [];
|
|
232
|
+
try {
|
|
233
|
+
const res = await fetch(`${host}/api/tags`, { signal: AbortSignal.timeout(5e3) });
|
|
234
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
235
|
+
const data = await res.json();
|
|
236
|
+
models = data.models ?? [];
|
|
237
|
+
} catch {
|
|
238
|
+
console.log(theme.error(` \u2717 Cannot connect to Ollama at ${host}`));
|
|
239
|
+
console.log(theme.dim(" Make sure Ollama is running: ollama serve\n"));
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (models.length === 0) {
|
|
243
|
+
console.log(theme.warning(" \u26A0 Ollama is running but no models installed."));
|
|
244
|
+
console.log(theme.dim(" Install a model: ollama pull llama3.1\n"));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
console.log(theme.success(` \u2713 Connected! Found ${models.length} model(s):
|
|
248
|
+
`));
|
|
249
|
+
for (const m of models) {
|
|
250
|
+
const size = m.details?.parameter_size ?? `${(m.size / 1e9).toFixed(1)}GB`;
|
|
251
|
+
console.log(` \u2022 ${m.name} ${theme.dim(`(${size})`)}`);
|
|
252
|
+
}
|
|
253
|
+
console.log();
|
|
254
|
+
if (host !== OLLAMA_DEFAULT_HOST) {
|
|
255
|
+
const urls = { ...customBaseUrls, ollama: `${host}/v1` };
|
|
256
|
+
this.config.set("customBaseUrls", urls);
|
|
257
|
+
}
|
|
258
|
+
this.config.save();
|
|
259
|
+
console.log(theme.success(" Ollama configured! Use /provider ollama to switch.\n"));
|
|
260
|
+
}
|
|
261
|
+
async setupProxy() {
|
|
262
|
+
const current = this.config.get("proxy") ?? "";
|
|
263
|
+
console.log("\nHTTP/HTTPS Proxy Configuration");
|
|
264
|
+
console.log(theme.dim(" Used for providers that require a proxy (e.g. Gemini in China)"));
|
|
265
|
+
if (current) {
|
|
266
|
+
console.log(theme.dim(` Current: ${current}`));
|
|
267
|
+
}
|
|
268
|
+
const action = await select({
|
|
269
|
+
message: "Action:",
|
|
270
|
+
choices: current ? [
|
|
271
|
+
{ value: "keep", name: "Keep current" },
|
|
272
|
+
{ value: "change", name: "Update" },
|
|
273
|
+
{ value: "clear", name: "Remove proxy" }
|
|
274
|
+
] : [
|
|
275
|
+
{ value: "change", name: "Set proxy" },
|
|
276
|
+
{ value: "skip", name: "Skip" }
|
|
277
|
+
]
|
|
278
|
+
});
|
|
279
|
+
if (action === "keep" || action === "skip") return;
|
|
280
|
+
if (action === "clear") {
|
|
281
|
+
this.config.set("proxy", void 0);
|
|
282
|
+
this.config.save();
|
|
283
|
+
console.log(theme.success("Proxy cleared.\n"));
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const proxyUrl = await input({
|
|
287
|
+
message: "Enter proxy URL (e.g. http://127.0.0.1:10809):",
|
|
288
|
+
default: current || void 0,
|
|
289
|
+
validate: (val) => {
|
|
290
|
+
if (!val) return "Proxy URL cannot be empty";
|
|
291
|
+
if (!val.startsWith("http://") && !val.startsWith("https://")) {
|
|
292
|
+
return "Must start with http:// or https://";
|
|
293
|
+
}
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
this.config.set("proxy", proxyUrl);
|
|
298
|
+
this.config.save();
|
|
299
|
+
console.log(theme.success(`Proxy saved: ${proxyUrl}
|
|
300
|
+
`));
|
|
301
|
+
}
|
|
302
|
+
/** Behavior toggles — alwaysYolo, mcpEnabled, and friends (v0.4.126+). */
|
|
303
|
+
async setupBehavior() {
|
|
304
|
+
let running = true;
|
|
305
|
+
while (running) {
|
|
306
|
+
const yoloOn = this.config.get("alwaysYolo") === true;
|
|
307
|
+
const mcpEnabled = this.config.get("mcpEnabled") !== false;
|
|
308
|
+
const yoloStatus = yoloOn ? theme.warning("[ON]") : theme.dim("[off]");
|
|
309
|
+
const mcpStatus = mcpEnabled ? theme.success("[enabled]") : theme.warning("[disabled]");
|
|
310
|
+
console.log(theme.heading("\n\u2699 Behavior Toggles\n"));
|
|
311
|
+
const action = await select({
|
|
312
|
+
message: "Which toggle?",
|
|
313
|
+
choices: [
|
|
314
|
+
{ value: "yolo", name: `alwaysYolo ${yoloStatus}` },
|
|
315
|
+
{ value: "mcp", name: `mcpEnabled ${mcpStatus}` },
|
|
316
|
+
{ value: "done", name: "Done" }
|
|
317
|
+
]
|
|
318
|
+
});
|
|
319
|
+
if (action === "yolo") {
|
|
320
|
+
await this.toggleAlwaysYolo();
|
|
321
|
+
} else if (action === "mcp") {
|
|
322
|
+
await this.toggleMcpEnabled();
|
|
323
|
+
} else {
|
|
324
|
+
running = false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async toggleAlwaysYolo() {
|
|
329
|
+
const current = this.config.get("alwaysYolo") === true;
|
|
330
|
+
console.log(" alwaysYolo: " + (current ? theme.warning("ON") : theme.dim("off")));
|
|
331
|
+
console.log(
|
|
332
|
+
theme.dim(
|
|
333
|
+
" When ON, REPL starts with sessionAutoApprove enabled \u2014 write\n tools (write_file/edit_file, etc.) run without confirmation prompts. Destructive tools still require confirmation. Only enable this\n if you fully trust the working directory you run aicli in.\n"
|
|
334
|
+
)
|
|
335
|
+
);
|
|
336
|
+
const next = await confirm({
|
|
337
|
+
message: current ? "Keep alwaysYolo enabled?" : "Enable alwaysYolo (skip write confirmations; destructive still confirms)?",
|
|
338
|
+
default: current
|
|
339
|
+
});
|
|
340
|
+
if (next === current) {
|
|
341
|
+
console.log(theme.dim(" No change.\n"));
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
this.config.set("alwaysYolo", next);
|
|
345
|
+
this.config.save();
|
|
346
|
+
if (next) {
|
|
347
|
+
console.log(theme.warning(" \u26A1 alwaysYolo enabled \u2014 confirmations will be skipped on next REPL start.\n"));
|
|
348
|
+
} else {
|
|
349
|
+
console.log(theme.success(" alwaysYolo disabled \u2014 confirmations restored on next REPL start.\n"));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
async toggleMcpEnabled() {
|
|
353
|
+
const current = this.config.get("mcpEnabled") !== false;
|
|
354
|
+
console.log(" mcpEnabled: " + (current ? theme.success("enabled") : theme.warning("disabled")));
|
|
355
|
+
console.log(
|
|
356
|
+
theme.dim(
|
|
357
|
+
" Global switch for the MCP (Model Context Protocol) subsystem. When disabled,\n REPL startup skips loadProjectMcpConfig() and mcpManager.connectAll() \u2014\n no MCP servers are spawned, no MCP tools appear in the tool list, faster boot.\n Per-server entries in config.mcpServers are preserved but ignored. Disable this\n if you don't use MCP and want a leaner startup / smaller tool schema.\n"
|
|
358
|
+
)
|
|
359
|
+
);
|
|
360
|
+
const next = await confirm({
|
|
361
|
+
message: current ? "Keep MCP enabled?" : "Re-enable MCP?",
|
|
362
|
+
default: current
|
|
363
|
+
});
|
|
364
|
+
if (next === current) {
|
|
365
|
+
console.log(theme.dim(" No change.\n"));
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
this.config.set("mcpEnabled", next);
|
|
369
|
+
this.config.save();
|
|
370
|
+
if (next) {
|
|
371
|
+
console.log(theme.success(" MCP enabled \u2014 servers will connect on next REPL start.\n"));
|
|
372
|
+
} else {
|
|
373
|
+
console.log(theme.warning(" \u{1F50C} MCP disabled \u2014 no MCP servers will load on next REPL start.\n"));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async setupGoogleSearch() {
|
|
377
|
+
console.log("\nGoogle Custom Search Configuration");
|
|
378
|
+
console.log(theme.dim(" Enables AI to search the web via google_search tool"));
|
|
379
|
+
console.log(theme.dim(" Requires: API Key + Search Engine ID (cx)"));
|
|
380
|
+
console.log(theme.dim(" Get API Key: https://console.cloud.google.com/apis/credentials"));
|
|
381
|
+
console.log(theme.dim(" Get CX: https://programmablesearchengine.google.com/"));
|
|
382
|
+
const existingKey = this.config.getApiKey("google-search");
|
|
383
|
+
if (existingKey) {
|
|
384
|
+
console.log(theme.dim(`
|
|
385
|
+
API Key: ${maskKey(existingKey)}`));
|
|
386
|
+
} else {
|
|
387
|
+
console.log(theme.dim("\n API Key: (not configured)"));
|
|
388
|
+
}
|
|
389
|
+
const keyAction = await select({
|
|
390
|
+
message: "API Key:",
|
|
391
|
+
choices: existingKey ? [
|
|
392
|
+
{ value: "keep", name: "Keep current" },
|
|
393
|
+
{ value: "change", name: "Update API Key" }
|
|
394
|
+
] : [
|
|
395
|
+
{ value: "change", name: "Enter API Key" },
|
|
396
|
+
{ value: "skip", name: "Skip" }
|
|
397
|
+
]
|
|
398
|
+
});
|
|
399
|
+
if (keyAction === "change") {
|
|
400
|
+
const newKey = await password({
|
|
401
|
+
message: "Enter Google API Key:",
|
|
402
|
+
validate: (val) => val.length > 0 || "API key cannot be empty"
|
|
403
|
+
});
|
|
404
|
+
this.config.setApiKey("google-search", newKey);
|
|
405
|
+
console.log(theme.success(` API Key saved: ${maskKey(newKey)}`));
|
|
406
|
+
}
|
|
407
|
+
const existingCx = this.config.get("googleSearchEngineId") ?? "";
|
|
408
|
+
if (existingCx) {
|
|
409
|
+
console.log(theme.dim(`
|
|
410
|
+
Search Engine ID: ${existingCx}`));
|
|
411
|
+
} else {
|
|
412
|
+
console.log(theme.dim("\n Search Engine ID: (not configured)"));
|
|
413
|
+
}
|
|
414
|
+
const cxAction = await select({
|
|
415
|
+
message: "Search Engine ID (cx):",
|
|
416
|
+
choices: existingCx ? [
|
|
417
|
+
{ value: "keep", name: "Keep current" },
|
|
418
|
+
{ value: "change", name: "Update CX" }
|
|
419
|
+
] : [
|
|
420
|
+
{ value: "change", name: "Enter CX" },
|
|
421
|
+
{ value: "skip", name: "Skip" }
|
|
422
|
+
]
|
|
423
|
+
});
|
|
424
|
+
if (cxAction === "change") {
|
|
425
|
+
const newCx = await input({
|
|
426
|
+
message: "Enter Search Engine ID (cx):",
|
|
427
|
+
default: existingCx || void 0,
|
|
428
|
+
validate: (val) => val.length > 0 || "Search Engine ID cannot be empty"
|
|
429
|
+
});
|
|
430
|
+
this.config.set("googleSearchEngineId", newCx);
|
|
431
|
+
this.config.save();
|
|
432
|
+
console.log(theme.success(` Search Engine ID saved: ${newCx}`));
|
|
433
|
+
}
|
|
434
|
+
console.log();
|
|
435
|
+
}
|
|
436
|
+
/** 收集用户身份档案 — aicli 的 "灵魂" 功能 */
|
|
437
|
+
async setupProfile() {
|
|
438
|
+
const existing = this.config.get("userProfile") ?? {};
|
|
439
|
+
console.log(theme.heading("\n\u{1F464} Your Profile"));
|
|
440
|
+
console.log(theme.dim(" This helps AI know who you are \u2014 across all providers and sessions.\n"));
|
|
441
|
+
const name = await input({
|
|
442
|
+
message: "Your name:",
|
|
443
|
+
default: existing.name || void 0
|
|
444
|
+
});
|
|
445
|
+
const nickname = await input({
|
|
446
|
+
message: "Preferred nickname (AI will call you this):",
|
|
447
|
+
default: existing.nickname || void 0
|
|
448
|
+
});
|
|
449
|
+
const role = await input({
|
|
450
|
+
message: 'Your role (e.g. "Full-stack developer", "Student"):',
|
|
451
|
+
default: existing.role || void 0
|
|
452
|
+
});
|
|
453
|
+
const bio = await input({
|
|
454
|
+
message: "Brief intro (1-2 sentences about your background):",
|
|
455
|
+
default: existing.bio || void 0
|
|
456
|
+
});
|
|
457
|
+
const interestsStr = await input({
|
|
458
|
+
message: "Interests / tech stack (comma-separated):",
|
|
459
|
+
default: existing.interests?.join(", ") || void 0
|
|
460
|
+
});
|
|
461
|
+
const locale = await input({
|
|
462
|
+
message: 'Preferred language (e.g. "zh-CN", "en", "ja"):',
|
|
463
|
+
default: existing.locale || void 0
|
|
464
|
+
});
|
|
465
|
+
const profile = {};
|
|
466
|
+
if (name) profile.name = name;
|
|
467
|
+
if (nickname) profile.nickname = nickname;
|
|
468
|
+
if (role) profile.role = role;
|
|
469
|
+
if (bio) profile.bio = bio;
|
|
470
|
+
if (interestsStr) profile.interests = interestsStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
471
|
+
if (locale) profile.locale = locale;
|
|
472
|
+
if (existing.extra) profile.extra = existing.extra;
|
|
473
|
+
this.config.set("userProfile", profile);
|
|
474
|
+
this.config.save();
|
|
475
|
+
const displayName = nickname || name || "User";
|
|
476
|
+
console.log(theme.success(`
|
|
477
|
+
Profile saved! AI will know you as "${displayName}".
|
|
478
|
+
`));
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
export {
|
|
482
|
+
SetupWizard
|
|
483
|
+
};
|
|
@@ -3,15 +3,16 @@ import {
|
|
|
3
3
|
ToolRegistry,
|
|
4
4
|
googleSearchContext,
|
|
5
5
|
truncateOutput
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-TJBG4F2R.js";
|
|
7
|
+
import "./chunk-LZBD2VGJ.js";
|
|
8
8
|
import "./chunk-GLSR2NR3.js";
|
|
9
9
|
import "./chunk-UUSRWSSX.js";
|
|
10
10
|
import "./chunk-CKH4KQ4E.js";
|
|
11
11
|
import "./chunk-BXP6YZ2P.js";
|
|
12
|
+
import "./chunk-5C76XDDF.js";
|
|
12
13
|
import {
|
|
13
14
|
runTool
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-2MR7EP24.js";
|
|
15
16
|
import {
|
|
16
17
|
getDangerLevel,
|
|
17
18
|
runLeanAgentLoop
|
|
@@ -19,10 +20,10 @@ import {
|
|
|
19
20
|
import "./chunk-TU3L3PW7.js";
|
|
20
21
|
import "./chunk-HOSJZMQS.js";
|
|
21
22
|
import "./chunk-4BKXL7SM.js";
|
|
22
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-YDTYBUAB.js";
|
|
23
24
|
import {
|
|
24
25
|
SUBAGENT_ALLOWED_TOOLS
|
|
25
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-Y7KJPNQI.js";
|
|
26
27
|
import "./chunk-6RPGQ5CG.js";
|
|
27
28
|
import "./chunk-KHYD3WXE.js";
|
|
28
29
|
import "./chunk-NYNSYLFY.js";
|
|
@@ -350,7 +351,8 @@ Example:
|
|
|
350
351
|
systemPrompt,
|
|
351
352
|
stream: false,
|
|
352
353
|
temperature: 0.3,
|
|
353
|
-
maxTokens: 8192
|
|
354
|
+
maxTokens: 8192,
|
|
355
|
+
...this.config.thinking ?? {}
|
|
354
356
|
});
|
|
355
357
|
let jsonStr = response.content.trim();
|
|
356
358
|
const fenceMatch = jsonStr.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
@@ -550,7 +552,8 @@ Use the same language as the original goal.`,
|
|
|
550
552
|
model: this.config.defaultModel,
|
|
551
553
|
stream: false,
|
|
552
554
|
temperature: 0.3,
|
|
553
|
-
maxTokens: 4096
|
|
555
|
+
maxTokens: 4096,
|
|
556
|
+
...this.config.thinking ?? {}
|
|
554
557
|
});
|
|
555
558
|
console.log();
|
|
556
559
|
console.log(chalk.bold.white(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
CostTracker
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-V4D7YD24.js";
|
|
5
5
|
import {
|
|
6
6
|
formatCost,
|
|
7
7
|
getPricing
|
|
8
8
|
} from "./chunk-YRD6M5XI.js";
|
|
9
9
|
import {
|
|
10
10
|
ConfigManager
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-X6NWK2YK.js";
|
|
12
12
|
import "./chunk-TU3L3PW7.js";
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-Y7KJPNQI.js";
|
|
14
14
|
import "./chunk-IW3Q7AE5.js";
|
|
15
15
|
|
|
16
16
|
// src/cli/usage.ts
|