@amodalai/amodal 0.3.90 → 0.3.92

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 (62) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/src/commands/audit.d.ts +1 -3
  3. package/dist/src/commands/audit.d.ts.map +1 -1
  4. package/dist/src/commands/audit.js +4 -53
  5. package/dist/src/commands/audit.js.map +1 -1
  6. package/dist/src/commands/build-manifest-types.js +1 -1
  7. package/dist/src/commands/build-tools.d.ts +3 -10
  8. package/dist/src/commands/build-tools.d.ts.map +1 -1
  9. package/dist/src/commands/build-tools.js +5 -118
  10. package/dist/src/commands/build-tools.js.map +1 -1
  11. package/dist/src/commands/build.js +1 -1
  12. package/dist/src/commands/build.js.map +1 -1
  13. package/dist/src/commands/deploy.d.ts +1 -1
  14. package/dist/src/commands/deploy.d.ts.map +1 -1
  15. package/dist/src/commands/deploy.js +3 -61
  16. package/dist/src/commands/deploy.js.map +1 -1
  17. package/dist/src/commands/deployments.d.ts.map +1 -1
  18. package/dist/src/commands/deployments.js +3 -36
  19. package/dist/src/commands/deployments.js.map +1 -1
  20. package/dist/src/commands/dev.d.ts.map +1 -1
  21. package/dist/src/commands/dev.js +7 -10
  22. package/dist/src/commands/dev.js.map +1 -1
  23. package/dist/src/commands/experiment.d.ts +1 -3
  24. package/dist/src/commands/experiment.d.ts.map +1 -1
  25. package/dist/src/commands/experiment.js +4 -102
  26. package/dist/src/commands/experiment.js.map +1 -1
  27. package/dist/src/commands/promote.d.ts.map +1 -1
  28. package/dist/src/commands/promote.js +3 -21
  29. package/dist/src/commands/promote.js.map +1 -1
  30. package/dist/src/commands/rollback.d.ts.map +1 -1
  31. package/dist/src/commands/rollback.js +3 -24
  32. package/dist/src/commands/rollback.js.map +1 -1
  33. package/dist/src/commands/secrets.d.ts.map +1 -1
  34. package/dist/src/commands/secrets.js +2 -102
  35. package/dist/src/commands/secrets.js.map +1 -1
  36. package/dist/src/commands/status.d.ts.map +1 -1
  37. package/dist/src/commands/status.js +3 -49
  38. package/dist/src/commands/status.js.map +1 -1
  39. package/dist/tsconfig.tsbuildinfo +1 -1
  40. package/package.json +7 -8
  41. package/src/commands/audit.ts +4 -71
  42. package/src/commands/build-manifest-types.ts +1 -1
  43. package/src/commands/build-tools.ts +5 -142
  44. package/src/commands/build.ts +1 -1
  45. package/src/commands/deploy.test.ts +2 -13
  46. package/src/commands/deploy.ts +5 -67
  47. package/src/commands/deployments.ts +3 -39
  48. package/src/commands/dev.ts +7 -10
  49. package/src/commands/experiment.ts +4 -110
  50. package/src/commands/promote.ts +3 -21
  51. package/src/commands/rollback.ts +3 -24
  52. package/src/commands/secrets.test.ts +12 -134
  53. package/src/commands/secrets.ts +2 -116
  54. package/src/commands/status.ts +3 -51
  55. package/dist/src/shared/platform-client.d.ts +0 -110
  56. package/dist/src/shared/platform-client.d.ts.map +0 -1
  57. package/dist/src/shared/platform-client.js +0 -263
  58. package/dist/src/shared/platform-client.js.map +0 -1
  59. package/src/commands/audit.test.ts +0 -92
  60. package/src/commands/experiment.test.ts +0 -125
  61. package/src/shared/platform-client.test.ts +0 -70
  62. package/src/shared/platform-client.ts +0 -343
