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