claudmax 2.0.4 → 3.0.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/index.js +274 -138
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -9,13 +9,22 @@ const os = require('os');
|
|
|
9
9
|
const https = require('https');
|
|
10
10
|
const { execSync } = require('child_process');
|
|
11
11
|
|
|
12
|
-
// ── Constants
|
|
13
|
-
const MCP_PKG = 'claudmax-mcp';
|
|
14
|
-
const API_BASE = 'https://api.claudmax.pro';
|
|
15
|
-
|
|
12
|
+
// ── Constants ──────────────────────────────────────────────────────────────
|
|
13
|
+
const MCP_PKG = 'claudmax-mcp-server';
|
|
14
|
+
const API_BASE = process.env.CLAUDMAX_API_BASE || 'https://api.claudmax.pro';
|
|
16
15
|
const HOME = os.homedir();
|
|
17
16
|
|
|
18
|
-
// ──
|
|
17
|
+
// ── CLI args ──────────────────────────────────────────────────────────────
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
const flags = {};
|
|
20
|
+
for (let i = 0; i < args.length; i++) {
|
|
21
|
+
if (args[i].startsWith('--')) {
|
|
22
|
+
const key = args[i].slice(2);
|
|
23
|
+
flags[key] = args[i + 1] !== undefined && !args[i + 1].startsWith('--') ? args[i + 1] : true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ── Color helpers ─────────────────────────────────────────────────────────
|
|
19
28
|
const C = {
|
|
20
29
|
reset: '\x1b[0m',
|
|
21
30
|
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
@@ -32,8 +41,9 @@ const CHECK = C.green('\u2713');
|
|
|
32
41
|
const CROSS = C.red('\u2717');
|
|
33
42
|
const WARN = C.yellow('\u26A0');
|
|
34
43
|
const INFO = C.cyan('\u2139');
|
|
44
|
+
const ARROW = C.cyan('\u25b6');
|
|
35
45
|
|
|
36
|
-
// ── Readline helper
|
|
46
|
+
// ── Readline helper ────────────────────────────────────────────────────────
|
|
37
47
|
function createRL() {
|
|
38
48
|
return readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
39
49
|
}
|
|
@@ -42,7 +52,7 @@ function ask(rl, question) {
|
|
|
42
52
|
return new Promise((resolve) => rl.question(question, resolve));
|
|
43
53
|
}
|
|
44
54
|
|
|
45
|
-
// ── File helpers
|
|
55
|
+
// ── File helpers ─────────────────────────────────────────────────────────
|
|
46
56
|
function readJson(filePath) {
|
|
47
57
|
try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
|
|
48
58
|
catch { return null; }
|
|
@@ -70,73 +80,112 @@ function ensureDir(dir) {
|
|
|
70
80
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
71
81
|
}
|
|
72
82
|
|
|
73
|
-
|
|
83
|
+
function fileExists(filePath) {
|
|
84
|
+
try { return fs.existsSync(filePath); } catch { return false; }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ── IDE auto-detector ─────────────────────────────────────────────────────
|
|
88
|
+
function detectIDESilent() {
|
|
89
|
+
const detected = [];
|
|
90
|
+
|
|
91
|
+
if (fileExists(path.join(HOME, '.claude', 'settings.json')) ||
|
|
92
|
+
fileExists(path.join(HOME, '.claude.json'))) {
|
|
93
|
+
detected.push('claude-code');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const cursorDir = path.join(HOME, '.cursor', 'mcp.json');
|
|
97
|
+
if (fileExists(cursorDir) ||
|
|
98
|
+
fileExists(path.join(HOME, 'Library', 'Application Support', 'Cursor'))) {
|
|
99
|
+
detected.push('cursor');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (fileExists(path.join(HOME, '.windsurf', 'mcp.json'))) {
|
|
103
|
+
detected.push('windsurf');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// VS Code
|
|
107
|
+
const vscodePaths = [
|
|
108
|
+
path.join(HOME, 'Library', 'Application Support', 'Code', 'User', 'settings.json'),
|
|
109
|
+
path.join(HOME, '.config', 'Code', 'User', 'settings.json'),
|
|
110
|
+
path.join(process.env.APPDATA || '', 'Code', 'User', 'settings.json'),
|
|
111
|
+
];
|
|
112
|
+
for (const p of vscodePaths) {
|
|
113
|
+
if (fileExists(p)) { detected.push('vscode'); break; }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return [...new Set(detected)];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getVSCodeSettingsPath() {
|
|
120
|
+
switch (process.platform) {
|
|
121
|
+
case 'darwin':
|
|
122
|
+
return path.join(HOME, 'Library', 'Application Support', 'Code', 'User', 'settings.json');
|
|
123
|
+
case 'win32':
|
|
124
|
+
return path.join(process.env.APPDATA || '', 'Code', 'User', 'settings.json');
|
|
125
|
+
default:
|
|
126
|
+
return path.join(HOME, '.config', 'Code', 'User', 'settings.json');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── API verification ─────────────────────────────────────────────────────
|
|
74
131
|
function verifyConnection(apiKey) {
|
|
75
132
|
return new Promise((resolve) => {
|
|
133
|
+
const url = new URL(`${API_BASE}/v1/models`);
|
|
76
134
|
const options = {
|
|
77
|
-
hostname:
|
|
135
|
+
hostname: url.hostname,
|
|
78
136
|
port: 443,
|
|
79
|
-
path:
|
|
80
|
-
method: '
|
|
137
|
+
path: url.pathname,
|
|
138
|
+
method: 'GET',
|
|
81
139
|
headers: {
|
|
82
|
-
'
|
|
83
|
-
'User-Agent': 'ClaudMax-CLI/
|
|
140
|
+
'x-api-key': apiKey,
|
|
141
|
+
'User-Agent': 'ClaudMax-CLI/3.0.0',
|
|
84
142
|
},
|
|
85
143
|
timeout: 15000,
|
|
86
144
|
};
|
|
87
145
|
|
|
88
146
|
const req = https.request(options, (res) => {
|
|
89
147
|
let body = '';
|
|
90
|
-
res.on('data', (
|
|
148
|
+
res.on('data', (c) => { body += c; });
|
|
91
149
|
res.on('end', () => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
resolve({ status: res.statusCode, data, ok: true });
|
|
96
|
-
} catch {
|
|
97
|
-
resolve({ status: res.statusCode, error: body, ok: false });
|
|
98
|
-
}
|
|
150
|
+
if (res.statusCode === 200) resolve({ ok: true, status: res.statusCode });
|
|
151
|
+
else if (res.statusCode === 401) resolve({ ok: false, status: res.statusCode, error: 'invalid_key' });
|
|
152
|
+
else resolve({ ok: false, status: res.statusCode, error: body });
|
|
99
153
|
});
|
|
100
154
|
});
|
|
101
155
|
|
|
102
|
-
req.on('error', (err) => resolve({ status: 0, error: err.message
|
|
103
|
-
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout'
|
|
104
|
-
|
|
105
|
-
// key-check endpoint expects { apiKey } in body
|
|
106
|
-
req.write(JSON.stringify({ apiKey }));
|
|
156
|
+
req.on('error', (err) => resolve({ ok: false, status: 0, error: err.message }));
|
|
157
|
+
req.on('timeout', () => { req.destroy(); resolve({ ok: false, status: 0, error: 'timeout' }); });
|
|
107
158
|
req.end();
|
|
108
159
|
});
|
|
109
160
|
}
|
|
110
161
|
|
|
111
|
-
// ── IDE configurators
|
|
112
|
-
function
|
|
113
|
-
process.stdout.write(`
|
|
162
|
+
// ── IDE configurators ────────────────────────────────────────────────────
|
|
163
|
+
function configureClaudeCode(apiKey) {
|
|
164
|
+
process.stdout.write(` ${ARROW} Claude Code CLI...`);
|
|
114
165
|
|
|
115
166
|
const settingsPath = path.join(HOME, '.claude', 'settings.json');
|
|
116
|
-
const dotClaudePath = path.join(HOME, '.claude.json');
|
|
117
|
-
|
|
118
167
|
ensureDir(path.dirname(settingsPath));
|
|
119
|
-
|
|
120
|
-
// settings.json — Claude Code reads ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL from here
|
|
121
168
|
const settings = readJson(settingsPath) || {};
|
|
122
169
|
deepMerge(settings, {
|
|
123
170
|
env: {
|
|
124
171
|
ANTHROPIC_AUTH_TOKEN: apiKey,
|
|
125
|
-
ANTHROPIC_BASE_URL: API_BASE
|
|
172
|
+
ANTHROPIC_BASE_URL: `${API_BASE}/v1`,
|
|
126
173
|
ANTHROPIC_MODEL: 'claude-opus-4-6',
|
|
127
174
|
ANTHROPIC_SMALL_FAST_MODEL: 'claude-haiku-4-5',
|
|
128
175
|
ANTHROPIC_DEFAULT_SONNET_MODEL: 'claude-sonnet-4-6',
|
|
129
176
|
ANTHROPIC_DEFAULT_OPUS_MODEL: 'claude-opus-4-6',
|
|
130
177
|
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'claude-haiku-4-5',
|
|
178
|
+
// Billing bypass: disable non-essential API traffic
|
|
131
179
|
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1',
|
|
180
|
+
CLAUDE_CODE_SKIP_VERSION_CHECK: '1',
|
|
132
181
|
},
|
|
133
182
|
hasCompletedOnboarding: true,
|
|
183
|
+
telemetryEnabled: false,
|
|
184
|
+
collectAnalytics: false,
|
|
134
185
|
});
|
|
135
186
|
writeJson(settingsPath, settings);
|
|
136
|
-
process.stdout.write(`${CHECK} `);
|
|
137
|
-
process.stdout.write(`${C.dim(settingsPath + '\n')}`);
|
|
138
187
|
|
|
139
|
-
|
|
188
|
+
const dotClaudePath = path.join(HOME, '.claude.json');
|
|
140
189
|
const dotClaude = readJson(dotClaudePath) || {};
|
|
141
190
|
if (!dotClaude.mcpServers) dotClaude.mcpServers = {};
|
|
142
191
|
dotClaude.mcpServers['ClaudMax'] = {
|
|
@@ -148,11 +197,47 @@ function configureClaudeCLI(apiKey) {
|
|
|
148
197
|
},
|
|
149
198
|
};
|
|
150
199
|
writeJson(dotClaudePath, dotClaude);
|
|
151
|
-
|
|
200
|
+
|
|
201
|
+
process.stdout.write(`${CHECK}\n`);
|
|
202
|
+
process.stdout.write(` ${C.dim(settingsPath)}\n`);
|
|
203
|
+
process.stdout.write(` ${C.dim(dotClaudePath)}\n`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function configureVSCodeClaude(apiKey) {
|
|
207
|
+
process.stdout.write(` ${ARROW} VS Code Claude Extension...`);
|
|
208
|
+
|
|
209
|
+
// Claude Code settings (extension auto-detects)
|
|
210
|
+
const settingsPath = path.join(HOME, '.claude', 'settings.json');
|
|
211
|
+
ensureDir(path.dirname(settingsPath));
|
|
212
|
+
const settings = readJson(settingsPath) || {};
|
|
213
|
+
deepMerge(settings, {
|
|
214
|
+
env: {
|
|
215
|
+
ANTHROPIC_AUTH_TOKEN: apiKey,
|
|
216
|
+
ANTHROPIC_BASE_URL: `${API_BASE}/v1`,
|
|
217
|
+
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1',
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
writeJson(settingsPath, settings);
|
|
221
|
+
|
|
222
|
+
// VS Code extension settings
|
|
223
|
+
const vsSettingsPath = getVSCodeSettingsPath();
|
|
224
|
+
ensureDir(path.dirname(vsSettingsPath));
|
|
225
|
+
const vsSettings = readJson(vsSettingsPath) || {};
|
|
226
|
+
deepMerge(vsSettings, {
|
|
227
|
+
'anthropic.authToken': apiKey,
|
|
228
|
+
'anthropic.baseUrl': `${API_BASE}/v1`,
|
|
229
|
+
'claude.codeDisableNonessentialTraffic': true,
|
|
230
|
+
});
|
|
231
|
+
writeJson(vsSettingsPath, vsSettings);
|
|
232
|
+
|
|
233
|
+
process.stdout.write(`${CHECK}\n`);
|
|
234
|
+
process.stdout.write(` ${C.dim(settingsPath)}\n`);
|
|
235
|
+
process.stdout.write(` ${C.dim(vsSettingsPath)}\n`);
|
|
152
236
|
}
|
|
153
237
|
|
|
154
238
|
function configureCursor(apiKey) {
|
|
155
|
-
process.stdout.write(`
|
|
239
|
+
process.stdout.write(` ${ARROW} Cursor...`);
|
|
240
|
+
|
|
156
241
|
const mcpPath = path.join(HOME, '.cursor', 'mcp.json');
|
|
157
242
|
ensureDir(path.dirname(mcpPath));
|
|
158
243
|
const existing = readJson(mcpPath) || {};
|
|
@@ -160,17 +245,17 @@ function configureCursor(apiKey) {
|
|
|
160
245
|
existing.mcpServers['ClaudMax'] = {
|
|
161
246
|
command: 'npx',
|
|
162
247
|
args: ['-y', MCP_PKG],
|
|
163
|
-
env: {
|
|
164
|
-
CLAUDMAX_API_KEY: apiKey,
|
|
165
|
-
CLAUDMAX_URL: API_BASE,
|
|
166
|
-
},
|
|
248
|
+
env: { CLAUDMAX_API_KEY: apiKey, CLAUDMAX_URL: API_BASE },
|
|
167
249
|
};
|
|
168
250
|
writeJson(mcpPath, existing);
|
|
169
|
-
|
|
251
|
+
|
|
252
|
+
process.stdout.write(`${CHECK}\n`);
|
|
253
|
+
process.stdout.write(` ${C.dim(mcpPath)}\n`);
|
|
170
254
|
}
|
|
171
255
|
|
|
172
256
|
function configureWindsurf(apiKey) {
|
|
173
|
-
process.stdout.write(`
|
|
257
|
+
process.stdout.write(` ${ARROW} Windsurf...`);
|
|
258
|
+
|
|
174
259
|
const mcpPath = path.join(HOME, '.windsurf', 'mcp.json');
|
|
175
260
|
ensureDir(path.dirname(mcpPath));
|
|
176
261
|
const existing = readJson(mcpPath) || {};
|
|
@@ -178,165 +263,216 @@ function configureWindsurf(apiKey) {
|
|
|
178
263
|
existing.mcpServers['ClaudMax'] = {
|
|
179
264
|
command: 'npx',
|
|
180
265
|
args: ['-y', MCP_PKG],
|
|
181
|
-
env: {
|
|
182
|
-
CLAUDMAX_API_KEY: apiKey,
|
|
183
|
-
CLAUDMAX_URL: API_BASE,
|
|
184
|
-
},
|
|
266
|
+
env: { CLAUDMAX_API_KEY: apiKey, CLAUDMAX_URL: API_BASE },
|
|
185
267
|
};
|
|
186
268
|
writeJson(mcpPath, existing);
|
|
187
|
-
|
|
269
|
+
|
|
270
|
+
process.stdout.write(`${CHECK}\n`);
|
|
271
|
+
process.stdout.write(` ${C.dim(mcpPath)}\n`);
|
|
188
272
|
}
|
|
189
273
|
|
|
190
274
|
function configureCline(apiKey) {
|
|
191
|
-
process.stdout.write(`
|
|
192
|
-
const settingsPath =
|
|
275
|
+
process.stdout.write(` ${ARROW} Cline...`);
|
|
276
|
+
const settingsPath = getVSCodeSettingsPath();
|
|
193
277
|
ensureDir(path.dirname(settingsPath));
|
|
194
278
|
const existing = readJson(settingsPath) || {};
|
|
195
279
|
deepMerge(existing, {
|
|
196
280
|
'cline.apiProvider': 'anthropic',
|
|
197
|
-
'cline.anthropicBaseUrl': API_BASE
|
|
281
|
+
'cline.anthropicBaseUrl': `${API_BASE}/v1`,
|
|
198
282
|
'cline.apiKey': apiKey,
|
|
199
283
|
});
|
|
200
284
|
writeJson(settingsPath, existing);
|
|
201
|
-
|
|
285
|
+
process.stdout.write(`${CHECK}\n`);
|
|
286
|
+
process.stdout.write(` ${C.dim(settingsPath)}\n`);
|
|
202
287
|
}
|
|
203
288
|
|
|
204
289
|
function configureRooCode(apiKey) {
|
|
205
|
-
process.stdout.write(`
|
|
206
|
-
const settingsPath =
|
|
290
|
+
process.stdout.write(` ${ARROW} Roo Code...`);
|
|
291
|
+
const settingsPath = getVSCodeSettingsPath();
|
|
207
292
|
ensureDir(path.dirname(settingsPath));
|
|
208
293
|
const existing = readJson(settingsPath) || {};
|
|
209
294
|
deepMerge(existing, {
|
|
210
295
|
'roo-cline.apiProvider': 'anthropic',
|
|
211
|
-
'roo-cline.anthropicBaseUrl': API_BASE
|
|
296
|
+
'roo-cline.anthropicBaseUrl': `${API_BASE}/v1`,
|
|
212
297
|
'roo-cline.apiKey': apiKey,
|
|
213
298
|
});
|
|
214
299
|
writeJson(settingsPath, existing);
|
|
215
|
-
|
|
300
|
+
process.stdout.write(`${CHECK}\n`);
|
|
301
|
+
process.stdout.write(` ${C.dim(settingsPath)}\n`);
|
|
216
302
|
}
|
|
217
303
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
304
|
+
// ── IDE registry ────────────────────────────────────────────────────────────
|
|
305
|
+
const IDES = [
|
|
306
|
+
{ id: 'claude-code', name: 'Claude Code (CLI)', configure: configureClaudeCode },
|
|
307
|
+
{ id: 'vscode', name: 'VS Code (Claude Extension)', configure: configureVSCodeClaude },
|
|
308
|
+
{ id: 'cursor', name: 'Cursor', configure: configureCursor },
|
|
309
|
+
{ id: 'windsurf', name: 'Windsurf', configure: configureWindsurf },
|
|
310
|
+
{ id: 'cline', name: 'Cline (VS Code Extension)', configure: configureCline },
|
|
311
|
+
{ id: 'roo', name: 'Roo Code (VS Code Extension)', configure: configureRooCode },
|
|
312
|
+
];
|
|
313
|
+
|
|
314
|
+
// ── Banner ────────────────────────────────────────────────────────────────
|
|
315
|
+
function printBanner() {
|
|
316
|
+
console.log('');
|
|
317
|
+
console.log(C.magenta(' \u256d\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\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\u256e'));
|
|
318
|
+
console.log(C.magenta(' \u2502') + C.bold(' \u2726 ClaudMax Setup ') + C.magenta('\u2502'));
|
|
319
|
+
console.log(C.magenta(' \u2570\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\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\u2510'));
|
|
320
|
+
console.log('');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function printHelp() {
|
|
324
|
+
console.log(`
|
|
325
|
+
${C.bold('Usage:')} npx claudmax [options]
|
|
326
|
+
|
|
327
|
+
${C.bold('Options:')}
|
|
328
|
+
--api-key <key> Your ClaudMax API key (required in non-interactive mode)
|
|
329
|
+
--ide <ides> Comma-separated IDEs: claude-code,vscode,cursor,windsurf,cline,roo
|
|
330
|
+
Or "all" to configure every supported IDE
|
|
331
|
+
Or "auto" to auto-detect installed IDEs (default)
|
|
332
|
+
--skip-mcp Skip MCP server installation
|
|
333
|
+
--verify Verify API key after configuration
|
|
334
|
+
--help, -h Show this help message
|
|
335
|
+
|
|
336
|
+
${C.bold('Examples:')}
|
|
337
|
+
npx claudmax Interactive mode
|
|
338
|
+
npx claudmax --api-key sk-ant-... Configure all detected IDEs
|
|
339
|
+
npx claudmax --api-key sk-ant-... --ide all Configure all IDEs
|
|
340
|
+
npx claudmax --api-key sk-ant-... --ide claude-code,cursor
|
|
341
|
+
npx claudmax --api-key sk-ant-... --ide all --verify
|
|
342
|
+
|
|
343
|
+
${C.bold('Supported IDEs:')}
|
|
344
|
+
claude-code - Claude Code CLI
|
|
345
|
+
vscode - VS Code with Claude extension
|
|
346
|
+
cursor - Cursor AI editor
|
|
347
|
+
windsrf - Windsurf AI editor
|
|
348
|
+
cline - Cline VS Code extension
|
|
349
|
+
roo - Roo Code VS Code extension
|
|
350
|
+
`);
|
|
221
351
|
}
|
|
222
352
|
|
|
223
|
-
// ── MCP
|
|
224
|
-
function
|
|
225
|
-
|
|
353
|
+
// ── MCP install ──────────────────────────────────────────────────────────
|
|
354
|
+
function installMCP() {
|
|
355
|
+
if (flags['skip-mcp']) return;
|
|
356
|
+
process.stdout.write(` ${ARROW} Installing ClaudMax MCP server...`);
|
|
226
357
|
try {
|
|
227
|
-
execSync('npm install -g ' + MCP_PKG, { encoding: 'utf8', timeout: 60000 });
|
|
228
|
-
|
|
229
|
-
return true;
|
|
358
|
+
execSync('npm install -g ' + MCP_PKG, { encoding: 'utf8', timeout: 60000, stdio: 'pipe' });
|
|
359
|
+
process.stdout.write(`${CHECK}\n`);
|
|
230
360
|
} catch (err) {
|
|
231
|
-
const msg = err.message || '';
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
console.log(`${WARN} Permission denied — try: sudo npm install -g ${MCP_PKG}`);
|
|
361
|
+
const msg = (err.stderr || err.message || '').toLowerCase();
|
|
362
|
+
if (msg.includes('eacces') || msg.includes('permission')) {
|
|
363
|
+
process.stdout.write(`${WARN} Permission denied — run: ${C.bold('sudo npm install -g ' + MCP_PKG)}\n`);
|
|
235
364
|
} else {
|
|
236
|
-
|
|
365
|
+
process.stdout.write(`${WARN} npm install failed. Run manually: ${C.bold('npm install -g ' + MCP_PKG)}\n`);
|
|
237
366
|
}
|
|
238
|
-
return false;
|
|
239
367
|
}
|
|
240
368
|
}
|
|
241
369
|
|
|
242
|
-
// ──
|
|
243
|
-
function printBanner() {
|
|
244
|
-
console.log('');
|
|
245
|
-
console.log(C.magenta(' ╔════════════════════════════════════════════════════════════╗'));
|
|
246
|
-
console.log(C.magenta(' ║') + C.bold(' ⚡ ClaudMax Setup ⚡ ') + C.magenta('║'));
|
|
247
|
-
console.log(C.magenta(' ╚════════════════════════════════════════════════════════════╝'));
|
|
248
|
-
console.log('');
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
370
|
+
// ── Main ──────────────────────────────────────────────────────────────────
|
|
252
371
|
async function main() {
|
|
253
|
-
|
|
372
|
+
if (flags.help || flags.h || args.includes('-h')) {
|
|
373
|
+
printHelp();
|
|
374
|
+
process.exit(0);
|
|
375
|
+
}
|
|
376
|
+
|
|
254
377
|
printBanner();
|
|
255
378
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
apiKey = apiKey.trim();
|
|
379
|
+
const rl = createRL();
|
|
380
|
+
const detected = detectIDESilent();
|
|
259
381
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
process.exit(1);
|
|
264
|
-
}
|
|
382
|
+
// ── Parse --ide ──────────────────────────────────────────────────────
|
|
383
|
+
let targetIDEStr = flags.ide || 'auto';
|
|
384
|
+
let selectedIds = [];
|
|
265
385
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
386
|
+
if (targetIDEStr === 'auto') {
|
|
387
|
+
selectedIds = detected;
|
|
388
|
+
if (selectedIds.length > 0) {
|
|
389
|
+
console.log(` ${INFO} Auto-detected IDEs: ${selectedIds.join(', ')}`);
|
|
390
|
+
} else {
|
|
391
|
+
console.log(` ${WARN} No IDEs auto-detected. Use ${C.bold('--ide all')} to configure all.`);
|
|
392
|
+
targetIDEStr = 'all';
|
|
393
|
+
}
|
|
394
|
+
}
|
|
275
395
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
396
|
+
if (targetIDEStr === 'all') {
|
|
397
|
+
selectedIds = IDES.map((i) => i.id);
|
|
398
|
+
} else if (targetIDEStr !== 'auto') {
|
|
399
|
+
selectedIds = targetIDEStr.split(',').map((s) => s.trim()).filter(Boolean);
|
|
279
400
|
}
|
|
280
|
-
console.log('');
|
|
281
|
-
console.log(` ${C.dim("Enter numbers separated by spaces, or 'a' for all:")}`);
|
|
282
401
|
|
|
283
|
-
|
|
284
|
-
let selectedIds = [];
|
|
402
|
+
selectedIds = [...new Set(selectedIds)];
|
|
285
403
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
404
|
+
// ── API key ──────────────────────────────────────────────────────────
|
|
405
|
+
let apiKey = (flags['api-key'] || flags.apiKey || '').trim();
|
|
406
|
+
|
|
407
|
+
if (!apiKey) {
|
|
408
|
+
if (!process.stdin.isTTY) {
|
|
409
|
+
console.log(` ${CROSS} ${C.red('API key required. Use:')} ${C.bold('--api-key sk-ant-...')}\n`);
|
|
410
|
+
rl.close();
|
|
411
|
+
process.exit(1);
|
|
412
|
+
}
|
|
413
|
+
process.stdout.write(` ${C.bold('Enter your ClaudMax API key')}\n`);
|
|
414
|
+
process.stdout.write(` ${C.dim('Get your key at:')} ${C.cyan('https://claudmax.pro/check-usage')}\n\n`);
|
|
415
|
+
while (!apiKey.trim()) {
|
|
416
|
+
apiKey = await ask(rl, ` ${C.bold('API Key:')} `);
|
|
417
|
+
if (!apiKey.trim()) console.log(` ${CROSS} API key cannot be empty.\n`);
|
|
418
|
+
}
|
|
290
419
|
}
|
|
291
420
|
|
|
292
|
-
|
|
421
|
+
apiKey = apiKey.trim();
|
|
422
|
+
process.stdout.write(` ${CHECK} API key set: ${C.dim(apiKey.slice(0, 10) + '...' + apiKey.slice(-4))}\n\n`);
|
|
293
423
|
|
|
294
|
-
// ── Configure IDEs
|
|
424
|
+
// ── Configure IDEs ────────────────────────────────────────────────────
|
|
295
425
|
if (selectedIds.length > 0) {
|
|
426
|
+
process.stdout.write(` ${C.bold('Configuring:')} ${selectedIds.map(id => {
|
|
427
|
+
const ide = IDES.find(i => i.id === id);
|
|
428
|
+
return ide ? ide.name : id;
|
|
429
|
+
}).join(', ')}\n\n`);
|
|
430
|
+
|
|
296
431
|
const selectedIDEs = IDES.filter((ide) => selectedIds.includes(ide.id));
|
|
297
432
|
for (const ide of selectedIDEs) {
|
|
298
433
|
try {
|
|
299
434
|
ide.configure(apiKey);
|
|
300
435
|
} catch (err) {
|
|
301
|
-
|
|
436
|
+
process.stdout.write(` ${CROSS} Failed: ${err.message}\n`);
|
|
302
437
|
}
|
|
303
438
|
}
|
|
304
|
-
console.log('');
|
|
305
439
|
} else {
|
|
306
|
-
console.log(` ${WARN}
|
|
307
|
-
console.log('');
|
|
440
|
+
console.log(` ${WARN} No IDEs selected.\n`);
|
|
308
441
|
}
|
|
309
442
|
|
|
310
|
-
// ── Install MCP server ───────────────────────────────────────────────────
|
|
311
|
-
installMCPServer();
|
|
312
443
|
console.log('');
|
|
313
444
|
|
|
314
|
-
// ──
|
|
315
|
-
|
|
316
|
-
|
|
445
|
+
// ── Install MCP ──────────────────────────────────────────────────────
|
|
446
|
+
installMCP();
|
|
447
|
+
console.log('');
|
|
317
448
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
449
|
+
// ── Verify ──────────────────────────────────────────────────────────
|
|
450
|
+
if (flags.verify || flags.v) {
|
|
451
|
+
process.stdout.write(` ${ARROW} Verifying connection...`);
|
|
452
|
+
const result = await verifyConnection(apiKey);
|
|
453
|
+
if (result.ok) {
|
|
454
|
+
process.stdout.write(`\r ${CHECK} Connected — API key is valid.\n`);
|
|
455
|
+
} else if (result.status === 401) {
|
|
456
|
+
process.stdout.write(`\r ${CROSS} Invalid API key.\n`);
|
|
457
|
+
rl.close();
|
|
458
|
+
process.exit(1);
|
|
459
|
+
} else {
|
|
460
|
+
process.stdout.write(`\r ${WARN} HTTP ${result.status || '?'} — could not verify.\n`);
|
|
461
|
+
}
|
|
462
|
+
console.log('');
|
|
326
463
|
}
|
|
327
464
|
|
|
328
|
-
// ── Summary
|
|
329
|
-
console.log('');
|
|
330
|
-
console.log(C.magenta('
|
|
331
|
-
console.log(C.magenta('
|
|
332
|
-
console.log(
|
|
333
|
-
console.log(C.magenta(' ╚════════════════════════════════════════════════════════════╝'));
|
|
465
|
+
// ── Summary ───────────────────────────────────────────────────────────
|
|
466
|
+
console.log(` ${C.magenta('\u250c\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\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\u2510')}`);
|
|
467
|
+
console.log(` ${C.magenta('\u2502')} ${C.green('\u2713')} Setup complete! ${C.magenta('\u2502')}`);
|
|
468
|
+
console.log(` ${C.magenta('\u2502')} Restart your IDE(s) to apply changes. ${C.magenta('\u2502')}`);
|
|
469
|
+
console.log(` ${C.magenta('\u2514\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\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\u2518')}`);
|
|
334
470
|
console.log('');
|
|
335
471
|
|
|
336
472
|
rl.close();
|
|
337
473
|
}
|
|
338
474
|
|
|
339
475
|
main().catch((err) => {
|
|
340
|
-
console.error(
|
|
476
|
+
console.error(`\n${C.red('\u2717 Fatal error:')} ${err.message}\n`);
|
|
341
477
|
process.exit(1);
|
|
342
478
|
});
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudmax",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "ClaudMax CLI — Configure Claude Code, Cursor, Windsurf, Cline, and Roo Code to use ClaudMax API gateway with one command",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"claudmax": "
|
|
7
|
+
"claudmax": "index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node ./index.js"
|