claudmax 1.0.12 → 1.0.14
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 +152 -219
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -7,25 +7,16 @@ const fs = require('fs');
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const os = require('os');
|
|
9
9
|
const https = require('https');
|
|
10
|
-
const { execSync
|
|
10
|
+
const { execSync } = require('child_process');
|
|
11
11
|
|
|
12
12
|
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
13
|
-
const PKG_NAME = 'ClaudMax';
|
|
14
|
-
const API_BASE = 'https://api.claudmax.pro';
|
|
15
13
|
const MCP_PKG = 'claudmax-mcp';
|
|
14
|
+
const API_BASE = 'https://api.claudmax.pro';
|
|
16
15
|
|
|
17
16
|
const HOME = os.homedir();
|
|
18
17
|
const CONFIG_DIR = path.join(HOME, '.claudmax');
|
|
19
18
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
20
19
|
|
|
21
|
-
// Detect shell profile
|
|
22
|
-
function getShellProfile() {
|
|
23
|
-
const profile = process.env.SHELL?.includes('zsh') !== false
|
|
24
|
-
? path.join(HOME, '.zshrc')
|
|
25
|
-
: path.join(HOME, '.bashrc');
|
|
26
|
-
return profile;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
20
|
// ── Color helpers ─────────────────────────────────────────────────────────────
|
|
30
21
|
const C = {
|
|
31
22
|
reset: '\x1b[0m',
|
|
@@ -84,21 +75,6 @@ function ensureDir(dir) {
|
|
|
84
75
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
85
76
|
}
|
|
86
77
|
|
|
87
|
-
function fileContains(filePath, substring) {
|
|
88
|
-
try {
|
|
89
|
-
return fs.readFileSync(filePath, 'utf8').includes(substring);
|
|
90
|
-
} catch { return false; }
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function appendToFile(filePath, content) {
|
|
94
|
-
const current = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '';
|
|
95
|
-
if (!current.includes(content.trim())) {
|
|
96
|
-
fs.appendFileSync(filePath, '\n' + content + '\n');
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
78
|
// ── Version check helpers ─────────────────────────────────────────────────────
|
|
103
79
|
function getCommandVersion(cmd) {
|
|
104
80
|
try {
|
|
@@ -185,7 +161,7 @@ function installClaudeCLI() {
|
|
|
185
161
|
function verifyConnection(apiKey) {
|
|
186
162
|
return new Promise((resolve) => {
|
|
187
163
|
const options = {
|
|
188
|
-
hostname:
|
|
164
|
+
hostname: API_BASE.replace('https://', ''),
|
|
189
165
|
port: 443,
|
|
190
166
|
path: '/v1/key-status',
|
|
191
167
|
method: 'POST',
|
|
@@ -223,34 +199,32 @@ function verifyConnection(apiKey) {
|
|
|
223
199
|
|
|
224
200
|
// ── System configuration ──────────────────────────────────────────────────────
|
|
225
201
|
function configureSystem(apiKey) {
|
|
226
|
-
console.log(
|
|
202
|
+
console.log(` ${ARROW} ${C.bold('Saving configuration...')}`);
|
|
227
203
|
|
|
228
|
-
//
|
|
204
|
+
// Save config file
|
|
229
205
|
ensureDir(CONFIG_DIR);
|
|
230
206
|
writeJson(CONFIG_FILE, {
|
|
231
207
|
apiKey,
|
|
232
208
|
baseUrl: API_BASE,
|
|
233
209
|
configuredAt: new Date().toISOString(),
|
|
234
|
-
version: '1.0.
|
|
210
|
+
version: '1.0.14',
|
|
235
211
|
});
|
|
236
|
-
console.log(`
|
|
237
|
-
console.log(`
|
|
238
|
-
|
|
239
|
-
return null;
|
|
212
|
+
console.log(` ${CHECK} Config saved to ${C.magenta('~/.claudmax/config.json')}`);
|
|
213
|
+
console.log(` ${INFO} ${C.dim('Claude Code CLI reads settings.json — no shell profile modification needed.')}`);
|
|
240
214
|
}
|
|
241
215
|
|
|
242
216
|
// ── Claude Code CLI configuration ─────────────────────────────────────────────
|
|
243
217
|
function configureClaudeCLI(apiKey) {
|
|
244
|
-
console.log(
|
|
218
|
+
console.log(` ${ARROW} ${C.bold('Claude Code CLI...')}`);
|
|
245
219
|
|
|
246
220
|
const settingsPath = path.join(HOME, '.claude', 'settings.json');
|
|
247
221
|
const dotClaudePath = path.join(HOME, '.claude.json');
|
|
248
222
|
|
|
249
223
|
ensureDir(path.dirname(settingsPath));
|
|
250
224
|
|
|
251
|
-
// settings.json
|
|
252
|
-
const
|
|
253
|
-
|
|
225
|
+
// settings.json
|
|
226
|
+
const settings = readJson(settingsPath) || {};
|
|
227
|
+
deepMerge(settings, {
|
|
254
228
|
env: {
|
|
255
229
|
ANTHROPIC_AUTH_TOKEN: apiKey,
|
|
256
230
|
ANTHROPIC_BASE_URL: `${API_BASE}`,
|
|
@@ -264,7 +238,7 @@ function configureClaudeCLI(apiKey) {
|
|
|
264
238
|
hasCompletedOnboarding: true,
|
|
265
239
|
});
|
|
266
240
|
writeJson(settingsPath, settings);
|
|
267
|
-
console.log(`
|
|
241
|
+
console.log(` ${CHECK} Claude Code settings ${C.dim('~/.claude/settings.json')}`);
|
|
268
242
|
|
|
269
243
|
// .claude.json (MCP servers)
|
|
270
244
|
const dotClaude = readJson(dotClaudePath) || {};
|
|
@@ -278,7 +252,7 @@ function configureClaudeCLI(apiKey) {
|
|
|
278
252
|
},
|
|
279
253
|
};
|
|
280
254
|
writeJson(dotClaudePath, dotClaude);
|
|
281
|
-
console.log(`
|
|
255
|
+
console.log(` ${CHECK} MCP server registered ${C.dim('~/.claude.json')}`);
|
|
282
256
|
}
|
|
283
257
|
|
|
284
258
|
// ── IDE configurators ────────────────────────────────────────────────────────
|
|
@@ -294,14 +268,12 @@ function getVSCodeSettingsPath() {
|
|
|
294
268
|
}
|
|
295
269
|
|
|
296
270
|
function configureVSCodeClaude(apiKey) {
|
|
297
|
-
console.log(`\n${ARROW} ${C.bold('Configuring VS Code Claude Extension...')}`);
|
|
298
271
|
configureClaudeCLI(apiKey);
|
|
299
|
-
console.log(`
|
|
272
|
+
console.log(` ${INFO} Claude extension auto-detects Claude Code settings. ${C.dim('Restart VS Code after setup.')}`);
|
|
300
273
|
}
|
|
301
274
|
|
|
302
275
|
function configureCursor(apiKey) {
|
|
303
|
-
console.log(
|
|
304
|
-
|
|
276
|
+
console.log(` ${ARROW} ${C.bold('Cursor...')}`);
|
|
305
277
|
const mcpPath = path.join(HOME, '.cursor', 'mcp.json');
|
|
306
278
|
ensureDir(path.dirname(mcpPath));
|
|
307
279
|
const existing = readJson(mcpPath) || {};
|
|
@@ -315,14 +287,13 @@ function configureCursor(apiKey) {
|
|
|
315
287
|
},
|
|
316
288
|
};
|
|
317
289
|
writeJson(mcpPath, existing);
|
|
318
|
-
console.log(`
|
|
319
|
-
console.log(`
|
|
320
|
-
console.log(`
|
|
290
|
+
console.log(` ${CHECK} Cursor configured ${C.dim('~/.cursor/mcp.json')}`);
|
|
291
|
+
console.log(` ${INFO} ${C.dim('Add model in Cursor: Settings > Models > Add custom model')}`);
|
|
292
|
+
console.log(` ${C.dim('Base URL: https://api.claudmax.pro/v1/chat')}`);
|
|
321
293
|
}
|
|
322
294
|
|
|
323
295
|
function configureWindsurf(apiKey) {
|
|
324
|
-
console.log(
|
|
325
|
-
|
|
296
|
+
console.log(` ${ARROW} ${C.bold('Windsurf...')}`);
|
|
326
297
|
const mcpPath = path.join(HOME, '.windsurf', 'mcp.json');
|
|
327
298
|
ensureDir(path.dirname(mcpPath));
|
|
328
299
|
const existing = readJson(mcpPath) || {};
|
|
@@ -336,13 +307,13 @@ function configureWindsurf(apiKey) {
|
|
|
336
307
|
},
|
|
337
308
|
};
|
|
338
309
|
writeJson(mcpPath, existing);
|
|
339
|
-
console.log(`
|
|
340
|
-
console.log(`
|
|
341
|
-
console.log(`
|
|
310
|
+
console.log(` ${CHECK} Windsurf configured ${C.dim('~/.windsurf/mcp.json')}`);
|
|
311
|
+
console.log(` ${INFO} ${C.dim('Set base URL in Windsurf: Settings > AI Provider')}`);
|
|
312
|
+
console.log(` ${C.dim('https://api.claudmax.pro/v1/chat')}`);
|
|
342
313
|
}
|
|
343
314
|
|
|
344
315
|
function configureCline(apiKey) {
|
|
345
|
-
console.log(
|
|
316
|
+
console.log(` ${ARROW} ${C.bold('Cline...')}`);
|
|
346
317
|
const settingsPath = getVSCodeSettingsPath();
|
|
347
318
|
ensureDir(path.dirname(settingsPath));
|
|
348
319
|
const existing = readJson(settingsPath) || {};
|
|
@@ -352,11 +323,11 @@ function configureCline(apiKey) {
|
|
|
352
323
|
'cline.apiKey': apiKey,
|
|
353
324
|
});
|
|
354
325
|
writeJson(settingsPath, existing);
|
|
355
|
-
console.log(`
|
|
326
|
+
console.log(` ${CHECK} Cline configured ${C.dim(settingsPath.replace(HOME, '~'))}`);
|
|
356
327
|
}
|
|
357
328
|
|
|
358
329
|
function configureRooCode(apiKey) {
|
|
359
|
-
console.log(
|
|
330
|
+
console.log(` ${ARROW} ${C.bold('Roo Code...')}`);
|
|
360
331
|
const settingsPath = getVSCodeSettingsPath();
|
|
361
332
|
ensureDir(path.dirname(settingsPath));
|
|
362
333
|
const existing = readJson(settingsPath) || {};
|
|
@@ -366,20 +337,18 @@ function configureRooCode(apiKey) {
|
|
|
366
337
|
'roo-cline.apiKey': apiKey,
|
|
367
338
|
});
|
|
368
339
|
writeJson(settingsPath, existing);
|
|
369
|
-
console.log(`
|
|
340
|
+
console.log(` ${CHECK} Roo Code configured ${C.dim(settingsPath.replace(HOME, '~'))}`);
|
|
370
341
|
}
|
|
371
342
|
|
|
372
343
|
// ── MCP server install ───────────────────────────────────────────────────────
|
|
373
344
|
function installMCPServer() {
|
|
374
|
-
console.log(
|
|
345
|
+
console.log(` ${ARROW} ${C.bold('Installing MCP server...')}`);
|
|
375
346
|
try {
|
|
376
347
|
execSync('npm install -g ' + MCP_PKG, { stdio: 'pipe', timeout: 60000 });
|
|
377
|
-
console.log(`
|
|
348
|
+
console.log(` ${CHECK} ${C.green('MCP server installed')}`);
|
|
378
349
|
return true;
|
|
379
350
|
} catch (err) {
|
|
380
|
-
|
|
381
|
-
console.log(` ${WARN} Could not install ${MCP_PKG}: ${C.dim(String(errMsg).trim())}`);
|
|
382
|
-
console.log(` ${INFO} Install manually later: ${C.bold('npm install -g ' + MCP_PKG)}`);
|
|
351
|
+
console.log(` ${WARN} ${C.yellow('Could not install MCP server. Run manually:')} ${C.bold('npm install -g ' + MCP_PKG)}`);
|
|
383
352
|
return false;
|
|
384
353
|
}
|
|
385
354
|
}
|
|
@@ -387,122 +356,46 @@ function installMCPServer() {
|
|
|
387
356
|
// ── Banner ────────────────────────────────────────────────────────────────────
|
|
388
357
|
function printBanner() {
|
|
389
358
|
console.log('');
|
|
390
|
-
console.log(C.
|
|
391
|
-
console.log(C.
|
|
392
|
-
console.log(C.
|
|
359
|
+
console.log(C.purple(' ╔════════════════════════════════════════════════════════════════╗'));
|
|
360
|
+
console.log(C.purple(' ║') + C.bold(' ') + C.purple('║'));
|
|
361
|
+
console.log(C.purple(' ║') + C.bold(' ⚡ ClaudMax CLI Setup Wizard ⚡ ') + C.purple('║'));
|
|
362
|
+
console.log(C.purple(' ║') + C.dim(' One-command setup for Claude API ') + C.purple('║'));
|
|
363
|
+
console.log(C.purple(' ║') + C.bold(' ') + C.purple('║'));
|
|
364
|
+
console.log(C.purple(' ╚════════════════════════════════════════════════════════════════╝'));
|
|
393
365
|
console.log('');
|
|
394
366
|
}
|
|
395
367
|
|
|
396
|
-
// ──
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const config = readJson(CONFIG_FILE);
|
|
403
|
-
if (!config || !config.apiKey) {
|
|
404
|
-
console.log(`\n${WARN} No API key configured. Run ${C.bold('npx claudmax')} first.\n`);
|
|
405
|
-
process.exit(1);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
console.log(`\n${C.magenta('\u2554' + '\u2550'.repeat(48) + '\u2557')}`);
|
|
409
|
-
console.log(C.magenta('\u2551') + C.bold(' \u2726 ClaudMax Status \u2726 ') + C.magenta('\u2551'));
|
|
410
|
-
console.log(C.magenta('\u255A' + '\u2550'.repeat(48) + '\u255D'));
|
|
368
|
+
// ── Step header ────────────────────────────────────────────────────────────────
|
|
369
|
+
function printStep(stepNum, title) {
|
|
370
|
+
console.log('');
|
|
371
|
+
console.log(C.bold(' ┌─────────────────────────────────────────────────────────┐'));
|
|
372
|
+
console.log(C.bold(' │ ') + C.purple('Step ' + stepNum) + C.bold(' ' + title.padEnd(42) + '│'));
|
|
373
|
+
console.log(C.bold(' └─────────────────────────────────────────────────────────┘'));
|
|
411
374
|
console.log('');
|
|
412
|
-
|
|
413
|
-
// Fetch key status
|
|
414
|
-
const body = JSON.stringify({ apiKey: config.apiKey });
|
|
415
|
-
const postData = Buffer.from(body);
|
|
416
|
-
|
|
417
|
-
const options = {
|
|
418
|
-
hostname: 'api.claudmax.pro',
|
|
419
|
-
port: 443,
|
|
420
|
-
path: '/v1/key-status',
|
|
421
|
-
method: 'POST',
|
|
422
|
-
headers: {
|
|
423
|
-
'Content-Type': 'application/json',
|
|
424
|
-
'Content-Length': postData.length,
|
|
425
|
-
'User-Agent': 'ClaudMax-CLI/1.0.0',
|
|
426
|
-
},
|
|
427
|
-
timeout: 15000,
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
return new Promise((resolve) => {
|
|
431
|
-
const req = https.request(options, (res) => {
|
|
432
|
-
let data = '';
|
|
433
|
-
res.on('data', (chunk) => { data += chunk; });
|
|
434
|
-
res.on('end', () => {
|
|
435
|
-
try {
|
|
436
|
-
const json = JSON.parse(data);
|
|
437
|
-
if (res.statusCode === 200) {
|
|
438
|
-
const pct = json.tokensLimit > 0 ? Math.min(100, (json.tokensUsed / json.tokensLimit) * 100) : 0;
|
|
439
|
-
const tierColors = { free: C.dim('Gray'), '5x': C.cyan('Blue'), '20x': C.magenta('Purple'), unlimited: C.green('Gold') };
|
|
440
|
-
console.log(` ${CHECK} ${C.green('API Key is active')}`);
|
|
441
|
-
console.log(` ${INFO} Name: ${C.bold(json.name || 'ClaudMax Key')}`);
|
|
442
|
-
console.log(` ${INFO} Prefix: ${C.magenta(json.prefix)}••••••`);
|
|
443
|
-
console.log(` ${INFO} Tier: ${tierColors[json.tier] || C.dim('Free')} (${C.bold(json.tier?.toUpperCase() || 'FREE')})`);
|
|
444
|
-
console.log(` ${INFO} Status: ${json.isActive ? C.green('Active') : C.red('Inactive')}`);
|
|
445
|
-
console.log('');
|
|
446
|
-
console.log(` ${C.bold('Usage (5h window):')}`);
|
|
447
|
-
const barLen = 30;
|
|
448
|
-
const filled = Math.round((pct / 100) * barLen);
|
|
449
|
-
const bar = C.purple('\u2588'.repeat(filled)) + C.dim('\u2591'.repeat(barLen - filled));
|
|
450
|
-
console.log(` ${bar} ${C.dim(`${pct.toFixed(1)}%`)}`);
|
|
451
|
-
console.log(` ${INFO} Requests: ${C.cyan(json.requestsUsed?.toLocaleString() || 0)} / ${C.cyan(json.requestsLimit?.toLocaleString() || 'N/A')}`);
|
|
452
|
-
console.log(` ${INFO} Tokens: ${C.cyan((json.tokensUsed || 0).toLocaleString())} / ${C.cyan((json.tokensLimit || 0).toLocaleString())}`);
|
|
453
|
-
console.log('');
|
|
454
|
-
if (json.windowResetAt) {
|
|
455
|
-
const reset = new Date(json.windowResetAt);
|
|
456
|
-
console.log(` ${INFO} Resets: ${C.dim(reset.toLocaleString())}`);
|
|
457
|
-
}
|
|
458
|
-
} else {
|
|
459
|
-
console.log(` ${CROSS} ${C.red('Failed to fetch status: ' + (json.error || `HTTP ${res.statusCode}`))}`);
|
|
460
|
-
}
|
|
461
|
-
} catch {
|
|
462
|
-
console.log(` ${CROSS} ${C.red('Failed to parse response')}`);
|
|
463
|
-
}
|
|
464
|
-
console.log('');
|
|
465
|
-
resolve();
|
|
466
|
-
});
|
|
467
|
-
});
|
|
468
|
-
req.on('error', (err) => {
|
|
469
|
-
console.log(` ${WARN} ${C.yellow('Network error: ' + err.message)}`);
|
|
470
|
-
console.log('');
|
|
471
|
-
resolve();
|
|
472
|
-
});
|
|
473
|
-
req.on('timeout', () => { req.destroy(); console.log(` ${CROSS} ${C.red('Request timed out')}\n`); resolve(); });
|
|
474
|
-
req.write(postData);
|
|
475
|
-
req.end();
|
|
476
|
-
});
|
|
477
375
|
}
|
|
478
376
|
|
|
479
|
-
// ──
|
|
480
|
-
function
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
console.log(
|
|
377
|
+
// ── Section box ────────────────────────────────────────────────────────────────
|
|
378
|
+
function printBox(lines) {
|
|
379
|
+
const maxLen = Math.max(...lines.map(l => l.length), 40);
|
|
380
|
+
const top = ' ' + C.dim('\u250C' + '\u2500'.repeat(maxLen + 4) + '\u2510');
|
|
381
|
+
const bot = ' ' + C.dim('\u2514' + '\u2500'.repeat(maxLen + 4) + '\u2518');
|
|
382
|
+
console.log(top);
|
|
383
|
+
lines.forEach(line => {
|
|
384
|
+
const padded = line + ' '.repeat(maxLen - line.length);
|
|
385
|
+
console.log(' ' + C.dim('\u2502') + ' ' + padded + ' ' + C.dim('\u2502'));
|
|
386
|
+
});
|
|
387
|
+
console.log(bot);
|
|
485
388
|
console.log('');
|
|
486
389
|
}
|
|
487
390
|
|
|
488
391
|
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
489
392
|
async function main() {
|
|
490
|
-
if (command === 'status') {
|
|
491
|
-
await showStatus();
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
if (command === '--help' || command === '-h') {
|
|
495
|
-
showHelp();
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Default: interactive setup wizard
|
|
500
393
|
const rl = createRL();
|
|
501
394
|
|
|
502
395
|
printBanner();
|
|
503
396
|
|
|
504
397
|
// ── Step 1: Check dependencies ─────────────────────────────────────────────
|
|
505
|
-
|
|
398
|
+
printStep(1, 'System Dependencies');
|
|
506
399
|
|
|
507
400
|
let nodeOk = isCommandAvailable('node');
|
|
508
401
|
let gitOk = isCommandAvailable('git');
|
|
@@ -517,56 +410,120 @@ async function main() {
|
|
|
517
410
|
for (const dep of deps) {
|
|
518
411
|
if (dep.ok) {
|
|
519
412
|
const version = getCommandVersion(dep.cmd);
|
|
520
|
-
console.log(`
|
|
413
|
+
console.log(` ${CHECK} ${C.green(dep.label)} ${C.dim(version || '')}`);
|
|
521
414
|
} else {
|
|
522
|
-
console.log(`
|
|
415
|
+
console.log(` ${CROSS} ${C.red(dep.label)} ${C.dim('not found')}`);
|
|
523
416
|
}
|
|
524
417
|
}
|
|
525
418
|
|
|
526
|
-
// Install missing deps
|
|
527
419
|
if (!nodeOk) {
|
|
528
420
|
if (!installDep('Node.js', installNode)) {
|
|
529
|
-
console.log(`\n${C.red('Node.js installation failed. Please install manually from https://nodejs.org')}`);
|
|
421
|
+
console.log(`\n ${CROSS} ${C.red('Node.js installation failed. Please install manually from https://nodejs.org')}`);
|
|
530
422
|
} else {
|
|
531
423
|
nodeOk = isCommandAvailable('node');
|
|
532
424
|
}
|
|
533
425
|
}
|
|
534
426
|
|
|
535
|
-
|
|
536
|
-
|
|
427
|
+
if (nodeOk && !gitOk) {
|
|
428
|
+
if (!installDep('Git', installGit)) {
|
|
429
|
+
console.log(`\n ${WARN} ${C.yellow('Git installation failed. You can install it manually.')}`);
|
|
430
|
+
} else {
|
|
431
|
+
gitOk = isCommandAvailable('git');
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (nodeOk && !claudeOk) {
|
|
436
|
+
const installClaude = await ask(rl, `\n ${WARN} ${C.yellow('Claude CLI not found. Install it?')} ${C.bold('[Y/n]: ')}`);
|
|
437
|
+
if (!installClaude || installClaude.toLowerCase() === 'y' || installClaude.toLowerCase() === 'yes') {
|
|
438
|
+
if (!installDep('Claude CLI', installClaudeCLI)) {
|
|
439
|
+
console.log(`\n ${WARN} ${C.yellow('Claude CLI installation failed. Run: npm i -g @anthropic-ai/claude-code')}`);
|
|
440
|
+
} else {
|
|
441
|
+
claudeOk = isCommandAvailable('claude');
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// ── Step 2: API key prompt ───────────────────────────────────────────────
|
|
447
|
+
printStep(2, 'API Key Configuration');
|
|
448
|
+
|
|
449
|
+
printBox([
|
|
450
|
+
C.bold(' Enter your API key to get started.'),
|
|
451
|
+
'',
|
|
452
|
+
|
|
453
|
+
]);
|
|
537
454
|
|
|
538
455
|
let apiKey = '';
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
456
|
+
let attempts = 0;
|
|
457
|
+
const maxAttempts = 3;
|
|
458
|
+
while (!apiKey.trim() && attempts < maxAttempts) {
|
|
459
|
+
if (attempts > 0) {
|
|
542
460
|
console.log(` ${CROSS} ${C.red('API key cannot be empty. Please try again.')}`);
|
|
543
461
|
}
|
|
462
|
+
apiKey = await ask(rl, ` ${C.bold('API Key')}: `);
|
|
463
|
+
attempts++;
|
|
544
464
|
}
|
|
545
465
|
apiKey = apiKey.trim();
|
|
546
|
-
console.log('');
|
|
547
466
|
|
|
548
|
-
|
|
467
|
+
if (!apiKey) {
|
|
468
|
+
console.log(`\n ${CROSS} ${C.red('No API key provided. Run')} ${C.bold('npx claudmax')} ${C.red('to try again.')}`);
|
|
469
|
+
rl.close();
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Mask the displayed key
|
|
474
|
+
const maskedKey = apiKey.length > 8
|
|
475
|
+
? apiKey.slice(0, 4) + '\u2022'.repeat(apiKey.length - 8) + apiKey.slice(-4)
|
|
476
|
+
: '\u2022'.repeat(apiKey.length);
|
|
477
|
+
console.log(` ${CHECK} ${C.green('Key received')} ${C.dim(maskedKey)}`);
|
|
478
|
+
|
|
479
|
+
// ── Step 3: Verify API key ───────────────────────────────────────────────
|
|
480
|
+
printStep(3, 'Validating Credentials');
|
|
481
|
+
|
|
482
|
+
process.stdout.write(` ${C.dim('Verifying...')} `);
|
|
483
|
+
const result = await verifyConnection(apiKey);
|
|
484
|
+
|
|
485
|
+
if (result.ok) {
|
|
486
|
+
if (result.status === 401) {
|
|
487
|
+
console.log(`\n ${CROSS} ${C.red('Invalid API key. Please check your key and try again.')}`);
|
|
488
|
+
console.log(` ${INFO} ${C.dim('Get a valid key at: https://claudmax.pro/admin/dashboard')}`);
|
|
489
|
+
rl.close();
|
|
490
|
+
process.exit(1);
|
|
491
|
+
} else {
|
|
492
|
+
const data = result.data;
|
|
493
|
+
const tier = (data.tier || 'free').toUpperCase();
|
|
494
|
+
const limit = data.requestsLimit?.toLocaleString() || 'N/A';
|
|
495
|
+
const tokenLimit = data.tokensLimit ? (data.tokensLimit >= 1e9 ? 'Unlimited' : (data.tokensLimit / 1e6).toFixed(0) + 'M tokens/5h') : 'N/A';
|
|
496
|
+
console.log(`\n ${CHECK} ${C.green('Authentication successful')}`);
|
|
497
|
+
console.log(` ${C.dim('Plan:')} ${C.magenta(tier)} ${C.dim('Requests:')} ${C.cyan(limit + '/5h')} ${C.dim('Tokens:')} ${C.cyan(tokenLimit)}`);
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
console.log(`\n ${WARN} ${C.yellow('Could not verify (network error). Continuing anyway...')}`);
|
|
501
|
+
console.log(` ${INFO} ${C.dim(result.error || 'Connection failed')}`);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// ── Step 4: Configure system ──────────────────────────────────────────────
|
|
505
|
+
printStep(4, 'System Configuration');
|
|
549
506
|
configureSystem(apiKey);
|
|
550
507
|
|
|
551
|
-
// ── Step
|
|
552
|
-
|
|
508
|
+
// ── Step 5: IDE selection ─────────────────────────────────────────────────
|
|
509
|
+
printStep(5, 'IDE Configuration');
|
|
553
510
|
|
|
554
511
|
const IDES = [
|
|
555
|
-
{ id: 1, name: 'Claude Code
|
|
512
|
+
{ id: 1, name: 'Claude Code CLI', shortName: 'Claude Code', configure: configureClaudeCLI },
|
|
556
513
|
{ id: 2, name: 'VS Code (Claude Extension)', shortName: 'VS Code', configure: configureVSCodeClaude },
|
|
557
|
-
{ id: 3, name: 'Cursor',
|
|
558
|
-
{ id: 4, name: 'Windsurf',
|
|
514
|
+
{ id: 3, name: 'Cursor', shortName: 'Cursor', configure: configureCursor },
|
|
515
|
+
{ id: 4, name: 'Windsurf', shortName: 'Windsurf', configure: configureWindsurf },
|
|
559
516
|
{ id: 5, name: 'Cline (VS Code Extension)', shortName: 'Cline', configure: configureCline },
|
|
560
517
|
{ id: 6, name: 'Roo Code (VS Code Extension)', shortName: 'Roo Code', configure: configureRooCode },
|
|
561
518
|
];
|
|
562
519
|
|
|
563
520
|
for (const ide of IDES) {
|
|
564
|
-
console.log(`
|
|
521
|
+
console.log(` ${C.purple('[' + ide.id + ']')} ${ide.name}`);
|
|
565
522
|
}
|
|
566
523
|
console.log('');
|
|
567
|
-
console.log(`
|
|
524
|
+
console.log(` ${C.dim("Enter numbers separated by spaces (e.g. 1 3 4), " + C.bold("'a'") + " for all, or press Enter to skip")}`);
|
|
568
525
|
|
|
569
|
-
const choice = await ask(rl, `
|
|
526
|
+
const choice = await ask(rl, ` ${C.bold('Your choice')}: `);
|
|
570
527
|
let selectedIds = [];
|
|
571
528
|
|
|
572
529
|
if (choice.trim().toLowerCase() === 'a') {
|
|
@@ -575,66 +532,42 @@ async function main() {
|
|
|
575
532
|
selectedIds = choice.trim().split(/[\s,]+/).map(Number).filter((n) => n >= 1 && n <= IDES.length);
|
|
576
533
|
}
|
|
577
534
|
|
|
578
|
-
console.log('');
|
|
579
|
-
|
|
580
|
-
// ── Configure selected IDEs ──────────────────────────────────────────────
|
|
581
535
|
if (selectedIds.length > 0) {
|
|
582
536
|
const selectedIDEs = IDES.filter((ide) => selectedIds.includes(ide.id));
|
|
583
537
|
for (const ide of selectedIDEs) {
|
|
584
538
|
try {
|
|
585
539
|
ide.configure(apiKey);
|
|
586
540
|
} catch (err) {
|
|
587
|
-
console.log(`
|
|
541
|
+
console.log(` ${CROSS} ${C.red('Failed to configure')} ${ide.name}: ${err.message}`);
|
|
588
542
|
}
|
|
589
543
|
}
|
|
590
544
|
} else {
|
|
591
|
-
console.log(`
|
|
545
|
+
console.log(` ${WARN} ${C.yellow('No IDEs selected. Configure them manually later with')} ${C.bold('claudmax configure')}`);
|
|
592
546
|
}
|
|
593
547
|
|
|
594
|
-
// ── Install MCP server
|
|
548
|
+
// ── Step 6: Install MCP server ───────────────────────────────────────────
|
|
549
|
+
printStep(6, 'MCP Server Setup');
|
|
595
550
|
installMCPServer();
|
|
596
551
|
|
|
597
|
-
// ── Verify connection ─────────────────────────────────────────────────────
|
|
598
|
-
console.log(`\n${ARROW} ${C.bold('Verifying connection to ClaudMax API...')}`);
|
|
599
|
-
const result = await verifyConnection(apiKey);
|
|
600
|
-
|
|
601
|
-
if (result.ok) {
|
|
602
|
-
const data = result.data;
|
|
603
|
-
if (result.status === 401) {
|
|
604
|
-
console.log(` ${CROSS} ${C.red('Invalid API key. Please check and try again.')}`);
|
|
605
|
-
console.log(` ${INFO} ${C.dim(data.error || 'Authentication failed')}`);
|
|
606
|
-
rl.close();
|
|
607
|
-
process.exit(1);
|
|
608
|
-
} else {
|
|
609
|
-
console.log(` ${CHECK} ${C.green('Connected — API key is valid.')}`);
|
|
610
|
-
const tier = (data.tier || 'free').toUpperCase();
|
|
611
|
-
const limit = data.requestsLimit?.toLocaleString() || 'N/A';
|
|
612
|
-
const tokenLimit = data.tokensLimit ? (data.tokensLimit >= 1e9 ? 'Unlimited' : (data.tokensLimit / 1e6).toFixed(0) + 'M tokens/5h') : 'N/A';
|
|
613
|
-
console.log(` ${INFO} Plan: ${C.magenta(tier)} | Requests: ${C.cyan(limit)} | Tokens: ${C.cyan(tokenLimit)}`);
|
|
614
|
-
}
|
|
615
|
-
} else {
|
|
616
|
-
console.log(` ${WARN} ${C.yellow('Could not verify API key (network error).')}`);
|
|
617
|
-
console.log(` ${INFO} ${C.dim(result.error || 'Connection failed')}`);
|
|
618
|
-
}
|
|
619
|
-
|
|
620
552
|
// ── Summary ───────────────────────────────────────────────────────────────
|
|
621
553
|
console.log('');
|
|
622
|
-
console.log(C.
|
|
623
|
-
console.log(C.
|
|
624
|
-
console.log(C.
|
|
625
|
-
console.log(
|
|
554
|
+
console.log(C.green(' \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\u2510'));
|
|
555
|
+
console.log(C.green(' \u2502') + C.bold(' \u2726 Setup Complete! \u2726 ') + C.green('\u2502'));
|
|
556
|
+
console.log(C.green(' \u251C\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\u2534'));
|
|
557
|
+
console.log('');
|
|
558
|
+
console.log(` ${INFO} ${C.bold('Next steps:')}`);
|
|
559
|
+
console.log(` 1. ${C.dim('Restart your IDE(s) to apply settings.')}`);
|
|
560
|
+
console.log(` 2. ${C.dim('Test it:')} ${C.bold('claude --version')}`);
|
|
561
|
+
console.log(` 3. ${C.dim('Start chatting:')} ${C.bold('claude')}`);
|
|
562
|
+
console.log(` 4. ${C.dim('Check usage:')} ${C.bold('npx claudmax status')}`);
|
|
626
563
|
console.log('');
|
|
627
|
-
console.log(`
|
|
628
|
-
console.log(` 1. ${C.dim('Restart your IDE(s) to apply.')}`);
|
|
629
|
-
console.log(` 2. ${C.dim('Verify with:')} ${C.bold('claude --version')}`);
|
|
630
|
-
console.log(` 3. ${C.dim('Start chatting:')} ${C.bold('claude')}`);
|
|
631
|
-
console.log(` 4. ${C.dim('Check usage:')} ${C.bold('npx claudmax status')}`);
|
|
564
|
+
console.log(` ${INFO} Config saved at: ${C.magenta(CONFIG_FILE)}`);
|
|
632
565
|
console.log('');
|
|
633
566
|
|
|
634
567
|
rl.close();
|
|
635
568
|
}
|
|
636
569
|
|
|
637
570
|
main().catch((err) => {
|
|
638
|
-
console.error('\n' + C.red('\u2717 Fatal error: ' +
|
|
571
|
+
console.error('\n' + C.red('\u2717 Fatal error: ' + err.message));
|
|
639
572
|
process.exit(1);
|
|
640
573
|
});
|
package/package.json
CHANGED