From fd400476045cee3059a92a9b4c452a88b98c7431 Mon Sep 17 00:00:00 2001 From: rafaelscosta Date: Wed, 20 May 2026 22:42:23 -0300 Subject: [PATCH] fix(installer): resolve npm cli when launched via npx --- .aiox-core/install-manifest.yaml | 6 ++-- .aiox-core/package.json | 2 +- CHANGELOG.md | 13 +++++++++ compat/aiox-core/package.json | 4 +-- package-lock.json | 6 ++-- package.json | 2 +- packages/installer/package.json | 2 +- packages/installer/src/wizard/pro-setup.js | 26 +++++++++++++++-- tests/installer/pro-setup-auth.test.js | 33 ++++++++++++++++++++++ 9 files changed, 81 insertions(+), 13 deletions(-) diff --git a/.aiox-core/install-manifest.yaml b/.aiox-core/install-manifest.yaml index acdb4f412d..6860ee9e80 100644 --- a/.aiox-core/install-manifest.yaml +++ b/.aiox-core/install-manifest.yaml @@ -7,8 +7,8 @@ # - SHA256 hashes for change detection # - File types for categorization # -version: 5.2.8 -generated_at: "2026-05-21T00:21:31.048Z" +version: 5.2.9 +generated_at: "2026-05-21T01:42:24.351Z" generator: scripts/generate-install-manifest.js file_count: 1129 files: @@ -3701,7 +3701,7 @@ files: type: monitor size: 818 - path: package.json - hash: sha256:bc1bd0eae7c04feb1d1659fb9715a7c908e1cf2bb146341ed3b0d24f630cc9a9 + hash: sha256:d8dbe037240a366d545ca4259d2059e705b709706dcf2a18a5a52d9b543b7ed9 type: other size: 1445 - path: product/checklists/accessibility-wcag-checklist.md diff --git a/.aiox-core/package.json b/.aiox-core/package.json index 134a6dcb6b..9243688b98 100644 --- a/.aiox-core/package.json +++ b/.aiox-core/package.json @@ -1,6 +1,6 @@ { "name": "@aiox-squads/core-internal", - "version": "5.2.8", + "version": "5.2.9", "description": "Internal package manifest for AIOX framework — declares runtime dependencies consumed by scripts under .aiox-core/. This is NOT published independently; it ships inside @aiox-squads/core (the top-level package). Kept name-distinct from the parent (`-internal` suffix) to avoid npm registry confusion. Version follows the parent package.", "private": true, "main": "index.js", diff --git a/CHANGELOG.md b/CHANGELOG.md index 6115fdcb99..c294f082e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,19 @@ All notable changes to Synkra AIOX will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.2.9] - 2026-05-21 + +### Fixed + +- **Pro artifact install no longer invokes `npx install` when the installer itself runs through `npx`**. + - Root cause: `npm_execpath` can point at `npx-cli.js` during `npx -p @aiox-squads/core aiox install`. + - Fix: the installer now derives the sibling `npm-cli.js` when available and otherwise falls back to `npm`, so Pro artifact extraction keeps running `npm install --prefix `. + - This targets Linux installs that authenticated and activated Pro successfully, then failed at content installation with `unexpected argument '--prefix' found`. + +### Changed + +- `@aiox-squads/installer` bumped to `3.3.8` to ship the Pro artifact extraction fix. + ## [5.2.8] - 2026-05-21 ### Fixed diff --git a/compat/aiox-core/package.json b/compat/aiox-core/package.json index 590da9928d..60e44f10f8 100644 --- a/compat/aiox-core/package.json +++ b/compat/aiox-core/package.json @@ -1,6 +1,6 @@ { "name": "aiox-core", - "version": "5.2.8", + "version": "5.2.9", "description": "Compatibility wrapper for @aiox-squads/core.", "license": "MIT", "bin": { @@ -15,7 +15,7 @@ "README.md" ], "dependencies": { - "@aiox-squads/core": "5.2.8" + "@aiox-squads/core": "5.2.9" }, "engines": { "node": ">=18" diff --git a/package-lock.json b/package-lock.json index 5f6a55249f..f5be15626c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@aiox-squads/core", - "version": "5.2.8", + "version": "5.2.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@aiox-squads/core", - "version": "5.2.8", + "version": "5.2.9", "license": "MIT", "workspaces": [ "packages/*" @@ -14518,7 +14518,7 @@ }, "packages/installer": { "name": "@aiox-squads/installer", - "version": "3.3.7", + "version": "3.3.8", "license": "MIT", "dependencies": { "@aiox-squads/core": "^5.1.0", diff --git a/package.json b/package.json index 651d1c20fa..b2d24904c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@aiox-squads/core", - "version": "5.2.8", + "version": "5.2.9", "description": "Synkra AIOX: AI-Orchestrated System for Full Stack Development - Core Framework", "bin": { "aiox": "bin/aiox.js", diff --git a/packages/installer/package.json b/packages/installer/package.json index 01638b0b5d..908fe481b0 100644 --- a/packages/installer/package.json +++ b/packages/installer/package.json @@ -1,6 +1,6 @@ { "name": "@aiox-squads/installer", - "version": "3.3.7", + "version": "3.3.8", "description": "AIOX Installer - Automated setup wizard for AIOX projects (greenfield & brownfield)", "main": "src/index.js", "bin": { diff --git a/packages/installer/src/wizard/pro-setup.js b/packages/installer/src/wizard/pro-setup.js index 6b5170cf69..a3e3bf21ce 100644 --- a/packages/installer/src/wizard/pro-setup.js +++ b/packages/installer/src/wizard/pro-setup.js @@ -32,17 +32,39 @@ function stripWrappingQuotes(value) { return String(value || '').trim().replace(/^"(.*)"$/, '$1'); } +function resolveNpmExecPath(npmExecPath, fileExists = fs.existsSync) { + if (!npmExecPath || !/\.js$/i.test(npmExecPath)) { + return null; + } + + const pathApi = npmExecPath.includes('\\') ? path.win32 : path.posix; + const basename = pathApi.basename(npmExecPath).toLowerCase(); + if (basename === 'npm-cli.js' && fileExists(npmExecPath)) { + return npmExecPath; + } + + if (basename === 'npx-cli.js') { + const npmCliPath = pathApi.join(pathApi.dirname(npmExecPath), 'npm-cli.js'); + if (fileExists(npmCliPath)) { + return npmCliPath; + } + } + + return null; +} + function resolveNpmInvocation(options = {}) { const platform = options.platform || process.platform; const env = options.env || process.env; const execPath = options.execPath || process.execPath; const fileExists = options.fileExists || fs.existsSync; const npmExecPath = stripWrappingQuotes(env.npm_execpath); + const npmCliPath = resolveNpmExecPath(npmExecPath, fileExists); - if (npmExecPath && /\.js$/i.test(npmExecPath) && fileExists(npmExecPath)) { + if (npmCliPath) { return { command: execPath, - prefixArgs: [npmExecPath], + prefixArgs: [npmCliPath], execOptions: {}, }; } diff --git a/tests/installer/pro-setup-auth.test.js b/tests/installer/pro-setup-auth.test.js index dbc7f06965..ef3b12ca8e 100644 --- a/tests/installer/pro-setup-auth.test.js +++ b/tests/installer/pro-setup-auth.test.js @@ -187,6 +187,39 @@ describe('pro-setup npm invocation', () => { }); }); + it('derives npm-cli.js when invoked through npx-cli.js', () => { + const invocation = proSetup._testing.resolveNpmInvocation({ + platform: 'linux', + execPath: '/usr/bin/node', + env: { + npm_execpath: '/usr/lib/node_modules/npm/bin/npx-cli.js', + }, + fileExists: (candidate) => candidate.endsWith('/npm-cli.js'), + }); + + expect(invocation).toEqual({ + command: '/usr/bin/node', + prefixArgs: ['/usr/lib/node_modules/npm/bin/npm-cli.js'], + execOptions: {}, + }); + }); + + it('does not execute npx-cli.js as npm when npm-cli.js cannot be found', () => { + const invocation = proSetup._testing.resolveNpmInvocation({ + platform: 'linux', + env: { + npm_execpath: '/usr/lib/node_modules/npm/bin/npx-cli.js', + }, + fileExists: () => false, + }); + + expect(invocation).toEqual({ + command: 'npm', + prefixArgs: [], + execOptions: {}, + }); + }); + it('falls back to shell execution for npm.cmd on Windows', () => { const invocation = proSetup._testing.resolveNpmInvocation({ platform: 'win32',