vent-hq 0.7.8 → 0.8.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stephan Gazarov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.mjs CHANGED
@@ -131,8 +131,175 @@ async function apiFetch(path3, apiKey, options = {}) {
131
131
  return res;
132
132
  }
133
133
 
134
+ // src/lib/output.ts
135
+ import { writeFileSync } from "node:fs";
136
+ var isTTY = process.stdout.isTTY;
137
+ var _verbose = false;
138
+ function setVerbose(v) {
139
+ _verbose = v;
140
+ }
141
+ function debug(msg) {
142
+ if (!_verbose) return;
143
+ const ts = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
144
+ process.stderr.write(`[vent ${ts}] ${msg}
145
+ `);
146
+ }
147
+ function isVerbose() {
148
+ return _verbose;
149
+ }
150
+ function stdoutSync(data) {
151
+ if (isTTY) {
152
+ process.stdout.write(data);
153
+ } else {
154
+ try {
155
+ writeFileSync(1, data);
156
+ } catch {
157
+ process.stdout.write(data);
158
+ }
159
+ }
160
+ }
161
+ var bold = (s) => isTTY ? `\x1B[1m${s}\x1B[0m` : s;
162
+ var dim = (s) => isTTY ? `\x1B[2m${s}\x1B[0m` : s;
163
+ var green = (s) => isTTY ? `\x1B[32m${s}\x1B[0m` : s;
164
+ var red = (s) => isTTY ? `\x1B[31m${s}\x1B[0m` : s;
165
+ var blue = (s) => isTTY ? `\x1B[34m${s}\x1B[0m` : s;
166
+ function printEvent(event, jsonMode) {
167
+ if (jsonMode) {
168
+ stdoutSync(JSON.stringify(event) + "\n");
169
+ return;
170
+ }
171
+ if (!isTTY) {
172
+ if (_verbose) {
173
+ const meta2 = event.metadata_json ?? {};
174
+ if (event.event_type === "test_completed") {
175
+ const name = meta2.test_name ?? "test";
176
+ const status = meta2.status ?? "unknown";
177
+ const durationMs = meta2.duration_ms;
178
+ const duration = durationMs != null ? (durationMs / 1e3).toFixed(1) + "s" : "";
179
+ process.stderr.write(` ${status === "completed" || status === "pass" ? "\u2714" : "\u2718"} ${name} ${duration}
180
+ `);
181
+ }
182
+ }
183
+ return;
184
+ }
185
+ const meta = event.metadata_json ?? {};
186
+ switch (event.event_type) {
187
+ case "test_completed":
188
+ printTestResult(meta);
189
+ break;
190
+ case "run_complete":
191
+ printRunComplete(meta);
192
+ break;
193
+ case "test_started": {
194
+ const name = meta.test_name ?? "test";
195
+ process.stderr.write(dim(` \u25B8 ${name}\u2026`) + "\n");
196
+ break;
197
+ }
198
+ default:
199
+ process.stderr.write(dim(` [${event.event_type}]`) + "\n");
200
+ }
201
+ }
202
+ function printTestResult(meta) {
203
+ const result = meta.result;
204
+ const testName = result?.name ?? meta.test_name ?? "test";
205
+ const testStatus = result?.status ?? meta.status;
206
+ const durationMs = result?.duration_ms ?? meta.duration_ms;
207
+ const statusIcon = testStatus === "completed" || testStatus === "pass" ? green("\u2714") : red("\u2718");
208
+ const duration = durationMs != null ? (durationMs / 1e3).toFixed(1) + "s" : "\u2014";
209
+ const parts = [statusIcon, bold(testName), dim(duration)];
210
+ if (result?.behavior?.intent_accuracy) {
211
+ parts.push(`intent: ${result.behavior.intent_accuracy.score}`);
212
+ }
213
+ if (result?.latency?.p50_ttfw_ms != null) {
214
+ parts.push(`p50: ${result.latency.p50_ttfw_ms}ms`);
215
+ }
216
+ stdoutSync(parts.join(" ") + "\n");
217
+ }
218
+ function printRunComplete(meta) {
219
+ const status = meta.status;
220
+ const agg = meta.aggregate;
221
+ const redTeam = agg?.red_team_tests;
222
+ const counts = redTeam ?? agg?.conversation_tests;
223
+ const total = meta.total_tests ?? counts?.total;
224
+ const passed = meta.passed_tests ?? counts?.passed;
225
+ const failed = meta.failed_tests ?? counts?.failed;
226
+ stdoutSync("\n");
227
+ if (status === "pass") {
228
+ stdoutSync(green(bold("Run passed")) + "\n");
229
+ } else {
230
+ stdoutSync(red(bold("Run failed")) + "\n");
231
+ }
232
+ if (total != null) {
233
+ const parts = [];
234
+ if (passed) parts.push(green(`${passed} passed`));
235
+ if (failed) parts.push(red(`${failed} failed`));
236
+ parts.push(`${total} total`);
237
+ stdoutSync(parts.join(dim(" \xB7 ")) + "\n");
238
+ }
239
+ }
240
+ function printSummary(testResults, runComplete, runId, jsonMode) {
241
+ const allTests = testResults.map((e) => {
242
+ const meta = e.metadata_json ?? {};
243
+ const r = meta.result;
244
+ if (r) return r;
245
+ return {
246
+ name: meta.test_name ?? "test",
247
+ status: meta.status ?? "unknown",
248
+ duration_ms: meta.duration_ms,
249
+ error: null
250
+ };
251
+ });
252
+ const agg = runComplete.aggregate;
253
+ const counts = agg?.red_team_tests ?? agg?.conversation_tests;
254
+ const summaryData = {
255
+ run_id: runId,
256
+ status: runComplete.status,
257
+ total: runComplete.total_tests ?? counts?.total,
258
+ passed: runComplete.passed_tests ?? counts?.passed,
259
+ failed: runComplete.failed_tests ?? counts?.failed,
260
+ tests: allTests,
261
+ check: `npx vent-hq status ${runId} --json`
262
+ };
263
+ if (jsonMode || !isTTY) {
264
+ stdoutSync(JSON.stringify(summaryData, null, 2) + "\n");
265
+ return;
266
+ }
267
+ const failures = allTests.filter((t2) => t2.status && t2.status !== "completed" && t2.status !== "pass");
268
+ if (failures.length > 0) {
269
+ stdoutSync("\n" + bold("Failed tests:") + "\n");
270
+ for (const t2 of failures) {
271
+ const duration = t2.duration_ms != null ? (t2.duration_ms / 1e3).toFixed(1) + "s" : "\u2014";
272
+ const parts = [red("\u2718"), bold(t2.name), dim(duration)];
273
+ if (t2.intent_accuracy != null) {
274
+ parts.push(`intent: ${t2.intent_accuracy}`);
275
+ }
276
+ stdoutSync(" " + parts.join(" ") + "\n");
277
+ }
278
+ }
279
+ process.stderr.write(dim(`Full details: vent status ${runId} --json`) + "\n");
280
+ }
281
+ function printError(message) {
282
+ const line = red(bold("error")) + ` ${message}
283
+ `;
284
+ process.stderr.write(line);
285
+ if (!isTTY) {
286
+ stdoutSync(line);
287
+ }
288
+ }
289
+ function printInfo(message) {
290
+ if (!isTTY && !_verbose) return;
291
+ process.stderr.write(blue("\u25B8") + ` ${message}
292
+ `);
293
+ }
294
+ function printSuccess(message) {
295
+ if (!isTTY && !_verbose) return;
296
+ process.stderr.write(green("\u2714") + ` ${message}
297
+ `);
298
+ }
299
+
134
300
  // src/lib/sse.ts
