genbox 1.0.113 → 1.0.115
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/list.js +19 -3
- package/dist/commands/start.js +132 -0
- package/dist/commands/stop.js +94 -0
- package/dist/genbox-selector.js +5 -1
- package/dist/index.js +5 -1
- package/package.json +13 -12
package/dist/commands/list.js
CHANGED
|
@@ -93,9 +93,15 @@ exports.listCommand = new commander_1.Command('list')
|
|
|
93
93
|
endHourInfo = chalk_1.default.red(' hour ending...');
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
// Show auto-destroy status
|
|
96
|
+
// Show auto-destroy status (not applicable for stopped/terminated)
|
|
97
97
|
let protectedInfo = '';
|
|
98
|
-
if (genbox.
|
|
98
|
+
if (genbox.status === 'stopped') {
|
|
99
|
+
protectedInfo = chalk_1.default.dim(' → gb start to resume');
|
|
100
|
+
}
|
|
101
|
+
else if (genbox.status === 'terminated') {
|
|
102
|
+
// No extra info for terminated
|
|
103
|
+
}
|
|
104
|
+
else if (genbox.autoDestroyOnInactivity === false) {
|
|
99
105
|
protectedInfo = chalk_1.default.yellow(' [protected]');
|
|
100
106
|
}
|
|
101
107
|
else if (genbox.protectedUntil) {
|
|
@@ -115,7 +121,17 @@ exports.listCommand = new commander_1.Command('list')
|
|
|
115
121
|
}
|
|
116
122
|
const namePart = chalk_1.default.cyan(nameWithProject.padEnd(nameWidth));
|
|
117
123
|
const statusPart = statusColor(genbox.status.padEnd(statusWidth));
|
|
118
|
-
|
|
124
|
+
// Show appropriate IP text based on status
|
|
125
|
+
let ipText = genbox.ipAddress || 'Pending IP';
|
|
126
|
+
if (!genbox.ipAddress) {
|
|
127
|
+
if (genbox.status === 'stopped') {
|
|
128
|
+
ipText = '(paused)';
|
|
129
|
+
}
|
|
130
|
+
else if (genbox.status === 'terminated') {
|
|
131
|
+
ipText = '-';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const ipPart = chalk_1.default.dim(ipText.padEnd(ipWidth));
|
|
119
135
|
const sizePart = `(${genbox.size})`.padEnd(sizeWidth);
|
|
120
136
|
const extraInfo = endHourInfo + protectedInfo;
|
|
121
137
|
console.log(`${namePart} ${statusPart} ${ipPart} ${sizePart}${extraInfo}`);
|
|
@@ -0,0 +1,132 @@
|
|
|
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.startCommand = void 0;
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
const api_1 = require("../api");
|
|
11
|
+
const genbox_selector_1 = require("../genbox-selector");
|
|
12
|
+
const ssh_config_1 = require("../ssh-config");
|
|
13
|
+
exports.startCommand = new commander_1.Command('start')
|
|
14
|
+
.alias('resume')
|
|
15
|
+
.description('Start/resume a stopped Genbox')
|
|
16
|
+
.argument('[name]', 'Name of the Genbox to start')
|
|
17
|
+
.option('-w, --wait', 'Wait for genbox to be fully ready')
|
|
18
|
+
.action(async (name, options) => {
|
|
19
|
+
try {
|
|
20
|
+
const { genbox: target, cancelled } = await (0, genbox_selector_1.selectGenbox)(name, {
|
|
21
|
+
selectMessage: 'Select a genbox to start:',
|
|
22
|
+
statusFilter: 'stopped',
|
|
23
|
+
});
|
|
24
|
+
if (cancelled) {
|
|
25
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (!target) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (target.status !== 'stopped') {
|
|
32
|
+
if (target.status === 'running') {
|
|
33
|
+
console.log(chalk_1.default.yellow(`Genbox '${target.name}' is already running`));
|
|
34
|
+
console.log(chalk_1.default.dim(` IP: ${target.ipAddress}`));
|
|
35
|
+
console.log(chalk_1.default.dim(` SSH: ssh ${target.name}`));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
console.error(chalk_1.default.red(`Genbox '${target.name}' cannot be started (status: ${target.status})`));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// Start the genbox
|
|
42
|
+
const spinner = (0, ora_1.default)(`Starting ${target.name}...`).start();
|
|
43
|
+
const result = await (0, api_1.fetchApi)(`/genboxes/${target._id}/start`, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
});
|
|
46
|
+
// Show boot source info
|
|
47
|
+
const bootSourceLabels = {
|
|
48
|
+
snapshot: 'from snapshot (instant)',
|
|
49
|
+
template: 'from project template',
|
|
50
|
+
backup: 'from S3 backup',
|
|
51
|
+
base: 'fresh boot',
|
|
52
|
+
};
|
|
53
|
+
spinner.succeed(chalk_1.default.green(`Genbox '${target.name}' starting ${chalk_1.default.dim(`(${bootSourceLabels[result.bootSource]})`)}`));
|
|
54
|
+
console.log('');
|
|
55
|
+
console.log(chalk_1.default.dim(` Estimated boot time: ${result.estimatedBootTime}`));
|
|
56
|
+
// If we have an IP already (snapshot/template boot), show connection info
|
|
57
|
+
if (result.ipAddress) {
|
|
58
|
+
// Add SSH config entry
|
|
59
|
+
(0, ssh_config_1.addSshConfigEntry)({ name: result.name, ipAddress: result.ipAddress });
|
|
60
|
+
console.log('');
|
|
61
|
+
console.log(chalk_1.default.green(' Ready to connect:'));
|
|
62
|
+
console.log(chalk_1.default.cyan(` ssh ${result.name}`));
|
|
63
|
+
if (result.urls && Object.keys(result.urls).length > 0) {
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log(chalk_1.default.dim(' URLs:'));
|
|
66
|
+
for (const [service, url] of Object.entries(result.urls)) {
|
|
67
|
+
console.log(chalk_1.default.dim(` ${service}: ${url}`));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.log('');
|
|
73
|
+
console.log(chalk_1.default.dim(' Server is booting...'));
|
|
74
|
+
console.log(chalk_1.default.dim(` Run ${chalk_1.default.cyan(`gb status ${result.name}`)} to check progress`));
|
|
75
|
+
}
|
|
76
|
+
// If --wait flag, poll until ready
|
|
77
|
+
if (options.wait) {
|
|
78
|
+
console.log('');
|
|
79
|
+
const waitSpinner = (0, ora_1.default)('Waiting for genbox to be ready...').start();
|
|
80
|
+
let attempts = 0;
|
|
81
|
+
const maxAttempts = 60; // 5 minutes max (5s intervals)
|
|
82
|
+
while (attempts < maxAttempts) {
|
|
83
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
84
|
+
attempts++;
|
|
85
|
+
try {
|
|
86
|
+
const status = await (0, api_1.fetchApi)(`/genboxes/${result._id}`);
|
|
87
|
+
if (status.status === 'running' && status.ipAddress) {
|
|
88
|
+
waitSpinner.succeed(chalk_1.default.green('Genbox is ready!'));
|
|
89
|
+
// Add SSH config entry
|
|
90
|
+
(0, ssh_config_1.addSshConfigEntry)({ name: status.name, ipAddress: status.ipAddress });
|
|
91
|
+
console.log('');
|
|
92
|
+
console.log(chalk_1.default.green(' Connect with:'));
|
|
93
|
+
console.log(chalk_1.default.cyan(` ssh ${status.name}`));
|
|
94
|
+
if (status.urls && Object.keys(status.urls).length > 0) {
|
|
95
|
+
console.log('');
|
|
96
|
+
console.log(chalk_1.default.dim(' URLs:'));
|
|
97
|
+
for (const [service, url] of Object.entries(status.urls)) {
|
|
98
|
+
console.log(chalk_1.default.dim(` ${service}: ${url}`));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (status.status === 'error') {
|
|
104
|
+
waitSpinner.fail(chalk_1.default.red('Genbox failed to start'));
|
|
105
|
+
if (status.setupErrors) {
|
|
106
|
+
console.log(chalk_1.default.dim(` Errors: ${status.setupErrors}`));
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
waitSpinner.text = `Waiting for genbox to be ready... (${attempts * 5}s)`;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Ignore polling errors
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
waitSpinner.warn(chalk_1.default.yellow('Timed out waiting for genbox'));
|
|
117
|
+
console.log(chalk_1.default.dim(` Run ${chalk_1.default.cyan(`gb status ${result.name}`)} to check progress`));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
if (error.name === 'ExitPromptError' || error.message?.includes('force closed')) {
|
|
122
|
+
console.log('');
|
|
123
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
127
|
+
(0, api_1.handleApiError)(error);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
131
|
+
}
|
|
132
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
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.stopCommand = void 0;
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const confirm_1 = __importDefault(require("@inquirer/confirm"));
|
|
10
|
+
const ora_1 = __importDefault(require("ora"));
|
|
11
|
+
const api_1 = require("../api");
|
|
12
|
+
const genbox_selector_1 = require("../genbox-selector");
|
|
13
|
+
const ssh_config_1 = require("../ssh-config");
|
|
14
|
+
exports.stopCommand = new commander_1.Command('stop')
|
|
15
|
+
.description('Stop a running Genbox (saves state for quick resume)')
|
|
16
|
+
.argument('[name]', 'Name of the Genbox to stop')
|
|
17
|
+
.option('-y, --yes', 'Skip confirmation prompt')
|
|
18
|
+
.action(async (name, options) => {
|
|
19
|
+
try {
|
|
20
|
+
const { genbox: target, cancelled } = await (0, genbox_selector_1.selectGenbox)(name, {
|
|
21
|
+
selectMessage: 'Select a genbox to stop:',
|
|
22
|
+
statusFilter: 'running',
|
|
23
|
+
});
|
|
24
|
+
if (cancelled) {
|
|
25
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (!target) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (target.status !== 'running') {
|
|
32
|
+
console.error(chalk_1.default.red(`Genbox '${target.name}' is not running (status: ${target.status})`));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Confirm
|
|
36
|
+
let confirmed = options.yes;
|
|
37
|
+
if (!confirmed) {
|
|
38
|
+
console.log('');
|
|
39
|
+
console.log(chalk_1.default.blue('Stop will:'));
|
|
40
|
+
console.log(chalk_1.default.dim(' 1. Save all uncommitted changes'));
|
|
41
|
+
console.log(chalk_1.default.dim(' 2. Create a snapshot for quick resume'));
|
|
42
|
+
console.log(chalk_1.default.dim(' 3. Destroy the server (to save costs)'));
|
|
43
|
+
console.log('');
|
|
44
|
+
confirmed = await (0, confirm_1.default)({
|
|
45
|
+
message: `Stop '${target.name}'?`,
|
|
46
|
+
default: true,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (!confirmed) {
|
|
50
|
+
console.log(chalk_1.default.dim('Operation cancelled.'));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// Stop the genbox
|
|
54
|
+
const spinner = (0, ora_1.default)(`Stopping ${target.name}...`).start();
|
|
55
|
+
spinner.text = 'Saving changes and creating snapshot...';
|
|
56
|
+
const result = await (0, api_1.fetchApi)(`/genboxes/${target._id}/stop`, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
});
|
|
59
|
+
if (result.success) {
|
|
60
|
+
spinner.succeed(chalk_1.default.green(`Genbox '${target.name}' stopped`));
|
|
61
|
+
console.log('');
|
|
62
|
+
if (result.bootSource === 'snapshot') {
|
|
63
|
+
console.log(chalk_1.default.green(' Snapshot created for instant resume'));
|
|
64
|
+
console.log(chalk_1.default.dim(` Resume time: ${result.estimatedResumeTime}`));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.log(chalk_1.default.yellow(' Backup created (snapshot not available on your plan)'));
|
|
68
|
+
console.log(chalk_1.default.dim(` Resume time: ${result.estimatedResumeTime}`));
|
|
69
|
+
console.log('');
|
|
70
|
+
console.log(chalk_1.default.cyan(' Upgrade to Pro for instant snapshots:'));
|
|
71
|
+
console.log(chalk_1.default.dim(' https://genbox.dev/pricing'));
|
|
72
|
+
}
|
|
73
|
+
console.log('');
|
|
74
|
+
console.log(chalk_1.default.dim(` To resume: ${chalk_1.default.cyan(`gb start ${target.name}`)}`));
|
|
75
|
+
// Remove SSH config entry (server is being destroyed)
|
|
76
|
+
(0, ssh_config_1.removeSshConfigEntry)(target.name);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
spinner.fail(chalk_1.default.red(`Failed to stop genbox: ${result.message}`));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
if (error.name === 'ExitPromptError' || error.message?.includes('force closed')) {
|
|
84
|
+
console.log('');
|
|
85
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
89
|
+
(0, api_1.handleApiError)(error);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
93
|
+
}
|
|
94
|
+
});
|
package/dist/genbox-selector.js
CHANGED
|
@@ -60,10 +60,14 @@ function isInProjectContext() {
|
|
|
60
60
|
*/
|
|
61
61
|
async function selectGenbox(name, options = {}) {
|
|
62
62
|
const projectName = getProjectContext();
|
|
63
|
-
|
|
63
|
+
let genboxes = await getGenboxes({
|
|
64
64
|
all: options.all,
|
|
65
65
|
includeTerminated: options.includeTerminated,
|
|
66
66
|
});
|
|
67
|
+
// Filter by status if specified
|
|
68
|
+
if (options.statusFilter) {
|
|
69
|
+
genboxes = genboxes.filter(g => g.status === options.statusFilter);
|
|
70
|
+
}
|
|
67
71
|
// If name is provided, find it directly
|
|
68
72
|
if (name) {
|
|
69
73
|
const genbox = genboxes.find(g => g.name === name);
|
package/dist/index.js
CHANGED
|
@@ -35,6 +35,8 @@ const cleanup_ssh_1 = require("./commands/cleanup-ssh");
|
|
|
35
35
|
const restart_1 = require("./commands/restart");
|
|
36
36
|
const backup_1 = require("./commands/backup");
|
|
37
37
|
const backups_1 = require("./commands/backups");
|
|
38
|
+
const stop_1 = require("./commands/stop");
|
|
39
|
+
const start_1 = require("./commands/start");
|
|
38
40
|
program
|
|
39
41
|
.addCommand(init_1.initCommand)
|
|
40
42
|
.addCommand(create_1.createCommand)
|
|
@@ -62,5 +64,7 @@ program
|
|
|
62
64
|
.addCommand(cleanup_ssh_1.cleanupSshCommand)
|
|
63
65
|
.addCommand(restart_1.restartCommand)
|
|
64
66
|
.addCommand(backup_1.backupCommand)
|
|
65
|
-
.addCommand(backups_1.backupsCommand)
|
|
67
|
+
.addCommand(backups_1.backupsCommand)
|
|
68
|
+
.addCommand(stop_1.stopCommand)
|
|
69
|
+
.addCommand(start_1.startCommand);
|
|
66
70
|
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genbox",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.115",
|
|
4
4
|
"description": "Genbox CLI - AI-Powered Development Environments",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -10,6 +10,17 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"dist/**/*"
|
|
12
12
|
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"dev": "ts-node src/index.ts",
|
|
17
|
+
"prepublishOnly": "npm run build",
|
|
18
|
+
"publish:patch": "./scripts/publish.sh patch",
|
|
19
|
+
"publish:minor": "./scripts/publish.sh minor",
|
|
20
|
+
"publish:major": "./scripts/publish.sh major",
|
|
21
|
+
"release": "./scripts/publish.sh patch",
|
|
22
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
23
|
+
},
|
|
13
24
|
"keywords": [
|
|
14
25
|
"genbox",
|
|
15
26
|
"ai",
|
|
@@ -54,15 +65,5 @@
|
|
|
54
65
|
"inquirer": "^13.0.2",
|
|
55
66
|
"js-yaml": "^4.1.1",
|
|
56
67
|
"ora": "^9.0.0"
|
|
57
|
-
},
|
|
58
|
-
"scripts": {
|
|
59
|
-
"build": "tsc",
|
|
60
|
-
"start": "node dist/index.js",
|
|
61
|
-
"dev": "ts-node src/index.ts",
|
|
62
|
-
"publish:patch": "./scripts/publish.sh patch",
|
|
63
|
-
"publish:minor": "./scripts/publish.sh minor",
|
|
64
|
-
"publish:major": "./scripts/publish.sh major",
|
|
65
|
-
"release": "./scripts/publish.sh patch",
|
|
66
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
67
68
|
}
|
|
68
|
-
}
|
|
69
|
+
}
|