genbox 1.0.0 → 1.0.2
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/api.js +42 -4
- package/dist/commands/connect.js +19 -8
- package/dist/commands/create.js +20 -1
- package/dist/commands/destroy.js +20 -9
- package/dist/commands/forward.js +18 -7
- package/dist/commands/list.js +50 -12
- package/dist/commands/restore-db.js +19 -9
- package/dist/commands/status.js +21 -9
- package/dist/commands/urls.js +22 -14
- package/dist/genbox-selector.js +142 -0
- package/package.json +1 -1
package/dist/api.js
CHANGED
|
@@ -1,8 +1,43 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AuthenticationError = void 0;
|
|
7
|
+
exports.handleApiError = handleApiError;
|
|
8
|
+
exports.isAuthError = isAuthError;
|
|
3
9
|
exports.fetchApi = fetchApi;
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
4
11
|
const config_store_1 = require("./config-store");
|
|
5
12
|
const API_URL = process.env.GENBOX_API_URL || 'https://api.genbox.dev';
|
|
13
|
+
class AuthenticationError extends Error {
|
|
14
|
+
constructor(message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = 'AuthenticationError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.AuthenticationError = AuthenticationError;
|
|
20
|
+
/**
|
|
21
|
+
* Handle errors with proper messages for auth errors
|
|
22
|
+
*/
|
|
23
|
+
function handleApiError(error) {
|
|
24
|
+
if (error instanceof AuthenticationError) {
|
|
25
|
+
console.error('');
|
|
26
|
+
console.error(chalk_1.default.red('✖ Not logged in'));
|
|
27
|
+
console.error('');
|
|
28
|
+
console.error(chalk_1.default.yellow(' Please authenticate first:'));
|
|
29
|
+
console.error(chalk_1.default.cyan(' $ genbox login'));
|
|
30
|
+
console.error('');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check if error is an authentication error
|
|
37
|
+
*/
|
|
38
|
+
function isAuthError(error) {
|
|
39
|
+
return error instanceof AuthenticationError;
|
|
40
|
+
}
|
|
6
41
|
async function fetchApi(endpoint, options = {}) {
|
|
7
42
|
const url = `${API_URL}${endpoint}`;
|
|
8
43
|
const token = config_store_1.ConfigStore.getToken();
|
|
@@ -13,15 +48,18 @@ async function fetchApi(endpoint, options = {}) {
|
|
|
13
48
|
if (token) {
|
|
14
49
|
headers['Authorization'] = `Bearer ${token}`;
|
|
15
50
|
}
|
|
16
|
-
else {
|
|
17
|
-
// Fallback for prototype (optional, or remove if we want forced auth)
|
|
18
|
-
headers['X-User-ID'] = 'user-1 (prototype)';
|
|
19
|
-
}
|
|
20
51
|
const response = await fetch(url, {
|
|
21
52
|
...options,
|
|
22
53
|
headers,
|
|
23
54
|
});
|
|
24
55
|
if (!response.ok) {
|
|
56
|
+
// Handle authentication errors with a friendly message
|
|
57
|
+
if (response.status === 401) {
|
|
58
|
+
throw new AuthenticationError('You are not logged in. Please run `genbox login` first.');
|
|
59
|
+
}
|
|
60
|
+
if (response.status === 403) {
|
|
61
|
+
throw new AuthenticationError('Access denied. Your session may have expired. Please run `genbox login` again.');
|
|
62
|
+
}
|
|
25
63
|
const text = await response.text();
|
|
26
64
|
throw new Error(`API Error (${response.status}): ${text}`);
|
|
27
65
|
}
|
package/dist/commands/connect.js
CHANGED
|
@@ -40,6 +40,7 @@ exports.connectCommand = void 0;
|
|
|
40
40
|
const commander_1 = require("commander");
|
|
41
41
|
const chalk_1 = __importDefault(require("chalk"));
|
|
42
42
|
const api_1 = require("../api");
|
|
43
|
+
const genbox_selector_1 = require("../genbox-selector");
|
|
43
44
|
const os = __importStar(require("os"));
|
|
44
45
|
const path = __importStar(require("path"));
|
|
45
46
|
const fs = __importStar(require("fs"));
|
|
@@ -59,24 +60,30 @@ function getPrivateSshKey() {
|
|
|
59
60
|
}
|
|
60
61
|
exports.connectCommand = new commander_1.Command('connect')
|
|
61
62
|
.description('SSH into a Genbox')
|
|
62
|
-
.argument('
|
|
63
|
-
.
|
|
63
|
+
.argument('[name]', 'Name of the Genbox (optional - will prompt if not provided)')
|
|
64
|
+
.option('-a, --all', 'Select from all genboxes (not just current project)')
|
|
65
|
+
.action(async (name, options) => {
|
|
64
66
|
try {
|
|
65
|
-
// 1.
|
|
66
|
-
const
|
|
67
|
-
|
|
67
|
+
// 1. Select Genbox (interactive if no name provided)
|
|
68
|
+
const { genbox: target, cancelled } = await (0, genbox_selector_1.selectGenbox)(name, {
|
|
69
|
+
all: options.all,
|
|
70
|
+
selectMessage: 'Select a genbox to connect to:',
|
|
71
|
+
});
|
|
72
|
+
if (cancelled) {
|
|
73
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
68
76
|
if (!target) {
|
|
69
|
-
console.error(chalk_1.default.red(`Genbox '${name}' not found.`));
|
|
70
77
|
return;
|
|
71
78
|
}
|
|
72
79
|
if (!target.ipAddress) {
|
|
73
|
-
console.error(chalk_1.default.yellow(`Genbox '${name}' is still provisioning (no IP). Please wait.`));
|
|
80
|
+
console.error(chalk_1.default.yellow(`Genbox '${target.name}' is still provisioning (no IP). Please wait.`));
|
|
74
81
|
return;
|
|
75
82
|
}
|
|
76
83
|
// 2. Get Key
|
|
77
84
|
const keyPath = getPrivateSshKey();
|
|
78
85
|
// 3. Connect
|
|
79
|
-
console.log(chalk_1.default.dim(`Connecting to ${chalk_1.default.bold(name)} (${target.ipAddress})...`));
|
|
86
|
+
console.log(chalk_1.default.dim(`Connecting to ${chalk_1.default.bold(target.name)} (${target.ipAddress})...`));
|
|
80
87
|
const sshArgs = [
|
|
81
88
|
'-i', keyPath,
|
|
82
89
|
'-o', 'StrictHostKeyChecking=no',
|
|
@@ -91,6 +98,10 @@ exports.connectCommand = new commander_1.Command('connect')
|
|
|
91
98
|
});
|
|
92
99
|
}
|
|
93
100
|
catch (error) {
|
|
101
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
102
|
+
(0, api_1.handleApiError)(error);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
94
105
|
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
95
106
|
}
|
|
96
107
|
});
|
package/dist/commands/create.js
CHANGED
|
@@ -204,7 +204,16 @@ exports.createCommand = new commander_1.Command('create')
|
|
|
204
204
|
console.log(` Destroy: ${chalk_1.default.cyan(`genbox destroy ${name}`)}`);
|
|
205
205
|
}
|
|
206
206
|
catch (innerError) {
|
|
207
|
-
|
|
207
|
+
if (innerError instanceof api_1.AuthenticationError) {
|
|
208
|
+
spinner.fail(chalk_1.default.red('Not logged in'));
|
|
209
|
+
console.error('');
|
|
210
|
+
console.error(chalk_1.default.yellow(' Please authenticate first:'));
|
|
211
|
+
console.error(chalk_1.default.cyan(' $ genbox login'));
|
|
212
|
+
console.error('');
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
spinner.fail(chalk_1.default.red(`Failed to create Genbox '${name}': ${innerError.message}`));
|
|
216
|
+
}
|
|
208
217
|
}
|
|
209
218
|
}
|
|
210
219
|
catch (error) {
|
|
@@ -214,6 +223,16 @@ exports.createCommand = new commander_1.Command('create')
|
|
|
214
223
|
console.log(chalk_1.default.dim('Cancelled.'));
|
|
215
224
|
return;
|
|
216
225
|
}
|
|
226
|
+
// Handle authentication errors with a helpful message
|
|
227
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
228
|
+
console.error('');
|
|
229
|
+
console.error(chalk_1.default.red('✖ Not logged in'));
|
|
230
|
+
console.error('');
|
|
231
|
+
console.error(chalk_1.default.yellow(' Please authenticate first:'));
|
|
232
|
+
console.error(chalk_1.default.cyan(' $ genbox login'));
|
|
233
|
+
console.error('');
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
217
236
|
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
218
237
|
}
|
|
219
238
|
});
|
package/dist/commands/destroy.js
CHANGED
|
@@ -9,18 +9,25 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
9
9
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
10
10
|
const ora_1 = __importDefault(require("ora"));
|
|
11
11
|
const api_1 = require("../api");
|
|
12
|
+
const genbox_selector_1 = require("../genbox-selector");
|
|
12
13
|
const ssh_config_1 = require("../ssh-config");
|
|
13
14
|
exports.destroyCommand = new commander_1.Command('destroy')
|
|
14
15
|
.description('Destroy a Genbox')
|
|
15
|
-
.argument('
|
|
16
|
+
.argument('[name]', 'Name of the Genbox to destroy (optional - will prompt if not provided)')
|
|
16
17
|
.option('-y, --yes', 'Skip confirmation')
|
|
18
|
+
.option('-a, --all', 'Select from all genboxes (not just current project)')
|
|
17
19
|
.action(async (name, options) => {
|
|
18
20
|
try {
|
|
19
|
-
// 1.
|
|
20
|
-
const
|
|
21
|
-
|
|
21
|
+
// 1. Select Genbox (interactive if no name provided)
|
|
22
|
+
const { genbox: target, cancelled } = await (0, genbox_selector_1.selectGenbox)(name, {
|
|
23
|
+
all: options.all,
|
|
24
|
+
selectMessage: 'Select a genbox to destroy:',
|
|
25
|
+
});
|
|
26
|
+
if (cancelled) {
|
|
27
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
22
30
|
if (!target) {
|
|
23
|
-
console.error(chalk_1.default.red(`Genbox '${name}' not found.`));
|
|
24
31
|
return;
|
|
25
32
|
}
|
|
26
33
|
// 2. Confirm
|
|
@@ -30,7 +37,7 @@ exports.destroyCommand = new commander_1.Command('destroy')
|
|
|
30
37
|
{
|
|
31
38
|
type: 'confirm',
|
|
32
39
|
name: 'confirmed',
|
|
33
|
-
message: `Are you sure you want to PERMANENTLY destroy '${name}' (${target.ipAddress})?`,
|
|
40
|
+
message: `Are you sure you want to PERMANENTLY destroy '${target.name}' (${target.ipAddress || 'no IP'})?`,
|
|
34
41
|
default: false,
|
|
35
42
|
},
|
|
36
43
|
]);
|
|
@@ -41,13 +48,13 @@ exports.destroyCommand = new commander_1.Command('destroy')
|
|
|
41
48
|
return;
|
|
42
49
|
}
|
|
43
50
|
// 3. Delete
|
|
44
|
-
const spinner = (0, ora_1.default)(`Destroying ${name}...`).start();
|
|
51
|
+
const spinner = (0, ora_1.default)(`Destroying ${target.name}...`).start();
|
|
45
52
|
await (0, api_1.fetchApi)(`/genboxes/${target._id}`, {
|
|
46
53
|
method: 'DELETE',
|
|
47
54
|
});
|
|
48
55
|
// Remove SSH config entry
|
|
49
|
-
(0, ssh_config_1.removeSshConfigEntry)(name);
|
|
50
|
-
spinner.succeed(chalk_1.default.green(`Genbox '${name}' destroyed successfully.`));
|
|
56
|
+
(0, ssh_config_1.removeSshConfigEntry)(target.name);
|
|
57
|
+
spinner.succeed(chalk_1.default.green(`Genbox '${target.name}' destroyed successfully.`));
|
|
51
58
|
console.log(chalk_1.default.dim(' SSH config entry removed'));
|
|
52
59
|
}
|
|
53
60
|
catch (error) {
|
|
@@ -57,6 +64,10 @@ exports.destroyCommand = new commander_1.Command('destroy')
|
|
|
57
64
|
console.log(chalk_1.default.dim('Cancelled.'));
|
|
58
65
|
return;
|
|
59
66
|
}
|
|
67
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
68
|
+
(0, api_1.handleApiError)(error);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
60
71
|
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
61
72
|
}
|
|
62
73
|
});
|
package/dist/commands/forward.js
CHANGED
|
@@ -40,6 +40,7 @@ exports.forwardCommand = void 0;
|
|
|
40
40
|
const commander_1 = require("commander");
|
|
41
41
|
const chalk_1 = __importDefault(require("chalk"));
|
|
42
42
|
const api_1 = require("../api");
|
|
43
|
+
const genbox_selector_1 = require("../genbox-selector");
|
|
43
44
|
const os = __importStar(require("os"));
|
|
44
45
|
const path = __importStar(require("path"));
|
|
45
46
|
const fs = __importStar(require("fs"));
|
|
@@ -60,19 +61,25 @@ function getPrivateSshKey() {
|
|
|
60
61
|
}
|
|
61
62
|
exports.forwardCommand = new commander_1.Command('forward')
|
|
62
63
|
.description('Set up SSH port forwarding to a Genbox for local debugging')
|
|
63
|
-
.argument('
|
|
64
|
+
.argument('[name]', 'Name of the Genbox (optional - will prompt if not provided)')
|
|
64
65
|
.option('-p, --ports <ports>', 'Additional ports to forward (comma-separated, e.g., "8080,9000")')
|
|
66
|
+
.option('-a, --all', 'Select from all genboxes (not just current project)')
|
|
65
67
|
.action(async (name, options) => {
|
|
66
68
|
try {
|
|
67
|
-
// 1.
|
|
68
|
-
const
|
|
69
|
-
|
|
69
|
+
// 1. Select Genbox (interactive if no name provided)
|
|
70
|
+
const { genbox: target, cancelled } = await (0, genbox_selector_1.selectGenbox)(name, {
|
|
71
|
+
all: options.all,
|
|
72
|
+
selectMessage: 'Select a genbox for port forwarding:',
|
|
73
|
+
});
|
|
74
|
+
if (cancelled) {
|
|
75
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
70
78
|
if (!target) {
|
|
71
|
-
console.error(chalk_1.default.red(`Genbox '${name}' not found.`));
|
|
72
79
|
return;
|
|
73
80
|
}
|
|
74
81
|
if (!target.ipAddress) {
|
|
75
|
-
console.error(chalk_1.default.yellow(`Genbox '${name}' is still provisioning (no IP). Please wait.`));
|
|
82
|
+
console.error(chalk_1.default.yellow(`Genbox '${target.name}' is still provisioning (no IP). Please wait.`));
|
|
76
83
|
return;
|
|
77
84
|
}
|
|
78
85
|
// 2. Get SSH key
|
|
@@ -117,7 +124,7 @@ exports.forwardCommand = new commander_1.Command('forward')
|
|
|
117
124
|
}
|
|
118
125
|
}
|
|
119
126
|
// 4. Display what will be forwarded
|
|
120
|
-
console.log(chalk_1.default.blue(`[INFO] Setting up port forwarding to ${chalk_1.default.bold(name)}...`));
|
|
127
|
+
console.log(chalk_1.default.blue(`[INFO] Setting up port forwarding to ${chalk_1.default.bold(target.name)}...`));
|
|
121
128
|
console.log('');
|
|
122
129
|
console.log(chalk_1.default.dim('Forwarded ports:'));
|
|
123
130
|
for (const mapping of portMappings) {
|
|
@@ -161,6 +168,10 @@ exports.forwardCommand = new commander_1.Command('forward')
|
|
|
161
168
|
});
|
|
162
169
|
}
|
|
163
170
|
catch (error) {
|
|
171
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
172
|
+
(0, api_1.handleApiError)(error);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
164
175
|
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
165
176
|
}
|
|
166
177
|
});
|
package/dist/commands/list.js
CHANGED
|
@@ -7,30 +7,68 @@ exports.listCommand = void 0;
|
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
9
|
const api_1 = require("../api");
|
|
10
|
+
const genbox_selector_1 = require("../genbox-selector");
|
|
10
11
|
exports.listCommand = new commander_1.Command('list')
|
|
11
|
-
.description('List
|
|
12
|
-
.option('-a, --all', '
|
|
12
|
+
.description('List genboxes (scoped to current project by default)')
|
|
13
|
+
.option('-a, --all', 'Show all genboxes across all projects')
|
|
14
|
+
.option('--terminated', 'Include terminated genboxes')
|
|
13
15
|
.action(async (options) => {
|
|
14
16
|
try {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
const projectName = (0, genbox_selector_1.getProjectContext)();
|
|
18
|
+
const genboxes = await (0, genbox_selector_1.getGenboxes)({
|
|
19
|
+
all: options.all,
|
|
20
|
+
includeTerminated: options.terminated,
|
|
21
|
+
});
|
|
22
|
+
// Show context header
|
|
23
|
+
if (options.all) {
|
|
24
|
+
console.log(chalk_1.default.bold('All Genboxes:'));
|
|
25
|
+
}
|
|
26
|
+
else if (projectName) {
|
|
27
|
+
console.log(chalk_1.default.bold(`Genboxes for ${chalk_1.default.cyan(projectName)}:`));
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.log(chalk_1.default.bold('Your Genboxes:'));
|
|
31
|
+
console.log(chalk_1.default.dim(' (Not in a project directory. Showing all genboxes.)'));
|
|
32
|
+
}
|
|
33
|
+
console.log(chalk_1.default.dim('────────────────────────────────────────────────────'));
|
|
22
34
|
if (genboxes.length === 0) {
|
|
23
|
-
|
|
35
|
+
if (projectName && !options.all) {
|
|
36
|
+
console.log(chalk_1.default.yellow(`No genboxes found for project '${projectName}'.`));
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log(chalk_1.default.dim(' Create one with:'));
|
|
39
|
+
console.log(chalk_1.default.cyan(' $ genbox create <name>'));
|
|
40
|
+
console.log('');
|
|
41
|
+
console.log(chalk_1.default.dim(` Or see all genboxes with:`));
|
|
42
|
+
console.log(chalk_1.default.cyan(' $ genbox list --all'));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
console.log(chalk_1.default.yellow('No genboxes found.'));
|
|
46
|
+
console.log('');
|
|
47
|
+
console.log(chalk_1.default.dim(' Create one with:'));
|
|
48
|
+
console.log(chalk_1.default.cyan(' $ genbox create <name>'));
|
|
49
|
+
}
|
|
24
50
|
return;
|
|
25
51
|
}
|
|
26
52
|
genboxes.forEach((genbox) => {
|
|
27
53
|
const statusColor = genbox.status === 'running' ? chalk_1.default.green :
|
|
28
54
|
genbox.status === 'terminated' ? chalk_1.default.red : chalk_1.default.yellow;
|
|
29
|
-
|
|
55
|
+
// Show project info when listing all
|
|
56
|
+
const projectInfo = options.all && genbox.project
|
|
57
|
+
? chalk_1.default.dim(` [${genbox.project}]`)
|
|
58
|
+
: '';
|
|
59
|
+
console.log(`${chalk_1.default.cyan(genbox.name)}${projectInfo} \t ${statusColor(genbox.status)} \t ${chalk_1.default.dim(genbox.ipAddress || 'Pending IP')} \t (${genbox.size})`);
|
|
30
60
|
});
|
|
31
|
-
console.log(chalk_1.default.dim('
|
|
61
|
+
console.log(chalk_1.default.dim('────────────────────────────────────────────────────'));
|
|
62
|
+
// Show hint if in project context and not showing all
|
|
63
|
+
if (projectName && !options.all) {
|
|
64
|
+
console.log(chalk_1.default.dim(` Showing ${genboxes.length} genbox(es) for this project. Use ${chalk_1.default.cyan('--all')} to see all.`));
|
|
65
|
+
}
|
|
32
66
|
}
|
|
33
67
|
catch (error) {
|
|
68
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
69
|
+
(0, api_1.handleApiError)(error);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
34
72
|
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
35
73
|
}
|
|
36
74
|
});
|
|
@@ -41,6 +41,7 @@ const commander_1 = require("commander");
|
|
|
41
41
|
const chalk_1 = __importDefault(require("chalk"));
|
|
42
42
|
const ora_1 = __importDefault(require("ora"));
|
|
43
43
|
const api_1 = require("../api");
|
|
44
|
+
const genbox_selector_1 = require("../genbox-selector");
|
|
44
45
|
const config_1 = require("../config");
|
|
45
46
|
const os = __importStar(require("os"));
|
|
46
47
|
const path = __importStar(require("path"));
|
|
@@ -113,10 +114,11 @@ function getDatabaseSettings() {
|
|
|
113
114
|
}
|
|
114
115
|
exports.restoreDbCommand = new commander_1.Command('restore-db')
|
|
115
116
|
.description('Restore local MongoDB database to a remote Genbox')
|
|
116
|
-
.argument('
|
|
117
|
+
.argument('[name]', 'Name of the Genbox (optional - will prompt if not provided)')
|
|
117
118
|
.option('--db <database>', 'Database name to restore (from genbox.yaml or project name)')
|
|
118
119
|
.option('--container <name>', 'Remote MongoDB container name (from genbox.yaml)')
|
|
119
120
|
.option('--local-uri <uri>', 'Local MongoDB URI (from genbox.yaml or MONGODB_URI env)')
|
|
121
|
+
.option('-a, --all', 'Select from all genboxes (not just current project)')
|
|
120
122
|
.action(async (name, options) => {
|
|
121
123
|
const spinner = (0, ora_1.default)();
|
|
122
124
|
try {
|
|
@@ -135,19 +137,23 @@ exports.restoreDbCommand = new commander_1.Command('restore-db')
|
|
|
135
137
|
console.log(chalk_1.default.dim(` Database: ${dbName}`));
|
|
136
138
|
console.log(chalk_1.default.dim(` Container: ${container}`));
|
|
137
139
|
console.log('');
|
|
138
|
-
// 3.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
// 3. Select Genbox (interactive if no name provided)
|
|
141
|
+
const { genbox: target, cancelled } = await (0, genbox_selector_1.selectGenbox)(name, {
|
|
142
|
+
all: options.all,
|
|
143
|
+
selectMessage: 'Select a genbox to restore database to:',
|
|
144
|
+
});
|
|
145
|
+
if (cancelled) {
|
|
146
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
142
149
|
if (!target) {
|
|
143
|
-
spinner.fail(`Genbox '${name}' not found.`);
|
|
144
150
|
return;
|
|
145
151
|
}
|
|
146
152
|
if (!target.ipAddress) {
|
|
147
|
-
|
|
153
|
+
console.error(chalk_1.default.yellow(`Genbox '${target.name}' is still provisioning (no IP). Please wait.`));
|
|
148
154
|
return;
|
|
149
155
|
}
|
|
150
|
-
|
|
156
|
+
console.log(chalk_1.default.green(`✓ Found Genbox: ${target.name} (${target.ipAddress})`));
|
|
151
157
|
// 4. Get SSH key
|
|
152
158
|
let keyPath;
|
|
153
159
|
try {
|
|
@@ -265,9 +271,13 @@ exports.restoreDbCommand = new commander_1.Command('restore-db')
|
|
|
265
271
|
catch {
|
|
266
272
|
console.log(chalk_1.default.dim(' Unable to fetch stats'));
|
|
267
273
|
}
|
|
268
|
-
console.log(chalk_1.default.green(`\n✓ Database '${dbName}' has been restored to ${name}`));
|
|
274
|
+
console.log(chalk_1.default.green(`\n✓ Database '${dbName}' has been restored to ${target.name}`));
|
|
269
275
|
}
|
|
270
276
|
catch (error) {
|
|
277
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
278
|
+
(0, api_1.handleApiError)(error);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
271
281
|
spinner.fail(`Error: ${error.message}`);
|
|
272
282
|
}
|
|
273
283
|
});
|
package/dist/commands/status.js
CHANGED
|
@@ -41,6 +41,7 @@ const commander_1 = require("commander");
|
|
|
41
41
|
const chalk_1 = __importDefault(require("chalk"));
|
|
42
42
|
const api_1 = require("../api");
|
|
43
43
|
const config_1 = require("../config");
|
|
44
|
+
const genbox_selector_1 = require("../genbox-selector");
|
|
44
45
|
const os = __importStar(require("os"));
|
|
45
46
|
const path = __importStar(require("path"));
|
|
46
47
|
const fs = __importStar(require("fs"));
|
|
@@ -79,20 +80,27 @@ function sshExec(ip, keyPath, command, timeoutSecs = 10) {
|
|
|
79
80
|
}
|
|
80
81
|
exports.statusCommand = new commander_1.Command('status')
|
|
81
82
|
.description('Check cloud-init progress and service status of a Genbox')
|
|
82
|
-
.argument('
|
|
83
|
-
.
|
|
83
|
+
.argument('[name]', 'Name of the Genbox (optional - will prompt if not provided)')
|
|
84
|
+
.option('-a, --all', 'Select from all genboxes (not just current project)')
|
|
85
|
+
.action(async (name, options) => {
|
|
84
86
|
try {
|
|
85
|
-
// 1.
|
|
86
|
-
const
|
|
87
|
-
|
|
87
|
+
// 1. Select Genbox (interactive if no name provided)
|
|
88
|
+
const { genbox: target, cancelled } = await (0, genbox_selector_1.selectGenbox)(name, {
|
|
89
|
+
all: options.all,
|
|
90
|
+
selectMessage: 'Select a genbox to check status:',
|
|
91
|
+
});
|
|
92
|
+
if (cancelled) {
|
|
93
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
88
96
|
if (!target) {
|
|
89
|
-
console.error(chalk_1.default.red(`Genbox '${name}' not found.`));
|
|
90
97
|
return;
|
|
91
98
|
}
|
|
92
99
|
if (!target.ipAddress) {
|
|
93
|
-
console.error(chalk_1.default.yellow(`Genbox '${name}' is still provisioning (no IP yet). Please wait.`));
|
|
100
|
+
console.error(chalk_1.default.yellow(`Genbox '${target.name}' is still provisioning (no IP yet). Please wait.`));
|
|
94
101
|
return;
|
|
95
102
|
}
|
|
103
|
+
const selectedName = target.name;
|
|
96
104
|
// 2. Get SSH key
|
|
97
105
|
let keyPath;
|
|
98
106
|
try {
|
|
@@ -102,7 +110,7 @@ exports.statusCommand = new commander_1.Command('status')
|
|
|
102
110
|
console.error(chalk_1.default.red(error.message));
|
|
103
111
|
return;
|
|
104
112
|
}
|
|
105
|
-
console.log(chalk_1.default.blue(`[INFO] Checking cloud-init progress on ${
|
|
113
|
+
console.log(chalk_1.default.blue(`[INFO] Checking cloud-init progress on ${selectedName}...`));
|
|
106
114
|
console.log('');
|
|
107
115
|
// 3. Check cloud-init status
|
|
108
116
|
const status = sshExec(target.ipAddress, keyPath, 'cloud-init status 2>&1');
|
|
@@ -281,7 +289,7 @@ exports.statusCommand = new commander_1.Command('status')
|
|
|
281
289
|
console.log(errorLog);
|
|
282
290
|
}
|
|
283
291
|
console.log('');
|
|
284
|
-
console.log(chalk_1.default.dim('Run `genbox connect ' +
|
|
292
|
+
console.log(chalk_1.default.dim('Run `genbox connect ' + selectedName + '` to investigate further.'));
|
|
285
293
|
}
|
|
286
294
|
}
|
|
287
295
|
else {
|
|
@@ -289,6 +297,10 @@ exports.statusCommand = new commander_1.Command('status')
|
|
|
289
297
|
}
|
|
290
298
|
}
|
|
291
299
|
catch (error) {
|
|
300
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
301
|
+
(0, api_1.handleApiError)(error);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
292
304
|
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
293
305
|
}
|
|
294
306
|
});
|
package/dist/commands/urls.js
CHANGED
|
@@ -6,22 +6,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.urlsCommand = void 0;
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
-
const ora_1 = __importDefault(require("ora"));
|
|
10
9
|
const api_1 = require("../api");
|
|
10
|
+
const genbox_selector_1 = require("../genbox-selector");
|
|
11
11
|
exports.urlsCommand = new commander_1.Command('urls')
|
|
12
12
|
.description('Show all URLs for a Genbox environment')
|
|
13
|
-
.argument('
|
|
14
|
-
.
|
|
15
|
-
|
|
13
|
+
.argument('[name]', 'Name of the Genbox (optional - will prompt if not provided)')
|
|
14
|
+
.option('-a, --all', 'Select from all genboxes (not just current project)')
|
|
15
|
+
.action(async (name, options) => {
|
|
16
16
|
try {
|
|
17
|
-
//
|
|
18
|
-
const
|
|
19
|
-
|
|
17
|
+
// Select Genbox (interactive if no name provided)
|
|
18
|
+
const { genbox, cancelled } = await (0, genbox_selector_1.selectGenbox)(name, {
|
|
19
|
+
all: options.all,
|
|
20
|
+
selectMessage: 'Select a genbox to view URLs:',
|
|
21
|
+
});
|
|
22
|
+
if (cancelled) {
|
|
23
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
20
26
|
if (!genbox) {
|
|
21
|
-
spinner.fail(chalk_1.default.red(`Genbox '${name}' not found`));
|
|
22
27
|
return;
|
|
23
28
|
}
|
|
24
|
-
spinner.stop();
|
|
25
29
|
console.log('\n' + chalk_1.default.bold('Environment Details'));
|
|
26
30
|
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
27
31
|
console.log(` ${chalk_1.default.bold('Name:')} ${genbox.name}`);
|
|
@@ -46,14 +50,18 @@ exports.urlsCommand = new commander_1.Command('urls')
|
|
|
46
50
|
}
|
|
47
51
|
// Show connection info
|
|
48
52
|
console.log('\n' + chalk_1.default.bold('Quick Commands:'));
|
|
49
|
-
console.log(` ${chalk_1.default.dim('SSH:')} genbox connect ${name}`);
|
|
50
|
-
console.log(` ${chalk_1.default.dim('Logs:')} genbox logs ${name}`);
|
|
51
|
-
console.log(` ${chalk_1.default.dim('Status:')} genbox status ${name}`);
|
|
52
|
-
console.log(` ${chalk_1.default.dim('Destroy:')} genbox destroy ${name}`);
|
|
53
|
+
console.log(` ${chalk_1.default.dim('SSH:')} genbox connect ${genbox.name}`);
|
|
54
|
+
console.log(` ${chalk_1.default.dim('Logs:')} genbox logs ${genbox.name}`);
|
|
55
|
+
console.log(` ${chalk_1.default.dim('Status:')} genbox status ${genbox.name}`);
|
|
56
|
+
console.log(` ${chalk_1.default.dim('Destroy:')} genbox destroy ${genbox.name}`);
|
|
53
57
|
console.log('');
|
|
54
58
|
}
|
|
55
59
|
catch (error) {
|
|
56
|
-
|
|
60
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
61
|
+
(0, api_1.handleApiError)(error);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
57
65
|
}
|
|
58
66
|
});
|
|
59
67
|
function getStatusColor(status) {
|
|
@@ -0,0 +1,142 @@
|
|
|
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.getProjectContext = getProjectContext;
|
|
7
|
+
exports.getGenboxes = getGenboxes;
|
|
8
|
+
exports.isInProjectContext = isInProjectContext;
|
|
9
|
+
exports.selectGenbox = selectGenbox;
|
|
10
|
+
exports.showProjectContext = showProjectContext;
|
|
11
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
12
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
13
|
+
const api_1 = require("./api");
|
|
14
|
+
const config_1 = require("./config");
|
|
15
|
+
/**
|
|
16
|
+
* Get project name from genbox.yaml if available
|
|
17
|
+
*/
|
|
18
|
+
function getProjectContext() {
|
|
19
|
+
if (!(0, config_1.hasConfig)()) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const config = (0, config_1.loadConfig)();
|
|
24
|
+
return config.project_name || null;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Fetch genboxes, optionally filtered by current project context
|
|
32
|
+
*/
|
|
33
|
+
async function getGenboxes(options = {}) {
|
|
34
|
+
const allGenboxes = await (0, api_1.fetchApi)('/genboxes');
|
|
35
|
+
let genboxes = allGenboxes;
|
|
36
|
+
// Filter out terminated unless explicitly requested
|
|
37
|
+
if (!options.includeTerminated) {
|
|
38
|
+
genboxes = genboxes.filter(g => g.status !== 'terminated');
|
|
39
|
+
}
|
|
40
|
+
// Filter by project context unless --all is specified
|
|
41
|
+
if (!options.all) {
|
|
42
|
+
const projectName = getProjectContext();
|
|
43
|
+
if (projectName) {
|
|
44
|
+
genboxes = genboxes.filter(g => g.project === projectName || g.workspace === projectName);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return genboxes;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Check if we're in a project context
|
|
51
|
+
*/
|
|
52
|
+
function isInProjectContext() {
|
|
53
|
+
return getProjectContext() !== null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Interactive genbox selector with project context awareness
|
|
57
|
+
* - If one genbox: auto-selects it
|
|
58
|
+
* - If multiple: shows interactive select
|
|
59
|
+
* - If none: shows appropriate message
|
|
60
|
+
*/
|
|
61
|
+
async function selectGenbox(name, options = {}) {
|
|
62
|
+
const projectName = getProjectContext();
|
|
63
|
+
const genboxes = await getGenboxes({
|
|
64
|
+
all: options.all,
|
|
65
|
+
includeTerminated: options.includeTerminated,
|
|
66
|
+
});
|
|
67
|
+
// If name is provided, find it directly
|
|
68
|
+
if (name) {
|
|
69
|
+
const genbox = genboxes.find(g => g.name === name);
|
|
70
|
+
if (!genbox) {
|
|
71
|
+
// Check if it exists in all genboxes (might be in different project)
|
|
72
|
+
if (!options.all && projectName) {
|
|
73
|
+
const allGenboxes = await getGenboxes({ all: true, includeTerminated: options.includeTerminated });
|
|
74
|
+
const existsElsewhere = allGenboxes.find(g => g.name === name);
|
|
75
|
+
if (existsElsewhere) {
|
|
76
|
+
console.error(chalk_1.default.yellow(`Genbox '${name}' exists but belongs to a different project.`));
|
|
77
|
+
console.error(chalk_1.default.dim(` Use ${chalk_1.default.cyan('--all')} flag to access it.`));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.error(chalk_1.default.red(`Genbox '${name}' not found.`));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
console.error(chalk_1.default.red(`Genbox '${name}' not found.`));
|
|
85
|
+
}
|
|
86
|
+
return { genbox: null, genboxes };
|
|
87
|
+
}
|
|
88
|
+
return { genbox, genboxes };
|
|
89
|
+
}
|
|
90
|
+
// No name provided - interactive selection
|
|
91
|
+
if (genboxes.length === 0) {
|
|
92
|
+
const emptyMsg = options.emptyMessage || (projectName
|
|
93
|
+
? `No genboxes found for project '${projectName}'.`
|
|
94
|
+
: 'No genboxes found.');
|
|
95
|
+
console.log(chalk_1.default.yellow(emptyMsg));
|
|
96
|
+
if (projectName && !options.all) {
|
|
97
|
+
console.log(chalk_1.default.dim(` Use ${chalk_1.default.cyan('genbox list --all')} to see all genboxes.`));
|
|
98
|
+
}
|
|
99
|
+
return { genbox: null, genboxes };
|
|
100
|
+
}
|
|
101
|
+
// Only one genbox - auto-select
|
|
102
|
+
if (genboxes.length === 1) {
|
|
103
|
+
const genbox = genboxes[0];
|
|
104
|
+
console.log(chalk_1.default.dim(`Auto-selected: ${chalk_1.default.cyan(genbox.name)}`));
|
|
105
|
+
return { genbox, genboxes };
|
|
106
|
+
}
|
|
107
|
+
// Multiple genboxes - interactive select
|
|
108
|
+
try {
|
|
109
|
+
const choices = genboxes.map(g => {
|
|
110
|
+
const statusColor = g.status === 'running' ? chalk_1.default.green :
|
|
111
|
+
g.status === 'terminated' ? chalk_1.default.red : chalk_1.default.yellow;
|
|
112
|
+
return {
|
|
113
|
+
name: `${g.name} ${statusColor(`(${g.status})`)} ${chalk_1.default.dim(g.ipAddress || 'No IP')}`,
|
|
114
|
+
value: g,
|
|
115
|
+
short: g.name,
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
const { selected } = await inquirer_1.default.prompt([{
|
|
119
|
+
type: 'list',
|
|
120
|
+
name: 'selected',
|
|
121
|
+
message: options.selectMessage || 'Select a genbox:',
|
|
122
|
+
choices,
|
|
123
|
+
}]);
|
|
124
|
+
return { genbox: selected, genboxes };
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
// Handle Ctrl+C
|
|
128
|
+
if (error.name === 'ExitPromptError' || error.message?.includes('force closed')) {
|
|
129
|
+
return { genbox: null, genboxes, cancelled: true };
|
|
130
|
+
}
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Display project context info
|
|
136
|
+
*/
|
|
137
|
+
function showProjectContext() {
|
|
138
|
+
const projectName = getProjectContext();
|
|
139
|
+
if (projectName) {
|
|
140
|
+
console.log(chalk_1.default.dim(`Project: ${projectName}`));
|
|
141
|
+
}
|
|
142
|
+
}
|