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.
Files changed (66) hide show
  1. package/LICENSE +207 -0
  2. package/README.md +135 -3
  3. package/package.json +44 -2
  4. package/src/api.js +16 -0
  5. package/src/badgr.js +2 -2
  6. package/src/commands/batch.js +11 -0
  7. package/src/commands/comfyui.js +31 -15
  8. package/src/commands/embed.js +13 -10
  9. package/src/commands/launch.js +8 -1
  10. package/src/commands/login.js +75 -20
  11. package/src/commands/run.js +45 -13
  12. package/src/commands/sbatch.js +6 -1
  13. package/src/commands/serve.js +44 -30
  14. package/src/commands/train.js +8 -12
  15. package/src/commands/transcribe.js +13 -10
  16. package/src/envFlag.js +10 -0
  17. package/src/onboarding.js +8 -1
  18. package/src/progress.js +48 -0
  19. package/tests/agent-images.test.js +0 -17
  20. package/tests/api.test.js +0 -168
  21. package/tests/artifactDownload.test.js +0 -113
  22. package/tests/artifacts.test.js +0 -168
  23. package/tests/batch.test.js +0 -641
  24. package/tests/browser.test.js +0 -51
  25. package/tests/capacity.test.js +0 -68
  26. package/tests/commands.test.js +0 -417
  27. package/tests/config.test.js +0 -96
  28. package/tests/connect.test.js +0 -83
  29. package/tests/detect.test.js +0 -191
  30. package/tests/down.test.js +0 -150
  31. package/tests/errors.test.js +0 -130
  32. package/tests/fallback-timeout.test.js +0 -41
  33. package/tests/fanout.test.js +0 -124
  34. package/tests/gpu-doctor-classifiers.test.js +0 -402
  35. package/tests/gpu-doctor-doctor.test.js +0 -304
  36. package/tests/gpu-doctor-probe-cache.test.js +0 -110
  37. package/tests/gpu-doctor-probes.test.js +0 -257
  38. package/tests/heartbeat.test.js +0 -70
  39. package/tests/job-progress-poll.test.js +0 -136
  40. package/tests/launch-command-argv.test.js +0 -93
  41. package/tests/launch-readiness.test.js +0 -403
  42. package/tests/launch.test.js +0 -440
  43. package/tests/onboarding.test.js +0 -134
  44. package/tests/productized-dry-run.test.js +0 -141
  45. package/tests/productized-runners.test.js +0 -237
  46. package/tests/pull.test.js +0 -266
  47. package/tests/rerun.test.js +0 -94
  48. package/tests/restart.test.js +0 -88
  49. package/tests/router.test.js +0 -98
  50. package/tests/run-lifecycle.test.js +0 -1054
  51. package/tests/sbatch.test.js +0 -190
  52. package/tests/secrets.test.js +0 -16
  53. package/tests/serve-apps.test.js +0 -189
  54. package/tests/serve-lifecycle.test.js +0 -931
  55. package/tests/slurm.test.js +0 -77
  56. package/tests/spec.test.js +0 -201
  57. package/tests/status.test.js +0 -73
  58. package/tests/store.test.js +0 -187
  59. package/tests/task.test.js +0 -109
  60. package/tests/template.test.js +0 -556
  61. package/tests/train-lora-dataset.test.js +0 -176
  62. package/tests/upload.test.js +0 -79
  63. package/tests/workload-rerun.test.js +0 -56
  64. package/tests/workload-spec.test.js +0 -180
  65. package/tests/workload-templates.test.js +0 -865
  66. package/tests/workload-workspace-paths.test.js +0 -46
@@ -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
- });
@@ -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
- });
@@ -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
- });