badgr-cli 1.1.4 → 1.1.6
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/README.md +12 -14
- package/package.json +2 -2
- package/src/api.js +28 -0
- package/src/badgr.js +1 -1
- package/src/commands/artifacts.js +5 -3
- package/src/commands/batch.js +6 -5
- package/src/commands/billing.js +3 -3
- package/src/commands/capacity.js +2 -2
- package/src/commands/deploy.js +2 -2
- package/src/commands/diagnose.js +276 -87
- package/src/commands/down.js +69 -14
- package/src/commands/embed.js +2 -2
- package/src/commands/heartbeat.js +3 -3
- package/src/commands/job.js +14 -3
- package/src/commands/login.js +4 -1
- package/src/commands/logs.js +35 -18
- package/src/commands/pull.js +4 -3
- package/src/commands/rerun.js +5 -3
- package/src/commands/restart.js +5 -3
- package/src/commands/run.js +5 -23
- package/src/commands/sbatch.js +2 -1
- package/src/commands/serve.js +145 -24
- package/src/commands/shell.js +2 -2
- package/src/commands/status.js +57 -13
- package/src/commands/test-run.js +2 -2
- package/src/commands/train.js +3 -3
- package/src/commands/transcribe.js +2 -2
- package/src/commands/up.js +2 -1
- package/src/config.js +19 -0
- package/src/deploymentLog.js +45 -0
- package/src/errors.js +7 -3
- package/src/fallback.js +51 -31
- package/src/onboarding.js +63 -4
- package/src/progress.js +5 -2
package/README.md
CHANGED
|
@@ -167,17 +167,15 @@ secrets, and runs free static checks as part of a Badgr Smoke Test.
|
|
|
167
167
|
Nothing runs on a GPU without explicit `--approve`.
|
|
168
168
|
|
|
169
169
|
```bash
|
|
170
|
-
#
|
|
170
|
+
# badgr diagnose IS the Badgr Smoke Test — free, no login, no GPU.
|
|
171
|
+
# Applicable free structural checks run automatically, every time.
|
|
171
172
|
badgr diagnose "https://github.com/org/repo/issues/123"
|
|
172
173
|
badgr diagnose ajayrajtp/vllm_gemma412b:latest
|
|
173
174
|
badgr diagnose ./vllm-error.log
|
|
174
175
|
badgr diagnose "https://github.com/org/repo"
|
|
175
176
|
badgr diagnose workflow.json
|
|
176
177
|
|
|
177
|
-
#
|
|
178
|
-
badgr diagnose "https://github.com/org/repo/issues/123" --smoke
|
|
179
|
-
|
|
180
|
-
# Approve a capped smoke test after diagnosis (login required)
|
|
178
|
+
# Start a real Badgr Run (paid GPU execution) after diagnosis (login required)
|
|
181
179
|
badgr diagnose "https://github.com/org/repo/issues/123" --approve
|
|
182
180
|
|
|
183
181
|
# Resume an existing case, e.g. one shared via a case link
|
|
@@ -190,8 +188,8 @@ badgr diagnose "https://github.com/org/repo/issues/123" --json
|
|
|
190
188
|
|
|
191
189
|
| Flag | Description |
|
|
192
190
|
|------|-------------|
|
|
193
|
-
| `--
|
|
194
|
-
| `--
|
|
191
|
+
| `--approve` | Start a real Badgr Run after diagnosis — actual GPU execution, capped at a hard maximum spend (opens browser sign-in automatically if not logged in) |
|
|
192
|
+
| `--smoke` | Deprecated, no-op. The free mechanical checks this used to gate (syntax, CLI-entrypoint corroboration, referenced-file presence, Docker `ENTRYPOINT`/`CMD` consistency, required env vars, plus a client-side local-file check) now always run as part of plain `badgr diagnose`. Kept only so existing scripts that pass it don't break. Not the same as `badgr run --smoke`, which launches a real, billable GPU job |
|
|
195
193
|
| `--docker <image>` | Force Docker-image intake (override auto-detect) |
|
|
196
194
|
| `--repo <url>` | Force repository intake (override auto-detect) |
|
|
197
195
|
| `--comfyui <path>` | Force ComfyUI workflow intake (override auto-detect) |
|
|
@@ -199,13 +197,13 @@ badgr diagnose "https://github.com/org/repo/issues/123" --json
|
|
|
199
197
|
| `--json` | Machine-readable JSON output |
|
|
200
198
|
|
|
201
199
|
Every run prints one status: `NEEDS INFO` (fields still missing) →
|
|
202
|
-
`READY` (complete evidence-backed command, no
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
actual successful
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
200
|
+
`READY` (complete evidence-backed command, no applicable checks to run) →
|
|
201
|
+
`SMOKE CHECKED` (applicable checks ran, all passed or were skipped) /
|
|
202
|
+
`INVALID` (an applicable check actually failed) → `VERIFIED` (reserved for
|
|
203
|
+
an actual successful Badgr Run — never assigned from diagnosis alone).
|
|
204
|
+
`READY`/`SMOKE CHECKED` results also print the canonical `badgr
|
|
205
|
+
run`/`badgr serve` command `--approve` would run, plus a shareable case
|
|
206
|
+
link — pass that case ID or URL back into `badgr diagnose
|
|
209
207
|
<case_id_or_url> --approve` to resume the same case without re-diagnosing.
|
|
210
208
|
|
|
211
209
|
`badgr run-issue` is an alias for `badgr diagnose`, matching the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "badgr-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "Badgr — run or serve GPU workloads from one command",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@inquirer/prompts": "^8.5.2",
|
|
16
16
|
"archiver": "^7.0.1",
|
|
17
|
-
"badgr-shared": "0.1.
|
|
17
|
+
"badgr-shared": "^0.1.2",
|
|
18
18
|
"chalk": "^5.3.0",
|
|
19
19
|
"js-yaml": "^4.1.0",
|
|
20
20
|
"tar": "^7.4.3"
|
package/src/api.js
CHANGED
|
@@ -228,6 +228,34 @@ export async function terminateDeployment(config, deploymentId) {
|
|
|
228
228
|
throw lastErr;
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
+
// ---- Admin (michaelhireitem@gmail.com only) ---------------------------------
|
|
232
|
+
// CLI counterparts to the web admin dashboard's own /admin/api/deployments/*
|
|
233
|
+
// routes (main.py) -- those are gated by a browser session cookie the CLI's
|
|
234
|
+
// Authorization: Bearer <api_key> auth can never satisfy, so
|
|
235
|
+
// deployment_routes.py exposes these two under /v1/admin/deployments/*
|
|
236
|
+
// instead, gated by the same is_admin_email() check via API key. A non-admin
|
|
237
|
+
// caller gets a plain 403 from callApi, same as any other access-denied route.
|
|
238
|
+
|
|
239
|
+
// Every non-terminal deployment across every user (not just the caller's
|
|
240
|
+
// own), each carrying its owner's user_email/username so a stuck or
|
|
241
|
+
// forgotten GPU can be identified without logging into that customer's
|
|
242
|
+
// account. Backs `badgr status --admin`.
|
|
243
|
+
export function listAllActiveDeploymentsAdmin(config) {
|
|
244
|
+
return callApi('/admin/deployments/active', { apiKey: config.apiKey, baseUrl: config.baseUrl, timeoutMs: 15_000 });
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Force-terminate any deployment regardless of owner -- bypasses the
|
|
248
|
+
// per-user ownership check DELETE /deployments/{id} enforces. Backs
|
|
249
|
+
// `badgr down <id> --admin`.
|
|
250
|
+
export function adminTerminateDeployment(config, deploymentId) {
|
|
251
|
+
return callApi(`/admin/deployments/${deploymentId}`, {
|
|
252
|
+
method: 'DELETE',
|
|
253
|
+
apiKey: config.apiKey,
|
|
254
|
+
baseUrl: config.baseUrl,
|
|
255
|
+
timeoutMs: 20_000,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
231
259
|
export function restartDeployment(config, deploymentId) {
|
|
232
260
|
return callApi(`/deployments/${deploymentId}/restart`, {
|
|
233
261
|
method: 'POST',
|
package/src/badgr.js
CHANGED
|
@@ -119,7 +119,7 @@ ${chalk.bold('EXAMPLES')}
|
|
|
119
119
|
badgr diagnose --docker ajayrajtp/vllm_gemma412b:latest
|
|
120
120
|
badgr diagnose --repo https://github.com/org/repo
|
|
121
121
|
badgr diagnose --comfyui workflow.json
|
|
122
|
-
badgr diagnose "https://github.com/org/repo/issues/123" --approve ${chalk.dim('#
|
|
122
|
+
badgr diagnose "https://github.com/org/repo/issues/123" --approve ${chalk.dim('# start a real Badgr Run')}
|
|
123
123
|
|
|
124
124
|
${chalk.dim('# Local model-fit and workload diagnosis (no network):')}
|
|
125
125
|
badgr doctor
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import {
|
|
3
|
+
import { CONFIG_DIR } from '../config.js';
|
|
4
|
+
import { ensureLoggedInReady, withReauthRetry } from '../onboarding.js';
|
|
4
5
|
import { downloadAndExtractArtifact } from '../artifactDownload.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -37,7 +38,7 @@ export function listFilesRecursive(dir, prefix = '') {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export async function artifactsCommand(config, args, chalk) {
|
|
40
|
-
|
|
41
|
+
config = await ensureLoggedInReady(config, chalk);
|
|
41
42
|
const { deploymentId, flags } = parseArtifactsArgs(args);
|
|
42
43
|
if (!deploymentId) {
|
|
43
44
|
console.error(chalk.red('\n Usage: badgr artifacts <deployment-id> [--output <dir>]\n'));
|
|
@@ -49,7 +50,8 @@ export async function artifactsCommand(config, args, chalk) {
|
|
|
49
50
|
const destDir = flags.output ? path.resolve(flags.output) : defaultDestDir(deploymentId);
|
|
50
51
|
|
|
51
52
|
try {
|
|
52
|
-
await
|
|
53
|
+
const refreshed = await withReauthRetry(config, chalk, cfg => downloadAndExtractArtifact(cfg, deploymentId, destDir));
|
|
54
|
+
config = refreshed.config;
|
|
53
55
|
} catch (err) {
|
|
54
56
|
if (err.httpStatus === 404) {
|
|
55
57
|
console.error(chalk.red(`\n ✗ No artifact found for ${deploymentId}.\n`));
|
package/src/commands/batch.js
CHANGED
|
@@ -17,7 +17,8 @@ import { existsSync, readFileSync, unlinkSync, statSync, readdirSync } from 'fs'
|
|
|
17
17
|
import { join, resolve } from 'path';
|
|
18
18
|
import os from 'os';
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { CONFIG_DIR } from '../config.js';
|
|
21
|
+
import { ensureBadgrReady, ensureLoggedInReady } from '../onboarding.js';
|
|
21
22
|
import { parseWorkloadYaml } from '../workloadSpec.js';
|
|
22
23
|
import { addReceipt, updateReceipt, generateReceiptId, loadStore, selectedComputeFromDeployment } from '../store.js';
|
|
23
24
|
import { normalizeTier, callWithFallback } from '../fallback.js';
|
|
@@ -392,7 +393,7 @@ async function runSubcommand(config, args, chalk) {
|
|
|
392
393
|
return;
|
|
393
394
|
}
|
|
394
395
|
|
|
395
|
-
|
|
396
|
+
config = await ensureBadgrReady(config, chalk);
|
|
396
397
|
|
|
397
398
|
let spec;
|
|
398
399
|
try {
|
|
@@ -593,7 +594,7 @@ async function statusSubcommand(config, args, chalk) {
|
|
|
593
594
|
process.exitCode = 1;
|
|
594
595
|
return;
|
|
595
596
|
}
|
|
596
|
-
|
|
597
|
+
config = await ensureLoggedInReady(config, chalk);
|
|
597
598
|
|
|
598
599
|
let dep;
|
|
599
600
|
try {
|
|
@@ -628,7 +629,7 @@ async function artifactsSubcommand(config, args, chalk) {
|
|
|
628
629
|
process.exitCode = 1;
|
|
629
630
|
return;
|
|
630
631
|
}
|
|
631
|
-
|
|
632
|
+
config = await ensureLoggedInReady(config, chalk);
|
|
632
633
|
|
|
633
634
|
let destDir;
|
|
634
635
|
try {
|
|
@@ -676,7 +677,7 @@ async function receiptSubcommand(config, args, chalk) {
|
|
|
676
677
|
process.exitCode = 1;
|
|
677
678
|
return;
|
|
678
679
|
}
|
|
679
|
-
|
|
680
|
+
config = await ensureLoggedInReady(config, chalk);
|
|
680
681
|
|
|
681
682
|
const local = findBatchRun(runId);
|
|
682
683
|
let dep = null;
|
package/src/commands/billing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ensureLoggedInReady } from '../onboarding.js';
|
|
2
2
|
import { callApi } from '../api.js';
|
|
3
3
|
import { openBrowser } from '../browser.js';
|
|
4
4
|
|
|
@@ -17,7 +17,7 @@ EXAMPLES
|
|
|
17
17
|
`;
|
|
18
18
|
|
|
19
19
|
async function billingStatus(config, chalk) {
|
|
20
|
-
|
|
20
|
+
config = await ensureLoggedInReady(config, chalk);
|
|
21
21
|
try {
|
|
22
22
|
const apiUrl = config.baseUrl.replace('/v1', '').replace('/api/v1', '');
|
|
23
23
|
const data = await callApi('/api/me', {
|
|
@@ -47,7 +47,7 @@ async function billingStatus(config, chalk) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
async function billingAdd(config, amount, chalk) {
|
|
50
|
-
|
|
50
|
+
config = await ensureLoggedInReady(config, chalk);
|
|
51
51
|
const amountInt = parseInt(amount, 10);
|
|
52
52
|
if (!amountInt || amountInt < 5) {
|
|
53
53
|
console.error(chalk.red(' Minimum top-up is $5. Example: badgr billing add 5'));
|
package/src/commands/capacity.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ensureLoggedInReady } from '../onboarding.js';
|
|
2
2
|
import { callApi } from '../api.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -19,7 +19,7 @@ function parseCapacityArgs(args) {
|
|
|
19
19
|
|
|
20
20
|
export async function capacityCommand(config, args, chalk) {
|
|
21
21
|
const flags = parseCapacityArgs(args);
|
|
22
|
-
|
|
22
|
+
config = await ensureLoggedInReady(config, chalk);
|
|
23
23
|
|
|
24
24
|
// No --max-price: no cap. Availability browsing shouldn't silently hide
|
|
25
25
|
// real capacity behind an unrequested price ceiling.
|
package/src/commands/deploy.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
2
|
import { findCheapest, findById } from '../router.js';
|
|
3
|
-
import {
|
|
3
|
+
import { ensureBadgrReady } from '../onboarding.js';
|
|
4
4
|
|
|
5
5
|
export function parseDeployArgs(args) {
|
|
6
6
|
const nameFlag = args.indexOf('--name');
|
|
@@ -24,7 +24,7 @@ export async function deployCommand(config, args, chalk) {
|
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
config = await ensureBadgrReady(config, chalk);
|
|
28
28
|
|
|
29
29
|
const deployId = randomUUID().split('-')[0];
|
|
30
30
|
const name = rawName ?? script.replace(/[^a-z0-9]/gi, '-').replace(/-+/g, '-').toLowerCase();
|