scalemax 0.1.2 → 0.1.3
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/dist/commands/configure.js +8 -0
- package/dist/detect/codex.js +19 -2
- package/dist/index.js +8 -0
- package/dist/install/codex.d.ts +12 -0
- package/dist/install/codex.js +39 -0
- package/dist/types/client.d.ts +3 -0
- package/dist/utils/runtime.d.ts +6 -0
- package/dist/utils/runtime.js +16 -0
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import { SCALEMAX_BASE_URL } from '../constants.js';
|
|
|
6
6
|
import { loadApiKey } from '../utils/keyStore.js';
|
|
7
7
|
import { homeDir } from '../utils/paths.js';
|
|
8
8
|
import { launchCodex } from '../configure/codex.js';
|
|
9
|
+
import { ensureCodexInstalled } from '../install/codex.js';
|
|
9
10
|
export async function detectAll(home = homeDir()) {
|
|
10
11
|
const ctx = { home, platform: process.platform, env: process.env };
|
|
11
12
|
return Promise.all(clients.map(c => c.detect(ctx)));
|
|
@@ -61,6 +62,13 @@ export async function configureCommand(opts = {}) {
|
|
|
61
62
|
console.log(chalk.yellow('No detected clients to configure.'));
|
|
62
63
|
return;
|
|
63
64
|
}
|
|
65
|
+
if (chosen.includes('codex')) {
|
|
66
|
+
const install = await ensureCodexInstalled(process.stdin.isTTY);
|
|
67
|
+
if (!install.ok)
|
|
68
|
+
console.log(chalk.yellow(install.message));
|
|
69
|
+
else
|
|
70
|
+
console.log(chalk.green(`✓ ${install.message}`));
|
|
71
|
+
}
|
|
64
72
|
const successful = new Set();
|
|
65
73
|
for (const client of clients.filter(c => chosen.includes(c.id))) {
|
|
66
74
|
const spinner = ora(`Configuring ${client.name}`).start();
|
package/dist/detect/codex.js
CHANGED
|
@@ -1,2 +1,19 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { commandExists } from '../utils/exec.js';
|
|
4
|
+
export async function detect(ctx) {
|
|
5
|
+
const codexDir = path.join(ctx.home, '.codex');
|
|
6
|
+
const config = path.join(codexDir, 'config.toml');
|
|
7
|
+
const auth = path.join(codexDir, 'auth.json');
|
|
8
|
+
const paths = [];
|
|
9
|
+
const executable = await commandExists('codex');
|
|
10
|
+
if (executable)
|
|
11
|
+
paths.push('command:codex');
|
|
12
|
+
if (await fs.pathExists(config))
|
|
13
|
+
paths.push(config);
|
|
14
|
+
if (await fs.pathExists(auth))
|
|
15
|
+
paths.push(auth);
|
|
16
|
+
if (!paths.length && await fs.pathExists(codexDir))
|
|
17
|
+
paths.push(codexDir);
|
|
18
|
+
return { id: 'codex', name: 'Codex', installed: executable, reason: executable ? undefined : (paths.length ? 'configuration exists but codex executable is missing' : 'not detected'), paths, executable, configured: await fs.pathExists(config) && await fs.pathExists(auth), installable: true };
|
|
19
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { modelsCommand } from './commands/models.js';
|
|
|
12
12
|
import { updateCommand } from './commands/update.js';
|
|
13
13
|
import { removeCommand } from './commands/remove.js';
|
|
14
14
|
import { ensureInstallerApiKey } from './utils/installerConfig.js';
|
|
15
|
+
import { isNodeSupported, nodeUpgradeHint, runtimeReport } from './utils/runtime.js';
|
|
15
16
|
function packageVersion() {
|
|
16
17
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
17
18
|
const candidates = [path.join(here, '..', 'package.json'), path.join(here, 'package.json')];
|
|
@@ -30,10 +31,17 @@ function welcome() {
|
|
|
30
31
|
console.log('\nThe fastest way to connect every AI coding client.\n');
|
|
31
32
|
}
|
|
32
33
|
async function main() {
|
|
34
|
+
if (!isNodeSupported()) {
|
|
35
|
+
console.error(chalk.red(`ScaleMax requires Node.js >=18.17. Current: ${process.versions.node}`));
|
|
36
|
+
console.error(nodeUpgradeHint());
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
33
40
|
const [, , command] = process.argv;
|
|
34
41
|
try {
|
|
35
42
|
if (!command) {
|
|
36
43
|
welcome();
|
|
44
|
+
console.log(await runtimeReport());
|
|
37
45
|
const apiKey = await ensureInstallerApiKey(process.env.HOME || process.env.USERPROFILE || process.cwd());
|
|
38
46
|
if (!apiKey)
|
|
39
47
|
return;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function isCodexExecutableAvailable(): Promise<boolean>;
|
|
2
|
+
export declare function getCodexVersion(): Promise<string | undefined>;
|
|
3
|
+
export declare function installCodexWithNpm(): Promise<{
|
|
4
|
+
ok: boolean;
|
|
5
|
+
message: string;
|
|
6
|
+
output?: string;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function ensureCodexInstalled(interactive?: boolean): Promise<{
|
|
9
|
+
ok: boolean;
|
|
10
|
+
installed: boolean;
|
|
11
|
+
message: string;
|
|
12
|
+
}>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { confirm, isCancel } from '@clack/prompts';
|
|
2
|
+
import { execa } from 'execa';
|
|
3
|
+
import { commandExists } from '../utils/exec.js';
|
|
4
|
+
export async function isCodexExecutableAvailable() { return commandExists('codex'); }
|
|
5
|
+
export async function getCodexVersion() { try {
|
|
6
|
+
return (await execa('codex', ['--version'], { reject: false })).stdout.trim();
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return undefined;
|
|
10
|
+
} }
|
|
11
|
+
export async function installCodexWithNpm() {
|
|
12
|
+
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
13
|
+
const args = ['install', '-g', '@openai/codex@latest'];
|
|
14
|
+
try {
|
|
15
|
+
const res = await execa(npm, args, { all: true, reject: false, stdio: 'pipe' });
|
|
16
|
+
if (res.exitCode === 0)
|
|
17
|
+
return { ok: true, message: 'Codex installed with npm.' };
|
|
18
|
+
return { ok: false, message: `npm install exited with ${res.exitCode}.`, output: res.all };
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
return { ok: false, message: `Could not run npm install -g @openai/codex@latest: ${err instanceof Error ? err.message : String(err)}` };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function ensureCodexInstalled(interactive = true) {
|
|
25
|
+
if (await isCodexExecutableAvailable())
|
|
26
|
+
return { ok: true, installed: false, message: `Codex detected${(await getCodexVersion()) ? ` (${await getCodexVersion()})` : ''}.` };
|
|
27
|
+
if (!interactive)
|
|
28
|
+
return { ok: false, installed: false, message: 'Codex executable was not found on PATH.' };
|
|
29
|
+
const ans = await confirm({ message: 'Codex CLI is not installed. Install it now with npm install -g @openai/codex@latest?', initialValue: true });
|
|
30
|
+
if (isCancel(ans) || ans === false)
|
|
31
|
+
return { ok: false, installed: false, message: 'Codex installation skipped.' };
|
|
32
|
+
const res = await installCodexWithNpm();
|
|
33
|
+
if (!res.ok)
|
|
34
|
+
return { ok: false, installed: false, message: `${res.message}
|
|
35
|
+
If this is a permission issue, install Node with nvm/fnm/volta or run: npm install -g @openai/codex@latest` };
|
|
36
|
+
if (!(await isCodexExecutableAvailable()))
|
|
37
|
+
return { ok: false, installed: true, message: 'Codex install finished but codex is not on PATH. Open a new terminal or refresh PATH, then rerun npx scalemax.' };
|
|
38
|
+
return { ok: true, installed: true, message: 'Codex installed and detected.' };
|
|
39
|
+
}
|
package/dist/types/client.d.ts
CHANGED
|
@@ -17,6 +17,9 @@ export interface DetectionResult {
|
|
|
17
17
|
installed: boolean;
|
|
18
18
|
reason?: string;
|
|
19
19
|
paths: string[];
|
|
20
|
+
executable?: boolean;
|
|
21
|
+
configured?: boolean;
|
|
22
|
+
installable?: boolean;
|
|
20
23
|
}
|
|
21
24
|
export interface ConfigureContext extends DetectContext {
|
|
22
25
|
apiKey: string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const MIN_NODE = "18.17.0";
|
|
2
|
+
export declare function compareSemver(a: string, b: string): number;
|
|
3
|
+
export declare function isNodeSupported(version?: string): boolean;
|
|
4
|
+
export declare function npmVersion(): Promise<string | undefined>;
|
|
5
|
+
export declare function runtimeReport(): Promise<string>;
|
|
6
|
+
export declare function nodeUpgradeHint(): string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
export const MIN_NODE = '18.17.0';
|
|
3
|
+
export function compareSemver(a, b) { const pa = a.replace(/^v/, '').split('.').map(Number), pb = b.replace(/^v/, '').split('.').map(Number); for (let i = 0; i < 3; i++) {
|
|
4
|
+
const d = (pa[i] || 0) - (pb[i] || 0);
|
|
5
|
+
if (d)
|
|
6
|
+
return d;
|
|
7
|
+
} return 0; }
|
|
8
|
+
export function isNodeSupported(version = process.versions.node) { return compareSemver(version, MIN_NODE) >= 0; }
|
|
9
|
+
export async function npmVersion() { try {
|
|
10
|
+
return (await execa(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['-v'])).stdout.trim();
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return undefined;
|
|
14
|
+
} }
|
|
15
|
+
export async function runtimeReport() { return `Node ${process.versions.node} ${isNodeSupported() ? '✓' : '✗'} (required >=${MIN_NODE}) · npm ${await npmVersion() || 'not found'}`; }
|
|
16
|
+
export function nodeUpgradeHint() { return ['Install Node.js LTS, then rerun: npx scalemax@latest', 'macOS Homebrew: brew install node', 'nvm: nvm install --lts && nvm use --lts', 'fnm: fnm install --lts && fnm use --lts', 'volta: volta install node@lts npm'].join('\\n'); }
|