@smooai/testing 1.0.0

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/dist/cli.mjs ADDED
@@ -0,0 +1,1365 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // src/cli/utils/auth.ts
13
+ async function getAuthToken(credentials) {
14
+ if (cachedToken && Date.now() < tokenExpiresAt - 6e4) {
15
+ return cachedToken;
16
+ }
17
+ const response = await fetch(credentials.authUrl, {
18
+ method: "POST",
19
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
20
+ body: new URLSearchParams({
21
+ grant_type: "client_credentials",
22
+ provider: "client_credentials",
23
+ client_id: credentials.clientId,
24
+ client_secret: credentials.clientSecret
25
+ })
26
+ });
27
+ if (!response.ok) {
28
+ const body = await response.text().catch(() => "");
29
+ throw new Error(`Authentication failed: HTTP ${response.status}${body ? ` \u2014 ${body}` : ""}`);
30
+ }
31
+ const data = await response.json();
32
+ if (!data.access_token) {
33
+ throw new Error("Authentication failed: no access_token in response");
34
+ }
35
+ cachedToken = data.access_token;
36
+ tokenExpiresAt = Date.now() + (data.expires_in || 3600) * 1e3;
37
+ return cachedToken;
38
+ }
39
+ function clearTokenCache() {
40
+ cachedToken = null;
41
+ tokenExpiresAt = 0;
42
+ }
43
+ var cachedToken, tokenExpiresAt;
44
+ var init_auth = __esm({
45
+ "src/cli/utils/auth.ts"() {
46
+ "use strict";
47
+ cachedToken = null;
48
+ tokenExpiresAt = 0;
49
+ }
50
+ });
51
+
52
+ // src/cli/utils/api-client.ts
53
+ var ApiClient;
54
+ var init_api_client = __esm({
55
+ "src/cli/utils/api-client.ts"() {
56
+ "use strict";
57
+ init_auth();
58
+ ApiClient = class {
59
+ credentials;
60
+ baseUrl;
61
+ constructor(credentials) {
62
+ this.credentials = credentials;
63
+ this.baseUrl = credentials.apiUrl.replace(/\/+$/, "");
64
+ }
65
+ orgPath(path) {
66
+ return `${this.baseUrl}/organizations/${this.credentials.orgId}${path}`;
67
+ }
68
+ async request(method, url, body, retry = true) {
69
+ const token = await getAuthToken(this.credentials);
70
+ const response = await fetch(url, {
71
+ method,
72
+ headers: {
73
+ Authorization: `Bearer ${token}`,
74
+ "Content-Type": "application/json"
75
+ },
76
+ body: body != null ? JSON.stringify(body) : void 0
77
+ });
78
+ if (response.status === 401 && retry) {
79
+ clearTokenCache();
80
+ return this.request(method, url, body, false);
81
+ }
82
+ if (!response.ok) {
83
+ const errorBody = await response.text().catch(() => "");
84
+ throw new Error(`API error: HTTP ${response.status} ${response.statusText}${errorBody ? ` \u2014 ${errorBody}` : ""}`);
85
+ }
86
+ const text = await response.text();
87
+ if (!text) return void 0;
88
+ return JSON.parse(text);
89
+ }
90
+ async get(path, params) {
91
+ let url = this.orgPath(path);
92
+ if (params) {
93
+ const searchParams = new URLSearchParams();
94
+ for (const [key, value] of Object.entries(params)) {
95
+ if (value != null) searchParams.set(key, String(value));
96
+ }
97
+ const qs = searchParams.toString();
98
+ if (qs) url += `?${qs}`;
99
+ }
100
+ return this.request("GET", url);
101
+ }
102
+ async post(path, body) {
103
+ return this.request("POST", this.orgPath(path), body);
104
+ }
105
+ async patch(path, body) {
106
+ return this.request("PATCH", this.orgPath(path), body);
107
+ }
108
+ async delete(path) {
109
+ return this.request("DELETE", this.orgPath(path));
110
+ }
111
+ };
112
+ }
113
+ });
114
+
115
+ // src/cli/utils/credentials.ts
116
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
117
+ import { homedir } from "os";
118
+ import { join } from "path";
119
+ function loadCredentials() {
120
+ const envClientId = process.env.SMOOAI_CLIENT_ID;
121
+ const envClientSecret = process.env.SMOOAI_CLIENT_SECRET;
122
+ const envOrgId = process.env.SMOOAI_ORG_ID;
123
+ if (envClientId && envClientSecret && envOrgId) {
124
+ return {
125
+ clientId: envClientId,
126
+ clientSecret: envClientSecret,
127
+ orgId: envOrgId,
128
+ apiUrl: process.env.SMOOAI_API_URL || DEFAULT_API_URL,
129
+ authUrl: process.env.SMOOAI_AUTH_URL || DEFAULT_AUTH_URL
130
+ };
131
+ }
132
+ try {
133
+ if (!existsSync(CREDENTIALS_FILE)) return null;
134
+ const raw = readFileSync(CREDENTIALS_FILE, "utf-8");
135
+ const parsed = JSON.parse(raw);
136
+ if (!parsed.clientId || !parsed.clientSecret || !parsed.orgId) return null;
137
+ return {
138
+ clientId: parsed.clientId,
139
+ clientSecret: parsed.clientSecret,
140
+ orgId: parsed.orgId,
141
+ apiUrl: parsed.apiUrl || DEFAULT_API_URL,
142
+ authUrl: parsed.authUrl || DEFAULT_AUTH_URL
143
+ };
144
+ } catch {
145
+ return null;
146
+ }
147
+ }
148
+ function saveCredentials(credentials) {
149
+ mkdirSync(SMOOAI_DIR, { recursive: true });
150
+ let existing = {};
151
+ try {
152
+ if (existsSync(CREDENTIALS_FILE)) {
153
+ existing = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
154
+ }
155
+ } catch {
156
+ }
157
+ const merged = { ...existing, ...credentials };
158
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify(merged, null, 2), { mode: 384 });
159
+ }
160
+ function clearCredentials() {
161
+ if (!existsSync(CREDENTIALS_FILE)) return;
162
+ try {
163
+ const existing = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
164
+ delete existing.clientId;
165
+ delete existing.clientSecret;
166
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify(existing, null, 2), { mode: 384 });
167
+ } catch {
168
+ }
169
+ }
170
+ function getCredentialsOrExit() {
171
+ const creds = loadCredentials();
172
+ if (!creds) {
173
+ console.error("Not logged in. Run `smooai-testing login` first, or set SMOOAI_CLIENT_ID, SMOOAI_CLIENT_SECRET, and SMOOAI_ORG_ID env vars.");
174
+ process.exit(1);
175
+ }
176
+ return creds;
177
+ }
178
+ var SMOOAI_DIR, CREDENTIALS_FILE, DEFAULT_API_URL, DEFAULT_AUTH_URL;
179
+ var init_credentials = __esm({
180
+ "src/cli/utils/credentials.ts"() {
181
+ "use strict";
182
+ SMOOAI_DIR = join(homedir(), ".smooai");
183
+ CREDENTIALS_FILE = join(SMOOAI_DIR, "credentials.json");
184
+ DEFAULT_API_URL = "https://api.production.smoo.ai";
185
+ DEFAULT_AUTH_URL = "https://auth.production.smoo.ai/token";
186
+ }
187
+ });
188
+
189
+ // src/cli/utils/output.ts
190
+ function isInteractive(jsonFlag) {
191
+ if (jsonFlag) return false;
192
+ return Boolean(process.stdout.isTTY);
193
+ }
194
+ function jsonOutput(data, exitCode = 0) {
195
+ console.log(JSON.stringify(data, null, 2));
196
+ process.exit(exitCode);
197
+ }
198
+ function errorOutput(message, details) {
199
+ if (isInteractive()) {
200
+ console.error(`Error: ${message}`);
201
+ if (details) console.error(details);
202
+ process.exit(1);
203
+ }
204
+ jsonOutput({ success: false, error: message, details }, 1);
205
+ }
206
+ var init_output = __esm({
207
+ "src/cli/utils/output.ts"() {
208
+ "use strict";
209
+ }
210
+ });
211
+
212
+ // src/cli/commands/cases/create.ts
213
+ var create_exports = {};
214
+ __export(create_exports, {
215
+ runCreate: () => runCreate
216
+ });
217
+ async function runCreate(options) {
218
+ try {
219
+ const creds = getCredentialsOrExit();
220
+ const client = new ApiClient(creds);
221
+ const body = { title: options.title };
222
+ if (options.description) body.description = options.description;
223
+ if (options.priority) body.priority = options.priority;
224
+ if (options.automationStatus) body.automationStatus = options.automationStatus;
225
+ if (options.tags) body.tags = options.tags.split(",").map((t) => t.trim());
226
+ const tc = await client.post("/testing/cases", body);
227
+ if (!isInteractive(options.json)) {
228
+ jsonOutput(tc);
229
+ }
230
+ console.log(`Created test case: ${tc.id}`);
231
+ console.log(` Title: ${tc.title}`);
232
+ console.log(` Priority: ${tc.priority ?? "N/A"}`);
233
+ } catch (err) {
234
+ errorOutput(err instanceof Error ? err.message : String(err));
235
+ }
236
+ }
237
+ var init_create = __esm({
238
+ "src/cli/commands/cases/create.ts"() {
239
+ "use strict";
240
+ init_api_client();
241
+ init_credentials();
242
+ init_output();
243
+ }
244
+ });
245
+
246
+ // src/cli/commands/cases/list.ts
247
+ var list_exports = {};
248
+ __export(list_exports, {
249
+ runList: () => runList
250
+ });
251
+ async function runList(options) {
252
+ try {
253
+ const creds = getCredentialsOrExit();
254
+ const client = new ApiClient(creds);
255
+ const result = await client.get("/testing/cases", {
256
+ tags: options.tags,
257
+ priority: options.priority,
258
+ automationStatus: options.automationStatus,
259
+ limit: options.limit,
260
+ offset: options.offset
261
+ });
262
+ if (!isInteractive(options.json)) {
263
+ jsonOutput(result);
264
+ }
265
+ console.log(`Test Cases (${result.data.length} of ${result.pagination.total}):
266
+ `);
267
+ for (const tc of result.data) {
268
+ const tags = tc.tags?.length ? ` [${tc.tags.join(", ")}]` : "";
269
+ console.log(` ${tc.id} ${(tc.priority ?? "-").padEnd(8)} ${tc.title}${tags}`);
270
+ }
271
+ if (result.pagination.hasMore) {
272
+ console.log(`
273
+ ... ${result.pagination.total - result.data.length} more. Use --offset to paginate.`);
274
+ }
275
+ } catch (err) {
276
+ errorOutput(err instanceof Error ? err.message : String(err));
277
+ }
278
+ }
279
+ var init_list = __esm({
280
+ "src/cli/commands/cases/list.ts"() {
281
+ "use strict";
282
+ init_api_client();
283
+ init_credentials();
284
+ init_output();
285
+ }
286
+ });
287
+
288
+ // src/cli/commands/cases/get.ts
289
+ var get_exports = {};
290
+ __export(get_exports, {
291
+ runGet: () => runGet
292
+ });
293
+ async function runGet(id, options) {
294
+ try {
295
+ const creds = getCredentialsOrExit();
296
+ const client = new ApiClient(creds);
297
+ const tc = await client.get(`/testing/cases/${id}`);
298
+ if (!isInteractive(options.json)) {
299
+ jsonOutput(tc);
300
+ }
301
+ console.log(`Test Case: ${tc.id}`);
302
+ console.log(` Title: ${tc.title}`);
303
+ console.log(` Priority: ${tc.priority ?? "N/A"}`);
304
+ console.log(` Automation: ${tc.automationStatus ?? "N/A"}`);
305
+ if (tc.description) console.log(` Description: ${tc.description}`);
306
+ if (tc.tags?.length) console.log(` Tags: ${tc.tags.join(", ")}`);
307
+ console.log(` Created: ${tc.createdAt}`);
308
+ } catch (err) {
309
+ errorOutput(err instanceof Error ? err.message : String(err));
310
+ }
311
+ }
312
+ var init_get = __esm({
313
+ "src/cli/commands/cases/get.ts"() {
314
+ "use strict";
315
+ init_api_client();
316
+ init_credentials();
317
+ init_output();
318
+ }
319
+ });
320
+
321
+ // src/cli/commands/cases/update.ts
322
+ var update_exports = {};
323
+ __export(update_exports, {
324
+ runUpdate: () => runUpdate
325
+ });
326
+ async function runUpdate(id, options) {
327
+ try {
328
+ const creds = getCredentialsOrExit();
329
+ const client = new ApiClient(creds);
330
+ const body = {};
331
+ if (options.title) body.title = options.title;
332
+ if (options.description) body.description = options.description;
333
+ if (options.priority) body.priority = options.priority;
334
+ if (options.automationStatus) body.automationStatus = options.automationStatus;
335
+ if (options.tags) body.tags = options.tags.split(",").map((t) => t.trim());
336
+ const tc = await client.patch(`/testing/cases/${id}`, body);
337
+ if (!isInteractive(options.json)) {
338
+ jsonOutput(tc);
339
+ }
340
+ console.log(`Updated test case: ${tc.id}`);
341
+ console.log(` Title: ${tc.title}`);
342
+ } catch (err) {
343
+ errorOutput(err instanceof Error ? err.message : String(err));
344
+ }
345
+ }
346
+ var init_update = __esm({
347
+ "src/cli/commands/cases/update.ts"() {
348
+ "use strict";
349
+ init_api_client();
350
+ init_credentials();
351
+ init_output();
352
+ }
353
+ });
354
+
355
+ // src/cli/commands/cases/delete.ts
356
+ var delete_exports = {};
357
+ __export(delete_exports, {
358
+ runDelete: () => runDelete
359
+ });
360
+ async function runDelete(id, options) {
361
+ try {
362
+ const creds = getCredentialsOrExit();
363
+ const client = new ApiClient(creds);
364
+ await client.delete(`/testing/cases/${id}`);
365
+ if (!isInteractive(options.json)) {
366
+ jsonOutput({ success: true, id });
367
+ }
368
+ console.log(`Deleted test case: ${id}`);
369
+ } catch (err) {
370
+ errorOutput(err instanceof Error ? err.message : String(err));
371
+ }
372
+ }
373
+ var init_delete = __esm({
374
+ "src/cli/commands/cases/delete.ts"() {
375
+ "use strict";
376
+ init_api_client();
377
+ init_credentials();
378
+ init_output();
379
+ }
380
+ });
381
+
382
+ // src/cli/commands/deployments/create.ts
383
+ var create_exports2 = {};
384
+ __export(create_exports2, {
385
+ runCreate: () => runCreate2
386
+ });
387
+ async function runCreate2(options) {
388
+ try {
389
+ const creds = getCredentialsOrExit();
390
+ const client = new ApiClient(creds);
391
+ const body = { name: options.name };
392
+ if (options.environmentId) body.environmentId = options.environmentId;
393
+ if (options.status) body.status = options.status;
394
+ if (options.source) body.source = options.source;
395
+ if (options.externalId) body.externalId = options.externalId;
396
+ if (options.externalUrl) body.externalUrl = options.externalUrl;
397
+ if (options.ref) body.ref = options.ref;
398
+ const deployment = await client.post("/testing/deployments", body);
399
+ if (!isInteractive(options.json)) {
400
+ jsonOutput(deployment);
401
+ }
402
+ console.log(`Created deployment: ${deployment.id}`);
403
+ console.log(` Name: ${deployment.name}`);
404
+ console.log(` Status: ${deployment.status}`);
405
+ } catch (err) {
406
+ errorOutput(err instanceof Error ? err.message : String(err));
407
+ }
408
+ }
409
+ var init_create2 = __esm({
410
+ "src/cli/commands/deployments/create.ts"() {
411
+ "use strict";
412
+ init_api_client();
413
+ init_credentials();
414
+ init_output();
415
+ }
416
+ });
417
+
418
+ // src/cli/commands/deployments/list.ts
419
+ var list_exports2 = {};
420
+ __export(list_exports2, {
421
+ runList: () => runList2
422
+ });
423
+ async function runList2(options) {
424
+ try {
425
+ const creds = getCredentialsOrExit();
426
+ const client = new ApiClient(creds);
427
+ const result = await client.get("/testing/deployments", {
428
+ status: options.status,
429
+ environmentId: options.environmentId,
430
+ source: options.source,
431
+ limit: options.limit,
432
+ offset: options.offset
433
+ });
434
+ if (!isInteractive(options.json)) {
435
+ jsonOutput(result);
436
+ }
437
+ console.log(`Deployments (${result.data.length} of ${result.pagination.total}):
438
+ `);
439
+ for (const d of result.data) {
440
+ const ref = d.ref ? ` (${d.ref})` : "";
441
+ console.log(` ${d.id} ${d.status.padEnd(12)} ${d.name}${ref}`);
442
+ }
443
+ if (result.pagination.hasMore) {
444
+ console.log(`
445
+ ... ${result.pagination.total - result.data.length} more. Use --offset to paginate.`);
446
+ }
447
+ } catch (err) {
448
+ errorOutput(err instanceof Error ? err.message : String(err));
449
+ }
450
+ }
451
+ var init_list2 = __esm({
452
+ "src/cli/commands/deployments/list.ts"() {
453
+ "use strict";
454
+ init_api_client();
455
+ init_credentials();
456
+ init_output();
457
+ }
458
+ });
459
+
460
+ // src/cli/commands/deployments/get.ts
461
+ var get_exports2 = {};
462
+ __export(get_exports2, {
463
+ runGet: () => runGet2
464
+ });
465
+ async function runGet2(id, options) {
466
+ try {
467
+ const creds = getCredentialsOrExit();
468
+ const client = new ApiClient(creds);
469
+ const deployment = await client.get(`/testing/deployments/${id}`);
470
+ if (!isInteractive(options.json)) {
471
+ jsonOutput(deployment);
472
+ }
473
+ console.log(`Deployment: ${deployment.id}`);
474
+ console.log(` Name: ${deployment.name}`);
475
+ console.log(` Status: ${deployment.status}`);
476
+ console.log(` Source: ${deployment.source ?? "N/A"}`);
477
+ console.log(` Ref: ${deployment.ref ?? "N/A"}`);
478
+ console.log(` External URL: ${deployment.externalUrl ?? "N/A"}`);
479
+ console.log(` Created: ${deployment.createdAt}`);
480
+ if (deployment.completedAt) {
481
+ console.log(` Completed: ${deployment.completedAt}`);
482
+ }
483
+ } catch (err) {
484
+ errorOutput(err instanceof Error ? err.message : String(err));
485
+ }
486
+ }
487
+ var init_get2 = __esm({
488
+ "src/cli/commands/deployments/get.ts"() {
489
+ "use strict";
490
+ init_api_client();
491
+ init_credentials();
492
+ init_output();
493
+ }
494
+ });
495
+
496
+ // src/cli/commands/deployments/update.ts
497
+ var update_exports2 = {};
498
+ __export(update_exports2, {
499
+ runUpdate: () => runUpdate2
500
+ });
501
+ async function runUpdate2(id, options) {
502
+ try {
503
+ const creds = getCredentialsOrExit();
504
+ const client = new ApiClient(creds);
505
+ const body = {};
506
+ if (options.name) body.name = options.name;
507
+ if (options.status) body.status = options.status;
508
+ if (options.externalUrl) body.externalUrl = options.externalUrl;
509
+ const deployment = await client.patch(`/testing/deployments/${id}`, body);
510
+ if (!isInteractive(options.json)) {
511
+ jsonOutput(deployment);
512
+ }
513
+ console.log(`Updated deployment: ${deployment.id}`);
514
+ console.log(` Status: ${deployment.status}`);
515
+ } catch (err) {
516
+ errorOutput(err instanceof Error ? err.message : String(err));
517
+ }
518
+ }
519
+ var init_update2 = __esm({
520
+ "src/cli/commands/deployments/update.ts"() {
521
+ "use strict";
522
+ init_api_client();
523
+ init_credentials();
524
+ init_output();
525
+ }
526
+ });
527
+
528
+ // src/cli/commands/deployments/delete.ts
529
+ var delete_exports2 = {};
530
+ __export(delete_exports2, {
531
+ runDelete: () => runDelete2
532
+ });
533
+ async function runDelete2(id, options) {
534
+ try {
535
+ const creds = getCredentialsOrExit();
536
+ const client = new ApiClient(creds);
537
+ await client.delete(`/testing/deployments/${id}`);
538
+ if (!isInteractive(options.json)) {
539
+ jsonOutput({ success: true, id });
540
+ }
541
+ console.log(`Deleted deployment: ${id}`);
542
+ } catch (err) {
543
+ errorOutput(err instanceof Error ? err.message : String(err));
544
+ }
545
+ }
546
+ var init_delete2 = __esm({
547
+ "src/cli/commands/deployments/delete.ts"() {
548
+ "use strict";
549
+ init_api_client();
550
+ init_credentials();
551
+ init_output();
552
+ }
553
+ });
554
+
555
+ // src/cli/commands/environments/create.ts
556
+ var create_exports3 = {};
557
+ __export(create_exports3, {
558
+ runCreate: () => runCreate3
559
+ });
560
+ async function runCreate3(options) {
561
+ try {
562
+ const creds = getCredentialsOrExit();
563
+ const client = new ApiClient(creds);
564
+ const body = { name: options.name };
565
+ if (options.description) body.description = options.description;
566
+ if (options.baseUrl) body.baseUrl = options.baseUrl;
567
+ const env = await client.post("/testing/environments", body);
568
+ if (!isInteractive(options.json)) {
569
+ jsonOutput(env);
570
+ }
571
+ console.log(`Created environment: ${env.id}`);
572
+ console.log(` Name: ${env.name}`);
573
+ } catch (err) {
574
+ errorOutput(err instanceof Error ? err.message : String(err));
575
+ }
576
+ }
577
+ var init_create3 = __esm({
578
+ "src/cli/commands/environments/create.ts"() {
579
+ "use strict";
580
+ init_api_client();
581
+ init_credentials();
582
+ init_output();
583
+ }
584
+ });
585
+
586
+ // src/cli/commands/environments/list.ts
587
+ var list_exports3 = {};
588
+ __export(list_exports3, {
589
+ runList: () => runList3
590
+ });
591
+ async function runList3(options) {
592
+ try {
593
+ const creds = getCredentialsOrExit();
594
+ const client = new ApiClient(creds);
595
+ const envs = await client.get("/testing/environments");
596
+ if (!isInteractive(options.json)) {
597
+ jsonOutput(envs);
598
+ }
599
+ console.log(`Test Environments (${envs.length}):
600
+ `);
601
+ for (const env of envs) {
602
+ const url = env.baseUrl ? ` (${env.baseUrl})` : "";
603
+ console.log(` ${env.id} ${env.name}${url}`);
604
+ }
605
+ } catch (err) {
606
+ errorOutput(err instanceof Error ? err.message : String(err));
607
+ }
608
+ }
609
+ var init_list3 = __esm({
610
+ "src/cli/commands/environments/list.ts"() {
611
+ "use strict";
612
+ init_api_client();
613
+ init_credentials();
614
+ init_output();
615
+ }
616
+ });
617
+
618
+ // src/cli/commands/environments/get.ts
619
+ var get_exports3 = {};
620
+ __export(get_exports3, {
621
+ runGet: () => runGet3
622
+ });
623
+ async function runGet3(id, options) {
624
+ try {
625
+ const creds = getCredentialsOrExit();
626
+ const client = new ApiClient(creds);
627
+ const env = await client.get(`/testing/environments/${id}`);
628
+ if (!isInteractive(options.json)) {
629
+ jsonOutput(env);
630
+ }
631
+ console.log(`Environment: ${env.id}`);
632
+ console.log(` Name: ${env.name}`);
633
+ console.log(` Description: ${env.description ?? "N/A"}`);
634
+ console.log(` Base URL: ${env.baseUrl ?? "N/A"}`);
635
+ console.log(` Created: ${env.createdAt}`);
636
+ } catch (err) {
637
+ errorOutput(err instanceof Error ? err.message : String(err));
638
+ }
639
+ }
640
+ var init_get3 = __esm({
641
+ "src/cli/commands/environments/get.ts"() {
642
+ "use strict";
643
+ init_api_client();
644
+ init_credentials();
645
+ init_output();
646
+ }
647
+ });
648
+
649
+ // src/cli/commands/environments/update.ts
650
+ var update_exports3 = {};
651
+ __export(update_exports3, {
652
+ runUpdate: () => runUpdate3
653
+ });
654
+ async function runUpdate3(id, options) {
655
+ try {
656
+ const creds = getCredentialsOrExit();
657
+ const client = new ApiClient(creds);
658
+ const body = {};
659
+ if (options.name) body.name = options.name;
660
+ if (options.description) body.description = options.description;
661
+ if (options.baseUrl) body.baseUrl = options.baseUrl;
662
+ const env = await client.patch(`/testing/environments/${id}`, body);
663
+ if (!isInteractive(options.json)) {
664
+ jsonOutput(env);
665
+ }
666
+ console.log(`Updated environment: ${env.id}`);
667
+ console.log(` Name: ${env.name}`);
668
+ } catch (err) {
669
+ errorOutput(err instanceof Error ? err.message : String(err));
670
+ }
671
+ }
672
+ var init_update3 = __esm({
673
+ "src/cli/commands/environments/update.ts"() {
674
+ "use strict";
675
+ init_api_client();
676
+ init_credentials();
677
+ init_output();
678
+ }
679
+ });
680
+
681
+ // src/cli/commands/runs/create.ts
682
+ var create_exports4 = {};
683
+ __export(create_exports4, {
684
+ runCreate: () => runCreate4
685
+ });
686
+ async function runCreate4(options) {
687
+ try {
688
+ const creds = getCredentialsOrExit();
689
+ const client = new ApiClient(creds);
690
+ const body = { name: options.name };
691
+ if (options.environment) body.environment = options.environment;
692
+ if (options.environmentId) body.environmentId = options.environmentId;
693
+ if (options.deploymentId) body.deploymentId = options.deploymentId;
694
+ if (options.tool) body.tool = options.tool;
695
+ if (options.runnerName) body.runnerName = options.runnerName;
696
+ if (options.runnerUrl) body.runnerUrl = options.runnerUrl;
697
+ const run = await client.post("/testing/runs", body);
698
+ if (!isInteractive(options.json)) {
699
+ jsonOutput(run);
700
+ }
701
+ console.log(`Created test run: ${run.id}`);
702
+ console.log(` Name: ${run.name}`);
703
+ console.log(` Status: ${run.status}`);
704
+ } catch (err) {
705
+ errorOutput(err instanceof Error ? err.message : String(err));
706
+ }
707
+ }
708
+ var init_create4 = __esm({
709
+ "src/cli/commands/runs/create.ts"() {
710
+ "use strict";
711
+ init_api_client();
712
+ init_credentials();
713
+ init_output();
714
+ }
715
+ });
716
+
717
+ // src/cli/commands/runs/list.ts
718
+ var list_exports4 = {};
719
+ __export(list_exports4, {
720
+ runList: () => runList4
721
+ });
722
+ async function runList4(options) {
723
+ try {
724
+ const creds = getCredentialsOrExit();
725
+ const client = new ApiClient(creds);
726
+ const result = await client.get("/testing/runs", {
727
+ status: options.status,
728
+ environmentId: options.environmentId,
729
+ tool: options.tool,
730
+ limit: options.limit,
731
+ offset: options.offset
732
+ });
733
+ if (!isInteractive(options.json)) {
734
+ jsonOutput(result);
735
+ }
736
+ console.log(`Test Runs (${result.data.length} of ${result.pagination.total}):
737
+ `);
738
+ for (const run of result.data) {
739
+ const summary = run.summary ? ` [${run.summary.passed ?? 0}P/${run.summary.failed ?? 0}F/${run.summary.skipped ?? 0}S]` : "";
740
+ console.log(` ${run.id} ${run.status.padEnd(8)} ${run.name}${summary}`);
741
+ }
742
+ if (result.pagination.hasMore) {
743
+ console.log(`
744
+ ... ${result.pagination.total - result.data.length} more. Use --offset to paginate.`);
745
+ }
746
+ } catch (err) {
747
+ errorOutput(err instanceof Error ? err.message : String(err));
748
+ }
749
+ }
750
+ var init_list4 = __esm({
751
+ "src/cli/commands/runs/list.ts"() {
752
+ "use strict";
753
+ init_api_client();
754
+ init_credentials();
755
+ init_output();
756
+ }
757
+ });
758
+
759
+ // src/cli/commands/runs/get.ts
760
+ var get_exports4 = {};
761
+ __export(get_exports4, {
762
+ runGet: () => runGet4
763
+ });
764
+ async function runGet4(id, options) {
765
+ try {
766
+ const creds = getCredentialsOrExit();
767
+ const client = new ApiClient(creds);
768
+ const run = await client.get(`/testing/runs/${id}`);
769
+ if (!isInteractive(options.json)) {
770
+ jsonOutput(run);
771
+ }
772
+ console.log(`Test Run: ${run.id}`);
773
+ console.log(` Name: ${run.name}`);
774
+ console.log(` Status: ${run.status}`);
775
+ console.log(` Tool: ${run.tool ?? "N/A"}`);
776
+ if (run.summary) {
777
+ console.log(` Summary: ${run.summary.passed ?? 0} passed, ${run.summary.failed ?? 0} failed, ${run.summary.skipped ?? 0} skipped`);
778
+ }
779
+ if (run.durationMs) {
780
+ console.log(` Duration: ${(run.durationMs / 1e3).toFixed(1)}s`);
781
+ }
782
+ console.log(` Created: ${run.createdAt}`);
783
+ if (run.completedAt) {
784
+ console.log(` Completed: ${run.completedAt}`);
785
+ }
786
+ if (run.results && run.results.length > 0) {
787
+ console.log(` Results: ${run.results.length} test(s)`);
788
+ }
789
+ } catch (err) {
790
+ errorOutput(err instanceof Error ? err.message : String(err));
791
+ }
792
+ }
793
+ var init_get4 = __esm({
794
+ "src/cli/commands/runs/get.ts"() {
795
+ "use strict";
796
+ init_api_client();
797
+ init_credentials();
798
+ init_output();
799
+ }
800
+ });
801
+
802
+ // src/cli/commands/runs/update.ts
803
+ var update_exports4 = {};
804
+ __export(update_exports4, {
805
+ runUpdate: () => runUpdate4
806
+ });
807
+ async function runUpdate4(id, options) {
808
+ try {
809
+ const creds = getCredentialsOrExit();
810
+ const client = new ApiClient(creds);
811
+ const body = {};
812
+ if (options.status) body.status = options.status;
813
+ const run = await client.patch(`/testing/runs/${id}`, body);
814
+ if (!isInteractive(options.json)) {
815
+ jsonOutput(run);
816
+ }
817
+ console.log(`Updated test run: ${run.id}`);
818
+ console.log(` Status: ${run.status}`);
819
+ } catch (err) {
820
+ errorOutput(err instanceof Error ? err.message : String(err));
821
+ }
822
+ }
823
+ var init_update4 = __esm({
824
+ "src/cli/commands/runs/update.ts"() {
825
+ "use strict";
826
+ init_api_client();
827
+ init_credentials();
828
+ init_output();
829
+ }
830
+ });
831
+
832
+ // src/cli/components/Banner.tsx
833
+ import { Box, Text } from "ink";
834
+ import BigText from "ink-big-text";
835
+ import Gradient from "ink-gradient";
836
+ import { jsx, jsxs } from "react/jsx-runtime";
837
+ function Banner({ title }) {
838
+ return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [
839
+ /* @__PURE__ */ jsx(Gradient, { colors: ["#f49f0a", "#ff6b6c"], children: /* @__PURE__ */ jsx(BigText, { text: "Smoo AI", font: "tiny" }) }),
840
+ /* @__PURE__ */ jsx(Text, { bold: true, children: title })
841
+ ] });
842
+ }
843
+ var init_Banner = __esm({
844
+ "src/cli/components/Banner.tsx"() {
845
+ "use strict";
846
+ }
847
+ });
848
+
849
+ // src/cli/components/TaskList.tsx
850
+ import { Box as Box2, Text as Text2 } from "ink";
851
+ import Spinner from "ink-spinner";
852
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
853
+ function TaskList({ tasks }) {
854
+ return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: tasks.map((task, i) => /* @__PURE__ */ jsxs2(Box2, { children: [
855
+ /* @__PURE__ */ jsxs2(Box2, { width: 3, children: [
856
+ task.status === "running" && /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
857
+ task.status === "done" && /* @__PURE__ */ jsx2(Text2, { color: "green", children: "\u2713" }),
858
+ task.status === "error" && /* @__PURE__ */ jsx2(Text2, { color: "red", children: "\u2717" }),
859
+ task.status === "pending" && /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "\u25CB" })
860
+ ] }),
861
+ /* @__PURE__ */ jsx2(Text2, { color: task.status === "error" ? "red" : task.status === "done" ? "green" : void 0, children: task.label }),
862
+ task.error && /* @__PURE__ */ jsxs2(Text2, { color: "red", children: [
863
+ " \u2014 ",
864
+ task.error
865
+ ] })
866
+ ] }, i)) });
867
+ }
868
+ var init_TaskList = __esm({
869
+ "src/cli/components/TaskList.tsx"() {
870
+ "use strict";
871
+ }
872
+ });
873
+
874
+ // src/cli/utils/ctrf.ts
875
+ import { readFileSync as readFileSync2 } from "fs";
876
+ import { z } from "zod";
877
+ function parseCtrfFile(filePath) {
878
+ let raw;
879
+ try {
880
+ raw = readFileSync2(filePath, "utf-8");
881
+ } catch (err) {
882
+ throw new Error(`Failed to read CTRF file: ${filePath} \u2014 ${err instanceof Error ? err.message : String(err)}`);
883
+ }
884
+ let parsed;
885
+ try {
886
+ parsed = JSON.parse(raw);
887
+ } catch {
888
+ throw new Error(`Invalid JSON in CTRF file: ${filePath}`);
889
+ }
890
+ const result = CtrfReportSchema.safeParse(parsed);
891
+ if (!result.success) {
892
+ const issues = result.error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
893
+ throw new Error(`Invalid CTRF format:
894
+ ${issues}`);
895
+ }
896
+ return result.data;
897
+ }
898
+ function summarizeCtrfResults(report) {
899
+ const summary = report.results.summary;
900
+ if (summary) {
901
+ const total = summary.tests ?? 0;
902
+ const passed = summary.passed ?? 0;
903
+ const failed = summary.failed ?? 0;
904
+ const skipped = summary.skipped ?? 0;
905
+ const pending = summary.pending ?? 0;
906
+ const other = summary.other ?? 0;
907
+ return { total, passed, failed, skipped, pending, other, hasFailed: failed > 0 };
908
+ }
909
+ const tests = report.results.tests ?? [];
910
+ const counts = { total: tests.length, passed: 0, failed: 0, skipped: 0, pending: 0, other: 0 };
911
+ for (const test of tests) {
912
+ if (test.status in counts) {
913
+ counts[test.status]++;
914
+ }
915
+ }
916
+ return { ...counts, hasFailed: counts.failed > 0 };
917
+ }
918
+ var CtrfTestSchema, CtrfReportSchema;
919
+ var init_ctrf = __esm({
920
+ "src/cli/utils/ctrf.ts"() {
921
+ "use strict";
922
+ CtrfTestSchema = z.object({
923
+ name: z.string(),
924
+ status: z.enum(["passed", "failed", "skipped", "pending", "other"]),
925
+ duration: z.number().optional(),
926
+ suite: z.string().optional(),
927
+ filePath: z.string().optional(),
928
+ message: z.string().optional(),
929
+ trace: z.string().optional(),
930
+ retries: z.number().optional(),
931
+ flaky: z.boolean().optional(),
932
+ browser: z.string().optional(),
933
+ tags: z.array(z.string()).optional(),
934
+ extra: z.record(z.string(), z.unknown()).optional()
935
+ });
936
+ CtrfReportSchema = z.object({
937
+ results: z.object({
938
+ tool: z.object({
939
+ name: z.string().optional(),
940
+ version: z.string().optional()
941
+ }).optional(),
942
+ summary: z.object({
943
+ tests: z.number().optional(),
944
+ passed: z.number().optional(),
945
+ failed: z.number().optional(),
946
+ skipped: z.number().optional(),
947
+ pending: z.number().optional(),
948
+ other: z.number().optional(),
949
+ start: z.number().optional(),
950
+ stop: z.number().optional()
951
+ }).optional(),
952
+ tests: z.array(CtrfTestSchema).optional(),
953
+ environment: z.record(z.string(), z.unknown()).optional()
954
+ })
955
+ });
956
+ }
957
+ });
958
+
959
+ // src/cli/commands/runs/report.tsx
960
+ var report_exports = {};
961
+ __export(report_exports, {
962
+ reportLogic: () => reportLogic,
963
+ runReport: () => runReport
964
+ });
965
+ import { basename } from "path";
966
+ import { render, Box as Box3, Text as Text3 } from "ink";
967
+ import { useEffect, useState } from "react";
968
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
969
+ async function reportLogic(ctrfFile, options) {
970
+ const report = parseCtrfFile(ctrfFile);
971
+ const summary = summarizeCtrfResults(report);
972
+ const creds = getCredentialsOrExit();
973
+ const client = new ApiClient(creds);
974
+ const runName = options.name ?? basename(ctrfFile, ".json");
975
+ const runBody = {
976
+ name: runName,
977
+ tool: options.tool ?? report.results.tool?.name,
978
+ buildName: options.buildName ?? process.env.GITHUB_SHA
979
+ };
980
+ if (options.environment) runBody.environment = options.environment;
981
+ if (options.deploymentId) runBody.deploymentId = options.deploymentId;
982
+ if (options.buildUrl) {
983
+ runBody.buildUrl = options.buildUrl;
984
+ } else if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) {
985
+ runBody.buildUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
986
+ }
987
+ const run = await client.post("/testing/runs", runBody);
988
+ let resultCount = 0;
989
+ try {
990
+ const resultResponse = await client.post(`/testing/runs/${run.id}/results`, {
991
+ results: report.results
992
+ });
993
+ resultCount = resultResponse.count;
994
+ } catch (err) {
995
+ await client.patch(`/testing/runs/${run.id}`, {
996
+ status: "errored",
997
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
998
+ });
999
+ throw err;
1000
+ }
1001
+ const updatedRun = await client.get(`/testing/runs/${run.id}`);
1002
+ return { run: updatedRun, resultCount, summary };
1003
+ }
1004
+ function ReportUI({ ctrfFile, options }) {
1005
+ const [tasks, setTasks] = useState([
1006
+ { label: "Parsing CTRF report", status: "pending" },
1007
+ { label: "Authenticating", status: "pending" },
1008
+ { label: "Creating test run", status: "pending" },
1009
+ { label: "Submitting results", status: "pending" }
1010
+ ]);
1011
+ const [result, setResult] = useState(null);
1012
+ useEffect(() => {
1013
+ (async () => {
1014
+ try {
1015
+ setTasks((t) => t.map((task, i) => i === 0 ? { ...task, status: "running" } : task));
1016
+ const report = parseCtrfFile(ctrfFile);
1017
+ const summary = summarizeCtrfResults(report);
1018
+ setTasks((t) => t.map((task, i) => i === 0 ? { ...task, status: "done" } : task));
1019
+ setTasks((t) => t.map((task, i) => i === 1 ? { ...task, status: "running" } : task));
1020
+ const creds = getCredentialsOrExit();
1021
+ const client = new ApiClient(creds);
1022
+ setTasks((t) => t.map((task, i) => i === 1 ? { ...task, status: "done" } : task));
1023
+ setTasks((t) => t.map((task, i) => i === 2 ? { ...task, status: "running" } : task));
1024
+ const runName = options.name ?? basename(ctrfFile, ".json");
1025
+ const runBody = {
1026
+ name: runName,
1027
+ tool: options.tool ?? report.results.tool?.name,
1028
+ buildName: options.buildName ?? process.env.GITHUB_SHA
1029
+ };
1030
+ if (options.environment) runBody.environment = options.environment;
1031
+ if (options.deploymentId) runBody.deploymentId = options.deploymentId;
1032
+ if (options.buildUrl) {
1033
+ runBody.buildUrl = options.buildUrl;
1034
+ } else if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) {
1035
+ runBody.buildUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
1036
+ }
1037
+ const run = await client.post("/testing/runs", runBody);
1038
+ setTasks((t) => t.map((task, i) => i === 2 ? { ...task, status: "done" } : task));
1039
+ setTasks((t) => t.map((task, i) => i === 3 ? { ...task, status: "running" } : task));
1040
+ const resultResponse = await client.post(`/testing/runs/${run.id}/results`, {
1041
+ results: report.results
1042
+ });
1043
+ setTasks((t) => t.map((task, i) => i === 3 ? { ...task, status: "done" } : task));
1044
+ const updatedRun = await client.get(`/testing/runs/${run.id}`);
1045
+ setResult({ run: updatedRun, resultCount: resultResponse.count, summary });
1046
+ } catch (err) {
1047
+ setTasks(
1048
+ (t) => t.map((task) => task.status === "running" ? { ...task, status: "error", error: err instanceof Error ? err.message : String(err) } : task)
1049
+ );
1050
+ }
1051
+ })();
1052
+ }, []);
1053
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
1054
+ /* @__PURE__ */ jsx3(Banner, { title: "Report Test Results" }),
1055
+ /* @__PURE__ */ jsx3(TaskList, { tasks }),
1056
+ result && /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
1057
+ /* @__PURE__ */ jsxs3(Text3, { color: result.summary.hasFailed ? "red" : "green", bold: true, children: [
1058
+ result.summary.hasFailed ? "\u2717 FAILED" : "\u2713 PASSED",
1059
+ " \u2014 ",
1060
+ result.resultCount,
1061
+ " results submitted"
1062
+ ] }),
1063
+ /* @__PURE__ */ jsxs3(Text3, { children: [
1064
+ result.summary.passed,
1065
+ " passed, ",
1066
+ result.summary.failed,
1067
+ " failed, ",
1068
+ result.summary.skipped,
1069
+ " skipped"
1070
+ ] }),
1071
+ /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
1072
+ "Run ID: ",
1073
+ result.run.id
1074
+ ] })
1075
+ ] })
1076
+ ] });
1077
+ }
1078
+ function runReport(ctrfFile, options) {
1079
+ if (!isInteractive(options.json)) {
1080
+ reportLogic(ctrfFile, options).then(
1081
+ (result) => jsonOutput({
1082
+ success: true,
1083
+ runId: result.run.id,
1084
+ status: result.run.status,
1085
+ resultCount: result.resultCount,
1086
+ summary: result.summary
1087
+ }),
1088
+ (err) => {
1089
+ errorOutput(err instanceof Error ? err.message : String(err));
1090
+ }
1091
+ );
1092
+ return;
1093
+ }
1094
+ render(/* @__PURE__ */ jsx3(ReportUI, { ctrfFile, options }));
1095
+ }
1096
+ var init_report = __esm({
1097
+ "src/cli/commands/runs/report.tsx"() {
1098
+ "use strict";
1099
+ init_Banner();
1100
+ init_TaskList();
1101
+ init_api_client();
1102
+ init_credentials();
1103
+ init_ctrf();
1104
+ init_output();
1105
+ }
1106
+ });
1107
+
1108
+ // src/cli/commands/auth/login.tsx
1109
+ var login_exports = {};
1110
+ __export(login_exports, {
1111
+ loginLogic: () => loginLogic,
1112
+ runLogin: () => runLogin
1113
+ });
1114
+ import { render as render2, Box as Box4, Text as Text4 } from "ink";
1115
+ import { useEffect as useEffect2, useState as useState2 } from "react";
1116
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1117
+ async function loginLogic(options) {
1118
+ const clientId = options.clientId;
1119
+ const clientSecret = options.clientSecret;
1120
+ const orgId = options.orgId;
1121
+ const apiUrl = options.apiUrl ?? DEFAULT_API_URL2;
1122
+ const authUrl = options.authUrl ?? DEFAULT_AUTH_URL2;
1123
+ if (!clientId) throw new Error("Client ID is required. Use --client-id flag.");
1124
+ if (!clientSecret) throw new Error("Client secret is required. Use --client-secret flag.");
1125
+ if (!orgId) throw new Error("Organization ID is required. Use --org-id flag.");
1126
+ const credentials = { clientId, clientSecret, orgId, apiUrl, authUrl };
1127
+ const client = new ApiClient(credentials);
1128
+ await client.get("/testing/environments");
1129
+ saveCredentials(credentials);
1130
+ return { success: true, orgId };
1131
+ }
1132
+ function LoginUI({ options }) {
1133
+ const [tasks, setTasks] = useState2([
1134
+ { label: "Validating credentials", status: "pending" },
1135
+ { label: "Saving to ~/.smooai/credentials.json", status: "pending" }
1136
+ ]);
1137
+ const [result, setResult] = useState2(null);
1138
+ useEffect2(() => {
1139
+ (async () => {
1140
+ setTasks((t) => t.map((task, i) => i === 0 ? { ...task, status: "running" } : task));
1141
+ try {
1142
+ const res = await loginLogic(options);
1143
+ setTasks([
1144
+ { label: "Validating credentials", status: "done" },
1145
+ { label: "Saving to ~/.smooai/credentials.json", status: "done" }
1146
+ ]);
1147
+ setResult({ orgId: res.orgId });
1148
+ } catch (err) {
1149
+ setTasks(
1150
+ (t) => t.map((task) => task.status === "running" ? { ...task, status: "error", error: err instanceof Error ? err.message : String(err) } : task)
1151
+ );
1152
+ }
1153
+ })();
1154
+ }, []);
1155
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
1156
+ /* @__PURE__ */ jsx4(Banner, { title: "Login" }),
1157
+ /* @__PURE__ */ jsx4(TaskList, { tasks }),
1158
+ result && /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsxs4(Text4, { color: "green", bold: true, children: [
1159
+ "Logged in successfully! Organization: ",
1160
+ result.orgId
1161
+ ] }) })
1162
+ ] });
1163
+ }
1164
+ function runLogin(options) {
1165
+ if (!isInteractive(options.json)) {
1166
+ loginLogic(options).then(
1167
+ (result) => jsonOutput(result),
1168
+ (err) => jsonOutput({ success: false, error: err.message }, 1)
1169
+ );
1170
+ return;
1171
+ }
1172
+ render2(/* @__PURE__ */ jsx4(LoginUI, { options }));
1173
+ }
1174
+ var DEFAULT_API_URL2, DEFAULT_AUTH_URL2;
1175
+ var init_login = __esm({
1176
+ "src/cli/commands/auth/login.tsx"() {
1177
+ "use strict";
1178
+ init_Banner();
1179
+ init_TaskList();
1180
+ init_api_client();
1181
+ init_credentials();
1182
+ init_output();
1183
+ DEFAULT_API_URL2 = "https://api.production.smoo.ai";
1184
+ DEFAULT_AUTH_URL2 = "https://auth.production.smoo.ai/token";
1185
+ }
1186
+ });
1187
+
1188
+ // src/cli/commands/auth/logout.ts
1189
+ var logout_exports = {};
1190
+ __export(logout_exports, {
1191
+ runLogout: () => runLogout
1192
+ });
1193
+ function runLogout(options) {
1194
+ clearCredentials();
1195
+ if (!isInteractive(options.json)) {
1196
+ jsonOutput({ success: true, message: "Logged out" });
1197
+ }
1198
+ console.log("Logged out. M2M credentials removed from ~/.smooai/credentials.json");
1199
+ }
1200
+ var init_logout = __esm({
1201
+ "src/cli/commands/auth/logout.ts"() {
1202
+ "use strict";
1203
+ init_credentials();
1204
+ init_output();
1205
+ }
1206
+ });
1207
+
1208
+ // src/cli/commands/auth/status.ts
1209
+ var status_exports = {};
1210
+ __export(status_exports, {
1211
+ runStatus: () => runStatus
1212
+ });
1213
+ function runStatus(options) {
1214
+ const creds = loadCredentials();
1215
+ if (!isInteractive(options.json)) {
1216
+ jsonOutput({
1217
+ loggedIn: !!creds,
1218
+ orgId: creds?.orgId ?? null,
1219
+ apiUrl: creds?.apiUrl ?? null,
1220
+ authUrl: creds?.authUrl ?? null
1221
+ });
1222
+ }
1223
+ if (!creds) {
1224
+ console.log("Not logged in. Run `smooai-testing login` to authenticate.");
1225
+ return;
1226
+ }
1227
+ console.log(`Logged in`);
1228
+ console.log(` Organization: ${creds.orgId}`);
1229
+ console.log(` API URL: ${creds.apiUrl}`);
1230
+ console.log(` Auth URL: ${creds.authUrl}`);
1231
+ console.log(` Client ID: ${creds.clientId.slice(0, 8)}...`);
1232
+ }
1233
+ var init_status = __esm({
1234
+ "src/cli/commands/auth/status.ts"() {
1235
+ "use strict";
1236
+ init_credentials();
1237
+ init_output();
1238
+ }
1239
+ });
1240
+
1241
+ // src/cli/index.ts
1242
+ import { Command } from "commander";
1243
+
1244
+ // src/cli/commands/cases/index.ts
1245
+ function createCasesCommand(program2) {
1246
+ const cases = program2.command("cases").description("Manage test cases");
1247
+ cases.command("create").description("Create a test case").requiredOption("--title <title>", "Test case title").option("--description <desc>", "Description").option("--priority <priority>", "Priority (e.g., critical, high, medium, low)").option("--automation-status <status>", "Automation status").option("--tags <tags>", "Comma-separated tags").action(async (opts) => {
1248
+ const { runCreate: runCreate5 } = await Promise.resolve().then(() => (init_create(), create_exports));
1249
+ runCreate5({ ...opts, json: program2.opts().json ?? opts.json });
1250
+ });
1251
+ cases.command("list").description("List test cases").option("--tags <tags>", "Filter by comma-separated tags").option("--priority <priority>", "Filter by priority").option("--automation-status <status>", "Filter by automation status").option("--limit <n>", "Max results", "50").option("--offset <n>", "Offset", "0").action(async (opts) => {
1252
+ const { runList: runList5 } = await Promise.resolve().then(() => (init_list(), list_exports));
1253
+ runList5({ ...opts, json: program2.opts().json ?? opts.json });
1254
+ });
1255
+ cases.command("get").description("Get a test case by ID").argument("<id>", "Test case ID").action(async (id, opts) => {
1256
+ const { runGet: runGet5 } = await Promise.resolve().then(() => (init_get(), get_exports));
1257
+ runGet5(id, { json: program2.opts().json ?? opts.json });
1258
+ });
1259
+ cases.command("update").description("Update a test case").argument("<id>", "Test case ID").option("--title <title>", "New title").option("--description <desc>", "New description").option("--priority <priority>", "New priority").option("--automation-status <status>", "New automation status").option("--tags <tags>", "New comma-separated tags").action(async (id, opts) => {
1260
+ const { runUpdate: runUpdate5 } = await Promise.resolve().then(() => (init_update(), update_exports));
1261
+ runUpdate5(id, { ...opts, json: program2.opts().json ?? opts.json });
1262
+ });
1263
+ cases.command("delete").description("Delete a test case").argument("<id>", "Test case ID").action(async (id, opts) => {
1264
+ const { runDelete: runDelete3 } = await Promise.resolve().then(() => (init_delete(), delete_exports));
1265
+ runDelete3(id, { json: program2.opts().json ?? opts.json });
1266
+ });
1267
+ return cases;
1268
+ }
1269
+
1270
+ // src/cli/commands/deployments/index.ts
1271
+ function createDeploymentsCommand(program2) {
1272
+ const deployments = program2.command("deployments").description("Manage deployments");
1273
+ deployments.command("create").description("Create a deployment").requiredOption("--name <name>", "Deployment name").option("--environment-id <id>", "Environment ID").option("--status <status>", "Status (pending, in_progress, success, failure, cancelled)").option("--source <source>", "Source (e.g., github, gitlab)").option("--external-id <id>", "External ID").option("--external-url <url>", "External URL").option("--ref <ref>", "Git ref").action(async (opts) => {
1274
+ const { runCreate: runCreate5 } = await Promise.resolve().then(() => (init_create2(), create_exports2));
1275
+ runCreate5({ ...opts, json: program2.opts().json ?? opts.json });
1276
+ });
1277
+ deployments.command("list").description("List deployments").option("--status <status>", "Filter by status").option("--environment-id <id>", "Filter by environment ID").option("--source <source>", "Filter by source").option("--limit <n>", "Max results", "50").option("--offset <n>", "Offset", "0").action(async (opts) => {
1278
+ const { runList: runList5 } = await Promise.resolve().then(() => (init_list2(), list_exports2));
1279
+ runList5({ ...opts, json: program2.opts().json ?? opts.json });
1280
+ });
1281
+ deployments.command("get").description("Get a deployment by ID").argument("<id>", "Deployment ID").action(async (id, opts) => {
1282
+ const { runGet: runGet5 } = await Promise.resolve().then(() => (init_get2(), get_exports2));
1283
+ runGet5(id, { json: program2.opts().json ?? opts.json });
1284
+ });
1285
+ deployments.command("update").description("Update a deployment").argument("<id>", "Deployment ID").option("--name <name>", "New name").option("--status <status>", "New status").option("--external-url <url>", "New external URL").action(async (id, opts) => {
1286
+ const { runUpdate: runUpdate5 } = await Promise.resolve().then(() => (init_update2(), update_exports2));
1287
+ runUpdate5(id, { ...opts, json: program2.opts().json ?? opts.json });
1288
+ });
1289
+ deployments.command("delete").description("Delete a deployment").argument("<id>", "Deployment ID").action(async (id, opts) => {
1290
+ const { runDelete: runDelete3 } = await Promise.resolve().then(() => (init_delete2(), delete_exports2));
1291
+ runDelete3(id, { json: program2.opts().json ?? opts.json });
1292
+ });
1293
+ return deployments;
1294
+ }
1295
+
1296
+ // src/cli/commands/environments/index.ts
1297
+ function createEnvironmentsCommand(program2) {
1298
+ const envs = program2.command("envs").description("Manage test environments");
1299
+ envs.command("create").description("Create a test environment").requiredOption("--name <name>", "Environment name").option("--description <desc>", "Description").option("--base-url <url>", "Base URL").action(async (opts) => {
1300
+ const { runCreate: runCreate5 } = await Promise.resolve().then(() => (init_create3(), create_exports3));
1301
+ runCreate5({ ...opts, json: program2.opts().json ?? opts.json });
1302
+ });
1303
+ envs.command("list").description("List test environments").action(async (opts) => {
1304
+ const { runList: runList5 } = await Promise.resolve().then(() => (init_list3(), list_exports3));
1305
+ runList5({ json: program2.opts().json ?? opts.json });
1306
+ });
1307
+ envs.command("get").description("Get a test environment by ID").argument("<id>", "Environment ID").action(async (id, opts) => {
1308
+ const { runGet: runGet5 } = await Promise.resolve().then(() => (init_get3(), get_exports3));
1309
+ runGet5(id, { json: program2.opts().json ?? opts.json });
1310
+ });
1311
+ envs.command("update").description("Update a test environment").argument("<id>", "Environment ID").option("--name <name>", "New name").option("--description <desc>", "New description").option("--base-url <url>", "New base URL").action(async (id, opts) => {
1312
+ const { runUpdate: runUpdate5 } = await Promise.resolve().then(() => (init_update3(), update_exports3));
1313
+ runUpdate5(id, { ...opts, json: program2.opts().json ?? opts.json });
1314
+ });
1315
+ return envs;
1316
+ }
1317
+
1318
+ // src/cli/commands/runs/index.ts
1319
+ function createRunsCommand(program2) {
1320
+ const runs = program2.command("runs").description("Manage test runs");
1321
+ runs.command("create").description("Create a test run").requiredOption("--name <name>", "Run name").option("--environment <name>", "Environment name (auto-creates if needed)").option("--environment-id <id>", "Environment ID").option("--deployment-id <id>", "Deployment ID").option("--tool <name>", "Tool name (e.g., vitest, playwright)").option("--runner-name <name>", "Runner name").option("--runner-url <url>", "Runner URL").action(async (opts) => {
1322
+ const { runCreate: runCreate5 } = await Promise.resolve().then(() => (init_create4(), create_exports4));
1323
+ runCreate5({ ...opts, json: program2.opts().json ?? opts.json });
1324
+ });
1325
+ runs.command("list").description("List test runs").option("--status <status>", "Filter by status").option("--environment-id <id>", "Filter by environment ID").option("--tool <name>", "Filter by tool").option("--limit <n>", "Max results", "50").option("--offset <n>", "Offset", "0").action(async (opts) => {
1326
+ const { runList: runList5 } = await Promise.resolve().then(() => (init_list4(), list_exports4));
1327
+ runList5({ ...opts, json: program2.opts().json ?? opts.json });
1328
+ });
1329
+ runs.command("get").description("Get a test run by ID").argument("<id>", "Test run ID").action(async (id, opts) => {
1330
+ const { runGet: runGet5 } = await Promise.resolve().then(() => (init_get4(), get_exports4));
1331
+ runGet5(id, { json: program2.opts().json ?? opts.json });
1332
+ });
1333
+ runs.command("update").description("Update a test run").argument("<id>", "Test run ID").option("--status <status>", "New status").action(async (id, opts) => {
1334
+ const { runUpdate: runUpdate5 } = await Promise.resolve().then(() => (init_update4(), update_exports4));
1335
+ runUpdate5(id, { ...opts, json: program2.opts().json ?? opts.json });
1336
+ });
1337
+ runs.command("report").description("Report test results from a CTRF file (create run + submit results)").argument("<ctrf-file>", "Path to CTRF JSON report file").option("--name <name>", "Run name (defaults to filename)").option("--environment <name>", "Environment name").option("--deployment-id <id>", "Deployment ID").option("--tool <name>", "Override tool name from CTRF").option("--build-name <name>", "Build name (e.g., git SHA)").option("--build-url <url>", "Build URL (e.g., CI run link)").action(async (ctrfFile, opts) => {
1338
+ const { runReport: runReport2 } = await Promise.resolve().then(() => (init_report(), report_exports));
1339
+ runReport2(ctrfFile, { ...opts, json: program2.opts().json ?? opts.json });
1340
+ });
1341
+ return runs;
1342
+ }
1343
+
1344
+ // src/cli/index.ts
1345
+ var program = new Command();
1346
+ program.name("smooai-testing").description("Smoo AI Testing SDK \u2014 manage test runs, cases, environments, and deployments").version("0.1.0");
1347
+ program.option("--json", "Output in JSON format (auto-enabled when no TTY detected)");
1348
+ program.command("login").description("Store M2M credentials for CLI access").requiredOption("--client-id <id>", "M2M client ID").requiredOption("--client-secret <secret>", "M2M client secret").requiredOption("--org-id <id>", "Organization ID").option("--api-url <url>", "API base URL", "https://api.production.smoo.ai").option("--auth-url <url>", "Auth token URL", "https://auth.production.smoo.ai/token").action(async (opts) => {
1349
+ const { runLogin: runLogin2 } = await Promise.resolve().then(() => (init_login(), login_exports));
1350
+ runLogin2({ ...opts, json: program.opts().json ?? opts.json });
1351
+ });
1352
+ program.command("logout").description("Remove stored credentials").action(async (opts) => {
1353
+ const { runLogout: runLogout2 } = await Promise.resolve().then(() => (init_logout(), logout_exports));
1354
+ runLogout2({ json: program.opts().json ?? opts.json });
1355
+ });
1356
+ program.command("status").description("Show current authentication status").action(async (opts) => {
1357
+ const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), status_exports));
1358
+ runStatus2({ json: program.opts().json ?? opts.json });
1359
+ });
1360
+ createRunsCommand(program);
1361
+ createCasesCommand(program);
1362
+ createEnvironmentsCommand(program);
1363
+ createDeploymentsCommand(program);
1364
+ program.parse();
1365
+ //# sourceMappingURL=cli.mjs.map