scalemax 0.1.2 → 0.1.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.
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ function cmp(a,b){a=String(a).replace(/^v/,'').split('.').map(Number);b=String(b).split('.').map(Number);for(var i=0;i<3;i++){var d=(a[i]||0)-(b[i]||0);if(d)return d;}return 0;}
4
+ var min='18.17.0';
5
+ if (cmp(process.versions.node,min)<0) {
6
+ console.error('ScaleMax requires Node.js >= '+min+'. Current: '+process.versions.node);
7
+ console.error('Install Node.js LTS, then rerun: npx scalemax@latest');
8
+ console.error('macOS: brew install node@22 && brew link --overwrite node@22');
9
+ console.error('Ubuntu/Debian/WSL: curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt-get install -y nodejs');
10
+ console.error('Fedora: sudo dnf install -y nodejs npm');
11
+ console.error('Arch: sudo pacman -S --needed nodejs npm');
12
+ console.error('Windows PowerShell: winget install OpenJS.NodeJS.LTS');
13
+ process.exit(1);
14
+ }
15
+ import('./index.js').catch(function(err){ console.error(err && err.stack || err); process.exit(1); });
package/dist/clients.js CHANGED
@@ -17,7 +17,7 @@ import * as vscodeConfig from './configure/vscode.js';
17
17
  import * as windsurfDetect from './detect/windsurf.js';
18
18
  import * as windsurfConfig from './configure/windsurf.js';
