@tpmjs/cli 0.1.2 → 0.1.4

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 (78) hide show
  1. package/dist/commands/agent/chat.js +68 -2
  2. package/dist/commands/agent/chat.js.map +1 -1
  3. package/dist/commands/agent/create.js +68 -2
  4. package/dist/commands/agent/create.js.map +1 -1
  5. package/dist/commands/agent/delete.js +68 -2
  6. package/dist/commands/agent/delete.js.map +1 -1
  7. package/dist/commands/agent/list.js +68 -2
  8. package/dist/commands/agent/list.js.map +1 -1
  9. package/dist/commands/agent/update.js +68 -2
  10. package/dist/commands/agent/update.js.map +1 -1
  11. package/dist/commands/auth/login.js +99 -17
  12. package/dist/commands/auth/login.js.map +1 -1
  13. package/dist/commands/auth/logout.js +16 -0
  14. package/dist/commands/auth/logout.js.map +1 -1
  15. package/dist/commands/auth/status.js +68 -2
  16. package/dist/commands/auth/status.js.map +1 -1
  17. package/dist/commands/auth/whoami.js +68 -2
  18. package/dist/commands/auth/whoami.js.map +1 -1
  19. package/dist/commands/collection/add.js +68 -2
  20. package/dist/commands/collection/add.js.map +1 -1
  21. package/dist/commands/collection/create.js +68 -2
  22. package/dist/commands/collection/create.js.map +1 -1
  23. package/dist/commands/collection/delete.js +68 -2
  24. package/dist/commands/collection/delete.js.map +1 -1
  25. package/dist/commands/collection/import.js +68 -2
  26. package/dist/commands/collection/import.js.map +1 -1
  27. package/dist/commands/collection/list.js +68 -2
  28. package/dist/commands/collection/list.js.map +1 -1
  29. package/dist/commands/collection/remove.js +68 -2
  30. package/dist/commands/collection/remove.js.map +1 -1
  31. package/dist/commands/collection/update.js +68 -2
  32. package/dist/commands/collection/update.js.map +1 -1
  33. package/dist/commands/doctor.js +68 -2
  34. package/dist/commands/doctor.js.map +1 -1
  35. package/dist/commands/mcp/config.js +16 -0
  36. package/dist/commands/mcp/config.js.map +1 -1
  37. package/dist/commands/mcp/serve.js +68 -2
  38. package/dist/commands/mcp/serve.js.map +1 -1
  39. package/dist/commands/playground.js +68 -2
  40. package/dist/commands/playground.js.map +1 -1
  41. package/dist/commands/publish/check.js +68 -2
  42. package/dist/commands/publish/check.js.map +1 -1
  43. package/dist/commands/publish/preview.js +16 -0
  44. package/dist/commands/publish/preview.js.map +1 -1
  45. package/dist/commands/scenario/generate.d.ts +19 -0
  46. package/dist/commands/scenario/generate.js +633 -0
  47. package/dist/commands/scenario/generate.js.map +1 -0
  48. package/dist/commands/scenario/info.d.ts +18 -0
  49. package/dist/commands/scenario/info.js +636 -0
  50. package/dist/commands/scenario/info.js.map +1 -0
  51. package/dist/commands/scenario/list.d.ts +20 -0
  52. package/dist/commands/scenario/list.js +652 -0
  53. package/dist/commands/scenario/list.js.map +1 -0
  54. package/dist/commands/scenario/run.d.ts +18 -0
  55. package/dist/commands/scenario/run.js +663 -0
  56. package/dist/commands/scenario/run.js.map +1 -0
  57. package/dist/commands/scenario/test.d.ts +17 -0
  58. package/dist/commands/scenario/test.js +620 -0
  59. package/dist/commands/scenario/test.js.map +1 -0
  60. package/dist/commands/tool/execute.js +68 -2
  61. package/dist/commands/tool/execute.js.map +1 -1
  62. package/dist/commands/tool/info.js +68 -2
  63. package/dist/commands/tool/info.js.map +1 -1
  64. package/dist/commands/tool/init.js +16 -0
  65. package/dist/commands/tool/init.js.map +1 -1
  66. package/dist/commands/tool/search.js +68 -2
  67. package/dist/commands/tool/search.js.map +1 -1
  68. package/dist/commands/tool/trending.js +68 -2
  69. package/dist/commands/tool/trending.js.map +1 -1
  70. package/dist/commands/tool/validate.js +68 -2
  71. package/dist/commands/tool/validate.js.map +1 -1
  72. package/dist/commands/update.js +16 -0
  73. package/dist/commands/update.js.map +1 -1
  74. package/dist/index.d.ts +80 -0
  75. package/dist/index.js +68 -2
  76. package/dist/index.js.map +1 -1
  77. package/oclif.manifest.json +290 -1
  78. package/package.json +4 -1
