@vostride/agent-qa-core 0.1.9 → 0.1.11

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/index.cjs CHANGED
@@ -93,14 +93,14 @@ function toObservationFrontmatter(data) {
93
93
  }
94
94
  async function ensureMemoryDirs(root) {
95
95
  await Promise.all(
96
- MEMORY_TIERS.map((tier) => (0, import_promises8.mkdir)((0, import_node_path11.join)(root, tier), { recursive: true }))
96
+ MEMORY_TIERS.map((tier) => (0, import_promises8.mkdir)((0, import_node_path12.join)(root, tier), { recursive: true }))
97
97
  );
98
98
  }
99
99
  async function writeObservation(root, tier, scope, data) {
100
- const dir = (0, import_node_path11.join)(root, tier, scope);
101
- const resolved = (0, import_node_path11.resolve)(dir);
102
- const boundary = (0, import_node_path11.resolve)(root);
103
- if (!resolved.startsWith(boundary + import_node_path11.sep)) {
100
+ const dir = (0, import_node_path12.join)(root, tier, scope);
101
+ const resolved = (0, import_node_path12.resolve)(dir);
102
+ const boundary = (0, import_node_path12.resolve)(root);
103
+ if (!resolved.startsWith(boundary + import_node_path12.sep)) {
104
104
  throw new Error(`Scope "${scope}" escapes memory root`);
105
105
  }
106
106
  const schema = "position" in data ? SuiteObservationWriteSchema : BaseObservationWriteSchema;
@@ -111,7 +111,7 @@ async function writeObservation(root, tier, scope, data) {
111
111
  ${(0, import_yaml3.stringify)({ ...frontmatter }, { lineWidth: 0 })}---
112
112
  ${validated.content}
113
113
  `;
114
- const filePath = (0, import_node_path11.join)(dir, `${data.id}.md`);
114
+ const filePath = (0, import_node_path12.join)(dir, `${data.id}.md`);
115
115
  await (0, import_promises8.writeFile)(filePath, content, "utf-8");
116
116
  return filePath;
117
117
  }
@@ -158,13 +158,13 @@ async function listObservations(dir) {
158
158
  }
159
159
  return entries.filter((f) => f.startsWith("obs_") && f.endsWith(".md")).sort();
160
160
  }
161
- var import_yaml3, import_promises8, import_node_path11, MEMORY_TIERS;
161
+ var import_yaml3, import_promises8, import_node_path12, MEMORY_TIERS;
162
162
  var init_observation_io = __esm({
163
163
  "src/memory/observation-io.ts"() {
164
164
  "use strict";
165
165
  import_yaml3 = require("yaml");
166
166
  import_promises8 = require("fs/promises");
167
- import_node_path11 = require("path");
167
+ import_node_path12 = require("path");
168
168
  init_schema();
169
169
  MEMORY_TIERS = ["products", "suites", "tests"];
170
170
  }
@@ -238,14 +238,14 @@ function isBetterSqlite3NodeGypSymlinkConflict(error) {
238
238
  return message.includes("node_gyp_bins/python3") && message.includes("EEXIST");
239
239
  }
240
240
  function resolveCurrentRuntimeNpmCliPath() {
241
- const nodeInstallRoot = (0, import_node_path18.dirname)((0, import_node_path18.dirname)(process.execPath));
242
- const npmCliPath = (0, import_node_path18.join)(nodeInstallRoot, "lib", "node_modules", "npm", "bin", "npm-cli.js");
243
- return (0, import_node_fs6.existsSync)(npmCliPath) ? npmCliPath : null;
241
+ const nodeInstallRoot = (0, import_node_path19.dirname)((0, import_node_path19.dirname)(process.execPath));
242
+ const npmCliPath = (0, import_node_path19.join)(nodeInstallRoot, "lib", "node_modules", "npm", "bin", "npm-cli.js");
243
+ return (0, import_node_fs7.existsSync)(npmCliPath) ? npmCliPath : null;
244
244
  }
245
245
  function resolveCurrentRuntimeNodeDir() {
246
- const nodeInstallRoot = (0, import_node_path18.dirname)((0, import_node_path18.dirname)(process.execPath));
247
- const nodeHeaderPath = (0, import_node_path18.join)(nodeInstallRoot, "include", "node", "node.h");
248
- return (0, import_node_fs6.existsSync)(nodeHeaderPath) ? nodeInstallRoot : null;
246
+ const nodeInstallRoot = (0, import_node_path19.dirname)((0, import_node_path19.dirname)(process.execPath));
247
+ const nodeHeaderPath = (0, import_node_path19.join)(nodeInstallRoot, "include", "node", "node.h");
248
+ return (0, import_node_fs7.existsSync)(nodeHeaderPath) ? nodeInstallRoot : null;
249
249
  }
250
250
  function resolveBetterSqliteRebuildCommand() {
251
251
  const npmCliPath = resolveCurrentRuntimeNpmCliPath();
@@ -266,31 +266,31 @@ function sleepSync(milliseconds) {
266
266
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
267
267
  }
268
268
  function waitForBetterSqlite3BuildOutput(installRoot, timeoutMs = 3e4) {
269
- const bindingPath = (0, import_node_path18.join)(installRoot, "build", "Release", "better_sqlite3.node");
269
+ const bindingPath = (0, import_node_path19.join)(installRoot, "build", "Release", "better_sqlite3.node");
270
270
  const deadline = Date.now() + timeoutMs;
271
271
  while (Date.now() < deadline) {
272
- if ((0, import_node_fs6.existsSync)(bindingPath)) {
272
+ if ((0, import_node_fs7.existsSync)(bindingPath)) {
273
273
  return true;
274
274
  }
275
275
  sleepSync(200);
276
276
  }
277
- return (0, import_node_fs6.existsSync)(bindingPath);
277
+ return (0, import_node_fs7.existsSync)(bindingPath);
278
278
  }
279
279
  function acquireBetterSqlite3RebuildLock(installRoot) {
280
- const lockRoot = (0, import_node_path18.join)((0, import_node_os3.tmpdir)(), "agent-qa-better-sqlite3-rebuild-locks");
280
+ const lockRoot = (0, import_node_path19.join)((0, import_node_os3.tmpdir)(), "agent-qa-better-sqlite3-rebuild-locks");
281
281
  const lockKey = (0, import_node_crypto6.createHash)("sha256").update(`${installRoot}\0${process.version}`).digest("hex");
282
- const lockDir = (0, import_node_path18.join)(lockRoot, lockKey);
282
+ const lockDir = (0, import_node_path19.join)(lockRoot, lockKey);
283
283
  const deadline = Date.now() + 12e4;
284
- (0, import_node_fs6.mkdirSync)(lockRoot, { recursive: true });
284
+ (0, import_node_fs7.mkdirSync)(lockRoot, { recursive: true });
285
285
  while (true) {
286
286
  try {
287
- (0, import_node_fs6.mkdirSync)(lockDir);
288
- (0, import_node_fs6.writeFileSync)((0, import_node_path18.join)(lockDir, "owner"), `${process.pid}
287
+ (0, import_node_fs7.mkdirSync)(lockDir);
288
+ (0, import_node_fs7.writeFileSync)((0, import_node_path19.join)(lockDir, "owner"), `${process.pid}
289
289
  ${(/* @__PURE__ */ new Date()).toISOString()}
290
290
  ${installRoot}
291
291
  `);
292
292
  return () => {
293
- (0, import_node_fs6.rmSync)(lockDir, { recursive: true, force: true });
293
+ (0, import_node_fs7.rmSync)(lockDir, { recursive: true, force: true });
294
294
  };
295
295
  } catch (error) {
296
296
  const code = error instanceof Error && "code" in error ? error.code : void 0;
@@ -298,7 +298,7 @@ ${installRoot}
298
298
  throw error;
299
299
  }
300
300
  if (Date.now() > deadline) {
301
- (0, import_node_fs6.rmSync)(lockDir, { recursive: true, force: true });
301
+ (0, import_node_fs7.rmSync)(lockDir, { recursive: true, force: true });
302
302
  continue;
303
303
  }
304
304
  sleepSync(200);
@@ -314,13 +314,13 @@ function rebuildBetterSqlite3ForCurrentRuntime(originalError) {
314
314
  }
315
315
  rebuildAttempted = true;
316
316
  const packageJsonPath = require2.resolve("better-sqlite3/package.json");
317
- const installRoot = (0, import_node_path18.dirname)(packageJsonPath);
317
+ const installRoot = (0, import_node_path19.dirname)(packageJsonPath);
318
318
  const rebuildCommand = resolveBetterSqliteRebuildCommand();
319
- const nodeGypDevDir = (0, import_node_path18.join)((0, import_node_os3.tmpdir)(), "agent-qa-node-gyp", process.version.replace(/^v/, ""));
320
- const npmCacheDir = (0, import_node_path18.join)((0, import_node_os3.tmpdir)(), "agent-qa-npm-cache");
319
+ const nodeGypDevDir = (0, import_node_path19.join)((0, import_node_os3.tmpdir)(), "agent-qa-node-gyp", process.version.replace(/^v/, ""));
320
+ const npmCacheDir = (0, import_node_path19.join)((0, import_node_os3.tmpdir)(), "agent-qa-npm-cache");
321
321
  const nodeDir = resolveCurrentRuntimeNodeDir();
322
- const nodeGypBinsPath = (0, import_node_path18.join)(installRoot, "build", "node_gyp_bins");
323
- const buildRoot = (0, import_node_path18.join)(installRoot, "build");
322
+ const nodeGypBinsPath = (0, import_node_path19.join)(installRoot, "build", "node_gyp_bins");
323
+ const buildRoot = (0, import_node_path19.join)(installRoot, "build");
324
324
  const releaseLock = acquireBetterSqlite3RebuildLock(installRoot);
325
325
  try {
326
326
  clearBetterSqlite3Cache();
@@ -333,10 +333,10 @@ function rebuildBetterSqlite3ForCurrentRuntime(originalError) {
333
333
  throw error;
334
334
  }
335
335
  }
336
- (0, import_node_fs6.mkdirSync)(nodeGypDevDir, { recursive: true });
337
- (0, import_node_fs6.mkdirSync)(npmCacheDir, { recursive: true });
338
- (0, import_node_fs6.rmSync)(buildRoot, { recursive: true, force: true });
339
- (0, import_node_fs6.rmSync)(nodeGypBinsPath, { recursive: true, force: true });
336
+ (0, import_node_fs7.mkdirSync)(nodeGypDevDir, { recursive: true });
337
+ (0, import_node_fs7.mkdirSync)(npmCacheDir, { recursive: true });
338
+ (0, import_node_fs7.rmSync)(buildRoot, { recursive: true, force: true });
339
+ (0, import_node_fs7.rmSync)(nodeGypBinsPath, { recursive: true, force: true });
340
340
  if (!process.env.VITEST) {
341
341
  console.warn(`[agent-qa] Rebuilding better-sqlite3 for Node ${process.version} in ${installRoot}`);
342
342
  }
@@ -390,16 +390,16 @@ function createBetterSqlite3Database(path5) {
390
390
  return openBetterSqlite3Database(path5);
391
391
  }
392
392
  }
393
- var import_node_child_process2, import_node_fs6, import_node_crypto6, import_node_module3, import_node_os3, import_node_path18, import_meta3, require2, sqliteConstructor, rebuildAttempted, rebuildFailureMessage;
393
+ var import_node_child_process2, import_node_fs7, import_node_crypto6, import_node_module3, import_node_os3, import_node_path19, import_meta3, require2, sqliteConstructor, rebuildAttempted, rebuildFailureMessage;
394
394
  var init_sqlite = __esm({
395
395
  "src/sqlite.ts"() {
396
396
  "use strict";
397
397
  import_node_child_process2 = require("child_process");
398
- import_node_fs6 = require("fs");
398
+ import_node_fs7 = require("fs");
399
399
  import_node_crypto6 = require("crypto");
400
400
  import_node_module3 = require("module");
401
401
  import_node_os3 = require("os");
402
- import_node_path18 = require("path");
402
+ import_node_path19 = require("path");
403
403
  import_meta3 = {};
404
404
  require2 = (0, import_node_module3.createRequire)(import_meta3.url);
405
405
  sqliteConstructor = null;
@@ -430,13 +430,13 @@ async function buildMemoryIndex(params) {
430
430
  const insert = db.prepare("INSERT INTO observations(title, content, id, trust) VALUES (?, ?, ?, ?)");
431
431
  const dirs = [];
432
432
  if (validatePathComponent(params.product)) {
433
- dirs.push({ path: (0, import_node_path19.join)(params.memoryRoot, "products", params.product), isSuite: false });
433
+ dirs.push({ path: (0, import_node_path20.join)(params.memoryRoot, "products", params.product), isSuite: false });
434
434
  }
435
435
  if (params.suiteId && validatePathComponent(params.suiteId)) {
436
- dirs.push({ path: (0, import_node_path19.join)(params.memoryRoot, "suites", params.suiteId), isSuite: true });
436
+ dirs.push({ path: (0, import_node_path20.join)(params.memoryRoot, "suites", params.suiteId), isSuite: true });
437
437
  }
438
438
  if (validatePathComponent(params.testId)) {
439
- dirs.push({ path: (0, import_node_path19.join)(params.memoryRoot, "tests", params.testId), isSuite: false });
439
+ dirs.push({ path: (0, import_node_path20.join)(params.memoryRoot, "tests", params.testId), isSuite: false });
440
440
  }
441
441
  if (dirs.length === 0) {
442
442
  console.warn("[memory-index] All path components failed validation", {
@@ -455,7 +455,7 @@ async function buildMemoryIndex(params) {
455
455
  for (const { path: dirPath, isSuite } of dirs) {
456
456
  const filenames = await listObservations(dirPath);
457
457
  for (const filename of filenames) {
458
- const filePath = (0, import_node_path19.join)(dirPath, filename);
458
+ const filePath = (0, import_node_path20.join)(dirPath, filename);
459
459
  let raw;
460
460
  try {
461
461
  raw = await (0, import_promises14.readFile)(filePath, "utf-8");
@@ -485,12 +485,12 @@ async function buildMemoryIndex(params) {
485
485
  return null;
486
486
  }
487
487
  }
488
- var import_promises14, import_node_path19;
488
+ var import_promises14, import_node_path20;
489
489
  var init_memory_index = __esm({
490
490
  "src/memory/memory-index.ts"() {
491
491
  "use strict";
492
492
  import_promises14 = require("fs/promises");
493
- import_node_path19 = require("path");
493
+ import_node_path20 = require("path");
494
494
  init_observation_io();
495
495
  init_security_scanner();
496
496
  init_sqlite();
@@ -508,12 +508,12 @@ var init_xml_escape = __esm({
508
508
  });
509
509
 
510
510
  // src/memory/lock-manager.ts
511
- var import_promises15, import_node_path20, LockManager;
511
+ var import_promises15, import_node_path21, LockManager;
512
512
  var init_lock_manager = __esm({
513
513
  "src/memory/lock-manager.ts"() {
514
514
  "use strict";
515
515
  import_promises15 = require("fs/promises");
516
- import_node_path20 = require("path");
516
+ import_node_path21 = require("path");
517
517
  LockManager = class {
518
518
  lockPath;
519
519
  timeout;
@@ -522,7 +522,7 @@ var init_lock_manager = __esm({
522
522
  this.timeout = timeout;
523
523
  }
524
524
  async acquire() {
525
- await (0, import_promises15.mkdir)((0, import_node_path20.dirname)(this.lockPath), { recursive: true });
525
+ await (0, import_promises15.mkdir)((0, import_node_path21.dirname)(this.lockPath), { recursive: true });
526
526
  const deadline = Date.now() + this.timeout;
527
527
  while (Date.now() < deadline) {
528
528
  try {
@@ -654,12 +654,12 @@ var local_provider_exports = {};
654
654
  __export(local_provider_exports, {
655
655
  LocalMemoryProvider: () => LocalMemoryProvider
656
656
  });
657
- var import_node_path21, import_node_fs7, import_promises16, LocalMemoryProvider;
657
+ var import_node_path22, import_node_fs8, import_promises16, LocalMemoryProvider;
658
658
  var init_local_provider = __esm({
659
659
  "src/memory/local-provider.ts"() {
660
660
  "use strict";
661
- import_node_path21 = require("path");
662
- import_node_fs7 = require("fs");
661
+ import_node_path22 = require("path");
662
+ import_node_fs8 = require("fs");
663
663
  import_promises16 = require("fs/promises");
664
664
  init_memory_index();
665
665
  init_xml_escape();
@@ -681,7 +681,7 @@ var init_local_provider = __esm({
681
681
  this.minTrust = opts?.minTrust ?? 0.3;
682
682
  this.maxInjections = opts?.maxInjections ?? 3;
683
683
  this.lockManager = new LockManager(
684
- (0, import_node_path21.join)(this.memoryRoot, ".curator.lock"),
684
+ (0, import_node_path22.join)(this.memoryRoot, ".curator.lock"),
685
685
  opts?.curatorLockTimeout ?? 12e4
686
686
  );
687
687
  }
@@ -751,9 +751,9 @@ ${lines.join("\n")}
751
751
  return writeObservation(this.memoryRoot, tier, scope, data);
752
752
  }
753
753
  async deleteObservation(tier, scope, id) {
754
- const filePath = (0, import_node_path21.join)(this.memoryRoot, tier, scope, `${id}.md`);
755
- const resolved = (0, import_node_path21.resolve)(filePath);
756
- if (!resolved.startsWith((0, import_node_path21.resolve)(this.memoryRoot) + import_node_path21.sep)) {
754
+ const filePath = (0, import_node_path22.join)(this.memoryRoot, tier, scope, `${id}.md`);
755
+ const resolved = (0, import_node_path22.resolve)(filePath);
756
+ if (!resolved.startsWith((0, import_node_path22.resolve)(this.memoryRoot) + import_node_path22.sep)) {
757
757
  throw new Error("Path escapes memory root");
758
758
  }
759
759
  try {
@@ -770,25 +770,25 @@ ${lines.join("\n")}
770
770
  const results = [];
771
771
  const tiers = ["products", "suites", "tests"];
772
772
  for (const tier of tiers) {
773
- const tierDir = (0, import_node_path21.join)(this.memoryRoot, tier);
773
+ const tierDir = (0, import_node_path22.join)(this.memoryRoot, tier);
774
774
  let scopes;
775
775
  try {
776
- scopes = (0, import_node_fs7.readdirSync)(tierDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
776
+ scopes = (0, import_node_fs8.readdirSync)(tierDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
777
777
  } catch {
778
778
  continue;
779
779
  }
780
780
  for (const scope of scopes) {
781
- const scopeDir = (0, import_node_path21.join)(tierDir, scope);
781
+ const scopeDir = (0, import_node_path22.join)(tierDir, scope);
782
782
  let files;
783
783
  try {
784
- files = (0, import_node_fs7.readdirSync)(scopeDir).filter((f) => f.startsWith("obs_") && f.endsWith(".md"));
784
+ files = (0, import_node_fs8.readdirSync)(scopeDir).filter((f) => f.startsWith("obs_") && f.endsWith(".md"));
785
785
  } catch {
786
786
  continue;
787
787
  }
788
788
  for (const file of files) {
789
789
  let raw;
790
790
  try {
791
- raw = (0, import_node_fs7.readFileSync)((0, import_node_path21.join)(scopeDir, file), "utf-8");
791
+ raw = (0, import_node_fs8.readFileSync)((0, import_node_path22.join)(scopeDir, file), "utf-8");
792
792
  } catch {
793
793
  continue;
794
794
  }
@@ -823,25 +823,25 @@ ${lines.join("\n")}
823
823
  const tiers = ["products", "suites", "tests"];
824
824
  const toInsert = [];
825
825
  for (const tier of tiers) {
826
- const tierDir = (0, import_node_path21.join)(this.memoryRoot, tier);
826
+ const tierDir = (0, import_node_path22.join)(this.memoryRoot, tier);
827
827
  let scopes;
828
828
  try {
829
- scopes = (0, import_node_fs7.readdirSync)(tierDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
829
+ scopes = (0, import_node_fs8.readdirSync)(tierDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
830
830
  } catch {
831
831
  continue;
832
832
  }
833
833
  for (const scope of scopes) {
834
- const scopeDir = (0, import_node_path21.join)(tierDir, scope);
834
+ const scopeDir = (0, import_node_path22.join)(tierDir, scope);
835
835
  let files;
836
836
  try {
837
- files = (0, import_node_fs7.readdirSync)(scopeDir).filter((f) => f.startsWith("obs_") && f.endsWith(".md"));
837
+ files = (0, import_node_fs8.readdirSync)(scopeDir).filter((f) => f.startsWith("obs_") && f.endsWith(".md"));
838
838
  } catch {
839
839
  continue;
840
840
  }
841
841
  for (const file of files) {
842
842
  let raw;
843
843
  try {
844
- raw = (0, import_node_fs7.readFileSync)((0, import_node_path21.join)(scopeDir, file), "utf-8");
844
+ raw = (0, import_node_fs8.readFileSync)((0, import_node_path22.join)(scopeDir, file), "utf-8");
845
845
  } catch {
846
846
  continue;
847
847
  }
@@ -1068,6 +1068,7 @@ __export(index_exports, {
1068
1068
  findSecretTemplates: () => findSecretTemplates,
1069
1069
  findUnresolvedTemplates: () => findUnresolvedTemplates,
1070
1070
  flushAnalytics: () => flushAnalytics,
1071
+ formatAppiumInstallGuidance: () => formatAppiumInstallGuidance,
1071
1072
  formatDiagnostics: () => formatDiagnostics,
1072
1073
  formatDiagnosticsPlain: () => formatDiagnosticsPlain,
1073
1074
  formatLogEntry: () => formatLogEntry,
@@ -1102,6 +1103,7 @@ __export(index_exports, {
1102
1103
  listObservations: () => listObservations,
1103
1104
  listProviders: () => listProviders,
1104
1105
  loadLLMAuthPlugins: () => loadLLMAuthPlugins,
1106
+ localAppiumBinaryNames: () => localAppiumBinaryNames,
1105
1107
  mapMobileCapabilities: () => mapMobileCapabilities,
1106
1108
  mapWebCapabilities: () => mapWebCapabilities,
1107
1109
  mergeRunAttributes: () => mergeRunAttributes,
@@ -1128,6 +1130,7 @@ __export(index_exports, {
1128
1130
  resolveAnalyticsAgentProduct: () => resolveAnalyticsAgentProduct,
1129
1131
  resolveAnalyticsIdentity: () => resolveAnalyticsIdentity,
1130
1132
  resolveAnalyticsStandardProperties: () => resolveAnalyticsStandardProperties,
1133
+ resolveAppiumExecutable: () => resolveAppiumExecutable,
1131
1134
  resolveBetterSqliteRebuildCommand: () => resolveBetterSqliteRebuildCommand,
1132
1135
  resolveFarmCredentials: () => resolveFarmCredentials,
1133
1136
  resolveFileReferences: () => resolveFileReferences,
@@ -4974,9 +4977,9 @@ function getHookCommand(runtime, entryFile) {
4974
4977
  }
4975
4978
  }
4976
4979
  async function checkDockerAvailable(dockerBin = DEFAULT_DOCKER_BIN) {
4977
- return new Promise((resolve7) => {
4980
+ return new Promise((resolve8) => {
4978
4981
  (0, import_node_child_process.execFile)(dockerBin, ["info", "--format", "{{.ID}}"], { timeout: 5e3 }, (err) => {
4979
- resolve7(!err);
4982
+ resolve8(!err);
4980
4983
  });
4981
4984
  });
4982
4985
  }
@@ -5089,22 +5092,22 @@ function filterSecretVariables(variables, secretStore) {
5089
5092
  );
5090
5093
  }
5091
5094
  function imageExists(dockerBin, image) {
5092
- return new Promise((resolve7) => {
5095
+ return new Promise((resolve8) => {
5093
5096
  (0, import_node_child_process.execFile)(dockerBin, ["image", "inspect", image], { timeout: 1e4 }, (err) => {
5094
- resolve7(!err);
5097
+ resolve8(!err);
5095
5098
  });
5096
5099
  });
5097
5100
  }
5098
5101
  function pullImage(dockerBin, image) {
5099
- return new Promise((resolve7, reject) => {
5102
+ return new Promise((resolve8, reject) => {
5100
5103
  (0, import_node_child_process.execFile)(dockerBin, ["pull", image], { timeout: 12e4 }, (err) => {
5101
5104
  if (err) reject(new Error(`Failed to pull image ${image}: ${err.message}`));
5102
- else resolve7();
5105
+ else resolve8();
5103
5106
  });
5104
5107
  });
5105
5108
  }
5106
5109
  function execDocker(dockerBin, args, timeout) {
5107
- return new Promise((resolve7, reject) => {
5110
+ return new Promise((resolve8, reject) => {
5108
5111
  let stdout = "";
5109
5112
  let stderr = "";
5110
5113
  let resolved = false;
@@ -5114,11 +5117,11 @@ function execDocker(dockerBin, args, timeout) {
5114
5117
  stdout = out ?? "";
5115
5118
  stderr = errOut ?? "";
5116
5119
  if (err && "killed" in err && err.killed) {
5117
- resolve7({ stdout, stderr: `Hook timed out after ${timeout}ms`, exitCode: 124 });
5120
+ resolve8({ stdout, stderr: `Hook timed out after ${timeout}ms`, exitCode: 124 });
5118
5121
  return;
5119
5122
  }
5120
5123
  const exitCode = err?.code ?? 0;
5121
- resolve7({ stdout, stderr, exitCode: typeof exitCode === "number" ? exitCode : 1 });
5124
+ resolve8({ stdout, stderr, exitCode: typeof exitCode === "number" ? exitCode : 1 });
5122
5125
  });
5123
5126
  });
5124
5127
  }
@@ -7194,14 +7197,41 @@ function resolveMobileRunConfig(input) {
7194
7197
  };
7195
7198
  }
7196
7199
 
7200
+ // src/mobile/appium-executable.ts
7201
+ var import_node_fs2 = require("fs");
7202
+ var import_node_path8 = require("path");
7203
+ function localAppiumBinaryNames(platform = process.platform) {
7204
+ return platform === "win32" ? ["appium.cmd", "appium.exe", "appium"] : ["appium"];
7205
+ }
7206
+ function resolveAppiumExecutable(opts = {}) {
7207
+ const exists = opts.exists ?? import_node_fs2.existsSync;
7208
+ const names = localAppiumBinaryNames(opts.platform ?? process.platform);
7209
+ let current = (0, import_node_path8.resolve)(opts.cwd ?? process.cwd());
7210
+ const root = (0, import_node_path8.parse)(current).root;
7211
+ while (true) {
7212
+ for (const name of names) {
7213
+ const candidate = (0, import_node_path8.join)(current, "node_modules", ".bin", name);
7214
+ if (exists(candidate)) {
7215
+ return { command: candidate, source: "local" };
7216
+ }
7217
+ }
7218
+ if (current === root) break;
7219
+ current = (0, import_node_path8.dirname)(current);
7220
+ }
7221
+ return { command: "appium", source: "path" };
7222
+ }
7223
+ function formatAppiumInstallGuidance() {
7224
+ return "Install Appium locally with `npm install -D appium` (or `pnpm add -D appium` / `yarn add -D appium`). If you prefer a machine-wide setup, use `npm install -g appium`.";
7225
+ }
7226
+
7197
7227
  // src/auth/store.ts
7198
7228
  var import_promises7 = require("fs/promises");
7199
- var import_node_path8 = require("path");
7229
+ var import_node_path9 = require("path");
7200
7230
  var import_node_os2 = require("os");
7201
7231
  function getAuthPath() {
7202
7232
  const xdg = process.env.XDG_DATA_HOME;
7203
- if (xdg) return (0, import_node_path8.join)(xdg, "agent-qa", "auth.json");
7204
- return (0, import_node_path8.join)((0, import_node_os2.homedir)(), ".agent-qa", "auth.json");
7233
+ if (xdg) return (0, import_node_path9.join)(xdg, "agent-qa", "auth.json");
7234
+ return (0, import_node_path9.join)((0, import_node_os2.homedir)(), ".agent-qa", "auth.json");
7205
7235
  }
7206
7236
  async function readAuth(path5) {
7207
7237
  const authPath = path5 ?? getAuthPath();
@@ -7216,7 +7246,7 @@ async function writeAuth(provider, credential, path5) {
7216
7246
  const authPath = path5 ?? getAuthPath();
7217
7247
  const store = await readAuth(authPath);
7218
7248
  store[provider] = credential;
7219
- await (0, import_promises7.mkdir)((0, import_node_path8.dirname)(authPath), { recursive: true });
7249
+ await (0, import_promises7.mkdir)((0, import_node_path9.dirname)(authPath), { recursive: true });
7220
7250
  await (0, import_promises7.writeFile)(authPath, JSON.stringify(store, null, 2), { mode: 384 });
7221
7251
  }
7222
7252
  async function removeAuth(provider, path5) {
@@ -7394,17 +7424,17 @@ async function resolveLLMAuth(configName, llmConfig, authPath) {
7394
7424
 
7395
7425
  // src/auth/plugin-loader.ts
7396
7426
  var import_node_module = require("module");
7397
- var import_node_fs2 = require("fs");
7398
- var import_node_path9 = require("path");
7427
+ var import_node_fs3 = require("fs");
7428
+ var import_node_path10 = require("path");
7399
7429
  var import_node_url = require("url");
7400
7430
  var import_meta = {};
7401
7431
  var loadedDeclarations = /* @__PURE__ */ new Set();
7402
7432
  function findPackageJson(packageName, baseDir) {
7403
- let current = (0, import_node_path9.resolve)(baseDir);
7433
+ let current = (0, import_node_path10.resolve)(baseDir);
7404
7434
  while (true) {
7405
- const candidate = (0, import_node_path9.resolve)(current, "node_modules", ...packageName.split("/"), "package.json");
7406
- if ((0, import_node_fs2.existsSync)(candidate)) return candidate;
7407
- const parent = (0, import_node_path9.dirname)(current);
7435
+ const candidate = (0, import_node_path10.resolve)(current, "node_modules", ...packageName.split("/"), "package.json");
7436
+ if ((0, import_node_fs3.existsSync)(candidate)) return candidate;
7437
+ const parent = (0, import_node_path10.dirname)(current);
7408
7438
  if (parent === current) return null;
7409
7439
  current = parent;
7410
7440
  }
@@ -7429,10 +7459,10 @@ function exportTargetFromPackageJson(pkg) {
7429
7459
  function resolvePackageImportFromBase(packageName, baseDir) {
7430
7460
  const packageJsonPath = findPackageJson(packageName, baseDir);
7431
7461
  if (!packageJsonPath) return null;
7432
- const pkg = JSON.parse((0, import_node_fs2.readFileSync)(packageJsonPath, "utf-8"));
7462
+ const pkg = JSON.parse((0, import_node_fs3.readFileSync)(packageJsonPath, "utf-8"));
7433
7463
  const target = exportTargetFromPackageJson(pkg);
7434
7464
  if (!target || !target.startsWith(".")) return null;
7435
- return (0, import_node_url.pathToFileURL)((0, import_node_path9.resolve)((0, import_node_path9.dirname)(packageJsonPath), target)).href;
7465
+ return (0, import_node_url.pathToFileURL)((0, import_node_path10.resolve)((0, import_node_path10.dirname)(packageJsonPath), target)).href;
7436
7466
  }
7437
7467
  function packageDeclarationValue(declaration) {
7438
7468
  return "package" in declaration && typeof declaration.package === "string" ? declaration.package.trim() : "";
@@ -7443,24 +7473,24 @@ function pathDeclarationValue(declaration) {
7443
7473
  function declarationKey(declaration, baseDir) {
7444
7474
  const packageName = packageDeclarationValue(declaration);
7445
7475
  if (packageName) return `package:${packageName}`;
7446
- return `path:${(0, import_node_path9.resolve)(baseDir, pathDeclarationValue(declaration))}`;
7476
+ return `path:${(0, import_node_path10.resolve)(baseDir, pathDeclarationValue(declaration))}`;
7447
7477
  }
7448
7478
  function resolveModuleSpecifier(declaration, baseDir) {
7449
7479
  const packageName = packageDeclarationValue(declaration);
7450
7480
  if (packageName) {
7451
7481
  const packageImport = resolvePackageImportFromBase(packageName, baseDir);
7452
7482
  if (packageImport) return packageImport;
7453
- const parentUrl = (0, import_node_url.pathToFileURL)((0, import_node_path9.resolve)(baseDir, "agent-qa.config.yaml")).href;
7483
+ const parentUrl = (0, import_node_url.pathToFileURL)((0, import_node_path10.resolve)(baseDir, "agent-qa.config.yaml")).href;
7454
7484
  try {
7455
7485
  const resolveImport = import_meta.resolve;
7456
7486
  return resolveImport(packageName, parentUrl);
7457
7487
  } catch {
7458
- const require3 = (0, import_node_module.createRequire)((0, import_node_path9.resolve)(baseDir, "agent-qa.config.yaml"));
7488
+ const require3 = (0, import_node_module.createRequire)((0, import_node_path10.resolve)(baseDir, "agent-qa.config.yaml"));
7459
7489
  return (0, import_node_url.pathToFileURL)(require3.resolve(packageName)).href;
7460
7490
  }
7461
7491
  }
7462
7492
  const pluginPath = pathDeclarationValue(declaration);
7463
- const absolutePath = (0, import_node_path9.isAbsolute)(pluginPath) ? pluginPath : (0, import_node_path9.resolve)(baseDir, pluginPath);
7493
+ const absolutePath = (0, import_node_path10.isAbsolute)(pluginPath) ? pluginPath : (0, import_node_path10.resolve)(baseDir, pluginPath);
7464
7494
  return (0, import_node_url.pathToFileURL)(absolutePath).href;
7465
7495
  }
7466
7496
  async function resolvePluginBundle(module2, source) {
@@ -7850,8 +7880,8 @@ function mapMobileCapabilities(match, platform, credentials, opts) {
7850
7880
  }
7851
7881
 
7852
7882
  // src/farm/browserstack.ts
7853
- var import_node_fs3 = require("fs");
7854
- var import_node_path10 = require("path");
7883
+ var import_node_fs4 = require("fs");
7884
+ var import_node_path11 = require("path");
7855
7885
  function isAbsoluteAppValue(value) {
7856
7886
  return value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
7857
7887
  }
@@ -7870,9 +7900,9 @@ async function resolveBrowserStackApp(config) {
7870
7900
  });
7871
7901
  }
7872
7902
  const appBaseDir = config.appBaseDir ?? process.cwd();
7873
- const resolvedPath = (0, import_node_path10.resolve)(appBaseDir, config.app);
7903
+ const resolvedPath = (0, import_node_path11.resolve)(appBaseDir, config.app);
7874
7904
  const mustExist = isExplicitRelativePath(config.app);
7875
- if (!(0, import_node_fs3.existsSync)(resolvedPath)) {
7905
+ if (!(0, import_node_fs4.existsSync)(resolvedPath)) {
7876
7906
  if (mustExist) {
7877
7907
  throw new MobileSetupError({
7878
7908
  category: "app-install",
@@ -7886,7 +7916,7 @@ async function resolveBrowserStackApp(config) {
7886
7916
  }
7887
7917
  const auth = Buffer.from(`${config.credentials.username}:${config.credentials.accessKey}`).toString("base64");
7888
7918
  const form = new FormData();
7889
- form.append("file", new Blob([(0, import_node_fs3.readFileSync)(resolvedPath)]), (0, import_node_path10.basename)(resolvedPath));
7919
+ form.append("file", new Blob([(0, import_node_fs4.readFileSync)(resolvedPath)]), (0, import_node_path11.basename)(resolvedPath));
7890
7920
  let res;
7891
7921
  try {
7892
7922
  res = await fetch("https://api-cloud.browserstack.com/app-automate/upload", {
@@ -8063,7 +8093,7 @@ async function runHooks(hooks, options = {}) {
8063
8093
  // src/memory/curator.ts
8064
8094
  var import_ai7 = require("ai");
8065
8095
  var import_zod19 = require("zod");
8066
- var import_node_path12 = require("path");
8096
+ var import_node_path13 = require("path");
8067
8097
  var import_promises9 = require("fs/promises");
8068
8098
  init_observation_io();
8069
8099
 
@@ -8212,7 +8242,7 @@ function collectKnownObservationIds(injectedObservationIds, dedupMatches) {
8212
8242
  async function findObservationFile(memoryRoot, id) {
8213
8243
  const tiers = ["products", "suites", "tests"];
8214
8244
  for (const tier of tiers) {
8215
- const tierDir = (0, import_node_path12.join)(memoryRoot, tier);
8245
+ const tierDir = (0, import_node_path13.join)(memoryRoot, tier);
8216
8246
  let scopes;
8217
8247
  try {
8218
8248
  scopes = await (0, import_promises9.readdir)(tierDir);
@@ -8220,7 +8250,7 @@ async function findObservationFile(memoryRoot, id) {
8220
8250
  continue;
8221
8251
  }
8222
8252
  for (const scope of scopes) {
8223
- const filePath = (0, import_node_path12.join)(tierDir, scope, `${id}.md`);
8253
+ const filePath = (0, import_node_path13.join)(tierDir, scope, `${id}.md`);
8224
8254
  try {
8225
8255
  await (0, import_promises9.access)(filePath);
8226
8256
  return { tier, scope, filePath };
@@ -8449,11 +8479,11 @@ ${userMessage}`;
8449
8479
  }
8450
8480
  if (ctx.suiteId && ctx.suiteContext) {
8451
8481
  try {
8452
- const suiteDir = (0, import_node_path12.join)(ctx.memoryRoot, "suites", ctx.suiteId);
8482
+ const suiteDir = (0, import_node_path13.join)(ctx.memoryRoot, "suites", ctx.suiteId);
8453
8483
  const files = await listObservations(suiteDir);
8454
8484
  for (const file of files) {
8455
8485
  try {
8456
- const filePath = (0, import_node_path12.join)(suiteDir, file);
8486
+ const filePath = (0, import_node_path13.join)(suiteDir, file);
8457
8487
  const raw = await (0, import_promises9.readFile)(filePath, "utf-8");
8458
8488
  const { data } = parseObservation(raw, file);
8459
8489
  if (!data || !("suite_snapshot" in data)) continue;
@@ -8917,6 +8947,7 @@ async function runSuite(suite, testEntries, config) {
8917
8947
  inlineHookSandboxOptions: config.sandboxOptions,
8918
8948
  secretStore: config.secretStore,
8919
8949
  secretRedactor: config.secretRedactor,
8950
+ accessibility: config.accessibility,
8920
8951
  logCapture: config.logCapture,
8921
8952
  screenshotSize: config.screenshotSize,
8922
8953
  effectiveResolution: config.effectiveResolution,
@@ -8984,7 +9015,8 @@ async function runSuite(suite, testEntries, config) {
8984
9015
  timeouts: config.timeouts,
8985
9016
  logger: config.logger,
8986
9017
  secretStore: config.secretStore,
8987
- secretRedactor: config.secretRedactor
9018
+ secretRedactor: config.secretRedactor,
9019
+ accessibility: config.accessibility
8988
9020
  }, filePath);
8989
9021
  if (config.circuitBreaker) {
8990
9022
  config.circuitBreaker.record({ withMemory: false, passed: ablationResult.status === "passed" });
@@ -9155,14 +9187,14 @@ async function runSuite(suite, testEntries, config) {
9155
9187
 
9156
9188
  // src/validation/validate.ts
9157
9189
  var import_promises12 = require("fs/promises");
9158
- var import_node_fs5 = require("fs");
9159
- var import_node_path16 = __toESM(require("path"), 1);
9190
+ var import_node_fs6 = require("fs");
9191
+ var import_node_path17 = __toESM(require("path"), 1);
9160
9192
  var import_yaml5 = require("yaml");
9161
9193
  var import_glob2 = require("glob");
9162
9194
 
9163
9195
  // src/hooks/parser.ts
9164
9196
  var import_promises11 = require("fs/promises");
9165
- var import_node_path13 = require("path");
9197
+ var import_node_path14 = require("path");
9166
9198
  var import_yaml4 = require("yaml");
9167
9199
  async function parseHooksFile(filePath) {
9168
9200
  let raw;
@@ -9184,31 +9216,31 @@ async function parseHooksFile(filePath) {
9184
9216
  );
9185
9217
  return { hooks: [], errors };
9186
9218
  }
9187
- const dir = (0, import_node_path13.dirname)(filePath);
9219
+ const dir = (0, import_node_path14.dirname)(filePath);
9188
9220
  const hooks = result.data.hooks.map((h) => ({
9189
9221
  ...h,
9190
- file: (0, import_node_path13.resolve)(dir, h.file),
9191
- deps: h.deps.map((d) => (0, import_node_path13.resolve)(dir, d)),
9192
- packageFile: h.packageFile ? (0, import_node_path13.resolve)(dir, h.packageFile) : void 0
9222
+ file: (0, import_node_path14.resolve)(dir, h.file),
9223
+ deps: h.deps.map((d) => (0, import_node_path14.resolve)(dir, d)),
9224
+ packageFile: h.packageFile ? (0, import_node_path14.resolve)(dir, h.packageFile) : void 0
9193
9225
  }));
9194
9226
  return { hooks, errors: [] };
9195
9227
  }
9196
9228
 
9197
9229
  // src/utils/path-validation.ts
9198
- var import_node_path14 = __toESM(require("path"), 1);
9230
+ var import_node_path15 = __toESM(require("path"), 1);
9199
9231
  function isPathInsideDir(childPath, parentDir) {
9200
9232
  if (!childPath) return false;
9201
- const resolved = import_node_path14.default.resolve(parentDir, childPath);
9202
- const relative2 = import_node_path14.default.relative(parentDir, resolved);
9203
- return !!relative2 && !relative2.startsWith("..") && !import_node_path14.default.isAbsolute(relative2);
9233
+ const resolved = import_node_path15.default.resolve(parentDir, childPath);
9234
+ const relative2 = import_node_path15.default.relative(parentDir, resolved);
9235
+ return !!relative2 && !relative2.startsWith("..") && !import_node_path15.default.isAbsolute(relative2);
9204
9236
  }
9205
9237
 
9206
9238
  // src/workspace/workspace-paths.ts
9207
- var import_node_fs4 = require("fs");
9208
- var import_node_path15 = __toESM(require("path"), 1);
9239
+ var import_node_fs5 = require("fs");
9240
+ var import_node_path16 = __toESM(require("path"), 1);
9209
9241
  var import_glob = require("glob");
9210
9242
  function toForwardSlash(value) {
9211
- return value.split(import_node_path15.default.sep).join("/");
9243
+ return value.split(import_node_path16.default.sep).join("/");
9212
9244
  }
9213
9245
  function trimLeadingCurrentDir(value) {
9214
9246
  return toForwardSlash(value).replace(/^\.\//, "");
@@ -9227,16 +9259,16 @@ function assertNonEmptyString(value, key) {
9227
9259
  }
9228
9260
  function resolveWorkspaceScalar(configDir, key, configuredPath) {
9229
9261
  const value = assertNonEmptyString(configuredPath, key);
9230
- const absolutePath = import_node_path15.default.isAbsolute(value) ? import_node_path15.default.resolve(value) : import_node_path15.default.resolve(configDir, value);
9262
+ const absolutePath = import_node_path16.default.isAbsolute(value) ? import_node_path16.default.resolve(value) : import_node_path16.default.resolve(configDir, value);
9231
9263
  return {
9232
9264
  configuredPath: value,
9233
9265
  absolutePath,
9234
- workspaceRelativePath: trimLeadingCurrentDir(import_node_path15.default.relative(configDir, absolutePath))
9266
+ workspaceRelativePath: trimLeadingCurrentDir(import_node_path16.default.relative(configDir, absolutePath))
9235
9267
  };
9236
9268
  }
9237
9269
  function assertInsideWorkspace(configDir, absolutePath, filePath) {
9238
- const relativePath = import_node_path15.default.relative(configDir, absolutePath);
9239
- if (!relativePath || relativePath.startsWith("..") || import_node_path15.default.isAbsolute(relativePath)) {
9270
+ const relativePath = import_node_path16.default.relative(configDir, absolutePath);
9271
+ if (!relativePath || relativePath.startsWith("..") || import_node_path16.default.isAbsolute(relativePath)) {
9240
9272
  throw new Error(`Workspace file path escapes config directory: ${filePath}`);
9241
9273
  }
9242
9274
  return trimLeadingCurrentDir(relativePath);
@@ -9293,8 +9325,8 @@ function isIgnored(workspace, workspaceRelativePath) {
9293
9325
  return workspace.testPathIgnore.length > 0 && matchesAnyPattern(workspaceRelativePath, workspace.testPathIgnore);
9294
9326
  }
9295
9327
  function resolveWorkspacePaths(input) {
9296
- const configPath = import_node_path15.default.resolve(input.configPath);
9297
- const configDir = import_node_path15.default.dirname(configPath);
9328
+ const configPath = import_node_path16.default.resolve(input.configPath);
9329
+ const configDir = import_node_path16.default.dirname(configPath);
9298
9330
  const workspace = input.config.workspace;
9299
9331
  if (!workspace) {
9300
9332
  throw new Error("Missing required workspace config key: workspace");
@@ -9317,7 +9349,7 @@ function resolveWorkspacePaths(input) {
9317
9349
  ["workspace.envFile", resolved.envFile],
9318
9350
  ["workspace.secretsFile", resolved.secretsFile]
9319
9351
  ]) {
9320
- if (!(0, import_node_fs4.existsSync)(file.absolutePath)) {
9352
+ if (!(0, import_node_fs5.existsSync)(file.absolutePath)) {
9321
9353
  throw new Error(`Configured workspace file not found: ${key} -> ${file.absolutePath}`);
9322
9354
  }
9323
9355
  }
@@ -9332,14 +9364,14 @@ async function discoverWorkspaceFiles(input) {
9332
9364
  absolute: true,
9333
9365
  nodir: true
9334
9366
  });
9335
- return matches.map((match) => import_node_path15.default.resolve(match)).map((absolutePath) => ({
9367
+ return matches.map((match) => import_node_path16.default.resolve(match)).map((absolutePath) => ({
9336
9368
  kind: input.kind,
9337
9369
  absolutePath,
9338
- workspaceRelativePath: trimLeadingCurrentDir(import_node_path15.default.relative(input.workspace.configDir, absolutePath))
9370
+ workspaceRelativePath: trimLeadingCurrentDir(import_node_path16.default.relative(input.workspace.configDir, absolutePath))
9339
9371
  })).sort((a, b) => a.workspaceRelativePath.localeCompare(b.workspaceRelativePath));
9340
9372
  }
9341
9373
  async function resolveWorkspaceFileTarget(input) {
9342
- const absolutePath = import_node_path15.default.isAbsolute(input.filePath) ? import_node_path15.default.resolve(input.filePath) : import_node_path15.default.resolve(input.workspace.configDir, input.filePath);
9374
+ const absolutePath = import_node_path16.default.isAbsolute(input.filePath) ? import_node_path16.default.resolve(input.filePath) : import_node_path16.default.resolve(input.workspace.configDir, input.filePath);
9343
9375
  const workspaceRelativePath = assertInsideWorkspace(input.workspace.configDir, absolutePath, input.filePath);
9344
9376
  if (!isWorkspacePathMatch({
9345
9377
  workspace: input.workspace,
@@ -9348,7 +9380,7 @@ async function resolveWorkspaceFileTarget(input) {
9348
9380
  })) {
9349
9381
  throw new Error(`Workspace ${input.kind} file is not matched by configured workspace patterns: ${workspaceRelativePath}`);
9350
9382
  }
9351
- if (input.requireExisting && !(0, import_node_fs4.existsSync)(absolutePath)) {
9383
+ if (input.requireExisting && !(0, import_node_fs5.existsSync)(absolutePath)) {
9352
9384
  throw new Error(`Workspace ${input.kind} file not found: ${workspaceRelativePath}`);
9353
9385
  }
9354
9386
  return {
@@ -9469,8 +9501,8 @@ function validateSuiteFile(filePath, content, basedir) {
9469
9501
  });
9470
9502
  continue;
9471
9503
  }
9472
- const resolved = import_node_path16.default.resolve(basedir, testPath);
9473
- if (!(0, import_node_fs5.existsSync)(resolved)) {
9504
+ const resolved = import_node_path17.default.resolve(basedir, testPath);
9505
+ if (!(0, import_node_fs6.existsSync)(resolved)) {
9474
9506
  let line = 1;
9475
9507
  let column = 1;
9476
9508
  const seqNode = testsNode;
@@ -9491,7 +9523,7 @@ function validateSuiteFile(filePath, content, basedir) {
9491
9523
  return diagnostics;
9492
9524
  }
9493
9525
  function validateFilename(filePath) {
9494
- const basename3 = import_node_path16.default.basename(filePath);
9526
+ const basename3 = import_node_path17.default.basename(filePath);
9495
9527
  if (!VALID_FILENAME_RE.test(basename3)) {
9496
9528
  return [{
9497
9529
  file: filePath,
@@ -9505,7 +9537,7 @@ function validateFilename(filePath) {
9505
9537
  }
9506
9538
  async function validateHooksFile(hooksFilePath, options = {}) {
9507
9539
  const diagnostics = [];
9508
- if (!(0, import_node_fs5.existsSync)(hooksFilePath)) {
9540
+ if (!(0, import_node_fs6.existsSync)(hooksFilePath)) {
9509
9541
  if (options.required) {
9510
9542
  return [buildDiagnostic(hooksFilePath, `Configured hooks file not found: ${hooksFilePath}`)];
9511
9543
  }
@@ -9525,7 +9557,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
9525
9557
  return diagnostics;
9526
9558
  }
9527
9559
  for (const hook of hooks) {
9528
- if (!(0, import_node_fs5.existsSync)(hook.file)) {
9560
+ if (!(0, import_node_fs6.existsSync)(hook.file)) {
9529
9561
  diagnostics.push({
9530
9562
  file: hooksFilePath,
9531
9563
  line: 1,
@@ -9535,7 +9567,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
9535
9567
  });
9536
9568
  }
9537
9569
  for (const dep of hook.deps) {
9538
- if (!(0, import_node_fs5.existsSync)(dep)) {
9570
+ if (!(0, import_node_fs6.existsSync)(dep)) {
9539
9571
  diagnostics.push({
9540
9572
  file: hooksFilePath,
9541
9573
  line: 1,
@@ -9545,7 +9577,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
9545
9577
  });
9546
9578
  }
9547
9579
  }
9548
- if (hook.packageFile && !(0, import_node_fs5.existsSync)(hook.packageFile)) {
9580
+ if (hook.packageFile && !(0, import_node_fs6.existsSync)(hook.packageFile)) {
9549
9581
  diagnostics.push({
9550
9582
  file: hooksFilePath,
9551
9583
  line: 1,
@@ -9660,7 +9692,7 @@ function validateRequiredWorkspaceFiles(workspace) {
9660
9692
  ["workspace.envFile", workspace.envFile],
9661
9693
  ["workspace.secretsFile", workspace.secretsFile]
9662
9694
  ]) {
9663
- if (!(0, import_node_fs5.existsSync)(file.absolutePath)) {
9695
+ if (!(0, import_node_fs6.existsSync)(file.absolutePath)) {
9664
9696
  diagnostics.push(buildDiagnostic(file.absolutePath, `Configured workspace file not found: ${key} -> ${file.absolutePath}`));
9665
9697
  }
9666
9698
  }
@@ -9678,7 +9710,7 @@ async function validateProject(options = {}) {
9678
9710
  }
9679
9711
  filePaths = resolvedFiles;
9680
9712
  } else if (options.files && options.files.length > 0) {
9681
- filePaths = options.files.map((f) => import_node_path16.default.resolve(f));
9713
+ filePaths = options.files.map((f) => import_node_path17.default.resolve(f));
9682
9714
  } else if (options.workspace) {
9683
9715
  const [testFiles, suiteFiles] = await Promise.all([
9684
9716
  discoverWorkspaceFiles({ workspace: options.workspace, kind: "test" }),
@@ -9721,11 +9753,11 @@ async function validateProject(options = {}) {
9721
9753
  if (d.severity === "error") result.errorCount++;
9722
9754
  else result.warningCount++;
9723
9755
  }
9724
- if (hooksFilePath && hooksDiags.length === 0 && (0, import_node_fs5.existsSync)(hooksFilePath)) {
9756
+ if (hooksFilePath && hooksDiags.length === 0 && (0, import_node_fs6.existsSync)(hooksFilePath)) {
9725
9757
  const { hooks } = await parseHooksFile(hooksFilePath);
9726
9758
  const hookIds = new Set(hooks.map((h) => h.id));
9727
9759
  for (const filePath of filePaths) {
9728
- const workspaceRelativePath = options.workspace ? import_node_path16.default.relative(options.workspace.configDir, filePath).split(import_node_path16.default.sep).join("/") : filePath;
9760
+ const workspaceRelativePath = options.workspace ? import_node_path17.default.relative(options.workspace.configDir, filePath).split(import_node_path17.default.sep).join("/") : filePath;
9729
9761
  const isWorkspaceFile = options.workspace ? isWorkspacePathMatch({ workspace: options.workspace, kind: isSuiteFile(filePath) ? "suite" : "test", workspaceRelativePath }) : true;
9730
9762
  if (!isWorkspaceFile) continue;
9731
9763
  try {
@@ -9827,7 +9859,7 @@ function warnIfOutOfBounds(coords, viewport, actionType) {
9827
9859
 
9828
9860
  // src/hooks/migrate-hook-refs.ts
9829
9861
  var import_promises13 = require("fs/promises");
9830
- var import_node_path17 = require("path");
9862
+ var import_node_path18 = require("path");
9831
9863
  var import_yaml6 = require("yaml");
9832
9864
  var import_zod20 = require("zod");
9833
9865
  var import_agent_qa_ids8 = require("@vostride/agent-qa-ids");
@@ -9933,7 +9965,7 @@ function rewriteSteps(owner, filePath, hookNameToId, hookIds, errors) {
9933
9965
  return changed;
9934
9966
  }
9935
9967
  async function resolveWorkspaceMigrationContext(workspaceDir, configPath) {
9936
- const resolvedConfigPath = configPath ? (0, import_node_path17.resolve)(configPath) : (0, import_node_path17.resolve)(workspaceDir, "agent-qa.config.yaml");
9968
+ const resolvedConfigPath = configPath ? (0, import_node_path18.resolve)(configPath) : (0, import_node_path18.resolve)(workspaceDir, "agent-qa.config.yaml");
9937
9969
  const rawConfig = await (0, import_promises13.readFile)(resolvedConfigPath, "utf-8");
9938
9970
  const parsedConfig = (0, import_yaml6.parse)(rawConfig);
9939
9971
  const configResult = AgentQaConfigSchema.safeParse(parsedConfig);
@@ -9960,7 +9992,7 @@ async function resolveWorkspaceMigrationContext(workspaceDir, configPath) {
9960
9992
  };
9961
9993
  }
9962
9994
  async function auditWorkspaceHookReferenceMigration(options) {
9963
- const workspaceDir = (0, import_node_path17.resolve)(options.workspaceDir);
9995
+ const workspaceDir = (0, import_node_path18.resolve)(options.workspaceDir);
9964
9996
  const errors = [];
9965
9997
  let context;
9966
9998
  try {
@@ -10023,12 +10055,12 @@ async function auditWorkspaceHookReferenceMigration(options) {
10023
10055
  const hookIds = new Set(Object.values(hookNameToId));
10024
10056
  if (hooksChanged) {
10025
10057
  rewriteFiles.push({
10026
- path: (0, import_node_path17.relative)(context.configDir, context.hooksFilePath),
10058
+ path: (0, import_node_path18.relative)(context.configDir, context.hooksFilePath),
10027
10059
  content: (0, import_yaml6.stringify)({ hooks: normalizedHooks })
10028
10060
  });
10029
10061
  }
10030
10062
  for (const relativePath of context.targetFiles) {
10031
- const absolutePath = (0, import_node_path17.resolve)(context.configDir, relativePath);
10063
+ const absolutePath = (0, import_node_path18.resolve)(context.configDir, relativePath);
10032
10064
  let parsedFile;
10033
10065
  try {
10034
10066
  parsedFile = (0, import_yaml6.parse)(await (0, import_promises13.readFile)(absolutePath, "utf-8"));
@@ -10069,14 +10101,14 @@ async function applyWorkspaceHookReferenceMigration(options) {
10069
10101
  if (!audit.ok) {
10070
10102
  throw new HookReferenceMigrationError(audit.errors);
10071
10103
  }
10072
- const workspaceDir = options.configPath ? (0, import_node_path17.dirname)((0, import_node_path17.resolve)(options.configPath)) : (0, import_node_path17.resolve)(options.workspaceDir);
10104
+ const workspaceDir = options.configPath ? (0, import_node_path18.dirname)((0, import_node_path18.resolve)(options.configPath)) : (0, import_node_path18.resolve)(options.workspaceDir);
10073
10105
  for (const rewrite of audit.rewriteFiles) {
10074
- const outputPath = (0, import_node_path17.resolve)(workspaceDir, rewrite.path);
10075
- await (0, import_promises13.mkdir)((0, import_node_path17.dirname)(outputPath), { recursive: true });
10106
+ const outputPath = (0, import_node_path18.resolve)(workspaceDir, rewrite.path);
10107
+ await (0, import_promises13.mkdir)((0, import_node_path18.dirname)(outputPath), { recursive: true });
10076
10108
  await (0, import_promises13.writeFile)(outputPath, rewrite.content, "utf-8");
10077
10109
  }
10078
10110
  for (const cleanupTarget of audit.cleanupTargets) {
10079
- await (0, import_promises13.rm)((0, import_node_path17.resolve)(workspaceDir, cleanupTarget), { recursive: true, force: true });
10111
+ await (0, import_promises13.rm)((0, import_node_path18.resolve)(workspaceDir, cleanupTarget), { recursive: true, force: true });
10080
10112
  }
10081
10113
  return audit;
10082
10114
  }
@@ -10147,12 +10179,12 @@ var RUN_ARTIFACT_SCHEMA_VERSION = 1;
10147
10179
  // src/analytics/identity.ts
10148
10180
  var import_promises17 = require("fs/promises");
10149
10181
  var import_node_os4 = require("os");
10150
- var import_node_path22 = require("path");
10182
+ var import_node_path23 = require("path");
10151
10183
  var import_id_agent = require("id-agent");
10152
10184
  var LOCAL_ANALYTICS_ID_PATTERN = /^u_([a-z]+-){9}[a-z]+$/;
10153
10185
  function getAnalyticsIdentityPath(env = process.env, homeDir = (0, import_node_os4.homedir)()) {
10154
- if (env.XDG_DATA_HOME) return (0, import_node_path22.join)(env.XDG_DATA_HOME, "agent-qa", "analytics.json");
10155
- return (0, import_node_path22.join)(homeDir, ".agent-qa", "analytics.json");
10186
+ if (env.XDG_DATA_HOME) return (0, import_node_path23.join)(env.XDG_DATA_HOME, "agent-qa", "analytics.json");
10187
+ return (0, import_node_path23.join)(homeDir, ".agent-qa", "analytics.json");
10156
10188
  }
10157
10189
  function generateAnalyticsUserId() {
10158
10190
  return (0, import_id_agent.idAgent)({ prefix: "u", words: 10 });
@@ -10184,7 +10216,7 @@ async function readAnalyticsIdentity(path5) {
10184
10216
  }
10185
10217
  async function writeAnalyticsIdentity(distinctId, path5, isInternal = false) {
10186
10218
  const identityPath = path5 ?? getAnalyticsIdentityPath();
10187
- await (0, import_promises17.mkdir)((0, import_node_path22.dirname)(identityPath), { recursive: true });
10219
+ await (0, import_promises17.mkdir)((0, import_node_path23.dirname)(identityPath), { recursive: true });
10188
10220
  await (0, import_promises17.writeFile)(identityPath, JSON.stringify({
10189
10221
  distinctId,
10190
10222
  is_internal: isInternal
@@ -11295,6 +11327,7 @@ function isMacPlatform() {
11295
11327
  findSecretTemplates,
11296
11328
  findUnresolvedTemplates,
11297
11329
  flushAnalytics,
11330
+ formatAppiumInstallGuidance,
11298
11331
  formatDiagnostics,
11299
11332
  formatDiagnosticsPlain,
11300
11333
  formatLogEntry,
@@ -11329,6 +11362,7 @@ function isMacPlatform() {
11329
11362
  listObservations,
11330
11363
  listProviders,
11331
11364
  loadLLMAuthPlugins,
11365
+ localAppiumBinaryNames,
11332
11366
  mapMobileCapabilities,
11333
11367
  mapWebCapabilities,
11334
11368
  mergeRunAttributes,
@@ -11355,6 +11389,7 @@ function isMacPlatform() {
11355
11389
  resolveAnalyticsAgentProduct,
11356
11390
  resolveAnalyticsIdentity,
11357
11391
  resolveAnalyticsStandardProperties,
11392
+ resolveAppiumExecutable,
11358
11393
  resolveBetterSqliteRebuildCommand,
11359
11394
  resolveFarmCredentials,
11360
11395
  resolveFileReferences,