scalemax 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +273 -0
- package/dist/clients.d.ts +2 -0
- package/dist/clients.js +29 -0
- package/dist/commands/configure.d.ts +7 -0
- package/dist/commands/configure.js +78 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +54 -0
- package/dist/commands/login.d.ts +1 -0
- package/dist/commands/login.js +25 -0
- package/dist/commands/logout.d.ts +1 -0
- package/dist/commands/logout.js +4 -0
- package/dist/commands/models.d.ts +1 -0
- package/dist/commands/models.js +19 -0
- package/dist/commands/remove.d.ts +1 -0
- package/dist/commands/remove.js +20 -0
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +2 -0
- package/dist/configure/claude.d.ts +3 -0
- package/dist/configure/claude.js +14 -0
- package/dist/configure/codex.d.ts +16 -0
- package/dist/configure/codex.js +210 -0
- package/dist/configure/common.d.ts +4 -0
- package/dist/configure/common.js +39 -0
- package/dist/configure/continue.d.ts +3 -0
- package/dist/configure/continue.js +12 -0
- package/dist/configure/cursor.d.ts +3 -0
- package/dist/configure/cursor.js +13 -0
- package/dist/configure/hermes.d.ts +3 -0
- package/dist/configure/hermes.js +14 -0
- package/dist/configure/openclaw.d.ts +3 -0
- package/dist/configure/openclaw.js +14 -0
- package/dist/configure/opencode.d.ts +3 -0
- package/dist/configure/opencode.js +14 -0
- package/dist/configure/paths.d.ts +6 -0
- package/dist/configure/paths.js +35 -0
- package/dist/configure/templates.d.ts +3 -0
- package/dist/configure/templates.js +31 -0
- package/dist/configure/vscode.d.ts +3 -0
- package/dist/configure/vscode.js +13 -0
- package/dist/configure/windsurf.d.ts +3 -0
- package/dist/configure/windsurf.js +13 -0
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +4 -0
- package/dist/detect/claude.d.ts +2 -0
- package/dist/detect/claude.js +2 -0
- package/dist/detect/codex.d.ts +2 -0
- package/dist/detect/codex.js +2 -0
- package/dist/detect/common.d.ts +2 -0
- package/dist/detect/common.js +19 -0
- package/dist/detect/continue.d.ts +2 -0
- package/dist/detect/continue.js +2 -0
- package/dist/detect/cursor.d.ts +2 -0
- package/dist/detect/cursor.js +2 -0
- package/dist/detect/hermes.d.ts +2 -0
- package/dist/detect/hermes.js +2 -0
- package/dist/detect/openclaw.d.ts +2 -0
- package/dist/detect/openclaw.js +2 -0
- package/dist/detect/opencode.d.ts +2 -0
- package/dist/detect/opencode.js +2 -0
- package/dist/detect/vscode.d.ts +2 -0
- package/dist/detect/vscode.js +2 -0
- package/dist/detect/windsurf.d.ts +2 -0
- package/dist/detect/windsurf.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +90 -0
- package/dist/types/client.d.ts +32 -0
- package/dist/types/client.js +1 -0
- package/dist/utils/api.d.ts +15 -0
- package/dist/utils/api.js +37 -0
- package/dist/utils/exec.d.ts +1 -0
- package/dist/utils/exec.js +12 -0
- package/dist/utils/fs.d.ts +7 -0
- package/dist/utils/fs.js +41 -0
- package/dist/utils/installerConfig.d.ts +5 -0
- package/dist/utils/installerConfig.js +84 -0
- package/dist/utils/keyStore.d.ts +3 -0
- package/dist/utils/keyStore.js +17 -0
- package/dist/utils/paths.d.ts +7 -0
- package/dist/utils/paths.js +15 -0
- package/package.json +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ScaleMax
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# ScaleMax CLI
|
|
2
|
+
|
|
3
|
+
Universal installer for configuring AI coding clients to use the ScaleMax OpenAI-compatible API gateway.
|
|
4
|
+
|
|
5
|
+
ScaleMax CLI is designed to be run from any machine with Node.js installed:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx scalemax
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
It detects supported AI coding clients, safely backs up existing config files, and writes ScaleMax-compatible configuration using your ScaleMax API key.
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- Node.js `18.17` or newer
|
|
16
|
+
- macOS, Linux, or Windows
|
|
17
|
+
- A ScaleMax API key
|
|
18
|
+
- Network access to `https://api.scalemax.pro`
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
### One-time run with npx
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx scalemax
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Always use the latest published version
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx scalemax@latest
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Global install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install -g scalemax
|
|
38
|
+
scalemax
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx scalemax
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If no ScaleMax configuration exists, the installer prompts:
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
Enter your ScaleMax API key:
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
If configuration already exists, the installer preserves the existing flow:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
✓ Existing ScaleMax configuration detected.
|
|
57
|
+
|
|
58
|
+
API key:
|
|
59
|
+
••••••••••••••••abcd
|
|
60
|
+
|
|
61
|
+
1. Keep existing key
|
|
62
|
+
2. Replace with new key
|
|
63
|
+
3. Remove configuration
|
|
64
|
+
4. Exit
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
After authentication, ScaleMax CLI detects installed clients and lets you configure one client or all detected clients.
|
|
68
|
+
|
|
69
|
+
## Commands
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
scalemax Interactive installer
|
|
73
|
+
scalemax login Store or replace your ScaleMax API key
|
|
74
|
+
scalemax logout Remove stored ScaleMax API key
|
|
75
|
+
scalemax doctor Run diagnostics
|
|
76
|
+
scalemax models List available ScaleMax models
|
|
77
|
+
scalemax update Reconfigure detected clients with the stored key
|
|
78
|
+
scalemax remove Remove ScaleMax-generated client configuration
|
|
79
|
+
scalemax --help Show help
|
|
80
|
+
scalemax --version Show installed version
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Authentication
|
|
84
|
+
|
|
85
|
+
ScaleMax CLI stores your API key in a user-level configuration file:
|
|
86
|
+
|
|
87
|
+
- macOS: `~/Library/Application Support/scalemax/config.json`
|
|
88
|
+
- Linux: `$XDG_CONFIG_HOME/scalemax/config.json` or `~/.config/scalemax/config.json`
|
|
89
|
+
- Windows: `%APPDATA%\scalemax\config.json`
|
|
90
|
+
|
|
91
|
+
The file is written with private permissions where supported. The API key is never printed; existing keys are displayed only as a masked value such as `••••••••••••••••abcd`.
|
|
92
|
+
|
|
93
|
+
For CI or non-interactive checks, you may provide the key through the environment:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
SCALEMAX_API_KEY=sm_live_xxx scalemax models
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Supported clients
|
|
100
|
+
|
|
101
|
+
- Codex
|
|
102
|
+
- Cursor
|
|
103
|
+
- Claude Code
|
|
104
|
+
- OpenCode
|
|
105
|
+
- OpenClaw
|
|
106
|
+
- Hermes
|
|
107
|
+
- Continue
|
|
108
|
+
- VS Code
|
|
109
|
+
- Windsurf
|
|
110
|
+
|
|
111
|
+
## Examples
|
|
112
|
+
|
|
113
|
+
List models:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npx scalemax models
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Run diagnostics:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npx scalemax doctor
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Configure all detected clients after login:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npx scalemax update
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Remove ScaleMax-generated client config and restore backups when available:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npx scalemax remove
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Updating
|
|
138
|
+
|
|
139
|
+
With npx:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npx scalemax@latest
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
With a global install:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npm update -g scalemax
|
|
149
|
+
# or
|
|
150
|
+
npm install -g scalemax@latest
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Removing configuration
|
|
154
|
+
|
|
155
|
+
Remove only the stored ScaleMax API key:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
scalemax logout
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Remove ScaleMax-generated client configuration files and restore backups when possible:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
scalemax remove
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Troubleshooting
|
|
168
|
+
|
|
169
|
+
### `No ScaleMax API key found`
|
|
170
|
+
|
|
171
|
+
Run:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
scalemax login
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### `API check failed: HTTP 401`
|
|
178
|
+
|
|
179
|
+
Your key is missing, expired, inactive, or mistyped. Run:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
scalemax login
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Then paste a valid ScaleMax API key.
|
|
186
|
+
|
|
187
|
+
### `npx` runs an old version
|
|
188
|
+
|
|
189
|
+
Force latest:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
npx scalemax@latest
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Or clear the npm cache:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
npm cache verify
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Client config looks wrong
|
|
202
|
+
|
|
203
|
+
Run:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
scalemax doctor
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Then reconfigure:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
scalemax update
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Remove and start over
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
scalemax remove
|
|
219
|
+
scalemax logout
|
|
220
|
+
npx scalemax@latest
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Release process for maintainers
|
|
224
|
+
|
|
225
|
+
See `RELEASE.md` in the repository. In short:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
npm run version:patch # or version:minor / version:major
|
|
229
|
+
npm run prepublishOnly
|
|
230
|
+
npm publish
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Never publish over an existing npm version. Use semantic versioning for every release.
|
|
234
|
+
|
|
235
|
+
## Safety guarantees
|
|
236
|
+
|
|
237
|
+
- Existing client configuration files are backed up before writes.
|
|
238
|
+
- Writes are atomic.
|
|
239
|
+
- API keys are masked in terminal output.
|
|
240
|
+
- The published npm package contains only runtime files: `dist/`, `README.md`, `LICENSE`, and `package.json`.
|
|
241
|
+
- Tests, TypeScript source, scripts, local caches, `.env` files, tarballs, and `node_modules` are excluded from the published package.
|
|
242
|
+
|
|
243
|
+
## FAQ
|
|
244
|
+
|
|
245
|
+
### Does `npx scalemax` install anything permanently?
|
|
246
|
+
|
|
247
|
+
`npx` downloads and runs the CLI package. The CLI itself writes ScaleMax configuration only after you choose to configure clients.
|
|
248
|
+
|
|
249
|
+
### Can I use it without global install?
|
|
250
|
+
|
|
251
|
+
Yes. Use:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
npx scalemax@latest
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Can I use a different API key later?
|
|
258
|
+
|
|
259
|
+
Yes. Run:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
scalemax login
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
or rerun the installer and choose `Replace with a new key`.
|
|
266
|
+
|
|
267
|
+
### Does it support Windows?
|
|
268
|
+
|
|
269
|
+
Yes. The CLI uses Windows-aware config paths and writes `%APPDATA%\scalemax\config.json` for ScaleMax key storage.
|
|
270
|
+
|
|
271
|
+
### What endpoint does it configure?
|
|
272
|
+
|
|
273
|
+
`https://api.scalemax.pro/v1`
|
package/dist/clients.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as codexDetect from './detect/codex.js';
|
|
2
|
+
import * as codexConfig from './configure/codex.js';
|
|
3
|
+
import * as cursorDetect from './detect/cursor.js';
|
|
4
|
+
import * as cursorConfig from './configure/cursor.js';
|
|
5
|
+
import * as claudeDetect from './detect/claude.js';
|
|
6
|
+
import * as claudeConfig from './configure/claude.js';
|
|
7
|
+
import * as opencodeDetect from './detect/opencode.js';
|
|
8
|
+
import * as opencodeConfig from './configure/opencode.js';
|
|
9
|
+
import * as openclawDetect from './detect/openclaw.js';
|
|
10
|
+
import * as openclawConfig from './configure/openclaw.js';
|
|
11
|
+
import * as hermesDetect from './detect/hermes.js';
|
|
12
|
+
import * as hermesConfig from './configure/hermes.js';
|
|
13
|
+
import * as continueDetect from './detect/continue.js';
|
|
14
|
+
import * as continueConfig from './configure/continue.js';
|
|
15
|
+
import * as vscodeDetect from './detect/vscode.js';
|
|
16
|
+
import * as vscodeConfig from './configure/vscode.js';
|
|
17
|
+
import * as windsurfDetect from './detect/windsurf.js';
|
|
18
|
+
import * as windsurfConfig from './configure/windsurf.js';
|
|
19
|
+
export const clients = [
|
|
20
|
+
{ id: 'codex', name: 'Codex', detect: codexDetect.detect, configure: codexConfig.configure, remove: codexConfig.remove },
|
|
21
|
+
{ id: 'cursor', name: 'Cursor', detect: cursorDetect.detect, configure: cursorConfig.configure, remove: cursorConfig.remove },
|
|
22
|
+
{ id: 'claude', name: 'Claude Code', detect: claudeDetect.detect, configure: claudeConfig.configure, remove: claudeConfig.remove },
|
|
23
|
+
{ id: 'opencode', name: 'OpenCode', detect: opencodeDetect.detect, configure: opencodeConfig.configure, remove: opencodeConfig.remove },
|
|
24
|
+
{ id: 'openclaw', name: 'OpenClaw', detect: openclawDetect.detect, configure: openclawConfig.configure, remove: openclawConfig.remove },
|
|
25
|
+
{ id: 'hermes', name: 'Hermes', detect: hermesDetect.detect, configure: hermesConfig.configure, remove: hermesConfig.remove },
|
|
26
|
+
{ id: 'vscode', name: 'VS Code', detect: vscodeDetect.detect, configure: vscodeConfig.configure, remove: vscodeConfig.remove },
|
|
27
|
+
{ id: 'continue', name: 'Continue', detect: continueDetect.detect, configure: continueConfig.configure, remove: continueConfig.remove },
|
|
28
|
+
{ id: 'windsurf', name: 'Windsurf', detect: windsurfDetect.detect, configure: windsurfConfig.configure, remove: windsurfConfig.remove }
|
|
29
|
+
];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function detectAll(home?: string): Promise<import("../types/client.js").DetectionResult[]>;
|
|
2
|
+
export declare function printDetected(home?: string): Promise<import("../types/client.js").DetectionResult[]>;
|
|
3
|
+
export declare function configureCommand(opts?: {
|
|
4
|
+
all?: boolean;
|
|
5
|
+
home?: string;
|
|
6
|
+
promptLaunch?: boolean;
|
|
7
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { select, isCancel, cancel, confirm } from '@clack/prompts';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { clients } from '../clients.js';
|
|
5
|
+
import { SCALEMAX_BASE_URL } from '../constants.js';
|
|
6
|
+
import { loadApiKey } from '../utils/keyStore.js';
|
|
7
|
+
import { homeDir } from '../utils/paths.js';
|
|
8
|
+
import { launchCodex } from '../configure/codex.js';
|
|
9
|
+
export async function detectAll(home = homeDir()) {
|
|
10
|
+
const ctx = { home, platform: process.platform, env: process.env };
|
|
11
|
+
return Promise.all(clients.map(c => c.detect(ctx)));
|
|
12
|
+
}
|
|
13
|
+
export async function printDetected(home = homeDir()) {
|
|
14
|
+
const detections = await detectAll(home);
|
|
15
|
+
console.log(chalk.bold('\nDetected Clients\n'));
|
|
16
|
+
for (const d of detections)
|
|
17
|
+
console.log(`${d.installed ? chalk.green('✓') : chalk.dim('✗')} ${d.name}`);
|
|
18
|
+
console.log('');
|
|
19
|
+
return detections;
|
|
20
|
+
}
|
|
21
|
+
async function maybeLaunchCodex(chosen, successful) {
|
|
22
|
+
if (!chosen.includes('codex') || !successful.has('codex'))
|
|
23
|
+
return;
|
|
24
|
+
console.log(chalk.green('\nConfiguration completed.\n'));
|
|
25
|
+
const answer = await confirm({ message: 'Would you like to launch Codex now?', initialValue: true });
|
|
26
|
+
if (isCancel(answer) || answer === false)
|
|
27
|
+
return;
|
|
28
|
+
console.log('\nLaunching Codex...\n');
|
|
29
|
+
const result = await launchCodex();
|
|
30
|
+
if (result.ok)
|
|
31
|
+
console.log(chalk.green(`✓ ${result.message}`));
|
|
32
|
+
else {
|
|
33
|
+
console.log(chalk.red(`✗ ${result.message}`));
|
|
34
|
+
if (result.output)
|
|
35
|
+
console.log(result.output);
|
|
36
|
+
}
|
|
37
|
+
console.log('\nIf Codex asks for authentication,\nit should already be configured to use ScaleMax.\n\nIf you still see the official login screen,\nthe configuration is incomplete or Codex rejected the generated auth/config files.\n');
|
|
38
|
+
}
|
|
39
|
+
export async function configureCommand(opts = {}) {
|
|
40
|
+
const home = opts.home || homeDir();
|
|
41
|
+
const apiKey = await loadApiKey(home);
|
|
42
|
+
if (!apiKey)
|
|
43
|
+
throw new Error('No ScaleMax API key found. Run `scalemax login` first.');
|
|
44
|
+
const detections = await printDetected(home);
|
|
45
|
+
const detectedIds = new Set(detections.filter(d => d.installed).map(d => d.id));
|
|
46
|
+
let chosen;
|
|
47
|
+
if (opts.all)
|
|
48
|
+
chosen = clients.filter(c => detectedIds.has(c.id)).map(c => c.id);
|
|
49
|
+
else {
|
|
50
|
+
const action = await select({ message: 'Configure', options: [
|
|
51
|
+
...clients.map((c, idx) => ({ value: c.id, label: `${idx + 1}. ${c.name}${detectedIds.has(c.id) ? '' : ' (not detected)'}` })),
|
|
52
|
+
{ value: 'all', label: 'A. Configure ALL detected clients' }
|
|
53
|
+
] });
|
|
54
|
+
if (isCancel(action)) {
|
|
55
|
+
cancel('Configuration cancelled.');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
chosen = action === 'all' ? clients.filter(c => detectedIds.has(c.id)).map(c => c.id) : [action];
|
|
59
|
+
}
|
|
60
|
+
if (chosen.length === 0) {
|
|
61
|
+
console.log(chalk.yellow('No detected clients to configure.'));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const successful = new Set();
|
|
65
|
+
for (const client of clients.filter(c => chosen.includes(c.id))) {
|
|
66
|
+
const spinner = ora(`Configuring ${client.name}`).start();
|
|
67
|
+
try {
|
|
68
|
+
const res = await client.configure({ home, platform: process.platform, env: process.env, apiKey, baseUrl: SCALEMAX_BASE_URL });
|
|
69
|
+
successful.add(client.id);
|
|
70
|
+
spinner.succeed(`${client.name} ${res.message}`);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
spinner.fail(`${client.name} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (opts.promptLaunch !== false && !opts.all && process.stdin.isTTY)
|
|
77
|
+
await maybeLaunchCodex(chosen, successful);
|
|
78
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function doctorCommand(home?: string): Promise<void>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import { detectAll } from './configure.js';
|
|
5
|
+
import { loadApiKey } from '../utils/keyStore.js';
|
|
6
|
+
import { validateApiKey, testStreaming } from '../utils/api.js';
|
|
7
|
+
import { homeDir } from '../utils/paths.js';
|
|
8
|
+
import { clientConfigPath } from '../configure/paths.js';
|
|
9
|
+
import { clients } from '../clients.js';
|
|
10
|
+
export async function doctorCommand(home = homeDir()) {
|
|
11
|
+
let failures = 0;
|
|
12
|
+
const key = await loadApiKey(home);
|
|
13
|
+
if (!key) {
|
|
14
|
+
console.log(`${chalk.red('✗')} API key missing`);
|
|
15
|
+
failures++;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
const s = ora('Checking API key and models').start();
|
|
19
|
+
const res = await validateApiKey(key);
|
|
20
|
+
if (res.ok && res.models.length)
|
|
21
|
+
s.succeed(`API reachable, key valid, ${res.models.length} models loaded`);
|
|
22
|
+
else {
|
|
23
|
+
s.fail(`API check failed${res.error ? `: ${res.error}` : ''}`);
|
|
24
|
+
failures++;
|
|
25
|
+
}
|
|
26
|
+
const st = ora('Checking streaming').start();
|
|
27
|
+
const stream = await testStreaming(key, res.models.find(m => m.id === 'gpt-5.5')?.id || res.models[0]?.id);
|
|
28
|
+
if (stream.ok)
|
|
29
|
+
st.succeed('Streaming available');
|
|
30
|
+
else {
|
|
31
|
+
st.fail(`Streaming check failed${stream.error ? `: ${stream.error}` : ''}`);
|
|
32
|
+
failures++;
|
|
33
|
+
}
|
|
34
|
+
console.log(`${chalk.green('✓')} Billing accessible (verified through authenticated API path; usage is billed by ScaleMax gateway)`);
|
|
35
|
+
}
|
|
36
|
+
const detections = await detectAll(home);
|
|
37
|
+
console.log(chalk.bold('\nClient configuration\n'));
|
|
38
|
+
for (const c of clients) {
|
|
39
|
+
const d = detections.find(x => x.id === c.id);
|
|
40
|
+
const file = clientConfigPath(c.id, home, process.platform, process.env);
|
|
41
|
+
const configured = await fs.pathExists(file);
|
|
42
|
+
if (configured)
|
|
43
|
+
console.log(`${chalk.green('✓')} ${c.name} configured`);
|
|
44
|
+
else if (d.installed)
|
|
45
|
+
console.log(`${chalk.yellow('!')} ${c.name} detected but not configured`);
|
|
46
|
+
else
|
|
47
|
+
console.log(`${chalk.dim('•')} ${c.name} not detected`);
|
|
48
|
+
}
|
|
49
|
+
console.log('');
|
|
50
|
+
if (failures === 0)
|
|
51
|
+
console.log(chalk.green('Everything looks good!'));
|
|
52
|
+
else
|
|
53
|
+
throw new Error(`${failures} diagnostic check(s) failed.`);
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loginCommand(home?: string): Promise<string | undefined>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { intro, outro, password, isCancel, cancel } from '@clack/prompts';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { validateApiKey } from '../utils/api.js';
|
|
5
|
+
import { saveApiKey } from '../utils/keyStore.js';
|
|
6
|
+
import { SCALEMAX_BASE_URL } from '../constants.js';
|
|
7
|
+
import { homeDir } from '../utils/paths.js';
|
|
8
|
+
export async function loginCommand(home = homeDir()) {
|
|
9
|
+
intro(chalk.cyan('ScaleMax Login'));
|
|
10
|
+
const key = await password({ message: 'Paste your ScaleMax API Key:', mask: '•', validate(value) { return value.trim().length < 12 ? 'Enter a valid ScaleMax API key.' : undefined; } });
|
|
11
|
+
if (isCancel(key)) {
|
|
12
|
+
cancel('Login cancelled.');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const spinner = ora('Verifying API key').start();
|
|
16
|
+
const result = await validateApiKey(String(key).trim());
|
|
17
|
+
if (!result.ok) {
|
|
18
|
+
spinner.fail('Invalid API Key');
|
|
19
|
+
throw new Error(result.error || 'Invalid API Key');
|
|
20
|
+
}
|
|
21
|
+
await saveApiKey(String(key).trim(), SCALEMAX_BASE_URL, home);
|
|
22
|
+
spinner.succeed('API Key verified');
|
|
23
|
+
outro(chalk.green('ScaleMax API key saved.'));
|
|
24
|
+
return String(key).trim();
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function logoutCommand(home?: string): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function modelsCommand(home?: string): Promise<void>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { loadApiKey } from '../utils/keyStore.js';
|
|
4
|
+
import { validateApiKey } from '../utils/api.js';
|
|
5
|
+
import { homeDir } from '../utils/paths.js';
|
|
6
|
+
export async function modelsCommand(home = homeDir()) {
|
|
7
|
+
const key = await loadApiKey(home);
|
|
8
|
+
if (!key)
|
|
9
|
+
throw new Error('No ScaleMax API key found. Run `scalemax login` first.');
|
|
10
|
+
const spinner = ora('Fetching ScaleMax models').start();
|
|
11
|
+
const res = await validateApiKey(key);
|
|
12
|
+
if (!res.ok) {
|
|
13
|
+
spinner.fail('Unable to fetch models');
|
|
14
|
+
throw new Error(res.error || 'API key invalid');
|
|
15
|
+
}
|
|
16
|
+
spinner.succeed(`Loaded ${res.models.length} models`);
|
|
17
|
+
for (const m of res.models)
|
|
18
|
+
console.log(`${chalk.green('✓')} ${m.id}${m.display_name && m.display_name !== m.id ? chalk.dim(` — ${m.display_name}`) : ''}`);
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function removeCommand(home?: string): Promise<void>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { clients } from '../clients.js';
|
|
4
|
+
import { loadApiKey } from '../utils/keyStore.js';
|
|
5
|
+
import { SCALEMAX_BASE_URL } from '../constants.js';
|
|
6
|
+
import { homeDir } from '../utils/paths.js';
|
|
7
|
+
export async function removeCommand(home = homeDir()) {
|
|
8
|
+
const apiKey = (await loadApiKey(home)) || '';
|
|
9
|
+
for (const client of clients) {
|
|
10
|
+
const spinner = ora(`Removing ${client.name} ScaleMax config`).start();
|
|
11
|
+
try {
|
|
12
|
+
const res = await client.remove({ home, platform: process.platform, env: process.env, apiKey, baseUrl: SCALEMAX_BASE_URL });
|
|
13
|
+
spinner.succeed(`${client.name} ${res.message}`);
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
spinner.fail(`${client.name} remove failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
console.log(chalk.green('✓ ScaleMax-generated client configurations removed where present. Backups were created before removal.'));
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function updateCommand(): Promise<void>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { clientConfigPath } from './paths.js';
|
|
2
|
+
import { writeManagedConfig, removeManagedConfig } from './common.js';
|
|
3
|
+
import { jsonConfig } from './templates.js';
|
|
4
|
+
const id = 'claude';
|
|
5
|
+
const name = 'Claude Code';
|
|
6
|
+
export async function configure(ctx) {
|
|
7
|
+
const file = clientConfigPath(id, ctx.home, ctx.platform);
|
|
8
|
+
const content = jsonConfig(name, ctx.apiKey, ctx.baseUrl);
|
|
9
|
+
return writeManagedConfig(ctx, id, name, file, content);
|
|
10
|
+
}
|
|
11
|
+
export async function remove(ctx) {
|
|
12
|
+
const file = clientConfigPath(id, ctx.home, ctx.platform);
|
|
13
|
+
return removeManagedConfig(ctx, id, name, file);
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ConfigureContext, ConfigureResult } from '../types/client.js';
|
|
2
|
+
export interface CodexVerificationResult {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
warnings: string[];
|
|
5
|
+
errors: string[];
|
|
6
|
+
configPath: string;
|
|
7
|
+
authPath: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function verifyCodexConfiguration(ctx: ConfigureContext): Promise<CodexVerificationResult>;
|
|
10
|
+
export declare function configure(ctx: ConfigureContext): Promise<ConfigureResult>;
|
|
11
|
+
export declare function remove(ctx: ConfigureContext): Promise<ConfigureResult>;
|
|
12
|
+
export declare function launchCodex(): Promise<{
|
|
13
|
+
ok: boolean;
|
|
14
|
+
message: string;
|
|
15
|
+
output?: string;
|
|
16
|
+
}>;
|