@vostride/agent-qa-core 0.1.9 → 0.1.10

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;
@@ -9155,14 +9185,14 @@ async function runSuite(suite, testEntries, config) {
9155
9185
 
9156
9186
  // src/validation/validate.ts
9157
9187
  var import_promises12 = require("fs/promises");
9158
- var import_node_fs5 = require("fs");
9159
- var import_node_path16 = __toESM(require("path"), 1);
9188
+ var import_node_fs6 = require("fs");
9189
+ var import_node_path17 = __toESM(require("path"), 1);
9160
9190
  var import_yaml5 = require("yaml");
9161
9191
  var import_glob2 = require("glob");
9162
9192
 
9163
9193
  // src/hooks/parser.ts
9164
9194
  var import_promises11 = require("fs/promises");
9165
- var import_node_path13 = require("path");
9195
+ var import_node_path14 = require("path");
9166
9196
  var import_yaml4 = require("yaml");
9167
9197
  async function parseHooksFile(filePath) {
9168
9198
  let raw;
@@ -9184,31 +9214,31 @@ async function parseHooksFile(filePath) {
9184
9214
  );
9185
9215
  return { hooks: [], errors };
9186
9216
  }
9187
- const dir = (0, import_node_path13.dirname)(filePath);
9217
+ const dir = (0, import_node_path14.dirname)(filePath);
9188
9218
  const hooks = result.data.hooks.map((h) => ({
9189
9219
  ...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
9220
+ file: (0, import_node_path14.resolve)(dir, h.file),
9221
+ deps: h.deps.map((d) => (0, import_node_path14.resolve)(dir, d)),
9222
+ packageFile: h.packageFile ? (0, import_node_path14.resolve)(dir, h.packageFile) : void 0
9193
9223
  }));
9194
9224
  return { hooks, errors: [] };
9195
9225
  }
9196
9226
 
9197
9227
  // src/utils/path-validation.ts
9198
- var import_node_path14 = __toESM(require("path"), 1);
9228
+ var import_node_path15 = __toESM(require("path"), 1);
9199
9229
  function isPathInsideDir(childPath, parentDir) {
9200
9230
  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);
9231
+ const resolved = import_node_path15.default.resolve(parentDir, childPath);
9232
+ const relative2 = import_node_path15.default.relative(parentDir, resolved);
9233
+ return !!relative2 && !relative2.startsWith("..") && !import_node_path15.default.isAbsolute(relative2);
9204
9234
  }
9205
9235
 
9206
9236
  // src/workspace/workspace-paths.ts
9207
- var import_node_fs4 = require("fs");
9208
- var import_node_path15 = __toESM(require("path"), 1);
9237
+ var import_node_fs5 = require("fs");
9238
+ var import_node_path16 = __toESM(require("path"), 1);
9209
9239
  var import_glob = require("glob");
9210
9240
  function toForwardSlash(value) {
9211
- return value.split(import_node_path15.default.sep).join("/");
9241
+ return value.split(import_node_path16.default.sep).join("/");
9212
9242
  }
