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.
@@ -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 cloud-init progress on ${selectedName}...`));
190
+ console.log(chalk_1.default.blue(`[INFO] Checking status of ${selectedName}...`));
191
191
  console.log('');
192
- // 3. Check cloud-init status
193
- const status = sshExec(target.ipAddress, keyPath, 'cloud-init status 2>&1');
194
- if (!status) {
195
- console.log(chalk_1.default.yellow('[WARN] Unable to connect to server. It may still be booting.'));
196
- return;
197
- }
198
- // Check for SSH connection errors (timeout, refused, etc.)
199
- const sshErrors = [
200
- 'Operation timed out',
201
- 'Connection refused',
202
- 'Connection timed out',
203
- 'No route to host',
204
- 'Network is unreachable',
205
- 'ssh: connect to host',
206
- ];
207
- if (sshErrors.some(err => status.includes(err))) {
208
- console.log(chalk_1.default.yellow('[INFO] Server is still initializing - SSH is not yet available.'));
209
- console.log(chalk_1.default.dim(' This is normal for newly created servers.'));
210
- console.log(chalk_1.default.dim(' Please wait 1-2 minutes and try again.'));
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 (status.includes('running')) {
214
- // Get timing breakdown
215
- const timing = getTimingBreakdown(target.ipAddress, keyPath);
216
- if (timing.total !== null) {
217
- console.log(chalk_1.default.yellow(`[WARN] Cloud-init is still running... (elapsed: ${formatDuration(timing.total)})`));
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
- else {
220
- console.log(chalk_1.default.yellow('[WARN] Cloud-init is still running...'));
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
- console.log('');
223
- // Show timing breakdown so far
224
- if (timing.sshReady !== null) {
225
- console.log(chalk_1.default.blue('[INFO] === Timing So Far ==='));
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(` Elapsed: ${formatDuration(timing.total)}`);
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
- console.log(chalk_1.default.blue('[INFO] Latest setup progress:'));
233
- // Tail cloud-init output log for progress
234
- 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);
235
- if (logOutput) {
236
- console.log(logOutput);
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
- else if (status.includes('done')) {
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)})` : ''}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genbox",
3
- "version": "1.0.34",
3
+ "version": "1.0.36",
4
4
  "description": "Genbox CLI - AI-Powered Development Environments",
5
5
  "main": "dist/index.js",
6
6
  "bin": {