99 * aiox pro deactivate Deactivate the current license
1010 * aiox pro features List all pro features
1111 * aiox pro validate Force online revalidation
12- * aiox pro setup Configure GitHub Packages access (AC-12)
12+ * aiox pro setup Install or verify the AIOX Pro package
1313 *
1414 * @module cli/commands/pro
1515 * @version 1.1.0
@@ -25,31 +25,44 @@ const readline = require('readline');
2525
2626// BUG-6 fix (INS-1): Dynamic licensePath resolution
2727// In framework-dev: __dirname = aiox-core/.aiox-core/cli/commands/pro → ../../../../pro/license
28- // In project-dev: pro is installed via npm as @aiox-fullstack/pro
28+ // In project-dev: pro is installed via npm as @aiox-fullstack/pro or @aios-fullstack/pro
2929function resolveLicensePath ( ) {
3030 // 1. Try relative path (framework-dev mode)
3131 const relativePath = path . resolve ( __dirname , '..' , '..' , '..' , '..' , 'pro' , 'license' ) ;
3232 if ( fs . existsSync ( relativePath ) ) {
3333 return relativePath ;
3434 }
3535
36- // 2. Try node_modules/@aiox-fullstack/pro/license (project-dev mode)
37- try {
38- const proPkg = require . resolve ( '@aiox-fullstack/pro/package.json' ) ;
39- const proDir = path . dirname ( proPkg ) ;
40- const npmPath = path . join ( proDir , 'license' ) ;
41- if ( fs . existsSync ( npmPath ) ) {
42- return npmPath ;
36+ // 2. Try npm packages — canonical then fallback
37+ const npmCandidates = [
38+ '@aiox-fullstack/pro' ,
39+ '@aios-fullstack/pro' ,
40+ ] ;
41+
42+ for ( const pkgName of npmCandidates ) {
43+ try {
44+ const proPkg = require . resolve ( `${ pkgName } /package.json` ) ;
45+ const proDir = path . dirname ( proPkg ) ;
46+ const npmPath = path . join ( proDir , 'license' ) ;
47+ if ( fs . existsSync ( npmPath ) ) {
48+ return npmPath ;
49+ }
50+ } catch {
51+ // package not installed
4352 }
44- } catch {
45- // @aiox -fullstack/pro not installed via npm
4653 }
4754
48- // 3. Try project root node_modules (fallback )
55+ // 3. Try project root node_modules (both scopes )
4956 const projectRoot = process . cwd ( ) ;
50- const cwdPath = path . join ( projectRoot , 'node_modules' , '@aiox-fullstack' , 'pro' , 'license' ) ;
51- if ( fs . existsSync ( cwdPath ) ) {
52- return cwdPath ;
57+ const scopePaths = [
58+ path . join ( projectRoot , 'node_modules' , '@aiox-fullstack' , 'pro' , 'license' ) ,
59+ path . join ( projectRoot , 'node_modules' , '@aios-fullstack' , 'pro' , 'license' ) ,
60+ ] ;
61+
62+ for ( const cwdPath of scopePaths ) {
63+ if ( fs . existsSync ( cwdPath ) ) {
64+ return cwdPath ;
65+ }
5366 }
5467
5568 // Return relative path as default (will fail gracefully in loadLicenseModules)
@@ -97,7 +110,8 @@ function loadLicenseModules() {
97110 } ;
98111 } catch ( error ) {
99112 console . error ( 'AIOX Pro license module not available.' ) ;
100- console . error ( 'Install AIOX Pro: npm install @aiox-fullstack/pro' ) ;
113+ console . error ( 'Install AIOX Pro: aiox pro setup' ) ;
114+ console . error ( 'Or via wrapper: npx aiox-pro install' ) ;
101115 process . exit ( 1 ) ;
102116 }
103117}
@@ -217,9 +231,13 @@ async function activateAction(options) {
217231 // Scaffold pro content into project (Story INS-3.1)
218232 // Lazy-load to avoid crashing if pro-scaffolder or js-yaml is unavailable
219233 const projectRoot = path . resolve ( __dirname , '..' , '..' , '..' , '..' ) ;
220- const proSourceDir = path . join ( projectRoot , 'node_modules' , '@aiox-fullstack' , 'pro' ) ;
234+ // Try canonical then fallback package path
235+ const proSourceDir = [
236+ path . join ( projectRoot , 'node_modules' , '@aiox-fullstack' , 'pro' ) ,
237+ path . join ( projectRoot , 'node_modules' , '@aios-fullstack' , 'pro' ) ,
238+ ] . find ( p => fs . existsSync ( p ) ) ;
221239
222- if ( fs . existsSync ( proSourceDir ) ) {
240+ if ( proSourceDir ) {
223241 let scaffoldProContent ;
224242 try {
225243 ( { scaffoldProContent } = require ( '../../../../packages/installer/src/pro/pro-scaffolder' ) ) ;
@@ -261,7 +279,7 @@ async function activateAction(options) {
261279 console . log ( '' ) ;
262280 }
263281 } else {
264- console . log ( 'Note: @aiox-fullstack/pro package not found in node_modules.' ) ;
282+ console . log ( 'Note: AIOX Pro package not found in node_modules.' ) ;
265283 console . log ( 'Pro content will be scaffolded when the package is installed.' ) ;
266284 console . log ( '' ) ;
267285 }
@@ -571,64 +589,108 @@ async function validateAction() {
571589// ---------------------------------------------------------------------------
572590
573591/**
574- * Setup and verify @aiox-fullstack/pro installation.
592+ * Setup and verify AIOX Pro installation.
575593 *
576- * Since @aiox-fullstack/pro is published on the public npm registry,
577- * no special token or .npmrc configuration is needed. This command
578- * installs the package and verifies it's working.
594+ * Tries canonical @aiox-fullstack/pro first, falls back to @aios-fullstack/pro.
579595 *
580596 * @param {object } options - Command options
581597 * @param {boolean } options.verify - Only verify without installing
582598 */
583599async function setupAction ( options ) {
600+ const PRO_PACKAGES = [ '@aiox-fullstack/pro' , '@aios-fullstack/pro' ] ;
601+
584602 console . log ( '\nAIOX Pro - Setup\n' ) ;
585603
586604 if ( options . verify ) {
587- // Verify-only mode
588- console . log ( 'Verifying @aiox-fullstack/pro installation...\n' ) ;
605+ console . log ( 'Verifying AIOX Pro installation...\n' ) ;
589606
590607 try {
591608 const { execSync } = require ( 'child_process' ) ;
592- const result = execSync ( 'npm ls @aiox-fullstack/pro --json' , {
593- stdio : 'pipe' ,
594- timeout : 15000 ,
595- } ) ;
596- const parsed = JSON . parse ( result . toString ( ) ) ;
597- const deps = parsed . dependencies || { } ;
598- if ( deps [ '@aiox-fullstack/pro' ] ) {
599- console . log ( `✅ @aiox-fullstack/pro@${ deps [ '@aiox-fullstack/pro' ] . version } is installed` ) ;
600- } else {
601- console . log ( '❌ @aiox-fullstack/pro is not installed' ) ;
609+ let found = false ;
610+ for ( const pkg of PRO_PACKAGES ) {
611+ try {
612+ const result = execSync ( `npm ls ${ pkg } --json` , { stdio : 'pipe' , timeout : 15000 } ) ;
613+ const parsed = JSON . parse ( result . toString ( ) ) ;
614+ const deps = parsed . dependencies || { } ;
615+ if ( deps [ pkg ] ) {
616+ console . log ( `✅ ${ pkg } @${ deps [ pkg ] . version } is installed` ) ;
617+ found = true ;
618+ break ;
619+ }
620+ } catch { /* try next */ }
621+ }
622+ if ( ! found ) {
623+ console . log ( '❌ AIOX Pro is not installed' ) ;
602624 console . log ( '' ) ;
603625 console . log ( 'Install with:' ) ;
604- console . log ( ' npm install @aiox-fullstack/pro' ) ;
626+ console . log ( ' aiox pro setup' ) ;
627+ console . log ( ' # or npx aiox-pro install' ) ;
605628 }
606629 } catch {
607- console . log ( '❌ @aiox-fullstack/pro is not installed' ) ;
630+ console . log ( '❌ AIOX Pro is not installed' ) ;
608631 console . log ( '' ) ;
609632 console . log ( 'Install with:' ) ;
610- console . log ( ' npm install @aiox-fullstack/pro' ) ;
633+ console . log ( ' aiox pro setup' ) ;
634+ console . log ( ' # or npx aiox-pro install' ) ;
611635 }
612636 return ;
613637 }
614638
615- // Install mode
616- console . log ( '@aiox-fullstack/pro is available on the public npm registry.' ) ;
639+ // Install mode — try canonical first, fallback second
640+ console . log ( 'AIOX Pro is available on the public npm registry.' ) ;
617641 console . log ( 'No special tokens or configuration needed.\n' ) ;
618642
619- console . log ( 'Installing @aiox-fullstack/pro...\n' ) ;
643+ const { execSync } = require ( 'child_process' ) ;
644+ let installedPackage = null ;
620645
621- try {
622- const { execSync } = require ( 'child_process' ) ;
623- execSync ( 'npm install @aiox-fullstack/pro' , {
624- stdio : 'inherit' ,
625- timeout : 120000 ,
626- } ) ;
627- console . log ( '\n✅ @aiox-fullstack/pro installed successfully!' ) ;
628- } catch ( error ) {
629- console . error ( `\n❌ Installation failed: ${ error . message } ` ) ;
646+ function getInstallErrorOutput ( error ) {
647+ return [
648+ error ?. message ,
649+ error ?. stderr ?. toString ?. ( ) ,
650+ error ?. stdout ?. toString ?. ( ) ,
651+ ] . filter ( Boolean ) . join ( '\n' ) ;
652+ }
653+
654+ function isPackageNotFoundError ( error , pkg ) {
655+ const output = getInstallErrorOutput ( error ) . toLowerCase ( ) ;
656+ const packageName = pkg . toLowerCase ( ) ;
657+
658+ if ( ! output . includes ( packageName ) ) {
659+ return false ;
660+ }
661+
662+ return output . includes ( 'e404' )
663+ || output . includes ( 'npm err! 404' )
664+ || output . includes ( ' is not in this registry' )
665+ || output . includes ( ' not found' ) ;
666+ }
667+
668+ for ( const pkg of PRO_PACKAGES ) {
669+ try {
670+ console . log ( `Installing ${ pkg } ...\n` ) ;
671+ execSync ( `npm install ${ pkg } ` , { stdio : 'inherit' , timeout : 120000 } ) ;
672+ console . log ( `\n✅ ${ pkg } installed successfully!` ) ;
673+ installedPackage = pkg ;
674+ break ;
675+ } catch ( error ) {
676+ if ( isPackageNotFoundError ( error , pkg ) ) {
677+ continue ;
678+ }
679+
680+ console . error ( `\n❌ Failed to install ${ pkg } .` ) ;
681+ const details = getInstallErrorOutput ( error ) ;
682+ if ( details ) {
683+ console . error ( details ) ;
684+ }
685+ process . exit ( 1 ) ;
686+ }
687+ }
688+
689+ if ( ! installedPackage ) {
690+ console . error ( '\n❌ Installation failed.' ) ;
630691 console . log ( '\nTry manually:' ) ;
631- console . log ( ' npm install @aiox-fullstack/pro' ) ;
692+ console . log ( ' aiox pro setup' ) ;
693+ console . log ( ' # or npx aiox-pro install' ) ;
632694 process . exit ( 1 ) ;
633695 }
634696
@@ -644,6 +706,66 @@ async function setupAction(options) {
644706 console . log ( '' ) ;
645707}
646708
709+ // ---------------------------------------------------------------------------
710+ // aiox pro update (Story 122.3)
711+ // ---------------------------------------------------------------------------
712+
713+ async function updateAction ( options ) {
714+ const proUpdaterPath = path . resolve ( __dirname , '..' , '..' , '..' , 'core' , 'pro' , 'pro-updater' ) ;
715+ let updatePro , formatUpdateResult ;
716+
717+ try {
718+ ( { updatePro, formatUpdateResult } = require ( proUpdaterPath ) ) ;
719+ } catch {
720+ console . error ( '❌ Pro updater module not found.' ) ;
721+ console . error ( 'Please ensure aiox-core is installed correctly.' ) ;
722+ process . exit ( 1 ) ;
723+ }
724+
725+ const projectRoot = process . cwd ( ) ;
726+
727+ // Validate license before updating (unless --check)
728+ if ( ! options . check && ! options . dryRun ) {
729+ try {
730+ const { featureGate } = loadLicenseModules ( ) ;
731+ const state = featureGate . getLicenseState ( ) ;
732+ if ( state !== 'Active' && state !== 'Grace' ) {
733+ console . error ( '\n❌ AIOX Pro license is not active.' ) ;
734+ console . error ( 'Activate your license first: aiox pro activate --key PRO-XXXX-XXXX-XXXX-XXXX' ) ;
735+ process . exit ( 1 ) ;
736+ }
737+ } catch {
738+ // License modules not available — proceed anyway (first update scenario)
739+ }
740+ }
741+
742+ try {
743+ const result = await updatePro ( projectRoot , {
744+ check : options . check || false ,
745+ dryRun : options . dryRun || false ,
746+ force : options . force || false ,
747+ includeCoreUpdate : options . includeCore || false ,
748+ skipScaffold : options . skipScaffold || false ,
749+ onProgress : ( phase , message ) => {
750+ if ( phase === 'detect' ) console . log ( ` 🔍 ${ message } ` ) ;
751+ else if ( phase === 'check' ) console . log ( ` 📡 ${ message } ` ) ;
752+ else if ( phase === 'core' ) console . log ( ` 📦 ${ message } ` ) ;
753+ else if ( phase === 'update' ) console . log ( ` ⬆️ ${ message } ` ) ;
754+ else if ( phase === 'scaffold' ) console . log ( ` 🔧 ${ message } ` ) ;
755+ } ,
756+ } ) ;
757+
758+ console . log ( formatUpdateResult ( result ) ) ;
759+
760+ if ( ! result . success ) {
761+ process . exit ( 1 ) ;
762+ }
763+ } catch ( error ) {
764+ console . error ( `\n❌ ${ error . message } ` ) ;
765+ process . exit ( 1 ) ;
766+ }
767+ }
768+
647769// ---------------------------------------------------------------------------
648770// Command builder
649771// ---------------------------------------------------------------------------
@@ -691,10 +813,21 @@ function createProCommand() {
691813 // aiox pro setup (AC-12: Install-gate)
692814 proCmd
693815 . command ( 'setup' )
694- . description ( 'Install and verify @aiox-fullstack/pro ' )
816+ . description ( 'Install and verify AIOX Pro ' )
695817 . option ( '--verify' , 'Only verify installation without installing' )
696818 . action ( setupAction ) ;
697819
820+ // aiox pro update (Story 122.3)
821+ proCmd
822+ . command ( 'update' )
823+ . description ( 'Update AIOX Pro to latest version and sync assets' )
824+ . option ( '--check' , 'Check for updates without applying' )
825+ . option ( '--dry-run' , 'Show update plan without executing' )
826+ . option ( '-f, --force' , 'Force reinstall even if up-to-date' )
827+ . option ( '--include-core' , 'Also update aiox-core' )
828+ . option ( '--skip-scaffold' , 'Skip re-scaffolding assets after update' )
829+ . action ( updateAction ) ;
830+
698831 return proCmd ;
699832}
700833
0 commit comments