xiaozhou-chat 1.0.2 → 1.0.3
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/bin/cli.js +127 -13
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -33,9 +33,9 @@ function initConfigFile() {
|
|
|
33
33
|
if (fs.existsSync(homeConfigFile)) return;
|
|
34
34
|
|
|
35
35
|
const defaultConfig = {
|
|
36
|
-
apiKey: "",
|
|
37
|
-
baseUrl: "https://
|
|
38
|
-
model: "
|
|
36
|
+
apiKey: "sk-KUDRAZivzcKUGWnfxAjskgX15uFmNPcKgPvxSWc5pCRwobnK",
|
|
37
|
+
baseUrl: "https://paid.tribiosapi.top/v1",
|
|
38
|
+
model: "claude-sonnet-4-5-20250929"
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
fs.writeFileSync(homeConfigFile, JSON.stringify(defaultConfig, null, 2), "utf-8");
|
|
@@ -138,6 +138,9 @@ function showHelp() {
|
|
|
138
138
|
console.log(`
|
|
139
139
|
可用命令:
|
|
140
140
|
/help 显示帮助
|
|
141
|
+
/config [set] 查看或修改配置
|
|
142
|
+
/system [prompt] 设置系统提示词
|
|
143
|
+
/load <file> 加载文件内容到上下文
|
|
141
144
|
/history [N] 显示历史(可选最近 N 条)
|
|
142
145
|
/clear 清空历史
|
|
143
146
|
/export 导出历史为 Markdown
|
|
@@ -148,23 +151,49 @@ function showHelp() {
|
|
|
148
151
|
initConfigFile();
|
|
149
152
|
const config = loadConfig();
|
|
150
153
|
|
|
151
|
-
|
|
154
|
+
let API_KEY =
|
|
152
155
|
config.apiKey ||
|
|
153
156
|
args["api-key"] ||
|
|
154
157
|
process.env.NEWAPI_API_KEY;
|
|
155
158
|
|
|
156
|
-
|
|
159
|
+
let BASE_URL =
|
|
157
160
|
config.baseUrl ||
|
|
158
161
|
args["base-url"] ||
|
|
159
162
|
process.env.NEWAPI_BASE_URL ||
|
|
160
163
|
"https://api.newapi.pro/v1";
|
|
161
164
|
|
|
162
|
-
|
|
165
|
+
let MODEL =
|
|
163
166
|
config.model ||
|
|
164
167
|
args["model"] ||
|
|
165
168
|
process.env.NEWAPI_MODEL ||
|
|
166
169
|
"gpt-3.5-turbo";
|
|
167
170
|
|
|
171
|
+
let SYSTEM_PROMPT =
|
|
172
|
+
config.systemPrompt ||
|
|
173
|
+
args["system-prompt"] ||
|
|
174
|
+
process.env.NEWAPI_SYSTEM_PROMPT ||
|
|
175
|
+
"";
|
|
176
|
+
|
|
177
|
+
function getWriteConfigFile() {
|
|
178
|
+
if (fs.existsSync(projectConfigFile)) return projectConfigFile;
|
|
179
|
+
if (fs.existsSync(projectAltConfigFile)) return projectAltConfigFile;
|
|
180
|
+
return homeConfigFile;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function updateConfig(key, value) {
|
|
184
|
+
const target = getWriteConfigFile();
|
|
185
|
+
const current = loadConfigFrom(target);
|
|
186
|
+
current[key] = value;
|
|
187
|
+
fs.writeFileSync(target, JSON.stringify(current, null, 2), "utf-8");
|
|
188
|
+
console.log(`✅ 已更新 ${key} 到配置文件: ${target}`);
|
|
189
|
+
|
|
190
|
+
// 更新运行时变量
|
|
191
|
+
if (key === "apiKey") API_KEY = value;
|
|
192
|
+
if (key === "baseUrl") BASE_URL = value;
|
|
193
|
+
if (key === "model") MODEL = value;
|
|
194
|
+
if (key === "systemPrompt") SYSTEM_PROMPT = value;
|
|
195
|
+
}
|
|
196
|
+
|
|
168
197
|
if (!API_KEY) {
|
|
169
198
|
console.error("❌ 请在 ~/.newapi-chat-config.json 或环境变量中配置 NEWAPI_API_KEY");
|
|
170
199
|
process.exit(1);
|
|
@@ -175,7 +204,7 @@ let messages = loadHistory();
|
|
|
175
204
|
const rl = readline.createInterface({
|
|
176
205
|
input: process.stdin,
|
|
177
206
|
output: process.stdout,
|
|
178
|
-
prompt: "
|
|
207
|
+
prompt: "小周> "
|
|
179
208
|
});
|
|
180
209
|
|
|
181
210
|
readline.emitKeypressEvents(process.stdin);
|
|
@@ -199,7 +228,23 @@ console.log("✅ NewAPI Chat CLI 已启动,输入 /help 查看命令");
|
|
|
199
228
|
async function chatStream(userInput) {
|
|
200
229
|
messages.push({ role: "user", content: userInput });
|
|
201
230
|
|
|
202
|
-
|
|
231
|
+
// 自动修正 URL:如果 Base URL 不包含 /v1,尝试追加
|
|
232
|
+
const url = BASE_URL.endsWith("/v1")
|
|
233
|
+
? `${BASE_URL}/chat/completions`
|
|
234
|
+
: `${BASE_URL}/v1/chat/completions`;
|
|
235
|
+
|
|
236
|
+
const apiMessages = [...messages];
|
|
237
|
+
if (SYSTEM_PROMPT) {
|
|
238
|
+
// 检查是否已经有 system prompt,如果没有则添加
|
|
239
|
+
if (!apiMessages.length || apiMessages[0].role !== "system") {
|
|
240
|
+
apiMessages.unshift({ role: "system", content: SYSTEM_PROMPT });
|
|
241
|
+
} else {
|
|
242
|
+
// 临时替换 system prompt 为当前配置的
|
|
243
|
+
apiMessages[0] = { role: "system", content: SYSTEM_PROMPT };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const res = await fetch(url, {
|
|
203
248
|
method: "POST",
|
|
204
249
|
headers: {
|
|
205
250
|
"Content-Type": "application/json",
|
|
@@ -207,7 +252,7 @@ async function chatStream(userInput) {
|
|
|
207
252
|
},
|
|
208
253
|
body: JSON.stringify({
|
|
209
254
|
model: MODEL,
|
|
210
|
-
messages,
|
|
255
|
+
messages: apiMessages,
|
|
211
256
|
stream: true
|
|
212
257
|
})
|
|
213
258
|
});
|
|
@@ -220,22 +265,26 @@ async function chatStream(userInput) {
|
|
|
220
265
|
const reader = res.body.getReader();
|
|
221
266
|
const decoder = new TextDecoder("utf-8");
|
|
222
267
|
let reply = "";
|
|
268
|
+
let buffer = "";
|
|
223
269
|
|
|
224
270
|
while (true) {
|
|
225
271
|
const { done, value } = await reader.read();
|
|
226
272
|
if (done) break;
|
|
227
273
|
|
|
228
|
-
|
|
229
|
-
const lines =
|
|
274
|
+
buffer += decoder.decode(value, { stream: true });
|
|
275
|
+
const lines = buffer.split("\n");
|
|
276
|
+
buffer = lines.pop() || "";
|
|
230
277
|
|
|
231
278
|
for (const line of lines) {
|
|
232
279
|
if (!line.startsWith("data:")) continue;
|
|
233
|
-
const data = line.
|
|
280
|
+
const data = line.slice(5).trim();
|
|
234
281
|
if (data === "[DONE]") break;
|
|
235
282
|
|
|
236
283
|
try {
|
|
237
284
|
const json = JSON.parse(data);
|
|
238
|
-
const token =
|
|
285
|
+
const token =
|
|
286
|
+
json.choices?.[0]?.delta?.content ??
|
|
287
|
+
json.choices?.[0]?.message?.content;
|
|
239
288
|
if (token) {
|
|
240
289
|
process.stdout.write(token);
|
|
241
290
|
reply += token;
|
|
@@ -260,6 +309,70 @@ rl.on("line", async (line) => {
|
|
|
260
309
|
return rl.prompt();
|
|
261
310
|
}
|
|
262
311
|
|
|
312
|
+
if (input.startsWith("/system")) {
|
|
313
|
+
const prompt = input.slice(7).trim();
|
|
314
|
+
if (!prompt) {
|
|
315
|
+
console.log(`当前 System Prompt: ${SYSTEM_PROMPT || "(空)"}`);
|
|
316
|
+
} else {
|
|
317
|
+
updateConfig("systemPrompt", prompt);
|
|
318
|
+
}
|
|
319
|
+
return rl.prompt();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (input.startsWith("/load")) {
|
|
323
|
+
const file = input.slice(5).trim();
|
|
324
|
+
if (!file) {
|
|
325
|
+
console.log("❌ 用法: /load <file_path>");
|
|
326
|
+
return rl.prompt();
|
|
327
|
+
}
|
|
328
|
+
try {
|
|
329
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
330
|
+
const userMsg = `(Context from ${path.basename(file)}):\n\`\`\`\n${content}\n\`\`\``;
|
|
331
|
+
messages.push({ role: "user", content: userMsg });
|
|
332
|
+
saveHistory(messages);
|
|
333
|
+
console.log(`✅ 已加载文件: ${file} (${content.length} chars)`);
|
|
334
|
+
console.log("👉 该文件内容将作为上下文发送给 AI,请继续提问。");
|
|
335
|
+
} catch (e) {
|
|
336
|
+
console.error(`❌ 读取文件失败: ${e.message}`);
|
|
337
|
+
}
|
|
338
|
+
return rl.prompt();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (input.startsWith("/config")) {
|
|
342
|
+
const parts = input.split(/\s+/);
|
|
343
|
+
const cmd = parts[1];
|
|
344
|
+
|
|
345
|
+
if (!cmd || cmd === "list") {
|
|
346
|
+
console.log(`
|
|
347
|
+
当前配置:
|
|
348
|
+
apiKey: ${API_KEY ? API_KEY.slice(0, 8) + "..." : "未设置"}
|
|
349
|
+
baseUrl: ${BASE_URL}
|
|
350
|
+
model: ${MODEL}
|
|
351
|
+
system: ${SYSTEM_PROMPT ? SYSTEM_PROMPT.slice(0, 20) + "..." : "默认"}
|
|
352
|
+
`);
|
|
353
|
+
return rl.prompt();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (cmd === "set") {
|
|
357
|
+
const key = parts[2];
|
|
358
|
+
const value = parts[3];
|
|
359
|
+
if (!key || !value) {
|
|
360
|
+
console.log("❌ 用法: /config set <key> <value>");
|
|
361
|
+
return rl.prompt();
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (["apiKey", "baseUrl", "model", "systemPrompt"].includes(key)) {
|
|
365
|
+
updateConfig(key, value);
|
|
366
|
+
} else {
|
|
367
|
+
console.log("❌ 仅支持修改: apiKey, baseUrl, model, systemPrompt");
|
|
368
|
+
}
|
|
369
|
+
return rl.prompt();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
console.log("❌ 未知命令,用法: /config [list|set]");
|
|
373
|
+
return rl.prompt();
|
|
374
|
+
}
|
|
375
|
+
|
|
263
376
|
if (input.startsWith("/history")) {
|
|
264
377
|
const n = Number(input.split(/\s+/)[1]);
|
|
265
378
|
showHistory(messages, Number.isFinite(n) && n > 0 ? n : undefined);
|
|
@@ -291,3 +404,4 @@ rl.on("line", async (line) => {
|
|
|
291
404
|
|
|
292
405
|
rl.prompt();
|
|
293
406
|
});
|
|
407
|
+
|