@amodalai/amodal 0.3.89 → 0.3.91

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 (75) hide show
  1. package/CHANGELOG.md +46 -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.d.ts +23 -5
  12. package/dist/src/commands/build.d.ts.map +1 -1
  13. package/dist/src/commands/build.js +53 -34
  14. package/dist/src/commands/build.js.map +1 -1
  15. package/dist/src/commands/chat.d.ts +1 -1
  16. package/dist/src/commands/chat.js +5 -5
  17. package/dist/src/commands/chat.js.map +1 -1
  18. package/dist/src/commands/deploy.d.ts +1 -1
  19. package/dist/src/commands/deploy.d.ts.map +1 -1
  20. package/dist/src/commands/deploy.js +3 -61
  21. package/dist/src/commands/deploy.js.map +1 -1
  22. package/dist/src/commands/deployments.d.ts.map +1 -1
  23. package/dist/src/commands/deployments.js +3 -36
  24. package/dist/src/commands/deployments.js.map +1 -1
  25. package/dist/src/commands/dev.d.ts.map +1 -1
  26. package/dist/src/commands/dev.js +7 -10
  27. package/dist/src/commands/dev.js.map +1 -1
  28. package/dist/src/commands/experiment.d.ts +1 -3
  29. package/dist/src/commands/experiment.d.ts.map +1 -1
  30. package/dist/src/commands/experiment.js +4 -102
  31. package/dist/src/commands/experiment.js.map +1 -1
  32. package/dist/src/commands/promote.d.ts.map +1 -1
  33. package/dist/src/commands/promote.js +3 -21
  34. package/dist/src/commands/promote.js.map +1 -1
  35. package/dist/src/commands/rollback.d.ts.map +1 -1
  36. package/dist/src/commands/rollback.js +3 -24
  37. package/dist/src/commands/rollback.js.map +1 -1
  38. package/dist/src/commands/secrets.d.ts.map +1 -1
  39. package/dist/src/commands/secrets.js +2 -102
  40. package/dist/src/commands/secrets.js.map +1 -1
  41. package/dist/src/commands/serve.d.ts +2 -11
  42. package/dist/src/commands/serve.d.ts.map +1 -1
  43. package/dist/src/commands/serve.js +44 -87
  44. package/dist/src/commands/serve.js.map +1 -1
  45. package/dist/src/commands/status.d.ts.map +1 -1
  46. package/dist/src/commands/status.js +3 -49
  47. package/dist/src/commands/status.js.map +1 -1
  48. package/dist/tsconfig.tsbuildinfo +1 -1
  49. package/package.json +7 -8
  50. package/src/commands/audit.ts +4 -71
  51. package/src/commands/build-manifest-types.ts +1 -1
  52. package/src/commands/build-tools.ts +5 -142
  53. package/src/commands/build.test.ts +14 -9
  54. package/src/commands/build.ts +73 -33
  55. package/src/commands/chat.ts +5 -5
  56. package/src/commands/deploy.test.ts +2 -13
  57. package/src/commands/deploy.ts +5 -67
  58. package/src/commands/deployments.ts +3 -39
  59. package/src/commands/dev.ts +7 -10
  60. package/src/commands/experiment.ts +4 -110
  61. package/src/commands/promote.ts +3 -21
  62. package/src/commands/rollback.ts +3 -24
  63. package/src/commands/secrets.test.ts +12 -134
  64. package/src/commands/secrets.ts +2 -116
  65. package/src/commands/serve.ts +46 -93
  66. package/src/commands/status.ts +3 -51
  67. package/src/e2e-commands.test.ts +18 -17
  68. package/dist/src/shared/platform-client.d.ts +0 -123
  69. package/dist/src/shared/platform-client.d.ts.map +0 -1
  70. package/dist/src/shared/platform-client.js +0 -280
  71. package/dist/src/shared/platform-client.js.map +0 -1
  72. package/src/commands/audit.test.ts +0 -92
  73. package/src/commands/experiment.test.ts +0 -125
  74. package/src/shared/platform-client.test.ts +0 -106
  75. package/src/shared/platform-client.ts +0 -367