9213
9243
  function trimLeadingCurrentDir(value) {
9214
9244
  return toForwardSlash(value).replace(/^\.\//, "");
@@ -9227,16 +9257,16 @@ function assertNonEmptyString(value, key) {
9227
9257
  }
9228
9258
  function resolveWorkspaceScalar(configDir, key, configuredPath) {
9229
9259
  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);
9260
+ const absolutePath = import_node_path16.default.isAbsolute(value) ? import_node_path16.default.resolve(value) : import_node_path16.default.resolve(configDir, value);
9231
9261
  return {
9232
9262
  configuredPath: value,
9233
9263
  absolutePath,
9234
- workspaceRelativePath: trimLeadingCurrentDir(import_node_path15.default.relative(configDir, absolutePath))
9264
+ workspaceRelativePath: trimLeadingCurrentDir(import_node_path16.default.relative(configDir, absolutePath))
9235
9265
  };
9236
9266
  }
9237
9267
  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)) {
9268
+ const relativePath = import_node_path16.default.relative(configDir, absolutePath);
9269
+ if (!relativePath || relativePath.startsWith("..") || import_node_path16.default.isAbsolute(relativePath)) {
9240
9270
  throw new Error(`Workspace file path escapes config directory: ${filePath}`);
9241
9271
  }
9242
9272
  return trimLeadingCurrentDir(relativePath);
@@ -9293,8 +9323,8 @@ function isIgnored(workspace, workspaceRelativePath) {
9293
9323
  return workspace.testPathIgnore.length > 0 && matchesAnyPattern(workspaceRelativePath, workspace.testPathIgnore);
9294
9324
  }
9295
9325
  function resolveWorkspacePaths(input) {
9296
- const configPath = import_node_path15.default.resolve(input.configPath);
9297
- const configDir = import_node_path15.default.dirname(configPath);
9326
+ const configPath = import_node_path16.default.resolve(input.configPath);
9327
+ const configDir = import_node_path16.default.dirname(configPath);
9298
9328
  const workspace = input.config.workspace;
9299
9329
  if (!workspace) {
9300
9330
  throw new Error("Missing required workspace config key: workspace");
@@ -9317,7 +9347,7 @@ function resolveWorkspacePaths(input) {
9317
9347
  ["workspace.envFile", resolved.envFile],
9318
9348
  ["workspace.secretsFile", resolved.secretsFile]
9319
9349
  ]) {
9320
- if (!(0, import_node_fs4.existsSync)(file.absolutePath)) {
9350
+ if (!(0, import_node_fs5.existsSync)(file.absolutePath)) {
9321
9351
  throw new Error(`Configured workspace file not found: ${key} -> ${file.absolutePath}`);
9322
9352
  }
9323
9353
  }
@@ -9332,14 +9362,14 @@ async function discoverWorkspaceFiles(input) {
9332
9362
  absolute: true,
9333
9363
  nodir: true
9334
9364
  });
9335
- return matches.map((match) => import_node_path15.default.resolve(match)).map((absolutePath) => ({
9365
+ return matches.map((match) => import_node_path16.default.resolve(match)).map((absolutePath) => ({
9336
9366
  kind: input.kind,
9337
9367
  absolutePath,
9338
- workspaceRelativePath: trimLeadingCurrentDir(import_node_path15.default.relative(input.workspace.configDir, absolutePath))
9368
+ workspaceRelativePath: trimLeadingCurrentDir(import_node_path16.default.relative(input.workspace.configDir, absolutePath))
9339
9369
  })).sort((a, b) => a.workspaceRelativePath.localeCompare(b.workspaceRelativePath));
9340
9370
  }
9341
9371
  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);
9372
+ 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
9373
  const workspaceRelativePath = assertInsideWorkspace(input.workspace.configDir, absolutePath, input.filePath);
9344
9374
  if (!isWorkspacePathMatch({
9345
9375
  workspace: input.workspace,
@@ -9348,7 +9378,7 @@ async function resolveWorkspaceFileTarget(input) {
9348
9378
  })) {
9349
9379
  throw new Error(`Workspace ${input.kind} file is not matched by configured workspace patterns: ${workspaceRelativePath}`);
9350
9380
  }
9351
- if (input.requireExisting && !(0, import_node_fs4.existsSync)(absolutePath)) {
9381
+ if (input.requireExisting && !(0, import_node_fs5.existsSync)(absolutePath)) {
9352
9382
  throw new Error(`Workspace ${input.kind} file not found: ${workspaceRelativePath}`);
9353
9383
  }
9354
9384
  return {
@@ -9469,8 +9499,8 @@ function validateSuiteFile(filePath, content, basedir) {
9469
9499
  });
9470
9500
  continue;
9471
9501
  }
9472
- const resolved = import_node_path16.default.resolve(basedir, testPath);
9473
- if (!(0, import_node_fs5.existsSync)(resolved)) {
9502
+ const resolved = import_node_path17.default.resolve(basedir, testPath);
9503
+ if (!(0, import_node_fs6.existsSync)(resolved)) {
9474
9504
  let line = 1;
9475
9505
  let column = 1;
9476
9506
  const seqNode = testsNode;
@@ -9491,7 +9521,7 @@ function validateSuiteFile(filePath, content, basedir) {
9491
9521
  return diagnostics;
9492
9522
  }
9493
9523
  function validateFilename(filePath) {
9494
- const basename3 = import_node_path16.default.basename(filePath);
9524
+ const basename3 = import_node_path17.default.basename(filePath);
9495
9525
  if (!VALID_FILENAME_RE.test(basename3)) {
9496
9526
  return [{
9497
9527
  file: filePath,
@@ -9505,7 +9535,7 @@ function validateFilename(filePath) {
9505
9535
  }
9506
9536
  async function validateHooksFile(hooksFilePath, options = {}) {
9507
9537
  const diagnostics = [];
9508
- if (!(0, import_node_fs5.existsSync)(hooksFilePath)) {
9538
+ if (!(0, import_node_fs6.existsSync)(hooksFilePath)) {
9509
9539
  if (options.required) {
9510
9540
  return [buildDiagnostic(hooksFilePath, `Configured hooks file not found: ${hooksFilePath}`)];
9511
9541
  }
@@ -9525,7 +9555,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
9525
9555
  return diagnostics;
9526
9556
  }
9527
9557
  for (const hook of hooks) {
9528
- if (!(0, import_node_fs5.existsSync)(hook.file)) {
9558
+ if (!(0, import_node_fs6.existsSync)(hook.file)) {
9529
9559
  diagnostics.push({
9530
9560
  file: hooksFilePath,
9531
9561
  line: 1,
@@ -9535,7 +9565,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
9535
9565
  });
9536
9566
  }
9537
9567
  for (const dep of hook.deps) {
9538
- if (!(0, import_node_fs5.existsSync)(dep)) {
9568
+ if (!(0, import_node_fs6.existsSync)(dep)) {
9539
9569
  diagnostics.push({
9540
9570
  file: hooksFilePath,
9541
9571
  line: 1,
@@ -9545,7 +9575,7 @@ async function validateHooksFile(hooksFilePath, options = {}) {
9545
9575
  });
9546
9576
  }
9547
9577
  }
9548
- if (hook.packageFile && !(0, import_node_fs5.existsSync)(hook.packageFile)) {
9578
+ if (hook.packageFile && !(0, import_node_fs6.existsSync)(hook.packageFile)) {
9549
9579
  diagnostics.push({
9550
9580
  file: hooksFilePath,
9551
9581
  line: 1,
@@ -9660,7 +9690,7 @@ function validateRequiredWorkspaceFiles(workspace) {
9660
9690
  ["workspace.envFile", workspace.envFile],
9661
9691
  ["workspace.secretsFile", workspace.secretsFile]
9662
9692
  ]) {
9663
- if (!(0, import_node_fs5.existsSync)(file.absolutePath)) {
9693
+ if (!(0, import_node_fs6.existsSync)(file.absolutePath)) {
9664
9694
  diagnostics.push(buildDiagnostic(file.absolutePath, `Configured workspace file not found: ${key} -> ${file.absolutePath}`));
9665
9695
  }
9666
9696
  }
@@ -9678,7 +9708,7 @@ async function validateProject(options = {}) {
9678
9708
  }
9679
9709
  filePaths = resolvedFiles;
9680
9710
  } else if (options.files && options.files.length > 0) {
9681
- filePaths = options.files.map((f) => import_node_path16.default.resolve(f));
9711
+ filePaths = options.files.map((f) => import_node_path17.default.resolve(f));
9682
9712
  } else if (options.workspace) {
9683
9713
  const [testFiles, suiteFiles] = await Promise.all([
9684
9714
  discoverWorkspaceFiles({ workspace: options.workspace, kind: "test" }),
@@ -9721,11 +9751,11 @@ async function validateProject(options = {}) {
9721
9751
  if (d.severity === "error") result.errorCount++;
9722
9752
  else result.warningCount++;
9723
9753
  }
9724
- if (hooksFilePath && hooksDiags.length === 0 && (0, import_node_fs5.existsSync)(hooksFilePath)) {
9754
+ if (hooksFilePath && hooksDiags.length === 0 && (0, import_node_fs6.existsSync)(hooksFilePath)) {
9725
9755
  const { hooks } = await parseHooksFile(hooksFilePath);
9726
9756
  const hookIds = new Set(hooks.map((h) => h.id));
9727
9757
  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;
9758
+ const workspaceRelativePath = options.workspace ? import_node_path17.default.relative(options.workspace.configDir, filePath).split(import_node_path17.default.sep).join("/") : filePath;
9729
9759
  const isWorkspaceFile = options.workspace ? isWorkspacePathMatch({ workspace: options.workspace, kind: isSuiteFile(filePath) ? "suite" : "test", workspaceRelativePath }) : true;
9730
9760
  if (!isWorkspaceFile) continue;
9731
9761
  try {
@@ -9827,7 +9857,7 @@ function warnIfOutOfBounds(coords, viewport, actionType) {
9827
9857
 
9828
9858
  // src/hooks/migrate-hook-refs.ts
9829
9859
  var import_promises13 = require("fs/promises");
9830
- var import_node_path17 = require("path");
9860
+ var import_node_path18 = require("path");
9831
9861
  var import_yaml6 = require("yaml");
9832
9862
  var import_zod20 = require("zod");
9833
9863
  var import_agent_qa_ids8 = require("@vostride/agent-qa-ids");
@@ -9933,7 +9963,7 @@ function rewriteSteps(owner, filePath, hookNameToId, hookIds, errors) {
9933
9963
  return changed;
9934
9964
  }
9935
9965
  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");
9966
+ const resolvedConfigPath = configPath ? (0, import_node_path18.resolve)(configPath) : (0, import_node_path18.resolve)(workspaceDir, "agent-qa.config.yaml");
9937
9967
  const rawConfig = await (0, import_promises13.readFile)(resolvedConfigPath, "utf-8");
9938
9968
  const parsedConfig = (0, import_yaml6.parse)(rawConfig);
9939
9969
  const configResult = AgentQaConfigSchema.safeParse(parsedConfig);
@@ -9960,7 +9990,7 @@ async function resolveWorkspaceMigrationContext(workspaceDir, configPath) {
9960
9990
  };
9961
9991
  }
9962
9992
  async function auditWorkspaceHookReferenceMigration(options) {
9963
- const workspaceDir = (0, import_node_path17.resolve)(options.workspaceDir);
9993
+ const workspaceDir = (0, import_node_path18.resolve)(options.workspaceDir);
9964
9994
  const errors = [];
9965
9995
  let context;
9966
9996
  try {
@@ -10023,12 +10053,12 @@ async function auditWorkspaceHookReferenceMigration(options) {
10023
10053
  const hookIds = new Set(Object.values(hookNameToId));
10024
10054
  if (hooksChanged) {
10025
10055
  rewriteFiles.push({
10026
- path: (0, import_node_path17.relative)(context.configDir, context.hooksFilePath),
10056
+ path: (0, import_node_path18.relative)(context.configDir, context.hooksFilePath),
10027
10057
  content: (0, import_yaml6.stringify)({ hooks: normalizedHooks })
10028
10058
  });
10029
10059
  }
10030
10060
  for (const relativePath of context.targetFiles) {
10031
- const absolutePath = (0, import_node_path17.resolve)(context.configDir, relativePath);
10061
+ const absolutePath = (0, import_node_path18.resolve)(context.configDir, relativePath);
10032
10062
  let parsedFile;
10033
10063
  try {
10034
10064
  parsedFile = (0, import_yaml6.parse)(await (0, import_promises13.readFile)(absolutePath, "utf-8"));
@@ -10069,14 +10099,14 @@ async function applyWorkspaceHookReferenceMigration(options) {
10069
10099
  if (!audit.ok) {
10070
10100
  throw new HookReferenceMigrationError(audit.errors);
10071
10101
  }
10072
- const workspaceDir = options.configPath ? (0, import_node_path17.dirname)((0, import_node_path17.resolve)(options.configPath)) : (0, import_node_path17.resolve)(options.workspaceDir);
10102
+ const workspaceDir = options.configPath ? (0, import_node_path18.dirname)((0, import_node_path18.resolve)(options.configPath)) : (0, import_node_path18.resolve)(options.workspaceDir);
10073
10103
  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 });
10104
+ const outputPath = (0, import_node_path18.resolve)(workspaceDir, rewrite.path);
10105
+ await (0, import_promises13.mkdir)((0, import_node_path18.dirname)(outputPath), { recursive: true });
10076
10106
  await (0, import_promises13.writeFile)(outputPath, rewrite.content, "utf-8");
10077
10107
  }
10078
10108
  for (const cleanupTarget of audit.cleanupTargets) {
10079
- await (0, import_promises13.rm)((0, import_node_path17.resolve)(workspaceDir, cleanupTarget), { recursive: true, force: true });
10109
+ await (0, import_promises13.rm)((0, import_node_path18.resolve)(workspaceDir, cleanupTarget), { recursive: true, force: true });
10080
10110
  }
10081
10111
  return audit;
10082
10112
  }
@@ -10147,12 +10177,12 @@ var RUN_ARTIFACT_SCHEMA_VERSION = 1;
10147
10177
  // src/analytics/identity.ts
10148
10178
  var import_promises17 = require("fs/promises");
10149
10179
  var import_node_os4 = require("os");
10150
- var import_node_path22 = require("path");
10180
+ var import_node_path23 = require("path");
10151
10181
  var import_id_agent = require("id-agent");
10152
10182
  var LOCAL_ANALYTICS_ID_PATTERN = /^u_([a-z]+-){9}[a-z]+$/;
10153
10183
  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");
10184
+ if (env.XDG_DATA_HOME) return (0, import_node_path23.join)(env.XDG_DATA_HOME, "agent-qa", "analytics.json");
10185
+ return (0, import_node_path23.join)(homeDir, ".agent-qa", "analytics.json");
10156
10186
  }
10157
10187
  function generateAnalyticsUserId() {
10158
10188
  return (0, import_id_agent.idAgent)({ prefix: "u", words: 10 });
@@ -10184,7 +10214,7 @@ async function readAnalyticsIdentity(path5) {
10184
10214
  }
10185
10215
  async function writeAnalyticsIdentity(distinctId, path5, isInternal = false) {
10186
10216
  const identityPath = path5 ?? getAnalyticsIdentityPath();
10187
- await (0, import_promises17.mkdir)((0, import_node_path22.dirname)(identityPath), { recursive: true });
10217
+ await (0, import_promises17.mkdir)((0, import_node_path23.dirname)(identityPath), { recursive: true });
10188
10218
  await (0, import_promises17.writeFile)(identityPath, JSON.stringify({
10189
10219
  distinctId,
10190
10220
  is_internal: isInternal
@@ -11295,6 +11325,7 @@ function isMacPlatform() {
11295
11325
  findSecretTemplates,
11296
11326
  findUnresolvedTemplates,
11297
11327
  flushAnalytics,
11328
+ formatAppiumInstallGuidance,
11298
11329
  formatDiagnostics,
11299
11330
  formatDiagnosticsPlain,
11300
11331
  formatLogEntry,
@@ -11329,6 +11360,7 @@ function isMacPlatform() {
11329
11360
  listObservations,
11330
11361
  listProviders,
11331
11362
  loadLLMAuthPlugins,
11363
+ localAppiumBinaryNames,
11332
11364
  mapMobileCapabilities,
11333
11365
  mapWebCapabilities,
11334
11366
  mergeRunAttributes,
@@ -11355,6 +11387,7 @@ function isMacPlatform() {
11355
11387
  resolveAnalyticsAgentProduct,
11356
11388
  resolveAnalyticsIdentity,
11357
11389
  resolveAnalyticsStandardProperties,
11390
+ resolveAppiumExecutable,
11358
11391
  resolveBetterSqliteRebuildCommand,
11359
11392
  resolveFarmCredentials,
11360
11393
  resolveFileReferences,