maxai 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.
package/bin/maxai.js ADDED
@@ -0,0 +1,329 @@
1
+ #!/usr/bin/env node
2
+
3
+ const inquirer = require('inquirer');
4
+ const chalk = require('chalk');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+ const JSON5 = require('json5');
9
+
10
+ const { execSync } = require('child_process');
11
+
12
+ // ============ MAXAPI 配置 ============
13
+ const ENDPOINT = 'http://heibai.natapp1.cc';
14
+ const PROVIDER_NAME = 'maxapi';
15
+ const MODEL_ID = 'heibai';
16
+ const MODEL_NAME = 'MAXAPI (网站切换模型)';
17
+
18
+ // ============ 路径 ============
19
+ function getOpenClawPaths() {
20
+ const home = os.homedir();
21
+ const candidates = ['.openclaw', '.clawdbot', '.moltbot'];
22
+ for (const dir of candidates) {
23
+ const p = path.join(home, dir);
24
+ if (fs.existsSync(p)) {
25
+ return {
26
+ configDir: p,
27
+ configPath: path.join(p, 'openclaw.json'),
28
+ authProfiles: path.join(p, 'agents', 'main', 'agent', 'auth-profiles.json'),
29
+ };
30
+ }
31
+ }
32
+ const defaultDir = path.join(home, '.openclaw');
33
+ return {
34
+ configDir: defaultDir,
35
+ configPath: path.join(defaultDir, 'openclaw.json'),
36
+ authProfiles: path.join(defaultDir, 'agents', 'main', 'agent', 'auth-profiles.json'),
37
+ };
38
+ }
39
+
40
+ // ============ 读写配置 ============
41
+ function readJson(filePath) {
42
+ if (!fs.existsSync(filePath)) return null;
43
+ try {
44
+ const raw = fs.readFileSync(filePath, 'utf8').trim();
45
+ if (!raw || !raw.startsWith('{')) return null;
46
+ return JSON5.parse(raw);
47
+ } catch { return null; }
48
+ }
49
+
50
+ function writeJson(filePath, data) {
51
+ const dir = path.dirname(filePath);
52
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
53
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
54
+ }
55
+
56
+ function ensureStructure(config) {
57
+ const c = config || {};
58
+ if (!c.models) c.models = {};
59
+ if (!c.models.providers) c.models.providers = {};
60
+ if (!c.agents) c.agents = {};
61
+ if (!c.agents.defaults) c.agents.defaults = {};
62
+ if (!c.agents.defaults.model) c.agents.defaults.model = {};
63
+ if (!c.agents.defaults.models || typeof c.agents.defaults.models !== 'object' || Array.isArray(c.agents.defaults.models)) {
64
+ c.agents.defaults.models = {};
65
+ }
66
+ if (!c.auth) c.auth = {};
67
+ if (!c.auth.profiles) c.auth.profiles = {};
68
+ return c;
69
+ }
70
+
71
+ // ============ 写入 OpenClaw 配置 ============
72
+ function writeOpenClawConfig(apiKey) {
73
+ const paths = getOpenClawPaths();
74
+ const config = ensureStructure(readJson(paths.configPath) || {});
75
+ const key = apiKey.trim();
76
+
77
+ if (!config.models.mode) config.models.mode = 'merge';
78
+ // auth 是认证方式类型('api-key'),实际 key 放 apiKey 字段
79
+ config.models.providers[PROVIDER_NAME] = {
80
+ baseUrl: ENDPOINT,
81
+ auth: 'api-key',
82
+ apiKey: key,
83
+ api: 'openai-completions',
84
+ models: [{
85
+ id: MODEL_ID, name: MODEL_NAME,
86
+ reasoning: false, input: ['text'],
87
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
88
+ contextWindow: 204800, maxTokens: 131072,
89
+ }],
90
+ };
91
+
92
+ // auth.profiles 只存 provider 和 mode,不存 apiKey(OpenClaw 不识别)
93
+ const profileKey = `${PROVIDER_NAME}:default`;
94
+ config.auth.profiles[profileKey] = {
95
+ provider: PROVIDER_NAME,
96
+ mode: 'api_key',
97
+ };
98
+
99
+ // 注册到 agents.defaults.models,便于 OpenClaw 识别与切换
100
+ const modelKey = `${PROVIDER_NAME}/${MODEL_ID}`;
101
+ config.agents.defaults.models[modelKey] = { alias: PROVIDER_NAME };
102
+ if (!config.agents.defaults.model.primary) {
103
+ config.agents.defaults.model.primary = modelKey;
104
+ }
105
+
106
+ writeJson(paths.configPath, config);
107
+
108
+ console.log(chalk.green(' ✅ OpenClaw 配置完成'));
109
+ console.log(chalk.gray(` ${paths.configPath}`));
110
+ console.log(chalk.yellow(' 💡 配置后请重启网关: openclaw gateway restart'));
111
+ }
112
+
113
+ // ============ 写入 Claude Code 配置 ============
114
+ function writeClaudeCodeConfig(apiKey) {
115
+ const home = os.homedir();
116
+ // ~/.claude/settings.json
117
+ const claudeDir = path.join(home, '.claude');
118
+ const settingsPath = path.join(claudeDir, 'settings.json');
119
+ let settings = readJson(settingsPath) || {};
120
+ settings.apiBaseUrl = ENDPOINT;
121
+ writeJson(settingsPath, settings);
122
+
123
+ // 环境变量提示
124
+ console.log(chalk.green(' ✅ Claude Code 配置完成'));
125
+ console.log(chalk.gray(` ${settingsPath}`));
126
+ console.log(chalk.yellow(' 💡 还需设置环境变量 ANTHROPIC_AUTH_TOKEN:'));
127
+ if (process.platform === 'win32') {
128
+ console.log(chalk.cyan(` [Environment]::SetEnvironmentVariable('ANTHROPIC_AUTH_TOKEN', '${apiKey}', 'User')`));
129
+ } else {
130
+ console.log(chalk.cyan(` export ANTHROPIC_AUTH_TOKEN="${apiKey}"`));
131
+ console.log(chalk.gray(` 永久生效: echo 'export ANTHROPIC_AUTH_TOKEN="${apiKey}"' >> ~/.zshrc && source ~/.zshrc`));
132
+ }
133
+ }
134
+
135
+ // ============ 写入 Codex 配置 ============
136
+ function writeCodexConfig(apiKey) {
137
+ const home = os.homedir();
138
+ const codexDir = path.join(home, '.codex');
139
+ if (!fs.existsSync(codexDir)) fs.mkdirSync(codexDir, { recursive: true });
140
+
141
+ // ~/.codex/config.toml
142
+ const configPath = path.join(codexDir, 'config.toml');
143
+ const marker = '# >>> maxapi codex >>>';
144
+ const markerEnd = '# <<< maxapi codex <<<';
145
+ let existing = '';
146
+ if (fs.existsSync(configPath)) {
147
+ existing = fs.readFileSync(configPath, 'utf8');
148
+ const re = new RegExp(`${marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${markerEnd.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\n?`, 'g');
149
+ existing = existing.replace(re, '').trim();
150
+ }
151
+ const section = [marker, `model = "o4-mini"`, `provider = "openai"`, '',
152
+ `[providers.openai]`, `api_key = "${apiKey}"`, `base_url = "${ENDPOINT}"`, markerEnd].join('\n');
153
+ fs.writeFileSync(configPath, existing ? `${existing}\n\n${section}\n` : `${section}\n`, 'utf8');
154
+
155
+ // ~/.codex/auth.json
156
+ const authPath = path.join(codexDir, 'auth.json');
157
+ let auth = readJson(authPath) || {};
158
+ auth.OPENAI_API_KEY = apiKey;
159
+ writeJson(authPath, auth);
160
+
161
+ console.log(chalk.green(' ✅ Codex 配置完成'));
162
+ console.log(chalk.gray(` ${configPath}`));
163
+ }
164
+
165
+ // ============ Gateway 操作 ============
166
+ function findCliBinary() {
167
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
168
+ try {
169
+ execSync(`${process.platform === 'win32' ? 'where' : 'which'} ${name}`, { stdio: 'ignore' });
170
+ return name;
171
+ } catch { /* continue */ }
172
+ }
173
+ return null;
174
+ }
175
+
176
+ function safeRun(cmd, timeout = 30000) {
177
+ try {
178
+ return { ok: true, output: execSync(cmd, { encoding: 'utf8', stdio: 'pipe', timeout }).trim() };
179
+ } catch (e) {
180
+ return { ok: false, error: e.message, stderr: e.stderr?.toString() || '' };
181
+ }
182
+ }
183
+
184
+ function cleanAgents() {
185
+ console.log(chalk.gray(' 清理残留 agent 进程...'));
186
+ if (process.platform === 'win32') {
187
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
188
+ safeRun(`taskkill /F /FI "IMAGENAME eq node.exe" /FI "WINDOWTITLE eq *${name}*agent*" 2>nul`, 10000);
189
+ }
190
+ } else {
191
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
192
+ safeRun(`pkill -f '${name}.*agent' 2>/dev/null || true`, 10000);
193
+ }
194
+ }
195
+ console.log(chalk.gray(' 已清理残留 agent 进程'));
196
+ }
197
+
198
+ function restartGateway(cli) {
199
+ // 自动设置 gateway.mode(未设置时会导致 TUI 无法连接)
200
+ const modeCheck = safeRun(`${cli} config get gateway.mode 2>&1`, 5000);
201
+ const modeUnset = !modeCheck.ok || !modeCheck.output.trim() ||
202
+ modeCheck.output.includes('undefined') || modeCheck.output.includes('null') ||
203
+ modeCheck.output.includes('not set') || modeCheck.output.includes('unset');
204
+ if (modeUnset) {
205
+ console.log(chalk.gray(' 自动设置 gateway.mode = local ...'));
206
+ safeRun(`${cli} config set gateway.mode local`, 10000);
207
+ }
208
+
209
+ console.log(chalk.cyan(' 正在重启 Gateway...'));
210
+ const result = safeRun(`${cli} gateway restart`, 30000);
211
+ if (result.ok) {
212
+ console.log(chalk.green(' ✅ Gateway 已重启'));
213
+ } else {
214
+ console.log(chalk.yellow(' ⚠ Gateway 重启失败,尝试直接启动...'));
215
+ const start = safeRun(`${cli} gateway`, 15000);
216
+ if (start.ok) {
217
+ console.log(chalk.green(' ✅ Gateway 已启动'));
218
+ } else {
219
+ console.log(chalk.red(' ❌ Gateway 启动失败,请手动执行: ' + cli + ' gateway'));
220
+ }
221
+ }
222
+ }
223
+
224
+ async function testConversation(cli) {
225
+ console.log(chalk.cyan('\n 测试对话: 你是什么模型?\n'));
226
+ const sessionId = `mxosapi-test-${Date.now()}`;
227
+ const result = safeRun(`${cli} agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120`, 130000);
228
+
229
+ if (result.ok) {
230
+ try {
231
+ const json = JSON.parse(result.output);
232
+ if (json.message) {
233
+ console.log(chalk.green(' ✅ 对话测试成功'));
234
+ if (json.provider) console.log(chalk.cyan(` Provider: ${json.provider}`));
235
+ if (json.model) console.log(chalk.cyan(` Model: ${json.model}`));
236
+ console.log(chalk.yellow(` 回复: ${json.message.substring(0, 200)}`));
237
+ return;
238
+ }
239
+ if (json.error) {
240
+ console.log(chalk.red(` ❌ 对话失败: ${json.error.message || JSON.stringify(json.error)}`));
241
+ return;
242
+ }
243
+ } catch { /* not json, treat as plain text */ }
244
+ console.log(chalk.green(' ✅ 对话测试成功'));
245
+ console.log(chalk.yellow(` 回复: ${result.output.substring(0, 200)}`));
246
+ } else {
247
+ console.log(chalk.red(' ❌ 对话测试失败'));
248
+ console.log(chalk.gray(` ${(result.error || '').substring(0, 200)}`));
249
+ }
250
+ }
251
+
252
+ async function runGatewayTest() {
253
+ const cli = findCliBinary();
254
+ if (!cli) {
255
+ console.log(chalk.red('\n ❌ 未找到 openclaw/clawdbot/moltbot 命令'));
256
+ console.log(chalk.gray(' 请先安装 OpenClaw: curl -fsSL https://openclaw.ai/install.sh | bash'));
257
+ return;
258
+ }
259
+ console.log(chalk.gray(` 使用: ${cli}\n`));
260
+ cleanAgents();
261
+ restartGateway(cli);
262
+ await testConversation(cli);
263
+ }
264
+
265
+ // ============ 主菜单 ============
266
+ async function main() {
267
+ console.log(chalk.cyan.bold('\n MAXAPI 一键配置工具\n'));
268
+ console.log(chalk.gray(` 节点: ${ENDPOINT}\n`));
269
+
270
+ const { tool } = await inquirer.prompt([{
271
+ type: 'list',
272
+ name: 'tool',
273
+ message: '选择要配置的工具:',
274
+ choices: [
275
+ { name: ' 配置 OpenClaw', value: 'openclaw' },
276
+ { name: ' 配置 Claude Code', value: 'claude' },
277
+ { name: ' 配置 Codex', value: 'codex' },
278
+ { name: ' 全部配置', value: 'all' },
279
+ new inquirer.Separator(''),
280
+ { name: ' 测试对话', value: 'test' },
281
+ { name: ' 退出', value: 'exit' },
282
+ ],
283
+ }]);
284
+
285
+ if (tool === 'exit') {
286
+ console.log(chalk.cyan('\n👋 再见!\n'));
287
+ process.exit(0);
288
+ }
289
+
290
+ if (tool === 'test') {
291
+ await runGatewayTest();
292
+ console.log('');
293
+ return main();
294
+ }
295
+
296
+ const { apiKey } = await inquirer.prompt([{
297
+ type: 'input',
298
+ name: 'apiKey',
299
+ message: '请输入 API Key:',
300
+ validate: v => v.trim() ? true : 'API Key 不能为空',
301
+ }]);
302
+
303
+ console.log('');
304
+
305
+ if (tool === 'openclaw' || tool === 'all') writeOpenClawConfig(apiKey);
306
+ if (tool === 'claude' || tool === 'all') writeClaudeCodeConfig(apiKey);
307
+ if (tool === 'codex' || tool === 'all') writeCodexConfig(apiKey);
308
+
309
+ console.log(chalk.green.bold('\n✅ 配置完成!'));
310
+ console.log(chalk.gray(' 模型在 hub.maxapi.cn 网站切换'));
311
+
312
+ // 配置 OpenClaw 后自动测试
313
+ if (tool === 'openclaw' || tool === 'all') {
314
+ const { autoTest } = await inquirer.prompt([{
315
+ type: 'confirm',
316
+ name: 'autoTest',
317
+ message: '是否立即测试 OpenClaw Gateway?',
318
+ default: true,
319
+ }]);
320
+ if (autoTest) await runGatewayTest();
321
+ }
322
+
323
+ console.log('');
324
+ }
325
+
326
+ main().catch(e => {
327
+ console.error(chalk.red(`\n错误: ${e.message}\n`));
328
+ process.exit(1);
329
+ });
package/bin/mxosapi.js ADDED
@@ -0,0 +1,329 @@
1
+ #!/usr/bin/env node
2
+
3
+ const inquirer = require('inquirer');
4
+ const chalk = require('chalk');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+ const JSON5 = require('json5');
9
+
10
+ const { execSync } = require('child_process');
11
+
12
+ // ============ MAXAPI 配置 ============
13
+ const ENDPOINT = 'http://heibai.natapp1.cc';
14
+ const PROVIDER_NAME = 'maxapi';
15
+ const MODEL_ID = 'heibai';
16
+ const MODEL_NAME = 'MAXAPI (网站切换模型)';
17
+
18
+ // ============ 路径 ============
19
+ function getOpenClawPaths() {
20
+ const home = os.homedir();
21
+ const candidates = ['.openclaw', '.clawdbot', '.moltbot'];
22
+ for (const dir of candidates) {
23
+ const p = path.join(home, dir);
24
+ if (fs.existsSync(p)) {
25
+ return {
26
+ configDir: p,
27
+ configPath: path.join(p, 'openclaw.json'),
28
+ authProfiles: path.join(p, 'agents', 'main', 'agent', 'auth-profiles.json'),
29
+ };
30
+ }
31
+ }
32
+ const defaultDir = path.join(home, '.openclaw');
33
+ return {
34
+ configDir: defaultDir,
35
+ configPath: path.join(defaultDir, 'openclaw.json'),
36
+ authProfiles: path.join(defaultDir, 'agents', 'main', 'agent', 'auth-profiles.json'),
37
+ };
38
+ }
39
+
40
+ // ============ 读写配置 ============
41
+ function readJson(filePath) {
42
+ if (!fs.existsSync(filePath)) return null;
43
+ try {
44
+ const raw = fs.readFileSync(filePath, 'utf8').trim();
45
+ if (!raw || !raw.startsWith('{')) return null;
46
+ return JSON5.parse(raw);
47
+ } catch { return null; }
48
+ }
49
+
50
+ function writeJson(filePath, data) {
51
+ const dir = path.dirname(filePath);
52
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
53
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
54
+ }
55
+
56
+ function ensureStructure(config) {
57
+ const c = config || {};
58
+ if (!c.models) c.models = {};
59
+ if (!c.models.providers) c.models.providers = {};
60
+ if (!c.agents) c.agents = {};
61
+ if (!c.agents.defaults) c.agents.defaults = {};
62
+ if (!c.agents.defaults.model) c.agents.defaults.model = {};
63
+ if (!c.agents.defaults.models || typeof c.agents.defaults.models !== 'object' || Array.isArray(c.agents.defaults.models)) {
64
+ c.agents.defaults.models = {};
65
+ }
66
+ if (!c.auth) c.auth = {};
67
+ if (!c.auth.profiles) c.auth.profiles = {};
68
+ return c;
69
+ }
70
+
71
+ // ============ 写入 OpenClaw 配置 ============
72
+ function writeOpenClawConfig(apiKey) {
73
+ const paths = getOpenClawPaths();
74
+ const config = ensureStructure(readJson(paths.configPath) || {});
75
+ const key = apiKey.trim();
76
+
77
+ if (!config.models.mode) config.models.mode = 'merge';
78
+ // auth 是认证方式类型('api-key'),实际 key 放 apiKey 字段
79
+ config.models.providers[PROVIDER_NAME] = {
80
+ baseUrl: ENDPOINT,
81
+ auth: 'api-key',
82
+ apiKey: key,
83
+ api: 'openai-completions',
84
+ models: [{
85
+ id: MODEL_ID, name: MODEL_NAME,
86
+ reasoning: false, input: ['text'],
87
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
88
+ contextWindow: 204800, maxTokens: 131072,
89
+ }],
90
+ };
91
+
92
+ // auth.profiles 只存 provider 和 mode,不存 apiKey(OpenClaw 不识别)
93
+ const profileKey = `${PROVIDER_NAME}:default`;
94
+ config.auth.profiles[profileKey] = {
95
+ provider: PROVIDER_NAME,
96
+ mode: 'api_key',
97
+ };
98
+
99
+ // 注册到 agents.defaults.models,便于 OpenClaw 识别与切换
100
+ const modelKey = `${PROVIDER_NAME}/${MODEL_ID}`;
101
+ config.agents.defaults.models[modelKey] = { alias: PROVIDER_NAME };
102
+ if (!config.agents.defaults.model.primary) {
103
+ config.agents.defaults.model.primary = modelKey;
104
+ }
105
+
106
+ writeJson(paths.configPath, config);
107
+
108
+ console.log(chalk.green(' ✅ OpenClaw 配置完成'));
109
+ console.log(chalk.gray(` ${paths.configPath}`));
110
+ console.log(chalk.yellow(' 💡 配置后请重启网关: openclaw gateway restart'));
111
+ }
112
+
113
+ // ============ 写入 Claude Code 配置 ============
114
+ function writeClaudeCodeConfig(apiKey) {
115
+ const home = os.homedir();
116
+ // ~/.claude/settings.json
117
+ const claudeDir = path.join(home, '.claude');
118
+ const settingsPath = path.join(claudeDir, 'settings.json');
119
+ let settings = readJson(settingsPath) || {};
120
+ settings.apiBaseUrl = ENDPOINT;
121
+ writeJson(settingsPath, settings);
122
+
123
+ // 环境变量提示
124
+ console.log(chalk.green(' ✅ Claude Code 配置完成'));
125
+ console.log(chalk.gray(` ${settingsPath}`));
126
+ console.log(chalk.yellow(' 💡 还需设置环境变量 ANTHROPIC_AUTH_TOKEN:'));
127
+ if (process.platform === 'win32') {
128
+ console.log(chalk.cyan(` [Environment]::SetEnvironmentVariable('ANTHROPIC_AUTH_TOKEN', '${apiKey}', 'User')`));
129
+ } else {
130
+ console.log(chalk.cyan(` export ANTHROPIC_AUTH_TOKEN="${apiKey}"`));
131
+ console.log(chalk.gray(` 永久生效: echo 'export ANTHROPIC_AUTH_TOKEN="${apiKey}"' >> ~/.zshrc && source ~/.zshrc`));
132
+ }
133
+ }
134
+
135
+ // ============ 写入 Codex 配置 ============
136
+ function writeCodexConfig(apiKey) {
137
+ const home = os.homedir();
138
+ const codexDir = path.join(home, '.codex');
139
+ if (!fs.existsSync(codexDir)) fs.mkdirSync(codexDir, { recursive: true });
140
+
141
+ // ~/.codex/config.toml
142
+ const configPath = path.join(codexDir, 'config.toml');
143
+ const marker = '# >>> maxapi codex >>>';
144
+ const markerEnd = '# <<< maxapi codex <<<';
145
+ let existing = '';
146
+ if (fs.existsSync(configPath)) {
147
+ existing = fs.readFileSync(configPath, 'utf8');
148
+ const re = new RegExp(`${marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${markerEnd.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\n?`, 'g');
149
+ existing = existing.replace(re, '').trim();
150
+ }
151
+ const section = [marker, `model = "o4-mini"`, `provider = "openai"`, '',
152
+ `[providers.openai]`, `api_key = "${apiKey}"`, `base_url = "${ENDPOINT}"`, markerEnd].join('\n');
153
+ fs.writeFileSync(configPath, existing ? `${existing}\n\n${section}\n` : `${section}\n`, 'utf8');
154
+
155
+ // ~/.codex/auth.json
156
+ const authPath = path.join(codexDir, 'auth.json');
157
+ let auth = readJson(authPath) || {};
158
+ auth.OPENAI_API_KEY = apiKey;
159
+ writeJson(authPath, auth);
160
+
161
+ console.log(chalk.green(' ✅ Codex 配置完成'));
162
+ console.log(chalk.gray(` ${configPath}`));
163
+ }
164
+
165
+ // ============ Gateway 操作 ============
166
+ function findCliBinary() {
167
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
168
+ try {
169
+ execSync(`${process.platform === 'win32' ? 'where' : 'which'} ${name}`, { stdio: 'ignore' });
170
+ return name;
171
+ } catch { /* continue */ }
172
+ }
173
+ return null;
174
+ }
175
+
176
+ function safeRun(cmd, timeout = 30000) {
177
+ try {
178
+ return { ok: true, output: execSync(cmd, { encoding: 'utf8', stdio: 'pipe', timeout }).trim() };
179
+ } catch (e) {
180
+ return { ok: false, error: e.message, stderr: e.stderr?.toString() || '' };
181
+ }
182
+ }
183
+
184
+ function cleanAgents() {
185
+ console.log(chalk.gray(' 清理残留 agent 进程...'));
186
+ if (process.platform === 'win32') {
187
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
188
+ safeRun(`taskkill /F /FI "IMAGENAME eq node.exe" /FI "WINDOWTITLE eq *${name}*agent*" 2>nul`, 10000);
189
+ }
190
+ } else {
191
+ for (const name of ['openclaw', 'clawdbot', 'moltbot']) {
192
+ safeRun(`pkill -f '${name}.*agent' 2>/dev/null || true`, 10000);
193
+ }
194
+ }
195
+ console.log(chalk.gray(' 已清理残留 agent 进程'));
196
+ }
197
+
198
+ function restartGateway(cli) {
199
+ // 自动设置 gateway.mode(未设置时会导致 TUI 无法连接)
200
+ const modeCheck = safeRun(`${cli} config get gateway.mode 2>&1`, 5000);
201
+ const modeUnset = !modeCheck.ok || !modeCheck.output.trim() ||
202
+ modeCheck.output.includes('undefined') || modeCheck.output.includes('null') ||
203
+ modeCheck.output.includes('not set') || modeCheck.output.includes('unset');
204
+ if (modeUnset) {
205
+ console.log(chalk.gray(' 自动设置 gateway.mode = local ...'));
206
+ safeRun(`${cli} config set gateway.mode local`, 10000);
207
+ }
208
+
209
+ console.log(chalk.cyan(' 正在重启 Gateway...'));
210
+ const result = safeRun(`${cli} gateway restart`, 30000);
211
+ if (result.ok) {
212
+ console.log(chalk.green(' ✅ Gateway 已重启'));
213
+ } else {
214
+ console.log(chalk.yellow(' ⚠ Gateway 重启失败,尝试直接启动...'));
215
+ const start = safeRun(`${cli} gateway`, 15000);
216
+ if (start.ok) {
217
+ console.log(chalk.green(' ✅ Gateway 已启动'));
218
+ } else {
219
+ console.log(chalk.red(' ❌ Gateway 启动失败,请手动执行: ' + cli + ' gateway'));
220
+ }
221
+ }
222
+ }
223
+
224
+ async function testConversation(cli) {
225
+ console.log(chalk.cyan('\n 测试对话: 你是什么模型?\n'));
226
+ const sessionId = `mxosapi-test-${Date.now()}`;
227
+ const result = safeRun(`${cli} agent --session-id ${sessionId} --message "请回复你的模型名称" --json --timeout 120`, 130000);
228
+
229
+ if (result.ok) {
230
+ try {
231
+ const json = JSON.parse(result.output);
232
+ if (json.message) {
233
+ console.log(chalk.green(' ✅ 对话测试成功'));
234
+ if (json.provider) console.log(chalk.cyan(` Provider: ${json.provider}`));
235
+ if (json.model) console.log(chalk.cyan(` Model: ${json.model}`));
236
+ console.log(chalk.yellow(` 回复: ${json.message.substring(0, 200)}`));
237
+ return;
238
+ }
239
+ if (json.error) {
240
+ console.log(chalk.red(` ❌ 对话失败: ${json.error.message || JSON.stringify(json.error)}`));
241
+ return;
242
+ }
243
+ } catch { /* not json, treat as plain text */ }
244
+ console.log(chalk.green(' ✅ 对话测试成功'));
245
+ console.log(chalk.yellow(` 回复: ${result.output.substring(0, 200)}`));
246
+ } else {
247
+ console.log(chalk.red(' ❌ 对话测试失败'));
248
+ console.log(chalk.gray(` ${(result.error || '').substring(0, 200)}`));
249
+ }
250
+ }
251
+
252
+ async function runGatewayTest() {
253
+ const cli = findCliBinary();
254
+ if (!cli) {
255
+ console.log(chalk.red('\n ❌ 未找到 openclaw/clawdbot/moltbot 命令'));
256
+ console.log(chalk.gray(' 请先安装 OpenClaw: curl -fsSL https://openclaw.ai/install.sh | bash'));
257
+ return;
258
+ }
259
+ console.log(chalk.gray(` 使用: ${cli}\n`));
260
+ cleanAgents();
261
+ restartGateway(cli);
262
+ await testConversation(cli);
263
+ }
264
+
265
+ // ============ 主菜单 ============
266
+ async function main() {
267
+ console.log(chalk.cyan.bold('\n MAXAPI 一键配置工具\n'));
268
+ console.log(chalk.gray(` 节点: ${ENDPOINT}\n`));
269
+
270
+ const { tool } = await inquirer.prompt([{
271
+ type: 'list',
272
+ name: 'tool',
273
+ message: '选择要配置的工具:',
274
+ choices: [
275
+ { name: ' 配置 OpenClaw', value: 'openclaw' },
276
+ { name: ' 配置 Claude Code', value: 'claude' },
277
+ { name: ' 配置 Codex', value: 'codex' },
278
+ { name: ' 全部配置', value: 'all' },
279
+ new inquirer.Separator(''),
280
+ { name: ' 测试对话', value: 'test' },
281
+ { name: ' 退出', value: 'exit' },
282
+ ],
283
+ }]);
284
+
285
+ if (tool === 'exit') {
286
+ console.log(chalk.cyan('\n👋 再见!\n'));
287
+ process.exit(0);
288
+ }
289
+
290
+ if (tool === 'test') {
291
+ await runGatewayTest();
292
+ console.log('');
293
+ return main();
294
+ }
295
+
296
+ const { apiKey } = await inquirer.prompt([{
297
+ type: 'input',
298
+ name: 'apiKey',
299
+ message: '请输入 API Key:',
300
+ validate: v => v.trim() ? true : 'API Key 不能为空',
301
+ }]);
302
+
303
+ console.log('');
304
+
305
+ if (tool === 'openclaw' || tool === 'all') writeOpenClawConfig(apiKey);
306
+ if (tool === 'claude' || tool === 'all') writeClaudeCodeConfig(apiKey);
307
+ if (tool === 'codex' || tool === 'all') writeCodexConfig(apiKey);
308
+
309
+ console.log(chalk.green.bold('\n✅ 配置完成!'));
310
+ console.log(chalk.gray(' 模型在 hub.maxapi.cn 网站切换'));
311
+
312
+ // 配置 OpenClaw 后自动测试
313
+ if (tool === 'openclaw' || tool === 'all') {
314
+ const { autoTest } = await inquirer.prompt([{
315
+ type: 'confirm',
316
+ name: 'autoTest',
317
+ message: '是否立即测试 OpenClaw Gateway?',
318
+ default: true,
319
+ }]);
320
+ if (autoTest) await runGatewayTest();
321
+ }
322
+
323
+ console.log('');
324
+ }
325
+
326
+ main().catch(e => {
327
+ console.error(chalk.red(`\n错误: ${e.message}\n`));
328
+ process.exit(1);
329
+ });
package/mxosapi.bat ADDED
@@ -0,0 +1,137 @@
1
+ @echo off
2
+ chcp 65001 >nul 2>&1
3
+ title mxosapi - MAXAPI 配置工具
4
+
5
+ :: ========== 检测 WSL ==========
6
+ wsl echo ok >nul 2>&1
7
+ if %errorlevel% equ 0 (
8
+ echo.
9
+ echo [检测] 发现 WSL 环境
10
+ echo.
11
+ echo 1. 在 WSL 内安装并运行(推荐,OpenClaw 原生 Linux 支持)
12
+ echo 2. 在 Windows 本地运行
13
+ echo.
14
+ set /p WSL_CHOICE="请选择 [1/2,默认 1]: "
15
+ if "%WSL_CHOICE%"=="" set WSL_CHOICE=1
16
+ if "%WSL_CHOICE%"=="1" goto :wsl_setup
17
+ )
18
+ goto :win_check_node
19
+
20
+ :: ========== WSL 内安装 ==========
21
+ :wsl_setup
22
+ echo.
23
+ echo [WSL] 检查 WSL 内环境...
24
+
25
+ :: 检查 WSL 内 Node.js
26
+ wsl -- bash -c "command -v node" >nul 2>&1
27
+ if %errorlevel% neq 0 (
28
+ echo [WSL] 未检测到 Node.js,正在安装...
29
+ wsl -- bash -c "command -v curl >/dev/null || { sudo apt-get update -qq && sudo apt-get install -y -qq curl; }; curl -fsSL https://fnm.vercel.app/install | bash -s -- --skip-shell && export PATH=\"$HOME/.local/share/fnm:$PATH\" && eval \"$(fnm env)\" && fnm install --lts && fnm use lts-latest"
30
+ if %errorlevel% neq 0 (
31
+ echo [错误] WSL 内 Node.js 安装失败
32
+ pause
33
+ exit /b 1
34
+ )
35
+ echo [WSL] Node.js 安装成功
36
+ )
37
+
38
+ :: 检查 WSL 内 Git
39
+ wsl -- bash -c "command -v git" >nul 2>&1
40
+ if %errorlevel% neq 0 (
41
+ echo [WSL] 未检测到 Git,正在安装...
42
+ wsl -- bash -c "sudo apt-get update -qq && sudo apt-get install -y -qq git"
43
+ if %errorlevel% neq 0 (
44
+ echo [错误] WSL 内 Git 安装失败
45
+ pause
46
+ exit /b 1
47
+ )
48
+ echo [WSL] Git 安装成功
49
+ )
50
+
51
+ echo.
52
+ echo [WSL] 正在启动 mxosapi...
53
+ echo.
54
+ wsl -- bash -lc "export PATH=\"$HOME/.local/share/fnm:$PATH\"; eval \"$(fnm env 2>/dev/null)\"; npx mxosapi@latest"
55
+ if %errorlevel% neq 0 (
56
+ echo.
57
+ echo [提示] WSL 内运行失败,尝试: wsl -- bash -lc "npx mxosapi@latest"
58
+ pause
59
+ )
60
+ goto :eof
61
+
62
+ :: ========== Windows 本地:检查 Node.js ==========
63
+ :win_check_node
64
+ where node >nul 2>&1
65
+ if %errorlevel% neq 0 (
66
+ echo.
67
+ echo [提示] 未检测到 Node.js,正在自动安装...
68
+ echo.
69
+ goto :install_node
70
+ )
71
+
72
+ :: 检查 Node.js 版本 >= 18
73
+ for /f "tokens=1 delims=." %%a in ('node -v') do set NODE_VER=%%a
74
+ set NODE_VER=%NODE_VER:v=%
75
+ if %NODE_VER% LSS 18 (
76
+ echo.
77
+ echo [提示] Node.js 版本过低 (v%NODE_VER%),需要 >= 18,正在升级...
78
+ echo.
79
+ goto :install_node
80
+ )
81
+ goto :check_git
82
+
83
+ :install_node
84
+ where winget >nul 2>&1
85
+ if %errorlevel% equ 0 (
86
+ echo [安装] 通过 winget 安装 Node.js LTS...
87
+ winget install OpenJS.NodeJS.LTS --accept-source-agreements --accept-package-agreements --silent
88
+ goto :refresh_and_check_git
89
+ )
90
+ echo [安装] 未检测到 winget,请手动安装 Node.js: https://nodejs.org
91
+ pause
92
+ exit /b 1
93
+
94
+ :: ========== 检查并安装 Git ==========
95
+ :check_git
96
+ where git >nul 2>&1
97
+ if %errorlevel% neq 0 (
98
+ echo.
99
+ echo [提示] 未检测到 Git,正在自动安装...
100
+ echo.
101
+ goto :install_git
102
+ )
103
+ goto :run
104
+
105
+ :install_git
106
+ where winget >nul 2>&1
107
+ if %errorlevel% equ 0 (
108
+ echo [安装] 通过 winget 安装 Git...
109
+ winget install Git.Git --accept-source-agreements --accept-package-agreements --silent
110
+ goto :refresh_and_run
111
+ )
112
+ echo [提示] 未检测到 winget,Git 未安装(非必须,继续运行)
113
+ goto :run
114
+
115
+ :refresh_and_check_git
116
+ set "PATH=%ProgramFiles%\nodejs;%PATH%"
117
+ set "PATH=%LOCALAPPDATA%\fnm_multishells;%PATH%"
118
+ goto :check_git
119
+
120
+ :refresh_and_run
121
+ set "PATH=%ProgramFiles%\Git\cmd;%PATH%"
122
+ goto :run
123
+
124
+ :: ========== 运行 ==========
125
+ :run
126
+ powershell -Command "Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned -Force" >nul 2>&1
127
+
128
+ echo.
129
+ echo 正在启动 mxosapi...
130
+ echo.
131
+ npx.cmd mxosapi@latest %*
132
+
133
+ if %errorlevel% neq 0 (
134
+ echo.
135
+ echo [提示] 如果上面报错,请尝试手动运行:npx.cmd mxosapi
136
+ pause
137
+ )
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "maxai",
3
+ "version": "1.0.0",
4
+ "description": "MAXAPI 一键配置工具 — 快速配置 OpenClaw/Claude Code/Codex",
5
+ "main": "bin/maxai.js",
6
+ "bin": {
7
+ "maxai": "bin/maxai.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node bin/maxai.js"
11
+ },
12
+ "keywords": [
13
+ "openclaw",
14
+ "clawdbot",
15
+ "config",
16
+ "cli",
17
+ "maxapi",
18
+ "heibai",
19
+ "claude",
20
+ "codex",
21
+ "anthropic"
22
+ ],
23
+ "author": "MaxAPI Community",
24
+ "license": "MIT",
25
+ "files": [
26
+ "bin/",
27
+ "mxosapi.bat"
28
+ ],
29
+ "dependencies": {
30
+ "inquirer": "^8.2.5",
31
+ "chalk": "^4.1.2",
32
+ "json5": "^2.2.3"
33
+ },
34
+ "engines": {
35
+ "node": ">=14.0.0"
36
+ },
37
+ "preferGlobal": true
38
+ }