badgr-cli 1.0.18 → 1.0.19
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/package.json +1 -1
- package/src/commands/run.js +56 -5
package/package.json
CHANGED
package/src/commands/run.js
CHANGED
|
@@ -81,6 +81,42 @@ function renderStatusBar(chalk, { elapsedMs, ratePerHour, gpuUtil, cpuUtil, maxR
|
|
|
81
81
|
return chalk.dim(' ' + parts.join(' • '));
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
// Wait for status to leave 'starting' (smoke check running on backend).
|
|
85
|
+
// Returns the final dep object with status 'running' or 'failed'.
|
|
86
|
+
async function waitForRunning(config, depId, chalk) {
|
|
87
|
+
const POLL_MS = 3000;
|
|
88
|
+
const TIMEOUT_MS = 120_000; // 2 min max for smoke check
|
|
89
|
+
const startMs = Date.now();
|
|
90
|
+
let dots = 0;
|
|
91
|
+
|
|
92
|
+
process.stdout.write(chalk.dim(' Waiting for container to start'));
|
|
93
|
+
const ticker = setInterval(() => {
|
|
94
|
+
process.stdout.write('.');
|
|
95
|
+
dots++;
|
|
96
|
+
}, 1000);
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
while (Date.now() - startMs < TIMEOUT_MS) {
|
|
100
|
+
await new Promise(r => setTimeout(r, POLL_MS));
|
|
101
|
+
const dep = await callApi(`/deployments/${depId}`, {
|
|
102
|
+
apiKey: config.apiKey,
|
|
103
|
+
baseUrl: config.baseUrl,
|
|
104
|
+
});
|
|
105
|
+
if (dep.status !== 'starting') {
|
|
106
|
+
clearInterval(ticker);
|
|
107
|
+
process.stdout.write('\n');
|
|
108
|
+
return dep;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} finally {
|
|
112
|
+
clearInterval(ticker);
|
|
113
|
+
if (dots > 0) process.stdout.write('\n');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Timed out waiting — return whatever we have
|
|
117
|
+
return await callApi(`/deployments/${depId}`, { apiKey: config.apiKey, baseUrl: config.baseUrl });
|
|
118
|
+
}
|
|
119
|
+
|
|
84
120
|
async function attachToJob(config, depId, { chalk, maxRuntimeMs = null, maxCost = null, ratePerHour = 0, onTeardown }) {
|
|
85
121
|
const TERMINAL = new Set(['stopped', 'failed', 'completed']);
|
|
86
122
|
const POLL_MS = 4000;
|
|
@@ -129,14 +165,14 @@ async function attachToJob(config, depId, { chalk, maxRuntimeMs = null, maxCost
|
|
|
129
165
|
if (maxCost !== null && spentSoFar >= maxCost) {
|
|
130
166
|
tearing = true;
|
|
131
167
|
stopTicker();
|
|
132
|
-
onTeardown('max-cost');
|
|
168
|
+
await onTeardown('max-cost');
|
|
133
169
|
return { status: 'timeout', exitCode: null, runtimeMs: elapsedMs, failureType: null };
|
|
134
170
|
}
|
|
135
171
|
|
|
136
172
|
if (maxRuntimeMs !== null && elapsedMs >= maxRuntimeMs) {
|
|
137
173
|
tearing = true;
|
|
138
174
|
stopTicker();
|
|
139
|
-
onTeardown('max-runtime');
|
|
175
|
+
await onTeardown('max-runtime');
|
|
140
176
|
return { status: 'timeout', exitCode: null, runtimeMs: elapsedMs, failureType: null };
|
|
141
177
|
}
|
|
142
178
|
|
|
@@ -158,7 +194,7 @@ async function attachToJob(config, depId, { chalk, maxRuntimeMs = null, maxCost
|
|
|
158
194
|
} else if (consecutiveErrs >= HEARTBEAT_KILL_POLLS) {
|
|
159
195
|
tearing = true;
|
|
160
196
|
stopTicker();
|
|
161
|
-
onTeardown('heartbeat-lost');
|
|
197
|
+
await onTeardown('heartbeat-lost');
|
|
162
198
|
return { status: 'failed', exitCode: null, runtimeMs: elapsedMs, failureType: 'infrastructure' };
|
|
163
199
|
}
|
|
164
200
|
}
|
|
@@ -202,8 +238,9 @@ async function attachToJob(config, depId, { chalk, maxRuntimeMs = null, maxCost
|
|
|
202
238
|
// Skip provider util lines — they're shown in the status bar instead.
|
|
203
239
|
if (/\b(gpu_util|cpu_util|provider_status|uptime)=/.test(line)) continue;
|
|
204
240
|
|
|
241
|
+
const isErrorLine = /^error\b/i.test(line) || /Error response from daemon/i.test(line);
|
|
205
242
|
stopTicker();
|
|
206
|
-
console.log(` ${chalk.dim(line)}`);
|
|
243
|
+
console.log(` ${isErrorLine ? chalk.red(line) : chalk.dim(line)}`);
|
|
207
244
|
startTicker();
|
|
208
245
|
}
|
|
209
246
|
} catch {
|
|
@@ -460,6 +497,20 @@ export async function runCommand(config, args, chalk) {
|
|
|
460
497
|
return;
|
|
461
498
|
}
|
|
462
499
|
|
|
500
|
+
// If the backend is still running the smoke check, wait for it to finish.
|
|
501
|
+
if (dep.status === 'starting') {
|
|
502
|
+
console.log();
|
|
503
|
+
dep = await waitForRunning(config, dep.deployment_id, chalk);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (dep.status === 'failed') {
|
|
507
|
+
console.error(chalk.red('\n ✗ Container failed to start (infrastructure error).\n'));
|
|
508
|
+
console.error(chalk.dim(' The backend retried automatically. All attempts failed.'));
|
|
509
|
+
console.error(chalk.dim(` Contact support with receipt ID: ${rcptId}`));
|
|
510
|
+
console.log();
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
513
|
+
|
|
463
514
|
const ratePerHour = dep.cost_per_hour || 0;
|
|
464
515
|
console.log(chalk.dim('\n ── Live status (Ctrl+C to stop) ─────────────────────────────────\n'));
|
|
465
516
|
|
|
@@ -523,7 +574,7 @@ export async function runCommand(config, args, chalk) {
|
|
|
523
574
|
}
|
|
524
575
|
console.log();
|
|
525
576
|
process.exit(exitCode ?? 1);
|
|
526
|
-
} else {
|
|
577
|
+
} else if (finalStatus === 'completed' && (exitCode === 0 || exitCode === null)) {
|
|
527
578
|
console.log(chalk.green(`\n ✓ Complete\n`));
|
|
528
579
|
}
|
|
529
580
|
}
|