19
19
  export const clients = [
20
- { id: 'codex', name: 'Codex', detect: codexDetect.detect, configure: codexConfig.configure, remove: codexConfig.remove },
20
+ { id: 'codex', name: 'Codex', detect: codexDetect.detect, install: codexConfig.install, configure: codexConfig.configure, verify: codexConfig.verify, launch: codexConfig.launch, remove: codexConfig.remove },
21
21
  { id: 'cursor', name: 'Cursor', detect: cursorDetect.detect, configure: cursorConfig.configure, remove: cursorConfig.remove },
22
22
  { id: 'claude', name: 'Claude Code', detect: claudeDetect.detect, configure: claudeConfig.configure, remove: claudeConfig.remove },
23
23
  { id: 'opencode', name: 'OpenCode', detect: opencodeDetect.detect, configure: opencodeConfig.configure, remove: opencodeConfig.remove },
@@ -5,7 +5,6 @@ import { clients } from '../clients.js';
5
5
  import { SCALEMAX_BASE_URL } from '../constants.js';
6
6
  import { loadApiKey } from '../utils/keyStore.js';
7
7
  import { homeDir } from '../utils/paths.js';
8
- import { launchCodex } from '../configure/codex.js';
9
8
  export async function detectAll(home = homeDir()) {
10
9
  const ctx = { home, platform: process.platform, env: process.env };
11
10
  return Promise.all(clients.map(c => c.detect(ctx)));
@@ -26,13 +25,16 @@ async function maybeLaunchCodex(chosen, successful) {
26
25
  if (isCancel(answer) || answer === false)
27
26
  return;
28
27
  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);
28
+ const codex = clients.find(c => c.id === 'codex');
29
+ try {
30
+ const apiKey = await loadApiKey(homeDir());
31
+ if (!apiKey)
32
+ throw new Error('No API key found.');
33
+ const result = await codex?.launch?.({ home: homeDir(), platform: process.platform, env: process.env, apiKey, baseUrl: SCALEMAX_BASE_URL });
34
+ console.log(chalk.green(`✓ ${result?.message || 'Codex launched.'}`));
35
+ }
36
+ catch (err) {
37
+ console.log(chalk.red(`✗ ${err instanceof Error ? err.message : String(err)}`));
36
38
  }
37
39
  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
40
  }
@@ -63,11 +65,29 @@ export async function configureCommand(opts = {}) {
63
65
  }
64
66
  const successful = new Set();
65
67
  for (const client of clients.filter(c => chosen.includes(c.id))) {
68
+ const baseCtx = { home, platform: process.platform, env: process.env };
69
+ if (client.install) {
70
+ const spinner = ora(`Checking ${client.name}`).start();
71
+ try {
72
+ const res = await client.install({ ...baseCtx, interactive: process.stdin.isTTY });
73
+ spinner.succeed(`${client.name} ${res.message}`);
74
+ }
75
+ catch (err) {
76
+ spinner.fail(`${client.name} install failed: ${err instanceof Error ? err.message : String(err)}`);
77
+ continue;
78
+ }
79
+ }
66
80
  const spinner = ora(`Configuring ${client.name}`).start();
67
81
  try {
68
- const res = await client.configure({ home, platform: process.platform, env: process.env, apiKey, baseUrl: SCALEMAX_BASE_URL });
69
- successful.add(client.id);
82
+ const ctx = { ...baseCtx, apiKey, baseUrl: SCALEMAX_BASE_URL };
83
+ const res = await client.configure(ctx);
70
84
  spinner.succeed(`${client.name} ${res.message}`);
85
+ if (client.verify) {
86
+ const verify = ora(`Verifying ${client.name}`).start();
87
+ const v = await client.verify(ctx);
88
+ verify.succeed(`${client.name} ${v.message}`);
89
+ }
90
+ successful.add(client.id);
71
91
  }
72
92
  catch (err) {
73
93
  spinner.fail(`${client.name} failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -1,4 +1,4 @@
1
- import { ConfigureContext, ConfigureResult } from '../types/client.js';
1
+ import { ConfigureContext, ConfigureResult, InstallContext } from '../types/client.js';
2
2
  export interface CodexVerificationResult {
3
3
  ok: boolean;
4
4
  warnings: string[];
@@ -7,8 +7,11 @@ export interface CodexVerificationResult {
7
7
  authPath: string;
8
8
  }
9
9
  export declare function verifyCodexConfiguration(ctx: ConfigureContext): Promise<CodexVerificationResult>;
10
+ export declare function install(ctx: InstallContext): Promise<ConfigureResult>;
11
+ export declare function verify(ctx: ConfigureContext): Promise<ConfigureResult>;
10
12
  export declare function configure(ctx: ConfigureContext): Promise<ConfigureResult>;
11
13
  export declare function remove(ctx: ConfigureContext): Promise<ConfigureResult>;
14
+ export declare function launch(ctx?: ConfigureContext): Promise<ConfigureResult>;
12
15
  export declare function launchCodex(): Promise<{
13
16
  ok: boolean;
14
17
  message: string;
@@ -164,6 +164,20 @@ export async function verifyCodexConfiguration(ctx) {
164
164
  }
165
165
  return { ok: errors.length === 0, warnings, errors, configPath, authPath };
166
166
  }
167
+ export async function install(ctx) {
168
+ const mod = await import('../install/codex.js');
169
+ const res = await mod.ensureCodexInstalled(ctx.interactive ?? process.stdin.isTTY);
170
+ if (!res.ok)
171
+ throw new Error(res.message);
172
+ return { id, name, changed: res.installed, message: res.message };
173
+ }
174
+ export async function verify(ctx) {
175
+ const verification = await verifyCodexConfiguration(ctx);
176
+ if (!verification.ok)
177
+ throw new Error(verification.errors.join('\n'));
178
+ const warnings = verification.warnings.length ? ` Warnings: ${verification.warnings.join(' ')}` : '';
179
+ return { id, name, changed: false, path: verification.configPath, message: `verified.${warnings}` };
180
+ }
167
181
  export async function configure(ctx) {
168
182
  const configPath = codexConfigPath(ctx.home);
169
183
  const authPath = codexAuthPath(ctx.home);
@@ -195,6 +209,12 @@ export async function remove(ctx) {
195
209
  messages.push(`legacy config.json: ${await restoreOrRemove(legacyConfig, ctx.home)}`);
196
210
  return { id, name, changed: true, path: codexConfigPath(ctx.home), message: messages.join('; ') };
197
211
  }
212
+ export async function launch(ctx) {
213
+ const result = await launchCodex();
214
+ if (!result.ok)
215
+ throw new Error(result.output ? `${result.message} ${result.output}` : result.message);
216
+ return { id, name, changed: false, message: result.message };
217
+ }
198
218
  export async function launchCodex() {
199
219
  if (!(await commandExists('codex')))
200
220
  return { ok: false, message: 'Codex executable was not found on PATH.' };
@@ -1,2 +1,19 @@
1
- import { detectClient } from './common.js';
2
- export function detect(ctx) { return detectClient(ctx, 'codex', 'Codex'); }
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,8 @@ 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 } from './utils/runtime.js';
16
+ import { runSystemAudit } from './utils/systemAudit.js';
15
17
  function packageVersion() {
16
18
  const here = path.dirname(fileURLToPath(import.meta.url));
17
19
  const candidates = [path.join(here, '..', 'package.json'), path.join(here, 'package.json')];
@@ -30,14 +32,21 @@ function welcome() {
30
32
  console.log('\nThe fastest way to connect every AI coding client.\n');
31
33
  }
32
34
  async function main() {
35
+ if (!isNodeSupported()) {
36
+ console.error(chalk.red(`ScaleMax requires Node.js >=18.17. Current: ${process.versions.node}`));
37
+ console.error(nodeUpgradeHint());
38
+ process.exitCode = 1;
39
+ return;
40
+ }
33
41
  const [, , command] = process.argv;
34
42
  try {
35
43
  if (!command) {
36
44
  welcome();
45
+ await runSystemAudit();
37
46
  const apiKey = await ensureInstallerApiKey(process.env.HOME || process.env.USERPROFILE || process.cwd());
38
47
  if (!apiKey)
39
48
  return;
40
- await configureCommand();
49
+ await configureCommand({ promptLaunch: true });
41
50
  outro(chalk.green('ScaleMax is ready.'));
42
51
  return;
43
52
  }
@@ -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
+ }
@@ -3,7 +3,10 @@ export interface ClientDefinition {
3
3
  id: ClientId;
4
4
  name: string;
5
5
  detect: (ctx: DetectContext) => Promise<DetectionResult>;
6
+ install?: (ctx: InstallContext) => Promise<ConfigureResult>;
6
7
  configure: (ctx: ConfigureContext) => Promise<ConfigureResult>;
8
+ verify?: (ctx: ConfigureContext) => Promise<ConfigureResult>;
9
+ launch?: (ctx: ConfigureContext) => Promise<ConfigureResult>;
7
10
  remove: (ctx: ConfigureContext) => Promise<ConfigureResult>;
8
11
  }
9
12
  export interface DetectContext {
@@ -17,6 +20,12 @@ export interface DetectionResult {
17
20
  installed: boolean;
18
21
  reason?: string;
19
22
  paths: string[];
23
+ executable?: boolean;
24
+ configured?: boolean;
25
+ installable?: boolean;
26
+ }
27
+ export interface InstallContext extends DetectContext {
28
+ interactive?: boolean;
20
29
  }
21
30
  export interface ConfigureContext extends DetectContext {
22
31
  apiKey: string;
@@ -0,0 +1,24 @@
1
+ export declare const MIN_NODE = "18.17.0";
2
+ export declare const RECOMMENDED_NODE = "22";
3
+ export type OsFamily = 'macos' | 'ubuntu' | 'debian' | 'fedora' | 'arch' | 'windows' | 'wsl' | 'linux' | 'unknown';
4
+ export type RuntimeAudit = {
5
+ os: OsFamily;
6
+ platform: NodeJS.Platform;
7
+ arch: string;
8
+ isWsl: boolean;
9
+ nodeVersion: string;
10
+ nodeOk: boolean;
11
+ npmVersion?: string;
12
+ npmOk: boolean;
13
+ pathEntries: string[];
14
+ packageManager?: string;
15
+ installCommand?: string;
16
+ };
17
+ export declare function compareSemver(a: string, b: string): number;
18
+ export declare function isNodeSupported(version?: string): boolean;
19
+ export declare function npmVersion(): Promise<string | undefined>;
20
+ export declare function detectOsFamily(): Promise<OsFamily>;
21
+ export declare function runtimeAudit(): Promise<RuntimeAudit>;
22
+ export declare function nodeInstallCommand(osFamily: OsFamily, pm?: string): string | undefined;
23
+ export declare function nodeUpgradeHint(): string;
24
+ export declare function runtimeReport(): Promise<string>;
@@ -0,0 +1,72 @@
1
+ import os from 'node:os';
2
+ import { execa } from 'execa';
3
+ import { commandExists } from './exec.js';
4
+ export const MIN_NODE = '18.17.0';
5
+ export const RECOMMENDED_NODE = '22';
6
+ 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++) {
7
+ const d = (pa[i] || 0) - (pb[i] || 0);
8
+ if (d)
9
+ return d;
10
+ } return 0; }
11
+ export function isNodeSupported(version = process.versions.node) { return compareSemver(version, MIN_NODE) >= 0; }
12
+ export async function npmVersion() { try {
13
+ return (await execa(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['-v'])).stdout.trim();
14
+ }
15
+ catch {
16
+ return undefined;
17
+ } }
18
+ async function detectLinuxId() {
19
+ try {
20
+ const txt = await import('node:fs/promises').then(fs => fs.readFile('/etc/os-release', 'utf8'));
21
+ return (txt.match(/^ID=(.+)$/m)?.[1] || '').replace(/['"]/g, '').toLowerCase();
22
+ }
23
+ catch {
24
+ return '';
25
+ }
26
+ }
27
+ export async function detectOsFamily() {
28
+ const release = os.release().toLowerCase();
29
+ const isWsl = process.platform === 'linux' && (release.includes('microsoft') || release.includes('wsl'));
30
+ if (isWsl)
31
+ return 'wsl';
32
+ if (process.platform === 'darwin')
33
+ return 'macos';
34
+ if (process.platform === 'win32')
35
+ return 'windows';
36
+ if (process.platform === 'linux') {
37
+ const id = await detectLinuxId();
38
+ if (['ubuntu', 'debian', 'fedora', 'arch'].includes(id))
39
+ return id;
40
+ return 'linux';
41
+ }
42
+ return 'unknown';
43
+ }
44
+ async function firstCommand(commands) { for (const c of commands)
45
+ if (await commandExists(c))
46
+ return c; return undefined; }
47
+ export async function runtimeAudit() {
48
+ const osFamily = await detectOsFamily();
49
+ const npm = await npmVersion();
50
+ const pathEntries = (process.env.PATH || '').split(process.platform === 'win32' ? ';' : ':').filter(Boolean);
51
+ const packageManager = await firstCommand(process.platform === 'win32' ? ['winget', 'choco', 'scoop'] : ['brew', 'apt-get', 'dnf', 'pacman', 'npm']);
52
+ return { os: osFamily, platform: process.platform, arch: process.arch, isWsl: osFamily === 'wsl', nodeVersion: process.versions.node, nodeOk: isNodeSupported(), npmVersion: npm, npmOk: Boolean(npm), pathEntries, packageManager, installCommand: nodeInstallCommand(osFamily, packageManager) };
53
+ }
54
+ export function nodeInstallCommand(osFamily, pm) {
55
+ if (osFamily === 'macos' && pm === 'brew')
56
+ return 'brew install node@22 && brew link --overwrite node@22';
57
+ if ((osFamily === 'ubuntu' || osFamily === 'debian' || osFamily === 'wsl') && pm === 'apt-get')
58
+ return 'curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt-get install -y nodejs';
59
+ if (osFamily === 'fedora' && pm === 'dnf')
60
+ return 'sudo dnf install -y nodejs npm';
61
+ if (osFamily === 'arch' && pm === 'pacman')
62
+ return 'sudo pacman -S --needed nodejs npm';
63
+ if (osFamily === 'windows' && pm === 'winget')
64
+ return 'winget install OpenJS.NodeJS.LTS';
65
+ if (osFamily === 'windows' && pm === 'choco')
66
+ return 'choco install nodejs-lts -y';
67
+ if (osFamily === 'windows' && pm === 'scoop')
68
+ return 'scoop install nodejs-lts';
69
+ return undefined;
70
+ }
71
+ export function nodeUpgradeHint() { return ['Install Node.js LTS, then rerun: npx scalemax@latest', 'macOS Homebrew: brew install node@22 && brew link --overwrite node@22', 'Ubuntu/Debian/WSL: curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt-get install -y nodejs', 'Fedora: sudo dnf install -y nodejs npm', 'Arch: sudo pacman -S --needed nodejs npm', 'Windows PowerShell: winget install OpenJS.NodeJS.LTS'].join('\n'); }
72
+ export async function runtimeReport() { const a = await runtimeAudit(); return `OS ${a.os} ${a.arch} · Node ${a.nodeVersion} ${a.nodeOk ? '✓' : '✗'} (required >=${MIN_NODE}) · npm ${a.npmVersion || 'not found'} ${a.npmOk ? '✓' : '✗'} · PATH entries ${a.pathEntries.length}`; }
@@ -0,0 +1 @@
1
+ export declare function runSystemAudit(): Promise<void>;
@@ -0,0 +1,32 @@
1
+ import { confirm, isCancel } from '@clack/prompts';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { execa } from 'execa';
5
+ import { runtimeAudit, nodeUpgradeHint } from './runtime.js';
6
+ function check(label, ok, value) { console.log(`${ok ? chalk.green('✓') : chalk.red('✗')} ${label} ${value}`); }
7
+ async function runCommand(command) { const res = await execa(command, { shell: true, stdio: 'inherit', reject: false }); return res.exitCode === 0; }
8
+ export async function runSystemAudit() {
9
+ const spin = ora('Checking system').start();
10
+ const audit = await runtimeAudit();
11
+ spin.stop();
12
+ check('Operating system', true, `${audit.os}${audit.isWsl ? ' (WSL)' : ''}`);
13
+ check('Architecture', true, audit.arch);
14
+ check('Node.js', audit.nodeOk, `v${audit.nodeVersion}`);
15
+ check('npm', audit.npmOk, audit.npmVersion ? `v${audit.npmVersion}` : 'not found');
16
+ check('PATH', audit.pathEntries.length > 0, `${audit.pathEntries.length} entries`);
17
+ if (audit.nodeOk && audit.npmOk)
18
+ return;
19
+ console.log(chalk.yellow('\nScaleMax needs a supported Node.js/npm runtime before continuing.'));
20
+ if (audit.installCommand) {
21
+ console.log(`\nRecommended command:\n${chalk.cyan(audit.installCommand)}\n`);
22
+ const ans = await confirm({ message: 'Run this command now?', initialValue: false });
23
+ if (!isCancel(ans) && ans === true) {
24
+ const ok = await runCommand(audit.installCommand);
25
+ if (!ok)
26
+ throw new Error('Automatic Node/npm installation failed. Run the command above manually, then rerun npx scalemax.');
27
+ console.log(chalk.green('✓ Node/npm installation command completed. Re-run npx scalemax if this process does not resume automatically.'));
28
+ return;
29
+ }
30
+ }
31
+ throw new Error(`Node/npm setup is incomplete.\n${nodeUpgradeHint()}`);
32
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "scalemax",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Universal ScaleMax installer for AI coding clients",
5
5
  "type": "module",
6
6
  "bin": {
7
- "scalemax": "dist/index.js"
7
+ "scalemax": "dist/bootstrap.cjs"
8
8
  },
9
9
  "files": [
10
10
  "dist/",