genbox 1.0.0

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,488 @@
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
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.initCommand = void 0;
40
+ const commander_1 = require("commander");
41
+ const inquirer_1 = __importDefault(require("inquirer"));
42
+ const chalk_1 = __importDefault(require("chalk"));
43
+ const path_1 = __importDefault(require("path"));
44
+ const fs_1 = __importDefault(require("fs"));
45
+ const config_1 = require("../config");
46
+ const scan_1 = require("../scan");
47
+ const process = __importStar(require("process"));
48
+ const os = __importStar(require("os"));
49
+ exports.initCommand = new commander_1.Command('init')
50
+ .description('Initialize a new Genbox configuration')
51
+ .action(async () => {
52
+ try {
53
+ if ((0, config_1.hasConfig)()) {
54
+ console.log(chalk_1.default.yellow('genbox.yaml already exists.'));
55
+ const { overwrite } = await inquirer_1.default.prompt([
56
+ {
57
+ type: 'confirm',
58
+ name: 'overwrite',
59
+ message: 'Do you want to overwrite it?',
60
+ default: false,
61
+ },
62
+ ]);
63
+ if (!overwrite) {
64
+ return;
65
+ }
66
+ }
67
+ console.log(chalk_1.default.blue('Initializing Genbox...'));
68
+ console.log(chalk_1.default.dim('Scanning project...'));
69
+ const info = (0, scan_1.scanProject)(process.cwd());
70
+ console.log(chalk_1.default.green(`Detected: ${info.name}`));
71
+ if (info.hasDocker)
72
+ console.log(chalk_1.default.dim(' - Docker detected'));
73
+ // 1. Project Name
74
+ const { project_name } = await inquirer_1.default.prompt([
75
+ {
76
+ type: 'input',
77
+ name: 'project_name',
78
+ message: 'Project Name:',
79
+ default: info.name,
80
+ }
81
+ ]);
82
+ // 2. Scripts Selection
83
+ let selectedScripts = [];
84
+ if (info.foundScripts.length > 0) {
85
+ const scriptAnswers = await inquirer_1.default.prompt([
86
+ {
87
+ type: 'checkbox',
88
+ name: 'scripts',
89
+ message: 'Select setup scripts to run:',
90
+ choices: info.foundScripts,
91
+ default: info.foundScripts,
92
+ },
93
+ ]);
94
+ selectedScripts = scriptAnswers.scripts;
95
+ }
96
+ // 3. Server Size
97
+ const { server_size } = await inquirer_1.default.prompt([
98
+ {
99
+ type: 'select',
100
+ name: 'server_size',
101
+ message: 'Default Server Size:',
102
+ choices: ['small', 'medium', 'large', 'xl'],
103
+ default: 'small',
104
+ }
105
+ ]);
106
+ // 4. Git Repository Setup
107
+ let gitAuth;
108
+ let repoUrl = info.gitRemote;
109
+ let repoAuthMethod;
110
+ if (info.gitRemote) {
111
+ console.log('');
112
+ console.log(chalk_1.default.blue('=== Git Repository Setup ==='));
113
+ console.log(chalk_1.default.dim(`Detected remote: ${info.gitRemote}`));
114
+ // Ask about authentication method - default to PAT (token)
115
+ const { authMethod } = await inquirer_1.default.prompt([
116
+ {
117
+ type: 'select',
118
+ name: 'authMethod',
119
+ message: 'How should the genbox access this repository?',
120
+ choices: [
121
+ { name: 'Personal Access Token (PAT) - recommended', value: 'token' },
122
+ { name: 'SSH Key - use existing key', value: 'ssh' },
123
+ { name: 'Public - no authentication needed', value: 'public' },
124
+ ],
125
+ default: 'token',
126
+ }
127
+ ]);
128
+ if (authMethod === 'token') {
129
+ repoAuthMethod = 'token';
130
+ // Convert SSH URL to HTTPS if needed
131
+ if (info.gitRemoteType === 'ssh') {
132
+ const httpsUrl = (0, scan_1.sshToHttps)(info.gitRemote);
133
+ console.log(chalk_1.default.dim(` Will use HTTPS URL: ${httpsUrl}`));
134
+ repoUrl = httpsUrl;
135
+ }
136
+ gitAuth = { method: 'token' };
137
+ // Detect git provider for specific instructions
138
+ const isGitHub = info.gitRemote.includes('github.com');
139
+ const isGitLab = info.gitRemote.includes('gitlab.com');
140
+ const isBitbucket = info.gitRemote.includes('bitbucket.org');
141
+ console.log('');
142
+ console.log(chalk_1.default.yellow(' Personal Access Token Setup:'));
143
+ console.log('');
144
+ if (isGitHub) {
145
+ console.log(chalk_1.default.bold(' GitHub Fine-grained Token Setup:'));
146
+ console.log(chalk_1.default.dim(' 1. Go to: https://github.com/settings/tokens?type=beta'));
147
+ console.log(chalk_1.default.dim(' 2. Click "Generate new token"'));
148
+ console.log(chalk_1.default.dim(' 3. Token name: "genbox-' + project_name + '"'));
149
+ console.log(chalk_1.default.dim(' 4. Set expiration (90 days or custom)'));
150
+ console.log(chalk_1.default.dim(' 5. Repository access: Select "Only select repositories"'));
151
+ console.log(chalk_1.default.dim(' Then choose your repository from the dropdown'));
152
+ console.log(chalk_1.default.dim(' 6. Scroll down to "Repository permissions" (not Account permissions!)'));
153
+ console.log(chalk_1.default.dim(' - Contents: Read and write'));
154
+ console.log(chalk_1.default.dim(' - Pull requests: Read and write (optional, for PR creation)'));
155
+ console.log(chalk_1.default.dim(' 7. Click "Generate token" at the bottom and copy it'));
156
+ }
157
+ else if (isGitLab) {
158
+ console.log(chalk_1.default.bold(' GitLab Token Setup:'));
159
+ console.log(chalk_1.default.dim(' 1. Go to: https://gitlab.com/-/profile/personal_access_tokens'));
160
+ console.log(chalk_1.default.dim(' 2. Token name: "genbox-' + project_name + '"'));
161
+ console.log(chalk_1.default.dim(' 3. Select scopes: "read_repository", "write_repository"'));
162
+ console.log(chalk_1.default.dim(' 4. Click "Create personal access token"'));
163
+ }
164
+ else if (isBitbucket) {
165
+ console.log(chalk_1.default.bold(' Bitbucket App Password Setup:'));
166
+ console.log(chalk_1.default.dim(' 1. Go to: https://bitbucket.org/account/settings/app-passwords/'));
167
+ console.log(chalk_1.default.dim(' 2. Click "Create app password"'));
168
+ console.log(chalk_1.default.dim(' 3. Label: "genbox-' + project_name + '"'));
169
+ console.log(chalk_1.default.dim(' 4. Select: Repositories (Read, Write)'));
170
+ }
171
+ else {
172
+ console.log(chalk_1.default.dim(' Create a personal access token with repo read/write permissions'));
173
+ }
174
+ console.log('');
175
+ console.log(chalk_1.default.cyan(' Add the token to .env.genbox:'));
176
+ console.log(chalk_1.default.white(' GIT_TOKEN=ghp_xxxxxxxxxxxx'));
177
+ console.log('');
178
+ }
179
+ else if (authMethod === 'ssh') {
180
+ repoAuthMethod = 'ssh';
181
+ // Convert HTTPS URL to SSH if needed
182
+ if (info.gitRemoteType === 'https') {
183
+ const sshUrl = (0, scan_1.httpsToSsh)(info.gitRemote);
184
+ console.log(chalk_1.default.dim(` Will use SSH URL: ${sshUrl}`));
185
+ repoUrl = sshUrl;
186
+ }
187
+ // Ask consent before scanning SSH keys
188
+ const { scanSshKeys } = await inquirer_1.default.prompt([
189
+ {
190
+ type: 'confirm',
191
+ name: 'scanSshKeys',
192
+ message: 'May we scan ~/.ssh/ for available SSH keys?',
193
+ default: true,
194
+ }
195
+ ]);
196
+ if (scanSshKeys) {
197
+ const sshKeys = (0, scan_1.detectSshKeys)();
198
+ if (sshKeys.length > 0) {
199
+ console.log(chalk_1.default.green(` Found ${sshKeys.length} SSH key(s)`));
200
+ const keyChoices = sshKeys.map((key) => ({
201
+ name: `${key.name} (${key.type})`,
202
+ value: key.path,
203
+ }));
204
+ keyChoices.push({ name: 'Enter custom path...', value: 'custom' });
205
+ const { selectedKey } = await inquirer_1.default.prompt([
206
+ {
207
+ type: 'select',
208
+ name: 'selectedKey',
209
+ message: 'Select SSH key to use:',
210
+ choices: keyChoices,
211
+ }
212
+ ]);
213
+ if (selectedKey === 'custom') {
214
+ const { customKeyPath } = await inquirer_1.default.prompt([
215
+ {
216
+ type: 'input',
217
+ name: 'customKeyPath',
218
+ message: 'Enter path to SSH private key:',
219
+ default: path_1.default.join(os.homedir(), '.ssh', 'id_rsa'),
220
+ }
221
+ ]);
222
+ gitAuth = { method: 'ssh', ssh_key_path: customKeyPath };
223
+ }
224
+ else {
225
+ gitAuth = { method: 'ssh', ssh_key_path: selectedKey };
226
+ }
227
+ }
228
+ else {
229
+ console.log(chalk_1.default.yellow(' No SSH keys found'));
230
+ const { keyPath } = await inquirer_1.default.prompt([
231
+ {
232
+ type: 'input',
233
+ name: 'keyPath',
234
+ message: 'Enter path to SSH private key:',
235
+ default: path_1.default.join(os.homedir(), '.ssh', 'id_ed25519'),
236
+ }
237
+ ]);
238
+ gitAuth = { method: 'ssh', ssh_key_path: keyPath };
239
+ }
240
+ }
241
+ else {
242
+ // User declined scanning, ask for path directly
243
+ const { keyPath } = await inquirer_1.default.prompt([
244
+ {
245
+ type: 'input',
246
+ name: 'keyPath',
247
+ message: 'Enter path to SSH private key:',
248
+ default: path_1.default.join(os.homedir(), '.ssh', 'id_ed25519'),
249
+ }
250
+ ]);
251
+ gitAuth = { method: 'ssh', ssh_key_path: keyPath };
252
+ }
253
+ console.log('');
254
+ console.log(chalk_1.default.cyan(' Add your SSH private key to .env.genbox:'));
255
+ console.log(chalk_1.default.white(` GIT_SSH_KEY="$(cat ${gitAuth?.ssh_key_path})"`));
256
+ console.log('');
257
+ console.log(chalk_1.default.dim(' Or manually paste the key content'));
258
+ }
259
+ // If 'public', no auth needed
260
+ }
261
+ else {
262
+ // No git remote detected
263
+ console.log('');
264
+ console.log(chalk_1.default.dim(' No git remote detected.'));
265
+ const { addRepo } = await inquirer_1.default.prompt([
266
+ {
267
+ type: 'confirm',
268
+ name: 'addRepo',
269
+ message: 'Would you like to add a git repository?',
270
+ default: false,
271
+ }
272
+ ]);
273
+ if (addRepo) {
274
+ const { repoUrlInput } = await inquirer_1.default.prompt([
275
+ {
276
+ type: 'input',
277
+ name: 'repoUrlInput',
278
+ message: 'Enter repository URL (HTTPS recommended):',
279
+ validate: (input) => {
280
+ if (!input)
281
+ return 'Repository URL is required';
282
+ if (!input.startsWith('git@') && !input.startsWith('https://') && !input.startsWith('http://')) {
283
+ return 'Please enter a valid git URL (https://... or git@...)';
284
+ }
285
+ return true;
286
+ }
287
+ }
288
+ ]);
289
+ repoUrl = repoUrlInput;
290
+ const urlType = repoUrlInput.startsWith('git@') || repoUrlInput.startsWith('ssh://') ? 'ssh' : 'https';
291
+ if (urlType === 'https') {
292
+ gitAuth = { method: 'token' };
293
+ repoAuthMethod = 'token';
294
+ console.log(chalk_1.default.cyan(' Add GIT_TOKEN to .env.genbox for authentication'));
295
+ }
296
+ else {
297
+ // SSH URL entered, ask about key with consent
298
+ const { scanSshKeys } = await inquirer_1.default.prompt([
299
+ {
300
+ type: 'confirm',
301
+ name: 'scanSshKeys',
302
+ message: 'May we scan ~/.ssh/ for available SSH keys?',
303
+ default: true,
304
+ }
305
+ ]);
306
+ if (scanSshKeys) {
307
+ const sshKeys = (0, scan_1.detectSshKeys)();
308
+ if (sshKeys.length > 0) {
309
+ const { selectedKey } = await inquirer_1.default.prompt([
310
+ {
311
+ type: 'select',
312
+ name: 'selectedKey',
313
+ message: 'Select SSH key:',
314
+ choices: sshKeys.map((k) => ({ name: k.name, value: k.path })),
315
+ }
316
+ ]);
317
+ gitAuth = { method: 'ssh', ssh_key_path: selectedKey };
318
+ }
319
+ else {
320
+ gitAuth = { method: 'ssh', ssh_key_path: path_1.default.join(os.homedir(), '.ssh', 'id_ed25519') };
321
+ }
322
+ }
323
+ else {
324
+ const { keyPath } = await inquirer_1.default.prompt([
325
+ {
326
+ type: 'input',
327
+ name: 'keyPath',
328
+ message: 'Enter path to SSH private key:',
329
+ default: path_1.default.join(os.homedir(), '.ssh', 'id_ed25519'),
330
+ }
331
+ ]);
332
+ gitAuth = { method: 'ssh', ssh_key_path: keyPath };
333
+ }
334
+ repoAuthMethod = 'ssh';
335
+ }
336
+ }
337
+ }
338
+ // Smart Suggestion: Create setup script if none selected but Docker exists
339
+ let extraScripts = [];
340
+ if (selectedScripts.length === 0 && info.hasDocker) {
341
+ const { createSetup } = await inquirer_1.default.prompt([{
342
+ type: 'confirm',
343
+ name: 'createSetup',
344
+ message: 'No setup scripts selected. Create default Docker setup script (scripts/setup-genbox.sh)?',
345
+ default: true
346
+ }]);
347
+ if (createSetup) {
348
+ const scriptsDir = path_1.default.join(process.cwd(), 'scripts');
349
+ if (!fs_1.default.existsSync(scriptsDir))
350
+ fs_1.default.mkdirSync(scriptsDir);
351
+ // Setup directory logic
352
+ const repoName = repoUrl ? path_1.default.basename(repoUrl, '.git') : project_name;
353
+ const projectDir = repoUrl ? `/home/dev/${repoName}` : '/home/dev'; // If no git, assume files uploaded to root or handled differently
354
+ const setupFile = path_1.default.join(scriptsDir, 'setup-genbox.sh');
355
+ const setupContent = `#!/bin/bash
356
+ # Auto-generated by Genbox
357
+ echo "Starting services..."
358
+ cd ${projectDir} || echo "Directory ${projectDir} not found, staying in $(pwd)"
359
+
360
+ if [ -f "docker-compose.secure.yml" ]; then
361
+ docker compose -f docker-compose.secure.yml up -d
362
+ elif [ -f "docker-compose.yml" ]; then
363
+ docker compose up -d
364
+ else
365
+ echo "No docker-compose file found in $(pwd)!"
366
+ fi
367
+ `;
368
+ fs_1.default.writeFileSync(setupFile, setupContent, { mode: 0o755 });
369
+ console.log(chalk_1.default.green(`Created scripts/setup-genbox.sh`));
370
+ extraScripts.push('scripts/setup-genbox.sh');
371
+ }
372
+ }
373
+ // Build repos config
374
+ const reposConfig = {};
375
+ if (repoUrl) {
376
+ const repoName = path_1.default.basename(repoUrl, '.git');
377
+ reposConfig[repoName] = {
378
+ url: repoUrl,
379
+ path: `/home/dev/${repoName}`,
380
+ ...(repoAuthMethod && { auth: repoAuthMethod }),
381
+ };
382
+ }
383
+ const config = {
384
+ version: '1.0',
385
+ project_name: project_name,
386
+ system: {
387
+ languages: info.languages,
388
+ server_size: server_size,
389
+ },
390
+ scripts: [...selectedScripts, ...extraScripts],
391
+ repos: reposConfig,
392
+ files: [],
393
+ hooks: {
394
+ post_checkout: info.languages.node ? ['npm install'] : [],
395
+ },
396
+ ...(gitAuth && { git_auth: gitAuth }),
397
+ };
398
+ (0, config_1.saveConfig)(config);
399
+ console.log(chalk_1.default.green(`\n✔ Configuration saved to genbox.yaml`));
400
+ // 5. Generate .env.genbox file
401
+ if (!(0, config_1.hasEnvFile)()) {
402
+ // Check for existing .env or .env.local (without reading content yet)
403
+ const existingEnvFile = (0, config_1.findExistingEnvFile)();
404
+ if (existingEnvFile) {
405
+ // Ask consent before reading the file
406
+ const { copyEnv } = await inquirer_1.default.prompt([{
407
+ type: 'confirm',
408
+ name: 'copyEnv',
409
+ message: `Found ${existingEnvFile}. May we read and copy it to .env.genbox?`,
410
+ default: true
411
+ }]);
412
+ if (copyEnv) {
413
+ // User consented, now read the content
414
+ const content = (0, config_1.readEnvFileContent)(existingEnvFile);
415
+ (0, config_1.copyExistingEnvToGenbox)(content, existingEnvFile);
416
+ console.log(chalk_1.default.green(`✔ Copied ${existingEnvFile} to .env.genbox`));
417
+ console.log(chalk_1.default.dim(` Review and update ${(0, config_1.getEnvPath)()} as needed`));
418
+ }
419
+ else {
420
+ // User declined to copy, offer to create template instead
421
+ const { createTemplate } = await inquirer_1.default.prompt([{
422
+ type: 'confirm',
423
+ name: 'createTemplate',
424
+ message: 'Create .env.genbox with template variables instead?',
425
+ default: true
426
+ }]);
427
+ if (createTemplate) {
428
+ const envTemplate = (0, config_1.generateEnvTemplate)(project_name);
429
+ (0, config_1.saveEnvVars)(envTemplate);
430
+ console.log(chalk_1.default.green(`✔ Created .env.genbox with template variables`));
431
+ console.log(chalk_1.default.yellow(` Edit ${(0, config_1.getEnvPath)()} with your actual values`));
432
+ }
433
+ }
434
+ }
435
+ else {
436
+ // No existing .env file, create template
437
+ const { createEnv } = await inquirer_1.default.prompt([{
438
+ type: 'confirm',
439
+ name: 'createEnv',
440
+ message: 'Create .env.genbox file for sensitive environment variables?',
441
+ default: true
442
+ }]);
443
+ if (createEnv) {
444
+ const envTemplate = (0, config_1.generateEnvTemplate)(project_name);
445
+ (0, config_1.saveEnvVars)(envTemplate);
446
+ console.log(chalk_1.default.green(`✔ Created .env.genbox with template variables`));
447
+ console.log(chalk_1.default.yellow(` Edit ${(0, config_1.getEnvPath)()} with your actual values`));
448
+ }
449
+ }
450
+ // Add .env.genbox to .gitignore if it exists
451
+ if ((0, config_1.hasEnvFile)()) {
452
+ console.log(chalk_1.default.dim(` Remember: Don't commit .env.genbox to version control!`));
453
+ const gitignorePath = path_1.default.join(process.cwd(), '.gitignore');
454
+ if (fs_1.default.existsSync(gitignorePath)) {
455
+ const gitignoreContent = fs_1.default.readFileSync(gitignorePath, 'utf8');
456
+ if (!gitignoreContent.includes('.env.genbox')) {
457
+ fs_1.default.appendFileSync(gitignorePath, '\n# Genbox environment variables (sensitive)\n.env.genbox\n');
458
+ console.log(chalk_1.default.dim(` Added .env.genbox to .gitignore`));
459
+ }
460
+ }
461
+ }
462
+ }
463
+ else {
464
+ console.log(chalk_1.default.dim(` .env.genbox already exists, skipping...`));
465
+ }
466
+ // Add GIT_TOKEN entry if PAT auth was selected
467
+ if (repoAuthMethod === 'token' && (0, config_1.hasEnvFile)()) {
468
+ (0, config_1.ensureGitTokenInEnv)();
469
+ console.log(chalk_1.default.dim(` Added GIT_TOKEN entry to .env.genbox`));
470
+ }
471
+ console.log('');
472
+ console.log(chalk_1.default.bold('Next steps:'));
473
+ console.log(chalk_1.default.dim(` 1. Edit genbox.yaml to configure services, repos, etc.`));
474
+ console.log(chalk_1.default.dim(` 2. Edit .env.genbox with your environment variables`));
475
+ console.log(chalk_1.default.dim(` 3. Run 'genbox push' to upload configuration to the cloud`));
476
+ console.log(chalk_1.default.dim(` 4. Run 'genbox create <name>' to provision a dev environment`));
477
+ }
478
+ catch (error) {
479
+ // Handle user cancellation (Ctrl+C) gracefully
480
+ if (error.name === 'ExitPromptError' || error.message?.includes('force closed')) {
481
+ console.log('');
482
+ console.log(chalk_1.default.dim('Cancelled.'));
483
+ process.exit(0);
484
+ }
485
+ // Re-throw other errors
486
+ throw error;
487
+ }
488
+ });
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.listCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const api_1 = require("../api");
10
+ exports.listCommand = new commander_1.Command('list')
11
+ .description('List all your Genboxes')
12
+ .option('-a, --all', 'Include terminated genboxes')
13
+ .action(async (options) => {
14
+ try {
15
+ const allGenboxes = await (0, api_1.fetchApi)('/genboxes');
16
+ // Filter out terminated unless --all flag is used
17
+ const genboxes = options.all
18
+ ? allGenboxes
19
+ : allGenboxes.filter(a => a.status !== 'terminated');
20
+ console.log(chalk_1.default.bold('Your Genboxes:'));
21
+ console.log(chalk_1.default.dim('----------------------------------------------------'));
22
+ if (genboxes.length === 0) {
23
+ console.log(chalk_1.default.yellow('No active Genboxes found. Create one with `genbox create <name>`'));
24
+ return;
25
+ }
26
+ genboxes.forEach((genbox) => {
27
+ const statusColor = genbox.status === 'running' ? chalk_1.default.green :
28
+ genbox.status === 'terminated' ? chalk_1.default.red : chalk_1.default.yellow;
29
+ console.log(`${chalk_1.default.cyan(genbox.name)} \t ${statusColor(genbox.status)} \t ${chalk_1.default.dim(genbox.ipAddress || 'Pending IP')} \t (${genbox.size})`);
30
+ });
31
+ console.log(chalk_1.default.dim('----------------------------------------------------'));
32
+ }
33
+ catch (error) {
34
+ console.error(chalk_1.default.red(`Error: ${error.message}`));
35
+ }
36
+ });
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loginCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const inquirer_1 = __importDefault(require("inquirer"));
10
+ const api_1 = require("../api");
11
+ const config_store_1 = require("../config-store");
12
+ exports.loginCommand = new commander_1.Command('login')
13
+ .description('Login to Genbox')
14
+ .action(async () => {
15
+ try {
16
+ const answers = await inquirer_1.default.prompt([
17
+ {
18
+ type: 'input',
19
+ name: 'email',
20
+ message: 'Email:',
21
+ validate: (input) => input.includes('@') || 'Please enter a valid email',
22
+ },
23
+ {
24
+ type: 'password',
25
+ name: 'password',
26
+ message: 'Password:',
27
+ mask: '*',
28
+ },
29
+ ]);
30
+ const response = await (0, api_1.fetchApi)('/auth/login', {
31
+ method: 'POST',
32
+ body: JSON.stringify(answers),
33
+ });
34
+ if (response && response.access_token) {
35
+ config_store_1.ConfigStore.save({
36
+ auth: {
37
+ token: response.access_token,
38
+ user: response.user,
39
+ },
40
+ });
41
+ console.log(chalk_1.default.green('Successfully logged in!'));
42
+ }
43
+ else {
44
+ console.error(chalk_1.default.red('Login failed: No token received.'));
45
+ }
46
+ }
47
+ catch (error) {
48
+ // Handle user cancellation (Ctrl+C) gracefully
49
+ if (error.name === 'ExitPromptError' || error.message?.includes('force closed')) {
50
+ console.log('');
51
+ console.log(chalk_1.default.dim('Cancelled.'));
52
+ return;
53
+ }
54
+ console.error(chalk_1.default.red('Login failed:'), error.message);
55
+ }
56
+ });