badgr-cli 1.1.0 → 1.1.2
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/LICENSE +207 -0
- package/README.md +135 -3
- package/package.json +44 -2
- package/src/api.js +16 -0
- package/src/badgr.js +2 -2
- package/src/commands/batch.js +11 -0
- package/src/commands/comfyui.js +31 -15
- package/src/commands/embed.js +13 -10
- package/src/commands/launch.js +8 -1
- package/src/commands/login.js +75 -20
- package/src/commands/run.js +45 -13
- package/src/commands/sbatch.js +6 -1
- package/src/commands/serve.js +44 -30
- package/src/commands/train.js +8 -12
- package/src/commands/transcribe.js +13 -10
- package/src/envFlag.js +10 -0
- package/src/onboarding.js +8 -1
- package/src/progress.js +48 -0
- package/tests/agent-images.test.js +0 -17
- package/tests/api.test.js +0 -168
- package/tests/artifactDownload.test.js +0 -113
- package/tests/artifacts.test.js +0 -168
- package/tests/batch.test.js +0 -641
- package/tests/browser.test.js +0 -51
- package/tests/capacity.test.js +0 -68
- package/tests/commands.test.js +0 -417
- package/tests/config.test.js +0 -96
- package/tests/connect.test.js +0 -83
- package/tests/detect.test.js +0 -191
- package/tests/down.test.js +0 -150
- package/tests/errors.test.js +0 -130
- package/tests/fallback-timeout.test.js +0 -41
- package/tests/fanout.test.js +0 -124
- package/tests/gpu-doctor-classifiers.test.js +0 -402
- package/tests/gpu-doctor-doctor.test.js +0 -304
- package/tests/gpu-doctor-probe-cache.test.js +0 -110
- package/tests/gpu-doctor-probes.test.js +0 -257
- package/tests/heartbeat.test.js +0 -70
- package/tests/job-progress-poll.test.js +0 -136
- package/tests/launch-command-argv.test.js +0 -93
- package/tests/launch-readiness.test.js +0 -403
- package/tests/launch.test.js +0 -440
- package/tests/onboarding.test.js +0 -134
- package/tests/productized-dry-run.test.js +0 -141
- package/tests/productized-runners.test.js +0 -237
- package/tests/pull.test.js +0 -266
- package/tests/rerun.test.js +0 -94
- package/tests/restart.test.js +0 -88
- package/tests/router.test.js +0 -98
- package/tests/run-lifecycle.test.js +0 -1054
- package/tests/sbatch.test.js +0 -190
- package/tests/secrets.test.js +0 -16
- package/tests/serve-apps.test.js +0 -189
- package/tests/serve-lifecycle.test.js +0 -931
- package/tests/slurm.test.js +0 -77
- package/tests/spec.test.js +0 -201
- package/tests/status.test.js +0 -73
- package/tests/store.test.js +0 -187
- package/tests/task.test.js +0 -109
- package/tests/template.test.js +0 -556
- package/tests/train-lora-dataset.test.js +0 -176
- package/tests/upload.test.js +0 -79
- package/tests/workload-rerun.test.js +0 -56
- package/tests/workload-spec.test.js +0 -180
- package/tests/workload-templates.test.js +0 -865
- package/tests/workload-workspace-paths.test.js +0 -46
package/tests/rerun.test.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* badgr rerun — replays a past job or endpoint with its exact original spec
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
5
|
-
import { rerunCommand } from '../src/commands/rerun.js';
|
|
6
|
-
|
|
7
|
-
vi.mock('../src/api.js', () => ({
|
|
8
|
-
rerunDeployment: vi.fn(),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
vi.mock('../src/store.js', () => ({
|
|
12
|
-
findDeployment: vi.fn(() => null),
|
|
13
|
-
addDeployment: vi.fn(),
|
|
14
|
-
addReceipt: vi.fn(),
|
|
15
|
-
generateReceiptId: vi.fn(() => 'rcpt-rerun-001'),
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
vi.mock('../src/config.js', () => ({
|
|
19
|
-
requireApiKey: vi.fn(),
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
import * as api from '../src/api.js';
|
|
23
|
-
import * as store from '../src/store.js';
|
|
24
|
-
|
|
25
|
-
const config = { apiKey: 'sk-test', baseUrl: 'https://api.test/v1' };
|
|
26
|
-
|
|
27
|
-
const chalk = {
|
|
28
|
-
bold: s => s,
|
|
29
|
-
dim: s => s,
|
|
30
|
-
red: s => s,
|
|
31
|
-
yellow: s => s,
|
|
32
|
-
green: s => s,
|
|
33
|
-
cyan: s => s,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
beforeEach(() => {
|
|
37
|
-
process.exitCode = undefined;
|
|
38
|
-
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
39
|
-
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
40
|
-
vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
41
|
-
vi.resetAllMocks();
|
|
42
|
-
store.findDeployment.mockReturnValue(null);
|
|
43
|
-
store.generateReceiptId.mockReturnValue('rcpt-rerun-001');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
afterEach(() => {
|
|
47
|
-
vi.restoreAllMocks();
|
|
48
|
-
process.exitCode = undefined;
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('requires a deployment id', async () => {
|
|
52
|
-
await rerunCommand(config, [], chalk);
|
|
53
|
-
expect(api.rerunDeployment).not.toHaveBeenCalled();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('calls rerunDeployment and prints the new deployment id, never removing the source', async () => {
|
|
57
|
-
api.rerunDeployment.mockResolvedValue({
|
|
58
|
-
deployment_id: 'dep-new-002',
|
|
59
|
-
workload_type: 'job',
|
|
60
|
-
gpu_type: 'A100',
|
|
61
|
-
gpu_count: 1,
|
|
62
|
-
status: 'provisioning',
|
|
63
|
-
rerun_of: 'dep-old-001',
|
|
64
|
-
cost_per_hour: 1.9,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const lines = [];
|
|
68
|
-
console.log.mockImplementation((...args) => lines.push(args.join(' ')));
|
|
69
|
-
|
|
70
|
-
await rerunCommand(config, ['dep-old-001'], chalk);
|
|
71
|
-
|
|
72
|
-
expect(api.rerunDeployment).toHaveBeenCalledWith(config, 'dep-old-001');
|
|
73
|
-
expect(store.addDeployment).toHaveBeenCalledWith(expect.objectContaining({ id: 'dep-new-002' }));
|
|
74
|
-
expect(store.addReceipt).toHaveBeenCalledWith(expect.objectContaining({ rerunOf: 'dep-old-001' }));
|
|
75
|
-
expect(lines.some(l => l.includes('dep-new-002'))).toBe(true);
|
|
76
|
-
expect(process.exitCode).toBeFalsy();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('resolves a local name/alias to its deployment id before calling the API', async () => {
|
|
80
|
-
store.findDeployment.mockReturnValue({ id: 'dep-resolved-999' });
|
|
81
|
-
api.rerunDeployment.mockResolvedValue({ deployment_id: 'dep-new-003', workload_type: 'job', status: 'provisioning' });
|
|
82
|
-
|
|
83
|
-
await rerunCommand(config, ['my-job-alias'], chalk);
|
|
84
|
-
|
|
85
|
-
expect(api.rerunDeployment).toHaveBeenCalledWith(config, 'dep-resolved-999');
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('reports an error and sets exitCode on failure', async () => {
|
|
89
|
-
api.rerunDeployment.mockRejectedValue(new Error('deployment not found'));
|
|
90
|
-
|
|
91
|
-
await rerunCommand(config, ['dep-missing'], chalk);
|
|
92
|
-
|
|
93
|
-
expect(process.exitCode).toBe(1);
|
|
94
|
-
});
|
package/tests/restart.test.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* badgr restart — relaunches an endpoint with the same config and API key
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
5
|
-
import { restartCommand } from '../src/commands/restart.js';
|
|
6
|
-
|
|
7
|
-
vi.mock('../src/api.js', () => ({
|
|
8
|
-
restartDeployment: vi.fn(),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
vi.mock('../src/store.js', () => ({
|
|
12
|
-
findDeployment: vi.fn(() => null),
|
|
13
|
-
removeDeployment: vi.fn(),
|
|
14
|
-
addDeployment: vi.fn(),
|
|
15
|
-
addReceipt: vi.fn(),
|
|
16
|
-
generateReceiptId: vi.fn(() => 'rcpt-restart-001'),
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
vi.mock('../src/config.js', () => ({
|
|
20
|
-
requireApiKey: vi.fn(),
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
import * as api from '../src/api.js';
|
|
24
|
-
import * as store from '../src/store.js';
|
|
25
|
-
|
|
26
|
-
const config = { apiKey: 'sk-test', baseUrl: 'https://api.test/v1' };
|
|
27
|
-
|
|
28
|
-
const chalk = {
|
|
29
|
-
bold: s => s,
|
|
30
|
-
dim: s => s,
|
|
31
|
-
red: s => s,
|
|
32
|
-
yellow: s => s,
|
|
33
|
-
green: s => s,
|
|
34
|
-
cyan: s => s,
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
process.exitCode = undefined;
|
|
39
|
-
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
40
|
-
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
41
|
-
vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
42
|
-
vi.resetAllMocks();
|
|
43
|
-
store.findDeployment.mockReturnValue(null);
|
|
44
|
-
store.generateReceiptId.mockReturnValue('rcpt-restart-001');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
afterEach(() => {
|
|
48
|
-
vi.restoreAllMocks();
|
|
49
|
-
process.exitCode = undefined;
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('requires a deployment id', async () => {
|
|
53
|
-
await restartCommand(config, [], chalk);
|
|
54
|
-
expect(api.restartDeployment).not.toHaveBeenCalled();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('calls restartDeployment and prints the new deployment id and URL', async () => {
|
|
58
|
-
api.restartDeployment.mockResolvedValue({
|
|
59
|
-
deployment_id: 'dep-new-002',
|
|
60
|
-
workload_type: 'endpoint',
|
|
61
|
-
model: 'Qwen/Qwen2.5-7B-Instruct',
|
|
62
|
-
gpu_type: 'L40S',
|
|
63
|
-
gpu_count: 1,
|
|
64
|
-
status: 'provisioning',
|
|
65
|
-
endpoint_url: 'https://dep-new-002.aibadgr.com/v1',
|
|
66
|
-
cost_per_hour: 1.2,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
const lines = [];
|
|
70
|
-
console.log.mockImplementation((...args) => lines.push(args.join(' ')));
|
|
71
|
-
|
|
72
|
-
await restartCommand(config, ['dep-old-001'], chalk);
|
|
73
|
-
|
|
74
|
-
expect(api.restartDeployment).toHaveBeenCalledWith(config, 'dep-old-001');
|
|
75
|
-
expect(store.removeDeployment).toHaveBeenCalledWith('dep-old-001');
|
|
76
|
-
expect(store.addDeployment).toHaveBeenCalledWith(expect.objectContaining({ id: 'dep-new-002' }));
|
|
77
|
-
expect(lines.some(l => l.includes('dep-new-002'))).toBe(true);
|
|
78
|
-
expect(lines.some(l => l.includes('https://dep-new-002.aibadgr.com/v1'))).toBe(true);
|
|
79
|
-
expect(process.exitCode).toBeFalsy();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('reports an error and sets exitCode on failure', async () => {
|
|
83
|
-
api.restartDeployment.mockRejectedValue(new Error('deployment not found'));
|
|
84
|
-
|
|
85
|
-
await restartCommand(config, ['dep-missing'], chalk);
|
|
86
|
-
|
|
87
|
-
expect(process.exitCode).toBe(1);
|
|
88
|
-
});
|
package/tests/router.test.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
findById, findByCanonical, findCheapest, listAll,
|
|
4
|
-
estimateCost,
|
|
5
|
-
GPU_CATALOG,
|
|
6
|
-
} from '../src/router.js';
|
|
7
|
-
|
|
8
|
-
// getRoutePlan and PROVIDER_CATALOG have been removed from the CLI.
|
|
9
|
-
// Provider routing is now server-side only. Tests below cover the
|
|
10
|
-
// remaining local GPU catalog helpers.
|
|
11
|
-
|
|
12
|
-
describe('findById', () => {
|
|
13
|
-
it('finds GPU by id', () => {
|
|
14
|
-
expect(findById('rtx-4090').name).toContain('4090');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('returns null for unknown id', () => {
|
|
18
|
-
expect(findById('rtx-9999')).toBeNull();
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
describe('findByCanonical', () => {
|
|
23
|
-
it('finds GPU by canonical name', () => {
|
|
24
|
-
expect(findByCanonical('RTX_4090').id).toBe('rtx-4090');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('returns null for unknown canonical', () => {
|
|
28
|
-
expect(findByCanonical('RTX_9999')).toBeNull();
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
describe('findCheapest', () => {
|
|
33
|
-
it('returns a GPU with no requirements', () => {
|
|
34
|
-
expect(findCheapest()).not.toBeNull();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('returns the cheapest overall', () => {
|
|
38
|
-
const gpu = findCheapest();
|
|
39
|
-
const allRates = GPU_CATALOG.map(g => g.ratePerHour);
|
|
40
|
-
expect(gpu.ratePerHour).toBe(Math.min(...allRates));
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('filters by minVramGb', () => {
|
|
44
|
-
const gpu = findCheapest({ minVramGb: 40 });
|
|
45
|
-
expect(gpu.vramGb).toBeGreaterThanOrEqual(40);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('filters by tag', () => {
|
|
49
|
-
const gpu = findCheapest({ tag: 'large-model' });
|
|
50
|
-
expect(gpu.tags).toContain('large-model');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('returns null when no match', () => {
|
|
54
|
-
expect(findCheapest({ minVramGb: 999 })).toBeNull();
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe('listAll', () => {
|
|
59
|
-
it('returns all GPUs sorted by rate ascending', () => {
|
|
60
|
-
const gpus = listAll();
|
|
61
|
-
expect(gpus.length).toBe(GPU_CATALOG.length);
|
|
62
|
-
for (let i = 1; i < gpus.length; i++) {
|
|
63
|
-
expect(gpus[i].ratePerHour).toBeGreaterThanOrEqual(gpus[i - 1].ratePerHour);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('does not mutate the original catalog', () => {
|
|
68
|
-
const before = GPU_CATALOG.map(g => g.ratePerHour);
|
|
69
|
-
listAll();
|
|
70
|
-
expect(GPU_CATALOG.map(g => g.ratePerHour)).toEqual(before);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
describe('estimateCost', () => {
|
|
75
|
-
it('calculates cost for 60 minutes = 1 hour', () => {
|
|
76
|
-
expect(estimateCost(1.10, 60)).toBeCloseTo(1.10, 5);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('calculates cost for 30 minutes = half rate', () => {
|
|
80
|
-
expect(estimateCost(2.00, 30)).toBeCloseTo(1.00, 5);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('returns 0 for 0 minutes', () => {
|
|
84
|
-
expect(estimateCost(3.50, 0)).toBe(0);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe('L40S', () => {
|
|
89
|
-
it('exists in GPU_CATALOG', () => {
|
|
90
|
-
expect(findById('l40s')).not.toBeNull();
|
|
91
|
-
expect(findByCanonical('L40S').vramGb).toBe(48);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('has no PROVIDER_CATALOG (provider routing is server-side)', async () => {
|
|
95
|
-
const mod = await import('../src/router.js');
|
|
96
|
-
expect(mod.PROVIDER_CATALOG).toBeUndefined();
|
|
97
|
-
});
|
|
98
|
-
});
|