agent-qa 0.1.18 → 0.1.20
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.cjs +465 -355
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +365 -255
- package/dist/cli.js.map +1 -1
- package/package.json +9 -9
package/dist/cli.cjs
CHANGED
|
@@ -612,10 +612,14 @@ function buildExampleTest(platform, variant) {
|
|
|
612
612
|
const expectedUrl = variant === "pass" ? "https://www.iana.org/help/example-domains" : "https://www.iana.org/example-domains";
|
|
613
613
|
const testId = variant === "pass" ? "t_amber-birch-coral-delta-ember-falcon-garden-harbor-island-jungle" : "t_bronze-cedar-dune-ember-fjord-glade-harbor-ivory-jade-kelp";
|
|
614
614
|
const name = variant === "pass" ? "Example passing test" : "Example failing test";
|
|
615
|
+
const browserOverride = variant === "fail" && hasWeb(platform) ? `use:
|
|
616
|
+
browser:
|
|
617
|
+
headless: false
|
|
618
|
+
` : "";
|
|
615
619
|
return `test-id: ${testId}
|
|
616
620
|
name: ${name}
|
|
617
621
|
target: ${defaultExampleTargetName(platform)}
|
|
618
|
-
steps:
|
|
622
|
+
${browserOverride}steps:
|
|
619
623
|
- Verify the page says "Example Domain"
|
|
620
624
|
- Click on "Learn More"
|
|
621
625
|
- Verify the page url is "${expectedUrl}"
|
|
@@ -2117,12 +2121,12 @@ function createDoctorCommand() {
|
|
|
2117
2121
|
|
|
2118
2122
|
// src/commands/run.ts
|
|
2119
2123
|
var import_node_crypto = require("crypto");
|
|
2120
|
-
var
|
|
2124
|
+
var import_node_path10 = require("path");
|
|
2121
2125
|
var import_commander6 = require("commander");
|
|
2122
|
-
var
|
|
2126
|
+
var import_picocolors8 = __toESM(require("picocolors"), 1);
|
|
2123
2127
|
init_config();
|
|
2124
|
-
var import_agent_qa_core7 = require("@vostride/agent-qa-core");
|
|
2125
2128
|
var import_agent_qa_core8 = require("@vostride/agent-qa-core");
|
|
2129
|
+
var import_agent_qa_core9 = require("@vostride/agent-qa-core");
|
|
2126
2130
|
|
|
2127
2131
|
// src/devices.ts
|
|
2128
2132
|
var import_node_fs3 = require("fs");
|
|
@@ -2215,10 +2219,92 @@ function resolveProviderCredentials(transport, localBindings) {
|
|
|
2215
2219
|
|
|
2216
2220
|
// src/commands/run.ts
|
|
2217
2221
|
init_llm_utils();
|
|
2218
|
-
var
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
var
|
|
2222
|
+
var import_agent_qa_core10 = require("@vostride/agent-qa-core");
|
|
2223
|
+
|
|
2224
|
+
// src/version-notice.ts
|
|
2225
|
+
var import_node_fs4 = require("fs");
|
|
2226
|
+
var import_node_path9 = require("path");
|
|
2227
|
+
var import_picocolors7 = __toESM(require("picocolors"), 1);
|
|
2228
|
+
var import_agent_qa_core7 = require("@vostride/agent-qa-core");
|
|
2229
|
+
var RELEASES_URL = "https://github.com/vostride/agent-qa/releases";
|
|
2230
|
+
var UPDATE_COMMANDS = {
|
|
2231
|
+
pnpm: "pnpm add -D agent-qa@latest",
|
|
2232
|
+
npm: "npm install --save-dev agent-qa@latest",
|
|
2233
|
+
yarn: "yarn add --dev agent-qa@latest",
|
|
2234
|
+
bun: "bun add --dev agent-qa@latest"
|
|
2235
|
+
};
|
|
2236
|
+
var LOCKFILES = [
|
|
2237
|
+
{ file: "pnpm-lock.yaml", packageManager: "pnpm" },
|
|
2238
|
+
{ file: "package-lock.json", packageManager: "npm" },
|
|
2239
|
+
{ file: "npm-shrinkwrap.json", packageManager: "npm" },
|
|
2240
|
+
{ file: "yarn.lock", packageManager: "yarn" },
|
|
2241
|
+
{ file: "bun.lock", packageManager: "bun" },
|
|
2242
|
+
{ file: "bun.lockb", packageManager: "bun" }
|
|
2243
|
+
];
|
|
2244
|
+
function shouldPrintAgentQaUpdateNotice(context) {
|
|
2245
|
+
return Boolean(
|
|
2246
|
+
context.reporterSelection.console && !context.reporterSelection.stdoutLive && context.liveEvents !== "true" && context.effectiveLogLevel !== "quiet" && context.effectiveLogLevel !== "silent"
|
|
2247
|
+
);
|
|
2248
|
+
}
|
|
2249
|
+
function inferAgentQaDevDependencyUpdateCommand(cwd = process.cwd()) {
|
|
2250
|
+
try {
|
|
2251
|
+
const packageJsonPath = (0, import_node_path9.join)(cwd, "package.json");
|
|
2252
|
+
if (!(0, import_node_fs4.existsSync)(packageJsonPath)) return void 0;
|
|
2253
|
+
const manifest = JSON.parse((0, import_node_fs4.readFileSync)(packageJsonPath, "utf-8"));
|
|
2254
|
+
if (!isRecord(manifest) || !hasAgentQaDevDependency(manifest)) return void 0;
|
|
2255
|
+
const packageManager = inferPackageManager(manifest, cwd);
|
|
2256
|
+
return packageManager ? UPDATE_COMMANDS[packageManager] : void 0;
|
|
2257
|
+
} catch {
|
|
2258
|
+
return void 0;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
async function printAgentQaUpdateNoticeIfNeeded(context) {
|
|
2262
|
+
try {
|
|
2263
|
+
if (!shouldPrintAgentQaUpdateNotice(context)) return;
|
|
2264
|
+
const status = await (0, import_agent_qa_core7.getAgentQaUpdateStatus)();
|
|
2265
|
+
const installedVersion = status.installedVersion.trim();
|
|
2266
|
+
const latestVersion = typeof status.latestVersion === "string" ? status.latestVersion.trim() : "";
|
|
2267
|
+
if (!status.updateAvailable || !latestVersion) return;
|
|
2268
|
+
const command = inferAgentQaDevDependencyUpdateCommand(context.cwd);
|
|
2269
|
+
const log = context.log ?? console.log;
|
|
2270
|
+
log();
|
|
2271
|
+
log(`${import_picocolors7.default.bold("Update available:")} agent-qa v${latestVersion}`);
|
|
2272
|
+
if (installedVersion) {
|
|
2273
|
+
log(import_picocolors7.default.dim(`Current version: v${installedVersion}`));
|
|
2274
|
+
}
|
|
2275
|
+
log(`Releases: ${import_picocolors7.default.cyan(RELEASES_URL)}`);
|
|
2276
|
+
if (command) {
|
|
2277
|
+
log(`Update: ${import_picocolors7.default.cyan(command)}`);
|
|
2278
|
+
}
|
|
2279
|
+
} catch {
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
function hasAgentQaDevDependency(manifest) {
|
|
2283
|
+
const devDependencies = manifest.devDependencies;
|
|
2284
|
+
return isRecord(devDependencies) && Object.prototype.hasOwnProperty.call(devDependencies, "agent-qa");
|
|
2285
|
+
}
|
|
2286
|
+
function inferPackageManager(manifest, cwd) {
|
|
2287
|
+
if (typeof manifest.packageManager === "string") {
|
|
2288
|
+
return packageManagerFromSpecifier(manifest.packageManager);
|
|
2289
|
+
}
|
|
2290
|
+
const matchingLockfiles = LOCKFILES.filter(({ file }) => (0, import_node_fs4.existsSync)((0, import_node_path9.join)(cwd, file)));
|
|
2291
|
+
return matchingLockfiles.length === 1 ? matchingLockfiles[0].packageManager : void 0;
|
|
2292
|
+
}
|
|
2293
|
+
function packageManagerFromSpecifier(specifier) {
|
|
2294
|
+
const name = specifier.split("@")[0];
|
|
2295
|
+
if (name === "pnpm" || name === "npm" || name === "yarn" || name === "bun") {
|
|
2296
|
+
return name;
|
|
2297
|
+
}
|
|
2298
|
+
return void 0;
|
|
2299
|
+
}
|
|
2300
|
+
function isRecord(value) {
|
|
2301
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
// src/commands/run.ts
|
|
2305
|
+
var RUNTIME_ARTIFACTS_DIR = import_agent_qa_core8.DEFAULT_AGENT_QA_ARTIFACTS_DIR || ".agent-qa/artifacts";
|
|
2306
|
+
var RUNTIME_CACHE_DIR = import_agent_qa_core8.DEFAULT_AGENT_QA_CACHE_DIR || ".agent-qa/cache";
|
|
2307
|
+
var RUNTIME_VIDEOS_DIR = import_agent_qa_core8.DEFAULT_AGENT_QA_VIDEOS_DIR || ".agent-qa/artifacts/videos";
|
|
2222
2308
|
var _appiumManager = null;
|
|
2223
2309
|
async function acquireAppium(logLevel) {
|
|
2224
2310
|
if (process.env.AGENT_QA_APPIUM_URL) return process.env.AGENT_QA_APPIUM_URL;
|
|
@@ -2347,13 +2433,13 @@ function resolveDashboardVideoDir(config, configDir, dashboardEnabled) {
|
|
|
2347
2433
|
const recordingCfg = config.services?.recording;
|
|
2348
2434
|
const configuredVideoDir = recordingCfg?.videoDir;
|
|
2349
2435
|
if (configuredVideoDir) {
|
|
2350
|
-
return (0,
|
|
2436
|
+
return (0, import_node_path10.resolve)(configDir, configuredVideoDir);
|
|
2351
2437
|
}
|
|
2352
2438
|
if (dashboardEnabled) {
|
|
2353
2439
|
const dashArtifactsDir = config.services?.dashboard?.artifactsDir;
|
|
2354
|
-
return (0,
|
|
2440
|
+
return (0, import_node_path10.resolve)(configDir, dashArtifactsDir ?? RUNTIME_ARTIFACTS_DIR, "videos");
|
|
2355
2441
|
}
|
|
2356
|
-
return (0,
|
|
2442
|
+
return (0, import_node_path10.resolve)(configDir, RUNTIME_VIDEOS_DIR);
|
|
2357
2443
|
}
|
|
2358
2444
|
var SUPPORTED_REPORTERS = ["console", "junit", "stdout-live", "dashboard"];
|
|
2359
2445
|
function parseVarFlags(flags) {
|
|
@@ -2373,8 +2459,8 @@ function resolveRunnerAttribute(transport) {
|
|
|
2373
2459
|
return transport === "browserstack" ? "browserstack" : "local";
|
|
2374
2460
|
}
|
|
2375
2461
|
function buildCliRunAttributes(userAttributes, runner) {
|
|
2376
|
-
return (0,
|
|
2377
|
-
(0,
|
|
2462
|
+
return (0, import_agent_qa_core8.mergeRunAttributes)(
|
|
2463
|
+
(0, import_agent_qa_core8.buildInternalRunAttributes)({ trigger: "cli", runner }),
|
|
2378
2464
|
userAttributes
|
|
2379
2465
|
);
|
|
2380
2466
|
}
|
|
@@ -2387,17 +2473,17 @@ function readRunAttributesFromEnv() {
|
|
|
2387
2473
|
} catch {
|
|
2388
2474
|
throw new Error("inherited run attributes: AGENT_QA_RUN_ATTRIBUTES_JSON must contain valid run attributes JSON");
|
|
2389
2475
|
}
|
|
2390
|
-
const attributes = (0,
|
|
2476
|
+
const attributes = (0, import_agent_qa_core8.validateTrustedRunAttributes)(parsed, "inherited run attributes");
|
|
2391
2477
|
return Object.keys(attributes).length > 0 ? attributes : void 0;
|
|
2392
2478
|
}
|
|
2393
2479
|
function resolveCliRunAttributes(userAttributes, runner, inheritedAttributes) {
|
|
2394
2480
|
if (!inheritedAttributes) return buildCliRunAttributes(userAttributes, runner);
|
|
2395
|
-
const trigger = inheritedAttributes[
|
|
2481
|
+
const trigger = inheritedAttributes[import_agent_qa_core8.ATTR_TRIGGER];
|
|
2396
2482
|
const inheritedUserAttributes = Object.fromEntries(
|
|
2397
|
-
Object.entries(inheritedAttributes).filter(([key]) => key !==
|
|
2483
|
+
Object.entries(inheritedAttributes).filter(([key]) => key !== import_agent_qa_core8.ATTR_TRIGGER && key !== import_agent_qa_core8.ATTR_RUNNER)
|
|
2398
2484
|
);
|
|
2399
|
-
return (0,
|
|
2400
|
-
(0,
|
|
2485
|
+
return (0, import_agent_qa_core8.mergeRunAttributes)(
|
|
2486
|
+
(0, import_agent_qa_core8.buildInternalRunAttributes)({ trigger: trigger ?? "cli", runner }),
|
|
2401
2487
|
inheritedUserAttributes
|
|
2402
2488
|
);
|
|
2403
2489
|
}
|
|
@@ -2406,7 +2492,7 @@ function writeRunAttributesToEnv(attributes) {
|
|
|
2406
2492
|
}
|
|
2407
2493
|
function printRunAttributes(attributes) {
|
|
2408
2494
|
if (!attributes || Object.keys(attributes).length === 0) return;
|
|
2409
|
-
const block = (0,
|
|
2495
|
+
const block = (0, import_agent_qa_core8.formatRunAttributesBlock)(attributes);
|
|
2410
2496
|
if (block) console.log(block);
|
|
2411
2497
|
}
|
|
2412
2498
|
function resolveReporterSelection(opts, dashboardConfigured) {
|
|
@@ -2447,6 +2533,13 @@ async function flushAnalyticsRunReporter(reporter) {
|
|
|
2447
2533
|
} catch {
|
|
2448
2534
|
}
|
|
2449
2535
|
}
|
|
2536
|
+
async function maybePrintPostRunUpdateNotice(context) {
|
|
2537
|
+
try {
|
|
2538
|
+
if (!shouldPrintAgentQaUpdateNotice(context)) return;
|
|
2539
|
+
await printAgentQaUpdateNoticeIfNeeded(context);
|
|
2540
|
+
} catch {
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2450
2543
|
async function captureSingleAnalyticsResult(reporter, result) {
|
|
2451
2544
|
if (!reporter?.onRunEnd) {
|
|
2452
2545
|
await flushAnalyticsRunReporter(reporter);
|
|
@@ -2467,37 +2560,37 @@ async function captureSingleAnalyticsResult(reporter, result) {
|
|
|
2467
2560
|
function formatParseErrors(errors) {
|
|
2468
2561
|
for (const err of errors) {
|
|
2469
2562
|
const location = `${err.file}:${err.line}:${err.column}`;
|
|
2470
|
-
const severity = err.severity === "error" ?
|
|
2563
|
+
const severity = err.severity === "error" ? import_picocolors8.default.red("error") : import_picocolors8.default.yellow("warning");
|
|
2471
2564
|
console.error(`${severity}: ${err.message}`);
|
|
2472
|
-
console.error(` ${
|
|
2565
|
+
console.error(` ${import_picocolors8.default.blue("-->")} ${location}`);
|
|
2473
2566
|
if (err.source) {
|
|
2474
|
-
console.error(` ${
|
|
2567
|
+
console.error(` ${import_picocolors8.default.blue("|")} ${err.source}`);
|
|
2475
2568
|
}
|
|
2476
2569
|
if (err.suggestion) {
|
|
2477
|
-
console.error(` ${
|
|
2570
|
+
console.error(` ${import_picocolors8.default.blue("=")} ${import_picocolors8.default.bold("help")}: ${err.suggestion}`);
|
|
2478
2571
|
}
|
|
2479
2572
|
console.error();
|
|
2480
2573
|
}
|
|
2481
2574
|
}
|
|
2482
2575
|
function createTestRunId() {
|
|
2483
|
-
return process.env.AGENT_QA_RUN_ID ?? (0,
|
|
2576
|
+
return process.env.AGENT_QA_RUN_ID ?? (0, import_agent_qa_core8.generateRunId)();
|
|
2484
2577
|
}
|
|
2485
2578
|
function createSuiteRunId() {
|
|
2486
|
-
return process.env.AGENT_QA_SUITE_QUEUE_ID ?? process.env.AGENT_QA_RUN_ID ?? (0,
|
|
2579
|
+
return process.env.AGENT_QA_SUITE_QUEUE_ID ?? process.env.AGENT_QA_RUN_ID ?? (0, import_agent_qa_core8.generateRunId)();
|
|
2487
2580
|
}
|
|
2488
2581
|
function printRunId(runId) {
|
|
2489
2582
|
if (runId) console.log(`Run ID: ${runId}`);
|
|
2490
2583
|
}
|
|
2491
2584
|
function printMemoryStatus(log) {
|
|
2492
2585
|
const parts = [];
|
|
2493
|
-
if (log.added > 0) parts.push(
|
|
2586
|
+
if (log.added > 0) parts.push(import_picocolors8.default.green(`${log.added} added`));
|
|
2494
2587
|
if (log.confirmed > 0) parts.push(`${log.confirmed} confirmed`);
|
|
2495
|
-
if (log.deprecated > 0) parts.push(
|
|
2588
|
+
if (log.deprecated > 0) parts.push(import_picocolors8.default.yellow(`${log.deprecated} deprecated`));
|
|
2496
2589
|
if (parts.length === 0) parts.push("no changes");
|
|
2497
2590
|
const secs = log.curatorDuration >= 1e3 ? `${Math.round(log.curatorDuration / 1e3)}s` : `${log.curatorDuration}ms`;
|
|
2498
|
-
console.log(` ${
|
|
2591
|
+
console.log(` ${import_picocolors8.default.dim("Memory:")} ${parts.join(", ")} ${import_picocolors8.default.dim(`(${secs})`)}`);
|
|
2499
2592
|
for (const err of log.errors) {
|
|
2500
|
-
console.log(` ${
|
|
2593
|
+
console.log(` ${import_picocolors8.default.dim("Memory:")} ${import_picocolors8.default.yellow(`warning -- ${err}`)}`);
|
|
2501
2594
|
}
|
|
2502
2595
|
}
|
|
2503
2596
|
function isGlobLikePattern(value) {
|
|
@@ -2512,15 +2605,15 @@ function matchesCliWorkspacePattern(workspace, kind, pattern, record) {
|
|
|
2512
2605
|
testMatch: kind === "test" ? [pattern] : workspace.testMatch,
|
|
2513
2606
|
suiteMatch: kind === "suite" ? [pattern] : workspace.suiteMatch
|
|
2514
2607
|
};
|
|
2515
|
-
return (0,
|
|
2608
|
+
return (0, import_agent_qa_core9.isWorkspacePathMatch)({
|
|
2516
2609
|
workspace: patternWorkspace,
|
|
2517
2610
|
kind,
|
|
2518
2611
|
workspaceRelativePath: record.workspaceRelativePath
|
|
2519
2612
|
});
|
|
2520
2613
|
}
|
|
2521
2614
|
async function discoverFilesForCliPatterns(workspace, patterns) {
|
|
2522
|
-
const testRecords = await (0,
|
|
2523
|
-
const suiteRecords = await (0,
|
|
2615
|
+
const testRecords = await (0, import_agent_qa_core9.discoverWorkspaceFiles)({ workspace, kind: "test" });
|
|
2616
|
+
const suiteRecords = await (0, import_agent_qa_core9.discoverWorkspaceFiles)({ workspace, kind: "suite" });
|
|
2524
2617
|
const discoveredByKind = {
|
|
2525
2618
|
test: testRecords,
|
|
2526
2619
|
suite: suiteRecords
|
|
@@ -2541,7 +2634,7 @@ async function discoverFilesForCliPatterns(workspace, patterns) {
|
|
|
2541
2634
|
}
|
|
2542
2635
|
continue;
|
|
2543
2636
|
}
|
|
2544
|
-
const record = await (0,
|
|
2637
|
+
const record = await (0, import_agent_qa_core9.resolveWorkspaceFileTarget)({
|
|
2545
2638
|
workspace,
|
|
2546
2639
|
kind,
|
|
2547
2640
|
filePath: pattern,
|
|
@@ -2569,21 +2662,21 @@ function buildHooksArtifact(hooks) {
|
|
|
2569
2662
|
async function loadRuntimeSecrets(input3) {
|
|
2570
2663
|
const configuredSecretsFile = input3.config.workspace?.secretsFile;
|
|
2571
2664
|
if (typeof configuredSecretsFile !== "string" || configuredSecretsFile.trim().length === 0) {
|
|
2572
|
-
console.error(
|
|
2665
|
+
console.error(import_picocolors8.default.red("Error: workspace.secretsFile is required. Run `agent-qa init` or set it in agent-qa.config.yaml."));
|
|
2573
2666
|
process.exit(1);
|
|
2574
2667
|
}
|
|
2575
|
-
const secretsPath = input3.secretsFilePath ?? (0,
|
|
2668
|
+
const secretsPath = input3.secretsFilePath ?? (0, import_node_path10.resolve)(input3.configDir, configuredSecretsFile);
|
|
2576
2669
|
if (!input3.existsSync(secretsPath)) {
|
|
2577
|
-
console.error(
|
|
2578
|
-
console.error(
|
|
2670
|
+
console.error(import_picocolors8.default.red(`Error: Secrets file not found: ${secretsPath}`));
|
|
2671
|
+
console.error(import_picocolors8.default.dim("Create the file before running tests. It may be empty, but it must exist."));
|
|
2579
2672
|
process.exit(1);
|
|
2580
2673
|
}
|
|
2581
2674
|
let secretsContent;
|
|
2582
2675
|
try {
|
|
2583
2676
|
secretsContent = input3.readFileSync(secretsPath, "utf-8");
|
|
2584
2677
|
} catch {
|
|
2585
|
-
console.error(
|
|
2586
|
-
console.error(
|
|
2678
|
+
console.error(import_picocolors8.default.red(`Error: Secrets file could not be read: ${secretsPath}`));
|
|
2679
|
+
console.error(import_picocolors8.default.dim("Check the path and file permissions before running tests."));
|
|
2587
2680
|
process.exit(1);
|
|
2588
2681
|
}
|
|
2589
2682
|
try {
|
|
@@ -2600,12 +2693,12 @@ async function loadRuntimeSecrets(input3) {
|
|
|
2600
2693
|
};
|
|
2601
2694
|
} catch (err) {
|
|
2602
2695
|
const message = err instanceof Error ? err.message : String(err);
|
|
2603
|
-
console.error(
|
|
2696
|
+
console.error(import_picocolors8.default.red(`Error: Secrets file is invalid: ${message}`));
|
|
2604
2697
|
process.exit(1);
|
|
2605
2698
|
}
|
|
2606
2699
|
}
|
|
2607
2700
|
function buildRunArtifactContext(input3) {
|
|
2608
|
-
return (0,
|
|
2701
|
+
return (0, import_agent_qa_core8.redactAuthStateValue)({
|
|
2609
2702
|
artifact: {
|
|
2610
2703
|
kind: input3.kind,
|
|
2611
2704
|
config: {
|
|
@@ -2657,7 +2750,7 @@ async function resolveDeviceAndFarmSession(config, deviceName, testPlatform, tes
|
|
|
2657
2750
|
}
|
|
2658
2751
|
const browserstackApp = mobileResolved?.app.install?.browserstack;
|
|
2659
2752
|
if (!browserstackApp && hasNativeAppIdentity(mobileResolved) && !isBrowserMode(resolved.match)) {
|
|
2660
|
-
throw new
|
|
2753
|
+
throw new import_agent_qa_core8.MobileSetupError({
|
|
2661
2754
|
category: "app-install",
|
|
2662
2755
|
message: `BrowserStack native app target "${mobileResolved?.targetName ?? "unknown"}" requires app.browserstack; app.path is local-only and is not used as a BrowserStack fallback.`,
|
|
2663
2756
|
platform: resolved.platform,
|
|
@@ -2681,9 +2774,9 @@ async function resolveDeviceAndFarmSession(config, deviceName, testPlatform, tes
|
|
|
2681
2774
|
appSourceTrace: mobileResolved?.sourceTrace
|
|
2682
2775
|
});
|
|
2683
2776
|
} catch (err) {
|
|
2684
|
-
if (err instanceof
|
|
2777
|
+
if (err instanceof import_agent_qa_core8.MobileSetupError) throw err;
|
|
2685
2778
|
if (browserstackApp) {
|
|
2686
|
-
throw new
|
|
2779
|
+
throw new import_agent_qa_core8.MobileSetupError({
|
|
2687
2780
|
category: "app-install",
|
|
2688
2781
|
message: `Failed to resolve BrowserStack app reference: ${err instanceof Error ? err.message : String(err)}`,
|
|
2689
2782
|
platform: resolved.platform,
|
|
@@ -2705,25 +2798,25 @@ function hasNativeAppIdentity(mobileResolved) {
|
|
|
2705
2798
|
return Boolean(app?.bundleId || app?.appPackage || app?.appActivity || app?.deepLinkAppId);
|
|
2706
2799
|
}
|
|
2707
2800
|
function wrapMobileSetupError(err) {
|
|
2708
|
-
if (err instanceof
|
|
2801
|
+
if (err instanceof import_agent_qa_core8.MobileSetupError) {
|
|
2709
2802
|
throw new Error(`${err.category}: ${err.message}`);
|
|
2710
2803
|
}
|
|
2711
2804
|
throw err;
|
|
2712
2805
|
}
|
|
2713
2806
|
function resolveMobileRunOrThrow(input3) {
|
|
2714
2807
|
try {
|
|
2715
|
-
return (0,
|
|
2808
|
+
return (0, import_agent_qa_core8.resolveMobileRunConfig)(input3);
|
|
2716
2809
|
} catch (err) {
|
|
2717
2810
|
wrapMobileSetupError(err);
|
|
2718
2811
|
}
|
|
2719
2812
|
}
|
|
2720
2813
|
function getSelectedAuthStateUse(use) {
|
|
2721
|
-
return (0,
|
|
2814
|
+
return (0, import_agent_qa_core8.normalizeAuthStateUse)(use);
|
|
2722
2815
|
}
|
|
2723
2816
|
async function resolveSelectedAuthStateForRun(input3) {
|
|
2724
2817
|
if (!input3.authState || input3.authState.load === false) return void 0;
|
|
2725
2818
|
try {
|
|
2726
|
-
return await (0,
|
|
2819
|
+
return await (0, import_agent_qa_core8.resolveAuthStateForRun)({
|
|
2727
2820
|
configDir: input3.configDir,
|
|
2728
2821
|
authStateDir: input3.config.services?.authState?.dir,
|
|
2729
2822
|
targetName: input3.resolvedTarget.name,
|
|
@@ -2731,14 +2824,14 @@ async function resolveSelectedAuthStateForRun(input3) {
|
|
|
2731
2824
|
target: { platform: input3.resolvedTarget.platform }
|
|
2732
2825
|
});
|
|
2733
2826
|
} catch (err) {
|
|
2734
|
-
console.error(
|
|
2827
|
+
console.error(import_picocolors8.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
2735
2828
|
process.exit(2);
|
|
2736
2829
|
}
|
|
2737
2830
|
}
|
|
2738
2831
|
function resolveAuthStateCapturePathsForRun(input3) {
|
|
2739
2832
|
if (input3.authState?.capture !== true) return void 0;
|
|
2740
2833
|
try {
|
|
2741
|
-
return (0,
|
|
2834
|
+
return (0, import_agent_qa_core8.resolveAuthStatePaths)({
|
|
2742
2835
|
configDir: input3.configDir,
|
|
2743
2836
|
authStateDir: input3.config.services?.authState?.dir,
|
|
2744
2837
|
targetName: input3.resolvedTarget.name,
|
|
@@ -2746,7 +2839,7 @@ function resolveAuthStateCapturePathsForRun(input3) {
|
|
|
2746
2839
|
target: { platform: input3.resolvedTarget.platform }
|
|
2747
2840
|
});
|
|
2748
2841
|
} catch (err) {
|
|
2749
|
-
console.error(
|
|
2842
|
+
console.error(import_picocolors8.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
2750
2843
|
process.exit(2);
|
|
2751
2844
|
}
|
|
2752
2845
|
}
|
|
@@ -2776,7 +2869,7 @@ async function captureAuthStateForRun(input3) {
|
|
|
2776
2869
|
const payload = await context.storageState({ indexedDB: true });
|
|
2777
2870
|
const capturedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2778
2871
|
const metadata = {
|
|
2779
|
-
version:
|
|
2872
|
+
version: import_agent_qa_core8.AUTH_STATE_SCHEMA_VERSION,
|
|
2780
2873
|
kind: "web",
|
|
2781
2874
|
target: input3.paths.targetName,
|
|
2782
2875
|
name: input3.paths.stateName,
|
|
@@ -2839,7 +2932,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
|
|
|
2839
2932
|
const dashboardCfg = config.services?.dashboard;
|
|
2840
2933
|
const reporterSelectionResult = resolveReporterSelection(opts, Boolean(dashboardCfg));
|
|
2841
2934
|
if (reporterSelectionResult.error) {
|
|
2842
|
-
console.error(
|
|
2935
|
+
console.error(import_picocolors8.default.red(`Error: ${reporterSelectionResult.error}`));
|
|
2843
2936
|
process.exit(2);
|
|
2844
2937
|
}
|
|
2845
2938
|
const reporterSelection = reporterSelectionResult.selection;
|
|
@@ -2855,7 +2948,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
|
|
|
2855
2948
|
activeSuiteDbs.add(dashboardDb);
|
|
2856
2949
|
return { dashboardDb, logStorage: dashboardDb };
|
|
2857
2950
|
} catch (err) {
|
|
2858
|
-
console.warn(
|
|
2951
|
+
console.warn(import_picocolors8.default.yellow(`Warning: Could not load dashboard reporter: ${err instanceof Error ? err.message : String(err)}`));
|
|
2859
2952
|
return {};
|
|
2860
2953
|
}
|
|
2861
2954
|
};
|
|
@@ -2903,8 +2996,8 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
|
|
|
2903
2996
|
const testEntries = [];
|
|
2904
2997
|
const suiteMembers = [];
|
|
2905
2998
|
for (const { test: relativePath, id: suiteEntryId } of suite.tests) {
|
|
2906
|
-
if (!(0,
|
|
2907
|
-
console.error(
|
|
2999
|
+
if (!(0, import_agent_qa_core10.isPathInsideDir)(relativePath, configDir)) {
|
|
3000
|
+
console.error(import_picocolors8.default.red(`Path traversal rejected: ${relativePath}`));
|
|
2908
3001
|
const runId = createSuiteRunId();
|
|
2909
3002
|
if (dashboardDb) {
|
|
2910
3003
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -2942,7 +3035,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
|
|
|
2942
3035
|
const fileContent = await readFile5(absolutePath, "utf-8");
|
|
2943
3036
|
const parseResult = parseTestFile2(fileContent, absolutePath);
|
|
2944
3037
|
if (parseResult.errors.length > 0) {
|
|
2945
|
-
console.error(
|
|
3038
|
+
console.error(import_picocolors8.default.red(`Parse errors in ${relativePath}:`));
|
|
2946
3039
|
formatParseErrors(parseResult.errors);
|
|
2947
3040
|
const runId = createSuiteRunId();
|
|
2948
3041
|
if (dashboardDb) {
|
|
@@ -2984,7 +3077,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
|
|
|
2984
3077
|
process.exit(2);
|
|
2985
3078
|
}
|
|
2986
3079
|
if (parseResult.tests.length === 0) {
|
|
2987
|
-
console.error(
|
|
3080
|
+
console.error(import_picocolors8.default.red(`No test definition found in ${relativePath}`));
|
|
2988
3081
|
const runId = createSuiteRunId();
|
|
2989
3082
|
if (dashboardDb) {
|
|
2990
3083
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -3021,12 +3114,12 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
|
|
|
3021
3114
|
const testDef = parseResult.tests[0];
|
|
3022
3115
|
const childAuthStateUse = getSelectedAuthStateUse(testDef.use);
|
|
3023
3116
|
if (suiteAuthStateUse && childAuthStateUse && childAuthStateUse.name !== suiteAuthStateUse.name) {
|
|
3024
|
-
console.error(
|
|
3117
|
+
console.error(import_picocolors8.default.red(`Suite auth state "${suiteAuthStateUse.name}" conflicts with child test auth state "${childAuthStateUse.name}". Use one primary auth state per suite.`));
|
|
3025
3118
|
process.exit(2);
|
|
3026
3119
|
}
|
|
3027
3120
|
if (testDef["test-id"] && testDef["test-id"] !== suiteEntryId) {
|
|
3028
3121
|
const mismatchMsg = `ID mismatch in suite "${suite.name}": test file ${relativePath} has test-id "${testDef["test-id"]}" but suite entry specifies id "${suiteEntryId}"`;
|
|
3029
|
-
console.error(
|
|
3122
|
+
console.error(import_picocolors8.default.red(mismatchMsg));
|
|
3030
3123
|
const runId = createSuiteRunId();
|
|
3031
3124
|
if (dashboardDb) {
|
|
3032
3125
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -3155,7 +3248,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
|
|
|
3155
3248
|
mobileResolved = { ...mobileResolved, appium: { url: appiumUrl, managed: !process.env.AGENT_QA_APPIUM_URL } };
|
|
3156
3249
|
}
|
|
3157
3250
|
} catch (err) {
|
|
3158
|
-
throw new
|
|
3251
|
+
throw new import_agent_qa_core8.MobileSetupError({
|
|
3159
3252
|
category: "appium-startup",
|
|
3160
3253
|
message: `Could not auto-start Appium: ${err instanceof Error ? err.message : String(err)}`,
|
|
3161
3254
|
platform: suitePlatform,
|
|
@@ -3267,7 +3360,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
|
|
|
3267
3360
|
let suiteMemoryProvider;
|
|
3268
3361
|
let suiteCircuitBreaker;
|
|
3269
3362
|
const suiteMemoryConfig = config.services?.memory;
|
|
3270
|
-
const suiteMemoryRoot = (0,
|
|
3363
|
+
const suiteMemoryRoot = (0, import_agent_qa_core8.resolveMemoryRoot)(config, configDir);
|
|
3271
3364
|
if (opts.memory !== false && suiteMemoryConfig?.enabled !== false) {
|
|
3272
3365
|
try {
|
|
3273
3366
|
const { createMemoryProvider } = await import("@vostride/agent-qa-core");
|
|
@@ -3277,7 +3370,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
|
|
|
3277
3370
|
curatorLockTimeout: suiteMemoryConfig?.curatorLockTimeout
|
|
3278
3371
|
});
|
|
3279
3372
|
} catch (err) {
|
|
3280
|
-
console.warn(` ${
|
|
3373
|
+
console.warn(` ${import_picocolors8.default.dim("Memory:")} init error -- ${err.message}`);
|
|
3281
3374
|
}
|
|
3282
3375
|
if (suiteMemoryConfig?.circuitBreakerEnabled !== false) {
|
|
3283
3376
|
const { CircuitBreaker } = await import("@vostride/agent-qa-core");
|
|
@@ -3422,19 +3515,19 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
|
|
|
3422
3515
|
const batchOutcome = await flushSuiteParents();
|
|
3423
3516
|
if (batchOutcome.cancelled || shutdownRequested) break;
|
|
3424
3517
|
if (opts.bail && batchOutcome.failed) {
|
|
3425
|
-
console.log(
|
|
3518
|
+
console.log(import_picocolors8.default.yellow("\nBailing out \u2014 stopping after first failed suite"));
|
|
3426
3519
|
break;
|
|
3427
3520
|
}
|
|
3428
3521
|
const outcome = await runOneSuiteFile(suiteFile, suite);
|
|
3429
3522
|
if (outcome.cancelled || shutdownRequested) break;
|
|
3430
3523
|
if (opts.bail && outcome.failed) {
|
|
3431
|
-
console.log(
|
|
3524
|
+
console.log(import_picocolors8.default.yellow("\nBailing out \u2014 stopping after first failed suite"));
|
|
3432
3525
|
break;
|
|
3433
3526
|
}
|
|
3434
3527
|
}
|
|
3435
3528
|
const finalBatchOutcome = await flushSuiteParents();
|
|
3436
3529
|
if (opts.bail && finalBatchOutcome.failed) {
|
|
3437
|
-
console.log(
|
|
3530
|
+
console.log(import_picocolors8.default.yellow("\nBailing out \u2014 stopping after first failed suite"));
|
|
3438
3531
|
}
|
|
3439
3532
|
process.removeListener("SIGINT", handleShutdown);
|
|
3440
3533
|
process.removeListener("SIGTERM", handleShutdown);
|
|
@@ -3447,7 +3540,7 @@ function createRunCommand() {
|
|
|
3447
3540
|
try {
|
|
3448
3541
|
const modeFlags = [opts.test, opts.suite, opts.all].filter(Boolean).length;
|
|
3449
3542
|
if (modeFlags > 1) {
|
|
3450
|
-
console.error(
|
|
3543
|
+
console.error(import_picocolors8.default.red("Error: --test, --suite, and --all are mutually exclusive."));
|
|
3451
3544
|
process.exit(1);
|
|
3452
3545
|
}
|
|
3453
3546
|
const program2 = command.parent;
|
|
@@ -3463,8 +3556,8 @@ function createRunCommand() {
|
|
|
3463
3556
|
flags: flagOverrides,
|
|
3464
3557
|
loadAuthPlugins: !(opts.dryRun || opts.listTests)
|
|
3465
3558
|
});
|
|
3466
|
-
const configFilePath = (0,
|
|
3467
|
-
const workspacePaths = (0,
|
|
3559
|
+
const configFilePath = (0, import_node_path10.resolve)(globalOpts.config ?? "agent-qa.config.yaml");
|
|
3560
|
+
const workspacePaths = (0, import_agent_qa_core9.resolveWorkspacePaths)({
|
|
3468
3561
|
config,
|
|
3469
3562
|
configPath: configFilePath
|
|
3470
3563
|
});
|
|
@@ -3475,20 +3568,20 @@ function createRunCommand() {
|
|
|
3475
3568
|
} catch {
|
|
3476
3569
|
}
|
|
3477
3570
|
const { parseEnvFile } = await import("@vostride/agent-qa-core");
|
|
3478
|
-
const { readFileSync:
|
|
3571
|
+
const { readFileSync: readFileSync6, existsSync: existsSync9 } = await import("fs");
|
|
3479
3572
|
const configDir = workspacePaths.configDir;
|
|
3480
3573
|
const cliVars = parseVarFlags(opts.var);
|
|
3481
3574
|
const inheritedContextRunId = process.env.AGENT_QA_RUN_ID ?? process.env.AGENT_QA_SUITE_QUEUE_ID ?? process.env.AGENT_QA_PARENT_RUN_ID;
|
|
3482
3575
|
const inheritedRunAttributes = inheritedContextRunId ? readRunAttributesFromEnv() : void 0;
|
|
3483
3576
|
let userRunAttributes;
|
|
3484
3577
|
try {
|
|
3485
|
-
const parsedRunAttrs = (0,
|
|
3578
|
+
const parsedRunAttrs = (0, import_agent_qa_core8.parseRunAttrFlags)(opts.runAttr);
|
|
3486
3579
|
userRunAttributes = parsedRunAttrs.attributes;
|
|
3487
3580
|
for (const duplicateKey of parsedRunAttrs.duplicateKeys) {
|
|
3488
3581
|
console.warn(`Warning: duplicate --run-attr key "${duplicateKey}"; using last value`);
|
|
3489
3582
|
}
|
|
3490
3583
|
} catch (err) {
|
|
3491
|
-
console.error(
|
|
3584
|
+
console.error(import_picocolors8.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
3492
3585
|
process.exit(2);
|
|
3493
3586
|
}
|
|
3494
3587
|
const inlineVars = {};
|
|
@@ -3496,37 +3589,37 @@ function createRunCommand() {
|
|
|
3496
3589
|
config,
|
|
3497
3590
|
configDir,
|
|
3498
3591
|
secretsFilePath: workspacePaths.secretsFile.absolutePath,
|
|
3499
|
-
readFileSync:
|
|
3500
|
-
existsSync:
|
|
3592
|
+
readFileSync: readFileSync6,
|
|
3593
|
+
existsSync: existsSync9
|
|
3501
3594
|
});
|
|
3502
3595
|
let envFileVars = {};
|
|
3503
3596
|
let rawEnvFileContent = null;
|
|
3504
3597
|
let resolvedEnvFilePath = null;
|
|
3505
3598
|
resolvedEnvFilePath = workspacePaths.envFile.absolutePath;
|
|
3506
|
-
if (!
|
|
3507
|
-
console.error(
|
|
3599
|
+
if (!existsSync9(workspacePaths.envFile.absolutePath)) {
|
|
3600
|
+
console.error(import_picocolors8.default.red(`Error: Env file not found: ${workspacePaths.envFile.absolutePath}`));
|
|
3508
3601
|
process.exit(1);
|
|
3509
3602
|
}
|
|
3510
3603
|
try {
|
|
3511
|
-
rawEnvFileContent =
|
|
3604
|
+
rawEnvFileContent = readFileSync6(workspacePaths.envFile.absolutePath, "utf-8");
|
|
3512
3605
|
envFileVars = parseEnvFile(rawEnvFileContent);
|
|
3513
3606
|
} catch {
|
|
3514
|
-
console.error(
|
|
3607
|
+
console.error(import_picocolors8.default.red(`Error: Env file could not be read: ${workspacePaths.envFile.absolutePath}`));
|
|
3515
3608
|
process.exit(1);
|
|
3516
3609
|
}
|
|
3517
3610
|
let resolvedHooks;
|
|
3518
3611
|
let sandboxOptions;
|
|
3519
3612
|
const hooksFilePath = workspacePaths.hooksFile.absolutePath;
|
|
3520
|
-
if (!
|
|
3521
|
-
console.error(
|
|
3613
|
+
if (!existsSync9(hooksFilePath)) {
|
|
3614
|
+
console.error(import_picocolors8.default.red(`Error: Hooks file not found: ${hooksFilePath}`));
|
|
3522
3615
|
process.exit(1);
|
|
3523
3616
|
}
|
|
3524
3617
|
try {
|
|
3525
3618
|
const { parseHooksFile } = await import("@vostride/agent-qa-core");
|
|
3526
3619
|
const { hooks, errors } = await parseHooksFile(hooksFilePath);
|
|
3527
3620
|
if (errors.length > 0) {
|
|
3528
|
-
console.error(
|
|
3529
|
-
for (const err of errors) console.error(
|
|
3621
|
+
console.error(import_picocolors8.default.red("Hooks configuration errors:"));
|
|
3622
|
+
for (const err of errors) console.error(import_picocolors8.default.red(` - ${err}`));
|
|
3530
3623
|
process.exit(1);
|
|
3531
3624
|
}
|
|
3532
3625
|
resolvedHooks = new Map(hooks.map((h) => [h.id, h]));
|
|
@@ -3535,8 +3628,8 @@ function createRunCommand() {
|
|
|
3535
3628
|
secretRedactor: runtimeSecrets.secretRedactor
|
|
3536
3629
|
};
|
|
3537
3630
|
} catch (err) {
|
|
3538
|
-
console.error(
|
|
3539
|
-
if (err instanceof Error && err.message) console.error(
|
|
3631
|
+
console.error(import_picocolors8.default.red(`Error: Hooks file could not be read: ${hooksFilePath}`));
|
|
3632
|
+
if (err instanceof Error && err.message) console.error(import_picocolors8.default.dim(err.message));
|
|
3540
3633
|
process.exit(1);
|
|
3541
3634
|
}
|
|
3542
3635
|
let agentRulesContent;
|
|
@@ -3544,7 +3637,7 @@ function createRunCommand() {
|
|
|
3544
3637
|
try {
|
|
3545
3638
|
agentRulesContent = await readFileAsync(workspacePaths.agentRules.absolutePath, "utf-8");
|
|
3546
3639
|
} catch {
|
|
3547
|
-
console.error(
|
|
3640
|
+
console.error(import_picocolors8.default.red(`Error: Agent rules file not found: ${workspacePaths.agentRules.absolutePath}`));
|
|
3548
3641
|
process.exit(1);
|
|
3549
3642
|
}
|
|
3550
3643
|
const effectiveLogLevel = globalOpts.logLevel ?? (globalOpts.verbose ? "debug" : void 0) ?? (globalOpts.quiet ? "silent" : void 0) ?? config.services?.logging?.level ?? "warn";
|
|
@@ -3556,48 +3649,58 @@ function createRunCommand() {
|
|
|
3556
3649
|
discoveredTestFiles = selected.tests;
|
|
3557
3650
|
discoveredSuiteFiles = selected.suites;
|
|
3558
3651
|
} catch (err) {
|
|
3559
|
-
console.error(
|
|
3652
|
+
console.error(import_picocolors8.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
3560
3653
|
process.exit(1);
|
|
3561
3654
|
}
|
|
3562
3655
|
if (discoveredTestFiles.length === 0 && discoveredSuiteFiles.length === 0) {
|
|
3563
3656
|
const patternKind = patterns.every((pattern) => classifyRunPattern(pattern) === "suite") ? "suite" : "test";
|
|
3564
|
-
console.error(
|
|
3565
|
-
for (const p of patterns) console.error(
|
|
3657
|
+
console.error(import_picocolors8.default.red(`Error: No ${patternKind} files found matching patterns:`));
|
|
3658
|
+
for (const p of patterns) console.error(import_picocolors8.default.red(` ${p}`));
|
|
3566
3659
|
process.exit(2);
|
|
3567
3660
|
}
|
|
3568
3661
|
} else if (opts.suite) {
|
|
3569
|
-
discoveredSuiteFiles = (await (0,
|
|
3662
|
+
discoveredSuiteFiles = (await (0, import_agent_qa_core9.discoverWorkspaceFiles)({ workspace: workspacePaths, kind: "suite" })).map((record) => record.absolutePath);
|
|
3570
3663
|
if (discoveredSuiteFiles.length === 0) {
|
|
3571
|
-
console.error(
|
|
3664
|
+
console.error(import_picocolors8.default.red("Error: No suite files found matching workspace.suiteMatch"));
|
|
3572
3665
|
for (const p of workspacePaths.suiteMatch) {
|
|
3573
|
-
console.error(
|
|
3666
|
+
console.error(import_picocolors8.default.red(` ${p}`));
|
|
3574
3667
|
}
|
|
3575
3668
|
process.exit(2);
|
|
3576
3669
|
}
|
|
3577
3670
|
} else if (opts.all) {
|
|
3578
|
-
discoveredTestFiles = (await (0,
|
|
3579
|
-
discoveredSuiteFiles = (await (0,
|
|
3671
|
+
discoveredTestFiles = (await (0, import_agent_qa_core9.discoverWorkspaceFiles)({ workspace: workspacePaths, kind: "test" })).map((record) => record.absolutePath);
|
|
3672
|
+
discoveredSuiteFiles = (await (0, import_agent_qa_core9.discoverWorkspaceFiles)({ workspace: workspacePaths, kind: "suite" })).map((record) => record.absolutePath);
|
|
3580
3673
|
if (discoveredTestFiles.length === 0 && discoveredSuiteFiles.length === 0) {
|
|
3581
|
-
console.error(
|
|
3674
|
+
console.error(import_picocolors8.default.red("Error: No test or suite files found. Check workspace.testMatch and workspace.suiteMatch in agent-qa.config.yaml"));
|
|
3582
3675
|
process.exit(2);
|
|
3583
3676
|
}
|
|
3584
3677
|
} else {
|
|
3585
3678
|
if (!workspacePaths.testMatch || workspacePaths.testMatch.length === 0) {
|
|
3586
|
-
console.error(
|
|
3679
|
+
console.error(import_picocolors8.default.red("Error: No testMatch patterns in config. Add workspace.testMatch to agent-qa.config.yaml."));
|
|
3587
3680
|
process.exit(2);
|
|
3588
3681
|
}
|
|
3589
|
-
discoveredTestFiles = (await (0,
|
|
3682
|
+
discoveredTestFiles = (await (0, import_agent_qa_core9.discoverWorkspaceFiles)({ workspace: workspacePaths, kind: "test" })).map((record) => record.absolutePath);
|
|
3590
3683
|
if (discoveredTestFiles.length === 0) {
|
|
3591
|
-
console.error(
|
|
3684
|
+
console.error(import_picocolors8.default.red("Error: No test files found matching workspace.testMatch"));
|
|
3592
3685
|
for (const p of workspacePaths.testMatch) {
|
|
3593
|
-
console.error(
|
|
3686
|
+
console.error(import_picocolors8.default.red(` ${p}`));
|
|
3594
3687
|
}
|
|
3595
3688
|
process.exit(2);
|
|
3596
3689
|
}
|
|
3597
3690
|
}
|
|
3598
3691
|
if (discoveredSuiteFiles.length > 0 && discoveredTestFiles.length === 0) {
|
|
3599
3692
|
const hasFailed2 = await executeSuites(discoveredSuiteFiles, opts, config, globalOpts, configContent, effectiveLogLevel, { envFileVars, inlineVars, cliVars, rawEnvFileContent, resolvedEnvFilePath, userRunAttributes, inheritedRunAttributes, ...runtimeSecrets }, resolvedHooks && sandboxOptions ? { resolvedHooks, sandboxOptions } : void 0);
|
|
3600
|
-
|
|
3693
|
+
const exitCode2 = hasFailed2 ? 1 : 0;
|
|
3694
|
+
const suiteReporterSelection = resolveReporterSelection(opts, Boolean(config.services?.dashboard)).selection;
|
|
3695
|
+
if (suiteReporterSelection) {
|
|
3696
|
+
await maybePrintPostRunUpdateNotice({
|
|
3697
|
+
reporterSelection: suiteReporterSelection,
|
|
3698
|
+
effectiveLogLevel,
|
|
3699
|
+
liveEvents: process.env.AGENT_QA_LIVE_EVENTS,
|
|
3700
|
+
cwd: process.cwd()
|
|
3701
|
+
});
|
|
3702
|
+
}
|
|
3703
|
+
process.exit(exitCode2);
|
|
3601
3704
|
}
|
|
3602
3705
|
let suitesFailed = false;
|
|
3603
3706
|
if (discoveredSuiteFiles.length > 0 && discoveredTestFiles.length > 0) {
|
|
@@ -3605,7 +3708,7 @@ function createRunCommand() {
|
|
|
3605
3708
|
}
|
|
3606
3709
|
const files = discoveredTestFiles;
|
|
3607
3710
|
if (opts.listTests) {
|
|
3608
|
-
console.log(
|
|
3711
|
+
console.log(import_picocolors8.default.bold(`
|
|
3609
3712
|
Discovered ${files.length} test file(s):
|
|
3610
3713
|
`));
|
|
3611
3714
|
for (const file of files) {
|
|
@@ -3617,7 +3720,7 @@ Discovered ${files.length} test file(s):
|
|
|
3617
3720
|
const dashboardCfg2 = config.services?.dashboard;
|
|
3618
3721
|
const reporterSelectionResult = resolveReporterSelection(opts, Boolean(dashboardCfg2));
|
|
3619
3722
|
if (reporterSelectionResult.error) {
|
|
3620
|
-
console.error(
|
|
3723
|
+
console.error(import_picocolors8.default.red(`Error: ${reporterSelectionResult.error}`));
|
|
3621
3724
|
process.exit(2);
|
|
3622
3725
|
}
|
|
3623
3726
|
const reporterSelection = reporterSelectionResult.selection;
|
|
@@ -3634,13 +3737,13 @@ Discovered ${files.length} test file(s):
|
|
|
3634
3737
|
logStorage = dashboardDb;
|
|
3635
3738
|
_crashDb = dashboardDb;
|
|
3636
3739
|
} catch (err) {
|
|
3637
|
-
console.warn(
|
|
3740
|
+
console.warn(import_picocolors8.default.yellow(`Warning: Could not load dashboard reporter: ${err instanceof Error ? err.message : String(err)}`));
|
|
3638
3741
|
}
|
|
3639
3742
|
}
|
|
3640
3743
|
const { parseAllTests, formatParseError } = await import("@vostride/agent-qa-core");
|
|
3641
3744
|
const parseResult = await parseAllTests(files);
|
|
3642
3745
|
if (parseResult.errors.length > 0) {
|
|
3643
|
-
console.error(
|
|
3746
|
+
console.error(import_picocolors8.default.red(`Found ${parseResult.errors.length} parse error(s):
|
|
3644
3747
|
`));
|
|
3645
3748
|
for (const err of parseResult.errors) {
|
|
3646
3749
|
console.error(formatParseError(err));
|
|
@@ -3651,8 +3754,8 @@ Discovered ${files.length} test file(s):
|
|
|
3651
3754
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3652
3755
|
const { basename: basename2 } = await import("path");
|
|
3653
3756
|
const errorText = parseResult.errors.map((e) => formatParseError(e)).join("\n").replace(/\x1b\[[0-9;]*m/g, "");
|
|
3654
|
-
const { readFileSync:
|
|
3655
|
-
const rawContent =
|
|
3757
|
+
const { readFileSync: readFileSync7 } = await import("fs");
|
|
3758
|
+
const rawContent = readFileSync7(files[0], "utf-8");
|
|
3656
3759
|
const nameMatch = rawContent.match(/^\s*name:\s*(.+)$/m);
|
|
3657
3760
|
const targetMatch = rawContent.match(/^\s*target:\s*(.+)$/m);
|
|
3658
3761
|
let detectedPlatform = "web";
|
|
@@ -3694,7 +3797,7 @@ Discovered ${files.length} test file(s):
|
|
|
3694
3797
|
dashboardDb.insertRunArtifact({
|
|
3695
3798
|
runId: artifactRunId,
|
|
3696
3799
|
kind: "test",
|
|
3697
|
-
payload: (0,
|
|
3800
|
+
payload: (0, import_agent_qa_core8.redactAuthStateValue)({
|
|
3698
3801
|
config: {
|
|
3699
3802
|
rawConfigContent: configContent,
|
|
3700
3803
|
parsedConfig: config,
|
|
@@ -3736,17 +3839,17 @@ Discovered ${files.length} test file(s):
|
|
|
3736
3839
|
}
|
|
3737
3840
|
const filtered = parseResult.tests;
|
|
3738
3841
|
if (filtered.length === 0) {
|
|
3739
|
-
console.log(
|
|
3842
|
+
console.log(import_picocolors8.default.yellow("No tests match filters"));
|
|
3740
3843
|
process.exit(0);
|
|
3741
3844
|
}
|
|
3742
3845
|
if (opts.dryRun) {
|
|
3743
|
-
console.log(
|
|
3846
|
+
console.log(import_picocolors8.default.bold(`
|
|
3744
3847
|
Found ${filtered.length} test(s):
|
|
3745
3848
|
`));
|
|
3746
3849
|
for (const test of filtered) {
|
|
3747
|
-
console.log(` ${
|
|
3850
|
+
console.log(` ${import_picocolors8.default.green("\u25CF")} ${test.name}`);
|
|
3748
3851
|
if (opts.configDebug) {
|
|
3749
|
-
console.log(
|
|
3852
|
+
console.log(import_picocolors8.default.bold(`
|
|
3750
3853
|
Config for: ${test.name}`));
|
|
3751
3854
|
console.log(formatConfigDebug(config, test.use, flagOverrides));
|
|
3752
3855
|
console.log();
|
|
@@ -3796,7 +3899,7 @@ Found ${filtered.length} test(s):
|
|
|
3796
3899
|
}
|
|
3797
3900
|
}));
|
|
3798
3901
|
} catch (err) {
|
|
3799
|
-
console.warn(
|
|
3902
|
+
console.warn(import_picocolors8.default.yellow(`Warning: Could not load dashboard reporter: ${err instanceof Error ? err.message : String(err)}`));
|
|
3800
3903
|
}
|
|
3801
3904
|
}
|
|
3802
3905
|
const analyticsReporter = createAnalyticsRunReporter({ config, surface: "cli" });
|
|
@@ -3825,7 +3928,7 @@ Found ${filtered.length} test(s):
|
|
|
3825
3928
|
let circuitBreaker;
|
|
3826
3929
|
let circuitBreakerTripped = false;
|
|
3827
3930
|
const memoryGlobalConfig = config.services?.memory;
|
|
3828
|
-
const runtimeMemoryRoot = (0,
|
|
3931
|
+
const runtimeMemoryRoot = (0, import_agent_qa_core8.resolveMemoryRoot)(config, configDir);
|
|
3829
3932
|
if (opts.memory !== false && memoryGlobalConfig?.circuitBreakerEnabled !== false && memoryGlobalConfig?.enabled !== false) {
|
|
3830
3933
|
const { CircuitBreaker } = await import("@vostride/agent-qa-core");
|
|
3831
3934
|
circuitBreaker = new CircuitBreaker({
|
|
@@ -3865,7 +3968,7 @@ Found ${filtered.length} test(s):
|
|
|
3865
3968
|
(f) => parseResult.tests.some((t) => t.name === test.name)
|
|
3866
3969
|
) ?? "unknown";
|
|
3867
3970
|
analyticsFilePath = filePath;
|
|
3868
|
-
const runId = process.env.AGENT_QA_RUN_ID ?? (0,
|
|
3971
|
+
const runId = process.env.AGENT_QA_RUN_ID ?? (0, import_agent_qa_core8.generateRunId)();
|
|
3869
3972
|
analyticsRunId = runId;
|
|
3870
3973
|
analyticsStartedAt = Date.now();
|
|
3871
3974
|
currentRunId = runId;
|
|
@@ -3890,7 +3993,7 @@ Found ${filtered.length} test(s):
|
|
|
3890
3993
|
const testMergedConfig = { ...config, use: mergedUse };
|
|
3891
3994
|
const effectiveCacheEnabled = opts.cache !== false && mergedUse.cache !== false;
|
|
3892
3995
|
if (opts.configDebug) {
|
|
3893
|
-
console.log(
|
|
3996
|
+
console.log(import_picocolors8.default.bold(`
|
|
3894
3997
|
Config for: ${test.name}`));
|
|
3895
3998
|
console.log(formatConfigDebug(config, test.use, testFlagOverrides));
|
|
3896
3999
|
console.log();
|
|
@@ -3931,7 +4034,7 @@ Config for: ${test.name}`));
|
|
|
3931
4034
|
useDeviceName: testMergedUse.device,
|
|
3932
4035
|
appState: testMergedUse.mobile?.appState,
|
|
3933
4036
|
localBindings: testLocalBindings,
|
|
3934
|
-
configFilePath: (0,
|
|
4037
|
+
configFilePath: (0, import_node_path10.resolve)(configFilePath),
|
|
3935
4038
|
localConfigFilePath: testLocalBindings?.filePath,
|
|
3936
4039
|
appiumUrl: process.env.AGENT_QA_APPIUM_URL
|
|
3937
4040
|
});
|
|
@@ -3977,7 +4080,7 @@ Config for: ${test.name}`));
|
|
|
3977
4080
|
testMobileResolved = { ...testMobileResolved, appium: { url: appiumUrl, managed: !process.env.AGENT_QA_APPIUM_URL } };
|
|
3978
4081
|
}
|
|
3979
4082
|
} catch (err) {
|
|
3980
|
-
throw new
|
|
4083
|
+
throw new import_agent_qa_core8.MobileSetupError({
|
|
3981
4084
|
category: "appium-startup",
|
|
3982
4085
|
message: `Could not auto-start Appium: ${err instanceof Error ? err.message : String(err)}`,
|
|
3983
4086
|
platform: testPlatform,
|
|
@@ -4015,7 +4118,7 @@ Config for: ${test.name}`));
|
|
|
4015
4118
|
});
|
|
4016
4119
|
}
|
|
4017
4120
|
if (effectiveLogLevel === "debug") {
|
|
4018
|
-
console.log(
|
|
4121
|
+
console.log(import_picocolors8.default.dim(` Platform: ${testPlatform}${useFarm ? ` (farm: ${testResolvedDevice?.transport})` : ""}${testPlatform !== "web" ? " (adapter re-created)" : ""}`));
|
|
4019
4122
|
}
|
|
4020
4123
|
}
|
|
4021
4124
|
const mergedForLLM = { registry: config.registry, use: testMergedConfig.use ?? config.use };
|
|
@@ -4026,7 +4129,7 @@ Config for: ${test.name}`));
|
|
|
4026
4129
|
process.env.AGENT_QA_LLM_MODEL = plannerCfg.model;
|
|
4027
4130
|
process.env.AGENT_QA_LLM_PROVIDER = plannerCfg.provider;
|
|
4028
4131
|
if (effectiveLogLevel === "debug") {
|
|
4029
|
-
console.log(
|
|
4132
|
+
console.log(import_picocolors8.default.dim(` Model: ${plannerCfg.model} (${plannerCfg.provider})`));
|
|
4030
4133
|
}
|
|
4031
4134
|
const plannerModel = await createModel(plannerModelConfig);
|
|
4032
4135
|
const verifierModel = await createModel(verifierModelConfig);
|
|
@@ -4193,7 +4296,7 @@ Config for: ${test.name}`));
|
|
|
4193
4296
|
const testId = testDef["test-id"] ?? testDef.name;
|
|
4194
4297
|
memoryInitParams = { product, testId, memoryRoot };
|
|
4195
4298
|
} catch (err) {
|
|
4196
|
-
console.warn(` ${
|
|
4299
|
+
console.warn(` ${import_picocolors8.default.dim("Memory:")} init error -- ${err.message}`);
|
|
4197
4300
|
}
|
|
4198
4301
|
}
|
|
4199
4302
|
const result = await runTestWithRetry(testDef, {
|
|
@@ -4307,7 +4410,7 @@ Config for: ${test.name}`));
|
|
|
4307
4410
|
try {
|
|
4308
4411
|
const { shouldAblate, collectAllInjectedIds, deprecateOnFailure } = await import("@vostride/agent-qa-core");
|
|
4309
4412
|
if (shouldAblate(result, memoryProvider)) {
|
|
4310
|
-
console.log(` ${
|
|
4413
|
+
console.log(` ${import_picocolors8.default.dim("Memory:")} ablation retry -- re-running without memory...`);
|
|
4311
4414
|
const ablationAdapter = await createPlatformAdapter(testPlatform);
|
|
4312
4415
|
const mergedBrowser = mergedUse.browser;
|
|
4313
4416
|
const ablationPlatformConfig = buildPlatformConfig(testPlatform, testResolvedDevice, testTimeoutsForSetup, mergedBrowser ?? config.use?.browser, testMergedConfig.use?.logCapture ?? config.use?.logCapture, testFarmSession, testMobileResolved);
|
|
@@ -4355,7 +4458,7 @@ Config for: ${test.name}`));
|
|
|
4355
4458
|
circuitBreaker.record({ withMemory: false, passed: ablationResult.status === "passed" });
|
|
4356
4459
|
}
|
|
4357
4460
|
if (ablationResult.status === "passed") {
|
|
4358
|
-
console.log(` ${
|
|
4461
|
+
console.log(` ${import_picocolors8.default.dim("Memory:")} ablation confirmed memory caused failure -- penalizing observations`);
|
|
4359
4462
|
const injectedMap = collectAllInjectedIds(result, memoryProvider);
|
|
4360
4463
|
await deprecateOnFailure({
|
|
4361
4464
|
testResult: result,
|
|
@@ -4366,14 +4469,14 @@ Config for: ${test.name}`));
|
|
|
4366
4469
|
});
|
|
4367
4470
|
ablationHandledDeprecation = true;
|
|
4368
4471
|
} else {
|
|
4369
|
-
console.log(` ${
|
|
4472
|
+
console.log(` ${import_picocolors8.default.dim("Memory:")} ablation retry also failed -- memory not the cause`);
|
|
4370
4473
|
}
|
|
4371
4474
|
} finally {
|
|
4372
4475
|
await ablationAdapter.cleanup();
|
|
4373
4476
|
}
|
|
4374
4477
|
}
|
|
4375
4478
|
} catch (err) {
|
|
4376
|
-
console.warn(` ${
|
|
4479
|
+
console.warn(` ${import_picocolors8.default.dim("Memory:")} ablation error -- ${err.message}`);
|
|
4377
4480
|
}
|
|
4378
4481
|
}
|
|
4379
4482
|
if (memoryProvider && memoryConfig?.curatorEnabled !== false && !ablationHandledDeprecation) {
|
|
@@ -4407,7 +4510,7 @@ Config for: ${test.name}`));
|
|
|
4407
4510
|
}
|
|
4408
4511
|
}
|
|
4409
4512
|
} catch (err) {
|
|
4410
|
-
console.warn(` ${
|
|
4513
|
+
console.warn(` ${import_picocolors8.default.dim("Memory:")} curator error -- ${err.message}`);
|
|
4411
4514
|
}
|
|
4412
4515
|
}
|
|
4413
4516
|
if (testAuthStateUse?.capture === true) {
|
|
@@ -4421,12 +4524,12 @@ Config for: ${test.name}`));
|
|
|
4421
4524
|
}
|
|
4422
4525
|
if (circuitBreaker?.isTripped() && !circuitBreakerTripped) {
|
|
4423
4526
|
circuitBreakerTripped = true;
|
|
4424
|
-
console.log(` ${
|
|
4527
|
+
console.log(` ${import_picocolors8.default.yellow("Memory: circuit breaker tripped -- disabling memory injection for remaining tests")}`);
|
|
4425
4528
|
}
|
|
4426
4529
|
if (result.status === "failed" && !authStateCaptureFailed && actionCache && effectiveCacheEnabled && result.steps.length > 0) {
|
|
4427
4530
|
const { hashStepInstruction: hashStepInstruction2 } = await import("@vostride/agent-qa-core");
|
|
4428
4531
|
const { rm: rm3 } = await import("fs/promises");
|
|
4429
|
-
const { resolve: resolve9, join:
|
|
4532
|
+
const { resolve: resolve9, join: join9 } = await import("path");
|
|
4430
4533
|
const cacheDir = resolve9(configDir, config.services?.cache?.dir ?? RUNTIME_CACHE_DIR);
|
|
4431
4534
|
const cacheLog = logger.createScopedLogger("cache");
|
|
4432
4535
|
let purged = 0;
|
|
@@ -4434,7 +4537,7 @@ Config for: ${test.name}`));
|
|
|
4434
4537
|
const step = result.steps[i];
|
|
4435
4538
|
const stepHash = hashStepInstruction2(step.name, testPlatform, configContent, testFileContent, i);
|
|
4436
4539
|
try {
|
|
4437
|
-
await rm3(
|
|
4540
|
+
await rm3(join9(cacheDir, stepHash), { recursive: true, force: true });
|
|
4438
4541
|
purged++;
|
|
4439
4542
|
} catch {
|
|
4440
4543
|
}
|
|
@@ -4506,13 +4609,13 @@ Config for: ${test.name}`));
|
|
|
4506
4609
|
}
|
|
4507
4610
|
const parallelBatchFailed = await flushParallelRuns();
|
|
4508
4611
|
if (opts.bail && parallelBatchFailed) {
|
|
4509
|
-
console.log(
|
|
4612
|
+
console.log(import_picocolors8.default.yellow("\nBailing out \u2014 stopping after first failure"));
|
|
4510
4613
|
bailTriggered = true;
|
|
4511
4614
|
break;
|
|
4512
4615
|
}
|
|
4513
4616
|
const outcome = await runOneDirectTest(test);
|
|
4514
4617
|
if (opts.bail && outcome.failed) {
|
|
4515
|
-
console.log(
|
|
4618
|
+
console.log(import_picocolors8.default.yellow("\nBailing out \u2014 stopping after first failure"));
|
|
4516
4619
|
bailTriggered = true;
|
|
4517
4620
|
break;
|
|
4518
4621
|
}
|
|
@@ -4520,7 +4623,7 @@ Config for: ${test.name}`));
|
|
|
4520
4623
|
if (!bailTriggered) {
|
|
4521
4624
|
const parallelBatchFailed = await flushParallelRuns();
|
|
4522
4625
|
if (opts.bail && parallelBatchFailed) {
|
|
4523
|
-
console.log(
|
|
4626
|
+
console.log(import_picocolors8.default.yellow("\nBailing out \u2014 stopping after first failure"));
|
|
4524
4627
|
}
|
|
4525
4628
|
}
|
|
4526
4629
|
releaseAppium();
|
|
@@ -4534,12 +4637,19 @@ Config for: ${test.name}`));
|
|
|
4534
4637
|
dashboardDb.close();
|
|
4535
4638
|
}
|
|
4536
4639
|
const hasFailed = results.some((r) => r.status === "failed") || suitesFailed;
|
|
4537
|
-
|
|
4640
|
+
const exitCode = hasFailed ? 1 : 0;
|
|
4641
|
+
await maybePrintPostRunUpdateNotice({
|
|
4642
|
+
reporterSelection,
|
|
4643
|
+
effectiveLogLevel,
|
|
4644
|
+
liveEvents: process.env.AGENT_QA_LIVE_EVENTS,
|
|
4645
|
+
cwd: process.cwd()
|
|
4646
|
+
});
|
|
4647
|
+
process.exit(exitCode);
|
|
4538
4648
|
} catch (err) {
|
|
4539
4649
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4540
4650
|
const errStack = err instanceof Error ? err.stack ?? "" : "";
|
|
4541
4651
|
const formattedError = formatFrameworkError(errMsg);
|
|
4542
|
-
console.error(
|
|
4652
|
+
console.error(import_picocolors8.default.red(formattedError.displayMessage));
|
|
4543
4653
|
if (_crashDb) {
|
|
4544
4654
|
const fullError = `${errMsg}
|
|
4545
4655
|
${errStack}`;
|
|
@@ -4554,7 +4664,7 @@ ${errStack}`;
|
|
|
4554
4664
|
status: finalStatus,
|
|
4555
4665
|
duration: run.startedAt ? Date.now() - new Date(run.startedAt).getTime() : 0,
|
|
4556
4666
|
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4557
|
-
failureSummary: isBrowserClose ? "Browser closed by user" : (0,
|
|
4667
|
+
failureSummary: isBrowserClose ? "Browser closed by user" : (0, import_agent_qa_core8.redactAuthStateValue)(formattedError.failureSummary)
|
|
4558
4668
|
});
|
|
4559
4669
|
try {
|
|
4560
4670
|
const artifact = _crashDb.getRunArtifact(run.id);
|
|
@@ -4567,7 +4677,7 @@ ${errStack}`;
|
|
|
4567
4677
|
errors: [{
|
|
4568
4678
|
code: isBrowserClose ? "browser-disconnect" : "process-close",
|
|
4569
4679
|
phase: "process-close",
|
|
4570
|
-
message: isBrowserClose ? "Browser closed by user" : (0,
|
|
4680
|
+
message: isBrowserClose ? "Browser closed by user" : (0, import_agent_qa_core8.redactAuthStateValue)(formattedError.failureSummary)
|
|
4571
4681
|
}]
|
|
4572
4682
|
});
|
|
4573
4683
|
}
|
|
@@ -4591,10 +4701,10 @@ ${errStack}`;
|
|
|
4591
4701
|
|
|
4592
4702
|
// src/commands/dashboard.ts
|
|
4593
4703
|
var import_commander7 = require("commander");
|
|
4594
|
-
var
|
|
4704
|
+
var import_node_path11 = require("path");
|
|
4595
4705
|
var import_node_module2 = require("module");
|
|
4596
|
-
var
|
|
4597
|
-
var
|
|
4706
|
+
var import_picocolors9 = __toESM(require("picocolors"), 1);
|
|
4707
|
+
var import_agent_qa_core11 = require("@vostride/agent-qa-core");
|
|
4598
4708
|
init_config();
|
|
4599
4709
|
init_llm_utils();
|
|
4600
4710
|
init_model_defaults();
|
|
@@ -4606,9 +4716,9 @@ async function startDashboardBackedServices(opts, command, labels) {
|
|
|
4606
4716
|
const config = await resolveConfig({
|
|
4607
4717
|
configPath: globalOpts.config
|
|
4608
4718
|
});
|
|
4609
|
-
const configFilePath = (0,
|
|
4610
|
-
const configDir = (0,
|
|
4611
|
-
const workspacePaths = (0,
|
|
4719
|
+
const configFilePath = (0, import_node_path11.resolve)(globalOpts.config ?? "agent-qa.config.yaml");
|
|
4720
|
+
const configDir = (0, import_node_path11.dirname)(configFilePath);
|
|
4721
|
+
const workspacePaths = (0, import_agent_qa_core11.resolveWorkspacePaths)({
|
|
4612
4722
|
config,
|
|
4613
4723
|
configPath: configFilePath,
|
|
4614
4724
|
requireExistingFiles: true
|
|
@@ -4647,17 +4757,17 @@ async function startDashboardBackedServices(opts, command, labels) {
|
|
|
4647
4757
|
try {
|
|
4648
4758
|
const require2 = (0, import_node_module2.createRequire)(import_meta3.url);
|
|
4649
4759
|
const pkgPath = require2.resolve("@vostride/agent-qa-dashboard-ui/package.json");
|
|
4650
|
-
uiDir = (0,
|
|
4760
|
+
uiDir = (0, import_node_path11.resolve)((0, import_node_path11.dirname)(pkgPath), "dist");
|
|
4651
4761
|
} catch {
|
|
4652
4762
|
if (globalOpts.verbose) {
|
|
4653
|
-
console.log(
|
|
4763
|
+
console.log(import_picocolors9.default.yellow("Warning: @vostride/agent-qa-dashboard-ui not found \u2014 API-only mode"));
|
|
4654
4764
|
}
|
|
4655
4765
|
}
|
|
4656
4766
|
const { close } = await startServer({
|
|
4657
4767
|
db,
|
|
4658
4768
|
port,
|
|
4659
4769
|
uiDir,
|
|
4660
|
-
artifactsDir: (0,
|
|
4770
|
+
artifactsDir: (0, import_node_path11.resolve)(configDir, artifactsDir),
|
|
4661
4771
|
workspacePaths,
|
|
4662
4772
|
configPath: configFilePath,
|
|
4663
4773
|
mcp: mcpConfig,
|
|
@@ -4673,17 +4783,17 @@ async function startDashboardBackedServices(opts, command, labels) {
|
|
|
4673
4783
|
const shutdown = async () => {
|
|
4674
4784
|
if (shuttingDown) return;
|
|
4675
4785
|
shuttingDown = true;
|
|
4676
|
-
console.log(
|
|
4786
|
+
console.log(import_picocolors9.default.dim(`
|
|
4677
4787
|
Shutting down ${labels.shutdown}...`));
|
|
4678
4788
|
close();
|
|
4679
|
-
await (0,
|
|
4789
|
+
await (0, import_agent_qa_core11.flushAnalytics)({ config }).catch(() => {
|
|
4680
4790
|
});
|
|
4681
4791
|
process.exit(0);
|
|
4682
4792
|
};
|
|
4683
4793
|
process.on("SIGINT", shutdown);
|
|
4684
4794
|
process.on("SIGTERM", shutdown);
|
|
4685
4795
|
} catch (err) {
|
|
4686
|
-
console.error(
|
|
4796
|
+
console.error(import_picocolors9.default.red(`${labels.error} error: ${err instanceof Error ? err.message : String(err)}`));
|
|
4687
4797
|
process.exit(2);
|
|
4688
4798
|
}
|
|
4689
4799
|
}
|
|
@@ -4708,8 +4818,8 @@ function createServeCommand() {
|
|
|
4708
4818
|
|
|
4709
4819
|
// src/commands/mcp.ts
|
|
4710
4820
|
var import_commander8 = require("commander");
|
|
4711
|
-
var
|
|
4712
|
-
var
|
|
4821
|
+
var import_picocolors10 = __toESM(require("picocolors"), 1);
|
|
4822
|
+
var import_agent_qa_core12 = require("@vostride/agent-qa-core");
|
|
4713
4823
|
init_config();
|
|
4714
4824
|
function createMcpCommand() {
|
|
4715
4825
|
const cmd = new import_commander8.Command("mcp").description("Start MCP (Model Context Protocol) server over stdio").action(async (command) => {
|
|
@@ -4727,11 +4837,11 @@ function createMcpCommand() {
|
|
|
4727
4837
|
const startupOptions = { analyticsConfig: config, startupOutput: process.stderr };
|
|
4728
4838
|
await startMcpServer(startupOptions);
|
|
4729
4839
|
} finally {
|
|
4730
|
-
await (0,
|
|
4840
|
+
await (0, import_agent_qa_core12.flushAnalytics)(config ? { config } : void 0).catch(() => {
|
|
4731
4841
|
});
|
|
4732
4842
|
}
|
|
4733
4843
|
} catch (err) {
|
|
4734
|
-
console.error(
|
|
4844
|
+
console.error(import_picocolors10.default.red("Failed to start MCP server:"), err instanceof Error ? err.message : String(err));
|
|
4735
4845
|
process.exitCode = 1;
|
|
4736
4846
|
}
|
|
4737
4847
|
});
|
|
@@ -4740,7 +4850,7 @@ function createMcpCommand() {
|
|
|
4740
4850
|
|
|
4741
4851
|
// src/commands/config.ts
|
|
4742
4852
|
var import_commander9 = require("commander");
|
|
4743
|
-
var
|
|
4853
|
+
var import_picocolors11 = __toESM(require("picocolors"), 1);
|
|
4744
4854
|
var import_yaml5 = require("yaml");
|
|
4745
4855
|
var import_promises3 = require("fs/promises");
|
|
4746
4856
|
var import_node_readline2 = require("readline");
|
|
@@ -4786,14 +4896,14 @@ async function getValue(key, providedValue) {
|
|
|
4786
4896
|
}
|
|
4787
4897
|
async function setAction(key, value) {
|
|
4788
4898
|
if (isSensitiveKey(key)) {
|
|
4789
|
-
console.error(
|
|
4899
|
+
console.error(import_picocolors11.default.red("Credential values are not written by config set."));
|
|
4790
4900
|
console.error("Use agent-qa auth set --config <name> --type api-key|bearer-token for compatible or Gemini configs.");
|
|
4791
4901
|
console.error("Use plugins.auth plus the dashboard auth flow for subscription configs.");
|
|
4792
4902
|
return;
|
|
4793
4903
|
}
|
|
4794
4904
|
const resolved = await getValue(key, value);
|
|
4795
4905
|
if (!resolved) {
|
|
4796
|
-
console.error(
|
|
4906
|
+
console.error(import_picocolors11.default.red("No value provided."));
|
|
4797
4907
|
return;
|
|
4798
4908
|
}
|
|
4799
4909
|
const configPath = "agent-qa.config.yaml";
|
|
@@ -4810,7 +4920,7 @@ async function setAction(key, value) {
|
|
|
4810
4920
|
}
|
|
4811
4921
|
doc.setIn(key.split("."), resolved);
|
|
4812
4922
|
await (0, import_promises3.writeFile)(configPath, doc.toString());
|
|
4813
|
-
console.log(
|
|
4923
|
+
console.log(import_picocolors11.default.green(`\u2713 ${key} = ${resolved} saved to agent-qa.config.yaml`));
|
|
4814
4924
|
}
|
|
4815
4925
|
async function showAction() {
|
|
4816
4926
|
const fileConfig = await loadConfigFile("agent-qa.config.yaml");
|
|
@@ -4818,7 +4928,7 @@ async function showAction() {
|
|
|
4818
4928
|
const { mergeConfigs: mergeConfigs2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4819
4929
|
const globalConfig = mergeConfigs2(fileConfig, envOverrides, {});
|
|
4820
4930
|
const table = formatConfigDebug(globalConfig, {}, {});
|
|
4821
|
-
console.log(
|
|
4931
|
+
console.log(import_picocolors11.default.bold("\nResolved Configuration:\n"));
|
|
4822
4932
|
console.log(table);
|
|
4823
4933
|
console.log();
|
|
4824
4934
|
}
|
|
@@ -4831,7 +4941,7 @@ function createConfigCommand() {
|
|
|
4831
4941
|
|
|
4832
4942
|
// src/commands/queue.ts
|
|
4833
4943
|
var import_commander10 = require("commander");
|
|
4834
|
-
var
|
|
4944
|
+
var import_picocolors12 = __toESM(require("picocolors"), 1);
|
|
4835
4945
|
init_config();
|
|
4836
4946
|
function timeAgo(dateStr) {
|
|
4837
4947
|
const diff = Date.now() - new Date(dateStr).getTime();
|
|
@@ -4866,13 +4976,13 @@ function createQueueCommand() {
|
|
|
4866
4976
|
try {
|
|
4867
4977
|
const response = await fetch(endpoint);
|
|
4868
4978
|
if (!response.ok) {
|
|
4869
|
-
console.error(
|
|
4979
|
+
console.error(import_picocolors12.default.red(`Server returned ${response.status}: ${response.statusText}`));
|
|
4870
4980
|
process.exit(1);
|
|
4871
4981
|
}
|
|
4872
4982
|
data = await response.json();
|
|
4873
4983
|
} catch {
|
|
4874
|
-
console.error(
|
|
4875
|
-
console.error(
|
|
4984
|
+
console.error(import_picocolors12.default.red(`Could not connect to dashboard server at ${serverUrl}`));
|
|
4985
|
+
console.error(import_picocolors12.default.dim("Make sure the dashboard is running: agent-qa dashboard"));
|
|
4876
4986
|
process.exit(1);
|
|
4877
4987
|
}
|
|
4878
4988
|
if (opts.json) {
|
|
@@ -4880,19 +4990,19 @@ function createQueueCommand() {
|
|
|
4880
4990
|
return;
|
|
4881
4991
|
}
|
|
4882
4992
|
console.log();
|
|
4883
|
-
console.log(
|
|
4884
|
-
console.log(` Concurrency: ${
|
|
4993
|
+
console.log(import_picocolors12.default.bold("Queue Status"));
|
|
4994
|
+
console.log(` Concurrency: ${import_picocolors12.default.blue(String(data.concurrency))} slots (${import_picocolors12.default.yellow(String(data.activeSlots))} active)`);
|
|
4885
4995
|
console.log();
|
|
4886
4996
|
const pending = data.pending?.jobs ?? [];
|
|
4887
4997
|
const running = data.running?.jobs ?? [];
|
|
4888
4998
|
if (pending.length === 0 && running.length === 0) {
|
|
4889
|
-
console.log(
|
|
4999
|
+
console.log(import_picocolors12.default.dim(" Queue is empty \u2014 no pending or running jobs"));
|
|
4890
5000
|
console.log();
|
|
4891
5001
|
return;
|
|
4892
5002
|
}
|
|
4893
5003
|
if (pending.length > 0) {
|
|
4894
|
-
console.log(
|
|
4895
|
-
console.log(
|
|
5004
|
+
console.log(import_picocolors12.default.bold(`Pending (${pending.length} jobs):`));
|
|
5005
|
+
console.log(import_picocolors12.default.dim(" # ID Name Priority Source Queued"));
|
|
4896
5006
|
for (let i = 0; i < pending.length; i++) {
|
|
4897
5007
|
const job = pending[i];
|
|
4898
5008
|
const num = String(i + 1).padEnd(3);
|
|
@@ -4901,37 +5011,37 @@ function createQueueCommand() {
|
|
|
4901
5011
|
const priority = String(job.priority ?? 0).padEnd(8);
|
|
4902
5012
|
const source = (job.source ?? "").padEnd(10);
|
|
4903
5013
|
const queued = job.createdAt ? timeAgo(job.createdAt) : "";
|
|
4904
|
-
console.log(` ${num} ${
|
|
5014
|
+
console.log(` ${num} ${import_picocolors12.default.dim(id)} ${name} ${priority} ${source} ${import_picocolors12.default.dim(queued)}`);
|
|
4905
5015
|
}
|
|
4906
5016
|
console.log();
|
|
4907
5017
|
}
|
|
4908
5018
|
if (running.length > 0) {
|
|
4909
|
-
console.log(
|
|
4910
|
-
console.log(
|
|
5019
|
+
console.log(import_picocolors12.default.bold(`Running (${running.length} jobs):`));
|
|
5020
|
+
console.log(import_picocolors12.default.dim(" ID Name Source Started"));
|
|
4911
5021
|
for (const job of running) {
|
|
4912
5022
|
const id = (job.id ?? "").slice(0, 12).padEnd(12);
|
|
4913
5023
|
const name = (job.name ?? "").slice(0, 18).padEnd(18);
|
|
4914
5024
|
const source = (job.source ?? "").padEnd(10);
|
|
4915
5025
|
const started = job.startedAt ? timeAgo(job.startedAt) : "";
|
|
4916
|
-
console.log(` ${
|
|
5026
|
+
console.log(` ${import_picocolors12.default.green(id)} ${name} ${source} ${import_picocolors12.default.dim(started)}`);
|
|
4917
5027
|
}
|
|
4918
5028
|
console.log();
|
|
4919
5029
|
}
|
|
4920
5030
|
if (opts.all && data.recent?.length > 0) {
|
|
4921
|
-
console.log(
|
|
4922
|
-
console.log(
|
|
5031
|
+
console.log(import_picocolors12.default.bold(`Recent (${data.recent.length} jobs):`));
|
|
5032
|
+
console.log(import_picocolors12.default.dim(" ID Name Status Duration"));
|
|
4923
5033
|
for (const job of data.recent) {
|
|
4924
5034
|
const id = (job.id ?? "").slice(0, 12).padEnd(12);
|
|
4925
5035
|
const name = (job.name ?? "").slice(0, 18).padEnd(18);
|
|
4926
5036
|
const status = (job.status ?? "").padEnd(8);
|
|
4927
5037
|
const duration = job.duration ? `${Math.round(job.duration / 1e3)}s` : "";
|
|
4928
|
-
const statusColor = job.status === "passed" ?
|
|
4929
|
-
console.log(` ${
|
|
5038
|
+
const statusColor = job.status === "passed" ? import_picocolors12.default.green : job.status === "failed" ? import_picocolors12.default.red : import_picocolors12.default.dim;
|
|
5039
|
+
console.log(` ${import_picocolors12.default.dim(id)} ${name} ${statusColor(status)} ${import_picocolors12.default.dim(duration)}`);
|
|
4930
5040
|
}
|
|
4931
5041
|
console.log();
|
|
4932
5042
|
}
|
|
4933
5043
|
} catch (err) {
|
|
4934
|
-
console.error(
|
|
5044
|
+
console.error(import_picocolors12.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
4935
5045
|
process.exit(1);
|
|
4936
5046
|
}
|
|
4937
5047
|
});
|
|
@@ -4944,25 +5054,25 @@ function createQueueCommand() {
|
|
|
4944
5054
|
try {
|
|
4945
5055
|
response = await fetch(`${serverUrl}/api/runs/${runId}/cancel`, { method: "POST" });
|
|
4946
5056
|
} catch {
|
|
4947
|
-
console.error(
|
|
4948
|
-
console.error(
|
|
5057
|
+
console.error(import_picocolors12.default.red(`Could not connect to dashboard server at ${serverUrl}`));
|
|
5058
|
+
console.error(import_picocolors12.default.dim("Make sure the dashboard is running: agent-qa dashboard"));
|
|
4949
5059
|
process.exit(1);
|
|
4950
5060
|
}
|
|
4951
5061
|
if (response.ok) {
|
|
4952
5062
|
const data = await response.json();
|
|
4953
5063
|
if (data.cancelled) {
|
|
4954
|
-
console.log(
|
|
5064
|
+
console.log(import_picocolors12.default.green(`\u2713 Cancelled job ${runId}`));
|
|
4955
5065
|
} else {
|
|
4956
|
-
console.log(
|
|
5066
|
+
console.log(import_picocolors12.default.red(`Failed to cancel: ${data.error ?? "Unknown error"}`));
|
|
4957
5067
|
}
|
|
4958
5068
|
} else if (response.status === 404) {
|
|
4959
|
-
console.log(
|
|
5069
|
+
console.log(import_picocolors12.default.red(`Job not found: ${runId}`));
|
|
4960
5070
|
} else {
|
|
4961
5071
|
const data = await response.json().catch(() => ({}));
|
|
4962
|
-
console.log(
|
|
5072
|
+
console.log(import_picocolors12.default.red(`Failed to cancel: ${data.error ?? response.statusText}`));
|
|
4963
5073
|
}
|
|
4964
5074
|
} catch (err) {
|
|
4965
|
-
console.error(
|
|
5075
|
+
console.error(import_picocolors12.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
4966
5076
|
process.exit(1);
|
|
4967
5077
|
}
|
|
4968
5078
|
});
|
|
@@ -4971,7 +5081,7 @@ function createQueueCommand() {
|
|
|
4971
5081
|
|
|
4972
5082
|
// src/commands/validate.ts
|
|
4973
5083
|
var import_commander11 = require("commander");
|
|
4974
|
-
var
|
|
5084
|
+
var import_agent_qa_core13 = require("@vostride/agent-qa-core");
|
|
4975
5085
|
init_config();
|
|
4976
5086
|
function createValidateCommand() {
|
|
4977
5087
|
const cmd = new import_commander11.Command("validate").description("Validate config, test files, and suite references").argument("[files...]", "specific files to validate (default: auto-discover all)").action(async (files, command) => {
|
|
@@ -4979,15 +5089,15 @@ function createValidateCommand() {
|
|
|
4979
5089
|
let workspace;
|
|
4980
5090
|
try {
|
|
4981
5091
|
const config = await resolveConfig({ configPath });
|
|
4982
|
-
workspace = (0,
|
|
5092
|
+
workspace = (0, import_agent_qa_core13.resolveWorkspacePaths)({ config, configPath });
|
|
4983
5093
|
} catch {
|
|
4984
5094
|
}
|
|
4985
|
-
const result = await (0,
|
|
5095
|
+
const result = await (0, import_agent_qa_core13.validateProject)({
|
|
4986
5096
|
configPath,
|
|
4987
5097
|
files: files.length > 0 ? files : void 0,
|
|
4988
5098
|
workspace
|
|
4989
5099
|
});
|
|
4990
|
-
const output = (0,
|
|
5100
|
+
const output = (0, import_agent_qa_core13.formatDiagnostics)(result);
|
|
4991
5101
|
process.stdout.write(output);
|
|
4992
5102
|
if (result.errorCount > 0) {
|
|
4993
5103
|
process.exitCode = 1;
|
|
@@ -4999,7 +5109,7 @@ function createValidateCommand() {
|
|
|
4999
5109
|
// src/commands/auth.ts
|
|
5000
5110
|
var import_commander12 = require("commander");
|
|
5001
5111
|
var import_node_child_process4 = require("child_process");
|
|
5002
|
-
var
|
|
5112
|
+
var import_picocolors13 = __toESM(require("picocolors"), 1);
|
|
5003
5113
|
var import_input2 = __toESM(require("@inquirer/input"), 1);
|
|
5004
5114
|
var import_password = __toESM(require("@inquirer/password"), 1);
|
|
5005
5115
|
var PROVIDER_LABELS = {
|
|
@@ -5034,10 +5144,10 @@ function getGlobalConfigPath2(command) {
|
|
|
5034
5144
|
return command.parent?.parent?.opts().config ?? command.parent?.opts().config;
|
|
5035
5145
|
}
|
|
5036
5146
|
function printCredentialGuidance(configName, provider) {
|
|
5037
|
-
console.log(
|
|
5038
|
-
console.log(
|
|
5147
|
+
console.log(import_picocolors13.default.red(`Config ${configName} uses ${providerLabel(provider)} credentials.`));
|
|
5148
|
+
console.log(import_picocolors13.default.dim("Use agent-qa auth set --config <name> --type api-key"));
|
|
5039
5149
|
if (provider === "anthropic-compatible") {
|
|
5040
|
-
console.log(
|
|
5150
|
+
console.log(import_picocolors13.default.dim("or --type bearer-token"));
|
|
5041
5151
|
}
|
|
5042
5152
|
}
|
|
5043
5153
|
function applyAuthToModelConfig(llmConfig, auth) {
|
|
@@ -5057,15 +5167,15 @@ function applyAuthToModelConfig(llmConfig, auth) {
|
|
|
5057
5167
|
function statusForResolvedAuth(auth) {
|
|
5058
5168
|
switch (auth.kind) {
|
|
5059
5169
|
case "api-key":
|
|
5060
|
-
return
|
|
5170
|
+
return import_picocolors13.default.green("Saved API key");
|
|
5061
5171
|
case "bearer-token":
|
|
5062
|
-
return
|
|
5172
|
+
return import_picocolors13.default.green("Saved bearer token");
|
|
5063
5173
|
case "auth-fetch":
|
|
5064
|
-
return Number(auth.expires ?? Date.now() + 1) <= Date.now() ?
|
|
5174
|
+
return Number(auth.expires ?? Date.now() + 1) <= Date.now() ? import_picocolors13.default.red("Expired") : import_picocolors13.default.green("OAuth connected");
|
|
5065
5175
|
case "unauthenticated":
|
|
5066
|
-
return
|
|
5176
|
+
return import_picocolors13.default.dim("No credential");
|
|
5067
5177
|
case "missing":
|
|
5068
|
-
return
|
|
5178
|
+
return import_picocolors13.default.red("Missing credential");
|
|
5069
5179
|
}
|
|
5070
5180
|
}
|
|
5071
5181
|
function extractProviderError(err) {
|
|
@@ -5096,16 +5206,16 @@ function openAuthorizationUrl(url) {
|
|
|
5096
5206
|
});
|
|
5097
5207
|
}
|
|
5098
5208
|
function printMissingSubscriptionPluginGuidance(configName, provider) {
|
|
5099
|
-
console.log(
|
|
5100
|
-
console.log(
|
|
5101
|
-
console.log(
|
|
5102
|
-
console.log(
|
|
5103
|
-
console.log(
|
|
5104
|
-
console.log(
|
|
5105
|
-
console.log(
|
|
5106
|
-
console.log(
|
|
5107
|
-
console.log(
|
|
5108
|
-
console.log(
|
|
5209
|
+
console.log(import_picocolors13.default.red(`Provider "${provider}" is configured for "${configName}", but no auth plugin is registered.`));
|
|
5210
|
+
console.log(import_picocolors13.default.dim(`Config: ${configName}`));
|
|
5211
|
+
console.log(import_picocolors13.default.dim(`Provider: ${provider}`));
|
|
5212
|
+
console.log(import_picocolors13.default.dim("Add the subscription auth plugin to package.json and install with your package manager:"));
|
|
5213
|
+
console.log(import_picocolors13.default.dim(' "devDependencies": { "@vostride/agent-qa-subscription-auth": "<agent-qa version>" }'));
|
|
5214
|
+
console.log(import_picocolors13.default.dim("Declare it in agent-qa.config.yaml under plugins.auth:"));
|
|
5215
|
+
console.log(import_picocolors13.default.dim(" plugins:"));
|
|
5216
|
+
console.log(import_picocolors13.default.dim(" auth:"));
|
|
5217
|
+
console.log(import_picocolors13.default.dim(" - package: @vostride/agent-qa-subscription-auth"));
|
|
5218
|
+
console.log(import_picocolors13.default.dim("Then authenticate from agent-qa dashboard or rerun this command."));
|
|
5109
5219
|
}
|
|
5110
5220
|
async function readTokensFromPlugin(provider, plugin, started) {
|
|
5111
5221
|
if (started.waitForTokens) {
|
|
@@ -5113,19 +5223,19 @@ async function readTokensFromPlugin(provider, plugin, started) {
|
|
|
5113
5223
|
}
|
|
5114
5224
|
if (plugin.dashboardAuth.mode === "manual-code") {
|
|
5115
5225
|
if (!plugin.exchangeCode) {
|
|
5116
|
-
console.log(
|
|
5226
|
+
console.log(import_picocolors13.default.red(`Provider "${provider}" does not support CLI code exchange. Use agent-qa dashboard.`));
|
|
5117
5227
|
process.exitCode = 1;
|
|
5118
5228
|
return null;
|
|
5119
5229
|
}
|
|
5120
5230
|
const code = (await (0, import_input2.default)({ message: "Authorization code:" })).trim();
|
|
5121
5231
|
if (!code) {
|
|
5122
|
-
console.log(
|
|
5232
|
+
console.log(import_picocolors13.default.red("Authorization code is required"));
|
|
5123
5233
|
process.exitCode = 1;
|
|
5124
5234
|
return null;
|
|
5125
5235
|
}
|
|
5126
5236
|
return await plugin.exchangeCode({ code, sessionState: started.sessionState });
|
|
5127
5237
|
}
|
|
5128
|
-
console.log(
|
|
5238
|
+
console.log(import_picocolors13.default.red(`Provider "${provider}" does not support CLI auth login. Use agent-qa dashboard.`));
|
|
5129
5239
|
process.exitCode = 1;
|
|
5130
5240
|
return null;
|
|
5131
5241
|
}
|
|
@@ -5138,14 +5248,14 @@ async function runSubscriptionPluginLogin(configName, provider) {
|
|
|
5138
5248
|
return;
|
|
5139
5249
|
}
|
|
5140
5250
|
if (!plugin.startAuth) {
|
|
5141
|
-
console.log(
|
|
5251
|
+
console.log(import_picocolors13.default.red(`Provider "${provider}" does not support CLI auth login. Use agent-qa dashboard.`));
|
|
5142
5252
|
process.exitCode = 1;
|
|
5143
5253
|
return;
|
|
5144
5254
|
}
|
|
5145
5255
|
let started;
|
|
5146
5256
|
try {
|
|
5147
5257
|
started = await plugin.startAuth({ configName });
|
|
5148
|
-
console.log(`Open this URL to authenticate: ${
|
|
5258
|
+
console.log(`Open this URL to authenticate: ${import_picocolors13.default.cyan(started.authorizeUrl)}`);
|
|
5149
5259
|
openAuthorizationUrl(started.authorizeUrl);
|
|
5150
5260
|
const tokens = await readTokensFromPlugin(provider, plugin, started);
|
|
5151
5261
|
if (!tokens) return;
|
|
@@ -5154,9 +5264,9 @@ async function runSubscriptionPluginLogin(configName, provider) {
|
|
|
5154
5264
|
provider: plugin.credentialProviderId,
|
|
5155
5265
|
tokens
|
|
5156
5266
|
});
|
|
5157
|
-
console.log(
|
|
5267
|
+
console.log(import_picocolors13.default.green(`Authenticated ${configName} with ${plugin.label}`));
|
|
5158
5268
|
} catch (err) {
|
|
5159
|
-
console.log(
|
|
5269
|
+
console.log(import_picocolors13.default.red(err instanceof Error ? err.message : String(err)));
|
|
5160
5270
|
process.exitCode = 1;
|
|
5161
5271
|
} finally {
|
|
5162
5272
|
started?.cleanup?.();
|
|
@@ -5173,13 +5283,13 @@ function createAuthCommand() {
|
|
|
5173
5283
|
try {
|
|
5174
5284
|
const requestedName = normalizeConfigName(opts.config);
|
|
5175
5285
|
if (!requestedName) {
|
|
5176
|
-
console.log(
|
|
5286
|
+
console.log(import_picocolors13.default.red("configName is required"));
|
|
5177
5287
|
return;
|
|
5178
5288
|
}
|
|
5179
5289
|
const resolved = await resolveNamedConfig2(requestedName, configPath);
|
|
5180
5290
|
const resolvedName = normalizeConfigName(resolved.config.name);
|
|
5181
5291
|
if (!resolvedName) {
|
|
5182
|
-
console.log(
|
|
5292
|
+
console.log(import_picocolors13.default.red("Config name is invalid"));
|
|
5183
5293
|
return;
|
|
5184
5294
|
}
|
|
5185
5295
|
configName = resolvedName;
|
|
@@ -5190,7 +5300,7 @@ function createAuthCommand() {
|
|
|
5190
5300
|
}
|
|
5191
5301
|
provider = configProvider;
|
|
5192
5302
|
} catch (err) {
|
|
5193
|
-
console.log(
|
|
5303
|
+
console.log(import_picocolors13.default.red(err instanceof Error ? err.message : String(err)));
|
|
5194
5304
|
return;
|
|
5195
5305
|
}
|
|
5196
5306
|
await runSubscriptionPluginLogin(configName, provider);
|
|
@@ -5200,7 +5310,7 @@ function createAuthCommand() {
|
|
|
5200
5310
|
new import_commander12.Command("set").description("Save a credential for a named LLM config").requiredOption("--config <name>", "named LLM config to save a credential for").requiredOption("--type <type>", "credential type: api-key or bearer-token").argument("[secret]", "credential secret; prompted when omitted").action(async (secretArg, opts, command) => {
|
|
5201
5311
|
const credentialType = opts.type.trim();
|
|
5202
5312
|
if (credentialType !== "api-key" && credentialType !== "bearer-token") {
|
|
5203
|
-
console.log(
|
|
5313
|
+
console.log(import_picocolors13.default.red("type must be api-key or bearer-token"));
|
|
5204
5314
|
return;
|
|
5205
5315
|
}
|
|
5206
5316
|
const { resolveNamedConfig: resolveNamedConfig2 } = await Promise.resolve().then(() => (init_llm_utils(), llm_utils_exports));
|
|
@@ -5209,35 +5319,35 @@ function createAuthCommand() {
|
|
|
5209
5319
|
try {
|
|
5210
5320
|
resolved = await resolveNamedConfig2(opts.config, configPath);
|
|
5211
5321
|
} catch (err) {
|
|
5212
|
-
console.log(
|
|
5322
|
+
console.log(import_picocolors13.default.red(err instanceof Error ? err.message : String(err)));
|
|
5213
5323
|
return;
|
|
5214
5324
|
}
|
|
5215
5325
|
const configName = resolved.config.name ?? opts.config;
|
|
5216
5326
|
const provider = resolved.config.provider;
|
|
5217
5327
|
if (SUBSCRIPTION_PROVIDERS2.has(provider)) {
|
|
5218
|
-
console.log(
|
|
5328
|
+
console.log(import_picocolors13.default.red("Subscription providers use OAuth login"));
|
|
5219
5329
|
return;
|
|
5220
5330
|
}
|
|
5221
5331
|
if (credentialType === "bearer-token" && provider !== "anthropic-compatible") {
|
|
5222
|
-
console.log(
|
|
5332
|
+
console.log(import_picocolors13.default.red("bearer-token credentials are only supported for anthropic-compatible configs"));
|
|
5223
5333
|
return;
|
|
5224
5334
|
}
|
|
5225
5335
|
if (credentialType === "api-key" && !API_KEY_CREDENTIAL_PROVIDERS.has(provider)) {
|
|
5226
|
-
console.log(
|
|
5336
|
+
console.log(import_picocolors13.default.red("api-key credentials are not supported for this provider"));
|
|
5227
5337
|
return;
|
|
5228
5338
|
}
|
|
5229
5339
|
const secret = (secretArg ?? await (0, import_password.default)({ message: "Secret:" })).trim();
|
|
5230
5340
|
if (!secret) {
|
|
5231
|
-
console.log(
|
|
5341
|
+
console.log(import_picocolors13.default.red("Secret is required"));
|
|
5232
5342
|
return;
|
|
5233
5343
|
}
|
|
5234
5344
|
const { writeAuth } = await import("@vostride/agent-qa-core");
|
|
5235
5345
|
if (credentialType === "bearer-token") {
|
|
5236
5346
|
await writeAuth(configName, { type: "bearer", provider: "anthropic-compatible", token: secret });
|
|
5237
|
-
console.log(
|
|
5347
|
+
console.log(import_picocolors13.default.green(`Saved bearer token for ${configName}`));
|
|
5238
5348
|
} else {
|
|
5239
5349
|
await writeAuth(configName, { type: "api", provider, key: secret });
|
|
5240
|
-
console.log(
|
|
5350
|
+
console.log(import_picocolors13.default.green(`Saved API key for ${configName}`));
|
|
5241
5351
|
}
|
|
5242
5352
|
})
|
|
5243
5353
|
);
|
|
@@ -5246,7 +5356,7 @@ function createAuthCommand() {
|
|
|
5246
5356
|
const { loadAuthPluginsForRawConfig: loadAuthPluginsForRawConfig2, resolveModelAuth: resolveModelAuth2 } = await Promise.resolve().then(() => (init_llm_utils(), llm_utils_exports));
|
|
5247
5357
|
const { loadConfigFile: loadConfigFile2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
5248
5358
|
const configPath = getGlobalConfigPath2(this) ?? "agent-qa.config.yaml";
|
|
5249
|
-
console.log(
|
|
5359
|
+
console.log(import_picocolors13.default.bold("Auth status:\n"));
|
|
5250
5360
|
let llms;
|
|
5251
5361
|
let defaultLLM;
|
|
5252
5362
|
try {
|
|
@@ -5257,20 +5367,20 @@ function createAuthCommand() {
|
|
|
5257
5367
|
} catch {
|
|
5258
5368
|
}
|
|
5259
5369
|
if (!llms?.length) {
|
|
5260
|
-
console.log(
|
|
5370
|
+
console.log(import_picocolors13.default.dim(" No LLM configs found"));
|
|
5261
5371
|
return;
|
|
5262
5372
|
}
|
|
5263
5373
|
const maxNameLen = Math.max(...llms.map((c) => String(c.name ?? "").length), 4) + 12;
|
|
5264
5374
|
for (const llm of llms) {
|
|
5265
5375
|
const name = String(llm.name ?? "");
|
|
5266
5376
|
const isDefault = name === defaultLLM;
|
|
5267
|
-
const label = isDefault ? `${name} ${
|
|
5377
|
+
const label = isDefault ? `${name} ${import_picocolors13.default.dim("(default)")}` : name;
|
|
5268
5378
|
const rawLabel = isDefault ? `${name} (default)` : name;
|
|
5269
5379
|
const padLen = Math.max(0, maxNameLen - rawLabel.length);
|
|
5270
5380
|
const dots = ".".repeat(padLen + 2);
|
|
5271
5381
|
const provider = llm.provider;
|
|
5272
5382
|
const status = statusForResolvedAuth(await resolveModelAuth2(name, llm));
|
|
5273
|
-
console.log(` ${label} ${
|
|
5383
|
+
console.log(` ${label} ${import_picocolors13.default.dim(`[${providerLabel(provider)}]`)} ${import_picocolors13.default.dim(dots)} ${status}`);
|
|
5274
5384
|
}
|
|
5275
5385
|
})
|
|
5276
5386
|
);
|
|
@@ -5287,12 +5397,12 @@ function createAuthCommand() {
|
|
|
5287
5397
|
const resolved = await resolveNamedConfig2(void 0, configPath);
|
|
5288
5398
|
storeKey = resolved.config.name;
|
|
5289
5399
|
} catch {
|
|
5290
|
-
console.log(
|
|
5400
|
+
console.log(import_picocolors13.default.red("Specify --config <name> or run with a config file"));
|
|
5291
5401
|
return;
|
|
5292
5402
|
}
|
|
5293
5403
|
}
|
|
5294
5404
|
await removeAuth(storeKey);
|
|
5295
|
-
console.log(
|
|
5405
|
+
console.log(import_picocolors13.default.green(`Logged out from ${storeKey}`));
|
|
5296
5406
|
})
|
|
5297
5407
|
);
|
|
5298
5408
|
cmd.addCommand(
|
|
@@ -5307,19 +5417,19 @@ function createAuthCommand() {
|
|
|
5307
5417
|
planner = modelFields;
|
|
5308
5418
|
configName = name;
|
|
5309
5419
|
} catch (err) {
|
|
5310
|
-
console.log(
|
|
5420
|
+
console.log(import_picocolors13.default.red(err instanceof Error ? err.message : String(err)));
|
|
5311
5421
|
return;
|
|
5312
5422
|
}
|
|
5313
5423
|
if (opts.provider) planner = { ...planner, provider: opts.provider };
|
|
5314
5424
|
if (opts.model) planner = { ...planner, model: opts.model };
|
|
5315
|
-
console.log(
|
|
5425
|
+
console.log(import_picocolors13.default.dim(`Testing config: ${configName}...`));
|
|
5316
5426
|
const auth = await resolveModelAuth2(configName, planner);
|
|
5317
5427
|
if (auth.kind === "missing") {
|
|
5318
|
-
console.log(
|
|
5428
|
+
console.log(import_picocolors13.default.red(auth.message));
|
|
5319
5429
|
return;
|
|
5320
5430
|
}
|
|
5321
5431
|
if (auth.kind === "unauthenticated") {
|
|
5322
|
-
console.log(
|
|
5432
|
+
console.log(import_picocolors13.default.dim(auth.message));
|
|
5323
5433
|
}
|
|
5324
5434
|
const { createModel, getProviderOptions } = await import("@vostride/agent-qa-core");
|
|
5325
5435
|
const { generateText } = await import("ai");
|
|
@@ -5337,24 +5447,24 @@ function createAuthCommand() {
|
|
|
5337
5447
|
...providerOpts ? { providerOptions: providerOpts } : {}
|
|
5338
5448
|
});
|
|
5339
5449
|
const elapsed = Date.now() - start;
|
|
5340
|
-
console.log(
|
|
5450
|
+
console.log(import_picocolors13.default.green(`Connected to ${planner.provider}/${planner.model} (${elapsed}ms)`));
|
|
5341
5451
|
} catch (err) {
|
|
5342
5452
|
const { message, statusCode } = extractProviderError(err);
|
|
5343
5453
|
const fullContext = `${message} ${statusCode ?? ""}`;
|
|
5344
5454
|
if (/auth|unauthorized|401|invalid.*key|permission/i.test(fullContext)) {
|
|
5345
|
-
console.log(
|
|
5455
|
+
console.log(import_picocolors13.default.red("Authentication failed. Check the saved credential for this config."));
|
|
5346
5456
|
} else if (/model.*not.*support|not found|404|does not exist/i.test(fullContext)) {
|
|
5347
|
-
console.log(
|
|
5457
|
+
console.log(import_picocolors13.default.red("Model not found. Check the model name."));
|
|
5348
5458
|
} else if (/rate|429|quota|limit/i.test(fullContext)) {
|
|
5349
|
-
console.log(
|
|
5459
|
+
console.log(import_picocolors13.default.red("Rate limited. Try again later."));
|
|
5350
5460
|
} else if (/ECONNREFUSED|ENOTFOUND|timeout|abort|fetch failed|network/i.test(fullContext)) {
|
|
5351
|
-
console.log(
|
|
5461
|
+
console.log(import_picocolors13.default.red("Network error. Check the exact base URL and try again."));
|
|
5352
5462
|
} else if (/invalid_request|400|bad request/i.test(fullContext)) {
|
|
5353
|
-
console.log(
|
|
5463
|
+
console.log(import_picocolors13.default.red("Invalid request. Check model and provider settings."));
|
|
5354
5464
|
} else {
|
|
5355
|
-
console.log(
|
|
5465
|
+
console.log(import_picocolors13.default.red(`Connection failed. ${message}`));
|
|
5356
5466
|
}
|
|
5357
|
-
console.log(
|
|
5467
|
+
console.log(import_picocolors13.default.dim(message));
|
|
5358
5468
|
} finally {
|
|
5359
5469
|
clearTimeout(timeout);
|
|
5360
5470
|
}
|
|
@@ -5365,10 +5475,10 @@ function createAuthCommand() {
|
|
|
5365
5475
|
|
|
5366
5476
|
// src/commands/auth-state.ts
|
|
5367
5477
|
var import_promises4 = require("readline/promises");
|
|
5368
|
-
var
|
|
5478
|
+
var import_node_path12 = require("path");
|
|
5369
5479
|
var import_commander13 = require("commander");
|
|
5370
|
-
var
|
|
5371
|
-
var
|
|
5480
|
+
var import_picocolors14 = __toESM(require("picocolors"), 1);
|
|
5481
|
+
var import_agent_qa_core14 = require("@vostride/agent-qa-core");
|
|
5372
5482
|
init_config();
|
|
5373
5483
|
function getGlobalConfigPath3(command) {
|
|
5374
5484
|
return command.parent?.parent?.opts().config ?? command.parent?.opts().config;
|
|
@@ -5406,7 +5516,7 @@ async function waitForCaptureConfirmation(input3) {
|
|
|
5406
5516
|
}
|
|
5407
5517
|
async function authStateExists(paths) {
|
|
5408
5518
|
try {
|
|
5409
|
-
await (0,
|
|
5519
|
+
await (0, import_agent_qa_core14.readAuthStateMetadata)(paths);
|
|
5410
5520
|
return true;
|
|
5411
5521
|
} catch {
|
|
5412
5522
|
return false;
|
|
@@ -5418,7 +5528,7 @@ function buildConfirmationMessage(targetName, stateName, exists) {
|
|
|
5418
5528
|
}
|
|
5419
5529
|
async function loadCommandContext(command) {
|
|
5420
5530
|
const configPath = getGlobalConfigPath3(command) ?? "agent-qa.config.yaml";
|
|
5421
|
-
const configDir = (0,
|
|
5531
|
+
const configDir = (0, import_node_path12.dirname)((0, import_node_path12.resolve)(configPath));
|
|
5422
5532
|
const config = await resolveConfig({ configPath, loadAuthPlugins: false });
|
|
5423
5533
|
return { configPath, configDir, config };
|
|
5424
5534
|
}
|
|
@@ -5427,10 +5537,10 @@ function isSafeAuthStateError(message) {
|
|
|
5427
5537
|
}
|
|
5428
5538
|
function printSafeAuthStateError(error, fallback) {
|
|
5429
5539
|
const message = error instanceof Error ? error.message : String(error);
|
|
5430
|
-
console.error(
|
|
5540
|
+
console.error(import_picocolors14.default.red(isSafeAuthStateError(message) ? message : fallback));
|
|
5431
5541
|
}
|
|
5432
5542
|
function assertWebAuthStateTarget(input3) {
|
|
5433
|
-
(0,
|
|
5543
|
+
(0, import_agent_qa_core14.resolveAuthStatePaths)({
|
|
5434
5544
|
configDir: input3.configDir,
|
|
5435
5545
|
authStateDir: input3.config.services?.authState?.dir,
|
|
5436
5546
|
targetName: input3.target.name,
|
|
@@ -5467,7 +5577,7 @@ function createAuthStateCommand(deps = {}) {
|
|
|
5467
5577
|
assertWebAuthStateTarget({ configDir, config, target });
|
|
5468
5578
|
targetName = target.name;
|
|
5469
5579
|
}
|
|
5470
|
-
const metadata = await (0,
|
|
5580
|
+
const metadata = await (0, import_agent_qa_core14.listAuthStateMetadata)({
|
|
5471
5581
|
configDir,
|
|
5472
5582
|
authStateDir: config.services?.authState?.dir,
|
|
5473
5583
|
targetName
|
|
@@ -5487,7 +5597,7 @@ function createAuthStateCommand(deps = {}) {
|
|
|
5487
5597
|
const target = resolveTarget(config, opts.target);
|
|
5488
5598
|
resolvedTargetName = target.name;
|
|
5489
5599
|
if (opts.name) {
|
|
5490
|
-
await (0,
|
|
5600
|
+
await (0, import_agent_qa_core14.removeAuthStateFiles)({
|
|
5491
5601
|
configDir,
|
|
5492
5602
|
authStateDir: config.services?.authState?.dir,
|
|
5493
5603
|
targetName: target.name,
|
|
@@ -5497,7 +5607,7 @@ function createAuthStateCommand(deps = {}) {
|
|
|
5497
5607
|
console.log(`Removed auth state "${opts.name}" for target "${target.name}".`);
|
|
5498
5608
|
return;
|
|
5499
5609
|
}
|
|
5500
|
-
await (0,
|
|
5610
|
+
await (0, import_agent_qa_core14.removeAuthStateTarget)({
|
|
5501
5611
|
configDir,
|
|
5502
5612
|
authStateDir: config.services?.authState?.dir,
|
|
5503
5613
|
targetName: target.name,
|
|
@@ -5519,7 +5629,7 @@ function createAuthStateCommand(deps = {}) {
|
|
|
5519
5629
|
try {
|
|
5520
5630
|
const { configDir, config } = await loadCommandContext(command);
|
|
5521
5631
|
const target = resolveTarget(config, opts.target);
|
|
5522
|
-
const paths = (0,
|
|
5632
|
+
const paths = (0, import_agent_qa_core14.resolveAuthStatePaths)({
|
|
5523
5633
|
configDir,
|
|
5524
5634
|
authStateDir: config.services?.authState?.dir,
|
|
5525
5635
|
targetName: target.name,
|
|
@@ -5527,7 +5637,7 @@ function createAuthStateCommand(deps = {}) {
|
|
|
5527
5637
|
target: { platform: target.platform }
|
|
5528
5638
|
});
|
|
5529
5639
|
if (!target.url) {
|
|
5530
|
-
console.error(
|
|
5640
|
+
console.error(import_picocolors14.default.red(`Target "${target.name}" must define a web URL for auth-state capture.`));
|
|
5531
5641
|
process.exitCode = 1;
|
|
5532
5642
|
return;
|
|
5533
5643
|
}
|
|
@@ -5551,16 +5661,16 @@ function createAuthStateCommand(deps = {}) {
|
|
|
5551
5661
|
}
|
|
5552
5662
|
if (confirmation === "browser-closed") {
|
|
5553
5663
|
await closeBrowser(browser);
|
|
5554
|
-
console.error(
|
|
5664
|
+
console.error(import_picocolors14.default.red("Browser was closed before auth state was saved."));
|
|
5555
5665
|
process.exitCode = 1;
|
|
5556
5666
|
return;
|
|
5557
5667
|
}
|
|
5558
5668
|
const payload = await context.storageState({ indexedDB: true });
|
|
5559
5669
|
try {
|
|
5560
|
-
await (0,
|
|
5670
|
+
await (0, import_agent_qa_core14.writeAuthStateFiles)(paths, {
|
|
5561
5671
|
payload,
|
|
5562
5672
|
metadata: {
|
|
5563
|
-
version:
|
|
5673
|
+
version: import_agent_qa_core14.AUTH_STATE_SCHEMA_VERSION,
|
|
5564
5674
|
kind: "web",
|
|
5565
5675
|
target: target.name,
|
|
5566
5676
|
name: opts.name,
|
|
@@ -5569,7 +5679,7 @@ function createAuthStateCommand(deps = {}) {
|
|
|
5569
5679
|
});
|
|
5570
5680
|
} catch {
|
|
5571
5681
|
await closeBrowser(browser);
|
|
5572
|
-
console.error(
|
|
5682
|
+
console.error(import_picocolors14.default.red(`Could not save auth state "${opts.name}" for target "${target.name}".`));
|
|
5573
5683
|
process.exitCode = 1;
|
|
5574
5684
|
return;
|
|
5575
5685
|
}
|
|
@@ -5577,7 +5687,7 @@ function createAuthStateCommand(deps = {}) {
|
|
|
5577
5687
|
console.log(`Saved auth state "${opts.name}" for target "${target.name}".`);
|
|
5578
5688
|
} catch (error) {
|
|
5579
5689
|
await closeBrowser(browser);
|
|
5580
|
-
console.error(
|
|
5690
|
+
console.error(import_picocolors14.default.red(error instanceof Error ? error.message : String(error)));
|
|
5581
5691
|
process.exitCode = 1;
|
|
5582
5692
|
}
|
|
5583
5693
|
})
|
|
@@ -5588,34 +5698,34 @@ function createAuthStateCommand(deps = {}) {
|
|
|
5588
5698
|
// src/commands/devices.ts
|
|
5589
5699
|
var import_commander14 = require("commander");
|
|
5590
5700
|
var import_node_child_process5 = require("child_process");
|
|
5591
|
-
var
|
|
5592
|
-
var
|
|
5701
|
+
var import_node_fs5 = require("fs");
|
|
5702
|
+
var import_node_path13 = require("path");
|
|
5593
5703
|
var import_yaml6 = require("yaml");
|
|
5594
|
-
var
|
|
5704
|
+
var import_picocolors15 = __toESM(require("picocolors"), 1);
|
|
5595
5705
|
init_config();
|
|
5596
5706
|
function formatStatus2(transport, localBinding) {
|
|
5597
5707
|
if (transport !== "local") {
|
|
5598
|
-
return
|
|
5708
|
+
return import_picocolors15.default.green("Ready") + import_picocolors15.default.dim(" (no local binding required)");
|
|
5599
5709
|
}
|
|
5600
5710
|
if (!localBinding) {
|
|
5601
|
-
return
|
|
5711
|
+
return import_picocolors15.default.red("UNBOUND") + import_picocolors15.default.yellow(" - run `agent-qa devices init`");
|
|
5602
5712
|
}
|
|
5603
5713
|
const keys = Object.entries(localBinding).slice(0, 2);
|
|
5604
5714
|
const details = keys.map(([k, v]) => `${k}: ${v}`).join(", ");
|
|
5605
|
-
return
|
|
5715
|
+
return import_picocolors15.default.green("Bound") + import_picocolors15.default.dim(` (${details})`);
|
|
5606
5716
|
}
|
|
5607
5717
|
async function listDevices() {
|
|
5608
5718
|
let config;
|
|
5609
5719
|
try {
|
|
5610
5720
|
config = await resolveConfig({});
|
|
5611
5721
|
} catch (err) {
|
|
5612
|
-
console.error(
|
|
5722
|
+
console.error(import_picocolors15.default.red(`Config error: ${err instanceof Error ? err.message : String(err)}`));
|
|
5613
5723
|
process.exitCode = 1;
|
|
5614
5724
|
return;
|
|
5615
5725
|
}
|
|
5616
5726
|
const devices = config.registry?.devices;
|
|
5617
5727
|
if (!devices || Object.keys(devices).length === 0) {
|
|
5618
|
-
console.log(
|
|
5728
|
+
console.log(import_picocolors15.default.yellow("No devices configured in registry.devices"));
|
|
5619
5729
|
return;
|
|
5620
5730
|
}
|
|
5621
5731
|
const localBindings = loadLocalBindings();
|
|
@@ -5623,13 +5733,13 @@ async function listDevices() {
|
|
|
5623
5733
|
const platWidth = 10;
|
|
5624
5734
|
const transWidth = 14;
|
|
5625
5735
|
console.log("");
|
|
5626
|
-
console.log(
|
|
5736
|
+
console.log(import_picocolors15.default.bold(" Configured Devices"));
|
|
5627
5737
|
console.log("");
|
|
5628
5738
|
console.log(
|
|
5629
|
-
` ${
|
|
5739
|
+
` ${import_picocolors15.default.dim("Name".padEnd(nameWidth))} ${import_picocolors15.default.dim("Platform".padEnd(platWidth))} ${import_picocolors15.default.dim("Transport".padEnd(transWidth))} ${import_picocolors15.default.dim("Status")}`
|
|
5630
5740
|
);
|
|
5631
5741
|
console.log(
|
|
5632
|
-
` ${
|
|
5742
|
+
` ${import_picocolors15.default.dim("\u2500".repeat(nameWidth))} ${import_picocolors15.default.dim("\u2500".repeat(platWidth))} ${import_picocolors15.default.dim("\u2500".repeat(transWidth))} ${import_picocolors15.default.dim("\u2500".repeat(30))}`
|
|
5633
5743
|
);
|
|
5634
5744
|
for (const [name, profile] of Object.entries(devices)) {
|
|
5635
5745
|
const localBinding = localBindings?.devices?.[name] ?? null;
|
|
@@ -5680,41 +5790,41 @@ function scanSimctlDevices() {
|
|
|
5680
5790
|
}
|
|
5681
5791
|
}
|
|
5682
5792
|
function appendGitignoreEntry(dir, entry) {
|
|
5683
|
-
const gitignorePath = (0,
|
|
5793
|
+
const gitignorePath = (0, import_node_path13.join)(dir, ".gitignore");
|
|
5684
5794
|
let existing = "";
|
|
5685
|
-
if ((0,
|
|
5686
|
-
existing = (0,
|
|
5795
|
+
if ((0, import_node_fs5.existsSync)(gitignorePath)) {
|
|
5796
|
+
existing = (0, import_node_fs5.readFileSync)(gitignorePath, "utf-8");
|
|
5687
5797
|
}
|
|
5688
5798
|
if (existing.includes(entry)) return;
|
|
5689
5799
|
const append = (existing.length > 0 && !existing.endsWith("\n") ? "\n" : "") + entry + "\n";
|
|
5690
|
-
(0,
|
|
5800
|
+
(0, import_node_fs5.writeFileSync)(gitignorePath, existing + append);
|
|
5691
5801
|
}
|
|
5692
5802
|
async function initDevices() {
|
|
5693
5803
|
const dir = process.cwd();
|
|
5694
|
-
const localFilePath = (0,
|
|
5695
|
-
if ((0,
|
|
5696
|
-
console.log(
|
|
5804
|
+
const localFilePath = (0, import_node_path13.join)(dir, "agent-qa.local.yaml");
|
|
5805
|
+
if ((0, import_node_fs5.existsSync)(localFilePath)) {
|
|
5806
|
+
console.log(import_picocolors15.default.yellow("agent-qa.local.yaml already exists. Skipping generation."));
|
|
5697
5807
|
return;
|
|
5698
5808
|
}
|
|
5699
|
-
console.log(
|
|
5809
|
+
console.log(import_picocolors15.default.bold("\n Scanning for connected devices...\n"));
|
|
5700
5810
|
const adbDevices = scanAdbDevices();
|
|
5701
5811
|
const simctlDevices = scanSimctlDevices();
|
|
5702
5812
|
if (adbDevices.length > 0) {
|
|
5703
|
-
console.log(` ${
|
|
5813
|
+
console.log(` ${import_picocolors15.default.green("Android:")} found ${adbDevices.length} device(s)`);
|
|
5704
5814
|
for (const d of adbDevices) {
|
|
5705
|
-
console.log(` ${
|
|
5815
|
+
console.log(` ${import_picocolors15.default.dim("\u2022")} ${d.model} (${d.serial})`);
|
|
5706
5816
|
}
|
|
5707
5817
|
} else {
|
|
5708
|
-
console.log(` ${
|
|
5818
|
+
console.log(` ${import_picocolors15.default.dim("Android:")} no devices found (adb not available or no devices connected)`);
|
|
5709
5819
|
}
|
|
5710
5820
|
if (simctlDevices.length > 0) {
|
|
5711
5821
|
const booted = simctlDevices.filter((d) => d.state === "Booted");
|
|
5712
|
-
console.log(` ${
|
|
5822
|
+
console.log(` ${import_picocolors15.default.green("iOS:")} found ${simctlDevices.length} simulator(s) (${booted.length} booted)`);
|
|
5713
5823
|
for (const d of booted) {
|
|
5714
|
-
console.log(` ${
|
|
5824
|
+
console.log(` ${import_picocolors15.default.dim("\u2022")} ${d.name} (${d.udid})`);
|
|
5715
5825
|
}
|
|
5716
5826
|
} else {
|
|
5717
|
-
console.log(` ${
|
|
5827
|
+
console.log(` ${import_picocolors15.default.dim("iOS:")} no simulators found (simctl not available or none installed)`);
|
|
5718
5828
|
}
|
|
5719
5829
|
let config;
|
|
5720
5830
|
try {
|
|
@@ -5745,11 +5855,11 @@ async function initDevices() {
|
|
|
5745
5855
|
providers: {}
|
|
5746
5856
|
};
|
|
5747
5857
|
const yamlContent = "# Generated by agent-qa devices init\n# Edit device bindings for your local machine\n" + (0, import_yaml6.stringify)(localFileContent);
|
|
5748
|
-
(0,
|
|
5858
|
+
(0, import_node_fs5.writeFileSync)(localFilePath, yamlContent);
|
|
5749
5859
|
appendGitignoreEntry(dir, "agent-qa.local.yaml");
|
|
5750
5860
|
console.log("");
|
|
5751
|
-
console.log(
|
|
5752
|
-
console.log(
|
|
5861
|
+
console.log(import_picocolors15.default.green(` Generated agent-qa.local.yaml with ${bindCount} device binding(s)`));
|
|
5862
|
+
console.log(import_picocolors15.default.dim(" Added agent-qa.local.yaml to .gitignore"));
|
|
5753
5863
|
console.log("");
|
|
5754
5864
|
}
|
|
5755
5865
|
function createDevicesCommand() {
|
|
@@ -5761,7 +5871,7 @@ function createDevicesCommand() {
|
|
|
5761
5871
|
|
|
5762
5872
|
// src/commands/ids.ts
|
|
5763
5873
|
var import_commander15 = require("commander");
|
|
5764
|
-
var
|
|
5874
|
+
var import_picocolors16 = __toESM(require("picocolors"), 1);
|
|
5765
5875
|
var import_agent_qa_ids = require("@vostride/agent-qa-ids");
|
|
5766
5876
|
function parseEntityType(value) {
|
|
5767
5877
|
if (import_agent_qa_ids.ENTITY_ID_TYPES.includes(value)) {
|
|
@@ -5782,7 +5892,7 @@ function createIdsCommand() {
|
|
|
5782
5892
|
console.log(id);
|
|
5783
5893
|
}
|
|
5784
5894
|
} catch (err) {
|
|
5785
|
-
console.error(
|
|
5895
|
+
console.error(import_picocolors16.default.red(err instanceof Error ? err.message : String(err)));
|
|
5786
5896
|
process.exitCode = 1;
|
|
5787
5897
|
}
|
|
5788
5898
|
});
|
|
@@ -5801,15 +5911,15 @@ function createIdsCommand() {
|
|
|
5801
5911
|
if (opts.json) {
|
|
5802
5912
|
console.log(JSON.stringify(result, null, 2));
|
|
5803
5913
|
} else if (valid) {
|
|
5804
|
-
console.log(
|
|
5914
|
+
console.log(import_picocolors16.default.green(result.message));
|
|
5805
5915
|
} else {
|
|
5806
|
-
console.error(
|
|
5916
|
+
console.error(import_picocolors16.default.red(result.message));
|
|
5807
5917
|
}
|
|
5808
5918
|
if (!valid) {
|
|
5809
5919
|
process.exitCode = 1;
|
|
5810
5920
|
}
|
|
5811
5921
|
} catch (err) {
|
|
5812
|
-
console.error(
|
|
5922
|
+
console.error(import_picocolors16.default.red(err instanceof Error ? err.message : String(err)));
|
|
5813
5923
|
process.exitCode = 1;
|
|
5814
5924
|
}
|
|
5815
5925
|
});
|
|
@@ -5821,14 +5931,14 @@ var import_commander16 = require("commander");
|
|
|
5821
5931
|
var import_agent_qa_ids2 = require("@vostride/agent-qa-ids");
|
|
5822
5932
|
var import_yaml7 = require("yaml");
|
|
5823
5933
|
var import_promises5 = require("fs/promises");
|
|
5824
|
-
var
|
|
5825
|
-
var
|
|
5826
|
-
var
|
|
5934
|
+
var import_node_fs6 = require("fs");
|
|
5935
|
+
var import_node_path14 = require("path");
|
|
5936
|
+
var import_picocolors17 = __toESM(require("picocolors"), 1);
|
|
5827
5937
|
function createCreateTestCommand() {
|
|
5828
5938
|
const cmd = new import_commander16.Command("create-test").description("Scaffold a new test YAML file with auto-generated ID").argument("<path>", "output file path for the test YAML").action(async (path) => {
|
|
5829
|
-
const resolved = (0,
|
|
5830
|
-
if ((0,
|
|
5831
|
-
console.error(
|
|
5939
|
+
const resolved = (0, import_node_path14.resolve)(path);
|
|
5940
|
+
if ((0, import_node_fs6.existsSync)(resolved)) {
|
|
5941
|
+
console.error(import_picocolors17.default.red("Error: File already exists: " + resolved));
|
|
5832
5942
|
process.exitCode = 1;
|
|
5833
5943
|
return;
|
|
5834
5944
|
}
|
|
@@ -5840,10 +5950,10 @@ function createCreateTestCommand() {
|
|
|
5840
5950
|
steps: ["Describe your first test step here"]
|
|
5841
5951
|
};
|
|
5842
5952
|
const yamlContent = (0, import_yaml7.stringify)(template);
|
|
5843
|
-
await (0, import_promises5.mkdir)((0,
|
|
5953
|
+
await (0, import_promises5.mkdir)((0, import_node_path14.dirname)(resolved), { recursive: true });
|
|
5844
5954
|
await (0, import_promises5.writeFile)(resolved, yamlContent, "utf-8");
|
|
5845
|
-
console.log(
|
|
5846
|
-
console.log(
|
|
5955
|
+
console.log(import_picocolors17.default.green("Created " + resolved));
|
|
5956
|
+
console.log(import_picocolors17.default.dim(" test-id: " + testId));
|
|
5847
5957
|
});
|
|
5848
5958
|
return cmd;
|
|
5849
5959
|
}
|
|
@@ -5853,14 +5963,14 @@ var import_commander17 = require("commander");
|
|
|
5853
5963
|
var import_agent_qa_ids3 = require("@vostride/agent-qa-ids");
|
|
5854
5964
|
var import_yaml8 = require("yaml");
|
|
5855
5965
|
var import_promises6 = require("fs/promises");
|
|
5856
|
-
var
|
|
5857
|
-
var
|
|
5858
|
-
var
|
|
5966
|
+
var import_node_fs7 = require("fs");
|
|
5967
|
+
var import_node_path15 = require("path");
|
|
5968
|
+
var import_picocolors18 = __toESM(require("picocolors"), 1);
|
|
5859
5969
|
function createCreateSuiteCommand() {
|
|
5860
5970
|
const cmd = new import_commander17.Command("create-suite").description("Scaffold a new suite YAML file with auto-generated ID").argument("<path>", "output file path for the suite YAML").action(async (path) => {
|
|
5861
|
-
const resolved = (0,
|
|
5862
|
-
if ((0,
|
|
5863
|
-
console.error(
|
|
5971
|
+
const resolved = (0, import_node_path15.resolve)(path);
|
|
5972
|
+
if ((0, import_node_fs7.existsSync)(resolved)) {
|
|
5973
|
+
console.error(import_picocolors18.default.red("Error: File already exists: " + resolved));
|
|
5864
5974
|
process.exitCode = 1;
|
|
5865
5975
|
return;
|
|
5866
5976
|
}
|
|
@@ -5874,22 +5984,22 @@ function createCreateSuiteCommand() {
|
|
|
5874
5984
|
"tests: []",
|
|
5875
5985
|
"tests: []\n # - test: ./path/to/test.yaml\n # id: my-test-id"
|
|
5876
5986
|
);
|
|
5877
|
-
await (0, import_promises6.mkdir)((0,
|
|
5987
|
+
await (0, import_promises6.mkdir)((0, import_node_path15.dirname)(resolved), { recursive: true });
|
|
5878
5988
|
await (0, import_promises6.writeFile)(resolved, yamlContent, "utf-8");
|
|
5879
|
-
console.log(
|
|
5880
|
-
console.log(
|
|
5989
|
+
console.log(import_picocolors18.default.green("Created " + resolved));
|
|
5990
|
+
console.log(import_picocolors18.default.dim(" suite-id: " + suiteId));
|
|
5881
5991
|
});
|
|
5882
5992
|
return cmd;
|
|
5883
5993
|
}
|
|
5884
5994
|
|
|
5885
5995
|
// src/commands/clean-memory.ts
|
|
5886
5996
|
var import_commander18 = require("commander");
|
|
5887
|
-
var
|
|
5997
|
+
var import_picocolors19 = __toESM(require("picocolors"), 1);
|
|
5888
5998
|
var import_node_readline3 = require("readline");
|
|
5889
5999
|
var import_promises7 = require("fs/promises");
|
|
5890
|
-
var
|
|
5891
|
-
var
|
|
5892
|
-
var
|
|
6000
|
+
var import_node_fs8 = require("fs");
|
|
6001
|
+
var import_node_path16 = require("path");
|
|
6002
|
+
var import_agent_qa_core15 = require("@vostride/agent-qa-core");
|
|
5893
6003
|
init_config();
|
|
5894
6004
|
function formatSize(bytes) {
|
|
5895
6005
|
if (bytes === 0) return "0 B";
|
|
@@ -5897,13 +6007,13 @@ function formatSize(bytes) {
|
|
|
5897
6007
|
return `${(bytes / 1048576).toFixed(1)} MB`;
|
|
5898
6008
|
}
|
|
5899
6009
|
async function getOrphanInfo(memoryRoot, tier, scope) {
|
|
5900
|
-
const scopePath = (0,
|
|
5901
|
-
const obsFiles = await (0,
|
|
6010
|
+
const scopePath = (0, import_node_path16.join)(memoryRoot, tier, scope);
|
|
6011
|
+
const obsFiles = await (0, import_agent_qa_core15.listObservations)(scopePath);
|
|
5902
6012
|
let newest = null;
|
|
5903
6013
|
for (const filename of obsFiles) {
|
|
5904
6014
|
try {
|
|
5905
|
-
const content = await (0, import_promises7.readFile)((0,
|
|
5906
|
-
const { data } = (0,
|
|
6015
|
+
const content = await (0, import_promises7.readFile)((0, import_node_path16.join)(scopePath, filename), "utf-8");
|
|
6016
|
+
const { data } = (0, import_agent_qa_core15.parseObservation)(content, filename);
|
|
5907
6017
|
if (data?.last_confirmed) {
|
|
5908
6018
|
if (!newest || data.last_confirmed > newest) {
|
|
5909
6019
|
newest = data.last_confirmed;
|
|
@@ -5917,7 +6027,7 @@ async function getOrphanInfo(memoryRoot, tier, scope) {
|
|
|
5917
6027
|
const entries = await (0, import_promises7.readdir)(scopePath);
|
|
5918
6028
|
for (const entry of entries) {
|
|
5919
6029
|
try {
|
|
5920
|
-
const s = await (0, import_promises7.stat)((0,
|
|
6030
|
+
const s = await (0, import_promises7.stat)((0, import_node_path16.join)(scopePath, entry));
|
|
5921
6031
|
if (s.isFile()) totalSize += s.size;
|
|
5922
6032
|
} catch {
|
|
5923
6033
|
}
|
|
@@ -5925,7 +6035,7 @@ async function getOrphanInfo(memoryRoot, tier, scope) {
|
|
|
5925
6035
|
} catch {
|
|
5926
6036
|
}
|
|
5927
6037
|
return {
|
|
5928
|
-
path: (0,
|
|
6038
|
+
path: (0, import_node_path16.join)(tier, scope),
|
|
5929
6039
|
tier,
|
|
5930
6040
|
scope,
|
|
5931
6041
|
fileCount: obsFiles.length,
|
|
@@ -5938,17 +6048,17 @@ function createCleanMemoryCommand() {
|
|
|
5938
6048
|
const opts = command.opts();
|
|
5939
6049
|
const configPath = command.parent?.opts()?.config ?? "agent-qa.config.yaml";
|
|
5940
6050
|
const config = await resolveConfig({ configPath });
|
|
5941
|
-
const workspace = (0,
|
|
6051
|
+
const workspace = (0, import_agent_qa_core15.resolveWorkspacePaths)({ config, configPath });
|
|
5942
6052
|
const expectedProducts = /* @__PURE__ */ new Set();
|
|
5943
6053
|
const targets = config.registry?.targets ?? {};
|
|
5944
6054
|
for (const [name, entry] of Object.entries(targets)) {
|
|
5945
6055
|
expectedProducts.add(entry?.product ?? name);
|
|
5946
6056
|
}
|
|
5947
6057
|
const expectedTests = /* @__PURE__ */ new Set();
|
|
5948
|
-
for (const file of await (0,
|
|
6058
|
+
for (const file of await (0, import_agent_qa_core15.discoverWorkspaceFiles)({ workspace, kind: "test" })) {
|
|
5949
6059
|
try {
|
|
5950
6060
|
const content = await (0, import_promises7.readFile)(file.absolutePath, "utf-8");
|
|
5951
|
-
const result = (0,
|
|
6061
|
+
const result = (0, import_agent_qa_core15.parseTestFile)(content, file.workspaceRelativePath);
|
|
5952
6062
|
for (const t of result.tests) {
|
|
5953
6063
|
if (t["test-id"]) expectedTests.add(t["test-id"]);
|
|
5954
6064
|
expectedTests.add(t.name);
|
|
@@ -5957,17 +6067,17 @@ function createCleanMemoryCommand() {
|
|
|
5957
6067
|
}
|
|
5958
6068
|
}
|
|
5959
6069
|
const expectedSuites = /* @__PURE__ */ new Set();
|
|
5960
|
-
for (const file of await (0,
|
|
6070
|
+
for (const file of await (0, import_agent_qa_core15.discoverWorkspaceFiles)({ workspace, kind: "suite" })) {
|
|
5961
6071
|
try {
|
|
5962
|
-
const suite = await (0,
|
|
6072
|
+
const suite = await (0, import_agent_qa_core15.parseSuiteFile)(file.absolutePath);
|
|
5963
6073
|
if (suite["suite-id"]) expectedSuites.add(suite["suite-id"]);
|
|
5964
6074
|
expectedSuites.add(suite.name);
|
|
5965
6075
|
} catch {
|
|
5966
6076
|
}
|
|
5967
6077
|
}
|
|
5968
|
-
const memoryRoot = (0,
|
|
5969
|
-
if (!(0,
|
|
5970
|
-
console.log(
|
|
6078
|
+
const memoryRoot = (0, import_agent_qa_core15.resolveMemoryRoot)(config, workspace.configDir);
|
|
6079
|
+
if (!(0, import_node_fs8.existsSync)(memoryRoot)) {
|
|
6080
|
+
console.log(import_picocolors19.default.dim("No memory directory found."));
|
|
5971
6081
|
return;
|
|
5972
6082
|
}
|
|
5973
6083
|
const TIERS = [
|
|
@@ -5977,7 +6087,7 @@ function createCleanMemoryCommand() {
|
|
|
5977
6087
|
];
|
|
5978
6088
|
const orphans = [];
|
|
5979
6089
|
for (const tier of TIERS) {
|
|
5980
|
-
const tierPath = (0,
|
|
6090
|
+
const tierPath = (0, import_node_path16.join)(memoryRoot, tier.dir);
|
|
5981
6091
|
let entries;
|
|
5982
6092
|
try {
|
|
5983
6093
|
entries = await (0, import_promises7.readdir)(tierPath);
|
|
@@ -5986,7 +6096,7 @@ function createCleanMemoryCommand() {
|
|
|
5986
6096
|
}
|
|
5987
6097
|
for (const entry of entries) {
|
|
5988
6098
|
try {
|
|
5989
|
-
const s = await (0, import_promises7.stat)((0,
|
|
6099
|
+
const s = await (0, import_promises7.stat)((0, import_node_path16.join)(tierPath, entry));
|
|
5990
6100
|
if (!s.isDirectory()) continue;
|
|
5991
6101
|
} catch {
|
|
5992
6102
|
continue;
|
|
@@ -5997,17 +6107,17 @@ function createCleanMemoryCommand() {
|
|
|
5997
6107
|
}
|
|
5998
6108
|
}
|
|
5999
6109
|
if (orphans.length === 0) {
|
|
6000
|
-
console.log(
|
|
6110
|
+
console.log(import_picocolors19.default.dim("No orphaned memory directories found."));
|
|
6001
6111
|
return;
|
|
6002
6112
|
}
|
|
6003
6113
|
const maxPathLen = Math.max("Path".length, ...orphans.map((o) => o.path.length));
|
|
6004
6114
|
console.log("");
|
|
6005
|
-
console.log(
|
|
6115
|
+
console.log(import_picocolors19.default.bold("Orphaned memory directories:"));
|
|
6006
6116
|
console.log("");
|
|
6007
|
-
console.log(" " +
|
|
6008
|
-
console.log(" " +
|
|
6117
|
+
console.log(" " + import_picocolors19.default.dim("Path".padEnd(maxPathLen + 2) + "Files".padStart(7) + " " + "Last Confirmed".padEnd(20) + "Size".padStart(10)));
|
|
6118
|
+
console.log(" " + import_picocolors19.default.dim("-".repeat(maxPathLen + 2 + 7 + 2 + 20 + 10)));
|
|
6009
6119
|
for (const o of orphans) {
|
|
6010
|
-
const confirmed = o.lastConfirmed ??
|
|
6120
|
+
const confirmed = o.lastConfirmed ?? import_picocolors19.default.dim("never");
|
|
6011
6121
|
console.log(" " + o.path.padEnd(maxPathLen + 2) + String(o.fileCount).padStart(7) + " " + String(confirmed).padEnd(20) + formatSize(o.size).padStart(10));
|
|
6012
6122
|
}
|
|
6013
6123
|
console.log("");
|
|
@@ -6023,78 +6133,78 @@ function createCleanMemoryCommand() {
|
|
|
6023
6133
|
}
|
|
6024
6134
|
}
|
|
6025
6135
|
for (const orphan of orphans) {
|
|
6026
|
-
await (0, import_promises7.rm)((0,
|
|
6136
|
+
await (0, import_promises7.rm)((0, import_node_path16.join)(memoryRoot, orphan.tier, orphan.scope), { recursive: true, force: true });
|
|
6027
6137
|
}
|
|
6028
|
-
console.log(
|
|
6138
|
+
console.log(import_picocolors19.default.green(`Deleted ${orphans.length} orphaned director${orphans.length === 1 ? "y" : "ies"}.`));
|
|
6029
6139
|
});
|
|
6030
6140
|
return cmd;
|
|
6031
6141
|
}
|
|
6032
6142
|
|
|
6033
6143
|
// src/commands/skills.ts
|
|
6034
6144
|
var import_commander19 = require("commander");
|
|
6035
|
-
var
|
|
6036
|
-
var
|
|
6145
|
+
var import_node_fs9 = require("fs");
|
|
6146
|
+
var import_node_path17 = require("path");
|
|
6037
6147
|
var import_node_url = require("url");
|
|
6038
6148
|
var import_meta4 = {};
|
|
6039
6149
|
function findPackageRoot(startDir) {
|
|
6040
|
-
let current = (0,
|
|
6150
|
+
let current = (0, import_node_path17.resolve)(startDir);
|
|
6041
6151
|
while (true) {
|
|
6042
|
-
const packagePath = (0,
|
|
6043
|
-
if ((0,
|
|
6152
|
+
const packagePath = (0, import_node_path17.join)(current, "package.json");
|
|
6153
|
+
if ((0, import_node_fs9.existsSync)(packagePath)) {
|
|
6044
6154
|
try {
|
|
6045
|
-
const pkg = JSON.parse((0,
|
|
6155
|
+
const pkg = JSON.parse((0, import_node_fs9.readFileSync)(packagePath, "utf-8"));
|
|
6046
6156
|
if (pkg.name === "agent-qa") {
|
|
6047
6157
|
return current;
|
|
6048
6158
|
}
|
|
6049
6159
|
} catch {
|
|
6050
6160
|
}
|
|
6051
6161
|
}
|
|
6052
|
-
const parent = (0,
|
|
6162
|
+
const parent = (0, import_node_path17.dirname)(current);
|
|
6053
6163
|
if (parent === current) {
|
|
6054
|
-
return (0,
|
|
6164
|
+
return (0, import_node_path17.resolve)(startDir);
|
|
6055
6165
|
}
|
|
6056
6166
|
current = parent;
|
|
6057
6167
|
}
|
|
6058
6168
|
}
|
|
6059
6169
|
function hasSkillDirectories(dir) {
|
|
6060
|
-
if (!(0,
|
|
6170
|
+
if (!(0, import_node_fs9.existsSync)(dir)) return false;
|
|
6061
6171
|
try {
|
|
6062
|
-
return (0,
|
|
6063
|
-
const skillDir = (0,
|
|
6064
|
-
return (0,
|
|
6172
|
+
return (0, import_node_fs9.readdirSync)(dir).some((entry) => {
|
|
6173
|
+
const skillDir = (0, import_node_path17.join)(dir, entry);
|
|
6174
|
+
return (0, import_node_fs9.statSync)(skillDir).isDirectory() && (0, import_node_fs9.existsSync)((0, import_node_path17.join)(skillDir, "SKILL.md"));
|
|
6065
6175
|
});
|
|
6066
6176
|
} catch {
|
|
6067
6177
|
return false;
|
|
6068
6178
|
}
|
|
6069
6179
|
}
|
|
6070
|
-
function resolveAgentQaSkillsDirectory(startDir = (0,
|
|
6180
|
+
function resolveAgentQaSkillsDirectory(startDir = (0, import_node_path17.dirname)((0, import_node_url.fileURLToPath)(import_meta4.url))) {
|
|
6071
6181
|
const packageRoot = findPackageRoot(startDir);
|
|
6072
|
-
const packagedSkills = (0,
|
|
6182
|
+
const packagedSkills = (0, import_node_path17.join)(packageRoot, "skills");
|
|
6073
6183
|
if (hasSkillDirectories(packagedSkills)) {
|
|
6074
6184
|
return { path: packagedSkills, source: "package" };
|
|
6075
6185
|
}
|
|
6076
|
-
const sourceSkills = (0,
|
|
6186
|
+
const sourceSkills = (0, import_node_path17.resolve)(packageRoot, "../..", "skills");
|
|
6077
6187
|
if (hasSkillDirectories(sourceSkills)) {
|
|
6078
6188
|
return { path: sourceSkills, source: "source" };
|
|
6079
6189
|
}
|
|
6080
6190
|
return { path: packagedSkills, source: "missing" };
|
|
6081
6191
|
}
|
|
6082
6192
|
function parseSkillDescription(skillPath) {
|
|
6083
|
-
const body = (0,
|
|
6193
|
+
const body = (0, import_node_fs9.readFileSync)(skillPath, "utf-8");
|
|
6084
6194
|
const description = body.match(/^description:\s*(.+)$/m)?.[1]?.trim();
|
|
6085
6195
|
return description?.replace(/^["']|["']$/g, "");
|
|
6086
6196
|
}
|
|
6087
6197
|
function listAgentQaSkills(skillsDir) {
|
|
6088
6198
|
if (!hasSkillDirectories(skillsDir)) return [];
|
|
6089
|
-
return (0,
|
|
6090
|
-
const skillDir = (0,
|
|
6091
|
-
return (0,
|
|
6199
|
+
return (0, import_node_fs9.readdirSync)(skillsDir).filter((entry) => {
|
|
6200
|
+
const skillDir = (0, import_node_path17.join)(skillsDir, entry);
|
|
6201
|
+
return (0, import_node_fs9.statSync)(skillDir).isDirectory() && (0, import_node_fs9.existsSync)((0, import_node_path17.join)(skillDir, "SKILL.md"));
|
|
6092
6202
|
}).sort().map((entry) => {
|
|
6093
|
-
const skillDir = (0,
|
|
6203
|
+
const skillDir = (0, import_node_path17.join)(skillsDir, entry);
|
|
6094
6204
|
return {
|
|
6095
6205
|
name: entry,
|
|
6096
6206
|
path: skillDir,
|
|
6097
|
-
description: parseSkillDescription((0,
|
|
6207
|
+
description: parseSkillDescription((0, import_node_path17.join)(skillDir, "SKILL.md"))
|
|
6098
6208
|
};
|
|
6099
6209
|
});
|
|
6100
6210
|
}
|