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 +21 -0
- package/dist/index.mjs +202 -186
- package/package.json +9 -9
- package/dist/package-445J55MZ.mjs +0 -51
- package/dist/package-GMLASC6S.mjs +0 -51
- package/dist/package-HCBRPLMU.mjs +0 -51
- package/dist/package-ILIQGITC.mjs +0 -51
- package/dist/package-PLKYMAN6.mjs +0 -51
- package/dist/package-PU7T6XCQ.mjs +0 -51
- package/dist/package-QCFQTJ7U.mjs +0 -51
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
|
-
|
|
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
|
-
|
|
621
|
+
debug(`api-key resolved (${apiKey.slice(0, 8)}\u2026)`);
|
|
609
622
|
let config;
|
|
610
623
|
try {
|
|
611
624
|
if (args.file) {
|
|
612
|
-
|
|
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
|
-
|
|
628
|
+
debug(`config parsed \u2014 keys: ${Object.keys(config).join(", ")}`);
|
|
616
629
|
} else if (args.config) {
|
|
617
630
|
config = JSON.parse(args.config);
|
|
618
|
-
|
|
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
|
-
|
|
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
|
-
|
|
675
|
+
debug(`auto-port assigned: ${freePort}`);
|
|
663
676
|
}
|
|
664
|
-
|
|
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
|
-
|
|
685
|
+
debug(`API response status: ${res.status}`);
|
|
673
686
|
submitResult = await res.json();
|
|
674
687
|
} catch (err) {
|
|
675
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
727
|
+
debug(`relay error: ${msg}`);
|
|
715
728
|
printError(`Relay failed: ${msg}`);
|
|
716
729
|
return 2;
|
|
717
730
|
}
|
|
718
731
|
}
|
|
719
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
759
|
+
debug(`run_complete: status=${status} exitCode=${exitCode}`);
|
|
747
760
|
}
|
|
748
761
|
}
|
|
749
|
-
|
|
762
|
+
debug(`SSE stream ended \u2014 received ${eventCount} events total`);
|
|
750
763
|
} catch (err) {
|
|
751
764
|
if (err.name !== "AbortError") {
|
|
752
|
-
|
|
765
|
+
debug(`stream error: ${err.message}`);
|
|
753
766
|
printError(`Stream error: ${err.message}`);
|
|
754
767
|
exitCode = 2;
|
|
755
768
|
} else {
|
|
756
|
-
|
|
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
|
-
|
|
775
|
+
debug("cleaning up relay\u2026");
|
|
763
776
|
await relay.cleanup();
|
|
764
|
-
|
|
777
|
+
debug("relay cleaned up");
|
|
765
778
|
}
|
|
766
779
|
}
|
|
767
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
"
|
|
41
|
-
"@vent/
|
|
42
|
-
"
|
|
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
|
-
};
|