claudmax 1.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/bin/claudmax.js +291 -0
- package/index.js +583 -0
- package/package.json +33 -0
package/bin/claudmax.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ClaudMax CLI
|
|
5
|
+
* One command: npx claudmax
|
|
6
|
+
* Setup in seconds, start building immediately.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const { execSync } = require('child_process');
|
|
12
|
+
|
|
13
|
+
// ─── Constants ────────────────────────────────────────────────
|
|
14
|
+
const BOLD = '\x1b[1m';
|
|
15
|
+
const ORANGE = '\x1b[33m';
|
|
16
|
+
const GREEN = '\x1b[32m';
|
|
17
|
+
const RED = '\x1b[31m';
|
|
18
|
+
const DIM = '\x1b[2m';
|
|
19
|
+
const RESET = '\x1b[0m';
|
|
20
|
+
const CYAN = '\x1b[36m';
|
|
21
|
+
|
|
22
|
+
const CONFIG_DIR = path.join(process.env.HOME, '.claudmax');
|
|
23
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
24
|
+
const API_BASE = process.env.CLAUDEMAX_API_BASE || 'https://api.claudmax.pro';
|
|
25
|
+
const API_KEY_REGEX = /^sk-cmx_[a-zA-Z0-9]{32}$/;
|
|
26
|
+
|
|
27
|
+
// ─── Helpers ──────────────────────────────────────────────────
|
|
28
|
+
function printBanner() {
|
|
29
|
+
console.log('');
|
|
30
|
+
console.log(`${ORANGE}╔══════════════════════════════════════════════════════════╗${RESET}`);
|
|
31
|
+
console.log(`${ORANGE}║${RESET} ${ORANGE}║${RESET}`);
|
|
32
|
+
console.log(`${ORANGE}║${RESET} ${BOLD}⚡ ClaudMax${RESET} ${ORANGE}║${RESET}`);
|
|
33
|
+
console.log(`${ORANGE}║${RESET} ${ORANGE}║${RESET}`);
|
|
34
|
+
console.log(`${ORANGE}╚══════════════════════════════════════════════════════════╝${RESET}`);
|
|
35
|
+
console.log('');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function printSuccess(msg) {
|
|
39
|
+
console.log(`${GREEN}✓${RESET} ${msg}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function printError(msg) {
|
|
43
|
+
console.log(`${RED}✗${RESET} ${msg}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function printInfo(msg) {
|
|
47
|
+
console.log(`${DIM}${msg}${RESET}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getConfig() {
|
|
51
|
+
if (!fs.existsSync(CONFIG_FILE)) return null;
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function saveConfig(config) {
|
|
60
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
61
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ─── API Calls ────────────────────────────────────────────────
|
|
65
|
+
async function apiStatus(apiKey) {
|
|
66
|
+
try {
|
|
67
|
+
const res = await fetch(`${API_BASE}/api/v1/key-status`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: { 'Content-Type': 'application/json' },
|
|
70
|
+
body: JSON.stringify({ apiKey }),
|
|
71
|
+
});
|
|
72
|
+
return await res.json();
|
|
73
|
+
} catch (err) {
|
|
74
|
+
return { error: `Network error: ${err.message}` };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function apiCreateKey(name, tier) {
|
|
79
|
+
try {
|
|
80
|
+
const res = await fetch(`${API_BASE}/api/admin/keys`, {
|
|
81
|
+
method: 'POST',
|
|
82
|
+
headers: { 'Content-Type': 'application/json' },
|
|
83
|
+
body: JSON.stringify({ name, tier }),
|
|
84
|
+
});
|
|
85
|
+
return await res.json();
|
|
86
|
+
} catch (err) {
|
|
87
|
+
return { error: `Network error: ${err.message}` };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ─── Commands ─────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
async function cmdConfigure() {
|
|
94
|
+
printBanner();
|
|
95
|
+
console.log(`${BOLD}Configure your ClaudMax API key${RESET}`);
|
|
96
|
+
console.log('');
|
|
97
|
+
|
|
98
|
+
const config = getConfig();
|
|
99
|
+
if (config?.apiKey) {
|
|
100
|
+
console.log(`${DIM}Current key: ${config.apiKey.slice(0, 12)}...${RESET}`);
|
|
101
|
+
console.log('');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Prompt for key
|
|
105
|
+
process.stdout.write(`${BOLD}Enter your ClaudMax API key:${RESET}\n`);
|
|
106
|
+
process.stdout.write(`${DIM}API Key (sk-cmx_...): ${RESET}`);
|
|
107
|
+
|
|
108
|
+
const readline = require('readline');
|
|
109
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
110
|
+
const apiKey = await new Promise(resolve => rl.question('', resolve));
|
|
111
|
+
rl.close();
|
|
112
|
+
console.log('');
|
|
113
|
+
|
|
114
|
+
const trimmedKey = apiKey.trim();
|
|
115
|
+
|
|
116
|
+
if (!trimmedKey) {
|
|
117
|
+
printError('API key cannot be empty');
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!API_KEY_REGEX.test(trimmedKey)) {
|
|
122
|
+
printError('Invalid API key format. Keys start with sk-cmx_ followed by 32 characters.');
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Validate key
|
|
127
|
+
process.stdout.write(`${DIM}Validating API key... ${RESET}`);
|
|
128
|
+
const status = await apiStatus(trimmedKey);
|
|
129
|
+
|
|
130
|
+
if (status.error) {
|
|
131
|
+
console.log('');
|
|
132
|
+
printError(`Invalid API key: ${status.error}`);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Save config
|
|
137
|
+
saveConfig({ apiKey: trimmedKey, baseUrl: API_BASE });
|
|
138
|
+
|
|
139
|
+
console.log('');
|
|
140
|
+
printSuccess(`Connected! Configuration saved.`);
|
|
141
|
+
console.log('');
|
|
142
|
+
console.log(`${BOLD}Your key details:${RESET}`);
|
|
143
|
+
console.log(` Name: ${status.name || 'ClaudeMax Key'}`);
|
|
144
|
+
console.log(` Tier: ${(status.tier || 'free').toUpperCase()}`);
|
|
145
|
+
console.log(` Status: ${status.isActive ? `${GREEN}Active${RESET}` : `${RED}Revoked${RESET}`}`);
|
|
146
|
+
console.log('');
|
|
147
|
+
console.log(`${BOLD}Next steps:${RESET}`);
|
|
148
|
+
console.log(` Add these to your shell profile (~/.bashrc or ~/.zshrc):\n`);
|
|
149
|
+
console.log(` ${ORANGE}export ANTHROPIC_BASE_URL=${API_BASE}${RESET}`);
|
|
150
|
+
console.log(` ${ORANGE}export ANTHROPIC_API_KEY=${trimmedKey}${RESET}`);
|
|
151
|
+
console.log('');
|
|
152
|
+
console.log(` Then reload: ${DIM}source ~/.bashrc${RESET}`);
|
|
153
|
+
console.log('');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function cmdStatus() {
|
|
157
|
+
printBanner();
|
|
158
|
+
|
|
159
|
+
const config = getConfig();
|
|
160
|
+
if (!config?.apiKey) {
|
|
161
|
+
printError('No API key configured. Run: claudmax configure');
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
process.stdout.write(`${DIM}Checking status... ${RESET}`);
|
|
166
|
+
const status = await apiStatus(config.apiKey);
|
|
167
|
+
|
|
168
|
+
if (status.error) {
|
|
169
|
+
console.log('');
|
|
170
|
+
printError(`Failed: ${status.error}`);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.log('');
|
|
175
|
+
console.log(`${BOLD}API Key Status${RESET}`);
|
|
176
|
+
console.log(` Name: ${status.name || 'ClaudeMax Key'}`);
|
|
177
|
+
console.log(` Tier: ${(status.tier || 'free').toUpperCase()}`);
|
|
178
|
+
console.log(` Status: ${status.isActive ? `${GREEN}Active${RESET}` : `${RED}Revoked${RESET}`}`);
|
|
179
|
+
console.log(` Requests: ${status.requestsUsed?.toLocaleString() || 0} / ${status.requestsLimit?.toLocaleString() || '∞'}`);
|
|
180
|
+
console.log(` Tokens: ${((status.tokensUsed||0)/1000).toFixed(1)}K / ${((status.tokensLimit||0)/1000).toFixed(1)}K`);
|
|
181
|
+
console.log(` Window: ${status.windowResetAt ? new Date(status.windowResetAt).toLocaleString() : 'N/A'}`);
|
|
182
|
+
console.log(` Created: ${status.createdAt ? new Date(status.createdAt).toLocaleDateString() : 'N/A'}`);
|
|
183
|
+
console.log(` Last Used: ${status.lastUsedAt ? new Date(status.lastUsedAt).toLocaleString() : 'Never'}`);
|
|
184
|
+
console.log('');
|
|
185
|
+
|
|
186
|
+
// Usage bars
|
|
187
|
+
if (status.requestsLimit) {
|
|
188
|
+
const reqPct = Math.min(100, (status.requestsUsed / status.requestsLimit) * 100);
|
|
189
|
+
const bar = '█'.repeat(Math.floor(reqPct / 5)) + '░'.repeat(20 - Math.floor(reqPct / 5));
|
|
190
|
+
const barColor = reqPct > 80 ? RED : reqPct > 60 ? '\x1b[33m' : GREEN;
|
|
191
|
+
console.log(` Requests: [${barColor}${bar}${RESET}] ${reqPct.toFixed(0)}%`);
|
|
192
|
+
}
|
|
193
|
+
console.log('');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function cmdCreate() {
|
|
197
|
+
printBanner();
|
|
198
|
+
console.log(`${BOLD}Create a new API key${RESET}`);
|
|
199
|
+
console.log('');
|
|
200
|
+
|
|
201
|
+
const rl = require('readline').createInterface({ input: process.stdin, output: process.stdout });
|
|
202
|
+
|
|
203
|
+
const ask = (q) => new Promise(res => {
|
|
204
|
+
process.stdout.write(`${q}: `);
|
|
205
|
+
rl.question('', res);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const name = await ask('Key name (e.g. My App)');
|
|
209
|
+
console.log('');
|
|
210
|
+
console.log('Available tiers:');
|
|
211
|
+
console.log(' 1. Free (100 req/5h, 100K tokens/month)');
|
|
212
|
+
console.log(' 2. 5x Max (500 req/5h, 500K tokens/month)');
|
|
213
|
+
console.log(' 3. 20x Max (2000 req/5h, 2M tokens/month)');
|
|
214
|
+
const tierChoice = await ask('Choose tier (1-3)');
|
|
215
|
+
rl.close();
|
|
216
|
+
|
|
217
|
+
const tiers = { '1': 'free', '2': '5x', '3': '20x' };
|
|
218
|
+
const tier = tiers[tierChoice.trim()] || 'free';
|
|
219
|
+
|
|
220
|
+
process.stdout.write(`${DIM}Creating API key... ${RESET}`);
|
|
221
|
+
const result = await apiCreateKey(name || 'ClaudeMax Key', tier);
|
|
222
|
+
|
|
223
|
+
if (result.error || !result.key) {
|
|
224
|
+
console.log('');
|
|
225
|
+
printError(`Failed: ${result.error || 'Unknown error'}`);
|
|
226
|
+
console.log(`${DIM}Tip: You need an active ClaudeMax Pro subscription to create keys.${RESET}`);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
console.log('');
|
|
231
|
+
console.log(`${GREEN}✓ API key created!${RESET}`);
|
|
232
|
+
console.log('');
|
|
233
|
+
console.log(` ${BOLD}${result.key}${RESET}`);
|
|
234
|
+
console.log('');
|
|
235
|
+
console.log(`${RED}⚠ Save this key now — you won't see it again!${RESET}`);
|
|
236
|
+
console.log('');
|
|
237
|
+
|
|
238
|
+
// Auto-configure
|
|
239
|
+
saveConfig({ apiKey: result.key, baseUrl: API_BASE });
|
|
240
|
+
printSuccess('Key saved to ~/.claudmax/config.json');
|
|
241
|
+
console.log('');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function cmdHelp() {
|
|
245
|
+
printBanner();
|
|
246
|
+
console.log(`${BOLD}ClaudMax CLI${RESET} — Claude API gateway setup in seconds\n`);
|
|
247
|
+
console.log(`${BOLD}Usage:${RESET}`);
|
|
248
|
+
console.log(' npx claudmax Open setup wizard');
|
|
249
|
+
console.log(' claudmax configure Configure API key');
|
|
250
|
+
console.log(' claudmax status Check usage & limits');
|
|
251
|
+
console.log(' claudmax create Create new API key');
|
|
252
|
+
console.log(' claudmax help Show this help');
|
|
253
|
+
console.log(`${BOLD}Quick Start:${RESET}`);
|
|
254
|
+
console.log(` ${ORANGE}curl -fsSL https://claudmax.pro/setup.sh | bash${RESET}`);
|
|
255
|
+
console.log(` ${ORANGE}npx claudmax${RESET}\n`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ─── Main ─────────────────────────────────────────────────────
|
|
259
|
+
async function main() {
|
|
260
|
+
const cmd = process.argv[2] || 'configure';
|
|
261
|
+
|
|
262
|
+
switch (cmd) {
|
|
263
|
+
case 'configure':
|
|
264
|
+
case 'setup':
|
|
265
|
+
case 'init':
|
|
266
|
+
await cmdConfigure();
|
|
267
|
+
break;
|
|
268
|
+
case 'status':
|
|
269
|
+
case 'check':
|
|
270
|
+
await cmdStatus();
|
|
271
|
+
break;
|
|
272
|
+
case 'create':
|
|
273
|
+
case 'new':
|
|
274
|
+
await cmdCreate();
|
|
275
|
+
break;
|
|
276
|
+
case 'help':
|
|
277
|
+
case '--help':
|
|
278
|
+
case '-h':
|
|
279
|
+
cmdHelp();
|
|
280
|
+
break;
|
|
281
|
+
default:
|
|
282
|
+
printError(`Unknown command: ${cmd}`);
|
|
283
|
+
console.log(`Run ${ORANGE}claudmax help${RESET} for usage.`);
|
|
284
|
+
process.exit(1);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
main().catch(err => {
|
|
289
|
+
console.error(`${RED}Error:${RESET}`, err.message);
|
|
290
|
+
process.exit(1);
|
|
291
|
+
});
|
package/index.js
ADDED
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const readline = require('readline');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
const https = require('https');
|
|
10
|
+
const { execSync, exec } = require('child_process');
|
|
11
|
+
|
|
12
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
13
|
+
const PKG_NAME = 'ClaudMax';
|
|
14
|
+
const API_BASE = 'https://api.claudmax.pro';
|
|
15
|
+
const MCP_PKG = 'claudmax-mcp';
|
|
16
|
+
|
|
17
|
+
const HOME = os.homedir();
|
|
18
|
+
const CONFIG_DIR = path.join(HOME, '.claudmax');
|
|
19
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
20
|
+
|
|
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
|
+
// ── Color helpers ─────────────────────────────────────────────────────────────
|
|
30
|
+
const C = {
|
|
31
|
+
reset: '\x1b[0m',
|
|
32
|
+
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
33
|
+
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
34
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
35
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
36
|
+
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
37
|
+
blue: (s) => `\x1b[34m${s}\x1b[0m`,
|
|
38
|
+
magenta: (s) => `\x1b[35m${s}\x1b[0m`,
|
|
39
|
+
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
40
|
+
white: (s) => `\x1b[37m${s}\x1b[0m`,
|
|
41
|
+
purple: (s) => `\x1b[38;5;141m${s}\x1b[0m`,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const CHECK = C.green('\u2713');
|
|
45
|
+
const CROSS = C.red('\u2717');
|
|
46
|
+
const WARN = C.yellow('\u26A0');
|
|
47
|
+
const INFO = C.cyan('\u2139');
|
|
48
|
+
const ARROW = C.magenta('\u25b6');
|
|
49
|
+
|
|
50
|
+
// ── Readline helper ────────────────────────────────────────────────────────────
|
|
51
|
+
function createRL() {
|
|
52
|
+
return readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function ask(rl, question) {
|
|
56
|
+
return new Promise((resolve) => rl.question(question, resolve));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ── File helpers ──────────────────────────────────────────────────────────────
|
|
60
|
+
function readJson(filePath) {
|
|
61
|
+
try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
|
|
62
|
+
catch { return null; }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function writeJson(filePath, data) {
|
|
66
|
+
const dir = path.dirname(filePath);
|
|
67
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
68
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function deepMerge(target, source) {
|
|
72
|
+
for (const key of Object.keys(source)) {
|
|
73
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
74
|
+
if (!target[key] || typeof target[key] !== 'object') target[key] = {};
|
|
75
|
+
deepMerge(target[key], source[key]);
|
|
76
|
+
} else {
|
|
77
|
+
target[key] = source[key];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return target;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function ensureDir(dir) {
|
|
84
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
|
|
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
|
+
// ── Version check helpers ─────────────────────────────────────────────────────
|
|
103
|
+
function getCommandVersion(cmd) {
|
|
104
|
+
try {
|
|
105
|
+
const v = execSync(cmd + ' --version', { encoding: 'utf8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
106
|
+
return v.trim().split('\n')[0];
|
|
107
|
+
} catch { return null; }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isCommandAvailable(cmd) {
|
|
111
|
+
try {
|
|
112
|
+
execSync(cmd + ' --version', { stdio: ['pipe', 'pipe', 'pipe'], timeout: 5000 });
|
|
113
|
+
return true;
|
|
114
|
+
} catch { return false; }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── Dependency installers ─────────────────────────────────────────────────────
|
|
118
|
+
function installDep(label, installFn) {
|
|
119
|
+
console.log(`\n${ARROW} ${C.bold('Installing ' + label + '...')}`);
|
|
120
|
+
try {
|
|
121
|
+
installFn();
|
|
122
|
+
console.log(` ${CHECK} ${C.green(label + ' installed successfully.')}`);
|
|
123
|
+
return true;
|
|
124
|
+
} catch (err) {
|
|
125
|
+
console.log(` ${CROSS} ${C.red('Failed to install ' + label + ': ' + (err.message || 'unknown error'))}`);
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function installNode() {
|
|
131
|
+
const platform = process.platform;
|
|
132
|
+
let installCmd;
|
|
133
|
+
|
|
134
|
+
if (platform === 'win32') {
|
|
135
|
+
installCmd = 'winget install OpenJS.NodeJS.LTS';
|
|
136
|
+
} else if (platform === 'darwin') {
|
|
137
|
+
installCmd = 'brew install node';
|
|
138
|
+
} else {
|
|
139
|
+
// Linux
|
|
140
|
+
if (isCommandAvailable('apt-get')) {
|
|
141
|
+
execSync('curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt-get install -y nodejs', { stdio: 'inherit', timeout: 120000 });
|
|
142
|
+
return;
|
|
143
|
+
} else if (isCommandAvailable('yum')) {
|
|
144
|
+
execSync('curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash - && sudo yum install -y nodejs', { stdio: 'inherit', timeout: 120000 });
|
|
145
|
+
return;
|
|
146
|
+
} else if (isCommandAvailable('pacman')) {
|
|
147
|
+
execSync('sudo pacman -S nodejs npm', { stdio: 'inherit', timeout: 120000 });
|
|
148
|
+
return;
|
|
149
|
+
} else {
|
|
150
|
+
throw new Error('No supported package manager found. Please install Node.js manually from https://nodejs.org');
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
execSync(installCmd, { stdio: 'inherit', timeout: 120000 });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function installGit() {
|
|
159
|
+
const platform = process.platform;
|
|
160
|
+
if (platform === 'win32') {
|
|
161
|
+
execSync('winget install Git.Git', { stdio: 'inherit', timeout: 120000 });
|
|
162
|
+
} else if (platform === 'darwin') {
|
|
163
|
+
execSync('brew install git', { stdio: 'inherit', timeout: 120000 });
|
|
164
|
+
} else {
|
|
165
|
+
if (isCommandAvailable('apt-get')) {
|
|
166
|
+
execSync('sudo apt-get update && sudo apt-get install -y git', { stdio: 'inherit', timeout: 120000 });
|
|
167
|
+
} else if (isCommandAvailable('yum')) {
|
|
168
|
+
execSync('sudo yum install -y git', { stdio: 'inherit', timeout: 120000 });
|
|
169
|
+
} else if (isCommandAvailable('pacman')) {
|
|
170
|
+
execSync('sudo pacman -S git', { stdio: 'inherit', timeout: 120000 });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function installClaudeCLI() {
|
|
176
|
+
const platform = process.platform;
|
|
177
|
+
if (platform === 'win32') {
|
|
178
|
+
execSync('npm install -g @anthropic-ai/claude-code', { stdio: 'inherit', timeout: 120000 });
|
|
179
|
+
} else {
|
|
180
|
+
execSync('npm install -g @anthropic-ai/claude-code', { stdio: 'inherit', timeout: 120000 });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ── HTTPS verification ────────────────────────────────────────────────────────
|
|
185
|
+
function verifyConnection(apiKey) {
|
|
186
|
+
return new Promise((resolve) => {
|
|
187
|
+
const options = {
|
|
188
|
+
hostname: 'api.claudmax.pro',
|
|
189
|
+
port: 443,
|
|
190
|
+
path: '/v1/key-status',
|
|
191
|
+
method: 'POST',
|
|
192
|
+
headers: {
|
|
193
|
+
'Content-Type': 'application/json',
|
|
194
|
+
'User-Agent': 'ClaudMax-CLI/1.0.0',
|
|
195
|
+
},
|
|
196
|
+
timeout: 15000,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const req = https.request(options, (res) => {
|
|
200
|
+
let body = '';
|
|
201
|
+
res.on('data', (chunk) => { body += chunk; });
|
|
202
|
+
res.on('end', () => {
|
|
203
|
+
try {
|
|
204
|
+
const data = JSON.parse(body);
|
|
205
|
+
if (res.statusCode === 200 || res.statusCode === 401) {
|
|
206
|
+
resolve({ status: res.statusCode, data, ok: true });
|
|
207
|
+
} else {
|
|
208
|
+
resolve({ status: res.statusCode, error: body, ok: false });
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
resolve({ status: res.statusCode, error: body, ok: false });
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
req.on('error', (err) => resolve({ status: 0, error: err.message, ok: false }));
|
|
217
|
+
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout', ok: false }); });
|
|
218
|
+
|
|
219
|
+
req.write(JSON.stringify({ apiKey }));
|
|
220
|
+
req.end();
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ── System configuration ──────────────────────────────────────────────────────
|
|
225
|
+
function configureSystem(apiKey) {
|
|
226
|
+
console.log(`\n${ARROW} ${C.bold('Configuring system for ' + PKG_NAME + '...')}`);
|
|
227
|
+
|
|
228
|
+
// 1. Save config file
|
|
229
|
+
ensureDir(CONFIG_DIR);
|
|
230
|
+
writeJson(CONFIG_FILE, {
|
|
231
|
+
apiKey,
|
|
232
|
+
baseUrl: API_BASE,
|
|
233
|
+
configuredAt: new Date().toISOString(),
|
|
234
|
+
version: '1.0.0',
|
|
235
|
+
});
|
|
236
|
+
console.log(` ${CHECK} Saved config to ${C.magenta(CONFIG_FILE)}`);
|
|
237
|
+
|
|
238
|
+
// 2. Write environment variables to shell profile
|
|
239
|
+
const profile = getShellProfile();
|
|
240
|
+
const envBlock = [
|
|
241
|
+
'# ClaudMax Configuration',
|
|
242
|
+
`export ANTHROPIC_BASE_URL="${API_BASE}/v1/messages"`,
|
|
243
|
+
`export ANTHROPIC_API_KEY="${apiKey}"`,
|
|
244
|
+
'# Alias for quick access',
|
|
245
|
+
'alias claude="claude"',
|
|
246
|
+
'',
|
|
247
|
+
].join('\n');
|
|
248
|
+
|
|
249
|
+
const appended = appendToFile(profile, envBlock);
|
|
250
|
+
if (appended) {
|
|
251
|
+
console.log(` ${CHECK} Added environment variables to ${C.magenta(profile)}`);
|
|
252
|
+
console.log(` ${INFO} ${C.dim('Run: source ' + profile + ' or restart your terminal')}`);
|
|
253
|
+
} else {
|
|
254
|
+
console.log(` ${INFO} Environment variables already configured in ${C.magenta(profile)}`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Set for current process too
|
|
258
|
+
process.env.ANTHROPIC_BASE_URL = `${API_BASE}/v1/messages`;
|
|
259
|
+
process.env.ANTHROPIC_API_KEY = apiKey;
|
|
260
|
+
|
|
261
|
+
return profile;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ── Claude Code CLI configuration ─────────────────────────────────────────────
|
|
265
|
+
function configureClaudeCLI(apiKey) {
|
|
266
|
+
console.log(`\n${ARROW} ${C.bold('Configuring Claude Code CLI...')}`);
|
|
267
|
+
|
|
268
|
+
const settingsPath = path.join(HOME, '.claude', 'settings.json');
|
|
269
|
+
const dotClaudePath = path.join(HOME, '.claude.json');
|
|
270
|
+
|
|
271
|
+
ensureDir(path.dirname(settingsPath));
|
|
272
|
+
|
|
273
|
+
// settings.json
|
|
274
|
+
const settings = readJson(settingsPath) || {};
|
|
275
|
+
deepMerge(settings, {
|
|
276
|
+
env: {
|
|
277
|
+
ANTHROPIC_AUTH_TOKEN: apiKey,
|
|
278
|
+
ANTHROPIC_BASE_URL: `${API_BASE}/v1/messages`,
|
|
279
|
+
ANTHROPIC_MODEL: 'claude-opus-4-6',
|
|
280
|
+
ANTHROPIC_SMALL_FAST_MODEL: 'claude-haiku-4-5-20251001',
|
|
281
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: 'claude-sonnet-4-6',
|
|
282
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: 'claude-opus-4-6',
|
|
283
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'claude-haiku-4-5-20251001',
|
|
284
|
+
},
|
|
285
|
+
hasCompletedOnboarding: true,
|
|
286
|
+
});
|
|
287
|
+
writeJson(settingsPath, settings);
|
|
288
|
+
console.log(` ${CHECK} Wrote Claude Code settings: ${C.magenta(settingsPath)}`);
|
|
289
|
+
|
|
290
|
+
// .claude.json (MCP servers)
|
|
291
|
+
const dotClaude = readJson(dotClaudePath) || {};
|
|
292
|
+
if (!dotClaude.mcpServers) dotClaude.mcpServers = {};
|
|
293
|
+
dotClaude.mcpServers['ClaudMax'] = {
|
|
294
|
+
command: 'npx',
|
|
295
|
+
args: ['-y', MCP_PKG],
|
|
296
|
+
env: {
|
|
297
|
+
CLAUDMAX_API_KEY: apiKey,
|
|
298
|
+
CLAUDMAX_URL: API_BASE,
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
writeJson(dotClaudePath, dotClaude);
|
|
302
|
+
console.log(` ${CHECK} Registered ClaudMax MCP server: ${C.magenta(dotClaudePath)}`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ── IDE configurators ────────────────────────────────────────────────────────
|
|
306
|
+
function getVSCodeSettingsPath() {
|
|
307
|
+
switch (process.platform) {
|
|
308
|
+
case 'win32':
|
|
309
|
+
return path.join(process.env.APPDATA || path.join(HOME, 'AppData', 'Roaming'), 'Code', 'User', 'settings.json');
|
|
310
|
+
case 'darwin':
|
|
311
|
+
return path.join(HOME, 'Library', 'Application Support', 'Code', 'User', 'settings.json');
|
|
312
|
+
default:
|
|
313
|
+
return path.join(HOME, '.config', 'Code', 'User', 'settings.json');
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function configureVSCodeClaude(apiKey) {
|
|
318
|
+
console.log(`\n${ARROW} ${C.bold('Configuring VS Code Claude Extension...')}`);
|
|
319
|
+
configureClaudeCLI(apiKey);
|
|
320
|
+
console.log(` ${INFO} Claude extension auto-detects Claude Code CLI settings. Restart VS Code after setup.`);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function configureCursor(apiKey) {
|
|
324
|
+
console.log(`\n${ARROW} ${C.bold('Configuring Cursor...')}`);
|
|
325
|
+
|
|
326
|
+
const mcpPath = path.join(HOME, '.cursor', 'mcp.json');
|
|
327
|
+
ensureDir(path.dirname(mcpPath));
|
|
328
|
+
const existing = readJson(mcpPath) || {};
|
|
329
|
+
if (!existing.mcpServers) existing.mcpServers = {};
|
|
330
|
+
existing.mcpServers['ClaudMax'] = {
|
|
331
|
+
command: 'npx',
|
|
332
|
+
args: ['-y', MCP_PKG],
|
|
333
|
+
env: {
|
|
334
|
+
CLAUDMAX_API_KEY: apiKey,
|
|
335
|
+
CLAUDMAX_URL: API_BASE,
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
writeJson(mcpPath, existing);
|
|
339
|
+
console.log(` ${CHECK} Wrote Cursor MCP config: ${C.magenta(mcpPath)}`);
|
|
340
|
+
console.log(` ${INFO} Set base URL in Cursor: Settings > Models > Add custom model`);
|
|
341
|
+
console.log(` ${C.dim('https://api.claudmax.pro/v1/chat')}`);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function configureWindsurf(apiKey) {
|
|
345
|
+
console.log(`\n${ARROW} ${C.bold('Configuring Windsurf...')}`);
|
|
346
|
+
|
|
347
|
+
const mcpPath = path.join(HOME, '.windsurf', 'mcp.json');
|
|
348
|
+
ensureDir(path.dirname(mcpPath));
|
|
349
|
+
const existing = readJson(mcpPath) || {};
|
|
350
|
+
if (!existing.mcpServers) existing.mcpServers = {};
|
|
351
|
+
existing.mcpServers['ClaudMax'] = {
|
|
352
|
+
command: 'npx',
|
|
353
|
+
args: ['-y', MCP_PKG],
|
|
354
|
+
env: {
|
|
355
|
+
CLAUDMAX_API_KEY: apiKey,
|
|
356
|
+
CLAUDMAX_URL: API_BASE,
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
writeJson(mcpPath, existing);
|
|
360
|
+
console.log(` ${CHECK} Wrote Windsurf MCP config: ${C.magenta(mcpPath)}`);
|
|
361
|
+
console.log(` ${INFO} Set base URL in Windsurf: Settings > AI Provider`);
|
|
362
|
+
console.log(` ${C.dim('https://api.claudmax.pro/v1/chat')}`);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function configureCline(apiKey) {
|
|
366
|
+
console.log(`\n${ARROW} ${C.bold('Configuring Cline (VS Code)...')}`);
|
|
367
|
+
const settingsPath = getVSCodeSettingsPath();
|
|
368
|
+
ensureDir(path.dirname(settingsPath));
|
|
369
|
+
const existing = readJson(settingsPath) || {};
|
|
370
|
+
deepMerge(existing, {
|
|
371
|
+
'cline.apiProvider': 'anthropic',
|
|
372
|
+
'cline.anthropicBaseUrl': `${API_BASE}/v1/chat`,
|
|
373
|
+
'cline.apiKey': apiKey,
|
|
374
|
+
});
|
|
375
|
+
writeJson(settingsPath, existing);
|
|
376
|
+
console.log(` ${CHECK} Wrote Cline settings: ${C.magenta(settingsPath)}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function configureRooCode(apiKey) {
|
|
380
|
+
console.log(`\n${ARROW} ${C.bold('Configuring Roo Code (VS Code)...')}`);
|
|
381
|
+
const settingsPath = getVSCodeSettingsPath();
|
|
382
|
+
ensureDir(path.dirname(settingsPath));
|
|
383
|
+
const existing = readJson(settingsPath) || {};
|
|
384
|
+
deepMerge(existing, {
|
|
385
|
+
'roo-cline.apiProvider': 'anthropic',
|
|
386
|
+
'roo-cline.anthropicBaseUrl': `${API_BASE}/v1/chat`,
|
|
387
|
+
'roo-cline.apiKey': apiKey,
|
|
388
|
+
});
|
|
389
|
+
writeJson(settingsPath, existing);
|
|
390
|
+
console.log(` ${CHECK} Wrote Roo Code settings: ${C.magenta(settingsPath)}`);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ── MCP server install ───────────────────────────────────────────────────────
|
|
394
|
+
function installMCPServer() {
|
|
395
|
+
console.log(`\n${ARROW} ${C.bold('Installing ClaudMax MCP server globally...')}`);
|
|
396
|
+
try {
|
|
397
|
+
execSync('npm install -g ' + MCP_PKG, { stdio: 'pipe', timeout: 60000 });
|
|
398
|
+
console.log(` ${CHECK} ${C.green(MCP_PKG + ' installed successfully.')}`);
|
|
399
|
+
return true;
|
|
400
|
+
} catch (err) {
|
|
401
|
+
console.log(` ${WARN} Could not install ${MCP_PKG}: ${C.dim((err.stderr || err.message || '').trim())}`);
|
|
402
|
+
console.log(` ${INFO} Install manually later: ${C.bold('npm install -g ' + MCP_PKG)}`);
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// ── Banner ────────────────────────────────────────────────────────────────────
|
|
408
|
+
function printBanner() {
|
|
409
|
+
console.log('');
|
|
410
|
+
console.log(C.magenta('\u2554' + '\u2550'.repeat(48) + '\u2557'));
|
|
411
|
+
console.log(C.magenta('\u2551') + C.bold(' \u2726 ClaudMax Setup Wizard \u2726 ') + C.magenta('\u2551'));
|
|
412
|
+
console.log(C.magenta('\u255A' + '\u2550'.repeat(48) + '\u255D'));
|
|
413
|
+
console.log('');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
417
|
+
async function main() {
|
|
418
|
+
const rl = createRL();
|
|
419
|
+
|
|
420
|
+
printBanner();
|
|
421
|
+
|
|
422
|
+
// ── Step 1: Check dependencies ─────────────────────────────────────────────
|
|
423
|
+
console.log(C.bold('Step 1: Checking system dependencies...\n'));
|
|
424
|
+
|
|
425
|
+
let nodeOk = isCommandAvailable('node');
|
|
426
|
+
let gitOk = isCommandAvailable('git');
|
|
427
|
+
let claudeOk = isCommandAvailable('claude');
|
|
428
|
+
|
|
429
|
+
const deps = [
|
|
430
|
+
{ label: 'Node.js', ok: nodeOk, cmd: 'node --version' },
|
|
431
|
+
{ label: 'Git', ok: gitOk, cmd: 'git --version' },
|
|
432
|
+
{ label: 'Claude CLI', ok: claudeOk, cmd: 'claude --version' },
|
|
433
|
+
];
|
|
434
|
+
|
|
435
|
+
for (const dep of deps) {
|
|
436
|
+
if (dep.ok) {
|
|
437
|
+
const version = getCommandVersion(dep.cmd);
|
|
438
|
+
console.log(` ${CHECK} ${C.green(dep.label)} ${C.dim(version || '')}`);
|
|
439
|
+
} else {
|
|
440
|
+
console.log(` ${CROSS} ${C.red(dep.label)} ${C.dim('not found')}`);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Install missing deps
|
|
445
|
+
if (!nodeOk) {
|
|
446
|
+
if (!installDep('Node.js', installNode)) {
|
|
447
|
+
console.log(`\n${C.red('Node.js installation failed. Please install manually from https://nodejs.org')}`);
|
|
448
|
+
} else {
|
|
449
|
+
nodeOk = isCommandAvailable('node');
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (nodeOk && !gitOk) {
|
|
454
|
+
if (!installDep('Git', installGit)) {
|
|
455
|
+
console.log(`\n${C.yellow('Git installation failed. You can install it manually.')}`);
|
|
456
|
+
} else {
|
|
457
|
+
gitOk = isCommandAvailable('git');
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (nodeOk && !claudeOk) {
|
|
462
|
+
const installClaude = await ask(rl, `\n${WARN} Claude CLI not found. Install it? ${C.bold('[Y/n]: ')}`);
|
|
463
|
+
if (!installClaude || installClaude.toLowerCase() === 'y' || installClaude.toLowerCase() === 'yes') {
|
|
464
|
+
if (!installDep('Claude CLI', installClaudeCLI)) {
|
|
465
|
+
console.log(`\n${C.yellow('Claude CLI installation failed. You can install it with: npm i -g @anthropic-ai/claude-code')}`);
|
|
466
|
+
} else {
|
|
467
|
+
claudeOk = isCommandAvailable('claude');
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
console.log('');
|
|
473
|
+
|
|
474
|
+
// ── Step 2: API key prompt ───────────────────────────────────────────────
|
|
475
|
+
console.log(C.bold('Step 2: Enter your ClaudMax API key\n'));
|
|
476
|
+
console.log(` ${INFO} Get your API key from the ${C.bold('ClaudMax Admin Dashboard')}: ${C.cyan('https://claudmax.pro/admin/dashboard')}`);
|
|
477
|
+
console.log(` ${INFO} Default superadmin: ${C.magenta('superadmin')} / ${C.magenta('claudmax-super-2026')}`);
|
|
478
|
+
console.log('');
|
|
479
|
+
|
|
480
|
+
let apiKey = '';
|
|
481
|
+
while (!apiKey.trim()) {
|
|
482
|
+
apiKey = await ask(rl, ` ${C.bold('API Key')}: `);
|
|
483
|
+
if (!apiKey.trim()) {
|
|
484
|
+
console.log(` ${CROSS} ${C.red('API key cannot be empty. Please try again.')}`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
apiKey = apiKey.trim();
|
|
488
|
+
console.log('');
|
|
489
|
+
|
|
490
|
+
// ── Step 3: Verify API key ───────────────────────────────────────────────
|
|
491
|
+
console.log(C.bold('Step 3: Verifying API key...\n'));
|
|
492
|
+
const result = await verifyConnection(apiKey);
|
|
493
|
+
|
|
494
|
+
if (result.ok) {
|
|
495
|
+
const data = result.data;
|
|
496
|
+
if (result.status === 401) {
|
|
497
|
+
console.log(` ${CROSS} ${C.red('Invalid API key. Please check and try again.')}`);
|
|
498
|
+
console.log(` ${INFO} ${C.dim(data.error || 'Authentication failed')}`);
|
|
499
|
+
rl.close();
|
|
500
|
+
process.exit(1);
|
|
501
|
+
} else {
|
|
502
|
+
console.log(` ${CHECK} ${C.green('API key is valid!')}`);
|
|
503
|
+
const tier = (data.tier || 'free').toUpperCase();
|
|
504
|
+
const limit = data.requestsLimit?.toLocaleString() || 'N/A';
|
|
505
|
+
const tokenLimit = data.tokensLimit ? (data.tokensLimit >= 1e9 ? 'Unlimited' : (data.tokensLimit / 1e6).toFixed(0) + 'M tokens/5h') : 'N/A';
|
|
506
|
+
console.log(` ${INFO} Plan: ${C.magenta(tier)} | Requests: ${C.cyan(limit)} | Tokens: ${C.cyan(tokenLimit)}`);
|
|
507
|
+
}
|
|
508
|
+
} else {
|
|
509
|
+
console.log(` ${WARN} ${C.yellow('Could not verify API key (network error). Continuing with setup anyway...')}`);
|
|
510
|
+
console.log(` ${INFO} ${C.dim(result.error || 'Connection failed')}`);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// ── Step 4: Configure system ──────────────────────────────────────────────
|
|
514
|
+
console.log('');
|
|
515
|
+
const profile = configureSystem(apiKey);
|
|
516
|
+
|
|
517
|
+
// ── Step 5: IDE selection ─────────────────────────────────────────────────
|
|
518
|
+
console.log('');
|
|
519
|
+
console.log(C.bold('Step 5: Select IDEs to configure\n'));
|
|
520
|
+
|
|
521
|
+
const IDES = [
|
|
522
|
+
{ id: 1, name: 'Claude Code CLI', shortName: 'Claude Code', configure: configureClaudeCLI },
|
|
523
|
+
{ id: 2, name: 'VS Code (Claude Extension)', shortName: 'VS Code', configure: configureVSCodeClaude },
|
|
524
|
+
{ id: 3, name: 'Cursor', shortName: 'Cursor', configure: configureCursor },
|
|
525
|
+
{ id: 4, name: 'Windsurf', shortName: 'Windsurf', configure: configureWindsurf },
|
|
526
|
+
{ id: 5, name: 'Cline (VS Code Extension)', shortName: 'Cline', configure: configureCline },
|
|
527
|
+
{ id: 6, name: 'Roo Code (VS Code Extension)', shortName: 'Roo Code', configure: configureRooCode },
|
|
528
|
+
];
|
|
529
|
+
|
|
530
|
+
for (const ide of IDES) {
|
|
531
|
+
console.log(` ${C.purple('[' + ide.id + ']')} ${ide.name}`);
|
|
532
|
+
}
|
|
533
|
+
console.log('');
|
|
534
|
+
console.log(` ${C.dim("Enter numbers separated by spaces (e.g. 1 3 4), " + C.bold("'a'") + " for all, or press Enter to skip")}`);
|
|
535
|
+
|
|
536
|
+
const choice = await ask(rl, ` ${C.bold('Your choice')}: `);
|
|
537
|
+
let selectedIds = [];
|
|
538
|
+
|
|
539
|
+
if (choice.trim().toLowerCase() === 'a') {
|
|
540
|
+
selectedIds = IDES.map((ide) => ide.id);
|
|
541
|
+
} else if (choice.trim()) {
|
|
542
|
+
selectedIds = choice.trim().split(/[\s,]+/).map(Number).filter((n) => n >= 1 && n <= IDES.length);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (selectedIds.length > 0) {
|
|
546
|
+
const selectedIDEs = IDES.filter((ide) => selectedIds.includes(ide.id));
|
|
547
|
+
for (const ide of selectedIDEs) {
|
|
548
|
+
try {
|
|
549
|
+
ide.configure(apiKey);
|
|
550
|
+
} catch (err) {
|
|
551
|
+
console.log(` ${CROSS} Failed to configure ${ide.name}: ${err.message}`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
} else {
|
|
555
|
+
console.log(`\n ${WARN} ${C.yellow('No IDEs selected. You can configure them manually later.')}`);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// ── Step 6: Install MCP server ───────────────────────────────────────────
|
|
559
|
+
installMCPServer();
|
|
560
|
+
|
|
561
|
+
// ── Summary ───────────────────────────────────────────────────────────────
|
|
562
|
+
console.log('');
|
|
563
|
+
console.log(C.magenta('\u2554' + '\u2550'.repeat(48) + '\u2557'));
|
|
564
|
+
console.log(C.magenta('\u2551') + C.bold(' \u2713 Setup complete! ') + C.magenta('\u2551'));
|
|
565
|
+
console.log(C.magenta('\u2551') + C.green(' All systems configured successfully. ') + C.magenta('\u2551'));
|
|
566
|
+
console.log(C.magenta('\u255A' + '\u2550'.repeat(48) + '\u255D'));
|
|
567
|
+
console.log('');
|
|
568
|
+
console.log(` ${INFO} ${C.bold('Next steps:')}`);
|
|
569
|
+
console.log(` 1. ${C.dim('Run:')} ${C.bold('source ' + profile)} ${C.dim('(or restart your terminal)')}`);
|
|
570
|
+
console.log(` 2. ${C.dim('Verify with:')} ${C.bold('claude --version')}`);
|
|
571
|
+
console.log(` 3. ${C.dim('Start chatting:')} ${C.bold('claude')}`);
|
|
572
|
+
console.log(` 4. ${C.dim('Check usage:')} ${C.bold('npx claudmax status')}`);
|
|
573
|
+
console.log('');
|
|
574
|
+
console.log(` ${INFO} Config saved at: ${C.magenta(CONFIG_FILE)}`);
|
|
575
|
+
console.log('');
|
|
576
|
+
|
|
577
|
+
rl.close();
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
main().catch((err) => {
|
|
581
|
+
console.error('\n' + C.red('\u2717 Fatal error: ' + err.message));
|
|
582
|
+
process.exit(1);
|
|
583
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claudmax",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "ClaudMax CLI — Configure Claude Code, Cursor, Windsurf, Cline, and Roo Code to use ClaudMax API gateway with one command",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"claudmax": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node ./index.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"claudmax",
|
|
14
|
+
"claude",
|
|
15
|
+
"claude-code",
|
|
16
|
+
"anthropic",
|
|
17
|
+
"ai",
|
|
18
|
+
"api",
|
|
19
|
+
"proxy",
|
|
20
|
+
"gateway",
|
|
21
|
+
"cursor",
|
|
22
|
+
"windsurf",
|
|
23
|
+
"cline",
|
|
24
|
+
"mcp",
|
|
25
|
+
"setup",
|
|
26
|
+
"openrouter"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=16.0.0"
|
|
31
|
+
},
|
|
32
|
+
"preferGlobal": true
|
|
33
|
+
}
|