135
301
  function log(msg) {
302
+ if (!isVerbose()) return;
136
303
  const ts = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
137
304
  const line = `[vent:sse ${ts}] ${msg}
138
305
  `;
@@ -398,7 +565,7 @@ async function startRelay(relayConfig) {
398
565
  };
399
566
  const client = new RelayClient(clientConfig);
400
567
  client.on("log", (msg) => {
401
- process.stderr.write(`${msg}
568
+ if (isVerbose()) process.stderr.write(`${msg}
402
569
  `);
403
570
  });
404
571
  await client.connect();
@@ -441,181 +608,27 @@ async function waitForHealth(port, endpoint, timeoutMs = 3e4) {
441
608
  throw new Error(`Agent health check timed out after ${timeoutMs}ms at ${url}`);
442
609
  }
443
610
 
444
- // src/lib/output.ts
445
- import { writeFileSync } from "node:fs";
446
- var isTTY = process.stdout.isTTY;
447
- function stdoutSync(data) {
448
- if (isTTY) {
449
- process.stdout.write(data);
450
- } else {
451
- try {
452
- writeFileSync(1, data);
453
- } catch {
454
- process.stdout.write(data);
455
- }
456
- }
457
- }
458
- var bold = (s) => isTTY ? `\x1B[1m${s}\x1B[0m` : s;
459
- var dim = (s) => isTTY ? `\x1B[2m${s}\x1B[0m` : s;
460
- var green = (s) => isTTY ? `\x1B[32m${s}\x1B[0m` : s;
461
- var red = (s) => isTTY ? `\x1B[31m${s}\x1B[0m` : s;
462
- var blue = (s) => isTTY ? `\x1B[34m${s}\x1B[0m` : s;
463
- function printEvent(event, jsonMode) {
464
- if (jsonMode) {
465
- stdoutSync(JSON.stringify(event) + "\n");
466
- return;
467
- }
468
- if (!isTTY) {
469
- const meta2 = event.metadata_json ?? {};
470
- if (event.event_type === "test_completed") {
471
- const name = meta2.test_name ?? "test";
472
- const status = meta2.status ?? "unknown";
473
- const durationMs = meta2.duration_ms;
474
- const duration = durationMs != null ? (durationMs / 1e3).toFixed(1) + "s" : "";
475
- process.stderr.write(` ${status === "completed" || status === "pass" ? "\u2714" : "\u2718"} ${name} ${duration}
476
- `);
477
- }
478
- return;
479
- }
480
- const meta = event.metadata_json ?? {};
481
- switch (event.event_type) {
482
- case "test_completed":
483
- printTestResult(meta);
484
- break;
485
- case "run_complete":
486
- printRunComplete(meta);
487
- break;
488
- case "test_started": {
489
- const name = meta.test_name ?? "test";
490
- process.stderr.write(dim(` \u25B8 ${name}\u2026`) + "\n");
491
- break;
492
- }
493
- default:
494
- process.stderr.write(dim(` [${event.event_type}]`) + "\n");
495
- }
496
- }
497
- function printTestResult(meta) {
498
- const result = meta.result;
499
- const testName = result?.name ?? meta.test_name ?? "test";
500
- const testStatus = result?.status ?? meta.status;
501
- const durationMs = result?.duration_ms ?? meta.duration_ms;
502
- const statusIcon = testStatus === "completed" || testStatus === "pass" ? green("\u2714") : red("\u2718");
503
- const duration = durationMs != null ? (durationMs / 1e3).toFixed(1) + "s" : "\u2014";
504
- const parts = [statusIcon, bold(testName), dim(duration)];
505
- if (result?.behavior?.intent_accuracy) {
506
- parts.push(`intent: ${result.behavior.intent_accuracy.score}`);
507
- }
508
- if (result?.latency?.p50_ttfw_ms != null) {
509
- parts.push(`p50: ${result.latency.p50_ttfw_ms}ms`);
510
- }
511
- stdoutSync(parts.join(" ") + "\n");
512
- }
513
- function printRunComplete(meta) {
514
- const status = meta.status;
515
- const agg = meta.aggregate;
516
- const redTeam = agg?.red_team_tests;
517
- const counts = redTeam ?? agg?.conversation_tests;
518
- const total = meta.total_tests ?? counts?.total;
519
- const passed = meta.passed_tests ?? counts?.passed;
520
- const failed = meta.failed_tests ?? counts?.failed;
521
- stdoutSync("\n");
522
- if (status === "pass") {
523
- stdoutSync(green(bold("Run passed")) + "\n");
524
- } else {
525
- stdoutSync(red(bold("Run failed")) + "\n");
526
- }
527
- if (total != null) {
528
- const parts = [];
529
- if (passed) parts.push(green(`${passed} passed`));
530
- if (failed) parts.push(red(`${failed} failed`));
531
- parts.push(`${total} total`);
532
- stdoutSync(parts.join(dim(" \xB7 ")) + "\n");
533
- }
534
- }
535
- function printSummary(testResults, runComplete, runId, jsonMode) {
536
- const allTests = testResults.map((e) => {
537
- const meta = e.metadata_json ?? {};
538
- const r = meta.result;
539
- return {
540
- name: r?.name ?? meta.test_name ?? "test",
541
- status: r?.status ?? meta.status,
542
- duration_ms: r?.duration_ms ?? meta.duration_ms,
543
- intent_accuracy: r?.behavior?.intent_accuracy?.score,
544
- p50_ttfw_ms: r?.latency?.p50_ttfw_ms,
545
- error: r?.error ?? void 0
546
- };
547
- });
548
- const agg = runComplete.aggregate;
549
- const counts = agg?.red_team_tests ?? agg?.conversation_tests;
550
- const summaryData = {
551
- run_id: runId,
552
- status: runComplete.status,
553
- total: runComplete.total_tests ?? counts?.total,
554
- passed: runComplete.passed_tests ?? counts?.passed,
555
- failed: runComplete.failed_tests ?? counts?.failed,
556
- tests: allTests,
557
- check: `npx vent-hq status ${runId} --json`
558
- };
559
- if (jsonMode || !isTTY) {
560
- stdoutSync(JSON.stringify(summaryData) + "\n");
561
- return;
562
- }
563
- const failures = allTests.filter((t2) => t2.status && t2.status !== "completed" && t2.status !== "pass");
564
- if (failures.length > 0) {
565
- stdoutSync("\n" + bold("Failed tests:") + "\n");
566
- for (const t2 of failures) {
567
- const duration = t2.duration_ms != null ? (t2.duration_ms / 1e3).toFixed(1) + "s" : "\u2014";
568
- const parts = [red("\u2718"), bold(t2.name), dim(duration)];
569
- if (t2.intent_accuracy != null) {
570
- parts.push(`intent: ${t2.intent_accuracy}`);
571
- }
572
- stdoutSync(" " + parts.join(" ") + "\n");
573
- }
574
- }
575
- process.stderr.write(dim(`Full details: vent status ${runId} --json`) + "\n");
576
- }
577
- function printError(message) {
578
- const line = red(bold("error")) + ` ${message}
579
- `;
580
- process.stderr.write(line);
581
- if (!isTTY) {
582
- stdoutSync(line);
583
- }
584
- }
585
- function printInfo(message) {
586
- process.stderr.write(blue("\u25B8") + ` ${message}
587
- `);
588
- }
589
- function printSuccess(message) {
590
- process.stderr.write(green("\u2714") + ` ${message}
591
- `);
592
- }
593
-
594
611
  // src/commands/run.ts
595
612
  var isTTY2 = process.stdout.isTTY;
596
- function log2(msg) {
597
- const ts = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
598
- process.stderr.write(`[vent ${ts}] ${msg}
599
- `);
600
- }
601
613
  async function runCommand(args) {
602
- log2(`start args=${JSON.stringify({ file: args.file, test: args.test, json: args.json, submit: args.submit })}`);
614
+ if (args.verbose) setVerbose(true);
615
+ debug(`start args=${JSON.stringify({ file: args.file, test: args.test, json: args.json, submit: args.submit })}`);
603
616
  const apiKey = args.apiKey ?? await loadApiKey();
604
617
  if (!apiKey) {
605
618
  printError("No API key found. Set VENT_API_KEY, run `npx vent-hq login`, or pass --api-key.");
606
619
  return 2;
607
620
  }
608
- log2(`api-key resolved (${apiKey.slice(0, 8)}\u2026)`);
621
+ debug(`api-key resolved (${apiKey.slice(0, 8)}\u2026)`);
609
622
  let config;
610
623
  try {
611
624
  if (args.file) {
612
- log2(`reading config file: ${args.file}`);
625
+ debug(`reading config file: ${args.file}`);
613
626
  const raw = await fs2.readFile(args.file, "utf-8");
614
627
  config = JSON.parse(raw);
615
- log2(`config parsed \u2014 keys: ${Object.keys(config).join(", ")}`);
628
+ debug(`config parsed \u2014 keys: ${Object.keys(config).join(", ")}`);
616
629
  } else if (args.config) {
617
630
  config = JSON.parse(args.config);
618
- log2("config parsed from --config flag");
631
+ debug("config parsed from --config flag");
619
632
  } else {
620
633
  printError("Provide --config '{...}' or -f <file>.");
621
634
  return 2;
@@ -653,15 +666,15 @@ async function runCommand(args) {
653
666
  cfg2.red_team_tests = redMatch;
654
667
  cfg2.conversation_tests = void 0;
655
668
  }
656
- log2(`filtered to test: ${args.test}`);
669
+ debug(`filtered to test: ${args.test}`);
657
670
  }
658
671
  const cfg = config;
659
672
  if (cfg.connection?.start_command) {
660
673
  const freePort = await findFreePort();
661
674
  cfg.connection.agent_port = freePort;
662
- log2(`auto-port assigned: ${freePort}`);
675
+ debug(`auto-port assigned: ${freePort}`);
663
676
  }
664
- log2("submitting run to API\u2026");
677
+ debug("submitting run to API\u2026");
665
678
  printInfo("Submitting run\u2026");
666
679
  let submitResult;
667
680
  try {
@@ -669,20 +682,20 @@ async function runCommand(args) {
669
682
  method: "POST",
670
683
  body: JSON.stringify({ config })
671
684
  });
672
- log2(`API response status: ${res.status}`);
685
+ debug(`API response status: ${res.status}`);
673
686
  submitResult = await res.json();
674
687
  } catch (err) {
675
- log2(`submit error: ${err.message}`);
688
+ debug(`submit error: ${err.message}`);
676
689
  printError(`Submit failed: ${err.message}`);
677
690
  return 2;
678
691
  }
679
692
  const { run_id } = submitResult;
680
693
  if (!run_id) {
681
- log2(`no run_id in response: ${JSON.stringify(submitResult)}`);
694
+ debug(`no run_id in response: ${JSON.stringify(submitResult)}`);
682
695
  printError("Server returned no run_id. Response: " + JSON.stringify(submitResult));
683
696
  return 2;
684
697
  }
685
- log2(`run created: ${run_id} status=${submitResult.status} has_relay=${!!submitResult.relay_config}`);
698
+ debug(`run created: ${run_id} status=${submitResult.status} has_relay=${!!submitResult.relay_config}`);
686
699
  printInfo(`Run ${run_id} created.`);
687
700
  if (args.submit) {
688
701
  if (submitResult.relay_config) {
@@ -702,28 +715,28 @@ async function runCommand(args) {
702
715
  }
703
716
  let relay = null;
704
717
  if (submitResult.relay_config) {
705
- log2(`starting relay \u2014 agent_port=${submitResult.relay_config.agent_port} start_command="${submitResult.relay_config.start_command}" health=${submitResult.relay_config.health_endpoint}`);
718
+ debug(`starting relay \u2014 agent_port=${submitResult.relay_config.agent_port} start_command="${submitResult.relay_config.start_command}" health=${submitResult.relay_config.health_endpoint}`);
706
719
  printInfo("Starting relay for local agent\u2026");
707
720
  printInfo("Connecting to Vent cloud relay (timeout: 30s)\u2026");
708
721
  try {
709
722
  relay = await startRelay(submitResult.relay_config);
710
- log2("relay connected, agent healthy, run activated");
723
+ debug("relay connected, agent healthy, run activated");
711
724
  printInfo("Relay connected, agent started.");
712
725
  } catch (err) {
713
726
  const msg = err.message;
714
- log2(`relay error: ${msg}`);
727
+ debug(`relay error: ${msg}`);
715
728
  printError(`Relay failed: ${msg}`);
716
729
  return 2;
717
730
  }
718
731
  }
719
- log2(`connecting to SSE stream for run ${run_id}\u2026`);
732
+ debug(`connecting to SSE stream for run ${run_id}\u2026`);
720
733
  printInfo(`Streaming results for run ${run_id}\u2026`);
721
734
  const abortController = new AbortController();
722
735
  let exitCode = 0;
723
736
  const testResults = [];
724
737
  let runCompleteData = null;
725
738
  const onSignal = () => {
726
- log2("received SIGINT/SIGTERM \u2014 aborting stream");
739
+ debug("received SIGINT/SIGTERM \u2014 aborting stream");
727
740
  abortController.abort();
728
741
  };
729
742
  process.on("SIGINT", onSignal);
@@ -733,38 +746,38 @@ async function runCommand(args) {
733
746
  for await (const event of streamRunEvents(run_id, apiKey, abortController.signal)) {
734
747
  eventCount++;
735
748
  const meta = event.metadata_json ?? {};
736
- log2(`event #${eventCount}: type=${event.event_type} meta_keys=[${Object.keys(meta).join(",")}] message="${event.message ?? ""}"`);
749
+ debug(`event #${eventCount}: type=${event.event_type} meta_keys=[${Object.keys(meta).join(",")}] message="${event.message ?? ""}"`);
737
750
  printEvent(event, args.json);
738
751
  if (event.event_type === "test_completed") {
739
752
  testResults.push(event);
740
- log2(`test_completed: name=${meta.test_name} status=${meta.status} duration=${meta.duration_ms}ms completed=${meta.completed}/${meta.total}`);
753
+ debug(`test_completed: name=${meta.test_name} status=${meta.status} duration=${meta.duration_ms}ms completed=${meta.completed}/${meta.total}`);
741
754
  }
742
755
  if (event.event_type === "run_complete") {
743
756
  runCompleteData = meta;
744
757
  const status = meta.status;
745
758
  exitCode = status === "pass" ? 0 : 1;
746
- log2(`run_complete: status=${status} exitCode=${exitCode}`);
759
+ debug(`run_complete: status=${status} exitCode=${exitCode}`);
747
760
  }
748
761
  }
749
- log2(`SSE stream ended \u2014 received ${eventCount} events total`);
762
+ debug(`SSE stream ended \u2014 received ${eventCount} events total`);
750
763
  } catch (err) {
751
764
  if (err.name !== "AbortError") {
752
- log2(`stream error: ${err.message}`);
765
+ debug(`stream error: ${err.message}`);
753
766
  printError(`Stream error: ${err.message}`);
754
767
  exitCode = 2;
755
768
  } else {
756
- log2("stream aborted (user signal)");
769
+ debug("stream aborted (user signal)");
757
770
  }
758
771
  } finally {
759
772
  process.off("SIGINT", onSignal);
760
773
  process.off("SIGTERM", onSignal);
761
774
  if (relay) {
762
- log2("cleaning up relay\u2026");
775
+ debug("cleaning up relay\u2026");
763
776
  await relay.cleanup();
764
- log2("relay cleaned up");
777
+ debug("relay cleaned up");
765
778
  }
766
779
  }
767
- log2(`summary: testResults=${testResults.length} runComplete=${!!runCompleteData} exitCode=${exitCode}`);
780
+ debug(`summary: testResults=${testResults.length} runComplete=${!!runCompleteData} exitCode=${exitCode}`);
768
781
  if (runCompleteData) {
769
782
  printSummary(testResults, runCompleteData, run_id, args.json);
770
783
  } else if (!isTTY2) {
@@ -779,7 +792,7 @@ async function runCommand(args) {
779
792
  process.stdout.write(JSON.stringify({ run_id, status: "error" }) + "\n");
780
793
  }
781
794
  }
782
- log2(`exiting with code ${exitCode}`);
795
+ debug(`exiting with code ${exitCode}`);
783
796
  process.exit(exitCode);
784
797
  }
785
798
  function findFreePort() {
@@ -6815,7 +6828,8 @@ Options:
6815
6828
  --list List test names from suite file
6816
6829
  --api-key API key (overrides env/credentials)
6817
6830
  --json Output NDJSON instead of colored text
6818
- --submit Submit and return immediately (print run_id, don't wait for results)`;
6831
+ --submit Submit and return immediately (print run_id, don't wait for results)
6832
+ --verbose Show debug logs (SSE, relay, internal events)`;
6819
6833
  var STATUS_USAGE = `Usage: vent-hq status <run-id> [options]
6820
6834
 
6821
6835
  Options:
@@ -6861,7 +6875,8 @@ async function main() {
6861
6875
  "api-key": { type: "string" },
6862
6876
  json: { type: "boolean", default: false },
6863
6877
  submit: { type: "boolean", default: false },
6864
- "no-stream": { type: "boolean", default: false }
6878
+ "no-stream": { type: "boolean", default: false },
6879
+ verbose: { type: "boolean", default: false }
6865
6880
  },
6866
6881
  strict: true
6867
6882
  });
@@ -6898,7 +6913,8 @@ async function main() {
6898
6913
  test: values.test,
6899
6914
  apiKey: values["api-key"],
6900
6915
  json: values.json,
6901
- submit: values.submit || values["no-stream"]
6916
+ submit: values.submit || values["no-stream"],
6917
+ verbose: values.verbose
6902
6918
  });
6903
6919
  }
6904
6920
  case "status": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vent-hq",
3
- "version": "0.7.8",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "description": "Vent CLI — CI/CD for voice AI agents",
6
6
  "bin": {
@@ -9,10 +9,6 @@
9
9
  "files": [
10
10
  "dist"
11
11
  ],
12
- "scripts": {
13
- "build": "node scripts/bundle.mjs",
14
- "clean": "rm -rf dist"
15
- },
16
12
  "keywords": [
17
13
  "vent",
18
14
  "cli",
@@ -37,8 +33,12 @@
37
33
  },
38
34
  "devDependencies": {
39
35
  "@types/ws": "^8.5.0",
40
- "@vent/relay-client": "workspace:*",
41
- "@vent/shared": "workspace:*",
42
- "esbuild": "^0.24.0"
36
+ "esbuild": "^0.24.0",
37
+ "@vent/relay-client": "0.1.0",
38
+ "@vent/shared": "0.0.1"
39
+ },
40
+ "scripts": {
41
+ "build": "node scripts/bundle.mjs",
42
+ "clean": "rm -rf dist"
43
43
  }
44
- }
44
+ }
@@ -1,51 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./chunk-U4M3XDTH.mjs";
3
-
4
- // package.json
5
- var package_default = {
6
- name: "vent-hq",
7
- version: "0.7.2",
8
- type: "module",
9
- description: "Vent CLI \u2014 CI/CD for voice AI agents",
10
- bin: {
11
- "vent-hq": "dist/index.mjs"
12
- },
13
- files: [
14
- "dist"
15
- ],
16
- scripts: {
17
- build: "node scripts/bundle.mjs",
18
- clean: "rm -rf dist"
19
- },
20
- keywords: [
21
- "vent",
22
- "cli",
23
- "voice",
24
- "agent",
25
- "testing",
26
- "ci-cd"
27
- ],
28
- license: "MIT",
29
- publishConfig: {
30
- access: "public"
31
- },
32
- repository: {
33
- type: "git",
34
- url: "https://github.com/vent-hq/vent",
35
- directory: "packages/cli"
36
- },
37
- homepage: "https://ventmcp.dev",
38
- dependencies: {
39
- "@clack/prompts": "^1.1.0",
40
- ws: "^8.18.0"
41
- },
42
- devDependencies: {
43
- "@types/ws": "^8.5.0",
44
- "@vent/relay-client": "workspace:*",
45
- "@vent/shared": "workspace:*",
46
- esbuild: "^0.24.0"
47
- }
48
- };
49
- export {
50
- package_default as default
51
- };
@@ -1,51 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./chunk-U4M3XDTH.mjs";
3
-
4
- // package.json
5
- var package_default = {
6
- name: "vent-hq",
7
- version: "0.7.6",
8
- type: "module",
9
- description: "Vent CLI \u2014 CI/CD for voice AI agents",
10
- bin: {
11
- "vent-hq": "dist/index.mjs"
12
- },
13
- files: [
14
- "dist"
15
- ],
16
- scripts: {
17
- build: "node scripts/bundle.mjs",
18
- clean: "rm -rf dist"
19
- },
20
- keywords: [
21
- "vent",
22
- "cli",
23
- "voice",
24
- "agent",
25
- "testing",
26
- "ci-cd"
27
- ],
28
- license: "MIT",
29
- publishConfig: {
30
- access: "public"
31
- },
32
- repository: {
33
- type: "git",
34
- url: "https://github.com/vent-hq/vent",
35
- directory: "packages/cli"
36
- },
37
- homepage: "https://ventmcp.dev",
38
- dependencies: {
39
- "@clack/prompts": "^1.1.0",
40
- ws: "^8.18.0"
41
- },
42
- devDependencies: {
43
- "@types/ws": "^8.5.0",
44
- "@vent/relay-client": "workspace:*",
45
- "@vent/shared": "workspace:*",
46
- esbuild: "^0.24.0"
47
- }
48
- };
49
- export {
50
- package_default as default
51
- };
@@ -1,51 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./chunk-U4M3XDTH.mjs";
3
-
4
- // package.json
5
- var package_default = {
6
- name: "vent-hq",
7
- version: "0.7.5",
8
- type: "module",
9
- description: "Vent CLI \u2014 CI/CD for voice AI agents",
10
- bin: {
11
- "vent-hq": "dist/index.mjs"
12
- },
13
- files: [
14
- "dist"
15
- ],
16
- scripts: {
17
- build: "node scripts/bundle.mjs",
18
- clean: "rm -rf dist"
19
- },
20
- keywords: [
21
- "vent",
22
- "cli",
23
- "voice",
24
- "agent",
25
- "testing",
26
- "ci-cd"
27
- ],
28
- license: "MIT",
29
- publishConfig: {
30
- access: "public"
31
- },
32
- repository: {
33
- type: "git",
34
- url: "https://github.com/vent-hq/vent",
35
- directory: "packages/cli"
36
- },
37
- homepage: "https://ventmcp.dev",
38
- dependencies: {
39
- "@clack/prompts": "^1.1.0",
40
- ws: "^8.18.0"
41
- },
42
- devDependencies: {
43
- "@types/ws": "^8.5.0",
44
- "@vent/relay-client": "workspace:*",
45
- "@vent/shared": "workspace:*",
46
- esbuild: "^0.24.0"
47
- }
48
- };
49
- export {
50
- package_default as default
51
- };
@@ -1,51 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./chunk-U4M3XDTH.mjs";
3
-
4
- // package.json
5
- var package_default = {
6
- name: "vent-hq",
7
- version: "0.7.3",
8
- type: "module",
9
- description: "Vent CLI \u2014 CI/CD for voice AI agents",
10
- bin: {
11
- "vent-hq": "dist/index.mjs"
12
- },
13
- files: [
14
- "dist"
15
- ],
16
- scripts: {
17
- build: "node scripts/bundle.mjs",
18
- clean: "rm -rf dist"
19
- },
20
- keywords: [
21
- "vent",
22
- "cli",
23
- "voice",
24
- "agent",
25
- "testing",
26
- "ci-cd"
27
- ],
28
- license: "MIT",
29
- publishConfig: {
30
- access: "public"
31
- },
32
- repository: {
33
- type: "git",
34
- url: "https://github.com/vent-hq/vent",
35
- directory: "packages/cli"
36
- },
37
- homepage: "https://ventmcp.dev",
38
- dependencies: {
39
- "@clack/prompts": "^1.1.0",
40
- ws: "^8.18.0"
41
- },
42
- devDependencies: {
43
- "@types/ws": "^8.5.0",
44
- "@vent/relay-client": "workspace:*",
45
- "@vent/shared": "workspace:*",
46
- esbuild: "^0.24.0"
47
- }
48
- };
49
- export {
50
- package_default as default
51
- };
@@ -1,51 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./chunk-U4M3XDTH.mjs";
3
-
4
- // package.json
5
- var package_default = {
6
- name: "vent-hq",
7
- version: "0.7.4",
8
- type: "module",
9
- description: "Vent CLI \u2014 CI/CD for voice AI agents",
10
- bin: {
11
- "vent-hq": "dist/index.mjs"
12
- },
13
- files: [
14
- "dist"
15
- ],
16
- scripts: {
17
- build: "node scripts/bundle.mjs",
18
- clean: "rm -rf dist"
19
- },
20
- keywords: [
21
- "vent",
22
- "cli",
23
- "voice",
24
- "agent",
25
- "testing",
26
- "ci-cd"
27
- ],
28
- license: "MIT",
29
- publishConfig: {
30
- access: "public"
31
- },
32
- repository: {
33
- type: "git",
34
- url: "https://github.com/vent-hq/vent",
35
- directory: "packages/cli"
36
- },
37
- homepage: "https://ventmcp.dev",
38
- dependencies: {
39
- "@clack/prompts": "^1.1.0",
40
- ws: "^8.18.0"
41
- },
42
- devDependencies: {
43
- "@types/ws": "^8.5.0",
44
- "@vent/relay-client": "workspace:*",
45
- "@vent/shared": "workspace:*",
46
- esbuild: "^0.24.0"
47
- }
48
- };
49
- export {
50
- package_default as default
51
- };
@@ -1,51 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./chunk-U4M3XDTH.mjs";
3
-
4
- // package.json
5
- var package_default = {
6
- name: "vent-hq",
7
- version: "0.7.7",
8
- type: "module",
9
- description: "Vent CLI \u2014 CI/CD for voice AI agents",
10
- bin: {
11
- "vent-hq": "dist/index.mjs"
12
- },
13
- files: [
14
- "dist"
15
- ],
16
- scripts: {
17
- build: "node scripts/bundle.mjs",
18
- clean: "rm -rf dist"
19
- },
20
- keywords: [
21
- "vent",
22
- "cli",
23
- "voice",
24
- "agent",
25
- "testing",
26
- "ci-cd"
27
- ],
28
- license: "MIT",
29
- publishConfig: {
30
- access: "public"
31
- },
32
- repository: {
33
- type: "git",
34
- url: "https://github.com/vent-hq/vent",
35
- directory: "packages/cli"
36
- },
37
- homepage: "https://ventmcp.dev",
38
- dependencies: {
39
- "@clack/prompts": "^1.1.0",
40
- ws: "^8.18.0"
41
- },
42
- devDependencies: {
43
- "@types/ws": "^8.5.0",
44
- "@vent/relay-client": "workspace:*",
45
- "@vent/shared": "workspace:*",
46
- esbuild: "^0.24.0"
47
- }
48
- };
49
- export {
50
- package_default as default
51
- };
@@ -1,51 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./chunk-U4M3XDTH.mjs";
3
-
4
- // package.json
5
- var package_default = {
6
- name: "vent-hq",
7
- version: "0.7.1",
8
- type: "module",
9
- description: "Vent CLI \u2014 CI/CD for voice AI agents",
10
- bin: {
11
- "vent-hq": "dist/index.mjs"
12
- },
13
- files: [
14
- "dist"
15
- ],
16
- scripts: {
17
- build: "node scripts/bundle.mjs",
18
- clean: "rm -rf dist"
19
- },
20
- keywords: [
21
- "vent",
22
- "cli",
23
- "voice",
24
- "agent",
25
- "testing",
26
- "ci-cd"
27
- ],
28
- license: "MIT",
29
- publishConfig: {
30
- access: "public"
31
- },
32
- repository: {
33
- type: "git",
34
- url: "https://github.com/vent-hq/vent",
35
- directory: "packages/cli"
36
- },
37
- homepage: "https://ventmcp.dev",
38
- dependencies: {
39
- "@clack/prompts": "^1.1.0",
40
- ws: "^8.18.0"
41
- },
42
- devDependencies: {
43
- "@types/ws": "^8.5.0",
44
- "@vent/relay-client": "workspace:*",
45
- "@vent/shared": "workspace:*",
46
- esbuild: "^0.24.0"
47
- }
48
- };
49
- export {
50
- package_default as default
51
- };