@@ -1,280 +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
- * Upload a snapshot and deploy it.
155
- */
156
- async uploadSnapshot(snapshot, options = {}) {
157
- return this.request('POST', '/api/snapshot-deployments', {
158
- snapshot,
159
- environment: options.environment ?? 'production',
160
- appId: options.appId,
161
- });
162
- }
163
- /**
164
- * Trigger a runtime-app build on the build server.
165
- * Sends the repo tarball to the build server which builds the SPA and uploads to R2.
166
- */
167
- async triggerBuild(buildServerUrl, appId, deployId, repoTarball) {
168
- const url = `${buildServerUrl}/build`;
169
- const formData = new FormData();
170
- formData.append('appId', appId);
171
- formData.append('deployId', deployId);
172
- // Convert ReadStream to Blob for FormData
173
- const chunks = [];
174
- for await (const chunk of repoTarball) {
175
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- ReadStream chunks are Buffer/Uint8Array
176
- chunks.push(chunk);
177
- }
178
- const blob = new Blob(chunks, { type: 'application/gzip' });
179
- formData.append('repo', blob, 'repo.tar.gz');
180
- const resp = await fetch(url, {
181
- method: 'POST',
182
- headers: { Authorization: `Bearer ${this.apiKey}` },
183
- body: formData,
184
- });
185
- if (!resp.ok) {
186
- let detail = '';
187
- try {
188
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
189
- const errBody = await resp.json();
190
- detail = errBody.message ?? errBody.error ?? '';
191
- }
192
- catch { /* ignore */ }
193
- throw new Error(`Build server failed (${resp.status}): ${detail}`);
194
- }
195
- }
196
- /**
197
- * Trigger a remote build:
198
- * 1. Get scoped R2 temp credentials from the platform API
199
- * 2. Upload the tarball directly to R2 with those creds
200
- * 3. Tell the platform API to trigger a Fly Machine build
201
- *
202
- * Returns a buildId for polling.
203
- */
204
- async triggerRemoteBuild(appId, environment, tarballPath, message) {
205
- // Step 1: Mint scoped R2 temp credentials for the upload
206
- const uploadInfo = await this.request('POST', '/api/deploys/build?action=upload-url', { appId });
207
- // Step 2: Upload tarball directly to R2 with the scoped temp creds
208
- const { readFileSync } = await import('node:fs');
209
- const tarball = readFileSync(tarballPath);
210
- const { S3Client, PutObjectCommand } = await import('@aws-sdk/client-s3');
211
- const s3 = new S3Client({
212
- region: 'auto',
213
- endpoint: uploadInfo.endpoint,
214
- credentials: {
215
- accessKeyId: uploadInfo.accessKeyId,
216
- secretAccessKey: uploadInfo.secretAccessKey,
217
- sessionToken: uploadInfo.sessionToken,
218
- },
219
- });
220
- await s3.send(new PutObjectCommand({
221
- Bucket: uploadInfo.bucket,
222
- Key: uploadInfo.tarballKey,
223
- Body: tarball,
224
- ContentType: 'application/gzip',
225
- }));
226
- // Step 3: Trigger the build
227
- const result = await this.request('POST', '/api/deploys/build?action=trigger', { appId, tarballKey: uploadInfo.tarballKey, environment, message });
228
- return result;
229
- }
230
- /**
231
- * Poll build status.
232
- */
233
- async getBuildStatus(buildId) {
234
- return this.request('GET', `/api/builds/${encodeURIComponent(buildId)}/status`);
235
- }
236
- /**
237
- * List deployments for the authenticated app.
238
- */
239
- async listDeployments(options = {}) {
240
- const params = new URLSearchParams();
241
- if (options.environment)
242
- params.set('environment', options.environment);
243
- if (options.limit)
244
- params.set('limit', String(options.limit));
245
- const qs = params.toString();
246
- return this.request('GET', `/api/snapshot-deployments${qs ? `?${qs}` : ''}`);
247
- }
248
- /**
249
- * Rollback to a previous deployment.
250
- */
251
- async rollback(options = {}) {
252
- return this.request('POST', '/api/snapshot-deployments/rollback', {
253
- deployId: options.deployId,
254
- environment: options.environment ?? 'production',
255
- });
256
- }
257
- /**
258
- * Promote a deployment from one environment to another.
259
- */
260
- async promote(fromEnv, toEnv = 'production') {
261
- return this.request('POST', '/api/snapshot-deployments/promote', {
262
- fromEnvironment: fromEnv,
263
- toEnvironment: toEnv,
264
- });
265
- }
266
- /**
267
- * Get status of a specific deployment.
268
- */
269
- async getStatus(deployId) {
270
- return this.request('GET', `/api/snapshot-deployments/${deployId}`);
271
- }
272
- /**
273
- * Get the active snapshot for an environment.
274
- */
275
- async getActiveSnapshot(environment = 'production') {
276
- const params = new URLSearchParams({ environment });
277
- return this.request('GET', `/api/snapshot-deployments/active?${params.toString()}`);
278
- }
279
- }
280
- //# sourceMappingURL=platform-client.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"platform-client.js","sourceRoot":"","sources":["../../../src/shared/platform-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAC,eAAe,EAAC,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAC,UAAU,EAAC,MAAM,sBAAsB,CAAC;AAkBhD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAG3C;IACC,IAAI,GAAG,GAAG,OAAO,EAAE,GAAG,CAAC;IACvB,IAAI,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC;IAE7B,2BAA2B;IAC3B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,GAAG,MAAM,eAAe,EAAE,CAAC;QACrC,IAAI,IAAI,EAAE,WAAW,EAAE,CAAC;YACtB,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC;QACzB,CAAC;IACH,CAAC;IAED,6BAA6B;IAC7B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,EAAE,GAAG,MAAM,UAAU,EAAE,CAAC;QAC9B,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC;YACvB,MAAM,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;YAC3B,4CAA4C;YAC5C,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;gBAC5B,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;YACxB,CAAC;QACH,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,IAAI,CAAC,GAAG;QAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAChD,IAAI,CAAC,MAAM;QAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAEtD,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,sFAAsF,CAAC,CAAC;IAClH,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;IAEtG,OAAO,EAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,MAAM,EAAC,CAAC;AAC/C,CAAC;AAED;;;;;GAKG;AACH,MAAM,OAAO,cAAc;IACR,OAAO,CAAS;IAChB,MAAM,CAAS;IAEhC,YAAY,OAAyC;QACnD,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAClE,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,sFAAsF,CAAC,CAAC;QAClH,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;QACtG,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAyC;QAC3D,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;QACpD,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAEO,OAAO;QACb,OAAO;YACL,eAAe,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;YACxC,cAAc,EAAE,kBAAkB;SACnC,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,OAAO,CAAI,MAAc,EAAE,IAAY,EAAE,IAAc;QACnE,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,CAAC;QACrC,IAAI,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC1B,MAAM;YACN,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;YACvB,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9C,CAAC,CAAC;QAEH,sBAAsB;QACtB,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACxB,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;YAC/C,IAAI,SAAS,EAAE,CAAC;gBACd,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;oBACtB,MAAM;oBACN,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;oBACvB,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC9C,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC;gBACH,uEAAuE;gBACvE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,EAAsB,CAAC;gBACtD,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,CAAC;YAAC,MAAM,CAAC;gBACP,sBAAsB;YACxB,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,gBAAgB,MAAM,IAAI,IAAI,YAAY,IAAI,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC;QACrF,CAAC;QAED,uEAAuE;QACvE,OAAO,IAAI,CAAC,IAAI,EAAgB,CAAC;IACnC,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,eAAe;QAC3B,IAAI,CAAC;YACH,MAAM,EAAC,UAAU,EAAC,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;YAC1D,MAAM,EAAE,GAAG,MAAM,UAAU,EAAE,CAAC;YAC9B,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY;gBAAE,OAAO,KAAK,CAAC;YAE7C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,mBAAmB,EAAE;gBAC1D,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAC,cAAc,EAAE,kBAAkB,EAAC;gBAC7C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAC,aAAa,EAAE,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAC,CAAC;aAChE,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,OAAO,KAAK,CAAC;YAE1B,uEAAuE;YACvE,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAoD,CAAC;YACnF,IAAI,CAAC,IAAI,CAAC,YAAY;gBAAE,OAAO,KAAK,CAAC;YAErC,mBAAmB;YACnB,+FAA+F;YAC9F,IAAoC,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC;YAEjE,qBAAqB;YACrB,EAAE,CAAC,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC;YACtC,IAAI,IAAI,CAAC,aAAa;gBAAE,EAAE,CAAC,QAAQ,CAAC,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC;YACtE,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;YACrD,MAAM,EAAC,OAAO,EAAC,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;YAC1C,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;YACvC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;YACjD,MAAM,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAC,IAAI,EAAE,KAAK,EAAC,CAAC,CAAC;YAE3E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAChE,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,QAAwB,EAAE,UAG3C,EAAE;QACJ,OAAO,IAAI,CAAC,OAAO,CAAyB,MAAM,EAAE,2BAA2B,EAAE;YAC/E,QAAQ;YACR,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,YAAY;YAChD,KAAK,EAAE,OAAO,CAAC,KAAK;SACrB,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,YAAY,CAChB,cAAsB,EACtB,KAAa,EACb,QAAgB,EAChB,WAAyC;QAEzC,MAAM,GAAG,GAAG,GAAG,cAAc,QAAQ,CAAC;QAEtC,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;QAChC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAChC,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAEtC,0CAA0C;QAC1C,MAAM,MAAM,GAAiB,EAAE,CAAC;QAChC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;YACtC,kHAAkH;YAClH,MAAM,CAAC,IAAI,CAAC,KAAmB,CAAC,CAAC;QACnC,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE,EAAC,IAAI,EAAE,kBAAkB,EAAC,CAAC,CAAC;QAC1D,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;QAE7C,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC5B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAC,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE,EAAC;YACjD,IAAI,EAAE,QAAQ;SACf,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC;gBACH,uEAAuE;gBACvE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,EAAwC,CAAC;gBACxE,MAAM,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAClD,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,CAAC,MAAM,MAAM,MAAM,EAAE,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,kBAAkB,CACtB,KAAa,EACb,WAAmB,EACnB,WAAmB,EACnB,OAAgB;QAEhB,yDAAyD;QAEzD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,OAAO,CASnC,MAAM,EACN,sCAAsC,EACtC,EAAC,KAAK,EAAC,CACR,CAAC;QAEF,mEAAmE;QACnE,MAAM,EAAC,YAAY,EAAC,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;QAE1C,MAAM,EAAC,QAAQ,EAAE,gBAAgB,EAAC,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACxE,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC;YACtB,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,UAAU,CAAC,QAAQ;YAC7B,WAAW,EAAE;gBACX,WAAW,EAAE,UAAU,CAAC,WAAW;gBACnC,eAAe,EAAE,UAAU,CAAC,eAAe;gBAC3C,YAAY,EAAE,UAAU,CAAC,YAAY;aACtC;SACF,CAAC,CAAC;QAEH,MAAM,EAAE,CAAC,IAAI,CACX,IAAI,gBAAgB,CAAC;YACnB,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,GAAG,EAAE,UAAU,CAAC,UAAU;YAC1B,IAAI,EAAE,OAAO;YACb,WAAW,EAAE,kBAAkB;SAChC,CAAC,CACH,CAAC;QAEF,4BAA4B;QAE5B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAC/B,MAAM,EACN,mCAAmC,EACnC,EAAC,KAAK,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,EAAE,WAAW,EAAE,OAAO,EAAC,CACjE,CAAC;QAEF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,OAAe;QAMlC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,eAAe,kBAAkB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClF,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,UAGlB,EAAE;QACJ,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,IAAI,OAAO,CAAC,WAAW;YAAE,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;QACxE,IAAI,OAAO,CAAC,KAAK;YAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9D,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC,OAAO,CAA2B,KAAK,EAAE,4BAA4B,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzG,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ,CAAC,UAGX,EAAE;QACJ,OAAO,IAAI,CAAC,OAAO,CAAyB,MAAM,EAAE,oCAAoC,EAAE;YACxF,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,YAAY;SACjD,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAC,OAAe,EAAE,QAAgB,YAAY;QACzD,OAAO,IAAI,CAAC,OAAO,CAAyB,MAAM,EAAE,mCAAmC,EAAE;YACvF,eAAe,EAAE,OAAO;YACxB,aAAa,EAAE,KAAK;SACrB,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS,CAAC,QAAgB;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAyB,KAAK,EAAE,6BAA6B,QAAQ,EAAE,CAAC,CAAC;IAC9F,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,iBAAiB,CAAC,cAAsB,YAAY;QACxD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAC,WAAW,EAAC,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,OAAO,CAAiB,KAAK,EAAE,oCAAoC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACtG,CAAC;CACF"}
@@ -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,106 +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('uploads snapshot via POST', async () => {
45
- const mockResponse = {
46
- id: 'deploy-abc1234',
47
- environment: 'production',
48
- isActive: true,
49
- createdAt: new Date().toISOString(),
50
- createdBy: 'test',
51
- source: 'cli',
52
- snapshotSize: 1024,
53
- };
54
-
55
- globalThis.fetch = vi.fn().mockResolvedValue({
56
- ok: true,
57
- json: () => Promise.resolve(mockResponse),
58
- });
59
-
60
- const client = new PlatformClient({url: 'http://localhost:4000', apiKey: 'key'});
61
- const result = await client.uploadSnapshot({
62
- deployId: 'deploy-abc1234',
63
- createdAt: new Date().toISOString(),
64
- createdBy: 'test',
65
- source: 'cli',
66
- config: {name: 'test', version: '1.0', models: {main: {provider: 'a', model: 'b'}}},
67
- connections: {},
68
- skills: [],
69
- automations: [],
70
- knowledge: [],
71
- });
72
-
73
- expect(result.id).toBe('deploy-abc1234');
74
-
75
- const fetchCall = (globalThis.fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [string, RequestInit];
76
- expect(fetchCall[0]).toBe('http://localhost:4000/api/snapshot-deployments');
77
- expect(fetchCall[1].method).toBe('POST');
78
- });
79
-
80
- it('lists deployments via GET', async () => {
81
- globalThis.fetch = vi.fn().mockResolvedValue({
82
- ok: true,
83
- json: () => Promise.resolve([]),
84
- });
85
-
86
- const client = new PlatformClient({url: 'http://localhost:4000', apiKey: 'key'});
87
- const result = await client.listDeployments({environment: 'staging', limit: 5});
88
-
89
- expect(result).toEqual([]);
90
-
91
- const fetchCall = (globalThis.fetch as ReturnType<typeof vi.fn>).mock.calls[0] as [string];
92
- expect(fetchCall[0]).toContain('environment=staging');
93
- expect(fetchCall[0]).toContain('limit=5');
94
- });
95
-
96
- it('throws on non-ok response', async () => {
97
- globalThis.fetch = vi.fn().mockResolvedValue({
98
- ok: false,
99
- status: 401,
100
- json: () => Promise.resolve({error: 'Unauthorized'}),
101
- });
102
-
103
- const client = new PlatformClient({url: 'http://localhost:4000', apiKey: 'bad-key'});
104
- await expect(client.listDeployments()).rejects.toThrow('failed (401)');
105
- });
106
- });