xiaozhou-chat 1.0.0

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.
Files changed (3) hide show
  1. package/README.md +28 -0
  2. package/bin/cli.js +149 -0
  3. package/package.json +21 -0
package/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # my-newapi-chat
2
+
3
+ CLI chatbot based on NewAPI.
4
+
5
+ ## 安装
6
+ npm i -g my-newapi-chat
7
+
8
+ ## 配置
9
+ export NEWAPI_API_KEY="你的Key"
10
+
11
+ 可选:
12
+ export NEWAPI_BASE_URL="https://api.newapi.pro/v1"
13
+ export NEWAPI_MODEL="gpt-3.5-turbo"
14
+
15
+ ## 使用
16
+ newapi-chat
17
+
18
+ ## CLI 参数
19
+ newapi-chat --model gpt-4 --base-url https://api.newapi.pro/v1
20
+
21
+ ## 历史记录
22
+ 默认保存在:
23
+ ~/.newapi-chat-history.json
24
+
25
+ ## Windows 打包
26
+ npm i -g pkg
27
+ npm run build:win
28
+ dist/newapi-chat.exe
package/bin/cli.js ADDED
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import os from "node:os";
5
+ import readline from "node:readline";
6
+ import minimist from "minimist";
7
+
8
+ const args = minimist(process.argv.slice(2));
9
+
10
+ const configFile = path.join(os.homedir(), ".newapi-chat-config.json");
11
+ const historyFile = path.join(os.homedir(), ".newapi-chat-history.json");
12
+
13
+ function initConfigFile() {
14
+ if (fs.existsSync(configFile)) return;
15
+
16
+ const defaultConfig = {
17
+ apiKey: "",
18
+ baseUrl: "https://api.newapi.pro/v1",
19
+ model: "gpt-3.5-turbo"
20
+ };
21
+
22
+ fs.writeFileSync(configFile, JSON.stringify(defaultConfig, null, 2), "utf-8");
23
+ console.log(`✅ 已创建配置文件: ${configFile}`);
24
+ console.log("👉 请编辑该文件填入 apiKey");
25
+ }
26
+
27
+ function loadConfig() {
28
+ if (!fs.existsSync(configFile)) return {};
29
+ try {
30
+ return JSON.parse(fs.readFileSync(configFile, "utf-8"));
31
+ } catch {
32
+ return {};
33
+ }
34
+ }
35
+
36
+ function loadHistory() {
37
+ if (!fs.existsSync(historyFile)) return [];
38
+ try {
39
+ return JSON.parse(fs.readFileSync(historyFile, "utf-8"));
40
+ } catch {
41
+ return [];
42
+ }
43
+ }
44
+
45
+ function saveHistory(messages) {
46
+ fs.writeFileSync(historyFile, JSON.stringify(messages, null, 2));
47
+ }
48
+
49
+ initConfigFile();
50
+ const config = loadConfig();
51
+
52
+ const API_KEY = config.apiKey || process.env.NEWAPI_API_KEY;
53
+ const BASE_URL =
54
+ config.baseUrl ||
55
+ args["base-url"] ||
56
+ process.env.NEWAPI_BASE_URL ||
57
+ "https://api.newapi.pro/v1";
58
+ const MODEL =
59
+ config.model ||
60
+ args["model"] ||
61
+ process.env.NEWAPI_MODEL ||
62
+ "gpt-3.5-turbo";
63
+
64
+ if (!API_KEY) {
65
+ console.error("❌ 请在 ~/.newapi-chat-config.json 或环境变量中配置 NEWAPI_API_KEY");
66
+ process.exit(1);
67
+ }
68
+
69
+ let messages = loadHistory();
70
+
71
+ const rl = readline.createInterface({
72
+ input: process.stdin,
73
+ output: process.stdout,
74
+ prompt: "你> "
75
+ });
76
+
77
+ console.log("✅ NewAPI Chat CLI 已启动,输入 exit 退出");
78
+
79
+ async function chatStream(userInput) {
80
+ messages.push({ role: "user", content: userInput });
81
+
82
+ const res = await fetch(`${BASE_URL}/chat/completions`, {
83
+ method: "POST",
84
+ headers: {
85
+ "Content-Type": "application/json",
86
+ Authorization: `Bearer ${API_KEY}`
87
+ },
88
+ body: JSON.stringify({
89
+ model: MODEL,
90
+ messages,
91
+ stream: true
92
+ })
93
+ });
94
+
95
+ if (!res.ok) {
96
+ const text = await res.text();
97
+ throw new Error(`API 错误: ${res.status} ${text}`);
98
+ }
99
+
100
+ const reader = res.body.getReader();
101
+ const decoder = new TextDecoder("utf-8");
102
+ let reply = "";
103
+
104
+ while (true) {
105
+ const { done, value } = await reader.read();
106
+ if (done) break;
107
+
108
+ const chunk = decoder.decode(value);
109
+ const lines = chunk.split("\n").filter(Boolean);
110
+
111
+ for (const line of lines) {
112
+ if (!line.startsWith("data:")) continue;
113
+ const data = line.replace("data: ", "").trim();
114
+ if (data === "[DONE]") break;
115
+
116
+ try {
117
+ const json = JSON.parse(data);
118
+ const token = json.choices?.[0]?.delta?.content;
119
+ if (token) {
120
+ process.stdout.write(token);
121
+ reply += token;
122
+ }
123
+ } catch {}
124
+ }
125
+ }
126
+
127
+ process.stdout.write("\n");
128
+ messages.push({ role: "assistant", content: reply });
129
+ saveHistory(messages);
130
+ }
131
+
132
+ rl.prompt();
133
+
134
+ rl.on("line", async (line) => {
135
+ const input = line.trim();
136
+ if (!input) return rl.prompt();
137
+ if (input === "exit" || input === "quit") {
138
+ rl.close();
139
+ return;
140
+ }
141
+
142
+ try {
143
+ await chatStream(input);
144
+ } catch (e) {
145
+ console.error("❌", e.message);
146
+ }
147
+
148
+ rl.prompt();
149
+ });
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "xiaozhou-chat",
3
+ "version": "1.0.0",
4
+ "description": "CLI chatbot based on NewAPI",
5
+ "bin": {
6
+ "xiaozhou-chat": "bin/cli.js"
7
+ },
8
+ "type": "module",
9
+ "engines": {
10
+ "node": ">=18"
11
+ },
12
+ "scripts": {
13
+ "build:win": "pkg . --targets node18-win-x64 --output dist/newapi-chat.exe"
14
+ },
15
+ "dependencies": {
16
+ "minimist": "^1.2.8"
17
+ },
18
+ "pkg": {
19
+ "assets": []
20
+ }
21
+ }