shenxiang-ai-cli 0.1.0 → 0.2.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/dist/index.js +531 -321
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/chat.ts
|
|
7
|
-
import
|
|
7
|
+
import inquirer from "inquirer";
|
|
8
8
|
|
|
9
9
|
// src/core/providers/openai-compatible.ts
|
|
10
10
|
import OpenAI from "openai";
|
|
@@ -21,7 +21,9 @@ var OpenAICompatibleProvider = class {
|
|
|
21
21
|
}
|
|
22
22
|
const client = new OpenAI({
|
|
23
23
|
apiKey: config3.apiKey,
|
|
24
|
-
baseURL: config3.baseUrl
|
|
24
|
+
baseURL: config3.baseUrl,
|
|
25
|
+
timeout: 12e4
|
|
26
|
+
// 2min timeout
|
|
25
27
|
});
|
|
26
28
|
const openaiTools = tools.map((t2) => ({
|
|
27
29
|
type: "function",
|
|
@@ -70,6 +72,7 @@ var OpenAICompatibleProvider = class {
|
|
|
70
72
|
});
|
|
71
73
|
const toolCallAccumulator = /* @__PURE__ */ new Map();
|
|
72
74
|
let tokenUsage;
|
|
75
|
+
let toolCallStartEmitted = false;
|
|
73
76
|
for await (const chunk of stream) {
|
|
74
77
|
if (chunk.usage) {
|
|
75
78
|
tokenUsage = {
|
|
@@ -85,6 +88,10 @@ var OpenAICompatibleProvider = class {
|
|
|
85
88
|
yield { type: "text", content: delta.content };
|
|
86
89
|
}
|
|
87
90
|
if (delta.tool_calls) {
|
|
91
|
+
if (!toolCallStartEmitted) {
|
|
92
|
+
toolCallStartEmitted = true;
|
|
93
|
+
yield { type: "tool_call_start" };
|
|
94
|
+
}
|
|
88
95
|
for (const tc of delta.tool_calls) {
|
|
89
96
|
const idx = tc.index;
|
|
90
97
|
if (!toolCallAccumulator.has(idx)) {
|
|
@@ -176,14 +183,18 @@ var OpenAICompatibleProvider = class {
|
|
|
176
183
|
body.tools = kimiTools;
|
|
177
184
|
}
|
|
178
185
|
try {
|
|
186
|
+
const controller = new AbortController();
|
|
187
|
+
const connectTimeout = setTimeout(() => controller.abort(), 3e4);
|
|
179
188
|
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
180
189
|
method: "POST",
|
|
181
190
|
headers: {
|
|
182
191
|
"Content-Type": "application/json",
|
|
183
192
|
"Authorization": `Bearer ${config3.apiKey}`
|
|
184
193
|
},
|
|
185
|
-
body: JSON.stringify(body)
|
|
194
|
+
body: JSON.stringify(body),
|
|
195
|
+
signal: controller.signal
|
|
186
196
|
});
|
|
197
|
+
clearTimeout(connectTimeout);
|
|
187
198
|
if (!response.ok) {
|
|
188
199
|
const errText = await response.text();
|
|
189
200
|
yield { type: "error", error: `${response.status} ${errText}` };
|
|
@@ -199,71 +210,83 @@ var OpenAICompatibleProvider = class {
|
|
|
199
210
|
const toolCallAccumulator = /* @__PURE__ */ new Map();
|
|
200
211
|
let tokenUsage;
|
|
201
212
|
let reasoningContent = "";
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const choice = parsed.choices?.[0];
|
|
227
|
-
if (!choice) continue;
|
|
228
|
-
const delta = choice.delta || {};
|
|
229
|
-
if (delta.reasoning_content) {
|
|
230
|
-
reasoningContent += delta.reasoning_content;
|
|
231
|
-
}
|
|
232
|
-
if (delta.content) {
|
|
233
|
-
yield { type: "text", content: delta.content };
|
|
234
|
-
}
|
|
235
|
-
if (delta.tool_calls) {
|
|
236
|
-
for (const tc of delta.tool_calls) {
|
|
237
|
-
const idx = tc.index ?? 0;
|
|
238
|
-
if (!toolCallAccumulator.has(idx)) {
|
|
239
|
-
toolCallAccumulator.set(idx, { id: tc.id || "", functionName: tc.function?.name || "", arguments: "" });
|
|
240
|
-
}
|
|
241
|
-
const acc = toolCallAccumulator.get(idx);
|
|
242
|
-
if (tc.id) acc.id = tc.id;
|
|
243
|
-
if (tc.function?.name) acc.functionName = tc.function.name;
|
|
244
|
-
if (tc.function?.arguments) acc.arguments += tc.function.arguments;
|
|
213
|
+
let finished = false;
|
|
214
|
+
let toolCallStartEmitted = false;
|
|
215
|
+
const READ_TIMEOUT_MS = 12e4;
|
|
216
|
+
try {
|
|
217
|
+
while (true) {
|
|
218
|
+
const readPromise = reader.read();
|
|
219
|
+
const timeoutPromise = new Promise(
|
|
220
|
+
(resolve) => setTimeout(() => resolve({ done: true, value: void 0 }), READ_TIMEOUT_MS)
|
|
221
|
+
);
|
|
222
|
+
const { done, value } = await Promise.race([readPromise, timeoutPromise]);
|
|
223
|
+
if (done) break;
|
|
224
|
+
buffer += decoder.decode(value, { stream: true });
|
|
225
|
+
const lines = buffer.split("\n");
|
|
226
|
+
buffer = lines.pop() || "";
|
|
227
|
+
for (const line of lines) {
|
|
228
|
+
const trimmed = line.trim();
|
|
229
|
+
if (!trimmed.startsWith("data: ")) continue;
|
|
230
|
+
const data = trimmed.slice(6);
|
|
231
|
+
if (data === "[DONE]") continue;
|
|
232
|
+
let parsed;
|
|
233
|
+
try {
|
|
234
|
+
parsed = JSON.parse(data);
|
|
235
|
+
} catch {
|
|
236
|
+
continue;
|
|
245
237
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
type: "function",
|
|
252
|
-
function: { name: acc.functionName, arguments: acc.arguments }
|
|
238
|
+
if (parsed.usage) {
|
|
239
|
+
tokenUsage = {
|
|
240
|
+
promptTokens: parsed.usage.prompt_tokens || 0,
|
|
241
|
+
completionTokens: parsed.usage.completion_tokens || 0,
|
|
242
|
+
totalTokens: parsed.usage.total_tokens || 0
|
|
253
243
|
};
|
|
254
|
-
yield { type: "tool_call", toolCall };
|
|
255
244
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
245
|
+
const choice = parsed.choices?.[0];
|
|
246
|
+
if (!choice) continue;
|
|
247
|
+
const delta = choice.delta || {};
|
|
248
|
+
if (delta.reasoning_content) {
|
|
249
|
+
reasoningContent += delta.reasoning_content;
|
|
250
|
+
}
|
|
251
|
+
if (delta.content) {
|
|
252
|
+
yield { type: "text", content: delta.content };
|
|
253
|
+
}
|
|
254
|
+
if (delta.tool_calls) {
|
|
255
|
+
if (!toolCallStartEmitted) {
|
|
256
|
+
toolCallStartEmitted = true;
|
|
257
|
+
yield { type: "tool_call_start" };
|
|
258
|
+
}
|
|
259
|
+
for (const tc of delta.tool_calls) {
|
|
260
|
+
const idx = tc.index ?? 0;
|
|
261
|
+
if (!toolCallAccumulator.has(idx)) {
|
|
262
|
+
toolCallAccumulator.set(idx, { id: tc.id || "", functionName: tc.function?.name || "", arguments: "" });
|
|
263
|
+
}
|
|
264
|
+
const acc = toolCallAccumulator.get(idx);
|
|
265
|
+
if (tc.id) acc.id = tc.id;
|
|
266
|
+
if (tc.function?.name) acc.functionName = tc.function.name;
|
|
267
|
+
if (tc.function?.arguments) acc.arguments += tc.function.arguments;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (choice.finish_reason === "tool_calls" || choice.finish_reason === "stop") {
|
|
271
|
+
finished = true;
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
262
274
|
}
|
|
275
|
+
if (finished) break;
|
|
276
|
+
}
|
|
277
|
+
} finally {
|
|
278
|
+
try {
|
|
279
|
+
reader.cancel();
|
|
280
|
+
} catch {
|
|
263
281
|
}
|
|
264
282
|
}
|
|
265
283
|
for (const [_, acc] of toolCallAccumulator) {
|
|
266
|
-
|
|
284
|
+
const toolCall = {
|
|
285
|
+
id: acc.id,
|
|
286
|
+
type: "function",
|
|
287
|
+
function: { name: acc.functionName, arguments: acc.arguments }
|
|
288
|
+
};
|
|
289
|
+
yield { type: "tool_call", toolCall };
|
|
267
290
|
}
|
|
268
291
|
yield { type: "done", usage: tokenUsage, content: reasoningContent || void 0 };
|
|
269
292
|
} catch (err) {
|
|
@@ -403,22 +426,49 @@ var AnthropicProvider = class {
|
|
|
403
426
|
|
|
404
427
|
// src/core/providers/types.ts
|
|
405
428
|
var MODEL_PROVIDERS = {
|
|
406
|
-
// Kimi (月之暗面 Moonshot)
|
|
407
|
-
"kimi-k2.5": { provider: "kimi", displayName: "Kimi K2.5
|
|
408
|
-
"kimi-k2-0905-preview": { provider: "kimi", displayName: "Kimi K2" },
|
|
409
|
-
"moonshot-v1-auto": { provider: "kimi", displayName: "Moonshot v1 Auto" },
|
|
410
|
-
// OpenAI
|
|
411
|
-
"gpt-4o": { provider: "openai", displayName: "GPT-4o" },
|
|
412
|
-
"gpt-4o-mini": { provider: "openai", displayName: "GPT-4o Mini" },
|
|
413
|
-
|
|
414
|
-
"
|
|
415
|
-
"
|
|
416
|
-
//
|
|
417
|
-
"
|
|
418
|
-
"
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
"
|
|
429
|
+
// ── Kimi (月之暗面 Moonshot) ──────────────────
|
|
430
|
+
"kimi-k2.5": { provider: "kimi", displayName: "Kimi K2.5", group: "Kimi \u6708\u4E4B\u6697\u9762" },
|
|
431
|
+
"kimi-k2-0905-preview": { provider: "kimi", displayName: "Kimi K2", group: "Kimi \u6708\u4E4B\u6697\u9762" },
|
|
432
|
+
"moonshot-v1-auto": { provider: "kimi", displayName: "Moonshot v1 Auto", group: "Kimi \u6708\u4E4B\u6697\u9762" },
|
|
433
|
+
// ── OpenAI ────────────────────────────────────
|
|
434
|
+
"gpt-4o": { provider: "openai", displayName: "GPT-4o", group: "OpenAI" },
|
|
435
|
+
"gpt-4o-mini": { provider: "openai", displayName: "GPT-4o Mini", group: "OpenAI" },
|
|
436
|
+
"gpt-4.1": { provider: "openai", displayName: "GPT-4.1", group: "OpenAI" },
|
|
437
|
+
"gpt-4.1-mini": { provider: "openai", displayName: "GPT-4.1 Mini", group: "OpenAI" },
|
|
438
|
+
"o3-mini": { provider: "openai", displayName: "o3-mini (\u63A8\u7406)", group: "OpenAI" },
|
|
439
|
+
// ── Anthropic Claude ──────────────────────────
|
|
440
|
+
"claude-sonnet-4-20250514": { provider: "anthropic", displayName: "Claude Sonnet 4", group: "Anthropic Claude" },
|
|
441
|
+
"claude-3-5-sonnet-20241022": { provider: "anthropic", displayName: "Claude 3.5 Sonnet", group: "Anthropic Claude" },
|
|
442
|
+
"claude-3-5-haiku-20241022": { provider: "anthropic", displayName: "Claude 3.5 Haiku", group: "Anthropic Claude" },
|
|
443
|
+
// ── DeepSeek ──────────────────────────────────
|
|
444
|
+
"deepseek-chat": { provider: "deepseek", displayName: "DeepSeek V3", group: "DeepSeek \u6DF1\u5EA6\u6C42\u7D22" },
|
|
445
|
+
"deepseek-reasoner": { provider: "deepseek", displayName: "DeepSeek R1 (\u63A8\u7406)", group: "DeepSeek \u6DF1\u5EA6\u6C42\u7D22" },
|
|
446
|
+
// ── 通义千问 Qwen ────────────────────────────
|
|
447
|
+
"qwen-max": { provider: "qwen", displayName: "\u901A\u4E49\u5343\u95EE Max", group: "\u901A\u4E49\u5343\u95EE Qwen" },
|
|
448
|
+
"qwen-plus": { provider: "qwen", displayName: "\u901A\u4E49\u5343\u95EE Plus", group: "\u901A\u4E49\u5343\u95EE Qwen" },
|
|
449
|
+
"qwen-turbo": { provider: "qwen", displayName: "\u901A\u4E49\u5343\u95EE Turbo", group: "\u901A\u4E49\u5343\u95EE Qwen" },
|
|
450
|
+
"qwen-coder-plus": { provider: "qwen", displayName: "\u901A\u4E49\u5343\u95EE Coder Plus", group: "\u901A\u4E49\u5343\u95EE Qwen" },
|
|
451
|
+
// ── 智谱 GLM ──────────────────────────────────
|
|
452
|
+
"glm-4-plus": { provider: "zhipu", displayName: "GLM-4 Plus", group: "\u667A\u8C31 GLM" },
|
|
453
|
+
"glm-4-flash": { provider: "zhipu", displayName: "GLM-4 Flash (\u514D\u8D39)", group: "\u667A\u8C31 GLM" },
|
|
454
|
+
"glm-4-long": { provider: "zhipu", displayName: "GLM-4 Long (\u957F\u6587\u672C)", group: "\u667A\u8C31 GLM" },
|
|
455
|
+
"codegeex-4": { provider: "zhipu", displayName: "CodeGeeX 4 (\u7F16\u7A0B)", group: "\u667A\u8C31 GLM" },
|
|
456
|
+
// ── 豆包 Doubao (字节跳动) ────────────────────
|
|
457
|
+
"doubao-1.5-pro-256k": { provider: "doubao", displayName: "\u8C46\u5305 1.5 Pro 256K", group: "\u8C46\u5305 Doubao" },
|
|
458
|
+
"doubao-1.5-lite-32k": { provider: "doubao", displayName: "\u8C46\u5305 1.5 Lite 32K", group: "\u8C46\u5305 Doubao" },
|
|
459
|
+
// ── 百川 Baichuan ────────────────────────────
|
|
460
|
+
"Baichuan4-Turbo": { provider: "baichuan", displayName: "\u767E\u5DDD4 Turbo", group: "\u767E\u5DDD Baichuan" },
|
|
461
|
+
// ── 零一万物 Yi ───────────────────────────────
|
|
462
|
+
"yi-lightning": { provider: "lingyiwanwu", displayName: "Yi Lightning", group: "\u96F6\u4E00\u4E07\u7269 Yi" },
|
|
463
|
+
// ── Gemini (Google) ───────────────────────────
|
|
464
|
+
"gemini-2.5-flash": { provider: "gemini", displayName: "Gemini 2.5 Flash", group: "Google Gemini" },
|
|
465
|
+
"gemini-2.5-pro": { provider: "gemini", displayName: "Gemini 2.5 Pro", group: "Google Gemini" },
|
|
466
|
+
// ── Mistral ───────────────────────────────────
|
|
467
|
+
"mistral-large-latest": { provider: "mistral", displayName: "Mistral Large", group: "Mistral" },
|
|
468
|
+
"codestral-latest": { provider: "mistral", displayName: "Codestral (\u7F16\u7A0B)", group: "Mistral" },
|
|
469
|
+
// ── Groq (超快推理) ──────────────────────────
|
|
470
|
+
"llama-3.3-70b-versatile": { provider: "groq", displayName: "Llama 3.3 70B", group: "Groq" },
|
|
471
|
+
"llama-3.1-8b-instant": { provider: "groq", displayName: "Llama 3.1 8B (\u6781\u901F)", group: "Groq" }
|
|
422
472
|
};
|
|
423
473
|
|
|
424
474
|
// src/core/providers/index.ts
|
|
@@ -427,6 +477,13 @@ var providers = {
|
|
|
427
477
|
deepseek: new OpenAICompatibleProvider("deepseek"),
|
|
428
478
|
qwen: new OpenAICompatibleProvider("qwen"),
|
|
429
479
|
kimi: new OpenAICompatibleProvider("kimi"),
|
|
480
|
+
zhipu: new OpenAICompatibleProvider("zhipu"),
|
|
481
|
+
doubao: new OpenAICompatibleProvider("doubao"),
|
|
482
|
+
baichuan: new OpenAICompatibleProvider("baichuan"),
|
|
483
|
+
lingyiwanwu: new OpenAICompatibleProvider("lingyiwanwu"),
|
|
484
|
+
gemini: new OpenAICompatibleProvider("gemini"),
|
|
485
|
+
mistral: new OpenAICompatibleProvider("mistral"),
|
|
486
|
+
groq: new OpenAICompatibleProvider("groq"),
|
|
430
487
|
anthropic: new AnthropicProvider()
|
|
431
488
|
};
|
|
432
489
|
var DEFAULT_BASE_URLS = {
|
|
@@ -434,7 +491,14 @@ var DEFAULT_BASE_URLS = {
|
|
|
434
491
|
deepseek: "https://api.deepseek.com",
|
|
435
492
|
qwen: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
436
493
|
kimi: "https://api.moonshot.cn/v1",
|
|
437
|
-
anthropic: "https://api.anthropic.com"
|
|
494
|
+
anthropic: "https://api.anthropic.com",
|
|
495
|
+
zhipu: "https://open.bigmodel.cn/api/paas/v4",
|
|
496
|
+
doubao: "https://ark.cn-beijing.volces.com/api/v3",
|
|
497
|
+
baichuan: "https://api.baichuan-ai.com/v1",
|
|
498
|
+
lingyiwanwu: "https://api.lingyiwanwu.com/v1",
|
|
499
|
+
gemini: "https://generativelanguage.googleapis.com/v1beta/openai",
|
|
500
|
+
mistral: "https://api.mistral.ai/v1",
|
|
501
|
+
groq: "https://api.groq.com/openai/v1"
|
|
438
502
|
};
|
|
439
503
|
function getProvider(model) {
|
|
440
504
|
const info = MODEL_PROVIDERS[model];
|
|
@@ -453,7 +517,8 @@ function listModels() {
|
|
|
453
517
|
return Object.entries(MODEL_PROVIDERS).map(([id, info]) => ({
|
|
454
518
|
id,
|
|
455
519
|
name: info.displayName,
|
|
456
|
-
provider: info.provider
|
|
520
|
+
provider: info.provider,
|
|
521
|
+
group: info.group
|
|
457
522
|
}));
|
|
458
523
|
}
|
|
459
524
|
|
|
@@ -1090,22 +1155,25 @@ var theme = {
|
|
|
1090
1155
|
tokenValue: chalk.hex("#A78BFA")
|
|
1091
1156
|
};
|
|
1092
1157
|
function banner() {
|
|
1093
|
-
const
|
|
1094
|
-
const
|
|
1095
|
-
const
|
|
1096
|
-
const
|
|
1097
|
-
const
|
|
1098
|
-
const
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
${
|
|
1103
|
-
${
|
|
1104
|
-
${
|
|
1105
|
-
${
|
|
1158
|
+
const p = theme.brand;
|
|
1159
|
+
const b = theme.brandBold;
|
|
1160
|
+
const d = theme.dim;
|
|
1161
|
+
const m = theme.muted;
|
|
1162
|
+
const y = chalk.hex("#FBBF24");
|
|
1163
|
+
const r = chalk.hex("#F87171");
|
|
1164
|
+
const w = chalk.white.bold;
|
|
1165
|
+
const lines = [
|
|
1166
|
+
``,
|
|
1167
|
+
` ${p("/\\_/\\")} ${b("\u6C88\u7FD4\u7684AI\u52A9\u624B")} ${m("v0.2.1")}`,
|
|
1168
|
+
` ${p("(")} ${y("o")}${p(".")}${y("o")} ${p(")")} ${d("\u7EC8\u7AEF\u91CC\u7684AI\u5168\u6808\u5F00\u53D1\u642D\u6863")}`,
|
|
1169
|
+
` ${p("> ")}${r("^")}${p(" <")}`,
|
|
1170
|
+
` ${p("/|")}${p(" |")}${p("\\")} ${d("\u4F60\u7684\u7F16\u7A0B\u597D\u4F19\u4F34")} ${chalk.hex("#A78BFA")("\u{1F43E}")}`,
|
|
1171
|
+
` ${p("(_| |_)")}`
|
|
1172
|
+
];
|
|
1173
|
+
return lines.join("\n");
|
|
1106
1174
|
}
|
|
1107
1175
|
function separator() {
|
|
1108
|
-
return theme.dim("\u2500".repeat(
|
|
1176
|
+
return theme.dim("\u2500".repeat(40));
|
|
1109
1177
|
}
|
|
1110
1178
|
|
|
1111
1179
|
// src/ui/renderer.ts
|
|
@@ -1636,13 +1704,41 @@ function getFrameworkGuide(framework) {
|
|
|
1636
1704
|
|
|
1637
1705
|
// src/utils/config.ts
|
|
1638
1706
|
import Conf from "conf";
|
|
1707
|
+
var PROVIDER_KEY_MAP = {
|
|
1708
|
+
openai: "openaiApiKey",
|
|
1709
|
+
anthropic: "anthropicApiKey",
|
|
1710
|
+
deepseek: "deepseekApiKey",
|
|
1711
|
+
kimi: "kimiApiKey",
|
|
1712
|
+
qwen: "qwenApiKey",
|
|
1713
|
+
zhipu: "zhipuApiKey",
|
|
1714
|
+
doubao: "doubaoApiKey",
|
|
1715
|
+
baichuan: "baichuanApiKey",
|
|
1716
|
+
lingyiwanwu: "lingyiwanwuApiKey",
|
|
1717
|
+
gemini: "geminiApiKey",
|
|
1718
|
+
mistral: "mistralApiKey",
|
|
1719
|
+
groq: "groqApiKey"
|
|
1720
|
+
};
|
|
1721
|
+
var PROVIDER_NAMES = {
|
|
1722
|
+
openai: "OpenAI",
|
|
1723
|
+
anthropic: "Anthropic Claude",
|
|
1724
|
+
deepseek: "DeepSeek \u6DF1\u5EA6\u6C42\u7D22",
|
|
1725
|
+
kimi: "Kimi \u6708\u4E4B\u6697\u9762",
|
|
1726
|
+
qwen: "\u901A\u4E49\u5343\u95EE Qwen",
|
|
1727
|
+
zhipu: "\u667A\u8C31 GLM",
|
|
1728
|
+
doubao: "\u8C46\u5305 Doubao",
|
|
1729
|
+
baichuan: "\u767E\u5DDD Baichuan",
|
|
1730
|
+
lingyiwanwu: "\u96F6\u4E00\u4E07\u7269 Yi",
|
|
1731
|
+
gemini: "Google Gemini",
|
|
1732
|
+
mistral: "Mistral",
|
|
1733
|
+
groq: "Groq"
|
|
1734
|
+
};
|
|
1639
1735
|
var defaults = {
|
|
1640
1736
|
locale: "zh",
|
|
1641
1737
|
model: "kimi-k2.5",
|
|
1642
1738
|
apiBaseUrl: "http://localhost:3210"
|
|
1643
1739
|
};
|
|
1644
1740
|
var config = new Conf({
|
|
1645
|
-
projectName: "
|
|
1741
|
+
projectName: "sxai",
|
|
1646
1742
|
defaults
|
|
1647
1743
|
});
|
|
1648
1744
|
function getConfig() {
|
|
@@ -1655,11 +1751,29 @@ function getConfig() {
|
|
|
1655
1751
|
openaiApiKey: config.get("openaiApiKey"),
|
|
1656
1752
|
anthropicApiKey: config.get("anthropicApiKey"),
|
|
1657
1753
|
deepseekApiKey: config.get("deepseekApiKey"),
|
|
1658
|
-
kimiApiKey: config.get("kimiApiKey")
|
|
1754
|
+
kimiApiKey: config.get("kimiApiKey"),
|
|
1755
|
+
qwenApiKey: config.get("qwenApiKey"),
|
|
1756
|
+
zhipuApiKey: config.get("zhipuApiKey"),
|
|
1757
|
+
doubaoApiKey: config.get("doubaoApiKey"),
|
|
1758
|
+
baichuanApiKey: config.get("baichuanApiKey"),
|
|
1759
|
+
lingyiwanwuApiKey: config.get("lingyiwanwuApiKey"),
|
|
1760
|
+
geminiApiKey: config.get("geminiApiKey"),
|
|
1761
|
+
mistralApiKey: config.get("mistralApiKey"),
|
|
1762
|
+
groqApiKey: config.get("groqApiKey")
|
|
1659
1763
|
};
|
|
1660
1764
|
}
|
|
1661
1765
|
function setConfig(key, value) {
|
|
1662
|
-
|
|
1766
|
+
if (value === void 0) {
|
|
1767
|
+
config.delete(key);
|
|
1768
|
+
} else {
|
|
1769
|
+
config.set(key, value);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
function getProviderApiKey(provider) {
|
|
1773
|
+
const cfg = getConfig();
|
|
1774
|
+
const keyName = PROVIDER_KEY_MAP[provider];
|
|
1775
|
+
if (!keyName) return void 0;
|
|
1776
|
+
return cfg[keyName];
|
|
1663
1777
|
}
|
|
1664
1778
|
function getConfigPath() {
|
|
1665
1779
|
return config.path;
|
|
@@ -1733,6 +1847,8 @@ var Agent = class {
|
|
|
1733
1847
|
let reasoningContent = "";
|
|
1734
1848
|
const spinner = ora({ text: t("thinking"), spinner: "dots" }).start();
|
|
1735
1849
|
let spinnerStopped = false;
|
|
1850
|
+
let hasTextOutput = false;
|
|
1851
|
+
let toolSpinnerStarted = false;
|
|
1736
1852
|
try {
|
|
1737
1853
|
for await (const chunk of provider.chatStream(this.messages, this.tools, config3)) {
|
|
1738
1854
|
if (!spinnerStopped && (chunk.type === "text" || chunk.type === "tool_call")) {
|
|
@@ -1741,12 +1857,26 @@ var Agent = class {
|
|
|
1741
1857
|
if (chunk.type === "text") {
|
|
1742
1858
|
process.stdout.write("\n");
|
|
1743
1859
|
}
|
|
1860
|
+
if (chunk.type === "tool_call" && !hasTextOutput) {
|
|
1861
|
+
spinner.text = "\u6B63\u5728\u51C6\u5907\u64CD\u4F5C...";
|
|
1862
|
+
spinner.start();
|
|
1863
|
+
toolSpinnerStarted = true;
|
|
1864
|
+
}
|
|
1744
1865
|
}
|
|
1745
1866
|
switch (chunk.type) {
|
|
1746
1867
|
case "text":
|
|
1747
1868
|
responseText += chunk.content || "";
|
|
1869
|
+
hasTextOutput = true;
|
|
1748
1870
|
streamChunk(chunk.content || "");
|
|
1749
1871
|
break;
|
|
1872
|
+
case "tool_call_start":
|
|
1873
|
+
if (!toolSpinnerStarted) {
|
|
1874
|
+
if (hasTextOutput) process.stdout.write("\n");
|
|
1875
|
+
spinner.text = "\u6B63\u5728\u51C6\u5907\u64CD\u4F5C...";
|
|
1876
|
+
spinner.start();
|
|
1877
|
+
toolSpinnerStarted = true;
|
|
1878
|
+
}
|
|
1879
|
+
break;
|
|
1750
1880
|
case "tool_call":
|
|
1751
1881
|
if (chunk.toolCall) {
|
|
1752
1882
|
toolCalls.push(chunk.toolCall);
|
|
@@ -1770,11 +1900,13 @@ var Agent = class {
|
|
|
1770
1900
|
}
|
|
1771
1901
|
} catch (err) {
|
|
1772
1902
|
if (!spinnerStopped) spinner.stop();
|
|
1903
|
+
if (toolSpinnerStarted) spinner.stop();
|
|
1773
1904
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1774
1905
|
printError(t("errors.apiError", { message: msg }));
|
|
1775
1906
|
return "";
|
|
1776
1907
|
}
|
|
1777
1908
|
if (!spinnerStopped) spinner.stop();
|
|
1909
|
+
if (toolSpinnerStarted) spinner.stop();
|
|
1778
1910
|
if (responseText) {
|
|
1779
1911
|
process.stdout.write("\n");
|
|
1780
1912
|
}
|
|
@@ -1816,43 +1948,29 @@ var Agent = class {
|
|
|
1816
1948
|
return finalResponse;
|
|
1817
1949
|
}
|
|
1818
1950
|
/**
|
|
1819
|
-
* Get provider configuration based on current settings
|
|
1951
|
+
* Get provider configuration based on current settings.
|
|
1952
|
+
* Priority: user's own API key (BYOK, direct) > server proxy (via authToken)
|
|
1820
1953
|
*/
|
|
1821
1954
|
getProviderConfig() {
|
|
1822
1955
|
const cfg = getConfig();
|
|
1823
1956
|
const model = cfg.model;
|
|
1824
1957
|
const providerName = getProviderName(model);
|
|
1825
|
-
|
|
1826
|
-
if (
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
break;
|
|
1833
|
-
case "anthropic":
|
|
1834
|
-
apiKey = cfg.anthropicApiKey || "";
|
|
1835
|
-
break;
|
|
1836
|
-
case "deepseek":
|
|
1837
|
-
apiKey = cfg.deepseekApiKey || "";
|
|
1838
|
-
break;
|
|
1839
|
-
case "kimi":
|
|
1840
|
-
apiKey = cfg.kimiApiKey || "";
|
|
1841
|
-
break;
|
|
1842
|
-
case "qwen":
|
|
1843
|
-
apiKey = cfg.openaiApiKey || "";
|
|
1844
|
-
break;
|
|
1845
|
-
default:
|
|
1846
|
-
apiKey = cfg.openaiApiKey || "";
|
|
1847
|
-
}
|
|
1958
|
+
const ownKey = getProviderApiKey(providerName);
|
|
1959
|
+
if (ownKey) {
|
|
1960
|
+
return {
|
|
1961
|
+
apiKey: ownKey,
|
|
1962
|
+
baseUrl: getDefaultBaseUrl(providerName),
|
|
1963
|
+
model
|
|
1964
|
+
};
|
|
1848
1965
|
}
|
|
1849
|
-
let baseUrl;
|
|
1850
1966
|
if (cfg.authToken) {
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1967
|
+
return {
|
|
1968
|
+
apiKey: cfg.authToken,
|
|
1969
|
+
baseUrl: `${cfg.apiBaseUrl}/api/ai/proxy`,
|
|
1970
|
+
model
|
|
1971
|
+
};
|
|
1854
1972
|
}
|
|
1855
|
-
return { apiKey, baseUrl, model };
|
|
1973
|
+
return { apiKey: "", baseUrl: getDefaultBaseUrl(providerName), model };
|
|
1856
1974
|
}
|
|
1857
1975
|
/**
|
|
1858
1976
|
* Trim old messages to prevent context overflow
|
|
@@ -1887,31 +2005,44 @@ var Agent = class {
|
|
|
1887
2005
|
};
|
|
1888
2006
|
|
|
1889
2007
|
// src/commands/chat.ts
|
|
1890
|
-
function question(rl, prompt) {
|
|
1891
|
-
return new Promise((resolve, reject) => {
|
|
1892
|
-
const onClose = () => reject(new Error("readline closed"));
|
|
1893
|
-
rl.once("close", onClose);
|
|
1894
|
-
rl.question(prompt, (answer) => {
|
|
1895
|
-
rl.removeListener("close", onClose);
|
|
1896
|
-
resolve(answer);
|
|
1897
|
-
});
|
|
1898
|
-
});
|
|
1899
|
-
}
|
|
1900
2008
|
async function chatCommand() {
|
|
1901
2009
|
const config3 = getConfig();
|
|
1902
2010
|
setLocale(config3.locale);
|
|
2011
|
+
process.on("unhandledRejection", (reason) => {
|
|
2012
|
+
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
2013
|
+
if (msg.includes("abort") || msg.includes("cancel") || msg.includes("ABORT")) return;
|
|
2014
|
+
console.error(theme.error(`
|
|
2015
|
+
\u26A0 \u5F02\u6B65\u9519\u8BEF: ${msg}`));
|
|
2016
|
+
});
|
|
2017
|
+
process.on("uncaughtException", (err) => {
|
|
2018
|
+
console.error(theme.error(`
|
|
2019
|
+
\u26A0 \u672A\u6355\u83B7\u9519\u8BEF: ${err.message}`));
|
|
2020
|
+
});
|
|
1903
2021
|
console.log(banner());
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
const
|
|
1907
|
-
if (
|
|
2022
|
+
console.log(theme.dim(` ${t("exitHint")}`));
|
|
2023
|
+
const providerName = getProviderName(config3.model);
|
|
2024
|
+
const hasOwnKey = !!getProviderApiKey(providerName);
|
|
2025
|
+
if (hasOwnKey) {
|
|
2026
|
+
} else if (!config3.authToken || !config3.userEmail) {
|
|
2027
|
+
console.log();
|
|
2028
|
+
printInfo("\u9996\u6B21\u4F7F\u7528\uFF0C\u8BF7\u6CE8\u518C\u8D26\u6237\uFF08\u914D\u7F6E\u81EA\u5DF1\u7684API Key\u53EF\u8DF3\u8FC7\u6CE8\u518C\uFF0C\u672C\u5730\u4F7F\u7528\uFF09");
|
|
2029
|
+
console.log(theme.dim(" \u63D0\u793A: sxai set-key kimi <your-key> \u5373\u53EF\u514D\u767B\u5F55\u4F7F\u7528"));
|
|
2030
|
+
console.log();
|
|
2031
|
+
const loggedIn = await doInlineRegister();
|
|
2032
|
+
if (!loggedIn) {
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
1908
2035
|
console.log();
|
|
1909
|
-
printWarning("\u5C1A\u672A\u914D\u7F6EAPI\u5BC6\u94A5\u3002\u8BF7\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u4E4B\u4E00:");
|
|
1910
|
-
console.log(theme.dim(" sxai set-key kimi <your-key> # Kimi (\u6708\u4E4B\u6697\u9762)"));
|
|
1911
|
-
console.log(theme.dim(" sxai set-key deepseek <your-key>"));
|
|
1912
|
-
console.log(theme.dim(" sxai set-key openai <your-key>"));
|
|
1913
|
-
console.log(theme.dim(" sxai config # \u4EA4\u4E92\u5F0F\u914D\u7F6E"));
|
|
1914
2036
|
}
|
|
2037
|
+
registerQuestionFn(async (prompt) => {
|
|
2038
|
+
const { answer } = await inquirer.prompt([{
|
|
2039
|
+
type: "input",
|
|
2040
|
+
name: "answer",
|
|
2041
|
+
message: prompt,
|
|
2042
|
+
prefix: ""
|
|
2043
|
+
}]);
|
|
2044
|
+
return answer;
|
|
2045
|
+
});
|
|
1915
2046
|
const agent = new Agent();
|
|
1916
2047
|
try {
|
|
1917
2048
|
await agent.init();
|
|
@@ -1929,36 +2060,20 @@ async function chatCommand() {
|
|
|
1929
2060
|
printError(`\u521D\u59CB\u5316\u5931\u8D25: ${err instanceof Error ? err.message : err}`);
|
|
1930
2061
|
process.exit(1);
|
|
1931
2062
|
}
|
|
1932
|
-
const rl = readline.createInterface({
|
|
1933
|
-
input: process.stdin,
|
|
1934
|
-
output: process.stdout,
|
|
1935
|
-
terminal: true
|
|
1936
|
-
});
|
|
1937
|
-
registerQuestionFn((prompt2) => question(rl, prompt2));
|
|
1938
|
-
let ctrlCCount = 0;
|
|
1939
|
-
process.on("SIGINT", () => {
|
|
1940
|
-
ctrlCCount++;
|
|
1941
|
-
if (ctrlCCount >= 2) {
|
|
1942
|
-
console.log();
|
|
1943
|
-
printSuccess(t("exit"));
|
|
1944
|
-
process.exit(0);
|
|
1945
|
-
}
|
|
1946
|
-
console.log(theme.dim("\n(\u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA)"));
|
|
1947
|
-
setTimeout(() => {
|
|
1948
|
-
ctrlCCount = 0;
|
|
1949
|
-
}, 2e3);
|
|
1950
|
-
});
|
|
1951
|
-
const prompt = theme.user(t("prompt"));
|
|
1952
2063
|
while (true) {
|
|
1953
2064
|
let input;
|
|
1954
2065
|
try {
|
|
1955
|
-
|
|
2066
|
+
const result = await inquirer.prompt([{
|
|
2067
|
+
type: "input",
|
|
2068
|
+
name: "input",
|
|
2069
|
+
message: theme.user("\u4F60:"),
|
|
2070
|
+
prefix: ""
|
|
2071
|
+
}]);
|
|
2072
|
+
input = result.input.trim();
|
|
1956
2073
|
} catch {
|
|
1957
2074
|
break;
|
|
1958
2075
|
}
|
|
1959
|
-
input = input.trim();
|
|
1960
2076
|
if (!input) continue;
|
|
1961
|
-
ctrlCCount = 0;
|
|
1962
2077
|
if (input.startsWith("/")) {
|
|
1963
2078
|
const result = await handleSlashCommand(input, agent);
|
|
1964
2079
|
if (result === "exit") break;
|
|
@@ -1977,11 +2092,75 @@ async function chatCommand() {
|
|
|
1977
2092
|
}
|
|
1978
2093
|
console.log();
|
|
1979
2094
|
}
|
|
1980
|
-
rl.close();
|
|
1981
2095
|
console.log();
|
|
1982
2096
|
printSuccess(t("exit"));
|
|
1983
2097
|
process.exit(0);
|
|
1984
2098
|
}
|
|
2099
|
+
async function doInlineRegister() {
|
|
2100
|
+
const config3 = getConfig();
|
|
2101
|
+
const { email } = await inquirer.prompt([{
|
|
2102
|
+
type: "input",
|
|
2103
|
+
name: "email",
|
|
2104
|
+
message: "\u90AE\u7BB1:",
|
|
2105
|
+
validate: (v) => v.includes("@") ? true : "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u90AE\u7BB1"
|
|
2106
|
+
}]);
|
|
2107
|
+
const { password } = await inquirer.prompt([{
|
|
2108
|
+
type: "password",
|
|
2109
|
+
name: "password",
|
|
2110
|
+
message: "\u8BBE\u7F6E\u5BC6\u7801 (\u81F3\u5C116\u4F4D):",
|
|
2111
|
+
mask: "*",
|
|
2112
|
+
validate: (v) => v.length >= 6 ? true : "\u5BC6\u7801\u81F3\u5C116\u4F4D"
|
|
2113
|
+
}]);
|
|
2114
|
+
const { name } = await inquirer.prompt([{
|
|
2115
|
+
type: "input",
|
|
2116
|
+
name: "name",
|
|
2117
|
+
message: "\u6635\u79F0:",
|
|
2118
|
+
default: email.split("@")[0]
|
|
2119
|
+
}]);
|
|
2120
|
+
try {
|
|
2121
|
+
const regResp = await fetch(`${config3.apiBaseUrl}/api/auth/register`, {
|
|
2122
|
+
method: "POST",
|
|
2123
|
+
headers: { "Content-Type": "application/json" },
|
|
2124
|
+
body: JSON.stringify({ email, password, name })
|
|
2125
|
+
});
|
|
2126
|
+
if (regResp.ok) {
|
|
2127
|
+
const data = await regResp.json();
|
|
2128
|
+
setConfig("authToken", data.token);
|
|
2129
|
+
setConfig("userEmail", data.user.email);
|
|
2130
|
+
console.log();
|
|
2131
|
+
printSuccess(`\u6CE8\u518C\u6210\u529F\uFF01\u6B22\u8FCE ${data.user.name}`);
|
|
2132
|
+
console.log(theme.dim(` \u8BA1\u5212: ${data.user.plan} \xB7 \u6BCF\u65E5 ${data.user.dailyLimit} \u6B21\u8BF7\u6C42`));
|
|
2133
|
+
return true;
|
|
2134
|
+
}
|
|
2135
|
+
const regErr = await regResp.json();
|
|
2136
|
+
if (regErr.error.includes("\u5DF2\u6CE8\u518C")) {
|
|
2137
|
+
printInfo("\u8BE5\u90AE\u7BB1\u5DF2\u6CE8\u518C\uFF0C\u6B63\u5728\u5C1D\u8BD5\u767B\u5F55...");
|
|
2138
|
+
const loginResp = await fetch(`${config3.apiBaseUrl}/api/auth/login`, {
|
|
2139
|
+
method: "POST",
|
|
2140
|
+
headers: { "Content-Type": "application/json" },
|
|
2141
|
+
body: JSON.stringify({ email, password })
|
|
2142
|
+
});
|
|
2143
|
+
if (loginResp.ok) {
|
|
2144
|
+
const data = await loginResp.json();
|
|
2145
|
+
setConfig("authToken", data.token);
|
|
2146
|
+
setConfig("userEmail", data.user.email);
|
|
2147
|
+
const roleTip = data.user.role === "admin" ? theme.error(" [\u7BA1\u7406\u5458]") : "";
|
|
2148
|
+
printSuccess(`\u767B\u5F55\u6210\u529F\uFF01\u6B22\u8FCE\u56DE\u6765 ${data.user.name || data.user.email}${roleTip}`);
|
|
2149
|
+
return true;
|
|
2150
|
+
} else {
|
|
2151
|
+
printError("\u5BC6\u7801\u9519\u8BEF\u3002\u8BF7\u91CD\u65B0\u8FD0\u884C sxai \u518D\u8BD5\u3002");
|
|
2152
|
+
return false;
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
printError(`\u6CE8\u518C\u5931\u8D25: ${regErr.error}`);
|
|
2156
|
+
return false;
|
|
2157
|
+
} catch {
|
|
2158
|
+
printError("\u7F51\u7EDC\u8FDE\u63A5\u5931\u8D25\u3002\u8BF7\u786E\u8BA4\u540E\u7AEF\u670D\u52A1\u6B63\u5728\u8FD0\u884C\u3002");
|
|
2159
|
+
console.log(theme.dim(` \u540E\u7AEF\u5730\u5740: ${config3.apiBaseUrl}`));
|
|
2160
|
+
console.log(theme.dim(" \u542F\u52A8\u540E\u7AEF: cd packages/server && pnpm dev"));
|
|
2161
|
+
return false;
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
1985
2164
|
async function handleSlashCommand(input, agent) {
|
|
1986
2165
|
const [cmd, ...args] = input.split(" ");
|
|
1987
2166
|
switch (cmd) {
|
|
@@ -1991,30 +2170,43 @@ async function handleSlashCommand(input, agent) {
|
|
|
1991
2170
|
return "exit";
|
|
1992
2171
|
case "/help":
|
|
1993
2172
|
case "/h":
|
|
1994
|
-
console.log(
|
|
2173
|
+
console.log(`
|
|
2174
|
+
\u53EF\u7528\u547D\u4EE4:
|
|
2175
|
+
/help \u663E\u793A\u5E2E\u52A9
|
|
2176
|
+
/model \u67E5\u770B/\u5207\u6362\u6A21\u578B
|
|
2177
|
+
/clear \u6E05\u9664\u5BF9\u8BDD\u5386\u53F2
|
|
2178
|
+
/account \u67E5\u770B\u8D26\u6237\u4FE1\u606F
|
|
2179
|
+
/status \u67E5\u770B\u9879\u76EE\u4FE1\u606F
|
|
2180
|
+
/config \u67E5\u770B\u914D\u7F6E
|
|
2181
|
+
/logout \u9000\u51FA\u767B\u5F55
|
|
2182
|
+
/exit \u9000\u51FA\u7A0B\u5E8F
|
|
2183
|
+
`);
|
|
1995
2184
|
break;
|
|
1996
2185
|
case "/clear":
|
|
1997
2186
|
agent.clearHistory();
|
|
1998
2187
|
printSuccess("\u5BF9\u8BDD\u5386\u53F2\u5DF2\u6E05\u9664\u3002");
|
|
1999
2188
|
break;
|
|
2000
2189
|
case "/model": {
|
|
2001
|
-
const models = listModels();
|
|
2002
|
-
const currentModel = agent.getCurrentModel();
|
|
2003
|
-
console.log("\n\u53EF\u7528\u6A21\u578B:");
|
|
2004
|
-
for (const m of models) {
|
|
2005
|
-
const marker = m.id === currentModel ? theme.success(" \u25C9") : " \u25CB";
|
|
2006
|
-
console.log(`${marker} ${theme.bold(m.name)} ${theme.dim(`(${m.id})`)}`);
|
|
2007
|
-
}
|
|
2008
2190
|
if (args[0]) {
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
setConfig("model", target);
|
|
2012
|
-
printSuccess(`\u6A21\u578B\u5DF2\u5207\u6362\u4E3A: ${target}`);
|
|
2013
|
-
} else {
|
|
2014
|
-
printError(`\u672A\u77E5\u6A21\u578B: ${target}`);
|
|
2015
|
-
}
|
|
2191
|
+
setConfig("model", args[0]);
|
|
2192
|
+
printSuccess(`\u6A21\u578B\u5DF2\u5207\u6362\u4E3A: ${args[0]}`);
|
|
2016
2193
|
} else {
|
|
2017
|
-
|
|
2194
|
+
const models = listModels();
|
|
2195
|
+
const currentModel = agent.getCurrentModel();
|
|
2196
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2197
|
+
for (const m of models) {
|
|
2198
|
+
if (!groups.has(m.group)) groups.set(m.group, []);
|
|
2199
|
+
groups.get(m.group).push(m);
|
|
2200
|
+
}
|
|
2201
|
+
for (const [group, groupModels] of groups) {
|
|
2202
|
+
console.log(theme.dim(`
|
|
2203
|
+
\u2500\u2500 ${group} \u2500\u2500`));
|
|
2204
|
+
for (const m of groupModels) {
|
|
2205
|
+
const marker = m.id === currentModel ? theme.success(" \u25C9") : " \u25CB";
|
|
2206
|
+
console.log(` ${marker} ${m.name} ${theme.dim(`(${m.id})`)}`);
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
console.log(theme.dim("\n \u4F7F\u7528 /model <model-id> \u5207\u6362\u6A21\u578B"));
|
|
2018
2210
|
}
|
|
2019
2211
|
break;
|
|
2020
2212
|
}
|
|
@@ -2031,126 +2223,139 @@ async function handleSlashCommand(input, agent) {
|
|
|
2031
2223
|
}
|
|
2032
2224
|
break;
|
|
2033
2225
|
}
|
|
2226
|
+
case "/account": {
|
|
2227
|
+
const cfg = getConfig();
|
|
2228
|
+
if (cfg.authToken) {
|
|
2229
|
+
try {
|
|
2230
|
+
const resp = await fetch(`${cfg.apiBaseUrl}/api/auth/me`, {
|
|
2231
|
+
headers: { "Authorization": `Bearer ${cfg.authToken}` }
|
|
2232
|
+
});
|
|
2233
|
+
if (resp.ok) {
|
|
2234
|
+
const data = await resp.json();
|
|
2235
|
+
console.log(`
|
|
2236
|
+
\u90AE\u7BB1: ${data.user.email}`);
|
|
2237
|
+
console.log(` \u8BA1\u5212: ${data.user.plan}`);
|
|
2238
|
+
if (data.quota) {
|
|
2239
|
+
console.log(` \u4ECA\u65E5: ${data.quota.daily.used}/${data.quota.daily.limit}\u6B21`);
|
|
2240
|
+
console.log(` \u603B\u989D: ${fmtTokens(data.quota.lifetimeTokens.used)}/${fmtTokens(data.quota.lifetimeTokens.limit)} tokens`);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
} catch {
|
|
2244
|
+
}
|
|
2245
|
+
} else {
|
|
2246
|
+
printWarning("\u672A\u767B\u5F55");
|
|
2247
|
+
}
|
|
2248
|
+
break;
|
|
2249
|
+
}
|
|
2034
2250
|
case "/config": {
|
|
2035
|
-
const
|
|
2251
|
+
const cfg = getConfig();
|
|
2036
2252
|
console.log("\n\u5F53\u524D\u914D\u7F6E:");
|
|
2037
|
-
console.log(` \u6A21\u578B: ${
|
|
2038
|
-
console.log(` \u8BED\u8A00: ${
|
|
2039
|
-
console.log(` \u540E\u7AEF: ${
|
|
2040
|
-
console.log(` \u767B\u5F55: ${
|
|
2041
|
-
console.log(` Kimi Key: ${config3.kimiApiKey ? "\u5DF2\u914D\u7F6E" : "\u672A\u914D\u7F6E"}`);
|
|
2042
|
-
console.log(` OpenAI Key: ${config3.openaiApiKey ? "\u5DF2\u914D\u7F6E" : "\u672A\u914D\u7F6E"}`);
|
|
2043
|
-
console.log(` Anthropic Key: ${config3.anthropicApiKey ? "\u5DF2\u914D\u7F6E" : "\u672A\u914D\u7F6E"}`);
|
|
2044
|
-
console.log(` DeepSeek Key: ${config3.deepseekApiKey ? "\u5DF2\u914D\u7F6E" : "\u672A\u914D\u7F6E"}`);
|
|
2253
|
+
console.log(` \u6A21\u578B: ${cfg.model}`);
|
|
2254
|
+
console.log(` \u8BED\u8A00: ${cfg.locale}`);
|
|
2255
|
+
console.log(` \u540E\u7AEF: ${cfg.apiBaseUrl}`);
|
|
2256
|
+
console.log(` \u767B\u5F55: ${cfg.userEmail || "\u672A\u767B\u5F55"}`);
|
|
2045
2257
|
break;
|
|
2046
2258
|
}
|
|
2047
|
-
case "/login":
|
|
2048
|
-
printInfo("\u8BF7\u4F7F\u7528 sxai login \u547D\u4EE4\u767B\u5F55\u3002");
|
|
2049
|
-
break;
|
|
2050
2259
|
case "/logout":
|
|
2051
2260
|
setConfig("authToken", void 0);
|
|
2052
2261
|
setConfig("userEmail", void 0);
|
|
2053
|
-
printSuccess(
|
|
2054
|
-
|
|
2262
|
+
printSuccess("\u5DF2\u9000\u51FA\u767B\u5F55\u3002");
|
|
2263
|
+
return "exit";
|
|
2055
2264
|
default:
|
|
2056
|
-
printError(
|
|
2265
|
+
printError(`\u672A\u77E5\u547D\u4EE4: ${cmd}\u3002\u8F93\u5165 /help \u67E5\u770B\u5E2E\u52A9\u3002`);
|
|
2057
2266
|
}
|
|
2058
2267
|
}
|
|
2268
|
+
function fmtTokens(n) {
|
|
2269
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
2270
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
|
|
2271
|
+
return String(n);
|
|
2272
|
+
}
|
|
2059
2273
|
|
|
2060
2274
|
// src/commands/config.ts
|
|
2061
|
-
import
|
|
2275
|
+
import inquirer2 from "inquirer";
|
|
2062
2276
|
async function configCommand() {
|
|
2063
2277
|
const config3 = getConfig();
|
|
2064
2278
|
console.log(`
|
|
2065
|
-
\u914D\u7F6E\u6587\u4EF6
|
|
2279
|
+
\u914D\u7F6E\u6587\u4EF6: ${theme.dim(getConfigPath())}
|
|
2066
2280
|
`);
|
|
2067
|
-
const { action } = await
|
|
2281
|
+
const { action } = await inquirer2.prompt([{
|
|
2068
2282
|
type: "list",
|
|
2069
2283
|
name: "action",
|
|
2070
2284
|
message: "\u9009\u62E9\u8981\u914D\u7F6E\u7684\u9879\u76EE:",
|
|
2071
2285
|
choices: [
|
|
2072
|
-
{ name: "\u8BBE\u7F6EAI\u6A21\u578B", value: "model" },
|
|
2073
|
-
|
|
2074
|
-
{ name: "\
|
|
2075
|
-
{ name: "\
|
|
2076
|
-
{ name: "\
|
|
2077
|
-
{ name: "\
|
|
2078
|
-
{ name: "\
|
|
2079
|
-
{ name: "\
|
|
2286
|
+
{ name: "\u{1F916} \u8BBE\u7F6EAI\u6A21\u578B", value: "model" },
|
|
2287
|
+
new inquirer2.Separator("\u2500\u2500 API Key \u8BBE\u7F6E \u2500\u2500"),
|
|
2288
|
+
{ name: "\u{1F319} Kimi \u6708\u4E4B\u6697\u9762", value: "key_kimi" },
|
|
2289
|
+
{ name: "\u{1F9E0} OpenAI", value: "key_openai" },
|
|
2290
|
+
{ name: "\u{1F3AD} Anthropic Claude", value: "key_anthropic" },
|
|
2291
|
+
{ name: "\u{1F50D} DeepSeek \u6DF1\u5EA6\u6C42\u7D22", value: "key_deepseek" },
|
|
2292
|
+
{ name: "\u2601\uFE0F \u901A\u4E49\u5343\u95EE Qwen", value: "key_qwen" },
|
|
2293
|
+
{ name: "\u{1F52E} \u667A\u8C31 GLM", value: "key_zhipu" },
|
|
2294
|
+
{ name: "\u{1FAD8} \u8C46\u5305 Doubao", value: "key_doubao" },
|
|
2295
|
+
{ name: "\u{1F48E} Google Gemini", value: "key_gemini" },
|
|
2296
|
+
{ name: "\u{1F32C}\uFE0F Mistral", value: "key_mistral" },
|
|
2297
|
+
{ name: "\u26A1 Groq", value: "key_groq" },
|
|
2298
|
+
{ name: "\u{1F3D4}\uFE0F \u767E\u5DDD Baichuan", value: "key_baichuan" },
|
|
2299
|
+
{ name: "\u{1F31F} \u96F6\u4E00\u4E07\u7269 Yi", value: "key_lingyiwanwu" },
|
|
2300
|
+
new inquirer2.Separator("\u2500\u2500 \u5176\u4ED6\u8BBE\u7F6E \u2500\u2500"),
|
|
2301
|
+
{ name: "\u{1F310} \u8BBE\u7F6E\u540E\u7AEF\u670D\u52A1\u5730\u5740", value: "apiBaseUrl" },
|
|
2302
|
+
{ name: "\u{1F30D} \u8BBE\u7F6E\u754C\u9762\u8BED\u8A00", value: "locale" },
|
|
2303
|
+
{ name: "\u{1F4CB} \u67E5\u770B\u5F53\u524D\u914D\u7F6E", value: "show" },
|
|
2080
2304
|
{ name: "\u9000\u51FA", value: "exit" }
|
|
2081
2305
|
]
|
|
2082
2306
|
}]);
|
|
2307
|
+
if (action.startsWith("key_")) {
|
|
2308
|
+
const provider = action.replace("key_", "");
|
|
2309
|
+
const displayName = PROVIDER_NAMES[provider] || provider;
|
|
2310
|
+
const configKey = PROVIDER_KEY_MAP[provider];
|
|
2311
|
+
const currentKey = configKey ? config3[configKey] : void 0;
|
|
2312
|
+
if (currentKey) {
|
|
2313
|
+
printInfo(`\u5F53\u524D\u5DF2\u914D\u7F6E ${displayName} Key`);
|
|
2314
|
+
}
|
|
2315
|
+
const { key } = await inquirer2.prompt([{
|
|
2316
|
+
type: "password",
|
|
2317
|
+
name: "key",
|
|
2318
|
+
message: `\u8F93\u5165 ${displayName} API Key:`,
|
|
2319
|
+
mask: "*"
|
|
2320
|
+
}]);
|
|
2321
|
+
if (key && configKey) {
|
|
2322
|
+
setConfig(configKey, key);
|
|
2323
|
+
printSuccess(`${displayName} API Key \u5DF2\u4FDD\u5B58\u3002\u4F7F\u7528\u8BE5\u6A21\u578B\u4E0D\u6D88\u8017\u5E73\u53F0\u989D\u5EA6\u3002`);
|
|
2324
|
+
}
|
|
2325
|
+
return;
|
|
2326
|
+
}
|
|
2083
2327
|
switch (action) {
|
|
2084
2328
|
case "model": {
|
|
2085
2329
|
const models = listModels();
|
|
2086
|
-
const
|
|
2330
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2331
|
+
for (const m of models) {
|
|
2332
|
+
if (!groups.has(m.group)) groups.set(m.group, []);
|
|
2333
|
+
groups.get(m.group).push(m);
|
|
2334
|
+
}
|
|
2335
|
+
const choices = [];
|
|
2336
|
+
for (const [group, groupModels] of groups) {
|
|
2337
|
+
choices.push(new inquirer2.Separator(`\u2500\u2500 ${group} \u2500\u2500`));
|
|
2338
|
+
for (const m of groupModels) {
|
|
2339
|
+
choices.push({
|
|
2340
|
+
name: `${m.name} (${m.id})`,
|
|
2341
|
+
value: m.id
|
|
2342
|
+
});
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
const { model } = await inquirer2.prompt([{
|
|
2087
2346
|
type: "list",
|
|
2088
2347
|
name: "model",
|
|
2089
2348
|
message: "\u9009\u62E9AI\u6A21\u578B:",
|
|
2090
|
-
choices
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
})),
|
|
2094
|
-
default: config3.model
|
|
2349
|
+
choices,
|
|
2350
|
+
default: config3.model,
|
|
2351
|
+
pageSize: 20
|
|
2095
2352
|
}]);
|
|
2096
2353
|
setConfig("model", model);
|
|
2097
2354
|
printSuccess(`\u6A21\u578B\u5DF2\u8BBE\u7F6E\u4E3A: ${model}`);
|
|
2098
2355
|
break;
|
|
2099
2356
|
}
|
|
2100
|
-
case "kimi": {
|
|
2101
|
-
const { key } = await inquirer.prompt([{
|
|
2102
|
-
type: "password",
|
|
2103
|
-
name: "key",
|
|
2104
|
-
message: "\u8F93\u5165Kimi API Key (\u6708\u4E4B\u6697\u9762):",
|
|
2105
|
-
mask: "*"
|
|
2106
|
-
}]);
|
|
2107
|
-
if (key) {
|
|
2108
|
-
setConfig("kimiApiKey", key);
|
|
2109
|
-
printSuccess("Kimi API Key \u5DF2\u4FDD\u5B58\u3002");
|
|
2110
|
-
}
|
|
2111
|
-
break;
|
|
2112
|
-
}
|
|
2113
|
-
case "openai": {
|
|
2114
|
-
const { key } = await inquirer.prompt([{
|
|
2115
|
-
type: "password",
|
|
2116
|
-
name: "key",
|
|
2117
|
-
message: "\u8F93\u5165OpenAI API Key:",
|
|
2118
|
-
mask: "*"
|
|
2119
|
-
}]);
|
|
2120
|
-
if (key) {
|
|
2121
|
-
setConfig("openaiApiKey", key);
|
|
2122
|
-
printSuccess("OpenAI API Key \u5DF2\u4FDD\u5B58\u3002");
|
|
2123
|
-
}
|
|
2124
|
-
break;
|
|
2125
|
-
}
|
|
2126
|
-
case "anthropic": {
|
|
2127
|
-
const { key } = await inquirer.prompt([{
|
|
2128
|
-
type: "password",
|
|
2129
|
-
name: "key",
|
|
2130
|
-
message: "\u8F93\u5165Anthropic API Key:",
|
|
2131
|
-
mask: "*"
|
|
2132
|
-
}]);
|
|
2133
|
-
if (key) {
|
|
2134
|
-
setConfig("anthropicApiKey", key);
|
|
2135
|
-
printSuccess("Anthropic API Key \u5DF2\u4FDD\u5B58\u3002");
|
|
2136
|
-
}
|
|
2137
|
-
break;
|
|
2138
|
-
}
|
|
2139
|
-
case "deepseek": {
|
|
2140
|
-
const { key } = await inquirer.prompt([{
|
|
2141
|
-
type: "password",
|
|
2142
|
-
name: "key",
|
|
2143
|
-
message: "\u8F93\u5165DeepSeek API Key:",
|
|
2144
|
-
mask: "*"
|
|
2145
|
-
}]);
|
|
2146
|
-
if (key) {
|
|
2147
|
-
setConfig("deepseekApiKey", key);
|
|
2148
|
-
printSuccess("DeepSeek API Key \u5DF2\u4FDD\u5B58\u3002");
|
|
2149
|
-
}
|
|
2150
|
-
break;
|
|
2151
|
-
}
|
|
2152
2357
|
case "apiBaseUrl": {
|
|
2153
|
-
const { url } = await
|
|
2358
|
+
const { url } = await inquirer2.prompt([{
|
|
2154
2359
|
type: "input",
|
|
2155
2360
|
name: "url",
|
|
2156
2361
|
message: "\u8F93\u5165\u540E\u7AEF\u670D\u52A1\u5730\u5740:",
|
|
@@ -2161,7 +2366,7 @@ async function configCommand() {
|
|
|
2161
2366
|
break;
|
|
2162
2367
|
}
|
|
2163
2368
|
case "locale": {
|
|
2164
|
-
const { locale } = await
|
|
2369
|
+
const { locale } = await inquirer2.prompt([{
|
|
2165
2370
|
type: "list",
|
|
2166
2371
|
name: "locale",
|
|
2167
2372
|
message: "\u9009\u62E9\u754C\u9762\u8BED\u8A00:",
|
|
@@ -2177,14 +2382,18 @@ async function configCommand() {
|
|
|
2177
2382
|
}
|
|
2178
2383
|
case "show": {
|
|
2179
2384
|
console.log("\n\u5F53\u524D\u914D\u7F6E:");
|
|
2180
|
-
console.log(` \u6A21\u578B: ${config3.model}`);
|
|
2385
|
+
console.log(` \u6A21\u578B: ${theme.brandBold(config3.model)}`);
|
|
2181
2386
|
console.log(` \u8BED\u8A00: ${config3.locale}`);
|
|
2182
2387
|
console.log(` \u540E\u7AEF: ${config3.apiBaseUrl}`);
|
|
2183
|
-
console.log(` \u767B\u5F55: ${config3.userEmail || "\u672A\u767B\u5F55"}`);
|
|
2184
|
-
console.log(
|
|
2185
|
-
console.log(
|
|
2186
|
-
|
|
2187
|
-
|
|
2388
|
+
console.log(` \u767B\u5F55: ${config3.userEmail || theme.dim("\u672A\u767B\u5F55")}`);
|
|
2389
|
+
console.log();
|
|
2390
|
+
console.log(" API Key \u914D\u7F6E:");
|
|
2391
|
+
for (const [provider, keyName] of Object.entries(PROVIDER_KEY_MAP)) {
|
|
2392
|
+
const displayName = (PROVIDER_NAMES[provider] || provider).padEnd(20);
|
|
2393
|
+
const hasKey = !!config3[keyName];
|
|
2394
|
+
const status = hasKey ? theme.success("\u2713 \u5DF2\u914D\u7F6E") : theme.dim("\u672A\u914D\u7F6E");
|
|
2395
|
+
console.log(` ${displayName} ${status}`);
|
|
2396
|
+
}
|
|
2188
2397
|
break;
|
|
2189
2398
|
}
|
|
2190
2399
|
case "exit":
|
|
@@ -2193,12 +2402,12 @@ async function configCommand() {
|
|
|
2193
2402
|
}
|
|
2194
2403
|
|
|
2195
2404
|
// src/commands/login.ts
|
|
2196
|
-
import
|
|
2405
|
+
import inquirer3 from "inquirer";
|
|
2197
2406
|
async function loginCommand() {
|
|
2198
2407
|
const config3 = getConfig();
|
|
2199
2408
|
if (config3.authToken && config3.userEmail) {
|
|
2200
2409
|
printInfo(`\u5F53\u524D\u5DF2\u767B\u5F55: ${config3.userEmail}`);
|
|
2201
|
-
const { relogin } = await
|
|
2410
|
+
const { relogin } = await inquirer3.prompt([{
|
|
2202
2411
|
type: "confirm",
|
|
2203
2412
|
name: "relogin",
|
|
2204
2413
|
message: "\u662F\u5426\u91CD\u65B0\u767B\u5F55\uFF1F",
|
|
@@ -2207,7 +2416,7 @@ async function loginCommand() {
|
|
|
2207
2416
|
if (!relogin) return;
|
|
2208
2417
|
}
|
|
2209
2418
|
console.log(theme.brandBold("\n\u{1F511} \u767B\u5F55\u6C88\u7FD4\u7684AI\u52A9\u624B\n"));
|
|
2210
|
-
const { email } = await
|
|
2419
|
+
const { email } = await inquirer3.prompt([{
|
|
2211
2420
|
type: "input",
|
|
2212
2421
|
name: "email",
|
|
2213
2422
|
message: "\u90AE\u7BB1:",
|
|
@@ -2216,7 +2425,7 @@ async function loginCommand() {
|
|
|
2216
2425
|
return true;
|
|
2217
2426
|
}
|
|
2218
2427
|
}]);
|
|
2219
|
-
const { password } = await
|
|
2428
|
+
const { password } = await inquirer3.prompt([{
|
|
2220
2429
|
type: "password",
|
|
2221
2430
|
name: "password",
|
|
2222
2431
|
message: "\u5BC6\u7801:",
|
|
@@ -2242,7 +2451,7 @@ async function loginCommand() {
|
|
|
2242
2451
|
console.log();
|
|
2243
2452
|
} else if (response.status === 404) {
|
|
2244
2453
|
printInfo("\u8BE5\u90AE\u7BB1\u5C1A\u672A\u6CE8\u518C");
|
|
2245
|
-
const { register } = await
|
|
2454
|
+
const { register } = await inquirer3.prompt([{
|
|
2246
2455
|
type: "confirm",
|
|
2247
2456
|
name: "register",
|
|
2248
2457
|
message: "\u662F\u5426\u7ACB\u5373\u6CE8\u518C\uFF1F",
|
|
@@ -2276,12 +2485,12 @@ async function loginCommand() {
|
|
|
2276
2485
|
}
|
|
2277
2486
|
|
|
2278
2487
|
// src/commands/register.ts
|
|
2279
|
-
import
|
|
2488
|
+
import inquirer4 from "inquirer";
|
|
2280
2489
|
async function registerCommand() {
|
|
2281
2490
|
const config3 = getConfig();
|
|
2282
2491
|
if (config3.authToken && config3.userEmail) {
|
|
2283
2492
|
printInfo(`\u5F53\u524D\u5DF2\u767B\u5F55: ${config3.userEmail}`);
|
|
2284
|
-
const { proceed } = await
|
|
2493
|
+
const { proceed } = await inquirer4.prompt([{
|
|
2285
2494
|
type: "confirm",
|
|
2286
2495
|
name: "proceed",
|
|
2287
2496
|
message: "\u662F\u5426\u6CE8\u518C\u65B0\u8D26\u6237\uFF1F\uFF08\u5C06\u8986\u76D6\u5F53\u524D\u767B\u5F55\u72B6\u6001\uFF09",
|
|
@@ -2290,7 +2499,7 @@ async function registerCommand() {
|
|
|
2290
2499
|
if (!proceed) return;
|
|
2291
2500
|
}
|
|
2292
2501
|
console.log(theme.brandBold("\n\u{1F4DD} \u6CE8\u518C\u6C88\u7FD4\u7684AI\u52A9\u624B\u8D26\u6237\n"));
|
|
2293
|
-
const { email } = await
|
|
2502
|
+
const { email } = await inquirer4.prompt([{
|
|
2294
2503
|
type: "input",
|
|
2295
2504
|
name: "email",
|
|
2296
2505
|
message: "\u90AE\u7BB1:",
|
|
@@ -2299,13 +2508,13 @@ async function registerCommand() {
|
|
|
2299
2508
|
return true;
|
|
2300
2509
|
}
|
|
2301
2510
|
}]);
|
|
2302
|
-
const { name } = await
|
|
2511
|
+
const { name } = await inquirer4.prompt([{
|
|
2303
2512
|
type: "input",
|
|
2304
2513
|
name: "name",
|
|
2305
2514
|
message: "\u6635\u79F0 (\u53EF\u9009):",
|
|
2306
2515
|
default: email.split("@")[0]
|
|
2307
2516
|
}]);
|
|
2308
|
-
const { password } = await
|
|
2517
|
+
const { password } = await inquirer4.prompt([{
|
|
2309
2518
|
type: "password",
|
|
2310
2519
|
name: "password",
|
|
2311
2520
|
message: "\u5BC6\u7801 (\u81F3\u5C116\u4F4D):",
|
|
@@ -2315,7 +2524,7 @@ async function registerCommand() {
|
|
|
2315
2524
|
return true;
|
|
2316
2525
|
}
|
|
2317
2526
|
}]);
|
|
2318
|
-
const { confirmPassword } = await
|
|
2527
|
+
const { confirmPassword } = await inquirer4.prompt([{
|
|
2319
2528
|
type: "password",
|
|
2320
2529
|
name: "confirmPassword",
|
|
2321
2530
|
message: "\u786E\u8BA4\u5BC6\u7801:",
|
|
@@ -2399,9 +2608,9 @@ async function accountCommand() {
|
|
|
2399
2608
|
const dailyBar = progressBar(quota.daily.used, quota.daily.limit);
|
|
2400
2609
|
console.log(` \u4ECA\u65E5\u8BF7\u6C42 ${dailyBar} ${quota.daily.used}/${quota.daily.limit}`);
|
|
2401
2610
|
const monthBar = progressBar(quota.monthlyTokens.used, quota.monthlyTokens.limit);
|
|
2402
|
-
console.log(` \u672C\u6708Token ${monthBar} ${
|
|
2611
|
+
console.log(` \u672C\u6708Token ${monthBar} ${fmtTokens2(quota.monthlyTokens.used)}/${fmtTokens2(quota.monthlyTokens.limit)}`);
|
|
2403
2612
|
const lifeBar = progressBar(quota.lifetimeTokens.used, quota.lifetimeTokens.limit);
|
|
2404
|
-
console.log(` \u514D\u8D39\u989D\u5EA6 ${lifeBar} ${
|
|
2613
|
+
console.log(` \u514D\u8D39\u989D\u5EA6 ${lifeBar} ${fmtTokens2(quota.lifetimeTokens.used)}/${fmtTokens2(quota.lifetimeTokens.limit)}`);
|
|
2405
2614
|
if (!quota.allowed) {
|
|
2406
2615
|
console.log();
|
|
2407
2616
|
printWarning(quota.reason || "\u989D\u5EA6\u5DF2\u7528\u5B8C");
|
|
@@ -2411,9 +2620,9 @@ async function accountCommand() {
|
|
|
2411
2620
|
console.log();
|
|
2412
2621
|
console.log(theme.brandBold(" \u{1F4C8} \u7528\u91CF\u7EDF\u8BA1"));
|
|
2413
2622
|
console.log(theme.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
2414
|
-
console.log(` \u4ECA\u65E5 ${usage.today.requests}\u6B21\u8BF7\u6C42 \xB7 ${
|
|
2415
|
-
console.log(` \u672C\u6708 ${usage.thisMonth.requests}\u6B21\u8BF7\u6C42 \xB7 ${
|
|
2416
|
-
console.log(` \u7D2F\u8BA1 ${usage.lifetime.requests}\u6B21\u8BF7\u6C42 \xB7 ${
|
|
2623
|
+
console.log(` \u4ECA\u65E5 ${usage.today.requests}\u6B21\u8BF7\u6C42 \xB7 ${fmtTokens2(usage.today.tokens)} tokens`);
|
|
2624
|
+
console.log(` \u672C\u6708 ${usage.thisMonth.requests}\u6B21\u8BF7\u6C42 \xB7 ${fmtTokens2(usage.thisMonth.tokens)} tokens`);
|
|
2625
|
+
console.log(` \u7D2F\u8BA1 ${usage.lifetime.requests}\u6B21\u8BF7\u6C42 \xB7 ${fmtTokens2(usage.lifetime.tokens)} tokens`);
|
|
2417
2626
|
}
|
|
2418
2627
|
console.log();
|
|
2419
2628
|
} catch (err) {
|
|
@@ -2433,7 +2642,7 @@ function planLabel(plan) {
|
|
|
2433
2642
|
return plan;
|
|
2434
2643
|
}
|
|
2435
2644
|
}
|
|
2436
|
-
function
|
|
2645
|
+
function fmtTokens2(n) {
|
|
2437
2646
|
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
2438
2647
|
if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
|
|
2439
2648
|
return String(n);
|
|
@@ -2448,7 +2657,7 @@ function progressBar(used, limit, width = 16) {
|
|
|
2448
2657
|
}
|
|
2449
2658
|
|
|
2450
2659
|
// src/commands/admin.ts
|
|
2451
|
-
import
|
|
2660
|
+
import inquirer5 from "inquirer";
|
|
2452
2661
|
async function adminCommand() {
|
|
2453
2662
|
const config3 = getConfig();
|
|
2454
2663
|
if (!config3.authToken) {
|
|
@@ -2456,7 +2665,7 @@ async function adminCommand() {
|
|
|
2456
2665
|
return;
|
|
2457
2666
|
}
|
|
2458
2667
|
console.log(theme.brandBold("\n\u2699\uFE0F \u7BA1\u7406\u5458\u63A7\u5236\u53F0\n"));
|
|
2459
|
-
const { action } = await
|
|
2668
|
+
const { action } = await inquirer5.prompt([{
|
|
2460
2669
|
type: "list",
|
|
2461
2670
|
name: "action",
|
|
2462
2671
|
message: "\u9009\u62E9\u64CD\u4F5C:",
|
|
@@ -2532,7 +2741,7 @@ async function listUsers(config3) {
|
|
|
2532
2741
|
const roleTag = u.role === "admin" ? theme.error("\u7BA1\u7406\u5458") : "\u7528\u6237 ";
|
|
2533
2742
|
const planTag = u.plan === "free" ? theme.dim("\u514D\u8D39 ") : u.plan === "pro" ? theme.success("\u4E13\u4E1A ") : theme.info("\u4F01\u4E1A ");
|
|
2534
2743
|
console.log(
|
|
2535
|
-
` ${(u.email + status).padEnd(28)} ${planTag} ${roleTag} ${String(u.dailyLimit).padEnd(6)} ${
|
|
2744
|
+
` ${(u.email + status).padEnd(28)} ${planTag} ${roleTag} ${String(u.dailyLimit).padEnd(6)} ${fmtTokens3(u.totalFreeTokens).padEnd(12)} ${fmtTokens3(u.totalTokensUsed)}`
|
|
2536
2745
|
);
|
|
2537
2746
|
}
|
|
2538
2747
|
console.log();
|
|
@@ -2543,7 +2752,7 @@ async function listUsers(config3) {
|
|
|
2543
2752
|
async function setUserQuota(config3) {
|
|
2544
2753
|
const users = await fetchUserList(config3);
|
|
2545
2754
|
if (!users) return;
|
|
2546
|
-
const { userId } = await
|
|
2755
|
+
const { userId } = await inquirer5.prompt([{
|
|
2547
2756
|
type: "list",
|
|
2548
2757
|
name: "userId",
|
|
2549
2758
|
message: "\u9009\u62E9\u7528\u6237:",
|
|
@@ -2551,21 +2760,21 @@ async function setUserQuota(config3) {
|
|
|
2551
2760
|
}]);
|
|
2552
2761
|
const selectedUser = users.find((u) => u.id === userId);
|
|
2553
2762
|
console.log(theme.dim(`
|
|
2554
|
-
\u5F53\u524D\u989D\u5EA6: \u65E5\u9650=${selectedUser.dailyLimit}\u6B21 \u6708Token=${
|
|
2763
|
+
\u5F53\u524D\u989D\u5EA6: \u65E5\u9650=${selectedUser.dailyLimit}\u6B21 \u6708Token=${fmtTokens3(selectedUser.monthlyTokenLimit)} \u514D\u8D39\u603B\u989D=${fmtTokens3(selectedUser.totalFreeTokens)}
|
|
2555
2764
|
`));
|
|
2556
|
-
const { dailyLimit } = await
|
|
2765
|
+
const { dailyLimit } = await inquirer5.prompt([{
|
|
2557
2766
|
type: "number",
|
|
2558
2767
|
name: "dailyLimit",
|
|
2559
2768
|
message: "\u6BCF\u65E5\u8BF7\u6C42\u4E0A\u9650:",
|
|
2560
2769
|
default: selectedUser.dailyLimit
|
|
2561
2770
|
}]);
|
|
2562
|
-
const { monthlyTokenLimit } = await
|
|
2771
|
+
const { monthlyTokenLimit } = await inquirer5.prompt([{
|
|
2563
2772
|
type: "number",
|
|
2564
2773
|
name: "monthlyTokenLimit",
|
|
2565
2774
|
message: "\u6BCF\u6708Token\u4E0A\u9650:",
|
|
2566
2775
|
default: selectedUser.monthlyTokenLimit
|
|
2567
2776
|
}]);
|
|
2568
|
-
const { totalFreeTokens } = await
|
|
2777
|
+
const { totalFreeTokens } = await inquirer5.prompt([{
|
|
2569
2778
|
type: "number",
|
|
2570
2779
|
name: "totalFreeTokens",
|
|
2571
2780
|
message: "\u514D\u8D39Token\u603B\u989D:",
|
|
@@ -2591,13 +2800,13 @@ async function setUserQuota(config3) {
|
|
|
2591
2800
|
async function setUserPlan(config3) {
|
|
2592
2801
|
const users = await fetchUserList(config3);
|
|
2593
2802
|
if (!users) return;
|
|
2594
|
-
const { userId } = await
|
|
2803
|
+
const { userId } = await inquirer5.prompt([{
|
|
2595
2804
|
type: "list",
|
|
2596
2805
|
name: "userId",
|
|
2597
2806
|
message: "\u9009\u62E9\u7528\u6237:",
|
|
2598
2807
|
choices: users.map((u) => ({ name: `${u.email} (\u5F53\u524D: ${u.plan})`, value: u.id }))
|
|
2599
2808
|
}]);
|
|
2600
|
-
const { plan } = await
|
|
2809
|
+
const { plan } = await inquirer5.prompt([{
|
|
2601
2810
|
type: "list",
|
|
2602
2811
|
name: "plan",
|
|
2603
2812
|
message: "\u9009\u62E9\u8BA1\u5212:",
|
|
@@ -2627,7 +2836,7 @@ async function setUserPlan(config3) {
|
|
|
2627
2836
|
async function setUserRole(config3) {
|
|
2628
2837
|
const users = await fetchUserList(config3);
|
|
2629
2838
|
if (!users) return;
|
|
2630
|
-
const { userId } = await
|
|
2839
|
+
const { userId } = await inquirer5.prompt([{
|
|
2631
2840
|
type: "list",
|
|
2632
2841
|
name: "userId",
|
|
2633
2842
|
message: "\u9009\u62E9\u7528\u6237:",
|
|
@@ -2636,7 +2845,7 @@ async function setUserRole(config3) {
|
|
|
2636
2845
|
value: u.id
|
|
2637
2846
|
}))
|
|
2638
2847
|
}]);
|
|
2639
|
-
const { role } = await
|
|
2848
|
+
const { role } = await inquirer5.prompt([{
|
|
2640
2849
|
type: "list",
|
|
2641
2850
|
name: "role",
|
|
2642
2851
|
message: "\u8BBE\u7F6E\u89D2\u8272:",
|
|
@@ -2665,16 +2874,16 @@ async function setUserRole(config3) {
|
|
|
2665
2874
|
async function resetUsage(config3) {
|
|
2666
2875
|
const users = await fetchUserList(config3);
|
|
2667
2876
|
if (!users) return;
|
|
2668
|
-
const { userId } = await
|
|
2877
|
+
const { userId } = await inquirer5.prompt([{
|
|
2669
2878
|
type: "list",
|
|
2670
2879
|
name: "userId",
|
|
2671
2880
|
message: "\u9009\u62E9\u7528\u6237:",
|
|
2672
2881
|
choices: users.map((u) => ({
|
|
2673
|
-
name: `${u.email} (\u5DF2\u7528 ${
|
|
2882
|
+
name: `${u.email} (\u5DF2\u7528 ${fmtTokens3(u.totalTokensUsed)} tokens)`,
|
|
2674
2883
|
value: u.id
|
|
2675
2884
|
}))
|
|
2676
2885
|
}]);
|
|
2677
|
-
const { confirm } = await
|
|
2886
|
+
const { confirm } = await inquirer5.prompt([{
|
|
2678
2887
|
type: "confirm",
|
|
2679
2888
|
name: "confirm",
|
|
2680
2889
|
message: "\u786E\u5B9A\u8981\u91CD\u7F6E\u8BE5\u7528\u6237\u7684\u7D2F\u8BA1\u7528\u91CF\u5417\uFF1F",
|
|
@@ -2699,7 +2908,7 @@ async function resetUsage(config3) {
|
|
|
2699
2908
|
async function toggleUserStatus(config3) {
|
|
2700
2909
|
const users = await fetchUserList(config3);
|
|
2701
2910
|
if (!users) return;
|
|
2702
|
-
const { userId } = await
|
|
2911
|
+
const { userId } = await inquirer5.prompt([{
|
|
2703
2912
|
type: "list",
|
|
2704
2913
|
name: "userId",
|
|
2705
2914
|
message: "\u9009\u62E9\u7528\u6237:",
|
|
@@ -2737,7 +2946,7 @@ async function showStats(config3) {
|
|
|
2737
2946
|
console.log(` \u7528\u6237\u603B\u6570 ${data.users.total}`);
|
|
2738
2947
|
console.log(` \u6D3B\u8DC3\u7528\u6237 ${data.users.active}`);
|
|
2739
2948
|
console.log(` \u603B\u8BF7\u6C42\u6570 ${data.usage.totalRequests}`);
|
|
2740
|
-
console.log(` \u603BToken\u7528\u91CF ${
|
|
2949
|
+
console.log(` \u603BToken\u7528\u91CF ${fmtTokens3(Number(data.usage.totalTokens))}`);
|
|
2741
2950
|
console.log();
|
|
2742
2951
|
} catch {
|
|
2743
2952
|
printError("\u83B7\u53D6\u7EDF\u8BA1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5\u3002");
|
|
@@ -2758,7 +2967,7 @@ async function fetchUserList(config3) {
|
|
|
2758
2967
|
return null;
|
|
2759
2968
|
}
|
|
2760
2969
|
}
|
|
2761
|
-
function
|
|
2970
|
+
function fmtTokens3(n) {
|
|
2762
2971
|
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
2763
2972
|
if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
|
|
2764
2973
|
return String(n);
|
|
@@ -2795,19 +3004,20 @@ program.command("admin").description("\u7BA1\u7406\u5458\u63A7\u5236\u53F0\uFF08
|
|
|
2795
3004
|
program.command("config").description("\u914D\u7F6E\u6C88\u7FD4\u7684AI\u52A9\u624B").action(async () => {
|
|
2796
3005
|
await configCommand();
|
|
2797
3006
|
});
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
anthropic: "anthropicApiKey",
|
|
2802
|
-
deepseek: "deepseekApiKey",
|
|
2803
|
-
kimi: "kimiApiKey"
|
|
2804
|
-
};
|
|
2805
|
-
const configKey = keyMap[provider];
|
|
3007
|
+
var providerList = Object.keys(PROVIDER_KEY_MAP).join(", ");
|
|
3008
|
+
program.command("set-key <provider> <key>").description(`\u8BBE\u7F6EAPI\u5BC6\u94A5 (${providerList})`).action((provider, key) => {
|
|
3009
|
+
const configKey = PROVIDER_KEY_MAP[provider];
|
|
2806
3010
|
if (!configKey) {
|
|
2807
|
-
|
|
3011
|
+
printError(`\u672A\u77E5\u7684\u63D0\u4F9B\u5546: ${provider}`);
|
|
3012
|
+
console.log(`\u652F\u6301\u7684\u63D0\u4F9B\u5546: ${providerList}`);
|
|
2808
3013
|
process.exit(1);
|
|
2809
3014
|
}
|
|
2810
3015
|
setConfig(configKey, key);
|
|
2811
|
-
|
|
3016
|
+
const displayName = PROVIDER_NAMES[provider] || provider;
|
|
3017
|
+
printSuccess(`${displayName} API Key \u5DF2\u4FDD\u5B58\u3002\u914D\u7F6E\u81EA\u5DF1\u7684Key\u540E\uFF0C\u4F7F\u7528\u8BE5\u6A21\u578B\u4E0D\u6D88\u8017\u5E73\u53F0\u989D\u5EA6\u3002`);
|
|
3018
|
+
});
|
|
3019
|
+
program.command("use <model>").description("\u5207\u6362AI\u6A21\u578B (\u5982: sxai use gpt-4o)").action((model) => {
|
|
3020
|
+
setConfig("model", model);
|
|
3021
|
+
printSuccess(`\u6A21\u578B\u5DF2\u5207\u6362\u4E3A: ${model}`);
|
|
2812
3022
|
});
|
|
2813
3023
|
program.parse();
|