genbox 1.0.196 → 1.0.197
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/new.js +140 -0
- package/dist/commands/provider-command.js +338 -93
- package/dist/commands/session/attach.js +190 -0
- package/dist/commands/session/index.js +18 -7
- package/dist/commands/session/list.js +14 -188
- package/dist/lib/unified-session/index.js +8 -1
- package/dist/lib/unified-session/list-sessions.js +382 -0
- package/package.json +1 -1
package/dist/commands/new.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -40,6 +73,8 @@ exports.newCommand = new commander_1.Command('new')
|
|
|
40
73
|
.argument('[name]', 'Project name')
|
|
41
74
|
.option('--list', 'List available templates')
|
|
42
75
|
.option('-s, --size <size>', 'Genbox size: small, medium, large, xl')
|
|
76
|
+
.option('-l, --local', 'Create local genbox (Docker)')
|
|
77
|
+
.option('--cloud', 'Create cloud genbox (default)')
|
|
43
78
|
.option('-y, --yes', 'Skip interactive prompts, use defaults')
|
|
44
79
|
.action(async (templateArg, nameArg, options) => {
|
|
45
80
|
try {
|
|
@@ -126,6 +161,24 @@ exports.newCommand = new commander_1.Command('new')
|
|
|
126
161
|
projectName = `${template.name}-${(0, random_name_1.generateRandomName)()}`;
|
|
127
162
|
console.log(chalk_1.default.dim(`Generated name: ${projectName}`));
|
|
128
163
|
}
|
|
164
|
+
// Local vs Cloud selection
|
|
165
|
+
let isLocal = options.local || false;
|
|
166
|
+
if (!options.local && !options.cloud && !options.yes) {
|
|
167
|
+
const location = await (0, select_1.default)({
|
|
168
|
+
message: 'Where to create genbox?',
|
|
169
|
+
choices: [
|
|
170
|
+
{ name: `${chalk_1.default.cyan('☁')} Cloud ${chalk_1.default.dim('- Remote server, access anywhere')}`, value: 'cloud' },
|
|
171
|
+
{ name: `${chalk_1.default.green('🖥')} Local ${chalk_1.default.dim('- Docker container on this machine')}`, value: 'local' },
|
|
172
|
+
],
|
|
173
|
+
default: 'cloud',
|
|
174
|
+
});
|
|
175
|
+
isLocal = location === 'local';
|
|
176
|
+
}
|
|
177
|
+
// Handle local creation
|
|
178
|
+
if (isLocal) {
|
|
179
|
+
await createLocalFromTemplate(template, projectName);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
129
182
|
// Size selection
|
|
130
183
|
let size = options.size || preferences.defaultGenboxSize || template.recommendedSize;
|
|
131
184
|
if (!options.yes && !options.size) {
|
|
@@ -328,6 +381,93 @@ exports.newCommand = new commander_1.Command('new')
|
|
|
328
381
|
console.error(chalk_1.default.red('Error:'), error.message);
|
|
329
382
|
}
|
|
330
383
|
});
|
|
384
|
+
/**
|
|
385
|
+
* Create a local genbox from a starter template using Docker
|
|
386
|
+
*/
|
|
387
|
+
async function createLocalFromTemplate(template, projectName) {
|
|
388
|
+
console.log('');
|
|
389
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
390
|
+
console.log(` ${chalk_1.default.bold('Template:')} ${template.icon} ${template.displayName}`);
|
|
391
|
+
console.log(` ${chalk_1.default.bold('Name:')} ${projectName}`);
|
|
392
|
+
console.log(` ${chalk_1.default.bold('Location:')} ${chalk_1.default.green('Local (Docker)')}`);
|
|
393
|
+
console.log(` ${chalk_1.default.bold('Features:')} ${template.features.join(', ')}`);
|
|
394
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
395
|
+
const confirmed = await (0, confirm_1.default)({
|
|
396
|
+
message: `Create local genbox '${projectName}'?`,
|
|
397
|
+
default: true,
|
|
398
|
+
});
|
|
399
|
+
if (!confirmed) {
|
|
400
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const spinner = (0, ora_1.default)('Creating local genbox...').start();
|
|
404
|
+
try {
|
|
405
|
+
// Build the create command for the template
|
|
406
|
+
let createCmd;
|
|
407
|
+
if (template.createCommand) {
|
|
408
|
+
createCmd = template.createCommand.replace(/\{\{name\}\}/g, projectName);
|
|
409
|
+
}
|
|
410
|
+
else if (template.repoUrl) {
|
|
411
|
+
createCmd = `git clone https://${template.repoUrl} ${projectName}`;
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
spinner.fail('Template does not support local creation');
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
// Run the create command locally
|
|
418
|
+
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
419
|
+
spinner.text = 'Setting up project...';
|
|
420
|
+
execSync(createCmd, {
|
|
421
|
+
stdio: 'pipe',
|
|
422
|
+
cwd: process.cwd(),
|
|
423
|
+
});
|
|
424
|
+
// Run setup commands if any
|
|
425
|
+
if (template.setupCommands && template.setupCommands.length > 0) {
|
|
426
|
+
const projectPath = `${process.cwd()}/${projectName}`;
|
|
427
|
+
for (const cmd of template.setupCommands) {
|
|
428
|
+
const setupCmd = cmd.replace(/\{\{name\}\}/g, projectName);
|
|
429
|
+
spinner.text = `Running: ${setupCmd}`;
|
|
430
|
+
try {
|
|
431
|
+
execSync(setupCmd, {
|
|
432
|
+
stdio: 'pipe',
|
|
433
|
+
cwd: projectPath,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
catch {
|
|
437
|
+
// Some setup commands may fail, continue
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
spinner.succeed(chalk_1.default.green(`Local project '${projectName}' created!`));
|
|
442
|
+
console.log('');
|
|
443
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
444
|
+
console.log(` ${chalk_1.default.bold('Name:')} ${projectName}`);
|
|
445
|
+
console.log(` ${chalk_1.default.bold('Path:')} ${process.cwd()}/${projectName}`);
|
|
446
|
+
console.log(` ${chalk_1.default.bold('Type:')} Local`);
|
|
447
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
448
|
+
console.log('');
|
|
449
|
+
console.log(chalk_1.default.bold('Next steps:'));
|
|
450
|
+
console.log(` 1. ${chalk_1.default.cyan(`cd ${projectName}`)}`);
|
|
451
|
+
console.log(` 2. ${chalk_1.default.cyan('pnpm install')} ${chalk_1.default.dim('(or npm install)')}`);
|
|
452
|
+
console.log(` 3. ${chalk_1.default.cyan('pnpm dev')} ${chalk_1.default.dim('(or npm run dev)')}`);
|
|
453
|
+
console.log('');
|
|
454
|
+
console.log(chalk_1.default.dim('To start a Claude session in this project:'));
|
|
455
|
+
console.log(` ${chalk_1.default.cyan(`cd ${projectName} && claude`)}`);
|
|
456
|
+
console.log('');
|
|
457
|
+
}
|
|
458
|
+
catch (error) {
|
|
459
|
+
spinner.fail(chalk_1.default.red(`Failed to create local project: ${error.message}`));
|
|
460
|
+
if (error.message.includes('command not found')) {
|
|
461
|
+
console.log(chalk_1.default.yellow('\nMake sure the required tools are installed:'));
|
|
462
|
+
if (template.createCommand?.includes('npx')) {
|
|
463
|
+
console.log(chalk_1.default.dim(' - Node.js and npx'));
|
|
464
|
+
}
|
|
465
|
+
if (template.createCommand?.includes('bunx')) {
|
|
466
|
+
console.log(chalk_1.default.dim(' - Bun (https://bun.sh)'));
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
331
471
|
async function listTemplates() {
|
|
332
472
|
const spinner = (0, ora_1.default)('Fetching templates...').start();
|
|
333
473
|
try {
|
|
@@ -179,13 +179,42 @@ async function getGenboxes(includesStopped = true) {
|
|
|
179
179
|
*/
|
|
180
180
|
async function getExistingSessions(provider) {
|
|
181
181
|
const sessions = [];
|
|
182
|
-
|
|
182
|
+
const seenNames = new Set();
|
|
183
|
+
// Check UnifiedSessionManager for registered sessions
|
|
184
|
+
try {
|
|
185
|
+
const sessionManager = (0, unified_session_1.getUnifiedSessionManager)();
|
|
186
|
+
const registeredSessions = sessionManager.listSessions({
|
|
187
|
+
provider: provider,
|
|
188
|
+
status: ['starting', 'running', 'active', 'idle'],
|
|
189
|
+
});
|
|
190
|
+
for (const s of registeredSessions) {
|
|
191
|
+
// Verify the session is still alive (socket exists)
|
|
192
|
+
const socketPath = s.infrastructure?.dtachSocketPath ||
|
|
193
|
+
path.join(getDtachSocketDir(), `${s.name}.sock`);
|
|
194
|
+
if (isDtachSocketAlive(socketPath)) {
|
|
195
|
+
sessions.push({
|
|
196
|
+
name: s.name,
|
|
197
|
+
provider,
|
|
198
|
+
location: 'direct',
|
|
199
|
+
});
|
|
200
|
+
seenNames.add(s.name);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// Registry not available, fall back to socket scanning
|
|
206
|
+
}
|
|
207
|
+
// Also check local (direct) sessions by scanning socket directory
|
|
208
|
+
// (for backwards compatibility with sessions created before the registry)
|
|
183
209
|
const socketDir = getDtachSocketDir();
|
|
184
210
|
if (fs.existsSync(socketDir)) {
|
|
185
211
|
const files = fs.readdirSync(socketDir).filter(f => f.endsWith('.sock'));
|
|
186
212
|
for (const file of files) {
|
|
187
213
|
const socketPath = path.join(socketDir, file);
|
|
188
214
|
const sessionName = file.replace('.sock', '');
|
|
215
|
+
// Skip if already added from registry
|
|
216
|
+
if (seenNames.has(sessionName))
|
|
217
|
+
continue;
|
|
189
218
|
// Check if this session is for our provider
|
|
190
219
|
if (sessionName.toLowerCase().includes(provider)) {
|
|
191
220
|
if (isDtachSocketAlive(socketPath)) {
|
|
@@ -318,7 +347,9 @@ async function startSessionOnGenbox(provider, genbox) {
|
|
|
318
347
|
// Create session in background using nohup with dtach
|
|
319
348
|
const socketDir = getRemoteDtachSocketDir();
|
|
320
349
|
const socketPath = `${socketDir}/${sessionName}.sock`;
|
|
321
|
-
|
|
350
|
+
// Source .bashrc exports (GENBOX_PROJECT_DIR) and nvm, then cd to project directory
|
|
351
|
+
// Note: We use grep to extract just the export line since .bashrc may have early return for non-interactive shells
|
|
352
|
+
(0, child_process_1.execSync)(`ssh -i "${keyPath}" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null dev@${targetGenbox.ipAddress} "mkdir -p ${socketDir} && nohup dtach -n ${socketPath} bash -c 'eval \\$(grep GENBOX_PROJECT_DIR ~/.bashrc 2>/dev/null); cd \\$GENBOX_PROJECT_DIR 2>/dev/null; source ~/.nvm/nvm.sh 2>/dev/null; ${cliCommand}' > /dev/null 2>&1 &"`, { timeout: 30000 });
|
|
322
353
|
// Wait for session to start
|
|
323
354
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
324
355
|
console.log(chalk_1.default.green(`Session created: ${sessionName}`));
|
|
@@ -680,59 +711,321 @@ async function runInteractiveFlow(provider) {
|
|
|
680
711
|
break;
|
|
681
712
|
}
|
|
682
713
|
}
|
|
714
|
+
/**
|
|
715
|
+
* Create list subcommand for a provider
|
|
716
|
+
*/
|
|
717
|
+
function createListSubcommand(provider) {
|
|
718
|
+
return new commander_1.Command('list')
|
|
719
|
+
.description(`List all ${provider} sessions`)
|
|
720
|
+
.action(async () => {
|
|
721
|
+
try {
|
|
722
|
+
console.log(chalk_1.default.dim(`\nFetching ${provider} sessions...`));
|
|
723
|
+
const result = await (0, unified_session_1.listAllSessions)({ provider });
|
|
724
|
+
(0, unified_session_1.displaySimpleList)(result, provider);
|
|
725
|
+
}
|
|
726
|
+
catch (error) {
|
|
727
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
728
|
+
(0, api_1.handleApiError)(error);
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Create attach subcommand for a provider
|
|
737
|
+
*/
|
|
738
|
+
function createAttachSubcommand(provider) {
|
|
739
|
+
return new commander_1.Command('attach')
|
|
740
|
+
.description(`Attach to a ${provider} session`)
|
|
741
|
+
.argument('[session]', 'Session name to attach to')
|
|
742
|
+
.action(async (sessionName) => {
|
|
743
|
+
try {
|
|
744
|
+
const sessions = await getExistingSessions(provider);
|
|
745
|
+
if (sessions.length === 0) {
|
|
746
|
+
console.log(chalk_1.default.dim(`\n No ${provider} sessions found.\n`));
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
let session;
|
|
750
|
+
if (sessionName) {
|
|
751
|
+
const found = sessions.find(s => s.name === sessionName || s.name.includes(sessionName));
|
|
752
|
+
if (!found) {
|
|
753
|
+
console.log(chalk_1.default.red(`\nSession not found: ${sessionName}`));
|
|
754
|
+
console.log(chalk_1.default.dim(`Run 'gb ${provider} list' to see available sessions.\n`));
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
session = found;
|
|
758
|
+
}
|
|
759
|
+
else if (sessions.length === 1) {
|
|
760
|
+
// Auto-select if only one session
|
|
761
|
+
session = sessions[0];
|
|
762
|
+
console.log(chalk_1.default.dim(`\nAttaching to ${session.name}...`));
|
|
763
|
+
}
|
|
764
|
+
else {
|
|
765
|
+
// Show selection menu
|
|
766
|
+
const sessionChoices = sessions.map(s => ({
|
|
767
|
+
name: `${s.name} ${chalk_1.default.dim(`(${s.location === 'genbox' ? `on ${s.genboxName}` : 'direct'})`)}`,
|
|
768
|
+
value: s,
|
|
769
|
+
}));
|
|
770
|
+
try {
|
|
771
|
+
session = await (0, select_1.default)({
|
|
772
|
+
message: 'Select session to attach:',
|
|
773
|
+
choices: sessionChoices,
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
catch {
|
|
777
|
+
console.log(chalk_1.default.dim('\nCancelled.'));
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
await attachToSession(session);
|
|
782
|
+
}
|
|
783
|
+
catch (error) {
|
|
784
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
785
|
+
(0, api_1.handleApiError)(error);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Create stop subcommand for a provider
|
|
794
|
+
*/
|
|
795
|
+
function createStopSubcommand(provider) {
|
|
796
|
+
return new commander_1.Command('stop')
|
|
797
|
+
.description(`Stop a ${provider} session`)
|
|
798
|
+
.argument('[session]', 'Session name to stop')
|
|
799
|
+
.action(async (sessionName) => {
|
|
800
|
+
try {
|
|
801
|
+
const sessions = await getExistingSessions(provider);
|
|
802
|
+
if (sessions.length === 0) {
|
|
803
|
+
console.log(chalk_1.default.dim(`\n No ${provider} sessions found.\n`));
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
let sessionToStop;
|
|
807
|
+
if (sessionName) {
|
|
808
|
+
const found = sessions.find(s => s.name === sessionName || s.name.includes(sessionName));
|
|
809
|
+
if (!found) {
|
|
810
|
+
console.log(chalk_1.default.red(`\nSession not found: ${sessionName}`));
|
|
811
|
+
console.log(chalk_1.default.dim(`Run 'gb ${provider} list' to see available sessions.\n`));
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
sessionToStop = found;
|
|
815
|
+
}
|
|
816
|
+
else if (sessions.length === 1) {
|
|
817
|
+
sessionToStop = sessions[0];
|
|
818
|
+
console.log(chalk_1.default.dim(`\nStopping ${sessionToStop.name}...`));
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
const sessionChoices = sessions.map(s => ({
|
|
822
|
+
name: `${s.name} ${chalk_1.default.dim(`(${s.location === 'genbox' ? `on ${s.genboxName}` : 'direct'})`)}`,
|
|
823
|
+
value: s,
|
|
824
|
+
}));
|
|
825
|
+
try {
|
|
826
|
+
sessionToStop = await (0, select_1.default)({
|
|
827
|
+
message: 'Select session to stop:',
|
|
828
|
+
choices: sessionChoices,
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
catch {
|
|
832
|
+
console.log(chalk_1.default.dim('\nCancelled.'));
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
// Stop is same as kill for dtach sessions
|
|
837
|
+
const success = await killSession(sessionToStop);
|
|
838
|
+
if (success) {
|
|
839
|
+
console.log(chalk_1.default.green(`\n Stopped session: ${sessionToStop.name}\n`));
|
|
840
|
+
}
|
|
841
|
+
else {
|
|
842
|
+
console.log(chalk_1.default.red(`\n Failed to stop session: ${sessionToStop.name}\n`));
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
catch (error) {
|
|
846
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
847
|
+
(0, api_1.handleApiError)(error);
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Create kill subcommand for a provider
|
|
856
|
+
*/
|
|
857
|
+
function createKillSubcommand(provider) {
|
|
858
|
+
return new commander_1.Command('kill')
|
|
859
|
+
.description(`Kill a ${provider} session`)
|
|
860
|
+
.argument('[session]', 'Session name to kill')
|
|
861
|
+
.option('-y, --yes', 'Skip confirmation')
|
|
862
|
+
.action(async (sessionName, options) => {
|
|
863
|
+
try {
|
|
864
|
+
const sessions = await getExistingSessions(provider);
|
|
865
|
+
if (sessions.length === 0) {
|
|
866
|
+
console.log(chalk_1.default.dim(`\n No ${provider} sessions found.\n`));
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
let sessionToKill;
|
|
870
|
+
if (sessionName) {
|
|
871
|
+
const found = sessions.find(s => s.name === sessionName || s.name.includes(sessionName));
|
|
872
|
+
if (!found) {
|
|
873
|
+
console.log(chalk_1.default.red(`\nSession not found: ${sessionName}`));
|
|
874
|
+
console.log(chalk_1.default.dim(`Run 'gb ${provider} list' to see available sessions.\n`));
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
sessionToKill = found;
|
|
878
|
+
}
|
|
879
|
+
else if (sessions.length === 1) {
|
|
880
|
+
sessionToKill = sessions[0];
|
|
881
|
+
}
|
|
882
|
+
else {
|
|
883
|
+
const sessionChoices = sessions.map(s => ({
|
|
884
|
+
name: `${s.name} ${chalk_1.default.dim(`(${s.location === 'genbox' ? `on ${s.genboxName}` : 'direct'})`)}`,
|
|
885
|
+
value: s,
|
|
886
|
+
}));
|
|
887
|
+
try {
|
|
888
|
+
sessionToKill = await (0, select_1.default)({
|
|
889
|
+
message: 'Select session to kill:',
|
|
890
|
+
choices: sessionChoices,
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
catch {
|
|
894
|
+
console.log(chalk_1.default.dim('\nCancelled.'));
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
if (!options.yes) {
|
|
899
|
+
try {
|
|
900
|
+
const confirmed = await (0, prompts_1.confirm)({
|
|
901
|
+
message: `Kill session ${sessionToKill.name}?`,
|
|
902
|
+
default: false,
|
|
903
|
+
});
|
|
904
|
+
if (!confirmed) {
|
|
905
|
+
console.log(chalk_1.default.dim('\nCancelled.'));
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
catch {
|
|
910
|
+
console.log(chalk_1.default.dim('\nCancelled.'));
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
const success = await killSession(sessionToKill);
|
|
915
|
+
if (success) {
|
|
916
|
+
console.log(chalk_1.default.green(`\n Killed session: ${sessionToKill.name}\n`));
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
console.log(chalk_1.default.red(`\n Failed to kill session: ${sessionToKill.name}\n`));
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
catch (error) {
|
|
923
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
924
|
+
(0, api_1.handleApiError)(error);
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
928
|
+
}
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Create migrate subcommand for a provider
|
|
933
|
+
*/
|
|
934
|
+
function createMigrateSubcommand(provider) {
|
|
935
|
+
return new commander_1.Command('migrate')
|
|
936
|
+
.description(`Migrate a ${provider} session to a different environment`)
|
|
937
|
+
.argument('[session]', 'Session name to migrate')
|
|
938
|
+
.option('--to <target>', 'Target environment (cloud, local, native)')
|
|
939
|
+
.action(async (sessionName, options) => {
|
|
940
|
+
try {
|
|
941
|
+
const sessions = await getExistingSessions(provider);
|
|
942
|
+
if (sessions.length === 0) {
|
|
943
|
+
console.log(chalk_1.default.dim(`\n No ${provider} sessions found.\n`));
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
let sessionToMigrate;
|
|
947
|
+
if (sessionName) {
|
|
948
|
+
const found = sessions.find(s => s.name === sessionName || s.name.includes(sessionName));
|
|
949
|
+
if (!found) {
|
|
950
|
+
console.log(chalk_1.default.red(`\nSession not found: ${sessionName}`));
|
|
951
|
+
console.log(chalk_1.default.dim(`Run 'gb ${provider} list' to see available sessions.\n`));
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
sessionToMigrate = found;
|
|
955
|
+
}
|
|
956
|
+
else if (sessions.length === 1) {
|
|
957
|
+
sessionToMigrate = sessions[0];
|
|
958
|
+
}
|
|
959
|
+
else {
|
|
960
|
+
const sessionChoices = sessions.map(s => ({
|
|
961
|
+
name: `${s.name} ${chalk_1.default.dim(`(${s.location === 'genbox' ? `on ${s.genboxName}` : 'direct'})`)}`,
|
|
962
|
+
value: s,
|
|
963
|
+
}));
|
|
964
|
+
try {
|
|
965
|
+
sessionToMigrate = await (0, select_1.default)({
|
|
966
|
+
message: 'Select session to migrate:',
|
|
967
|
+
choices: sessionChoices,
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
catch {
|
|
971
|
+
console.log(chalk_1.default.dim('\nCancelled.'));
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
// Determine target
|
|
976
|
+
let target = options.to;
|
|
977
|
+
if (!target) {
|
|
978
|
+
const currentLocation = sessionToMigrate.location === 'genbox' ? 'cloud' : 'local';
|
|
979
|
+
const targetChoices = [
|
|
980
|
+
{ name: 'Cloud (Genbox)', value: 'cloud' },
|
|
981
|
+
{ name: 'Local (Native)', value: 'native' },
|
|
982
|
+
].filter(c => c.value !== currentLocation);
|
|
983
|
+
try {
|
|
984
|
+
target = await (0, select_1.default)({
|
|
985
|
+
message: `Migrate ${sessionToMigrate.name} to:`,
|
|
986
|
+
choices: targetChoices,
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
catch {
|
|
990
|
+
console.log(chalk_1.default.dim('\nCancelled.'));
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
console.log(chalk_1.default.dim(`\nMigrating ${sessionToMigrate.name} to ${target}...`));
|
|
995
|
+
console.log(chalk_1.default.yellow('\nMigration not yet implemented. Use `gb session migrate` for full migration support.\n'));
|
|
996
|
+
}
|
|
997
|
+
catch (error) {
|
|
998
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
999
|
+
(0, api_1.handleApiError)(error);
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
1003
|
+
}
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
683
1006
|
/**
|
|
684
1007
|
* Create a provider command (claude, gemini, or codex)
|
|
685
1008
|
*/
|
|
686
1009
|
function createProviderCommand(provider) {
|
|
687
1010
|
const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
688
|
-
|
|
1011
|
+
const cmd = new commander_1.Command(provider)
|
|
689
1012
|
.description(`Start or attach to a ${providerName} AI session`)
|
|
690
1013
|
.option('--on <genbox>', 'Use specific genbox')
|
|
691
1014
|
.option('--direct', 'Run directly on this machine (no isolation)')
|
|
692
|
-
.option('--attach <session>', 'Attach to existing session')
|
|
693
|
-
.option('--list', 'List all sessions')
|
|
694
|
-
.option('--kill', 'Kill session')
|
|
695
1015
|
.option('-y, --yes', 'Skip confirmations')
|
|
696
1016
|
.addHelpText('after', `
|
|
697
1017
|
Examples:
|
|
698
1018
|
gb ${provider} Interactive mode
|
|
1019
|
+
gb ${provider} list List all ${provider} sessions
|
|
1020
|
+
gb ${provider} attach [session] Attach to a session
|
|
1021
|
+
gb ${provider} stop [session] Stop a session
|
|
1022
|
+
gb ${provider} kill [session] Kill a session
|
|
1023
|
+
gb ${provider} migrate [session] Migrate a session
|
|
699
1024
|
gb ${provider} --on my-genbox Use specific genbox
|
|
700
|
-
gb ${provider} --attach swift-fox Attach to existing session
|
|
701
1025
|
gb ${provider} --direct Run without isolation
|
|
702
|
-
gb ${provider} --list List all ${provider} sessions
|
|
703
1026
|
`)
|
|
704
1027
|
.action(async (options) => {
|
|
705
1028
|
try {
|
|
706
|
-
// --list: Show all sessions for this provider
|
|
707
|
-
if (options.list) {
|
|
708
|
-
console.log(chalk_1.default.dim(`\nFetching ${provider} sessions...`));
|
|
709
|
-
const sessions = await getExistingSessions(provider);
|
|
710
|
-
if (sessions.length === 0) {
|
|
711
|
-
console.log(chalk_1.default.dim(`\n No ${provider} sessions found.\n`));
|
|
712
|
-
return;
|
|
713
|
-
}
|
|
714
|
-
console.log('');
|
|
715
|
-
for (const session of sessions) {
|
|
716
|
-
const locationLabel = session.location === 'genbox'
|
|
717
|
-
? chalk_1.default.blue(`on ${session.genboxName}`)
|
|
718
|
-
: chalk_1.default.yellow('direct');
|
|
719
|
-
console.log(` ${chalk_1.default.green('●')} ${session.name} ${chalk_1.default.dim(`(${locationLabel})`)}`);
|
|
720
|
-
}
|
|
721
|
-
console.log('');
|
|
722
|
-
return;
|
|
723
|
-
}
|
|
724
|
-
// --attach: Attach to specific session
|
|
725
|
-
if (options.attach) {
|
|
726
|
-
const sessions = await getExistingSessions(provider);
|
|
727
|
-
const session = sessions.find(s => s.name === options.attach || s.name.includes(options.attach));
|
|
728
|
-
if (!session) {
|
|
729
|
-
console.log(chalk_1.default.red(`\nSession not found: ${options.attach}`));
|
|
730
|
-
console.log(chalk_1.default.dim(`Run 'gb ${provider} --list' to see available sessions.\n`));
|
|
731
|
-
return;
|
|
732
|
-
}
|
|
733
|
-
await attachToSession(session);
|
|
734
|
-
return;
|
|
735
|
-
}
|
|
736
1029
|
// --on: Use specific genbox
|
|
737
1030
|
if (options.on) {
|
|
738
1031
|
const genboxes = await getGenboxes();
|
|
@@ -757,61 +1050,6 @@ Examples:
|
|
|
757
1050
|
await startDirectSession(provider);
|
|
758
1051
|
return;
|
|
759
1052
|
}
|
|
760
|
-
// --kill: Kill a session
|
|
761
|
-
if (options.kill) {
|
|
762
|
-
const sessions = await getExistingSessions(provider);
|
|
763
|
-
if (sessions.length === 0) {
|
|
764
|
-
console.log(chalk_1.default.dim(`\n No ${provider} sessions found.\n`));
|
|
765
|
-
return;
|
|
766
|
-
}
|
|
767
|
-
// If only one session, kill it directly (with confirmation)
|
|
768
|
-
let sessionToKill;
|
|
769
|
-
if (sessions.length === 1) {
|
|
770
|
-
sessionToKill = sessions[0];
|
|
771
|
-
}
|
|
772
|
-
else {
|
|
773
|
-
// Let user select which session to kill
|
|
774
|
-
const sessionChoices = sessions.map(s => ({
|
|
775
|
-
name: `${s.name} ${chalk_1.default.dim(`(${s.location === 'genbox' ? `on ${s.genboxName}` : 'direct'})`)}`,
|
|
776
|
-
value: s,
|
|
777
|
-
}));
|
|
778
|
-
try {
|
|
779
|
-
sessionToKill = await (0, select_1.default)({
|
|
780
|
-
message: 'Select session to kill:',
|
|
781
|
-
choices: sessionChoices,
|
|
782
|
-
});
|
|
783
|
-
}
|
|
784
|
-
catch {
|
|
785
|
-
console.log(chalk_1.default.dim('\nCancelled.'));
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
// Confirm unless --yes
|
|
790
|
-
if (!options.yes) {
|
|
791
|
-
try {
|
|
792
|
-
const confirmed = await (0, prompts_1.confirm)({
|
|
793
|
-
message: `Kill session ${sessionToKill.name}?`,
|
|
794
|
-
default: false,
|
|
795
|
-
});
|
|
796
|
-
if (!confirmed) {
|
|
797
|
-
console.log(chalk_1.default.dim('\nCancelled.'));
|
|
798
|
-
return;
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
catch {
|
|
802
|
-
console.log(chalk_1.default.dim('\nCancelled.'));
|
|
803
|
-
return;
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
const success = await killSession(sessionToKill);
|
|
807
|
-
if (success) {
|
|
808
|
-
console.log(chalk_1.default.green(`\n Killed session: ${sessionToKill.name}\n`));
|
|
809
|
-
}
|
|
810
|
-
else {
|
|
811
|
-
console.log(chalk_1.default.red(`\n Failed to kill session: ${sessionToKill.name}\n`));
|
|
812
|
-
}
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
1053
|
// Default: Interactive mode
|
|
816
1054
|
await runInteractiveFlow(provider);
|
|
817
1055
|
}
|
|
@@ -827,6 +1065,13 @@ Examples:
|
|
|
827
1065
|
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
828
1066
|
}
|
|
829
1067
|
});
|
|
1068
|
+
// Add subcommands
|
|
1069
|
+
cmd.addCommand(createListSubcommand(provider));
|
|
1070
|
+
cmd.addCommand(createAttachSubcommand(provider));
|
|
1071
|
+
cmd.addCommand(createStopSubcommand(provider));
|
|
1072
|
+
cmd.addCommand(createKillSubcommand(provider));
|
|
1073
|
+
cmd.addCommand(createMigrateSubcommand(provider));
|
|
1074
|
+
return cmd;
|
|
830
1075
|
}
|
|
831
1076
|
// Export individual commands
|
|
832
1077
|
exports.claudeCommand = createProviderCommand('claude');
|