@validate.qa/runner 1.0.68 → 1.0.69

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.
Files changed (3) hide show
  1. package/dist/cli.js +278 -208
  2. package/dist/cli.mjs +274 -204
  3. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -11298,7 +11298,7 @@ var require_thread_stream = __commonJS({
11298
11298
  var { version } = require_package();
11299
11299
  var { EventEmitter: EventEmitter2 } = __require("events");
11300
11300
  var { Worker } = __require("worker_threads");
11301
- var { join: join7 } = __require("path");
11301
+ var { join: join8 } = __require("path");
11302
11302
  var { pathToFileURL } = __require("url");
11303
11303
  var { wait } = require_wait();
11304
11304
  var {
@@ -11349,7 +11349,7 @@ var require_thread_stream = __commonJS({
11349
11349
  function createWorker(stream, opts) {
11350
11350
  const { filename, workerData } = opts;
11351
11351
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
11352
- const toExecute = bundlerOverrides["thread-stream-worker"] || join7(__dirname, "lib", "worker.js");
11352
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join8(__dirname, "lib", "worker.js");
11353
11353
  const worker = new Worker(toExecute, {
11354
11354
  ...opts.workerOpts,
11355
11355
  name: opts.workerOpts?.name || "thread-stream",
@@ -11817,7 +11817,7 @@ var require_transport = __commonJS({
11817
11817
  var { createRequire } = __require("module");
11818
11818
  var { existsSync: existsSync3 } = __require("fs");
11819
11819
  var getCallers = require_caller();
11820
- var { join: join7, isAbsolute, sep } = __require("path");
11820
+ var { join: join8, isAbsolute: isAbsolute2, sep } = __require("path");
11821
11821
  var { fileURLToPath } = __require("url");
11822
11822
  var sleep = require_atomic_sleep();
11823
11823
  var onExit = require_on_exit_leak_free();
@@ -11889,7 +11889,7 @@ var require_transport = __commonJS({
11889
11889
  return false;
11890
11890
  }
11891
11891
  }
11892
- return isAbsolute(path) && !existsSync3(path);
11892
+ return isAbsolute2(path) && !existsSync3(path);
11893
11893
  }
11894
11894
  function stripQuotes(value) {
11895
11895
  const first = value[0];
@@ -11970,7 +11970,7 @@ var require_transport = __commonJS({
11970
11970
  throw new Error("only one of target or targets can be specified");
11971
11971
  }
11972
11972
  if (targets) {
11973
- target = bundlerOverrides["pino-worker"] || join7(__dirname, "worker.js");
11973
+ target = bundlerOverrides["pino-worker"] || join8(__dirname, "worker.js");
11974
11974
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
11975
11975
  return {
11976
11976
  ...dest,
@@ -11988,7 +11988,7 @@ var require_transport = __commonJS({
11988
11988
  });
11989
11989
  });
11990
11990
  } else if (pipeline) {
11991
- target = bundlerOverrides["pino-worker"] || join7(__dirname, "worker.js");
11991
+ target = bundlerOverrides["pino-worker"] || join8(__dirname, "worker.js");
11992
11992
  options.pipelines = [pipeline.map((dest) => {
11993
11993
  return {
11994
11994
  ...dest,
@@ -12007,11 +12007,11 @@ var require_transport = __commonJS({
12007
12007
  return buildStream(fixTarget(target), options, worker, sync, name);
12008
12008
  function fixTarget(origin) {
12009
12009
  origin = bundlerOverrides[origin] || origin;
12010
- if (isAbsolute(origin) || origin.indexOf("file://") === 0) {
12010
+ if (isAbsolute2(origin) || origin.indexOf("file://") === 0) {
12011
12011
  return origin;
12012
12012
  }
12013
12013
  if (origin === "pino/file") {
12014
- return join7(__dirname, "..", "file.js");
12014
+ return join8(__dirname, "..", "file.js");
12015
12015
  }
12016
12016
  let fixTarget2;
12017
12017
  for (const filePath of callers) {
@@ -12992,7 +12992,7 @@ var require_safe_stable_stringify = __commonJS({
12992
12992
  return circularValue;
12993
12993
  }
12994
12994
  let res = "";
12995
- let join7 = ",";
12995
+ let join8 = ",";
12996
12996
  const originalIndentation = indentation;
12997
12997
  if (Array.isArray(value)) {
12998
12998
  if (value.length === 0) {
@@ -13006,7 +13006,7 @@ var require_safe_stable_stringify = __commonJS({
13006
13006
  indentation += spacer;
13007
13007
  res += `
13008
13008
  ${indentation}`;
13009
- join7 = `,
13009
+ join8 = `,
13010
13010
  ${indentation}`;
13011
13011
  }
13012
13012
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -13014,13 +13014,13 @@ ${indentation}`;
13014
13014
  for (; i < maximumValuesToStringify - 1; i++) {
13015
13015
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
13016
13016
  res += tmp2 !== void 0 ? tmp2 : "null";
13017
- res += join7;
13017
+ res += join8;
13018
13018
  }
13019
13019
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
13020
13020
  res += tmp !== void 0 ? tmp : "null";
13021
13021
  if (value.length - 1 > maximumBreadth) {
13022
13022
  const removedKeys = value.length - maximumBreadth - 1;
13023
- res += `${join7}"... ${getItemCount(removedKeys)} not stringified"`;
13023
+ res += `${join8}"... ${getItemCount(removedKeys)} not stringified"`;
13024
13024
  }
13025
13025
  if (spacer !== "") {
13026
13026
  res += `
@@ -13041,7 +13041,7 @@ ${originalIndentation}`;
13041
13041
  let separator = "";
13042
13042
  if (spacer !== "") {
13043
13043
  indentation += spacer;
13044
- join7 = `,
13044
+ join8 = `,
13045
13045
  ${indentation}`;
13046
13046
  whitespace = " ";
13047
13047
  }
@@ -13055,13 +13055,13 @@ ${indentation}`;
13055
13055
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
13056
13056
  if (tmp !== void 0) {
13057
13057
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
13058
- separator = join7;
13058
+ separator = join8;
13059
13059
  }
13060
13060
  }
13061
13061
  if (keyLength > maximumBreadth) {
13062
13062
  const removedKeys = keyLength - maximumBreadth;
13063
13063
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
13064
- separator = join7;
13064
+ separator = join8;
13065
13065
  }
13066
13066
  if (spacer !== "" && separator.length > 1) {
13067
13067
  res = `
@@ -13102,7 +13102,7 @@ ${originalIndentation}`;
13102
13102
  }
13103
13103
  const originalIndentation = indentation;
13104
13104
  let res = "";
13105
- let join7 = ",";
13105
+ let join8 = ",";
13106
13106
  if (Array.isArray(value)) {
13107
13107
  if (value.length === 0) {
13108
13108
  return "[]";
@@ -13115,7 +13115,7 @@ ${originalIndentation}`;
13115
13115
  indentation += spacer;
13116
13116
  res += `
13117
13117
  ${indentation}`;
13118
- join7 = `,
13118
+ join8 = `,
13119
13119
  ${indentation}`;
13120
13120
  }
13121
13121
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -13123,13 +13123,13 @@ ${indentation}`;
13123
13123
  for (; i < maximumValuesToStringify - 1; i++) {
13124
13124
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
13125
13125
  res += tmp2 !== void 0 ? tmp2 : "null";
13126
- res += join7;
13126
+ res += join8;
13127
13127
  }
13128
13128
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
13129
13129
  res += tmp !== void 0 ? tmp : "null";
13130
13130
  if (value.length - 1 > maximumBreadth) {
13131
13131
  const removedKeys = value.length - maximumBreadth - 1;
13132
- res += `${join7}"... ${getItemCount(removedKeys)} not stringified"`;
13132
+ res += `${join8}"... ${getItemCount(removedKeys)} not stringified"`;
13133
13133
  }
13134
13134
  if (spacer !== "") {
13135
13135
  res += `
@@ -13142,7 +13142,7 @@ ${originalIndentation}`;
13142
13142
  let whitespace = "";
13143
13143
  if (spacer !== "") {
13144
13144
  indentation += spacer;
13145
- join7 = `,
13145
+ join8 = `,
13146
13146
  ${indentation}`;
13147
13147
  whitespace = " ";
13148
13148
  }
@@ -13151,7 +13151,7 @@ ${indentation}`;
13151
13151
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
13152
13152
  if (tmp !== void 0) {
13153
13153
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
13154
- separator = join7;
13154
+ separator = join8;
13155
13155
  }
13156
13156
  }
13157
13157
  if (spacer !== "" && separator.length > 1) {
@@ -13209,20 +13209,20 @@ ${originalIndentation}`;
13209
13209
  indentation += spacer;
13210
13210
  let res2 = `
13211
13211
  ${indentation}`;
13212
- const join8 = `,
13212
+ const join9 = `,
13213
13213
  ${indentation}`;
13214
13214
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
13215
13215
  let i = 0;
13216
13216
  for (; i < maximumValuesToStringify - 1; i++) {
13217
13217
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
13218
13218
  res2 += tmp2 !== void 0 ? tmp2 : "null";
13219
- res2 += join8;
13219
+ res2 += join9;
13220
13220
  }
13221
13221
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
13222
13222
  res2 += tmp !== void 0 ? tmp : "null";
13223
13223
  if (value.length - 1 > maximumBreadth) {
13224
13224
  const removedKeys = value.length - maximumBreadth - 1;
13225
- res2 += `${join8}"... ${getItemCount(removedKeys)} not stringified"`;
13225
+ res2 += `${join9}"... ${getItemCount(removedKeys)} not stringified"`;
13226
13226
  }
13227
13227
  res2 += `
13228
13228
  ${originalIndentation}`;
@@ -13238,16 +13238,16 @@ ${originalIndentation}`;
13238
13238
  return '"[Object]"';
13239
13239
  }
13240
13240
  indentation += spacer;
13241
- const join7 = `,
13241
+ const join8 = `,
13242
13242
  ${indentation}`;
13243
13243
  let res = "";
13244
13244
  let separator = "";
13245
13245
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
13246
13246
  if (isTypedArrayWithEntries(value)) {
13247
- res += stringifyTypedArray(value, join7, maximumBreadth);
13247
+ res += stringifyTypedArray(value, join8, maximumBreadth);
13248
13248
  keys = keys.slice(value.length);
13249
13249
  maximumPropertiesToStringify -= value.length;
13250
- separator = join7;
13250
+ separator = join8;
13251
13251
  }
13252
13252
  if (deterministic) {
13253
13253
  keys = sort(keys, comparator);
@@ -13258,13 +13258,13 @@ ${indentation}`;
13258
13258
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
13259
13259
  if (tmp !== void 0) {
13260
13260
  res += `${separator}${strEscape(key2)}: ${tmp}`;
13261
- separator = join7;
13261
+ separator = join8;
13262
13262
  }
13263
13263
  }
13264
13264
  if (keyLength > maximumBreadth) {
13265
13265
  const removedKeys = keyLength - maximumBreadth;
13266
13266
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
13267
- separator = join7;
13267
+ separator = join8;
13268
13268
  }
13269
13269
  if (separator !== "") {
13270
13270
  res = `
@@ -61940,11 +61940,11 @@ var require_core = __commonJS({
61940
61940
  function mapOuter(r, f) {
61941
61941
  return r.matched ? f(r) : r;
61942
61942
  }
61943
- function ab(pa, pb, join7) {
61944
- return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) => join7(ma.value, vb, data, i, j)));
61943
+ function ab(pa, pb, join8) {
61944
+ return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) => join8(ma.value, vb, data, i, j)));
61945
61945
  }
61946
- function abc(pa, pb, pc, join7) {
61947
- return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) => join7(ma.value, mb.value, vc, data, i, j))));
61946
+ function abc(pa, pb, pc, join8) {
61947
+ return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) => join8(ma.value, mb.value, vc, data, i, j))));
61948
61948
  }
61949
61949
  function action(f) {
61950
61950
  return (data, i) => {
@@ -83529,48 +83529,29 @@ ${statePacket}`, redactionCredentials) },
83529
83529
  log2(" app data reset skipped by policy \u2014 scenario records after relaunch with preserved data.");
83530
83530
  return applyScenarioEntryDeepLink(driver, target, { appDataCleared: false, coldResetUnavailable: false, refuseGenerate: false, reason: "preserve_policy" }, log2);
83531
83531
  }
83532
- if (platform3 !== "IOS") {
83533
- const appDataCleared = await driver.resetAppState?.().catch((err) => {
83534
- log2(` app-data reset failed: ${err instanceof Error ? err.message : String(err)}`);
83535
- return false;
83536
- }) ?? false;
83537
- if (appDataCleared) {
83538
- return applyScenarioEntryDeepLink(driver, target, { appDataCleared: true, coldResetUnavailable: false, refuseGenerate: false }, log2);
83539
- }
83540
- await driver.relaunch();
83541
- if (resetPolicy === "CLEAR_APP_DATA") {
83542
- log2(" CLEAR_APP_DATA required but app-data reset was unavailable \u2014 refusing to generate from warm state.");
83543
- return {
83544
- appDataCleared: false,
83545
- coldResetUnavailable: true,
83546
- refuseGenerate: true,
83547
- reason: "clear_app_data_unavailable"
83548
- };
83549
- }
83550
- log2(" app-data reset unavailable \u2014 generating with @warm-state (relaunch only; may not match cold-boot replay).");
83551
- return applyScenarioEntryDeepLink(driver, target, {
83552
- appDataCleared: false,
83553
- coldResetUnavailable: true,
83554
- refuseGenerate: false,
83555
- reason: platform3 === "ANDROID" ? "android_reset_unavailable" : "reset_unavailable"
83556
- }, log2);
83532
+ const appDataCleared = await driver.resetAppState?.().catch((err) => {
83533
+ log2(` app-data reset failed: ${err instanceof Error ? err.message : String(err)}`);
83534
+ return false;
83535
+ }) ?? false;
83536
+ if (appDataCleared) {
83537
+ return applyScenarioEntryDeepLink(driver, target, { appDataCleared: true, coldResetUnavailable: false, refuseGenerate: false }, log2);
83557
83538
  }
83558
83539
  await driver.relaunch();
83559
83540
  if (resetPolicy === "CLEAR_APP_DATA") {
83560
- log2(" CLEAR_APP_DATA required but iOS has no supported clearApp primitive \u2014 refusing to generate from warm state.");
83541
+ log2(platform3 === "IOS" ? " CLEAR_APP_DATA required but this iOS device cannot be cleared (simulator required) \u2014 refusing to generate from warm state." : " CLEAR_APP_DATA required but app-data reset was unavailable \u2014 refusing to generate from warm state.");
83561
83542
  return {
83562
83543
  appDataCleared: false,
83563
83544
  coldResetUnavailable: true,
83564
83545
  refuseGenerate: true,
83565
- reason: "ios_clear_app_data_unavailable"
83546
+ reason: platform3 === "IOS" ? "ios_clear_app_data_unavailable" : "clear_app_data_unavailable"
83566
83547
  };
83567
83548
  }
83568
- log2(" iOS has no clearApp \u2014 scenario records after relaunch (warm-state risk for auth flows).");
83549
+ log2(platform3 === "IOS" ? " iOS app-data reset unavailable (physical device or simctl failure) \u2014 generating with @warm-state (relaunch only; may not match cold-boot replay)." : " app-data reset unavailable \u2014 generating with @warm-state (relaunch only; may not match cold-boot replay).");
83569
83550
  return applyScenarioEntryDeepLink(driver, target, {
83570
83551
  appDataCleared: false,
83571
83552
  coldResetUnavailable: true,
83572
83553
  refuseGenerate: false,
83573
- reason: "ios_no_clear_app"
83554
+ reason: platform3 === "IOS" ? "ios_no_clear_app" : platform3 === "ANDROID" ? "android_reset_unavailable" : "reset_unavailable"
83574
83555
  }, log2);
83575
83556
  }
83576
83557
  async function runMobileGeneratePhase(args) {
@@ -89031,7 +89012,7 @@ var require_kill_tree = __commonJS({
89031
89012
  var node_child_process_1 = __require("child_process");
89032
89013
  var node_util_1 = __require("util");
89033
89014
  var node_fs_1 = __require("fs");
89034
- var execFileAsync3 = (0, node_util_1.promisify)(node_child_process_1.execFile);
89015
+ var execFileAsync4 = (0, node_util_1.promisify)(node_child_process_1.execFile);
89035
89016
  var PS_TIMEOUT_MS = 2e3;
89036
89017
  var PS_BUFFER = 1024 * 1024;
89037
89018
  async function gracefulCloseWithTreeKill(options) {
@@ -89085,7 +89066,7 @@ var require_kill_tree = __commonJS({
89085
89066
  timer.unref?.();
89086
89067
  let stdout;
89087
89068
  try {
89088
- const result = await execFileAsync3("ps", ["-A", "-o", "pid=,ppid=,command="], {
89069
+ const result = await execFileAsync4("ps", ["-A", "-o", "pid=,ppid=,command="], {
89089
89070
  maxBuffer: PS_BUFFER,
89090
89071
  signal: controller.signal
89091
89072
  });
@@ -89123,7 +89104,7 @@ var require_kill_tree = __commonJS({
89123
89104
  timer.unref?.();
89124
89105
  let stdout;
89125
89106
  try {
89126
- const result = await execFileAsync3("ps", ["-A", "-o", "pid=,ppid=,command="], {
89107
+ const result = await execFileAsync4("ps", ["-A", "-o", "pid=,ppid=,command="], {
89127
89108
  maxBuffer: PS_BUFFER,
89128
89109
  signal: controller.signal
89129
89110
  });
@@ -89200,7 +89181,7 @@ var require_kill_tree = __commonJS({
89200
89181
  timer.unref?.();
89201
89182
  let stdout;
89202
89183
  try {
89203
- const result = await execFileAsync3("ps", ["-A", "-o", "pid=,ppid="], {
89184
+ const result = await execFileAsync4("ps", ["-A", "-o", "pid=,ppid="], {
89204
89185
  maxBuffer: PS_BUFFER,
89205
89186
  signal: controller.signal
89206
89187
  });
@@ -98750,8 +98731,8 @@ Do NOT add steps you did not execute. Do NOT skip steps from the recording.${led
98750
98731
  onRetry: (attempt, reason, delayMs) => logs2.push(` \u26A0 scenario-gen AI call retry ${attempt} (${reason}) \u2014 waiting ${delayMs}ms`)
98751
98732
  });
98752
98733
  } catch (aiErr) {
98753
- const errMsg2 = aiErr instanceof Error ? aiErr.message : String(aiErr);
98754
- logs2.push(` AI call failed at iteration ${iteration} (after retries): ${errMsg2}`);
98734
+ const errMsg3 = aiErr instanceof Error ? aiErr.message : String(aiErr);
98735
+ logs2.push(` AI call failed at iteration ${iteration} (after retries): ${errMsg3}`);
98755
98736
  throw (0, ai_retry_js_1.markAIProviderError)(aiErr);
98756
98737
  }
98757
98738
  const iterDuration = Date.now() - iterStart;
@@ -98794,8 +98775,8 @@ Do NOT add steps you did not execute. Do NOT skip steps from the recording.${led
98794
98775
  try {
98795
98776
  toolArgs = JSON.parse(toolCall.function.arguments);
98796
98777
  } catch (parseErr) {
98797
- const errMsg2 = `Invalid tool arguments: ${parseErr.message}`;
98798
- toolResults.push({ role: "tool", tool_call_id: toolCall.id, content: errMsg2 });
98778
+ const errMsg3 = `Invalid tool arguments: ${parseErr.message}`;
98779
+ toolResults.push({ role: "tool", tool_call_id: toolCall.id, content: errMsg3 });
98799
98780
  continue;
98800
98781
  }
98801
98782
  if (toolName === "finish_generate") {
@@ -100013,7 +99994,7 @@ var require_util4 = __commonJS({
100013
99994
  }
100014
99995
  path = url.path;
100015
99996
  }
100016
- var isAbsolute = exports.isAbsolute(path);
99997
+ var isAbsolute2 = exports.isAbsolute(path);
100017
99998
  var parts = path.split(/\/+/);
100018
99999
  for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
100019
100000
  part = parts[i];
@@ -100033,7 +100014,7 @@ var require_util4 = __commonJS({
100033
100014
  }
100034
100015
  path = parts.join("/");
100035
100016
  if (path === "") {
100036
- path = isAbsolute ? "/" : ".";
100017
+ path = isAbsolute2 ? "/" : ".";
100037
100018
  }
100038
100019
  if (url) {
100039
100020
  url.path = path;
@@ -100042,7 +100023,7 @@ var require_util4 = __commonJS({
100042
100023
  return path;
100043
100024
  }
100044
100025
  exports.normalize = normalize;
100045
- function join7(aRoot, aPath) {
100026
+ function join8(aRoot, aPath) {
100046
100027
  if (aRoot === "") {
100047
100028
  aRoot = ".";
100048
100029
  }
@@ -100074,7 +100055,7 @@ var require_util4 = __commonJS({
100074
100055
  }
100075
100056
  return joined;
100076
100057
  }
100077
- exports.join = join7;
100058
+ exports.join = join8;
100078
100059
  exports.isAbsolute = function(aPath) {
100079
100060
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
100080
100061
  };
@@ -100247,7 +100228,7 @@ var require_util4 = __commonJS({
100247
100228
  parsed.path = parsed.path.substring(0, index + 1);
100248
100229
  }
100249
100230
  }
100250
- sourceURL = join7(urlGenerate(parsed), sourceURL);
100231
+ sourceURL = join8(urlGenerate(parsed), sourceURL);
100251
100232
  }
100252
100233
  return normalize(sourceURL);
100253
100234
  }
@@ -313959,8 +313940,8 @@ Try a different fix approach. This is NOT your final script-driven attempt yet,
313959
313940
  ...(0, ai_provider_js_1.getJsonObjectResponseFormat)(agentModel)
313960
313941
  })), { label: `script-heal-${attempt}` });
313961
313942
  } catch (aiErr) {
313962
- const errMsg2 = aiErr instanceof Error ? aiErr.message : String(aiErr);
313963
- logs2.push(`\u26A0 AI call failed: ${errMsg2}`);
313943
+ const errMsg3 = aiErr instanceof Error ? aiErr.message : String(aiErr);
313944
+ logs2.push(`\u26A0 AI call failed: ${errMsg3}`);
313964
313945
  attemptHistory.push({
313965
313946
  phase: "script",
313966
313947
  attempt,
@@ -313968,13 +313949,13 @@ Try a different fix approach. This is NOT your final script-driven attempt yet,
313968
313949
  passed: false,
313969
313950
  changedCode: false,
313970
313951
  summary: "AI call failed before returning a heal result",
313971
- errorSummary: truncateForPrompt(errMsg2),
313952
+ errorSummary: truncateForPrompt(errMsg3),
313972
313953
  failedStep: summarizeFailedStep(currentStepResults)
313973
313954
  });
313974
313955
  if (feedbackEnabled) {
313975
- lastTransportFailure = truncateForPrompt(errMsg2, 200) ?? "the AI request failed";
313956
+ lastTransportFailure = truncateForPrompt(errMsg3, 200) ?? "the AI request failed";
313976
313957
  } else {
313977
- currentError = `AI call failed on attempt ${attempt}: ${errMsg2}`;
313958
+ currentError = `AI call failed on attempt ${attempt}: ${errMsg3}`;
313978
313959
  }
313979
313960
  markRecoverableTurn();
313980
313961
  continue;
@@ -314224,8 +314205,8 @@ ${currentError.slice(0, 2e3)}`
314224
314205
  // item 5b
314225
314206
  });
314226
314207
  } catch (runErr) {
314227
- const errMsg2 = runErr instanceof Error ? runErr.message : String(runErr);
314228
- logs2.push(`\u{1F4A5} Native execution threw: ${errMsg2}`);
314208
+ const errMsg3 = runErr instanceof Error ? runErr.message : String(runErr);
314209
+ logs2.push(`\u{1F4A5} Native execution threw: ${errMsg3}`);
314229
314210
  attemptHistory.push({
314230
314211
  phase: "script",
314231
314212
  attempt,
@@ -314233,9 +314214,9 @@ ${currentError.slice(0, 2e3)}`
314233
314214
  passed: false,
314234
314215
  changedCode: true,
314235
314216
  summary: truncateForPrompt(changeDesc) ?? "Applied code fix, but native execution threw before verification completed",
314236
- errorSummary: truncateForPrompt(errMsg2)
314217
+ errorSummary: truncateForPrompt(errMsg3)
314237
314218
  });
314238
- currentError = `Native execution threw: ${errMsg2}`;
314219
+ currentError = `Native execution threw: ${errMsg3}`;
314239
314220
  currentStepResults = [];
314240
314221
  continue;
314241
314222
  }
@@ -314991,9 +314972,9 @@ Last error (after heal attempts): ${lastErrorSnippet}` : `Heal aborted: same err
314991
314972
  previousErrorSignature = errorSig;
314992
314973
  }
314993
314974
  } catch (runErr) {
314994
- const errMsg2 = runErr instanceof Error ? runErr.message : String(runErr);
314995
- logs2.push(`\u{1F4A5} Native run threw: ${errMsg2}`);
314996
- currentError = errMsg2;
314975
+ const errMsg3 = runErr instanceof Error ? runErr.message : String(runErr);
314976
+ logs2.push(`\u{1F4A5} Native run threw: ${errMsg3}`);
314977
+ currentError = errMsg3;
314997
314978
  currentStepResults = [];
314998
314979
  }
314999
314980
  } else {
@@ -315555,8 +315536,8 @@ When you call finish_fix, preserve locators only when they are stable structural
315555
315536
  }
315556
315537
  }
315557
315538
  } catch (toolErr) {
315558
- const errMsg2 = toolErr instanceof Error ? toolErr.message : String(toolErr);
315559
- let errorContent = `Error: ${errMsg2}`;
315539
+ const errMsg3 = toolErr instanceof Error ? toolErr.message : String(toolErr);
315540
+ let errorContent = `Error: ${errMsg3}`;
315560
315541
  try {
315561
315542
  const consoleResult = await mcpClient.callTool({ name: "browser_console_messages", arguments: {} });
315562
315543
  const consoleText = Array.isArray(consoleResult.content) ? consoleResult.content.map((c) => c.type === "text" ? c.text ?? "" : "").join("\n") : String(consoleResult.content ?? "");
@@ -315577,8 +315558,8 @@ ${errorContent}`;
315577
315558
  }
315578
315559
  }
315579
315560
  } catch (mcpErr) {
315580
- const errMsg2 = mcpErr instanceof Error ? mcpErr.message : String(mcpErr);
315581
- logs2.push(`\u{1F4A5} MCP exploration failed: ${errMsg2}`);
315561
+ const errMsg3 = mcpErr instanceof Error ? mcpErr.message : String(mcpErr);
315562
+ logs2.push(`\u{1F4A5} MCP exploration failed: ${errMsg3}`);
315582
315563
  }
315583
315564
  if (verdict === "app_bug") {
315584
315565
  phase2AttemptHistory.push({
@@ -316141,9 +316122,9 @@ ${testBlocks}
316141
316122
  }
316142
316123
  emit(` \u2713 ${toolResultContent.slice(0, 200)}${toolResultContent.length > 200 ? "..." : ""}`);
316143
316124
  } catch (err) {
316144
- const errMsg2 = err instanceof Error ? err.message : String(err);
316145
- toolResultContent = `ERROR: ${errMsg2}`;
316146
- emit(` \u2717 ${errMsg2}`);
316125
+ const errMsg3 = err instanceof Error ? err.message : String(err);
316126
+ toolResultContent = `ERROR: ${errMsg3}`;
316127
+ emit(` \u2717 ${errMsg3}`);
316147
316128
  }
316148
316129
  toolResults.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
316149
316130
  }
@@ -321998,8 +321979,8 @@ var require_capture_page_normalizer = __commonJS({
321998
321979
  const snapItems = Array.isArray(snapResult.content) ? snapResult.content : [];
321999
321980
  snapshotText = extractInlineSnapshotYaml(snapItems.find((c) => c.type === "text")?.text || null);
322000
321981
  } catch (err) {
322001
- const errMsg2 = err instanceof Error ? err.message : String(err);
322002
- const isTimeout = /timeout|timed out|TimeoutError/i.test(errMsg2);
321982
+ const errMsg3 = err instanceof Error ? err.message : String(err);
321983
+ const isTimeout = /timeout|timed out|TimeoutError/i.test(errMsg3);
322003
321984
  return {
322004
321985
  snapshotText: null,
322005
321986
  meta: {
@@ -326021,9 +326002,9 @@ ${currentSummary}`;
326021
326002
  });
326022
326003
  }
326023
326004
  } catch (err) {
326024
- const errMsg2 = err instanceof Error ? err.message : String(err);
326025
- logs2.push(` Error persisting page: ${errMsg2}`);
326026
- toolResults.push({ role: "tool", tool_call_id: toolCall.id, content: `ERROR persisting page: ${errMsg2}` });
326005
+ const errMsg3 = err instanceof Error ? err.message : String(err);
326006
+ logs2.push(` Error persisting page: ${errMsg3}`);
326007
+ toolResults.push({ role: "tool", tool_call_id: toolCall.id, content: `ERROR persisting page: ${errMsg3}` });
326027
326008
  }
326028
326009
  continue;
326029
326010
  }
@@ -326225,12 +326206,12 @@ ${currentSummary}`;
326225
326206
  })
326226
326207
  });
326227
326208
  } catch (err) {
326228
- const errMsg2 = err instanceof Error ? err.message : String(err);
326229
- logs2.push(` Error persisting page: ${errMsg2}`);
326209
+ const errMsg3 = err instanceof Error ? err.message : String(err);
326210
+ logs2.push(` Error persisting page: ${errMsg3}`);
326230
326211
  toolResults.push({
326231
326212
  role: "tool",
326232
326213
  tool_call_id: toolCall.id,
326233
- content: `ERROR persisting page: ${errMsg2}`
326214
+ content: `ERROR persisting page: ${errMsg3}`
326234
326215
  });
326235
326216
  }
326236
326217
  continue;
@@ -326265,12 +326246,12 @@ ${currentSummary}`;
326265
326246
  content: JSON.stringify({ acknowledged: true })
326266
326247
  });
326267
326248
  } catch (err) {
326268
- const errMsg2 = err instanceof Error ? err.message : String(err);
326269
- logs2.push(` Error persisting transition: ${errMsg2}`);
326249
+ const errMsg3 = err instanceof Error ? err.message : String(err);
326250
+ logs2.push(` Error persisting transition: ${errMsg3}`);
326270
326251
  toolResults.push({
326271
326252
  role: "tool",
326272
326253
  tool_call_id: toolCall.id,
326273
- content: `ERROR persisting transition: ${errMsg2}`
326254
+ content: `ERROR persisting transition: ${errMsg3}`
326274
326255
  });
326275
326256
  }
326276
326257
  continue;
@@ -326925,15 +326906,15 @@ Exploration complete: ${summary}`);
326925
326906
  toolResultContent = (0, snapshot_context_compactor_js_1.formatMCPToolResult)(toolResultContent, MAX_TOOL_RESULT_CHARS);
326926
326907
  logs2.push(` OK: ${toolResultContent.slice(0, 200)}${toolResultContent.length > 200 ? "..." : ""}`);
326927
326908
  } catch (err) {
326928
- const errMsg2 = err instanceof Error ? err.message : String(err);
326929
- toolResultContent = `ERROR: ${errMsg2}`;
326930
- logs2.push(` Error: ${errMsg2}`);
326909
+ const errMsg3 = err instanceof Error ? err.message : String(err);
326910
+ toolResultContent = `ERROR: ${errMsg3}`;
326911
+ logs2.push(` Error: ${errMsg3}`);
326931
326912
  if (useV2) {
326932
326913
  (0, exploration_state_js_1.reduceEvent)(state2, {
326933
326914
  type: "ACTION_FAILED",
326934
326915
  route: state2.currentRoute,
326935
326916
  action: `${toolName}(${JSON.stringify(toolArgs).slice(0, 100)})`,
326936
- error: errMsg2
326917
+ error: errMsg3
326937
326918
  });
326938
326919
  }
326939
326920
  }
@@ -329779,10 +329760,10 @@ ${(0, inbox_tools_js_1.buildInboxAgentsGuidance)(inboxReg.email, inboxReg.passwo
329779
329760
  onLog: options.onLog,
329780
329761
  onProvenCommands: recordProvenCommands
329781
329762
  });
329782
- const errMsg2 = firstError?.message ?? (grokError instanceof Error ? grokError.message : "unknown error");
329763
+ const errMsg3 = firstError?.message ?? (grokError instanceof Error ? grokError.message : "unknown error");
329783
329764
  const fixPrompt = [
329784
329765
  "Your previous attempt did not produce a valid result file.",
329785
- `Error: ${errMsg2}`,
329766
+ `Error: ${errMsg3}`,
329786
329767
  `Re-read BRIEF.md and write a valid SurveyorOutput JSON to ${resultFilePath}.`,
329787
329768
  "Single JSON object. No prose. Then exit."
329788
329769
  ].join("\n");
@@ -331923,7 +331904,7 @@ ${(0, inbox_tools_js_1.buildInboxAgentsGuidance)(inboxReg.email, inboxReg.passwo
331923
331904
  log2(`First-pass discovery-result invalid or missing: ${firstError.message}`);
331924
331905
  }
331925
331906
  if (!parsedResult) {
331926
- const errMsg2 = firstError?.message ?? (grokError instanceof Error ? grokError.message : "unknown error");
331907
+ const errMsg3 = firstError?.message ?? (grokError instanceof Error ? grokError.message : "unknown error");
331927
331908
  if (planDebounceTimerRef.current) {
331928
331909
  clearTimeout(planDebounceTimerRef.current);
331929
331910
  planDebounceTimerRef.current = void 0;
@@ -331948,7 +331929,7 @@ ${(0, inbox_tools_js_1.buildInboxAgentsGuidance)(inboxReg.email, inboxReg.passwo
331948
331929
  ""
331949
331930
  ].join("\n");
331950
331931
  const fixPrompt = [
331951
- `Your previous attempt did not produce a valid result file. Error: ${errMsg2}`,
331932
+ `Your previous attempt did not produce a valid result file. Error: ${errMsg3}`,
331952
331933
  "Re-read BRIEF.md.",
331953
331934
  phaseContext,
331954
331935
  `Continue from where you left off \u2014 already verified tests are in ${testsDirPath}/`,
@@ -333577,10 +333558,10 @@ ${(0, inbox_tools_js_1.buildInboxAgentsGuidance)(inboxReg.email, inboxReg.passwo
333577
333558
  onLog: parserLog,
333578
333559
  onProvenCommands: recordProvenCommands
333579
333560
  });
333580
- const errMsg2 = firstError?.message ?? (claudeError instanceof Error ? claudeError.message : "unknown error");
333561
+ const errMsg3 = firstError?.message ?? (claudeError instanceof Error ? claudeError.message : "unknown error");
333581
333562
  const fixPrompt = [
333582
333563
  "Your previous attempt did not leave a valid result file.",
333583
- `Error: ${errMsg2}`,
333564
+ `Error: ${errMsg3}`,
333584
333565
  `Writing a valid SurveyorOutput JSON object to ${resultFilePath} is your ONLY priority now.`,
333585
333566
  "Procedure: navigate to at most 6-8 of the main pages, then IMMEDIATELY use",
333586
333567
  `the Write tool to save the SurveyorOutput JSON to ${resultFilePath}. Do this`,
@@ -339958,8 +339939,8 @@ var require_adm_zip = __commonJS({
339958
339939
  return null;
339959
339940
  }
339960
339941
  function fixPath(zipPath) {
339961
- const { join: join7, normalize, sep } = pth.posix;
339962
- return join7(pth.isAbsolute(zipPath) ? "/" : ".", normalize(sep + zipPath.split("\\").join(sep) + sep));
339942
+ const { join: join8, normalize, sep } = pth.posix;
339943
+ return join8(pth.isAbsolute(zipPath) ? "/" : ".", normalize(sep + zipPath.split("\\").join(sep) + sep));
339963
339944
  }
339964
339945
  function filenameFilter(filterfn) {
339965
339946
  if (filterfn instanceof RegExp) {
@@ -342794,11 +342775,11 @@ var require_winchars = __commonJS({
342794
342775
  var require_strip_absolute_path = __commonJS({
342795
342776
  "../../node_modules/tar/lib/strip-absolute-path.js"(exports, module) {
342796
342777
  "use strict";
342797
- var { isAbsolute, parse } = __require("path").win32;
342778
+ var { isAbsolute: isAbsolute2, parse } = __require("path").win32;
342798
342779
  module.exports = (path) => {
342799
342780
  let r = "";
342800
342781
  let parsed = parse(path);
342801
- while (isAbsolute(path) || parsed.root) {
342782
+ while (isAbsolute2(path) || parsed.root) {
342802
342783
  const root = path.charAt(0) === "/" && path.slice(0, 4) !== "//?/" ? "/" : parsed.root;
342803
342784
  path = path.slice(root.length);
342804
342785
  r += root;
@@ -345310,7 +345291,7 @@ var require_update = __commonJS({
345310
345291
  var require_opts_arg = __commonJS({
345311
345292
  "../../node_modules/tar/node_modules/mkdirp/lib/opts-arg.js"(exports, module) {
345312
345293
  "use strict";
345313
- var { promisify: promisify3 } = __require("util");
345294
+ var { promisify: promisify4 } = __require("util");
345314
345295
  var fs = __require("fs");
345315
345296
  var optsArg = (opts) => {
345316
345297
  if (!opts)
@@ -345324,9 +345305,9 @@ var require_opts_arg = __commonJS({
345324
345305
  else
345325
345306
  throw new TypeError("invalid options argument");
345326
345307
  opts.mkdir = opts.mkdir || opts.fs.mkdir || fs.mkdir;
345327
- opts.mkdirAsync = promisify3(opts.mkdir);
345308
+ opts.mkdirAsync = promisify4(opts.mkdir);
345328
345309
  opts.stat = opts.stat || opts.fs.stat || fs.stat;
345329
- opts.statAsync = promisify3(opts.stat);
345310
+ opts.statAsync = promisify4(opts.stat);
345330
345311
  opts.statSync = opts.statSync || opts.fs.statSync || fs.statSync;
345331
345312
  opts.mkdirSync = opts.mkdirSync || opts.fs.mkdirSync || fs.mkdirSync;
345332
345313
  return opts;
@@ -345579,10 +345560,10 @@ var require_chownr = __commonJS({
345579
345560
  }
345580
345561
  } : (path2, uid, gid) => lchownSync(path2, uid, gid);
345581
345562
  var nodeVersion = process.version;
345582
- var readdir2 = (path2, options, cb) => fs.readdir(path2, options, cb);
345563
+ var readdir3 = (path2, options, cb) => fs.readdir(path2, options, cb);
345583
345564
  var readdirSync = (path2, options) => fs.readdirSync(path2, options);
345584
345565
  if (/^v4\./.test(nodeVersion))
345585
- readdir2 = (path2, options, cb) => fs.readdir(path2, cb);
345566
+ readdir3 = (path2, options, cb) => fs.readdir(path2, cb);
345586
345567
  var chown = (cpath, uid, gid, cb) => {
345587
345568
  fs[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, (er) => {
345588
345569
  cb(er && er.code !== "ENOENT" ? er : null);
@@ -345609,7 +345590,7 @@ var require_chownr = __commonJS({
345609
345590
  }
345610
345591
  };
345611
345592
  var chownr = (p, uid, gid, cb) => {
345612
- readdir2(p, { withFileTypes: true }, (er, children) => {
345593
+ readdir3(p, { withFileTypes: true }, (er, children) => {
345613
345594
  if (er) {
345614
345595
  if (er.code === "ENOENT")
345615
345596
  return cb();
@@ -345883,7 +345864,7 @@ var require_path_reservations = __commonJS({
345883
345864
  var assert = __require("assert");
345884
345865
  var normalize = require_normalize_unicode();
345885
345866
  var stripSlashes = require_strip_trailing_slashes();
345886
- var { join: join7 } = __require("path");
345867
+ var { join: join8 } = __require("path");
345887
345868
  var platform3 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
345888
345869
  var isWindows = platform3 === "win32";
345889
345870
  module.exports = () => {
@@ -345892,7 +345873,7 @@ var require_path_reservations = __commonJS({
345892
345873
  const getDirs = (path) => {
345893
345874
  const dirs = path.split("/").slice(0, -1).reduce((set, path2) => {
345894
345875
  if (set.length) {
345895
- path2 = join7(set[set.length - 1], path2);
345876
+ path2 = join8(set[set.length - 1], path2);
345896
345877
  }
345897
345878
  set.push(path2 || "/");
345898
345879
  return set;
@@ -345960,7 +345941,7 @@ var require_path_reservations = __commonJS({
345960
345941
  };
345961
345942
  const reserve = (paths, fn) => {
345962
345943
  paths = isWindows ? ["win32 parallelization disabled"] : paths.map((p) => {
345963
- return stripSlashes(join7(normalize(p))).toLowerCase();
345944
+ return stripSlashes(join8(normalize(p))).toLowerCase();
345964
345945
  });
345965
345946
  const dirs = new Set(
345966
345947
  paths.map((path) => getDirs(path)).reduce((a, b) => a.concat(b))
@@ -350175,7 +350156,7 @@ var require_process_inventory = __commonJS({
350175
350156
  var node_child_process_1 = __require("child_process");
350176
350157
  var node_os_1 = __importDefault(__require("os"));
350177
350158
  var node_util_1 = __require("util");
350178
- var execFileAsync3 = (0, node_util_1.promisify)(node_child_process_1.execFile);
350159
+ var execFileAsync4 = (0, node_util_1.promisify)(node_child_process_1.execFile);
350179
350160
  var DEFAULT_LIMIT = 60;
350180
350161
  var DEFAULT_TIMEOUT_MS2 = 800;
350181
350162
  var MAX_COMMAND_LENGTH = 320;
@@ -350237,7 +350218,7 @@ var require_process_inventory = __commonJS({
350237
350218
  const timer = setTimeout(() => controller.abort(), timeoutMs);
350238
350219
  timer.unref?.();
350239
350220
  try {
350240
- const { stdout } = await execFileAsync3(command, args, {
350221
+ const { stdout } = await execFileAsync4(command, args, {
350241
350222
  maxBuffer: 1024 * 1024,
350242
350223
  signal: controller.signal
350243
350224
  });
@@ -351373,7 +351354,7 @@ var require_package4 = __commonJS({
351373
351354
  "package.json"(exports, module) {
351374
351355
  module.exports = {
351375
351356
  name: "@validate.qa/runner",
351376
- version: "1.0.68",
351357
+ version: "1.0.69",
351377
351358
  description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
351378
351359
  bin: {
351379
351360
  "validate-runner": "dist/cli.js"
@@ -351460,7 +351441,7 @@ function getConfigDir() {
351460
351441
  }
351461
351442
 
351462
351443
  // src/runner.ts
351463
- import { join as join6 } from "path";
351444
+ import { join as join7 } from "path";
351464
351445
  import { homedir as homedir3 } from "os";
351465
351446
  import { createHash } from "crypto";
351466
351447
 
@@ -352055,6 +352036,66 @@ function resetServerState(port = APPIUM_PORT, restartCount = 0) {
352055
352036
  };
352056
352037
  }
352057
352038
 
352039
+ // src/services/mobile/ios-simulator-reset.ts
352040
+ import { execFile } from "child_process";
352041
+ import { promisify } from "util";
352042
+ import { readdir, rm } from "fs/promises";
352043
+ import { join as join3, isAbsolute } from "path";
352044
+ var execFileAsync = promisify(execFile);
352045
+ var SIMCTL_TIMEOUT_MS = 15e3;
352046
+ var SIMULATOR_UDID_RE = /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i;
352047
+ function isIosSimulatorUdid(udid) {
352048
+ return typeof udid === "string" && SIMULATOR_UDID_RE.test(udid.trim());
352049
+ }
352050
+ function errMsg(err) {
352051
+ return err instanceof Error ? err.message : String(err);
352052
+ }
352053
+ async function simctl(args) {
352054
+ const { stdout } = await execFileAsync("xcrun", ["simctl", ...args], {
352055
+ timeout: SIMCTL_TIMEOUT_MS,
352056
+ encoding: "utf-8"
352057
+ });
352058
+ return stdout.trim();
352059
+ }
352060
+ async function clearIosSimulatorAppData(udid, bundleId, env = process.env) {
352061
+ if (!isIosSimulatorUdid(udid)) {
352062
+ return { ok: false, detail: `'${udid}' is not a simulator UDID \u2014 physical iOS devices cannot be cleared` };
352063
+ }
352064
+ let container;
352065
+ try {
352066
+ container = await simctl(["get_app_container", udid, bundleId, "data"]);
352067
+ } catch (err) {
352068
+ return { ok: false, detail: `simctl get_app_container failed (app not installed?): ${errMsg(err)}` };
352069
+ }
352070
+ if (!isAbsolute(container) || !container.includes("/Containers/Data/Application/")) {
352071
+ return { ok: false, detail: `unexpected app container path: ${container}` };
352072
+ }
352073
+ try {
352074
+ await simctl(["spawn", udid, "defaults", "delete", bundleId]);
352075
+ } catch {
352076
+ }
352077
+ try {
352078
+ const entries = await readdir(container);
352079
+ for (const entry of entries) {
352080
+ await rm(join3(container, entry), { recursive: true, force: true });
352081
+ }
352082
+ } catch (err) {
352083
+ return { ok: false, detail: `app container wipe failed: ${errMsg(err)}` };
352084
+ }
352085
+ const keychainPolicy = (env.VALIDATEQA_MOBILE_IOS_KEYCHAIN_RESET ?? "").trim().toLowerCase();
352086
+ if (keychainPolicy !== "never") {
352087
+ try {
352088
+ await simctl(["keychain", udid, "reset"]);
352089
+ } catch (err) {
352090
+ return {
352091
+ ok: false,
352092
+ detail: `simulator keychain reset failed (${errMsg(err)}) \u2014 app data was wiped but keychain items (e.g. auth tokens) may survive. Set VALIDATEQA_MOBILE_IOS_KEYCHAIN_RESET=never to skip the keychain step.`
352093
+ };
352094
+ }
352095
+ }
352096
+ return { ok: true };
352097
+ }
352098
+
352058
352099
  // src/services/mobile/appium-actions.ts
352059
352100
  var APPIUM_REQUEST_TIMEOUT_MS = 1e4;
352060
352101
  var APPIUM_WEBVIEW_CONTEXT_TIMEOUT_MS = 12e4;
@@ -352237,9 +352278,12 @@ async function tapAt(sessionPath, bounds) {
352237
352278
  }
352238
352279
  }
352239
352280
  function shouldResetMobileAppData(platform3, deviceId, env = process.env) {
352240
- if (platform3 !== "ANDROID") return false;
352241
352281
  const policy = (env.VALIDATEQA_MOBILE_APP_RESET ?? "").trim().toLowerCase();
352242
352282
  if (policy === "never") return false;
352283
+ if (platform3 === "IOS") {
352284
+ return isIosSimulatorUdid(deviceId);
352285
+ }
352286
+ if (platform3 !== "ANDROID") return false;
352243
352287
  if (policy === "always") return true;
352244
352288
  return !!deviceId && deviceId.startsWith("emulator-");
352245
352289
  }
@@ -353120,10 +353164,10 @@ async function pressHome(sessionPath, platform3) {
353120
353164
 
353121
353165
  // src/services/appium-executor.ts
353122
353166
  var import_runner_core8 = __toESM(require_dist2());
353123
- import { execFile as execFile2 } from "child_process";
353124
- import { promisify as promisify2 } from "util";
353167
+ import { execFile as execFile3 } from "child_process";
353168
+ import { promisify as promisify3 } from "util";
353125
353169
  import { tmpdir as tmpdir3 } from "os";
353126
- import { join as join4 } from "path";
353170
+ import { join as join5 } from "path";
353127
353171
 
353128
353172
  // src/services/device-discovery.ts
353129
353173
  import { execFileSync as execFileSync2 } from "child_process";
@@ -353430,9 +353474,6 @@ function countReadyDevicesByKind(platform3, snapshot = getCachedDeviceSnapshot()
353430
353474
  function selectFreeDevice(platform3, leased, snapshot = getCachedDeviceSnapshot(), requirements = {}) {
353431
353475
  const ready = snapshot.filter((d) => d.platform === platform3 && d.isAvailable && d.state === "BOOTED");
353432
353476
  if (ready.length === 0) return { kind: "busy", reason: `no booted ${platform3} device` };
353433
- if (requirements.resetPolicy === "CLEAR_APP_DATA" && platform3 !== "ANDROID") {
353434
- return { kind: "unsatisfiable", reason: "CLEAR_APP_DATA is unsupported on iOS" };
353435
- }
353436
353477
  let candidates = requirements.resetPolicy === "CLEAR_APP_DATA" ? ready.filter((d) => !d.isPhysical) : [...ready];
353437
353478
  const appId = requirements.appId?.trim();
353438
353479
  if (appId) {
@@ -353481,23 +353522,23 @@ function pickFreeDevice(platform3, leased, snapshot = getCachedDeviceSnapshot(),
353481
353522
 
353482
353523
  // src/services/mobile/mobile-network-capture.ts
353483
353524
  var import_runner_core7 = __toESM(require_dist2());
353484
- import { execFile, execFileSync as execFileSync3 } from "child_process";
353525
+ import { execFile as execFile2, execFileSync as execFileSync3 } from "child_process";
353485
353526
  import { randomUUID } from "crypto";
353486
- import { mkdtemp, rm, writeFile, readFile, readdir, mkdir, unlink } from "fs/promises";
353527
+ import { mkdtemp, rm as rm2, writeFile, readFile, readdir as readdir2, mkdir, unlink } from "fs/promises";
353487
353528
  import { tmpdir as tmpdir2, networkInterfaces } from "os";
353488
- import { join as join3 } from "path";
353489
- import { promisify } from "util";
353529
+ import { join as join4 } from "path";
353530
+ import { promisify as promisify2 } from "util";
353490
353531
  import {
353491
353532
  getLocal,
353492
353533
  generateCACertificate
353493
353534
  } from "mockttp";
353494
- var execFileAsync = promisify(execFile);
353535
+ var execFileAsync2 = promisify2(execFile2);
353495
353536
  var MAX_CALLS = 1e3;
353496
353537
  var MAX_BODY_CAPTURE_BYTES = 64 * 1024;
353497
353538
  var DEVICE_CMD_TIMEOUT_MS = 15e3;
353498
353539
  var CA_KEY_BITS = 2048;
353499
353540
  var ANDROID_EMULATOR_HOST_LOOPBACK = "10.0.2.2";
353500
- var PENDING_CLEANUP_DIR = join3(tmpdir2(), "validateqa-mobile-capture-pending");
353541
+ var PENDING_CLEANUP_DIR = join4(tmpdir2(), "validateqa-mobile-capture-pending");
353501
353542
  var IOS_SIM_HOST_PROXY_ENV = "VALIDATEQA_MOBILE_IOS_SIM_HOST_PROXY";
353502
353543
  var STALE_MARKER_RECONCILE_MS = 6 * 60 * 60 * 1e3;
353503
353544
  function envFlag(name) {
@@ -353679,7 +353720,7 @@ var MitmProxy = class {
353679
353720
  try {
353680
353721
  await server3.stop();
353681
353722
  } catch (err) {
353682
- this.onLog?.(`[mobile-net] proxy stop error (ignored): ${errMsg(err)}`);
353723
+ this.onLog?.(`[mobile-net] proxy stop error (ignored): ${errMsg2(err)}`);
353683
353724
  }
353684
353725
  this.onLog?.(
353685
353726
  `[mobile-net] proxy closed: ${this.calls.length} calls captured` + (this.dropped > 0 ? ` (${this.dropped} dropped over cap)` : "")
@@ -353704,7 +353745,7 @@ var MitmProxy = class {
353704
353745
  isHttps
353705
353746
  });
353706
353747
  } catch (err) {
353707
- this.onLog?.(`[mobile-net] request capture error (ignored): ${errMsg(err)}`);
353748
+ this.onLog?.(`[mobile-net] request capture error (ignored): ${errMsg2(err)}`);
353708
353749
  }
353709
353750
  }
353710
353751
  // ── response → finalize the matched request ───────────────────────────────
@@ -353747,7 +353788,7 @@ var MitmProxy = class {
353747
353788
  intercepted: true
353748
353789
  });
353749
353790
  } catch (err) {
353750
- this.onLog?.(`[mobile-net] response capture error (ignored): ${errMsg(err)}`);
353791
+ this.onLog?.(`[mobile-net] response capture error (ignored): ${errMsg2(err)}`);
353751
353792
  }
353752
353793
  }
353753
353794
  // ── tls-client-error → record WHY interception failed ─────────────────────
@@ -353824,17 +353865,17 @@ async function generateCaCert(dir, onLog) {
353824
353865
  // rely on stop() to remove the cert from the device promptly after the run
353825
353866
  // rather than on a short expiry.
353826
353867
  });
353827
- const caCertPath = join3(dir, "validateqa-mobile-ca.pem");
353868
+ const caCertPath = join4(dir, "validateqa-mobile-ca.pem");
353828
353869
  await writeFile(caCertPath, cert, "utf-8");
353829
353870
  return { caCertPath, caKeyPem: key, caCertPem: cert };
353830
353871
  } catch (err) {
353831
- onLog?.(`[mobile-net] CA generation failed; running plaintext-only: ${errMsg(err)}`);
353872
+ onLog?.(`[mobile-net] CA generation failed; running plaintext-only: ${errMsg2(err)}`);
353832
353873
  return null;
353833
353874
  }
353834
353875
  }
353835
353876
  async function androidReadProxy(deviceId) {
353836
353877
  try {
353837
- const { stdout } = await execFileAsync(
353878
+ const { stdout } = await execFileAsync2(
353838
353879
  "adb",
353839
353880
  ["-s", deviceId, "shell", "settings", "get", "global", "http_proxy"],
353840
353881
  { timeout: DEVICE_CMD_TIMEOUT_MS }
@@ -353847,33 +353888,33 @@ async function androidReadProxy(deviceId) {
353847
353888
  }
353848
353889
  async function androidSetProxy(deviceId, hostPort, onLog) {
353849
353890
  try {
353850
- await execFileAsync(
353891
+ await execFileAsync2(
353851
353892
  "adb",
353852
353893
  ["-s", deviceId, "shell", "settings", "put", "global", "http_proxy", hostPort],
353853
353894
  { timeout: DEVICE_CMD_TIMEOUT_MS }
353854
353895
  );
353855
353896
  return true;
353856
353897
  } catch (err) {
353857
- onLog?.(`[mobile-net] adb set http_proxy failed: ${errMsg(err)}`);
353898
+ onLog?.(`[mobile-net] adb set http_proxy failed: ${errMsg2(err)}`);
353858
353899
  return false;
353859
353900
  }
353860
353901
  }
353861
353902
  async function androidClearProxy(deviceId, originalProxy, onLog) {
353862
353903
  const restoreValue = originalProxy && originalProxy.length > 0 ? originalProxy : ":0";
353863
353904
  try {
353864
- await execFileAsync(
353905
+ await execFileAsync2(
353865
353906
  "adb",
353866
353907
  ["-s", deviceId, "shell", "settings", "put", "global", "http_proxy", restoreValue],
353867
353908
  { timeout: DEVICE_CMD_TIMEOUT_MS }
353868
353909
  );
353869
353910
  } catch (err) {
353870
- onLog?.(`[mobile-net] adb restore http_proxy failed: ${errMsg(err)}`);
353911
+ onLog?.(`[mobile-net] adb restore http_proxy failed: ${errMsg2(err)}`);
353871
353912
  }
353872
353913
  }
353873
353914
  async function androidInstallCa(deviceId, caCertPath) {
353874
353915
  const devicePath = "/sdcard/validateqa-mobile-ca.pem";
353875
353916
  try {
353876
- await execFileAsync("adb", ["-s", deviceId, "push", caCertPath, devicePath], {
353917
+ await execFileAsync2("adb", ["-s", deviceId, "push", caCertPath, devicePath], {
353877
353918
  timeout: DEVICE_CMD_TIMEOUT_MS
353878
353919
  });
353879
353920
  return {
@@ -353881,23 +353922,23 @@ async function androidInstallCa(deviceId, caCertPath) {
353881
353922
  note: `CA copied from ${caCertPath} to ${devicePath} but NOT auto-trusted: Android 7+ apps must opt into user CAs (network_security_config), and the system store requires a rooted/writable image. HTTPS bodies decrypt only for apps that trust user CAs; pinned apps never decrypt.`
353882
353923
  };
353883
353924
  } catch (err) {
353884
- return { installed: false, note: `CA push failed: ${errMsg(err)}` };
353925
+ return { installed: false, note: `CA push failed: ${errMsg2(err)}` };
353885
353926
  }
353886
353927
  }
353887
353928
  async function androidRemoveCa(deviceId, onLog) {
353888
353929
  try {
353889
- await execFileAsync(
353930
+ await execFileAsync2(
353890
353931
  "adb",
353891
353932
  ["-s", deviceId, "shell", "rm", "-f", "/sdcard/validateqa-mobile-ca.pem"],
353892
353933
  { timeout: DEVICE_CMD_TIMEOUT_MS }
353893
353934
  );
353894
353935
  } catch (err) {
353895
- onLog?.(`[mobile-net] adb remove CA failed: ${errMsg(err)}`);
353936
+ onLog?.(`[mobile-net] adb remove CA failed: ${errMsg2(err)}`);
353896
353937
  }
353897
353938
  }
353898
353939
  async function iosSimTrustCa(deviceId, caCertPath) {
353899
353940
  try {
353900
- await execFileAsync("xcrun", ["simctl", "keychain", deviceId, "add-root-cert", caCertPath], {
353941
+ await execFileAsync2("xcrun", ["simctl", "keychain", deviceId, "add-root-cert", caCertPath], {
353901
353942
  timeout: DEVICE_CMD_TIMEOUT_MS
353902
353943
  });
353903
353944
  return {
@@ -353905,18 +353946,18 @@ async function iosSimTrustCa(deviceId, caCertPath) {
353905
353946
  note: "CA added to simulator keychain (add-root-cert). NOTE: the simulator has no per-app HTTP proxy setting controllable via simctl \u2014 it shares the host network, so only traffic that actually routes through the proxy is intercepted; pinned apps still never decrypt."
353906
353947
  };
353907
353948
  } catch (err) {
353908
- return { trusted: false, note: `simctl add-root-cert failed: ${errMsg(err)}` };
353949
+ return { trusted: false, note: `simctl add-root-cert failed: ${errMsg2(err)}` };
353909
353950
  }
353910
353951
  }
353911
353952
  async function iosSimRemoveCa(deviceId, onLog) {
353912
353953
  if (process.env.VALIDATEQA_MOBILE_SIM_KEYCHAIN_RESET === "1") {
353913
353954
  try {
353914
- await execFileAsync("xcrun", ["simctl", "keychain", deviceId, "reset"], {
353955
+ await execFileAsync2("xcrun", ["simctl", "keychain", deviceId, "reset"], {
353915
353956
  timeout: DEVICE_CMD_TIMEOUT_MS
353916
353957
  });
353917
353958
  onLog?.(`[mobile-net] simctl keychain reset run for ${deviceId} (opt-in; cleared ALL sim certs).`);
353918
353959
  } catch (err) {
353919
- onLog?.(`[mobile-net] simctl keychain reset failed: ${errMsg(err)}`);
353960
+ onLog?.(`[mobile-net] simctl keychain reset failed: ${errMsg2(err)}`);
353920
353961
  }
353921
353962
  return;
353922
353963
  }
@@ -353924,7 +353965,7 @@ async function iosSimRemoveCa(deviceId, onLog) {
353924
353965
  `[mobile-net] NOTE: the validate.qa capture CA remains TRUSTED in simulator ${deviceId}. simctl has no scoped remove (only a full keychain reset that wipes ALL certs), so we leave the single CA in place. Use a throwaway simulator, or set VALIDATEQA_MOBILE_SIM_KEYCHAIN_RESET=1 to reset the whole sim keychain on teardown.`
353925
353966
  );
353926
353967
  }
353927
- function errMsg(err) {
353968
+ function errMsg2(err) {
353928
353969
  return err instanceof Error ? err.message : String(err);
353929
353970
  }
353930
353971
  function isProcessAlive(pid) {
@@ -354015,7 +354056,7 @@ async function iosSimConfigureHostProxy(host, port, onLog) {
354015
354056
  let service;
354016
354057
  let prior;
354017
354058
  try {
354018
- const { stdout: order } = await execFileAsync("networksetup", ["-listnetworkserviceorder"], {
354059
+ const { stdout: order } = await execFileAsync2("networksetup", ["-listnetworkserviceorder"], {
354019
354060
  timeout: DEVICE_CMD_TIMEOUT_MS
354020
354061
  });
354021
354062
  const svc = parsePrimaryNetworkService(order);
@@ -354024,26 +354065,26 @@ async function iosSimConfigureHostProxy(host, port, onLog) {
354024
354065
  }
354025
354066
  service = svc;
354026
354067
  const [{ stdout: webOut }, { stdout: secureOut }] = await Promise.all([
354027
- execFileAsync("networksetup", ["-getwebproxy", service], { timeout: DEVICE_CMD_TIMEOUT_MS }),
354028
- execFileAsync("networksetup", ["-getsecurewebproxy", service], { timeout: DEVICE_CMD_TIMEOUT_MS })
354068
+ execFileAsync2("networksetup", ["-getwebproxy", service], { timeout: DEVICE_CMD_TIMEOUT_MS }),
354069
+ execFileAsync2("networksetup", ["-getsecurewebproxy", service], { timeout: DEVICE_CMD_TIMEOUT_MS })
354029
354070
  ]);
354030
354071
  prior = { web: parseWebProxyState(webOut), secure: parseWebProxyState(secureOut) };
354031
354072
  for (const args of buildHostProxySetCommands(service, host, port)) {
354032
- await execFileAsync("networksetup", args, { timeout: DEVICE_CMD_TIMEOUT_MS });
354073
+ await execFileAsync2("networksetup", args, { timeout: DEVICE_CMD_TIMEOUT_MS });
354033
354074
  }
354034
354075
  onLog?.(`[mobile-net] macOS host proxy set on "${service}" \u2192 ${host}:${port} (iOS simulator shares the host network).`);
354035
354076
  return { configured: true, service, prior };
354036
354077
  } catch (err) {
354037
354078
  if (service && prior) {
354038
- onLog?.(`[mobile-net] host proxy set failed mid-way \u2014 rolling back "${service}" to prior state: ${errMsg(err)}`);
354079
+ onLog?.(`[mobile-net] host proxy set failed mid-way \u2014 rolling back "${service}" to prior state: ${errMsg2(err)}`);
354039
354080
  for (const args of buildHostProxyRestoreCommands(service, prior)) {
354040
354081
  try {
354041
- await execFileAsync("networksetup", args, { timeout: DEVICE_CMD_TIMEOUT_MS });
354082
+ await execFileAsync2("networksetup", args, { timeout: DEVICE_CMD_TIMEOUT_MS });
354042
354083
  } catch {
354043
354084
  }
354044
354085
  }
354045
354086
  }
354046
- return { configured: false, reason: `iOS simulator host proxy could not be configured: ${errMsg(err)}` };
354087
+ return { configured: false, reason: `iOS simulator host proxy could not be configured: ${errMsg2(err)}` };
354047
354088
  }
354048
354089
  }
354049
354090
  async function iosSimRestoreHostProxy(service, prior, onLog) {
@@ -354053,9 +354094,9 @@ async function iosSimRestoreHostProxy(service, prior, onLog) {
354053
354094
  };
354054
354095
  for (const args of buildHostProxyRestoreCommands(service, restore)) {
354055
354096
  try {
354056
- await execFileAsync("networksetup", args, { timeout: DEVICE_CMD_TIMEOUT_MS });
354097
+ await execFileAsync2("networksetup", args, { timeout: DEVICE_CMD_TIMEOUT_MS });
354057
354098
  } catch (err) {
354058
- onLog?.(`[mobile-net] networksetup restore (${args[0]}) failed: ${errMsg(err)}`);
354099
+ onLog?.(`[mobile-net] networksetup restore (${args[0]}) failed: ${errMsg2(err)}`);
354059
354100
  }
354060
354101
  }
354061
354102
  onLog?.(`[mobile-net] macOS host proxy restored on "${service}".`);
@@ -354063,7 +354104,7 @@ async function iosSimRestoreHostProxy(service, prior, onLog) {
354063
354104
  var activeCleanups = /* @__PURE__ */ new Map();
354064
354105
  var exitHandlersInstalled = false;
354065
354106
  function markerPath(sessionKey) {
354066
- return join3(PENDING_CLEANUP_DIR, `${sessionKey}.json`);
354107
+ return join4(PENDING_CLEANUP_DIR, `${sessionKey}.json`);
354067
354108
  }
354068
354109
  async function writePendingCleanup(sessionKey, marker) {
354069
354110
  try {
@@ -354107,7 +354148,7 @@ function restoreDeviceSync(marker, onLog) {
354107
354148
  async function reconcilePendingCleanups(onLog) {
354108
354149
  let files;
354109
354150
  try {
354110
- files = await readdir(PENDING_CLEANUP_DIR);
354151
+ files = await readdir2(PENDING_CLEANUP_DIR);
354111
354152
  } catch {
354112
354153
  return;
354113
354154
  }
@@ -354115,7 +354156,7 @@ async function reconcilePendingCleanups(onLog) {
354115
354156
  if (!file.endsWith(".json")) continue;
354116
354157
  const sessionKey = file.slice(0, -".json".length);
354117
354158
  if (activeCleanups.has(sessionKey)) continue;
354118
- const full = join3(PENDING_CLEANUP_DIR, file);
354159
+ const full = join4(PENDING_CLEANUP_DIR, file);
354119
354160
  try {
354120
354161
  const raw = await readFile(full, "utf-8");
354121
354162
  const marker = JSON.parse(raw);
@@ -354168,7 +354209,7 @@ async function startMobileNetworkCapture(options) {
354168
354209
  let caKeyPem = null;
354169
354210
  let caCertPem = null;
354170
354211
  try {
354171
- tmpDir = await mkdtemp(join3(tmpdir2(), "validateqa-mobile-ca-"));
354212
+ tmpDir = await mkdtemp(join4(tmpdir2(), "validateqa-mobile-ca-"));
354172
354213
  const ca = await generateCaCert(tmpDir, onLog);
354173
354214
  if (ca) {
354174
354215
  caCertPath = ca.caCertPath;
@@ -354176,7 +354217,7 @@ async function startMobileNetworkCapture(options) {
354176
354217
  caCertPem = ca.caCertPem;
354177
354218
  }
354178
354219
  } catch (err) {
354179
- onLog?.(`[mobile-net] CA setup degraded: ${errMsg(err)}`);
354220
+ onLog?.(`[mobile-net] CA setup degraded: ${errMsg2(err)}`);
354180
354221
  }
354181
354222
  if (!caKeyPem || !caCertPem) {
354182
354223
  try {
@@ -354184,9 +354225,9 @@ async function startMobileNetworkCapture(options) {
354184
354225
  caKeyPem = fallback.key;
354185
354226
  caCertPem = fallback.cert;
354186
354227
  } catch (err) {
354187
- onLog?.(`[mobile-net] fatal CA failure, cannot start proxy: ${errMsg(err)}`);
354228
+ onLog?.(`[mobile-net] fatal CA failure, cannot start proxy: ${errMsg2(err)}`);
354188
354229
  if (tmpDir) {
354189
- await rm(tmpDir, { recursive: true, force: true }).catch(() => void 0);
354230
+ await rm2(tmpDir, { recursive: true, force: true }).catch(() => void 0);
354190
354231
  }
354191
354232
  throw err instanceof Error ? err : new Error(String(err));
354192
354233
  }
@@ -354241,7 +354282,7 @@ async function startMobileNetworkCapture(options) {
354241
354282
  }
354242
354283
  }
354243
354284
  } catch (err) {
354244
- wiringReason = `device wiring degraded: ${errMsg(err)}`;
354285
+ wiringReason = `device wiring degraded: ${errMsg2(err)}`;
354245
354286
  onLog?.(`[mobile-net] ${wiringReason}`);
354246
354287
  }
354247
354288
  if (proxySet || caInstalledOnDevice || hostProxySet) {
@@ -354330,9 +354371,9 @@ async function startMobileNetworkCapture(options) {
354330
354371
  await proxy.close();
354331
354372
  if (capturedTmpDir) {
354332
354373
  try {
354333
- await rm(capturedTmpDir, { recursive: true, force: true });
354374
+ await rm2(capturedTmpDir, { recursive: true, force: true });
354334
354375
  } catch (err) {
354335
- onLog?.(`[mobile-net] temp CA cleanup failed: ${errMsg(err)}`);
354376
+ onLog?.(`[mobile-net] temp CA cleanup failed: ${errMsg2(err)}`);
354336
354377
  }
354337
354378
  }
354338
354379
  const apiCount = finalCalls.filter((c) => c.isApi).length;
@@ -354369,7 +354410,7 @@ var WD_SESSION_CREATE_TIMEOUT_MS = 12e4;
354369
354410
  var WD_ACTION_TIMEOUT_MS = 3e4;
354370
354411
  var APPIUM_DRIVER_DEAD_RE2 = /instrumentation process is not running|cannot be proxied to UiAutomator2 server|socket hang up|ECONNRESET/i;
354371
354412
  var MAX_SESSION_CREATE_ATTEMPTS = 3;
354372
- var execFileAsync2 = promisify2(execFile2);
354413
+ var execFileAsync3 = promisify3(execFile3);
354373
354414
  var MOBILE_TEXT_ATTRIBUTE_NAMES = ["text", "content-desc", "contentDescription", "name", "label", "value"];
354374
354415
  var WebDriverTransportError = class extends Error {
354375
354416
  isTransport = true;
@@ -354498,11 +354539,11 @@ function mobileParallelSessionCapabilities(platform3, portOffset = 0, deviceId =
354498
354539
  return { "appium:systemPort": readPort("APPIUM_SYSTEM_PORT", 8200) };
354499
354540
  }
354500
354541
  const safeDevice = deviceId.replace(/[^A-Za-z0-9._-]+/g, "-") || `device-${offset}`;
354501
- const derivedDataRoot = env.APPIUM_DERIVED_DATA_PATH?.trim() || join4(tmpdir3(), "validateqa-wda");
354542
+ const derivedDataRoot = env.APPIUM_DERIVED_DATA_PATH?.trim() || join5(tmpdir3(), "validateqa-wda");
354502
354543
  return {
354503
354544
  "appium:wdaLocalPort": readPort("APPIUM_WDA_LOCAL_PORT", 8100),
354504
354545
  "appium:mjpegServerPort": readPort("APPIUM_MJPEG_SERVER_PORT", 9100),
354505
- "appium:derivedDataPath": join4(derivedDataRoot, safeDevice)
354546
+ "appium:derivedDataPath": join5(derivedDataRoot, safeDevice)
354506
354547
  };
354507
354548
  }
354508
354549
  function isAndroidSystemPortBusyError(error2) {
@@ -354586,7 +354627,7 @@ async function removeStaleAndroidSystemPortForward(target, error2) {
354586
354627
  const args = androidSystemPortForwardRemovalArgs(target, error2);
354587
354628
  if (!args) return false;
354588
354629
  try {
354589
- await execFileAsync2("adb", args, { timeout: 1e4, windowsHide: true });
354630
+ await execFileAsync3("adb", args, { timeout: 1e4, windowsHide: true });
354590
354631
  return true;
354591
354632
  } catch {
354592
354633
  return false;
@@ -355060,7 +355101,7 @@ async function prepareAndroidInputEnvironment(deviceId) {
355060
355101
  ];
355061
355102
  for (const [namespace, key, value] of settingsToDisable) {
355062
355103
  try {
355063
- await execFileAsync2("adb", ["-s", deviceId, "shell", "settings", "put", namespace, key, value], {
355104
+ await execFileAsync3("adb", ["-s", deviceId, "shell", "settings", "put", namespace, key, value], {
355064
355105
  timeout: 8e3,
355065
355106
  windowsHide: true
355066
355107
  });
@@ -355072,14 +355113,14 @@ async function sendAndroidTextToFocusedInput(deviceId, value) {
355072
355113
  if (!deviceId) {
355073
355114
  throw new Error("Android keyboard fallback requires a device id");
355074
355115
  }
355075
- await execFileAsync2("adb", ["-s", deviceId, "shell", "input", "text", encodeAndroidInputText(value)], {
355116
+ await execFileAsync3("adb", ["-s", deviceId, "shell", "input", "text", encodeAndroidInputText(value)], {
355076
355117
  timeout: 1e4,
355077
355118
  windowsHide: true
355078
355119
  });
355079
355120
  }
355080
355121
  async function pressAndroidKeyboardEnter(deviceId) {
355081
355122
  if (!deviceId) throw new Error("Android keyboard Enter fallback requires a device id");
355082
- await execFileAsync2("adb", ["-s", deviceId, "shell", "input", "keyevent", "66"], {
355123
+ await execFileAsync3("adb", ["-s", deviceId, "shell", "input", "keyevent", "66"], {
355083
355124
  timeout: 1e4,
355084
355125
  windowsHide: true
355085
355126
  });
@@ -355600,7 +355641,14 @@ async function executeMobileTest(options) {
355600
355641
  const resetAllowed = shouldResetMobileAppData(options.target.platform, selectedDevice.id);
355601
355642
  if (resetAllowed) {
355602
355643
  try {
355603
- await executeMobileScript(sessionId, "mobile: clearApp", [{ appId: options.target.appId }]);
355644
+ if (options.target.platform === "IOS") {
355645
+ await terminateTargetApp(sessionId, "IOS", options.target.appId).catch(() => {
355646
+ });
355647
+ const wiped = await clearIosSimulatorAppData(selectedDevice.id, options.target.appId);
355648
+ if (!wiped.ok) throw new Error(wiped.detail);
355649
+ } else {
355650
+ await executeMobileScript(sessionId, "mobile: clearApp", [{ appId: options.target.appId }]);
355651
+ }
355604
355652
  clearedAppDataBeforeLaunch = true;
355605
355653
  log2(resetPolicy === "CLEAR_APP_DATA" ? "App data cleared before launch (target resetPolicy=CLEAR_APP_DATA)." : "App data cleared before launch (test was recorded from a cold logged-out state).");
355606
355654
  } catch (err) {
@@ -356261,7 +356309,7 @@ import { createServer } from "http";
356261
356309
  import { execFileSync as execFileSync4 } from "child_process";
356262
356310
  import { randomBytes, timingSafeEqual } from "crypto";
356263
356311
  import { homedir as homedir2 } from "os";
356264
- import { join as join5, dirname } from "path";
356312
+ import { join as join6, dirname } from "path";
356265
356313
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync3 } from "fs";
356266
356314
  init_dist();
356267
356315
  var MOBILE_BRIDGE_PORT2 = 9515;
@@ -356368,7 +356416,7 @@ async function getWindowSize3() {
356368
356416
  }
356369
356417
  var BRIDGE_PROCESS_MARKER = "validateqa-mobile-bridge";
356370
356418
  var BRIDGE_PORT = MOBILE_BRIDGE_PORT2;
356371
- var BRIDGE_PID_FILE = join5(homedir2(), ".validate.qa", "mobile-bridge.pid");
356419
+ var BRIDGE_PID_FILE = join6(homedir2(), ".validate.qa", "mobile-bridge.pid");
356372
356420
  function readBridgePidFile() {
356373
356421
  try {
356374
356422
  const raw = readFileSync2(BRIDGE_PID_FILE, "utf-8").trim();
@@ -357506,6 +357554,7 @@ var MobileBridgeDriver = class {
357506
357554
  sessionId;
357507
357555
  platform;
357508
357556
  appId;
357557
+ deviceId;
357509
357558
  allowAppReset;
357510
357559
  recoverSession;
357511
357560
  maxSessionRecoveries;
@@ -357526,6 +357575,7 @@ var MobileBridgeDriver = class {
357526
357575
  this.sessionId = config.sessionId;
357527
357576
  this.platform = config.platform;
357528
357577
  this.appId = config.appId;
357578
+ this.deviceId = config.deviceId;
357529
357579
  this.allowAppReset = config.allowAppReset === true;
357530
357580
  this.recoverSession = config.recoverSession;
357531
357581
  this.maxSessionRecoveries = Math.max(0, Math.floor(config.maxSessionRecoveries ?? 1));
@@ -357823,17 +357873,34 @@ var MobileBridgeDriver = class {
357823
357873
  });
357824
357874
  }
357825
357875
  /**
357826
- * Deterministic state reset: clear app data + relaunch (Android emulators
357827
- * only, per the runner-computed allowAppReset policy). Returns false when
357828
- * unsupported/disallowed or when clearApp throws — callers fall back to
357829
- * relaunch(), preserving today's behavior. On success, waits for cold-boot
357830
- * content so recording and replay agree on the true entry screen.
357876
+ * Deterministic state reset: clear app data + relaunch, per the
357877
+ * runner-computed allowAppReset policy (Android emulators / iOS simulators
357878
+ * only — a personal physical device is never wiped). Android clears via
357879
+ * `mobile: clearApp`; iOS terminates the app and wipes its simulator data
357880
+ * container + keychain through `simctl` (see ios-simulator-reset.ts).
357881
+ * Returns false when unsupported/disallowed or when the clear fails —
357882
+ * callers fall back to relaunch(), preserving today's behavior. On success,
357883
+ * waits for cold-boot content so recording and replay agree on the true
357884
+ * entry screen.
357831
357885
  */
357832
357886
  async resetAppState() {
357833
- if (!this.allowAppReset || this.platform !== "ANDROID") return false;
357887
+ if (!this.allowAppReset) return false;
357888
+ if (this.platform === "IOS" && !isIosSimulatorUdid(this.deviceId)) return false;
357834
357889
  return this.withSessionRecovery("app data reset", async () => {
357835
357890
  try {
357836
- await this.executeScript("mobile: clearApp", [{ appId: this.appId }]);
357891
+ if (this.platform === "IOS") {
357892
+ try {
357893
+ await this.executeScript("mobile: terminateApp", [{ ...this.appLifecycleArgs(), timeout: 5e3 }]);
357894
+ } catch (err) {
357895
+ if (isRecoverableAppiumSessionFailure(err)) throw err;
357896
+ }
357897
+ const wiped = await clearIosSimulatorAppData(this.deviceId ?? "", this.appId);
357898
+ if (!wiped.ok) {
357899
+ throw new Error(`iOS simulator app-data reset failed: ${wiped.detail}`);
357900
+ }
357901
+ } else {
357902
+ await this.executeScript("mobile: clearApp", [{ appId: this.appId }]);
357903
+ }
357837
357904
  await this.executeScript("mobile: activateApp", [this.appLifecycleArgs()]);
357838
357905
  try {
357839
357906
  await waitForColdBootSettle(this.sessionPath);
@@ -358499,7 +358566,7 @@ var log = {
358499
358566
  dim: (msg) => console.log(`${C2.gray}${msg}${C2.reset}`)
358500
358567
  };
358501
358568
  function getRunsDir() {
358502
- const dir = process.env.RUNS_DIR || join6(homedir3(), ".validate.qa", "runs");
358569
+ const dir = process.env.RUNS_DIR || join7(homedir3(), ".validate.qa", "runs");
358503
358570
  (0, import_runner_core12.ensurePrivateDirectory)(dir);
358504
358571
  return dir;
358505
358572
  }
@@ -360431,9 +360498,11 @@ ${finalVerifyResult.logs ?? ""}`;
360431
360498
  sessionId: session.sessionId,
360432
360499
  platform: platform3,
360433
360500
  appId: session.appId,
360434
- // Deterministic-state authorization: Android emulators only by
360435
- // default (VALIDATEQA_MOBILE_APP_RESET overrides) — a personal
360436
- // physical device must never have its app data wiped.
360501
+ deviceId: session.deviceId,
360502
+ // Deterministic-state authorization: Android emulators / iOS
360503
+ // simulators only (VALIDATEQA_MOBILE_APP_RESET overrides the
360504
+ // Android default) — a personal physical device must never have
360505
+ // its app data wiped.
360437
360506
  allowAppReset: mobileTarget.resetPolicy !== "PRESERVE_APP_DATA" && shouldResetMobileAppData(platform3, session.deviceId),
360438
360507
  maxSessionRecoveries: 1,
360439
360508
  recoverSession: async (cause) => {
@@ -360949,6 +361018,7 @@ ${finalVerifyResult.logs ?? ""}`;
360949
361018
  sessionId: opened.sessionId,
360950
361019
  platform: target.platform,
360951
361020
  appId: opened.appId,
361021
+ deviceId: opened.deviceId,
360952
361022
  allowAppReset: target.resetPolicy !== "PRESERVE_APP_DATA" && shouldResetMobileAppData(target.platform, opened.deviceId)
360953
361023
  });
360954
361024
  };