@@ -0,0 +1,620 @@
1
+ import { Command, Args, Flags } from '@oclif/core';
2
+ import Conf from 'conf';
3
+ import * as fs from 'fs';
4
+ import * as os from 'os';
5
+ import * as path from 'path';
6
+ import Table from 'cli-table3';
7
+ import ora from 'ora';
8
+ import pc from 'picocolors';
9
+
10
+ // src/commands/scenario/test.ts
11
+ var CONFIG_DIR = path.join(os.homedir(), ".tpmjs");
12
+ var CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials.json");
13
+ path.join(CONFIG_DIR, "history");
14
+ function ensureConfigDir() {
15
+ if (!fs.existsSync(CONFIG_DIR)) {
16
+ fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
17
+ }
18
+ }
19
+ var configStore = new Conf({
20
+ projectName: "tpmjs",
21
+ cwd: CONFIG_DIR,
22
+ configName: "config",
23
+ defaults: {
24
+ apiUrl: "https://tpmjs.com/api",
25
+ defaultOutput: "human",
26
+ verbose: false,
27
+ analytics: false
28
+ }
29
+ });
30
+ function getConfigValue(key) {
31
+ return configStore.get(key);
32
+ }
33
+ function loadCredentials() {
34
+ ensureConfigDir();
35
+ if (!fs.existsSync(CREDENTIALS_FILE)) {
36
+ return null;
37
+ }
38
+ try {
39
+ const content = fs.readFileSync(CREDENTIALS_FILE, "utf-8");
40
+ return JSON.parse(content);
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+ function getApiKey() {
46
+ if (process.env.TPMJS_API_KEY) {
47
+ return process.env.TPMJS_API_KEY;
48
+ }
49
+ const creds = loadCredentials();
50
+ if (creds?.apiKey) {
51
+ return creds.apiKey;
52
+ }
53
+ return void 0;
54
+ }
55
+ function getApiUrl() {
56
+ return process.env.TPMJS_API_URL ?? getConfigValue("apiUrl") ?? "https://tpmjs.com/api";
57
+ }
58
+
59
+ // src/lib/api-client.ts
60
+ var TpmClient = class {
61
+ baseUrl;
62
+ apiKey;
63
+ timeout;
64
+ constructor(options = {}) {
65
+ this.baseUrl = options.baseUrl ?? getApiUrl();
66
+ this.apiKey = options.apiKey ?? getApiKey();
67
+ this.timeout = options.timeout ?? 3e4;
68
+ }
69
+ async request(endpoint, options = {}) {
70
+ const url = `${this.baseUrl}${endpoint}`;
71
+ const headers = {
72
+ "Content-Type": "application/json",
73
+ ...options.headers
74
+ };
75
+ if (this.apiKey) {
76
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
77
+ }
78
+ const controller = new AbortController();
79
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
80
+ try {
81
+ const response = await fetch(url, {
82
+ ...options,
83
+ headers,
84
+ signal: controller.signal
85
+ });
86
+ const data = await response.json();
87
+ if (!response.ok) {
88
+ throw new ApiError(
89
+ data.message || data.error || `HTTP ${response.status}`,
90
+ response.status,
91
+ data
92
+ );
93
+ }
94
+ return data;
95
+ } finally {
96
+ clearTimeout(timeoutId);
97
+ }
98
+ }
99
+ // Health check
100
+ async health() {
101
+ return this.request("/health");
102
+ }
103
+ // Stats
104
+ async getStats() {
105
+ return this.request("/stats");
106
+ }
107
+ // Tools
108
+ async searchTools(options = {}) {
109
+ const params = new URLSearchParams();
110
+ if (options.query) params.set("q", options.query);
111
+ if (options.category) params.set("category", options.category);
112
+ if (options.limit) params.set("limit", String(options.limit));
113
+ if (options.offset) params.set("offset", String(options.offset));
114
+ const queryString = params.toString();
115
+ const endpoint = queryString ? `/tools?${queryString}` : "/tools";
116
+ return this.request(endpoint);
117
+ }
118
+ async getTool(packageName, toolName) {
119
+ return this.request(
120
+ `/tools/${encodeURIComponent(packageName)}/${encodeURIComponent(toolName)}`
121
+ );
122
+ }
123
+ async getToolBySlug(slug) {
124
+ const searchResult = await this.searchTools({ query: slug, limit: 1 });
125
+ if (searchResult.data && searchResult.data.length > 0) {
126
+ const tool = searchResult.data.find((t) => t.slug === slug) || searchResult.data[0];
127
+ return { success: true, data: tool };
128
+ }
129
+ return { success: false, error: "Tool not found" };
130
+ }
131
+ async getTrendingTools(options = {}) {
132
+ const params = new URLSearchParams();
133
+ if (options.limit) params.set("limit", String(options.limit));
134
+ if (options.offset) params.set("offset", String(options.offset));
135
+ const queryString = params.toString();
136
+ const endpoint = queryString ? `/tools/trending?${queryString}` : "/tools/trending";
137
+ return this.request(endpoint);
138
+ }
139
+ async validateTpmjsField(field) {
140
+ return this.request("/tools/validate", {
141
+ method: "POST",
142
+ body: JSON.stringify(field)
143
+ });
144
+ }
145
+ async executeTool(slug, params) {
146
+ return this.request(`/tools/${encodeURIComponent(slug)}/execute`, {
147
+ method: "POST",
148
+ body: JSON.stringify(params)
149
+ });
150
+ }
151
+ async *executeToolStream(slug, params) {
152
+ const url = `${this.baseUrl}/tools/${encodeURIComponent(slug)}/execute`;
153
+ const headers = {
154
+ "Content-Type": "application/json",
155
+ Accept: "text/event-stream"
156
+ };
157
+ if (this.apiKey) {
158
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
159
+ }
160
+ const response = await fetch(url, {
161
+ method: "POST",
162
+ headers,
163
+ body: JSON.stringify({ ...params, stream: true })
164
+ });
165
+ if (!response.ok) {
166
+ const errorText = await response.text();
167
+ throw new ApiError(errorText || `HTTP ${response.status}`, response.status);
168
+ }
169
+ if (!response.body) {
170
+ throw new ApiError("No response body", 0);
171
+ }
172
+ const reader = response.body.getReader();
173
+ const decoder = new TextDecoder();
174
+ let buffer = "";
175
+ try {
176
+ while (true) {
177
+ const { done, value } = await reader.read();
178
+ if (done) {
179
+ yield { type: "done", data: "" };
180
+ break;
181
+ }
182
+ buffer += decoder.decode(value, { stream: true });
183
+ const lines = buffer.split("\n");
184
+ buffer = lines.pop() ?? "";
185
+ for (const line of lines) {
186
+ if (line.startsWith("data: ")) {
187
+ const data = line.slice(6);
188
+ if (data === "[DONE]") {
189
+ yield { type: "done", data: "" };
190
+ return;
191
+ }
192
+ try {
193
+ const parsed = JSON.parse(data);
194
+ yield { type: parsed.type || "text", data: parsed.content || parsed.data || data };
195
+ } catch {
196
+ yield { type: "text", data };
197
+ }
198
+ }
199
+ }
200
+ }
201
+ } finally {
202
+ reader.releaseLock();
203
+ }
204
+ }
205
+ // Agents
206
+ async listAgents(options = {}) {
207
+ const params = new URLSearchParams();
208
+ if (options.limit) params.set("limit", String(options.limit));
209
+ if (options.offset) params.set("offset", String(options.offset));
210
+ const queryString = params.toString();
211
+ const endpoint = queryString ? `/agents?${queryString}` : "/agents";
212
+ return this.request(endpoint);
213
+ }
214
+ async getAgent(id) {
215
+ return this.request(`/agents/${id}`);
216
+ }
217
+ async createAgent(input) {
218
+ return this.request("/agents", {
219
+ method: "POST",
220
+ body: JSON.stringify(input)
221
+ });
222
+ }
223
+ async updateAgent(id, input) {
224
+ return this.request(`/agents/${id}`, {
225
+ method: "PATCH",
226
+ body: JSON.stringify(input)
227
+ });
228
+ }
229
+ async deleteAgent(id) {
230
+ return this.request(`/agents/${id}`, {
231
+ method: "DELETE"
232
+ });
233
+ }
234
+ // Collections
235
+ async listCollections(options = {}) {
236
+ const params = new URLSearchParams();
237
+ if (options.limit) params.set("limit", String(options.limit));
238
+ if (options.offset) params.set("offset", String(options.offset));
239
+ const queryString = params.toString();
240
+ const endpoint = queryString ? `/collections?${queryString}` : "/collections";
241
+ return this.request(endpoint);
242
+ }
243
+ async getCollection(id) {
244
+ return this.request(`/collections/${id}`);
245
+ }
246
+ async createCollection(input) {
247
+ return this.request("/collections", {
248
+ method: "POST",
249
+ body: JSON.stringify(input)
250
+ });
251
+ }
252
+ async updateCollection(id, input) {
253
+ return this.request(`/collections/${id}`, {
254
+ method: "PATCH",
255
+ body: JSON.stringify(input)
256
+ });
257
+ }
258
+ async deleteCollection(id) {
259
+ return this.request(`/collections/${id}`, {
260
+ method: "DELETE"
261
+ });
262
+ }
263
+ async addToolsToCollection(id, toolIds) {
264
+ for (const toolId of toolIds) {
265
+ await this.request(`/collections/${id}/tools/${toolId}`, {
266
+ method: "POST"
267
+ });
268
+ }
269
+ return { success: true };
270
+ }
271
+ async removeToolFromCollection(id, toolId) {
272
+ return this.request(`/collections/${id}/tools/${toolId}`, {
273
+ method: "DELETE"
274
+ });
275
+ }
276
+ // User
277
+ async whoami() {
278
+ return this.request("/user/profile");
279
+ }
280
+ async listApiKeys() {
281
+ return this.request("/user/tpmjs-api-keys");
282
+ }
283
+ // Scenarios
284
+ async listScenarios(options = {}) {
285
+ const params = new URLSearchParams();
286
+ if (options.limit) params.set("limit", String(options.limit));
287
+ if (options.offset) params.set("offset", String(options.offset));
288
+ if (options.collectionId) params.set("collectionId", options.collectionId);
289
+ if (options.tags) params.set("tags", options.tags);
290
+ if (options.sortBy) params.set("sortBy", options.sortBy);
291
+ const queryString = params.toString();
292
+ const endpoint = queryString ? `/scenarios?${queryString}` : "/scenarios";
293
+ return this.request(endpoint);
294
+ }
295
+ async listCollectionScenarios(collectionId, options = {}) {
296
+ const params = new URLSearchParams();
297
+ if (options.limit) params.set("limit", String(options.limit));
298
+ if (options.offset) params.set("offset", String(options.offset));
299
+ const queryString = params.toString();
300
+ const endpoint = queryString ? `/collections/${collectionId}/scenarios?${queryString}` : `/collections/${collectionId}/scenarios`;
301
+ return this.request(endpoint);
302
+ }
303
+ async getScenario(id) {
304
+ return this.request(`/scenarios/${id}`);
305
+ }
306
+ async createScenario(input) {
307
+ return this.request("/scenarios", {
308
+ method: "POST",
309
+ body: JSON.stringify(input)
310
+ });
311
+ }
312
+ async generateScenarios(collectionId, input = {}) {
313
+ return this.request(`/collections/${collectionId}/scenarios/generate`, {
314
+ method: "POST",
315
+ body: JSON.stringify(input)
316
+ });
317
+ }
318
+ async runScenario(scenarioId) {
319
+ return this.request(`/scenarios/${scenarioId}/run`, {
320
+ method: "POST"
321
+ });
322
+ }
323
+ async getScenarioRuns(scenarioId, options = {}) {
324
+ const params = new URLSearchParams();
325
+ if (options.limit) params.set("limit", String(options.limit));
326
+ if (options.offset) params.set("offset", String(options.offset));
327
+ const queryString = params.toString();
328
+ const endpoint = queryString ? `/scenarios/${scenarioId}/runs?${queryString}` : `/scenarios/${scenarioId}/runs`;
329
+ return this.request(endpoint);
330
+ }
331
+ // Check if authenticated
332
+ isAuthenticated() {
333
+ return !!this.apiKey;
334
+ }
335
+ };
336
+ var ApiError = class extends Error {
337
+ constructor(message, statusCode, data) {
338
+ super(message);
339
+ this.statusCode = statusCode;
340
+ this.data = data;
341
+ this.name = "ApiError";
342
+ }
343
+ };
344
+ var clientInstance = null;
345
+ function getClient(options) {
346
+ if (!clientInstance || options) {
347
+ clientInstance = new TpmClient(options);
348
+ }
349
+ return clientInstance;
350
+ }
351
+ var OutputFormatter = class {
352
+ options;
353
+ constructor(options = {}) {
354
+ this.options = options;
355
+ }
356
+ // Output as JSON
357
+ json(data) {
358
+ console.log(JSON.stringify(data, null, 2));
359
+ }
360
+ // Output a table
361
+ table(data, columns) {
362
+ if (this.options.json) {
363
+ this.json(data);
364
+ return;
365
+ }
366
+ const table = new Table({
367
+ head: columns.map((col) => pc.bold(col.header)),
368
+ colWidths: columns.map((col) => col.width ?? null),
369
+ style: {
370
+ head: [],
371
+ border: []
372
+ }
373
+ });
374
+ for (const row of data) {
375
+ table.push(columns.map((col) => String(row[col.key] ?? "")));
376
+ }
377
+ console.log(table.toString());
378
+ }
379
+ // Success message
380
+ success(message) {
381
+ if (this.options.json) return;
382
+ console.log(pc.green("\u2713"), message);
383
+ }
384
+ // Error message
385
+ error(message, details) {
386
+ if (this.options.json) {
387
+ this.json({ error: message, details });
388
+ return;
389
+ }
390
+ console.error(pc.red("\u2717"), message);
391
+ if (details && this.options.verbose) {
392
+ console.error(pc.dim(details));
393
+ }
394
+ }
395
+ // Warning message
396
+ warning(message) {
397
+ if (this.options.json) return;
398
+ console.log(pc.yellow("\u26A0"), message);
399
+ }
400
+ // Info message
401
+ info(message) {
402
+ if (this.options.json) return;
403
+ console.log(pc.blue("\u2139"), message);
404
+ }
405
+ // Debug message (only in verbose mode)
406
+ debug(message) {
407
+ if (this.options.json) return;
408
+ if (this.options.verbose) {
409
+ console.log(pc.dim(`[debug] ${message}`));
410
+ }
411
+ }
412
+ // Plain text output
413
+ text(message) {
414
+ if (this.options.json) return;
415
+ console.log(message);
416
+ }
417
+ // Heading
418
+ heading(text) {
419
+ if (this.options.json) return;
420
+ console.log();
421
+ console.log(pc.bold(pc.underline(text)));
422
+ console.log();
423
+ }
424
+ // Subheading
425
+ subheading(text) {
426
+ if (this.options.json) return;
427
+ console.log(pc.bold(text));
428
+ }
429
+ // Key-value pair
430
+ keyValue(key, value) {
431
+ if (this.options.json) return;
432
+ console.log(`${pc.dim(key + ":")} ${value ?? pc.dim("(not set)")}`);
433
+ }
434
+ // List item
435
+ listItem(text, indent = 0) {
436
+ if (this.options.json) return;
437
+ const prefix = " ".repeat(indent) + "\u2022";
438
+ console.log(`${prefix} ${text}`);
439
+ }
440
+ // Spinner
441
+ spinner(message) {
442
+ return ora({
443
+ text: message,
444
+ isSilent: this.options.json
445
+ }).start();
446
+ }
447
+ // Blank line
448
+ newLine() {
449
+ if (this.options.json) return;
450
+ console.log();
451
+ }
452
+ // Horizontal rule
453
+ hr() {
454
+ if (this.options.json) return;
455
+ console.log(pc.dim("\u2500".repeat(50)));
456
+ }
457
+ // Alias for hr
458
+ divider() {
459
+ this.hr();
460
+ }
461
+ // Code block
462
+ code(text, language) {
463
+ if (this.options.json) {
464
+ this.json({ code: text, language });
465
+ return;
466
+ }
467
+ console.log(pc.dim("```" + (language ?? "")));
468
+ console.log(text);
469
+ console.log(pc.dim("```"));
470
+ }
471
+ // Highlight text
472
+ highlight(text) {
473
+ return pc.cyan(text);
474
+ }
475
+ // Dim text
476
+ dim(text) {
477
+ return pc.dim(text);
478
+ }
479
+ // Bold text
480
+ bold(text) {
481
+ return pc.bold(text);
482
+ }
483
+ // Link (just returns text in terminal)
484
+ link(text, url) {
485
+ return `\x1B]8;;${url}\x07${pc.underline(pc.blue(text))}\x1B]8;;\x07`;
486
+ }
487
+ // Color helpers
488
+ green(text) {
489
+ return pc.green(text);
490
+ }
491
+ red(text) {
492
+ return pc.red(text);
493
+ }
494
+ yellow(text) {
495
+ return pc.yellow(text);
496
+ }
497
+ blue(text) {
498
+ return pc.blue(text);
499
+ }
500
+ cyan(text) {
501
+ return pc.cyan(text);
502
+ }
503
+ };
504
+ function createOutput(flags) {
505
+ return new OutputFormatter({
506
+ json: flags.json,
507
+ verbose: flags.verbose
508
+ });
509
+ }
510
+
511
+ // src/commands/scenario/test.ts
512
+ var ScenarioTest = class _ScenarioTest extends Command {
513
+ static description = "Run a single scenario by ID";
514
+ static examples = [
515
+ "<%= config.bin %> <%= command.id %> clu123abc456",
516
+ "<%= config.bin %> <%= command.id %> clu123abc456 --json",
517
+ "<%= config.bin %> <%= command.id %> clu123abc456 --verbose"
518
+ ];
519
+ static args = {
520
+ scenarioId: Args.string({
521
+ description: "Scenario ID to run",
522
+ required: true
523
+ })
524
+ };
525
+ static flags = {
526
+ json: Flags.boolean({
527
+ description: "Output in JSON format",
528
+ default: false
529
+ }),
530
+ verbose: Flags.boolean({
531
+ char: "v",
532
+ description: "Show verbose output including full reason",
533
+ default: false
534
+ })
535
+ };
536
+ async run() {
537
+ const { args, flags } = await this.parse(_ScenarioTest);
538
+ const output = createOutput(flags);
539
+ const client = getClient();
540
+ if (!client.isAuthenticated()) {
541
+ output.error("Not authenticated. Run `tpm auth login` first.");
542
+ return;
543
+ }
544
+ const infoSpinner = output.spinner("Fetching scenario...");
545
+ let scenarioName;
546
+ try {
547
+ const scenarioResponse = await client.getScenario(args.scenarioId);
548
+ const scenario = scenarioResponse.data;
549
+ scenarioName = scenario.name || scenario.prompt.slice(0, 50) + "...";
550
+ infoSpinner.stop();
551
+ output.text(output.bold(`Scenario: ${scenarioName}`));
552
+ if (scenario.collection) {
553
+ output.text(output.dim(`Collection: ${scenario.collection.name}`));
554
+ }
555
+ output.newLine();
556
+ } catch (error) {
557
+ infoSpinner.fail("Scenario not found");
558
+ output.error(
559
+ error instanceof Error ? error.message : "Unknown error",
560
+ flags.verbose ? String(error) : void 0
561
+ );
562
+ return;
563
+ }
564
+ const runSpinner = output.spinner("Executing scenario...");
565
+ try {
566
+ const result = await client.runScenario(args.scenarioId);
567
+ const runData = result.data;
568
+ if (runData.success) {
569
+ runSpinner.succeed(output.green("Scenario PASSED"));
570
+ } else if (runData.status === "error") {
571
+ runSpinner.fail(output.red("Scenario ERROR"));
572
+ } else {
573
+ runSpinner.fail(output.red("Scenario FAILED"));
574
+ }
575
+ output.newLine();
576
+ if (flags.json) {
577
+ output.json(runData);
578
+ return;
579
+ }
580
+ output.text(output.bold("Results"));
581
+ output.text(` Status: ${runData.status}`);
582
+ output.text(` Verdict: ${runData.evaluator?.verdict || "N/A"}`);
583
+ if (runData.evaluator?.reason) {
584
+ if (flags.verbose) {
585
+ output.text(` Reason: ${runData.evaluator.reason}`);
586
+ } else {
587
+ const truncatedReason = runData.evaluator.reason.length > 80 ? runData.evaluator.reason.slice(0, 80) + "..." : runData.evaluator.reason;
588
+ output.text(` Reason: ${truncatedReason}`);
589
+ }
590
+ }
591
+ output.newLine();
592
+ output.text(output.bold("Usage"));
593
+ if (runData.usage.executionTimeMs) {
594
+ output.text(` Duration: ${runData.usage.executionTimeMs}ms`);
595
+ }
596
+ if (runData.usage.totalTokens) {
597
+ output.text(
598
+ ` Tokens: ${runData.usage.totalTokens} (in: ${runData.usage.inputTokens}, out: ${runData.usage.outputTokens})`
599
+ );
600
+ }
601
+ output.newLine();
602
+ output.text(output.dim(`Run ID: ${runData.runId}`));
603
+ output.text(output.dim(`Quota remaining: ${runData.quotaRemaining} runs/day`));
604
+ if (!runData.success) {
605
+ this.exit(1);
606
+ }
607
+ } catch (error) {
608
+ runSpinner.fail("Failed to run scenario");
609
+ output.error(
610
+ error instanceof Error ? error.message : "Unknown error",
611
+ flags.verbose ? String(error) : void 0
612
+ );
613
+ this.exit(1);
614
+ }
615
+ }
616
+ };
617
+
618
+ export { ScenarioTest as default };
619
+ //# sourceMappingURL=test.js.map
620
+ //# sourceMappingURL=test.js.map