genbox 1.0.131 → 1.0.133
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/README.md +1 -2
- package/dist/commands/balance.js +9 -1
- package/dist/commands/completion.js +109 -11
- package/dist/commands/destroy.js +1 -0
- package/dist/commands/doctor.js +357 -0
- package/dist/commands/help.js +230 -7
- package/dist/commands/list.js +17 -3
- package/dist/commands/run-prompt.js +49 -8
- package/dist/commands/start.js +1 -0
- package/dist/commands/status.js +3 -6
- package/dist/commands/update.js +135 -0
- package/dist/commands/urls.js +15 -1
- package/dist/index.js +88 -2
- package/dist/utils/logo.js +22 -20
- package/package.json +4 -9
package/README.md
CHANGED
|
@@ -193,8 +193,7 @@ Environments auto-destroy after 58 minutes of inactivity to save credits.
|
|
|
193
193
|
|
|
194
194
|
- **Website**: [genbox.dev](https://genbox.dev)
|
|
195
195
|
- **Documentation**: [genbox.dev/docs](https://genbox.dev/docs)
|
|
196
|
-
- **
|
|
197
|
-
- **Issues**: [github.com/goodpass-co/genbox/issues](https://github.com/goodpass-co/genbox/issues)
|
|
196
|
+
- **Support**: [genbox.dev/support](https://genbox.dev/support)
|
|
198
197
|
|
|
199
198
|
## License
|
|
200
199
|
|
package/dist/commands/balance.js
CHANGED
|
@@ -9,9 +9,17 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
9
9
|
const api_1 = require("../api");
|
|
10
10
|
exports.balanceCommand = new commander_1.Command('balance')
|
|
11
11
|
.description('Show current credit balance')
|
|
12
|
-
.
|
|
12
|
+
.option('--json', 'Output as JSON (for scripting)')
|
|
13
|
+
.action(async (options) => {
|
|
13
14
|
try {
|
|
14
15
|
const user = await (0, api_1.fetchApi)('/users/me');
|
|
16
|
+
if (options.json) {
|
|
17
|
+
console.log(JSON.stringify({
|
|
18
|
+
userId: user.externalId,
|
|
19
|
+
credits: user.credits,
|
|
20
|
+
}, null, 2));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
15
23
|
console.log(chalk_1.default.bold('User ID:'), user.externalId);
|
|
16
24
|
console.log(chalk_1.default.bold('Credits:'), chalk_1.default.green(user.credits));
|
|
17
25
|
}
|
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.completionCommand = void 0;
|
|
39
|
+
exports.completeCommand = exports.completionCommand = void 0;
|
|
40
40
|
exports.isCompletionInstalledForCurrentShell = isCompletionInstalledForCurrentShell;
|
|
41
41
|
exports.getCompletionHint = getCompletionHint;
|
|
42
42
|
const commander_1 = require("commander");
|
|
@@ -44,6 +44,59 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
44
44
|
const os = __importStar(require("os"));
|
|
45
45
|
const path = __importStar(require("path"));
|
|
46
46
|
const fs = __importStar(require("fs"));
|
|
47
|
+
const api_1 = require("../api");
|
|
48
|
+
const genbox_selector_1 = require("../genbox-selector");
|
|
49
|
+
// Cache file for genbox names (for fast completion)
|
|
50
|
+
const CACHE_FILE = path.join(os.homedir(), '.genbox', 'completion-cache.json');
|
|
51
|
+
const CACHE_TTL = 60 * 1000; // 1 minute cache
|
|
52
|
+
/**
|
|
53
|
+
* Get cached genbox names or fetch fresh ones
|
|
54
|
+
*/
|
|
55
|
+
async function getCachedGenboxNames() {
|
|
56
|
+
const project = (0, genbox_selector_1.getProjectContext)() || undefined;
|
|
57
|
+
// Try to read from cache first
|
|
58
|
+
try {
|
|
59
|
+
if (fs.existsSync(CACHE_FILE)) {
|
|
60
|
+
const cache = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
|
|
61
|
+
const age = Date.now() - cache.timestamp;
|
|
62
|
+
// Use cache if fresh and same project context
|
|
63
|
+
if (age < CACHE_TTL && cache.project === project) {
|
|
64
|
+
return cache.genboxNames;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Cache read failed, continue to fetch
|
|
70
|
+
}
|
|
71
|
+
// Fetch fresh data
|
|
72
|
+
try {
|
|
73
|
+
const genboxes = await (0, api_1.fetchApi)('/genboxes');
|
|
74
|
+
const names = genboxes
|
|
75
|
+
.filter(g => g.status !== 'terminated')
|
|
76
|
+
.map(g => g.name);
|
|
77
|
+
// Save to cache
|
|
78
|
+
try {
|
|
79
|
+
const cacheDir = path.dirname(CACHE_FILE);
|
|
80
|
+
if (!fs.existsSync(cacheDir)) {
|
|
81
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
const cache = {
|
|
84
|
+
genboxNames: names,
|
|
85
|
+
timestamp: Date.now(),
|
|
86
|
+
project,
|
|
87
|
+
};
|
|
88
|
+
fs.writeFileSync(CACHE_FILE, JSON.stringify(cache));
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Cache write failed, ignore
|
|
92
|
+
}
|
|
93
|
+
return names;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// API call failed, return empty
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
47
100
|
// Get all command names for completion
|
|
48
101
|
const commands = [
|
|
49
102
|
'init', 'create', 'list', 'ls', 'destroy', 'delete', 'connect', 'balance',
|
|
@@ -141,6 +194,11 @@ function generateBashCompletion() {
|
|
|
141
194
|
# Or save to a file:
|
|
142
195
|
# gb completion bash > /etc/bash_completion.d/gb
|
|
143
196
|
|
|
197
|
+
# Get genbox names dynamically
|
|
198
|
+
_genbox_names() {
|
|
199
|
+
gb __complete genbox-names 2>/dev/null
|
|
200
|
+
}
|
|
201
|
+
|
|
144
202
|
_genbox_completions() {
|
|
145
203
|
local cur prev commands
|
|
146
204
|
COMPREPLY=()
|
|
@@ -186,9 +244,10 @@ _genbox_completions() {
|
|
|
186
244
|
COMPREPLY=( $(compgen -W "bash zsh fish --install" -- "\${cur}") )
|
|
187
245
|
return 0
|
|
188
246
|
;;
|
|
189
|
-
# Commands that take genbox names -
|
|
190
|
-
|
|
191
|
-
|
|
247
|
+
# Commands that take genbox names - dynamic completion
|
|
248
|
+
destroy|delete|connect|status|urls|forward|push|restart|stop|start|resume|extend|rebuild|run|run-prompt|backup|restore-db)
|
|
249
|
+
local genbox_names=$(_genbox_names)
|
|
250
|
+
COMPREPLY=( $(compgen -W "\${genbox_names}" -- "\${cur}") )
|
|
192
251
|
return 0
|
|
193
252
|
;;
|
|
194
253
|
-s|--size)
|
|
@@ -264,6 +323,13 @@ function generateZshCompletion() {
|
|
|
264
323
|
# Or save to a file in your fpath:
|
|
265
324
|
# gb completion zsh > ~/.zsh/completions/_gb
|
|
266
325
|
|
|
326
|
+
# Get genbox names dynamically
|
|
327
|
+
_genbox_names() {
|
|
328
|
+
local -a names
|
|
329
|
+
names=(\${(f)"$(gb __complete genbox-names 2>/dev/null)"})
|
|
330
|
+
_describe -t genboxes 'genbox name' names
|
|
331
|
+
}
|
|
332
|
+
|
|
267
333
|
_genbox() {
|
|
268
334
|
local -a commands
|
|
269
335
|
commands=(
|
|
@@ -386,29 +452,29 @@ _genbox() {
|
|
|
386
452
|
'(-y --yes)'{-y,--yes}'[Skip confirmation]' \\
|
|
387
453
|
'(-a --all)'{-a,--all}'[Bulk delete mode]' \\
|
|
388
454
|
'--save-changes[Handle changes]:action:(save trash skip)' \\
|
|
389
|
-
'1:name:'
|
|
455
|
+
'1:name:_genbox_names'
|
|
390
456
|
;;
|
|
391
457
|
status)
|
|
392
458
|
_arguments \\
|
|
393
459
|
'(-a --all)'{-a,--all}'[Select from all]' \\
|
|
394
460
|
'(-w --watch)'{-w,--watch}'[Watch mode]' \\
|
|
395
|
-
'1:name:'
|
|
461
|
+
'1:name:_genbox_names'
|
|
396
462
|
;;
|
|
397
463
|
connect|urls|restart|stop)
|
|
398
464
|
_arguments \\
|
|
399
465
|
'(-a --all)'{-a,--all}'[Select from all]' \\
|
|
400
|
-
'1:name:'
|
|
466
|
+
'1:name:_genbox_names'
|
|
401
467
|
;;
|
|
402
468
|
start|resume)
|
|
403
469
|
_arguments \\
|
|
404
470
|
'(-a --all)'{-a,--all}'[Select from all]' \\
|
|
405
|
-
'1:name:'
|
|
471
|
+
'1:name:_genbox_names'
|
|
406
472
|
;;
|
|
407
473
|
forward)
|
|
408
474
|
_arguments \\
|
|
409
475
|
'(-a --all)'{-a,--all}'[Select from all]' \\
|
|
410
476
|
'(-p --ports)'{-p,--ports}'[Additional ports]:ports:' \\
|
|
411
|
-
'1:name:'
|
|
477
|
+
'1:name:_genbox_names'
|
|
412
478
|
;;
|
|
413
479
|
extend)
|
|
414
480
|
_arguments \\
|
|
@@ -416,13 +482,13 @@ _genbox() {
|
|
|
416
482
|
'--hours[Hours to extend]:hours:' \\
|
|
417
483
|
'--disable-auto-destroy[Disable auto-destroy]' \\
|
|
418
484
|
'--enable-auto-destroy[Enable auto-destroy]' \\
|
|
419
|
-
'1:name:'
|
|
485
|
+
'1:name:_genbox_names'
|
|
420
486
|
;;
|
|
421
487
|
rebuild)
|
|
422
488
|
_arguments \\
|
|
423
489
|
'(-a --all)'{-a,--all}'[Select from all]' \\
|
|
424
490
|
'(-y --yes)'{-y,--yes}'[Skip confirmation]' \\
|
|
425
|
-
'1:name:'
|
|
491
|
+
'1:name:_genbox_names'
|
|
426
492
|
;;
|
|
427
493
|
help)
|
|
428
494
|
_values 'command' init create status connect forward restore-db urls push destroy
|
|
@@ -446,6 +512,11 @@ function generateFishCompletion() {
|
|
|
446
512
|
complete -c genbox -f
|
|
447
513
|
complete -c gb -f
|
|
448
514
|
|
|
515
|
+
# Function to get genbox names dynamically
|
|
516
|
+
function __fish_genbox_names
|
|
517
|
+
gb __complete genbox-names 2>/dev/null
|
|
518
|
+
end
|
|
519
|
+
|
|
449
520
|
# Main commands
|
|
450
521
|
complete -c genbox -c gb -n "__fish_use_subcommand" -a "init" -d "Initialize genbox.yaml configuration"
|
|
451
522
|
complete -c genbox -c gb -n "__fish_use_subcommand" -a "create" -d "Create a new Genbox environment"
|
|
@@ -533,6 +604,9 @@ complete -c genbox -c gb -n "__fish_seen_subcommand_from extend" -l hours -d "Ho
|
|
|
533
604
|
complete -c genbox -c gb -n "__fish_seen_subcommand_from extend" -l disable-auto-destroy -d "Disable auto-destroy"
|
|
534
605
|
complete -c genbox -c gb -n "__fish_seen_subcommand_from extend" -l enable-auto-destroy -d "Enable auto-destroy"
|
|
535
606
|
|
|
607
|
+
# Dynamic genbox name completion for commands that take a genbox name
|
|
608
|
+
complete -c genbox -c gb -n "__fish_seen_subcommand_from destroy delete connect status urls forward push restart stop start resume extend rebuild backup restore-db run run-prompt" -a "(__fish_genbox_names)" -d "Genbox name"
|
|
609
|
+
|
|
536
610
|
# Global options
|
|
537
611
|
complete -c genbox -c gb -s h -l help -d "Show help"
|
|
538
612
|
complete -c genbox -c gb -s v -l version -d "Show version"
|
|
@@ -633,3 +707,27 @@ function getCompletionHint() {
|
|
|
633
707
|
return `${chalk_1.default.dim('Enable tab completion for a better experience:')}
|
|
634
708
|
${chalk_1.default.cyan(`gb completion ${shellName} --install`)}`;
|
|
635
709
|
}
|
|
710
|
+
/**
|
|
711
|
+
* Hidden __complete command for dynamic completion support
|
|
712
|
+
* Used by shell completion scripts to get genbox names
|
|
713
|
+
*/
|
|
714
|
+
exports.completeCommand = new commander_1.Command('__complete')
|
|
715
|
+
.description('Internal command for shell completions')
|
|
716
|
+
.argument('<type>', 'Completion type: genbox-names')
|
|
717
|
+
.action(async (type) => {
|
|
718
|
+
switch (type) {
|
|
719
|
+
case 'genbox-names':
|
|
720
|
+
try {
|
|
721
|
+
const names = await getCachedGenboxNames();
|
|
722
|
+
// Output names, one per line (for shell parsing)
|
|
723
|
+
names.forEach(name => console.log(name));
|
|
724
|
+
}
|
|
725
|
+
catch {
|
|
726
|
+
// Silently fail - don't break completion
|
|
727
|
+
}
|
|
728
|
+
break;
|
|
729
|
+
default:
|
|
730
|
+
// Unknown type, output nothing
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
});
|
package/dist/commands/destroy.js
CHANGED
|
@@ -323,6 +323,7 @@ exports.destroyCommand = new commander_1.Command('destroy')
|
|
|
323
323
|
.option('-y, --yes', 'Skip confirmation and save-work prompts')
|
|
324
324
|
.option('-a, --all', 'Bulk delete mode - select multiple genboxes to destroy')
|
|
325
325
|
.option('--save-changes <action>', 'Handle uncommitted changes: save (commit/push/PR), trash (discard), skip (ignore)')
|
|
326
|
+
.addHelpText('after', '\nAliases: gb delete')
|
|
326
327
|
.action(async (name, options) => {
|
|
327
328
|
try {
|
|
328
329
|
// Bulk delete mode when --all is used without a specific name
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Doctor Command
|
|
4
|
+
*
|
|
5
|
+
* Diagnoses CLI setup and reports any issues.
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
41
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
|
+
};
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.doctorCommand = void 0;
|
|
45
|
+
const commander_1 = require("commander");
|
|
46
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
const os = __importStar(require("os"));
|
|
50
|
+
const child_process_1 = require("child_process");
|
|
51
|
+
const config_store_1 = require("../config-store");
|
|
52
|
+
const api_1 = require("../api");
|
|
53
|
+
async function runChecks() {
|
|
54
|
+
const results = [];
|
|
55
|
+
// 1. Check Node.js version
|
|
56
|
+
const nodeVersion = process.version;
|
|
57
|
+
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0], 10);
|
|
58
|
+
if (majorVersion >= 18) {
|
|
59
|
+
results.push({
|
|
60
|
+
name: 'Node.js',
|
|
61
|
+
status: 'ok',
|
|
62
|
+
message: `${nodeVersion} installed`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
results.push({
|
|
67
|
+
name: 'Node.js',
|
|
68
|
+
status: 'error',
|
|
69
|
+
message: `${nodeVersion} is too old`,
|
|
70
|
+
hint: 'Genbox CLI requires Node.js 18 or higher',
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// 2. Check authentication
|
|
74
|
+
const config = config_store_1.ConfigStore.load();
|
|
75
|
+
if (config.auth?.token) {
|
|
76
|
+
const user = config.auth.user;
|
|
77
|
+
if (user?.email) {
|
|
78
|
+
results.push({
|
|
79
|
+
name: 'Authentication',
|
|
80
|
+
status: 'ok',
|
|
81
|
+
message: `Logged in as ${user.email}`,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
results.push({
|
|
86
|
+
name: 'Authentication',
|
|
87
|
+
status: 'ok',
|
|
88
|
+
message: 'Authenticated',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// 3. Check API connectivity (only if authenticated)
|
|
92
|
+
try {
|
|
93
|
+
await (0, api_1.fetchApi)('/user/profile');
|
|
94
|
+
results.push({
|
|
95
|
+
name: 'API Connection',
|
|
96
|
+
status: 'ok',
|
|
97
|
+
message: 'Connected to API',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (error.message?.includes('401') || error.message?.includes('Unauthorized')) {
|
|
102
|
+
results.push({
|
|
103
|
+
name: 'API Connection',
|
|
104
|
+
status: 'error',
|
|
105
|
+
message: 'Token expired or invalid',
|
|
106
|
+
hint: 'Run `gb login` to re-authenticate',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
results.push({
|
|
111
|
+
name: 'API Connection',
|
|
112
|
+
status: 'warning',
|
|
113
|
+
message: `Could not reach API: ${error.message}`,
|
|
114
|
+
hint: 'Check your internet connection',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
results.push({
|
|
121
|
+
name: 'Authentication',
|
|
122
|
+
status: 'warning',
|
|
123
|
+
message: 'Not logged in',
|
|
124
|
+
hint: 'Run `gb login` to authenticate',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
// 4. Check SSH configuration
|
|
128
|
+
const sshConfigPath = path.join(os.homedir(), '.ssh', 'config');
|
|
129
|
+
if (fs.existsSync(sshConfigPath)) {
|
|
130
|
+
try {
|
|
131
|
+
const sshConfig = fs.readFileSync(sshConfigPath, 'utf8');
|
|
132
|
+
const hasGenboxHosts = sshConfig.includes('# Genbox') || sshConfig.includes('genbox.dev');
|
|
133
|
+
if (hasGenboxHosts) {
|
|
134
|
+
results.push({
|
|
135
|
+
name: 'SSH Config',
|
|
136
|
+
status: 'ok',
|
|
137
|
+
message: 'Genbox hosts configured',
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
results.push({
|
|
142
|
+
name: 'SSH Config',
|
|
143
|
+
status: 'ok',
|
|
144
|
+
message: 'SSH config exists (no genbox hosts yet)',
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
results.push({
|
|
150
|
+
name: 'SSH Config',
|
|
151
|
+
status: 'warning',
|
|
152
|
+
message: 'Could not read SSH config',
|
|
153
|
+
hint: 'Check permissions on ~/.ssh/config',
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
results.push({
|
|
159
|
+
name: 'SSH Config',
|
|
160
|
+
status: 'warning',
|
|
161
|
+
message: 'No SSH config file',
|
|
162
|
+
hint: 'SSH config will be created when you create a genbox',
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
// 5. Check SSH key
|
|
166
|
+
const sshKeyPath = path.join(os.homedir(), '.ssh', 'id_ed25519');
|
|
167
|
+
const sshKeyPathRsa = path.join(os.homedir(), '.ssh', 'id_rsa');
|
|
168
|
+
if (fs.existsSync(sshKeyPath)) {
|
|
169
|
+
results.push({
|
|
170
|
+
name: 'SSH Key',
|
|
171
|
+
status: 'ok',
|
|
172
|
+
message: 'Ed25519 key found',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
else if (fs.existsSync(sshKeyPathRsa)) {
|
|
176
|
+
results.push({
|
|
177
|
+
name: 'SSH Key',
|
|
178
|
+
status: 'ok',
|
|
179
|
+
message: 'RSA key found',
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
results.push({
|
|
184
|
+
name: 'SSH Key',
|
|
185
|
+
status: 'warning',
|
|
186
|
+
message: 'No SSH key found',
|
|
187
|
+
hint: 'Run `ssh-keygen -t ed25519` to create one',
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
// 6. Check genbox.yaml in current directory
|
|
191
|
+
const genboxYamlPath = path.join(process.cwd(), 'genbox.yaml');
|
|
192
|
+
if (fs.existsSync(genboxYamlPath)) {
|
|
193
|
+
try {
|
|
194
|
+
const yaml = require('js-yaml');
|
|
195
|
+
const content = fs.readFileSync(genboxYamlPath, 'utf8');
|
|
196
|
+
const config = yaml.load(content);
|
|
197
|
+
if (config.version && config.project_name) {
|
|
198
|
+
results.push({
|
|
199
|
+
name: 'Project Config',
|
|
200
|
+
status: 'ok',
|
|
201
|
+
message: `genbox.yaml found (v${config.version}, project: ${config.project_name})`,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
results.push({
|
|
206
|
+
name: 'Project Config',
|
|
207
|
+
status: 'warning',
|
|
208
|
+
message: 'genbox.yaml missing required fields',
|
|
209
|
+
hint: 'Ensure version and project_name are set',
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
results.push({
|
|
215
|
+
name: 'Project Config',
|
|
216
|
+
status: 'error',
|
|
217
|
+
message: 'Invalid genbox.yaml',
|
|
218
|
+
hint: error.message,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
results.push({
|
|
224
|
+
name: 'Project Config',
|
|
225
|
+
status: 'ok',
|
|
226
|
+
message: 'No genbox.yaml in current directory',
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// 7. Check .env.genbox
|
|
230
|
+
const envGenboxPath = path.join(process.cwd(), '.env.genbox');
|
|
231
|
+
if (fs.existsSync(genboxYamlPath)) {
|
|
232
|
+
if (fs.existsSync(envGenboxPath)) {
|
|
233
|
+
results.push({
|
|
234
|
+
name: 'Environment',
|
|
235
|
+
status: 'ok',
|
|
236
|
+
message: '.env.genbox found',
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
results.push({
|
|
241
|
+
name: 'Environment',
|
|
242
|
+
status: 'warning',
|
|
243
|
+
message: 'No .env.genbox file',
|
|
244
|
+
hint: 'Create .env.genbox for secrets (GITHUB_PAT, etc.)',
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// 8. Check git
|
|
249
|
+
try {
|
|
250
|
+
(0, child_process_1.execSync)('git --version', { stdio: 'pipe' });
|
|
251
|
+
results.push({
|
|
252
|
+
name: 'Git',
|
|
253
|
+
status: 'ok',
|
|
254
|
+
message: 'Git installed',
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
results.push({
|
|
259
|
+
name: 'Git',
|
|
260
|
+
status: 'warning',
|
|
261
|
+
message: 'Git not found',
|
|
262
|
+
hint: 'Git is recommended for version control',
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
// 9. Check shell completion
|
|
266
|
+
const shell = process.env.SHELL || '';
|
|
267
|
+
let completionInstalled = false;
|
|
268
|
+
const home = os.homedir();
|
|
269
|
+
if (shell.includes('zsh')) {
|
|
270
|
+
const zshrc = path.join(home, '.zshrc');
|
|
271
|
+
if (fs.existsSync(zshrc)) {
|
|
272
|
+
const content = fs.readFileSync(zshrc, 'utf8');
|
|
273
|
+
completionInstalled = content.includes('gb completion');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
else if (shell.includes('bash')) {
|
|
277
|
+
const bashrc = path.join(home, '.bashrc');
|
|
278
|
+
const bashProfile = path.join(home, '.bash_profile');
|
|
279
|
+
for (const file of [bashrc, bashProfile]) {
|
|
280
|
+
if (fs.existsSync(file)) {
|
|
281
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
282
|
+
if (content.includes('gb completion')) {
|
|
283
|
+
completionInstalled = true;
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
else if (shell.includes('fish')) {
|
|
290
|
+
const fishCompletion = path.join(home, '.config', 'fish', 'completions', 'gb.fish');
|
|
291
|
+
completionInstalled = fs.existsSync(fishCompletion);
|
|
292
|
+
}
|
|
293
|
+
if (completionInstalled) {
|
|
294
|
+
results.push({
|
|
295
|
+
name: 'Shell Completion',
|
|
296
|
+
status: 'ok',
|
|
297
|
+
message: 'Tab completion installed',
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
const shellName = shell.includes('zsh') ? 'zsh' : shell.includes('fish') ? 'fish' : 'bash';
|
|
302
|
+
results.push({
|
|
303
|
+
name: 'Shell Completion',
|
|
304
|
+
status: 'ok',
|
|
305
|
+
message: 'Tab completion not installed (optional)',
|
|
306
|
+
hint: `Run \`gb completion ${shellName} --install\` to enable`,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
return results;
|
|
310
|
+
}
|
|
311
|
+
exports.doctorCommand = new commander_1.Command('doctor')
|
|
312
|
+
.description('Check CLI configuration and diagnose issues')
|
|
313
|
+
.action(async () => {
|
|
314
|
+
console.log('');
|
|
315
|
+
console.log(chalk_1.default.bold('Genbox CLI Diagnostics'));
|
|
316
|
+
console.log('');
|
|
317
|
+
const results = await runChecks();
|
|
318
|
+
// Display results
|
|
319
|
+
let hasErrors = false;
|
|
320
|
+
let hasWarnings = false;
|
|
321
|
+
for (const result of results) {
|
|
322
|
+
let statusIcon;
|
|
323
|
+
let statusColor;
|
|
324
|
+
switch (result.status) {
|
|
325
|
+
case 'ok':
|
|
326
|
+
statusIcon = chalk_1.default.green('✓');
|
|
327
|
+
statusColor = chalk_1.default;
|
|
328
|
+
break;
|
|
329
|
+
case 'warning':
|
|
330
|
+
statusIcon = chalk_1.default.yellow('⚠');
|
|
331
|
+
statusColor = chalk_1.default.yellow;
|
|
332
|
+
hasWarnings = true;
|
|
333
|
+
break;
|
|
334
|
+
case 'error':
|
|
335
|
+
statusIcon = chalk_1.default.red('✗');
|
|
336
|
+
statusColor = chalk_1.default.red;
|
|
337
|
+
hasErrors = true;
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
console.log(`${statusIcon} ${chalk_1.default.bold(result.name)}: ${statusColor(result.message)}`);
|
|
341
|
+
if (result.hint) {
|
|
342
|
+
console.log(` ${chalk_1.default.dim(result.hint)}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// Summary
|
|
346
|
+
console.log('');
|
|
347
|
+
if (hasErrors) {
|
|
348
|
+
console.log(chalk_1.default.red('Some issues need to be fixed before using Genbox CLI.'));
|
|
349
|
+
}
|
|
350
|
+
else if (hasWarnings) {
|
|
351
|
+
console.log(chalk_1.default.yellow('Some optional improvements are available.'));
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
console.log(chalk_1.default.green('Everything looks good!'));
|
|
355
|
+
}
|
|
356
|
+
console.log('');
|
|
357
|
+
});
|