@@ -1 +0,0 @@
1
- {"version":3,"file":"platform-client.d.ts","sourceRoot":"","sources":["../../../src/shared/platform-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,CAAC,EAAE;IACpD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAAC,CAgCzC;AAED;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,CAAC,EAAE;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAC;IASrD;;;OAGG;WACU,MAAM,CAAC,OAAO,CAAC,EAAE;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,cAAc,CAAC;IAKvF,OAAO,CAAC,OAAO;YAOD,OAAO;IAoCrB;;;OAGG;YACW,eAAe;IAqC7B;;;OAGG;IACG,YAAY,CAChB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,OAAO,SAAS,EAAE,UAAU,GACxC,OAAO,CAAC,IAAI,CAAC;IAiChB;;;;;;;OAOG;IACG,kBAAkB,CACtB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAoD/B;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAC7C,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;QAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IAIF;;OAEG;IACG,eAAe,CAAC,OAAO,GAAE;QAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC;KACX,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAQlC;;OAEG;IACG,QAAQ,CAAC,OAAO,GAAE;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,cAAc,CAAC;IAOhC;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAqB,GAAG,OAAO,CAAC,cAAc,CAAC;IAOrF;;OAEG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAG3D"}
@@ -1,263 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2025 Amodal Labs, Inc.
4
- * SPDX-License-Identifier: MIT
5
- */
6
- import { readProjectLink } from '../commands/link.js';
7
- import { readRcFile } from '../commands/login.js';
8
- /**
9
- * Resolve platform URL and API key from multiple sources:
10
- * 1. Explicit options (flags)
11
- * 2. .amodal/project.json (platformUrl from `amodal link`)
12
- * 3. ~/.amodalrc (auth token from `amodal login`)
13
- * 4. Env vars (fallback)
14
- */
15
- export async function resolvePlatformConfig(options) {
16
- let url = options?.url;
17
- let apiKey = options?.apiKey;
18
- // Try project link for URL
19
- if (!url) {
20
- const link = await readProjectLink();
21
- if (link?.platformUrl) {
22
- url = link.platformUrl;
23
- }
24
- }
25
- // Try rc file for auth token
26
- if (!apiKey) {
27
- const rc = await readRcFile();
28
- if (rc.platform?.token) {
29
- apiKey = rc.platform.token;
30
- // Also use the URL from rc if still missing
31
- if (!url && rc.platform.url) {
32
- url = rc.platform.url;
33
- }
34
- }
35
- }
36
- // Env vars as fallback
37
- if (!url)
38
- url = process.env['PLATFORM_API_URL'];
39
- if (!apiKey)
40
- apiKey = process.env['PLATFORM_API_KEY'];
41
- if (!url)
42
- throw new Error('Platform URL not found. Run `amodal login` + `amodal link`, or set PLATFORM_API_URL.');
43
- if (!apiKey)
44
- throw new Error('Platform auth not found. Run `amodal login`, or set PLATFORM_API_KEY.');
45
- return { url: url.replace(/\/$/, ''), apiKey };
46
- }
47
- /**
48
- * Platform API client for snapshot deployments.
49
- *
50
- * Resolves credentials from: explicit options → project link → rc file → env vars.
51
- * Use `PlatformClient.create()` for async auto-discovery, or `new PlatformClient()` for sync usage.
52
- */
53
- export class PlatformClient {
54
- baseUrl;
55
- apiKey;
56
- constructor(options) {
57
- const url = options?.url ?? process.env['PLATFORM_API_URL'];
58
- const apiKey = options?.apiKey ?? process.env['PLATFORM_API_KEY'];
59
- if (!url)
60
- throw new Error('Platform URL not found. Run `amodal login` + `amodal link`, or set PLATFORM_API_URL.');
61
- if (!apiKey)
62
- throw new Error('Platform auth not found. Run `amodal login`, or set PLATFORM_API_KEY.');
63
- this.baseUrl = url.replace(/\/$/, '');
64
- this.apiKey = apiKey;
65
- }
66
- /**
67
- * Create a PlatformClient with auto-discovery of credentials.
68
- * Resolves from: explicit options → project link → rc file → env vars.
69
- */
70
- static async create(options) {
71
- const config = await resolvePlatformConfig(options);
72
- return new PlatformClient(config);
73
- }
74
- headers() {
75
- return {
76
- 'Authorization': `Bearer ${this.apiKey}`,
77
- 'Content-Type': 'application/json',
78
- };
79
- }
80
- async request(method, path, body) {
81
- const url = `${this.baseUrl}${path}`;
82
- let resp = await fetch(url, {
83
- method,
84
- headers: this.headers(),
85
- ...(body ? { body: JSON.stringify(body) } : {}),
86
- });
87
- // Auto-refresh on 401
88
- if (resp.status === 401) {
89
- const refreshed = await this.tryRefreshToken();
90
- if (refreshed) {
91
- resp = await fetch(url, {
92
- method,
93
- headers: this.headers(),
94
- ...(body ? { body: JSON.stringify(body) } : {}),
95
- });
96
- }
97
- }
98
- if (!resp.ok) {
99
- let detail = '';
100
- try {
101
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
102
- const errBody = await resp.json();
103
- detail = errBody.error ? `: ${errBody.error}` : '';
104
- }
105
- catch {
106
- // ignore parse errors
107
- }
108
- throw new Error(`Platform API ${method} ${path} failed (${resp.status})${detail}`);
109
- }
110
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
111
- return resp.json();
112
- }
113
- /**
114
- * Try to refresh the token using the stored refresh token.
115
- * Updates both the in-memory key and the rc file on success.
116
- */
117
- async tryRefreshToken() {
118
- try {
119
- const { readRcFile } = await import('../commands/login.js');
120
- const rc = await readRcFile();
121
- if (!rc.platform?.refreshToken)
122
- return false;
123
- const res = await fetch(`${this.baseUrl}/api/auth/refresh`, {
124
- method: 'POST',
125
- headers: { 'Content-Type': 'application/json' },
126
- body: JSON.stringify({ refresh_token: rc.platform.refreshToken }),
127
- });
128
- if (!res.ok)
129
- return false;
130
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
131
- const data = (await res.json());
132
- if (!data.access_token)
133
- return false;
134
- // Update in-memory
135
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- private field update
136
- this.apiKey = data.access_token;
137
- // Persist to rc file
138
- rc.platform.token = data.access_token;
139
- if (data.refresh_token)
140
- rc.platform.refreshToken = data.refresh_token;
141
- const { writeFile } = await import('node:fs/promises');
142
- const { homedir } = await import('node:os');
143
- const path = await import('node:path');
144
- const rcPath = path.join(homedir(), '.amodalrc');
145
- await writeFile(rcPath, JSON.stringify(rc, null, 2) + '\n', { mode: 0o600 });
146
- process.stderr.write('[auth] Token refreshed automatically.\n');
147
- return true;
148
- }
149
- catch {
150
- return false;
151
- }
152
- }
153
- /**
154
- * Trigger a runtime-app build on the build server.
155
- * Sends the repo tarball to the build server which builds the SPA and uploads to R2.
156
- */
157
- async triggerBuild(buildServerUrl, appId, deployId, repoTarball) {
158
- const url = `${buildServerUrl}/build`;
159
- const formData = new FormData();
160
- formData.append('appId', appId);
161
- formData.append('deployId', deployId);
162
- // Convert ReadStream to Blob for FormData
163
- const chunks = [];
164
- for await (const chunk of repoTarball) {
165
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- ReadStream chunks are Buffer/Uint8Array
166
- chunks.push(chunk);
167
- }
168
- const blob = new Blob(chunks, { type: 'application/gzip' });
169
- formData.append('repo', blob, 'repo.tar.gz');
170
- const resp = await fetch(url, {
171
- method: 'POST',
172
- headers: { Authorization: `Bearer ${this.apiKey}` },
173
- body: formData,
174
- });
175
- if (!resp.ok) {
176
- let detail = '';
177
- try {
178
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
179
- const errBody = await resp.json();
180
- detail = errBody.message ?? errBody.error ?? '';
181
- }
182
- catch { /* ignore */ }
183
- throw new Error(`Build server failed (${resp.status}): ${detail}`);
184
- }
185
- }
186
- /**
187
- * Trigger a remote build:
188
- * 1. Get scoped R2 temp credentials from the platform API
189
- * 2. Upload the tarball directly to R2 with those creds
190
- * 3. Tell the platform API to trigger a Fly Machine build
191
- *
192
- * Returns a buildId for polling.
193
- */
194
- async triggerRemoteBuild(appId, environment, tarballPath, message) {
195
- // Step 1: Mint scoped R2 temp credentials for the upload
196
- const uploadInfo = await this.request('POST', '/api/deploys/build?action=upload-url', { appId });
197
- // Step 2: Upload tarball directly to R2 with the scoped temp creds
198
- const { readFileSync } = await import('node:fs');
199
- const tarball = readFileSync(tarballPath);
200
- const { S3Client, PutObjectCommand } = await import('@aws-sdk/client-s3');
201
- const s3 = new S3Client({
202
- region: 'auto',
203
- endpoint: uploadInfo.endpoint,
204
- credentials: {
205
- accessKeyId: uploadInfo.accessKeyId,
206
- secretAccessKey: uploadInfo.secretAccessKey,
207
- sessionToken: uploadInfo.sessionToken,
208
- },
209
- });
210
- await s3.send(new PutObjectCommand({
211
- Bucket: uploadInfo.bucket,
212
- Key: uploadInfo.tarballKey,
213
- Body: tarball,
214
- ContentType: 'application/gzip',
215
- }));
216
- // Step 3: Trigger the build
217
- const result = await this.request('POST', '/api/deploys/build?action=trigger', { appId, tarballKey: uploadInfo.tarballKey, environment, message });
218
- return result;
219
- }
220
- /**
221
- * Poll build status.
222
- */
223
- async getBuildStatus(buildId) {
224
- return this.request('GET', `/api/builds/${encodeURIComponent(buildId)}/status`);
225
- }
226
- /**
227
- * List deployments for the authenticated app.
228
- */
229
- async listDeployments(options = {}) {
230
- const params = new URLSearchParams();
231
- if (options.environment)
232
- params.set('environment', options.environment);
233
- if (options.limit)
234
- params.set('limit', String(options.limit));
235
- const qs = params.toString();
236
- return this.request('GET', `/api/deployments${qs ? `?${qs}` : ''}`);
237
- }
238
- /**
239
- * Rollback to a previous deployment.
240
- */
241
- async rollback(options = {}) {
242
- return this.request('POST', '/api/deployments/rollback', {
243
- deployId: options.deployId,
244
- environment: options.environment ?? 'production',
245
- });
246
- }
247
- /**
248
- * Promote a deployment from one environment to another.
249
- */
250
- async promote(fromEnv, toEnv = 'production') {
251
- return this.request('POST', '/api/deployments/promote', {
252
- fromEnvironment: fromEnv,
253
- toEnvironment: toEnv,
254
- });
255
- }
256
- /**
257
- * Get status of a specific deployment.
258
- */
259
- async getStatus(deployId) {
260
- return this.request('GET', `/api/deployments/${deployId}`);
261
- }
262
- }
263
- //# sourceMappingURL=platform-client.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"platform-client.js","sourceRoot":"","sources":["../../../src/shared/platform-client.ts"],"names":[],"mappings":"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"}
@@ -1,92 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2025 Amodal Labs, Inc.
4
- * SPDX-License-Identifier: MIT
5
- */
6
-
7
- import {describe, it, expect, vi, beforeEach} from 'vitest';
8
-
9
- describe('runAudit', () => {
10
- let fetchSpy: ReturnType<typeof vi.fn>;
11
-
12
- beforeEach(() => {
13
- vi.clearAllMocks();
14
- fetchSpy = vi.fn();
15
- vi.stubGlobal('fetch', fetchSpy);
16
- });
17
-
18
- it('outputs JSON format', async () => {
19
- fetchSpy.mockResolvedValue({
20
- ok: true,
21
- json: () => Promise.resolve({
22
- sessionId: 'sess-1',
23
- events: [
24
- {id: 'e1', eventType: 'tool_call', data: {}, tokenCount: 10, durationMs: 50, createdAt: '2026-03-15T10:00:00Z'},
25
- ],
26
- }),
27
- });
28
-
29
- const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
30
-
31
- const {runAudit} = await import('./audit.js');
32
- await runAudit({
33
- sessionId: 'sess-1',
34
- format: 'json',
35
- platformUrl: 'http://localhost:4000',
36
- platformApiKey: 'key-123',
37
- });
38
-
39
- expect(fetchSpy).toHaveBeenCalledOnce();
40
- const output = stdoutSpy.mock.calls.map(([s]) => s).join('');
41
- expect(output).toContain('"sessionId"');
42
- stdoutSpy.mockRestore();
43
- });
44
-
45
- it('outputs table format by default', async () => {
46
- fetchSpy.mockResolvedValue({
47
- ok: true,
48
- json: () => Promise.resolve({
49
- sessionId: 'sess-1',
50
- events: [
51
- {id: 'e1', eventType: 'tool_call', data: {}, tokenCount: null, durationMs: 50, createdAt: '2026-03-15T10:00:00Z'},
52
- {id: 'e2', eventType: 'session_end', data: {}, tokenCount: null, durationMs: null, createdAt: '2026-03-15T10:01:00Z'},
53
- ],
54
- }),
55
- });
56
-
57
- const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
58
-
59
- const {runAudit} = await import('./audit.js');
60
- await runAudit({
61
- sessionId: 'sess-1',
62
- platformUrl: 'http://localhost:4000',
63
- platformApiKey: 'key-123',
64
- });
65
-
66
- const output = stdoutSpy.mock.calls.map(([s]) => s).join('');
67
- expect(output).toContain('tool_call');
68
- expect(output).toContain('session_end');
69
- expect(output).toContain('Total: 2 events');
70
- stdoutSpy.mockRestore();
71
- });
72
-
73
- it('handles empty events', async () => {
74
- fetchSpy.mockResolvedValue({
75
- ok: true,
76
- json: () => Promise.resolve({sessionId: 'sess-1', events: []}),
77
- });
78
-
79
- const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
80
-
81
- const {runAudit} = await import('./audit.js');
82
- await runAudit({
83
- sessionId: 'sess-1',
84
- platformUrl: 'http://localhost:4000',
85
- platformApiKey: 'key-123',
86
- });
87
-
88
- const output = stdoutSpy.mock.calls.map(([s]) => s).join('');
89
- expect(output).toContain('No audit events found');
90
- stdoutSpy.mockRestore();
91
- });
92
- });
@@ -1,125 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2025 Amodal Labs, Inc.
4
- * SPDX-License-Identifier: MIT
5
- */
6
-
7
- import {describe, it, expect, vi, beforeEach} from 'vitest';
8
-
9
- describe('runExperimentCommand', () => {
10
- let fetchSpy: ReturnType<typeof vi.fn>;
11
-
12
- beforeEach(() => {
13
- vi.clearAllMocks();
14
- fetchSpy = vi.fn();
15
- vi.stubGlobal('fetch', fetchSpy);
16
- });
17
-
18
- it('lists experiments', async () => {
19
- fetchSpy.mockResolvedValue({
20
- ok: true,
21
- json: () => Promise.resolve({
22
- experiments: [
23
- {id: 'exp-1', name: 'test-exp', status: 'draft'},
24
- ],
25
- }),
26
- });
27
-
28
- const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
29
-
30
- const {runExperimentCommand} = await import('./experiment.js');
31
- await runExperimentCommand({
32
- action: 'list',
33
- platformUrl: 'http://localhost:4000',
34
- platformApiKey: 'key-123',
35
- });
36
-
37
- const output = stdoutSpy.mock.calls.map(([s]) => s).join('');
38
- expect(output).toContain('exp-1');
39
- expect(output).toContain('test-exp');
40
- stdoutSpy.mockRestore();
41
- });
42
-
43
- it('creates an experiment', async () => {
44
- fetchSpy.mockResolvedValue({
45
- ok: true,
46
- json: () => Promise.resolve({id: 'exp-new'}),
47
- });
48
-
49
- const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
50
-
51
- const {runExperimentCommand} = await import('./experiment.js');
52
- await runExperimentCommand({
53
- action: 'create',
54
- name: 'my-experiment',
55
- controlConfig: '{"model":"claude-sonnet-4-20250514"}',
56
- variantConfig: '{"model":"gpt-4o"}',
57
- platformUrl: 'http://localhost:4000',
58
- platformApiKey: 'key-123',
59
- });
60
-
61
- expect(fetchSpy).toHaveBeenCalledOnce();
62
- const output = stdoutSpy.mock.calls.map(([s]) => s).join('');
63
- expect(output).toContain('Created experiment: exp-new');
64
- stdoutSpy.mockRestore();
65
- });
66
-
67
- it('deploys an experiment', async () => {
68
- fetchSpy.mockResolvedValue({ok: true, json: () => Promise.resolve({status: 'ok'})});
69
-
70
- const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
71
-
72
- const {runExperimentCommand} = await import('./experiment.js');
73
- await runExperimentCommand({
74
- action: 'deploy',
75
- id: 'exp-1',
76
- platformUrl: 'http://localhost:4000',
77
- platformApiKey: 'key-123',
78
- });
79
-
80
- const output = stdoutSpy.mock.calls.map(([s]) => s).join('');
81
- expect(output).toContain('Deployed experiment: exp-1');
82
- stdoutSpy.mockRestore();
83
- });
84
-
85
- it('watches an experiment', async () => {
86
- fetchSpy.mockResolvedValue({
87
- ok: true,
88
- json: () => Promise.resolve({id: 'exp-1', name: 'test', status: 'deployed'}),
89
- });
90
-
91
- const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
92
-
93
- const {runExperimentCommand} = await import('./experiment.js');
94
- await runExperimentCommand({
95
- action: 'watch',
96
- id: 'exp-1',
97
- platformUrl: 'http://localhost:4000',
98
- platformApiKey: 'key-123',
99
- });
100
-
101
- const output = stdoutSpy.mock.calls.map(([s]) => s).join('');
102
- expect(output).toContain('"id": "exp-1"');
103
- stdoutSpy.mockRestore();
104
- });
105
-
106
- it('handles empty experiment list', async () => {
107
- fetchSpy.mockResolvedValue({
108
- ok: true,
109
- json: () => Promise.resolve({experiments: []}),
110
- });
111
-
112
- const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
113
-
114
- const {runExperimentCommand} = await import('./experiment.js');
115
- await runExperimentCommand({
116
- action: 'list',
117
- platformUrl: 'http://localhost:4000',
118
- platformApiKey: 'key-123',
119
- });
120
-
121
- const output = stdoutSpy.mock.calls.map(([s]) => s).join('');
122
- expect(output).toContain('No experiments found');
123
- stdoutSpy.mockRestore();
124
- });
125
- });
@@ -1,70 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2025 Amodal Labs, Inc.
4
- * SPDX-License-Identifier: MIT
5
- */
6
-
7
- import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
8
- import {PlatformClient} from './platform-client.js';
9
-
10
- describe('PlatformClient', () => {
11
- const originalFetch = globalThis.fetch;
12
-
13
- beforeEach(() => {
14
- vi.restoreAllMocks();
15
- });
16
-
17
- afterEach(() => {
18
- globalThis.fetch = originalFetch;
19
- });
20
-
21
- it('throws if PLATFORM_API_URL not set', () => {
22
- const origUrl = process.env['PLATFORM_API_URL'];
23
- const origKey = process.env['PLATFORM_API_KEY'];
24
- delete process.env['PLATFORM_API_URL'];
25
- delete process.env['PLATFORM_API_KEY'];
26
-
27
- try {
28
- expect(() => new PlatformClient()).toThrow('Platform URL not found');
29
- } finally {
30
- if (origUrl) process.env['PLATFORM_API_URL'] = origUrl;
31
- if (origKey) process.env['PLATFORM_API_KEY'] = origKey;
32
- }
33
- });
34
-
35
- it('throws if PLATFORM_API_KEY not set', () => {
36
- expect(() => new PlatformClient({url: 'http://localhost:4000'})).toThrow('Platform auth not found');
37
- });
38
-
39
- it('creates client with explicit options', () => {
40
- const client = new PlatformClient({url: 'http://localhost:4000', apiKey: 'test-key'});
41
- expect(client).toBeDefined();
42
- });
43
-
44
- it('lists deployments via GET', async () => {
45
- globalThis.fetch = vi.fn().mockResolvedValue({
46
- ok: true,
47
- json: () => Promise.resolve([]),
48
- });
49
-
50
- const client = new PlatformClient({url: 'http://localhost:4000', apiKey: 'key'});
51
- const result = await client.listDeployments({environment: 'staging', limit: 5});
52
-
53
- expect(result).toEqual([]);
54
-
55
- const fetchCall = (globalThis.fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [string];
56
- expect(fetchCall[0]).toContain('environment=staging');
57
- expect(fetchCall[0]).toContain('limit=5');
58
- });
59
-
60
- it('throws on non-ok response', async () => {
61
- globalThis.fetch = vi.fn().mockResolvedValue({
62
- ok: false,
63
- status: 401,
64
- json: () => Promise.resolve({error: 'Unauthorized'}),
65
- });
66
-
67
- const client = new PlatformClient({url: 'http://localhost:4000', apiKey: 'bad-key'});
68
- await expect(client.listDeployments()).rejects.toThrow('failed (401)');
69
- });
70
- });