1+ /**
2+ * Pro Seats Self-Service CLI (EPIC-PRO-17 / STORY-PRO-17.5)
3+ *
4+ * aiox pro seats list [--email E] [--password P] [--token T]
5+ * aiox pro seats release <activationId> [--email E] [--password P] [--token T]
6+ *
7+ * @module cli/commands/pro/seats
8+ */
9+
10+ 'use strict' ;
11+
12+ const { Command } = require ( 'commander' ) ;
13+ const fs = require ( 'fs' ) ;
14+ const path = require ( 'path' ) ;
15+
16+ const PRO_PACKAGE = '@aiox-squads/pro' ;
17+
18+ function resolveLicensePath ( ) {
19+ const relativePath = path . resolve ( __dirname , '..' , '..' , '..' , '..' , 'pro' , 'license' ) ;
20+ if ( fs . existsSync ( relativePath ) ) {
21+ return relativePath ;
22+ }
23+
24+ try {
25+ const proPkg = require . resolve ( `${ PRO_PACKAGE } /package.json` ) ;
26+ const npmPath = path . join ( path . dirname ( proPkg ) , 'license' ) ;
27+ if ( fs . existsSync ( npmPath ) ) {
28+ return npmPath ;
29+ }
30+ } catch {
31+ // package not installed
32+ }
33+
34+ const cwdPath = path . join ( process . cwd ( ) , 'node_modules' , '@aiox-squads' , 'pro' , 'license' ) ;
35+ if ( fs . existsSync ( cwdPath ) ) {
36+ return cwdPath ;
37+ }
38+
39+ return relativePath ;
40+ }
41+
42+ const licensePath = resolveLicensePath ( ) ;
43+
44+ function loadModules ( ) {
45+ try {
46+ const { licenseApi } = require ( path . join ( licensePath , 'license-api' ) ) ;
47+ const { generateMachineId } = require ( path . join ( licensePath , 'machine-id' ) ) ;
48+ const { AuthError } = require ( path . join ( licensePath , 'errors' ) ) ;
49+ return { licenseApi, generateMachineId, AuthError } ;
50+ } catch ( error ) {
51+ console . error ( 'AIOX Pro license module not available.' ) ;
52+ console . error ( 'Install AIOX Pro: aiox pro setup' ) ;
53+ console . error ( error . message ) ;
54+ process . exit ( 1 ) ;
55+ }
56+ }
57+
58+ async function resolveAccessToken ( options , AuthError ) {
59+ const { licenseApi } = loadModules ( ) ;
60+
61+ if ( options . token ) {
62+ return options . token ;
63+ }
64+
65+ const email = options . email || process . env . AIOX_PRO_EMAIL ;
66+ const password = options . password || process . env . AIOX_PRO_PASSWORD ;
67+
68+ if ( ! email || ! password ) {
69+ console . error ( 'Authentication required.' ) ;
70+ console . error ( 'Use --email and --password, --token, or AIOX_PRO_EMAIL/AIOX_PRO_PASSWORD.' ) ;
71+ process . exit ( 1 ) ;
72+ }
73+
74+ try {
75+ const login = await licenseApi . login ( email , password ) ;
76+ return login . sessionToken ;
77+ } catch ( error ) {
78+ const message = error instanceof AuthError ? error . message : error . message ;
79+ console . error ( `Login failed: ${ message } ` ) ;
80+ process . exit ( 1 ) ;
81+ }
82+ }
83+
84+ function formatDate ( dateStr ) {
85+ if ( ! dateStr ) return '—' ;
86+ return new Date ( dateStr ) . toLocaleString ( 'pt-BR' ) ;
87+ }
88+
89+ async function listSeatsAction ( options ) {
90+ const { licenseApi, generateMachineId, AuthError } = loadModules ( ) ;
91+ const accessToken = await resolveAccessToken ( options , AuthError ) ;
92+ const machineId = generateMachineId ( ) ;
93+
94+ try {
95+ const result = await licenseApi . listSeats ( accessToken , machineId ) ;
96+
97+ console . log ( '\nAIOX Pro — Seats\n' ) ;
98+ console . log ( ` Used: ${ result . summary . used } /${ result . summary . max } ` ) ;
99+ console . log ( ` Available: ${ result . summary . available } ` ) ;
100+ console . log ( '' ) ;
101+
102+ if ( ! result . seats || result . seats . length === 0 ) {
103+ console . log ( ' No active seats.' ) ;
104+ console . log ( '' ) ;
105+ return ;
106+ }
107+
108+ for ( const seat of result . seats ) {
109+ const current = seat . isCurrentMachine ? ' (esta máquina)' : '' ;
110+ console . log ( ` • ${ seat . machineIdMasked } ${ current } ` ) ;
111+ console . log ( ` ID: ${ seat . id } ` ) ;
112+ if ( seat . machineName ) {
113+ console . log ( ` Nome: ${ seat . machineName } ` ) ;
114+ }
115+ if ( seat . aiosVersion ) {
116+ console . log ( ` Versão: ${ seat . aiosVersion } ` ) ;
117+ }
118+ console . log ( ` Ativado: ${ formatDate ( seat . activatedAt ) } ` ) ;
119+ console . log ( ` Validado: ${ formatDate ( seat . lastValidatedAt ) } ` ) ;
120+ console . log ( '' ) ;
121+ }
122+ } catch ( error ) {
123+ console . error ( `\nFailed to list seats: ${ error . message } ` ) ;
124+ if ( error . code ) {
125+ console . error ( `Error code: ${ error . code } ` ) ;
126+ }
127+ process . exit ( 1 ) ;
128+ }
129+ }
130+
131+ async function releaseSeatAction ( activationId , options ) {
132+ const { licenseApi, generateMachineId, AuthError } = loadModules ( ) ;
133+
134+ if ( ! activationId ) {
135+ console . error ( 'Usage: aiox pro seats release <activationId>' ) ;
136+ process . exit ( 1 ) ;
137+ }
138+
139+ const accessToken = await resolveAccessToken ( options , AuthError ) ;
140+ const machineId = generateMachineId ( ) ;
141+
142+ try {
143+ const result = await licenseApi . releaseSeat ( accessToken , activationId , machineId ) ;
144+
145+ console . log ( '\nSeat released successfully.\n' ) ;
146+ console . log ( ` Released: ${ result . releasedActivationId || activationId } ` ) ;
147+ console . log ( ` Available: ${ result . summary . available } /${ result . summary . max } ` ) ;
148+ console . log ( '' ) ;
149+ } catch ( error ) {
150+ console . error ( `\nFailed to release seat: ${ error . message } ` ) ;
151+ if ( error . code ) {
152+ console . error ( `Error code: ${ error . code } ` ) ;
153+ }
154+ process . exit ( 1 ) ;
155+ }
156+ }
157+
158+ function createSeatsCommand ( ) {
159+ const seatsCmd = new Command ( 'seats' ) . description ( 'List and release Pro seats (self-service)' ) ;
160+
161+ const authOptions = ( cmd ) => {
162+ cmd
163+ . option ( '--email <email>' , 'Account email' )
164+ . option ( '--password <password>' , 'Account password' )
165+ . option ( '--token <token>' , 'Supabase access token' ) ;
166+ return cmd ;
167+ } ;
168+
169+ authOptions ( seatsCmd . command ( 'list' ) . description ( 'List active seats' ) )
170+ . action ( listSeatsAction ) ;
171+
172+ authOptions (
173+ seatsCmd
174+ . command ( 'release' )
175+ . description ( 'Release a remote seat by activation id' )
176+ . argument ( '<activationId>' , 'Seat activation UUID' ) ,
177+ ) . action ( releaseSeatAction ) ;
178+
179+ return seatsCmd ;
180+ }
181+
182+ module . exports = {
183+ createSeatsCommand,
184+ } ;
0 commit comments