genbox 1.0.34 → 1.0.36
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/status.js +135 -39
- package/package.json +1 -1
package/dist/commands/status.js
CHANGED
|
@@ -187,56 +187,152 @@ exports.statusCommand = new commander_1.Command('status')
|
|
|
187
187
|
console.error(chalk_1.default.red(error.message));
|
|
188
188
|
return;
|
|
189
189
|
}
|
|
190
|
-
console.log(chalk_1.default.blue(`[INFO] Checking
|
|
190
|
+
console.log(chalk_1.default.blue(`[INFO] Checking status of ${selectedName}...`));
|
|
191
191
|
console.log('');
|
|
192
|
-
// 3. Check
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
'
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
192
|
+
// 3. Check status from DB first (more reliable than SSH)
|
|
193
|
+
const dbStatus = target.status; // 'provisioning' | 'running' | 'error' etc.
|
|
194
|
+
const setupDuration = target.setupDuration;
|
|
195
|
+
const setupCompletedAt = target.setupCompletedAt;
|
|
196
|
+
// If DB says RUNNING and we have setupCompletedAt, setup is complete
|
|
197
|
+
if (dbStatus === 'running' && setupCompletedAt) {
|
|
198
|
+
const durationStr = setupDuration ? formatDuration(setupDuration) : 'unknown';
|
|
199
|
+
console.log(chalk_1.default.green(`[SUCCESS] Setup completed! (took ${durationStr})`));
|
|
200
|
+
console.log('');
|
|
201
|
+
// Show billing info
|
|
202
|
+
const billing = {
|
|
203
|
+
creditsPerHour: target.creditsPerHour || 1,
|
|
204
|
+
totalCreditsUsed: target.totalCreditsUsed || 0,
|
|
205
|
+
totalHoursUsed: target.totalHoursUsed || 0,
|
|
206
|
+
currentHourEnd: target.currentHourEnd,
|
|
207
|
+
lastActivityAt: target.lastActivityAt,
|
|
208
|
+
autoDestroyOnInactivity: target.autoDestroyOnInactivity ?? true,
|
|
209
|
+
};
|
|
210
|
+
const now = new Date();
|
|
211
|
+
const minutesUntilBilling = billing.currentHourEnd
|
|
212
|
+
? Math.max(0, Math.ceil((new Date(billing.currentHourEnd).getTime() - now.getTime()) / (60 * 1000)))
|
|
213
|
+
: 0;
|
|
214
|
+
const minutesInactive = billing.lastActivityAt
|
|
215
|
+
? Math.floor((now.getTime() - new Date(billing.lastActivityAt).getTime()) / (60 * 1000))
|
|
216
|
+
: 0;
|
|
217
|
+
console.log(chalk_1.default.blue('[INFO] === Billing ==='));
|
|
218
|
+
console.log(` Size: ${target.size} (${billing.creditsPerHour} credit${billing.creditsPerHour > 1 ? 's' : ''}/hr)`);
|
|
219
|
+
console.log(` Current hour ends in: ${chalk_1.default.cyan(minutesUntilBilling + ' min')}`);
|
|
220
|
+
console.log(` Last activity: ${minutesInactive < 1 ? 'just now' : minutesInactive + ' min ago'}`);
|
|
221
|
+
console.log(` Total: ${billing.totalHoursUsed} hour${billing.totalHoursUsed !== 1 ? 's' : ''}, ${billing.totalCreditsUsed} credit${billing.totalCreditsUsed !== 1 ? 's' : ''}`);
|
|
222
|
+
if (billing.autoDestroyOnInactivity) {
|
|
223
|
+
const minutesUntilDestroy = Math.max(0, 60 - minutesInactive);
|
|
224
|
+
if (minutesInactive >= 45) {
|
|
225
|
+
console.log(chalk_1.default.yellow(` Auto-destroy in: ${minutesUntilDestroy} min (inactive)`));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
console.log('');
|
|
229
|
+
// Show Docker containers status
|
|
230
|
+
const dockerStatus = sshExec(target.ipAddress, keyPath, 'docker ps --format "{{.Names}}\\t{{.Status}}" 2>/dev/null', 10);
|
|
231
|
+
if (dockerStatus && dockerStatus.trim()) {
|
|
232
|
+
console.log(chalk_1.default.blue('[INFO] === Docker Services ==='));
|
|
233
|
+
console.log('NAMES\tSTATUS');
|
|
234
|
+
console.log(dockerStatus);
|
|
235
|
+
console.log('');
|
|
236
|
+
}
|
|
237
|
+
// Show PM2 processes
|
|
238
|
+
const pm2Status = sshExec(target.ipAddress, keyPath, 'source ~/.nvm/nvm.sh 2>/dev/null; pm2 list 2>/dev/null || echo ""', 10);
|
|
239
|
+
if (pm2Status && pm2Status.trim() && !pm2Status.includes('No process')) {
|
|
240
|
+
console.log(chalk_1.default.blue('[INFO] === PM2 Services ==='));
|
|
241
|
+
console.log(pm2Status);
|
|
242
|
+
console.log('');
|
|
243
|
+
}
|
|
244
|
+
// Show URLs if available
|
|
245
|
+
if (target.urls && Object.keys(target.urls).length > 0) {
|
|
246
|
+
console.log(chalk_1.default.blue('[INFO] === Service URLs ==='));
|
|
247
|
+
for (const [service, url] of Object.entries(target.urls)) {
|
|
248
|
+
console.log(` ${service}: ${chalk_1.default.cyan(url)}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
211
251
|
return;
|
|
212
252
|
}
|
|
213
|
-
if
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
253
|
+
// If DB says PROVISIONING, check if we can SSH to get progress
|
|
254
|
+
if (dbStatus === 'provisioning') {
|
|
255
|
+
// Try to SSH and check cloud-init progress
|
|
256
|
+
let status = sshExec(target.ipAddress, keyPath, 'cloud-init status 2>&1');
|
|
257
|
+
if (!status) {
|
|
258
|
+
console.log(chalk_1.default.yellow('[WARN] Unable to connect to server. It may still be booting.'));
|
|
259
|
+
return;
|
|
218
260
|
}
|
|
219
|
-
|
|
220
|
-
|
|
261
|
+
// Check for SSH connection errors (timeout, refused, etc.)
|
|
262
|
+
const sshErrors = [
|
|
263
|
+
'Operation timed out',
|
|
264
|
+
'Connection refused',
|
|
265
|
+
'Connection timed out',
|
|
266
|
+
'No route to host',
|
|
267
|
+
'Network is unreachable',
|
|
268
|
+
'ssh: connect to host',
|
|
269
|
+
];
|
|
270
|
+
if (sshErrors.some(err => status.includes(err))) {
|
|
271
|
+
console.log(chalk_1.default.yellow('[INFO] Server is still initializing - SSH is not yet available.'));
|
|
272
|
+
console.log(chalk_1.default.dim(' This is normal for newly created servers.'));
|
|
273
|
+
console.log(chalk_1.default.dim(' Please wait 1-2 minutes and try again.'));
|
|
274
|
+
return;
|
|
221
275
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
console.log(` SSH Ready: ${formatDuration(timing.sshReady)}`);
|
|
276
|
+
// Cloud-init still running
|
|
277
|
+
if (status.includes('running')) {
|
|
278
|
+
// Get timing breakdown
|
|
279
|
+
const timing = getTimingBreakdown(target.ipAddress, keyPath);
|
|
227
280
|
if (timing.total !== null) {
|
|
228
|
-
console.log(`
|
|
281
|
+
console.log(chalk_1.default.yellow(`[INFO] Setup in progress... (elapsed: ${formatDuration(timing.total)})`));
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
console.log(chalk_1.default.yellow('[INFO] Setup in progress...'));
|
|
229
285
|
}
|
|
230
286
|
console.log('');
|
|
287
|
+
// Show timing breakdown so far
|
|
288
|
+
if (timing.sshReady !== null) {
|
|
289
|
+
console.log(chalk_1.default.blue('[INFO] === Timing So Far ==='));
|
|
290
|
+
console.log(` SSH Ready: ${formatDuration(timing.sshReady)}`);
|
|
291
|
+
if (timing.total !== null) {
|
|
292
|
+
console.log(` Elapsed: ${formatDuration(timing.total)}`);
|
|
293
|
+
}
|
|
294
|
+
console.log('');
|
|
295
|
+
}
|
|
296
|
+
console.log(chalk_1.default.blue('[INFO] Latest setup progress:'));
|
|
297
|
+
// Tail cloud-init output log for progress
|
|
298
|
+
const logOutput = sshExec(target.ipAddress, keyPath, "sudo tail -30 /var/log/cloud-init-output.log 2>/dev/null | grep -E '^===|^#|Progress:|DONE|error|Error|failed|Failed' || sudo tail -20 /var/log/cloud-init-output.log", 15);
|
|
299
|
+
if (logOutput) {
|
|
300
|
+
console.log(logOutput);
|
|
301
|
+
}
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
// Cloud-init done but callback might not have been received yet
|
|
305
|
+
if (status.includes('done')) {
|
|
306
|
+
console.log(chalk_1.default.green('[SUCCESS] Setup completed!'));
|
|
307
|
+
console.log(chalk_1.default.dim(' (Waiting for status update...)'));
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
// Cloud-init not found (already cleaned up) - show generic status
|
|
311
|
+
if (status.includes('not found') || status.includes('command not found')) {
|
|
312
|
+
console.log(chalk_1.default.yellow('[INFO] Setup is finalizing...'));
|
|
313
|
+
return;
|
|
231
314
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
315
|
+
// Other status
|
|
316
|
+
console.log(chalk_1.default.yellow(`[INFO] Status: ${status}`));
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
// If DB says ERROR
|
|
320
|
+
if (dbStatus === 'error') {
|
|
321
|
+
console.log(chalk_1.default.red('[ERROR] Setup failed!'));
|
|
322
|
+
console.log(chalk_1.default.dim(' Run `genbox connect` to investigate.'));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
// Fallback: try legacy cloud-init check for backwards compatibility
|
|
326
|
+
let status = sshExec(target.ipAddress, keyPath, 'cloud-init status 2>&1');
|
|
327
|
+
if (!status || status.includes('not found')) {
|
|
328
|
+
// No cloud-init, check our status file
|
|
329
|
+
const genboxStatus = sshExec(target.ipAddress, keyPath, 'cat ~/.genbox-status 2>/dev/null');
|
|
330
|
+
if (genboxStatus && genboxStatus.includes('Setup Complete')) {
|
|
331
|
+
console.log(chalk_1.default.green('[SUCCESS] Setup completed!'));
|
|
332
|
+
return;
|
|
237
333
|
}
|
|
238
334
|
}
|
|
239
|
-
|
|
335
|
+
if (status && status.includes('done')) {
|
|
240
336
|
// Get timing breakdown
|
|
241
337
|
const timing = getTimingBreakdown(target.ipAddress, keyPath);
|
|
242
338
|
console.log(chalk_1.default.green(`[SUCCESS] Cloud-init completed!${timing.total !== null ? ` (${formatDuration(timing.total)})` : ''}`));
|