@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.js CHANGED
@@ -11293,7 +11293,7 @@ var require_thread_stream = __commonJS({
11293
11293
  var { version } = require_package();
11294
11294
  var { EventEmitter: EventEmitter2 } = require("events");
11295
11295
  var { Worker } = require("worker_threads");
11296
- var { join: join7 } = require("path");
11296
+ var { join: join8 } = require("path");
11297
11297
  var { pathToFileURL } = require("url");
11298
11298
  var { wait } = require_wait();
11299
11299
  var {
@@ -11344,7 +11344,7 @@ var require_thread_stream = __commonJS({
11344
11344
  function createWorker(stream, opts) {
11345
11345
  const { filename, workerData } = opts;
11346
11346
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
11347
- const toExecute = bundlerOverrides["thread-stream-worker"] || join7(__dirname, "lib", "worker.js");
11347
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join8(__dirname, "lib", "worker.js");
11348
11348
  const worker = new Worker(toExecute, {
11349
11349
  ...opts.workerOpts,
11350
11350
  name: opts.workerOpts?.name || "thread-stream",
@@ -11812,7 +11812,7 @@ var require_transport = __commonJS({
11812
11812
  var { createRequire } = require("module");
11813
11813
  var { existsSync: existsSync3 } = require("fs");
11814
11814
  var getCallers = require_caller();
11815
- var { join: join7, isAbsolute, sep } = require("path");
11815
+ var { join: join8, isAbsolute: isAbsolute2, sep } = require("path");
11816
11816
  var { fileURLToPath } = require("url");
11817
11817
  var sleep = require_atomic_sleep();
11818
11818
  var onExit = require_on_exit_leak_free();
@@ -11884,7 +11884,7 @@ var require_transport = __commonJS({
11884
11884
  return false;
11885
11885
  }
11886
11886
  }
11887
- return isAbsolute(path) && !existsSync3(path);
11887
+ return isAbsolute2(path) && !existsSync3(path);
11888
11888
  }
11889
11889
  function stripQuotes(value) {
11890
11890
  const first = value[0];
@@ -11965,7 +11965,7 @@ var require_transport = __commonJS({
11965
11965
  throw new Error("only one of target or targets can be specified");
11966
11966
  }
11967
11967
  if (targets) {
11968
- target = bundlerOverrides["pino-worker"] || join7(__dirname, "worker.js");
11968
+ target = bundlerOverrides["pino-worker"] || join8(__dirname, "worker.js");
11969
11969
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
11970
11970
  return {
11971
11971
  ...dest,
@@ -11983,7 +11983,7 @@ var require_transport = __commonJS({
11983
11983
  });
11984
11984
  });
11985
11985
  } else if (pipeline) {
11986
- target = bundlerOverrides["pino-worker"] || join7(__dirname, "worker.js");
11986
+ target = bundlerOverrides["pino-worker"] || join8(__dirname, "worker.js");
11987
11987
  options.pipelines = [pipeline.map((dest) => {
11988
11988
  return {
11989
11989
  ...dest,
@@ -12002,11 +12002,11 @@ var require_transport = __commonJS({
12002
12002
  return buildStream(fixTarget(target), options, worker, sync, name);
12003
12003
  function fixTarget(origin) {
12004
12004
  origin = bundlerOverrides[origin] || origin;
12005
- if (isAbsolute(origin) || origin.indexOf("file://") === 0) {
12005
+ if (isAbsolute2(origin) || origin.indexOf("file://") === 0) {
12006
12006
  return origin;
12007
12007
  }
12008
12008
  if (origin === "pino/file") {
12009
- return join7(__dirname, "..", "file.js");
12009
+ return join8(__dirname, "..", "file.js");
12010
12010
  }
12011
12011
  let fixTarget2;
12012
12012
  for (const filePath of callers) {
@@ -12987,7 +12987,7 @@ var require_safe_stable_stringify = __commonJS({
12987
12987
  return circularValue;
12988
12988
  }
12989
12989
  let res = "";
12990
- let join7 = ",";
12990
+ let join8 = ",";
12991
12991
  const originalIndentation = indentation;
12992
12992
  if (Array.isArray(value)) {
12993
12993
  if (value.length === 0) {
@@ -13001,7 +13001,7 @@ var require_safe_stable_stringify = __commonJS({
13001
13001
  indentation += spacer;
13002
13002
  res += `
13003
13003
  ${indentation}`;
13004
- join7 = `,
13004
+ join8 = `,
13005
13005
  ${indentation}`;
13006
13006
  }
13007
13007
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -13009,13 +13009,13 @@ ${indentation}`;
13009
13009
  for (; i < maximumValuesToStringify - 1; i++) {
13010
13010
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
13011
13011
  res += tmp2 !== void 0 ? tmp2 : "null";
13012
- res += join7;
13012
+ res += join8;
13013
13013
  }
13014
13014
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
13015
13015
  res += tmp !== void 0 ? tmp : "null";
13016
13016
  if (value.length - 1 > maximumBreadth) {
13017
13017
  const removedKeys = value.length - maximumBreadth - 1;
13018
- res += `${join7}"... ${getItemCount(removedKeys)} not stringified"`;
13018
+ res += `${join8}"... ${getItemCount(removedKeys)} not stringified"`;
13019
13019
  }
13020
13020
  if (spacer !== "") {
13021
13021
  res += `
@@ -13036,7 +13036,7 @@ ${originalIndentation}`;
13036
13036
  let separator = "";
13037
13037
  if (spacer !== "") {
13038
13038
  indentation += spacer;
13039
- join7 = `,
13039
+ join8 = `,
13040
13040
  ${indentation}`;
13041
13041
  whitespace = " ";
13042
13042
  }
@@ -13050,13 +13050,13 @@ ${indentation}`;
13050
13050
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
13051
13051
  if (tmp !== void 0) {
13052
13052
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
13053
- separator = join7;
13053
+ separator = join8;
13054
13054
  }
13055
13055
  }
13056
13056
  if (keyLength > maximumBreadth) {
13057
13057
  const removedKeys = keyLength - maximumBreadth;
13058
13058
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
13059
- separator = join7;
13059
+ separator = join8;
13060
13060
  }
13061
13061
  if (spacer !== "" && separator.length > 1) {
13062
13062
  res = `
@@ -13097,7 +13097,7 @@ ${originalIndentation}`;
13097
13097
  }
13098
13098
  const originalIndentation = indentation;
13099
13099
  let res = "";
13100
- let join7 = ",";
13100
+ let join8 = ",";
13101
13101
  if (Array.isArray(value)) {
13102
13102
  if (value.length === 0) {
13103
13103
  return "[]";
@@ -13110,7 +13110,7 @@ ${originalIndentation}`;
13110
13110
  indentation += spacer;
13111
13111
  res += `
13112
13112
  ${indentation}`;
13113
- join7 = `,
13113
+ join8 = `,
13114
13114
  ${indentation}`;
13115
13115
  }
13116
13116
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -13118,13 +13118,13 @@ ${indentation}`;
13118
13118
  for (; i < maximumValuesToStringify - 1; i++) {
13119
13119
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
13120
13120
  res += tmp2 !== void 0 ? tmp2 : "null";
13121
- res += join7;
13121
+ res += join8;
13122
13122
  }
13123
13123
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
13124
13124
  res += tmp !== void 0 ? tmp : "null";
13125
13125
  if (value.length - 1 > maximumBreadth) {
13126
13126
  const removedKeys = value.length - maximumBreadth - 1;
13127
- res += `${join7}"... ${getItemCount(removedKeys)} not stringified"`;
13127
+ res += `${join8}"... ${getItemCount(removedKeys)} not stringified"`;
13128
13128
  }
13129
13129
  if (spacer !== "") {
13130
13130
  res += `
@@ -13137,7 +13137,7 @@ ${originalIndentation}`;
13137
13137
  let whitespace = "";
13138
13138
  if (spacer !== "") {
13139
13139
  indentation += spacer;
13140
- join7 = `,
13140
+ join8 = `,
13141
13141
  ${indentation}`;
13142
13142
  whitespace = " ";
13143
13143
  }
@@ -13146,7 +13146,7 @@ ${indentation}`;
13146
13146
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
13147
13147
  if (tmp !== void 0) {
13148
13148
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
13149
- separator = join7;
13149
+ separator = join8;
13150
13150
  }
13151
13151
  }
13152
13152
  if (spacer !== "" && separator.length > 1) {
@@ -13204,20 +13204,20 @@ ${originalIndentation}`;
13204
13204
  indentation += spacer;
13205
13205
  let res2 = `
13206
13206
  ${indentation}`;
13207
- const join8 = `,
13207
+ const join9 = `,
13208
13208
  ${indentation}`;
13209
13209
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
13210
13210
  let i = 0;
13211
13211
  for (; i < maximumValuesToStringify - 1; i++) {
13212
13212
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
13213
13213
  res2 += tmp2 !== void 0 ? tmp2 : "null";
13214
- res2 += join8;
13214
+ res2 += join9;
13215
13215
  }
13216
13216
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
13217
13217
  res2 += tmp !== void 0 ? tmp : "null";
13218
13218
  if (value.length - 1 > maximumBreadth) {
13219
13219
  const removedKeys = value.length - maximumBreadth - 1;
13220
- res2 += `${join8}"... ${getItemCount(removedKeys)} not stringified"`;
13220
+ res2 += `${join9}"... ${getItemCount(removedKeys)} not stringified"`;
13221
13221
  }
13222
13222
  res2 += `
13223
13223
  ${originalIndentation}`;
@@ -13233,16 +13233,16 @@ ${originalIndentation}`;
13233
13233
  return '"[Object]"';
13234
13234
  }
13235
13235
  indentation += spacer;
13236
- const join7 = `,
13236
+ const join8 = `,
13237
13237
  ${indentation}`;
13238
13238
  let res = "";
13239
13239
  let separator = "";
13240
13240
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
13241
13241
  if (isTypedArrayWithEntries(value)) {
13242
- res += stringifyTypedArray(value, join7, maximumBreadth);
13242
+ res += stringifyTypedArray(value, join8, maximumBreadth);
13243
13243
  keys = keys.slice(value.length);
13244
13244
  maximumPropertiesToStringify -= value.length;
13245
- separator = join7;
13245
+ separator = join8;
13246
13246
  }
13247
13247
  if (deterministic) {
13248
13248
  keys = sort(keys, comparator);
@@ -13253,13 +13253,13 @@ ${indentation}`;
13253
13253
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
13254
13254
  if (tmp !== void 0) {
13255
13255
  res += `${separator}${strEscape(key2)}: ${tmp}`;
13256
- separator = join7;
13256
+ separator = join8;
13257
13257
  }
13258
13258
  }
13259
13259
  if (keyLength > maximumBreadth) {
13260
13260
  const removedKeys = keyLength - maximumBreadth;
13261
13261
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
13262
- separator = join7;
13262
+ separator = join8;
13263
13263
  }
13264
13264
  if (separator !== "") {
13265
13265
  res = `
@@ -61935,11 +61935,11 @@ var require_core = __commonJS({
61935
61935
  function mapOuter(r, f) {
61936
61936
  return r.matched ? f(r) : r;
61937
61937
  }
61938
- function ab(pa, pb, join7) {
61939
- return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) => join7(ma.value, vb, data, i, j)));
61938
+ function ab(pa, pb, join8) {
61939
+ return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) => join8(ma.value, vb, data, i, j)));
61940
61940
  }
61941
- function abc(pa, pb, pc, join7) {
61942
- 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))));
61941
+ function abc(pa, pb, pc, join8) {
61942
+ 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))));
61943
61943
  }
61944
61944
  function action(f) {
61945
61945
  return (data, i) => {
@@ -83524,48 +83524,29 @@ ${statePacket}`, redactionCredentials) },
83524
83524
  log2(" app data reset skipped by policy \u2014 scenario records after relaunch with preserved data.");
83525
83525
  return applyScenarioEntryDeepLink(driver, target, { appDataCleared: false, coldResetUnavailable: false, refuseGenerate: false, reason: "preserve_policy" }, log2);
83526
83526
  }
83527
- if (platform3 !== "IOS") {
83528
- const appDataCleared = await driver.resetAppState?.().catch((err) => {
83529
- log2(` app-data reset failed: ${err instanceof Error ? err.message : String(err)}`);
83530
- return false;
83531
- }) ?? false;
83532
- if (appDataCleared) {
83533
- return applyScenarioEntryDeepLink(driver, target, { appDataCleared: true, coldResetUnavailable: false, refuseGenerate: false }, log2);
83534
- }
83535
- await driver.relaunch();
83536
- if (resetPolicy === "CLEAR_APP_DATA") {
83537
- log2(" CLEAR_APP_DATA required but app-data reset was unavailable \u2014 refusing to generate from warm state.");
83538
- return {
83539
- appDataCleared: false,
83540
- coldResetUnavailable: true,
83541
- refuseGenerate: true,
83542
- reason: "clear_app_data_unavailable"
83543
- };
83544
- }
83545
- log2(" app-data reset unavailable \u2014 generating with @warm-state (relaunch only; may not match cold-boot replay).");
83546
- return applyScenarioEntryDeepLink(driver, target, {
83547
- appDataCleared: false,
83548
- coldResetUnavailable: true,
83549
- refuseGenerate: false,
83550
- reason: platform3 === "ANDROID" ? "android_reset_unavailable" : "reset_unavailable"
83551
- }, log2);
83527
+ const appDataCleared = await driver.resetAppState?.().catch((err) => {
83528
+ log2(` app-data reset failed: ${err instanceof Error ? err.message : String(err)}`);
83529
+ return false;
83530
+ }) ?? false;
83531
+ if (appDataCleared) {
83532
+ return applyScenarioEntryDeepLink(driver, target, { appDataCleared: true, coldResetUnavailable: false, refuseGenerate: false }, log2);
83552
83533
  }
83553
83534
  await driver.relaunch();
83554
83535
  if (resetPolicy === "CLEAR_APP_DATA") {
83555
- log2(" CLEAR_APP_DATA required but iOS has no supported clearApp primitive \u2014 refusing to generate from warm state.");
83536
+ 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.");
83556
83537
  return {
83557
83538
  appDataCleared: false,
83558
83539
  coldResetUnavailable: true,
83559
83540
  refuseGenerate: true,
83560
- reason: "ios_clear_app_data_unavailable"
83541
+ reason: platform3 === "IOS" ? "ios_clear_app_data_unavailable" : "clear_app_data_unavailable"
83561
83542
  };
83562
83543
  }
83563
- log2(" iOS has no clearApp \u2014 scenario records after relaunch (warm-state risk for auth flows).");
83544
+ 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).");
83564
83545
  return applyScenarioEntryDeepLink(driver, target, {
83565
83546
  appDataCleared: false,
83566
83547
  coldResetUnavailable: true,
83567
83548
  refuseGenerate: false,
83568
- reason: "ios_no_clear_app"
83549
+ reason: platform3 === "IOS" ? "ios_no_clear_app" : platform3 === "ANDROID" ? "android_reset_unavailable" : "reset_unavailable"
83569
83550
  }, log2);
83570
83551
  }
83571
83552
  async function runMobileGeneratePhase(args) {
@@ -89026,7 +89007,7 @@ var require_kill_tree = __commonJS({
89026
89007
  var node_child_process_1 = require("child_process");
89027
89008
  var node_util_1 = require("util");
89028
89009
  var node_fs_1 = require("fs");
89029
- var execFileAsync3 = (0, node_util_1.promisify)(node_child_process_1.execFile);
89010
+ var execFileAsync4 = (0, node_util_1.promisify)(node_child_process_1.execFile);
89030
89011
  var PS_TIMEOUT_MS = 2e3;
89031
89012
  var PS_BUFFER = 1024 * 1024;
89032
89013
  async function gracefulCloseWithTreeKill(options) {
@@ -89080,7 +89061,7 @@ var require_kill_tree = __commonJS({
89080
89061
  timer.unref?.();
89081
89062
  let stdout;
89082
89063
  try {
89083
- const result = await execFileAsync3("ps", ["-A", "-o", "pid=,ppid=,command="], {
89064
+ const result = await execFileAsync4("ps", ["-A", "-o", "pid=,ppid=,command="], {
89084
89065
  maxBuffer: PS_BUFFER,
89085
89066
  signal: controller.signal
89086
89067
  });
@@ -89118,7 +89099,7 @@ var require_kill_tree = __commonJS({
89118
89099
  timer.unref?.();
89119
89100
  let stdout;
89120
89101
  try {
89121
- const result = await execFileAsync3("ps", ["-A", "-o", "pid=,ppid=,command="], {
89102
+ const result = await execFileAsync4("ps", ["-A", "-o", "pid=,ppid=,command="], {
89122
89103
  maxBuffer: PS_BUFFER,
89123
89104
  signal: controller.signal
89124
89105
  });
@@ -89195,7 +89176,7 @@ var require_kill_tree = __commonJS({
89195
89176
  timer.unref?.();
89196
89177
  let stdout;
89197
89178
  try {
89198
- const result = await execFileAsync3("ps", ["-A", "-o", "pid=,ppid="], {
89179
+ const result = await execFileAsync4("ps", ["-A", "-o", "pid=,ppid="], {
89199
89180
  maxBuffer: PS_BUFFER,
89200
89181
  signal: controller.signal
89201
89182
  });
@@ -98745,8 +98726,8 @@ Do NOT add steps you did not execute. Do NOT skip steps from the recording.${led
98745
98726
  onRetry: (attempt, reason, delayMs) => logs2.push(` \u26A0 scenario-gen AI call retry ${attempt} (${reason}) \u2014 waiting ${delayMs}ms`)
98746
98727
  });
98747
98728
  } catch (aiErr) {
98748
- const errMsg2 = aiErr instanceof Error ? aiErr.message : String(aiErr);
98749
- logs2.push(` AI call failed at iteration ${iteration} (after retries): ${errMsg2}`);
98729
+ const errMsg3 = aiErr instanceof Error ? aiErr.message : String(aiErr);
98730
+ logs2.push(` AI call failed at iteration ${iteration} (after retries): ${errMsg3}`);
98750
98731
  throw (0, ai_retry_js_1.markAIProviderError)(aiErr);
98751
98732
  }
98752
98733
  const iterDuration = Date.now() - iterStart;
@@ -98789,8 +98770,8 @@ Do NOT add steps you did not execute. Do NOT skip steps from the recording.${led
98789
98770
  try {
98790
98771
  toolArgs = JSON.parse(toolCall.function.arguments);
98791
98772
  } catch (parseErr) {
98792
- const errMsg2 = `Invalid tool arguments: ${parseErr.message}`;
98793
- toolResults.push({ role: "tool", tool_call_id: toolCall.id, content: errMsg2 });
98773
+ const errMsg3 = `Invalid tool arguments: ${parseErr.message}`;
98774
+ toolResults.push({ role: "tool", tool_call_id: toolCall.id, content: errMsg3 });
98794
98775
  continue;
98795
98776
  }
98796
98777
  if (toolName === "finish_generate") {
@@ -100008,7 +99989,7 @@ var require_util4 = __commonJS({
100008
99989
  }
100009
99990
  path = url.path;
100010
99991
  }
100011
- var isAbsolute = exports2.isAbsolute(path);
99992
+ var isAbsolute2 = exports2.isAbsolute(path);
100012
99993
  var parts = path.split(/\/+/);
100013
99994
  for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
100014
99995
  part = parts[i];
@@ -100028,7 +100009,7 @@ var require_util4 = __commonJS({
100028
100009
  }
100029
100010
  path = parts.join("/");
100030
100011
  if (path === "") {
100031
- path = isAbsolute ? "/" : ".";
100012
+ path = isAbsolute2 ? "/" : ".";
100032
100013
  }
100033
100014
  if (url) {
100034
100015
  url.path = path;
@@ -100037,7 +100018,7 @@ var require_util4 = __commonJS({
100037
100018
  return path;
100038
100019
  }
100039
100020
  exports2.normalize = normalize;
100040
- function join7(aRoot, aPath) {
100021
+ function join8(aRoot, aPath) {
100041
100022
  if (aRoot === "") {
100042
100023
  aRoot = ".";
100043
100024
  }
@@ -100069,7 +100050,7 @@ var require_util4 = __commonJS({
100069
100050
  }
100070
100051
  return joined;
100071
100052
  }
100072
- exports2.join = join7;
100053
+ exports2.join = join8;
100073
100054
  exports2.isAbsolute = function(aPath) {
100074
100055
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
100075
100056
  };
@@ -100242,7 +100223,7 @@ var require_util4 = __commonJS({
100242
100223
  parsed.path = parsed.path.substring(0, index + 1);
100243
100224
  }
100244
100225
  }
100245
- sourceURL = join7(urlGenerate(parsed), sourceURL);
100226
+ sourceURL = join8(urlGenerate(parsed), sourceURL);
100246
100227
  }
100247
100228
  return normalize(sourceURL);
100248
100229
  }
@@ -313954,8 +313935,8 @@ Try a different fix approach. This is NOT your final script-driven attempt yet,
313954
313935
  ...(0, ai_provider_js_1.getJsonObjectResponseFormat)(agentModel)
313955
313936
  })), { label: `script-heal-${attempt}` });
313956
313937
  } catch (aiErr) {
313957
- const errMsg2 = aiErr instanceof Error ? aiErr.message : String(aiErr);
313958
- logs2.push(`\u26A0 AI call failed: ${errMsg2}`);
313938
+ const errMsg3 = aiErr instanceof Error ? aiErr.message : String(aiErr);
313939
+ logs2.push(`\u26A0 AI call failed: ${errMsg3}`);
313959
313940
  attemptHistory.push({
313960
313941
  phase: "script",
313961
313942
  attempt,
@@ -313963,13 +313944,13 @@ Try a different fix approach. This is NOT your final script-driven attempt yet,
313963
313944
  passed: false,
313964
313945
  changedCode: false,
313965
313946
  summary: "AI call failed before returning a heal result",
313966
- errorSummary: truncateForPrompt(errMsg2),
313947
+ errorSummary: truncateForPrompt(errMsg3),
313967
313948
  failedStep: summarizeFailedStep(currentStepResults)
313968
313949
  });
313969
313950
  if (feedbackEnabled) {
313970
- lastTransportFailure = truncateForPrompt(errMsg2, 200) ?? "the AI request failed";
313951
+ lastTransportFailure = truncateForPrompt(errMsg3, 200) ?? "the AI request failed";
313971
313952
  } else {
313972
- currentError = `AI call failed on attempt ${attempt}: ${errMsg2}`;
313953
+ currentError = `AI call failed on attempt ${attempt}: ${errMsg3}`;
313973
313954
  }
313974
313955
  markRecoverableTurn();
313975
313956
  continue;
@@ -314219,8 +314200,8 @@ ${currentError.slice(0, 2e3)}`
314219
314200
  // item 5b
314220
314201
  });
314221
314202
  } catch (runErr) {
314222
- const errMsg2 = runErr instanceof Error ? runErr.message : String(runErr);
314223
- logs2.push(`\u{1F4A5} Native execution threw: ${errMsg2}`);
314203
+ const errMsg3 = runErr instanceof Error ? runErr.message : String(runErr);
314204
+ logs2.push(`\u{1F4A5} Native execution threw: ${errMsg3}`);
314224
314205
  attemptHistory.push({
314225
314206
  phase: "script",
314226
314207
  attempt,
@@ -314228,9 +314209,9 @@ ${currentError.slice(0, 2e3)}`
314228
314209
  passed: false,
314229
314210
  changedCode: true,
314230
314211
  summary: truncateForPrompt(changeDesc) ?? "Applied code fix, but native execution threw before verification completed",
314231
- errorSummary: truncateForPrompt(errMsg2)
314212
+ errorSummary: truncateForPrompt(errMsg3)
314232
314213
  });
314233
- currentError = `Native execution threw: ${errMsg2}`;
314214
+ currentError = `Native execution threw: ${errMsg3}`;
314234
314215
  currentStepResults = [];
314235
314216
  continue;
314236
314217
  }
@@ -314986,9 +314967,9 @@ Last error (after heal attempts): ${lastErrorSnippet}` : `Heal aborted: same err
314986
314967
  previousErrorSignature = errorSig;
314987
314968
  }
314988
314969
  } catch (runErr) {
314989
- const errMsg2 = runErr instanceof Error ? runErr.message : String(runErr);
314990
- logs2.push(`\u{1F4A5} Native run threw: ${errMsg2}`);
314991
- currentError = errMsg2;
314970
+ const errMsg3 = runErr instanceof Error ? runErr.message : String(runErr);
314971
+ logs2.push(`\u{1F4A5} Native run threw: ${errMsg3}`);
314972
+ currentError = errMsg3;
314992
314973
  currentStepResults = [];
314993
314974
  }
314994
314975
  } else {
@@ -315550,8 +315531,8 @@ When you call finish_fix, preserve locators only when they are stable structural
315550
315531
  }
315551
315532
  }
315552
315533
  } catch (toolErr) {
315553
- const errMsg2 = toolErr instanceof Error ? toolErr.message : String(toolErr);
315554
- let errorContent = `Error: ${errMsg2}`;
315534
+ const errMsg3 = toolErr instanceof Error ? toolErr.message : String(toolErr);
315535
+ let errorContent = `Error: ${errMsg3}`;
315555
315536
  try {
315556
315537
  const consoleResult = await mcpClient.callTool({ name: "browser_console_messages", arguments: {} });
315557
315538
  const consoleText = Array.isArray(consoleResult.content) ? consoleResult.content.map((c) => c.type === "text" ? c.text ?? "" : "").join("\n") : String(consoleResult.content ?? "");
@@ -315572,8 +315553,8 @@ ${errorContent}`;
315572
315553
  }
315573
315554
  }
315574
315555
  } catch (mcpErr) {
315575
- const errMsg2 = mcpErr instanceof Error ? mcpErr.message : String(mcpErr);
315576
- logs2.push(`\u{1F4A5} MCP exploration failed: ${errMsg2}`);
315556
+ const errMsg3 = mcpErr instanceof Error ? mcpErr.message : String(mcpErr);
315557
+ logs2.push(`\u{1F4A5} MCP exploration failed: ${errMsg3}`);
315577
315558
  }
315578
315559
  if (verdict === "app_bug") {
315579
315560
  phase2AttemptHistory.push({
@@ -316136,9 +316117,9 @@ ${testBlocks}
316136
316117
  }
316137
316118
  emit(` \u2713 ${toolResultContent.slice(0, 200)}${toolResultContent.length > 200 ? "..." : ""}`);
316138
316119
  } catch (err) {
316139
- const errMsg2 = err instanceof Error ? err.message : String(err);
316140
- toolResultContent = `ERROR: ${errMsg2}`;
316141
- emit(` \u2717 ${errMsg2}`);
316120
+ const errMsg3 = err instanceof Error ? err.message : String(err);
316121
+ toolResultContent = `ERROR: ${errMsg3}`;
316122
+ emit(` \u2717 ${errMsg3}`);
316142
316123
  }
316143
316124
  toolResults.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
316144
316125
  }
@@ -321993,8 +321974,8 @@ var require_capture_page_normalizer = __commonJS({
321993
321974
  const snapItems = Array.isArray(snapResult.content) ? snapResult.content : [];
321994
321975
  snapshotText = extractInlineSnapshotYaml(snapItems.find((c) => c.type === "text")?.text || null);
321995
321976
  } catch (err) {
321996
- const errMsg2 = err instanceof Error ? err.message : String(err);
321997
- const isTimeout = /timeout|timed out|TimeoutError/i.test(errMsg2);
321977
+ const errMsg3 = err instanceof Error ? err.message : String(err);
321978
+ const isTimeout = /timeout|timed out|TimeoutError/i.test(errMsg3);
321998
321979
  return {
321999
321980
  snapshotText: null,
322000
321981
  meta: {
@@ -326016,9 +325997,9 @@ ${currentSummary}`;
326016
325997
  });
326017
325998
  }
326018
325999
  } catch (err) {
326019
- const errMsg2 = err instanceof Error ? err.message : String(err);
326020
- logs2.push(` Error persisting page: ${errMsg2}`);
326021
- toolResults.push({ role: "tool", tool_call_id: toolCall.id, content: `ERROR persisting page: ${errMsg2}` });
326000
+ const errMsg3 = err instanceof Error ? err.message : String(err);
326001
+ logs2.push(` Error persisting page: ${errMsg3}`);
326002
+ toolResults.push({ role: "tool", tool_call_id: toolCall.id, content: `ERROR persisting page: ${errMsg3}` });
326022
326003
  }
326023
326004
  continue;
326024
326005
  }
@@ -326220,12 +326201,12 @@ ${currentSummary}`;
326220
326201
  })
326221
326202
  });
326222
326203
  } catch (err) {
326223
- const errMsg2 = err instanceof Error ? err.message : String(err);
326224
- logs2.push(` Error persisting page: ${errMsg2}`);
326204
+ const errMsg3 = err instanceof Error ? err.message : String(err);
326205
+ logs2.push(` Error persisting page: ${errMsg3}`);
326225
326206
  toolResults.push({
326226
326207
  role: "tool",
326227
326208
  tool_call_id: toolCall.id,
326228
- content: `ERROR persisting page: ${errMsg2}`
326209
+ content: `ERROR persisting page: ${errMsg3}`
326229
326210
  });
326230
326211
  }
326231
326212
  continue;
@@ -326260,12 +326241,12 @@ ${currentSummary}`;
326260
326241
  content: JSON.stringify({ acknowledged: true })
326261
326242
  });
326262
326243
  } catch (err) {
326263
- const errMsg2 = err instanceof Error ? err.message : String(err);
326264
- logs2.push(` Error persisting transition: ${errMsg2}`);
326244
+ const errMsg3 = err instanceof Error ? err.message : String(err);
326245
+ logs2.push(` Error persisting transition: ${errMsg3}`);
326265
326246
  toolResults.push({
326266
326247
  role: "tool",
326267
326248
  tool_call_id: toolCall.id,
326268
- content: `ERROR persisting transition: ${errMsg2}`
326249
+ content: `ERROR persisting transition: ${errMsg3}`
326269
326250
  });
326270
326251
  }
326271
326252
  continue;
@@ -326920,15 +326901,15 @@ Exploration complete: ${summary}`);
326920
326901
  toolResultContent = (0, snapshot_context_compactor_js_1.formatMCPToolResult)(toolResultContent, MAX_TOOL_RESULT_CHARS);
326921
326902
  logs2.push(` OK: ${toolResultContent.slice(0, 200)}${toolResultContent.length > 200 ? "..." : ""}`);
326922
326903
  } catch (err) {
326923
- const errMsg2 = err instanceof Error ? err.message : String(err);
326924
- toolResultContent = `ERROR: ${errMsg2}`;
326925
- logs2.push(` Error: ${errMsg2}`);
326904
+ const errMsg3 = err instanceof Error ? err.message : String(err);
326905
+ toolResultContent = `ERROR: ${errMsg3}`;
326906
+ logs2.push(` Error: ${errMsg3}`);
326926
326907
  if (useV2) {
326927
326908
  (0, exploration_state_js_1.reduceEvent)(state2, {
326928
326909
  type: "ACTION_FAILED",
326929
326910
  route: state2.currentRoute,
326930
326911
  action: `${toolName}(${JSON.stringify(toolArgs).slice(0, 100)})`,
326931
- error: errMsg2
326912
+ error: errMsg3
326932
326913
  });
326933
326914
  }
326934
326915
  }
@@ -329774,10 +329755,10 @@ ${(0, inbox_tools_js_1.buildInboxAgentsGuidance)(inboxReg.email, inboxReg.passwo
329774
329755
  onLog: options.onLog,
329775
329756
  onProvenCommands: recordProvenCommands
329776
329757
  });
329777
- const errMsg2 = firstError?.message ?? (grokError instanceof Error ? grokError.message : "unknown error");
329758
+ const errMsg3 = firstError?.message ?? (grokError instanceof Error ? grokError.message : "unknown error");
329778
329759
  const fixPrompt = [
329779
329760
  "Your previous attempt did not produce a valid result file.",
329780
- `Error: ${errMsg2}`,
329761
+ `Error: ${errMsg3}`,
329781
329762
  `Re-read BRIEF.md and write a valid SurveyorOutput JSON to ${resultFilePath}.`,
329782
329763
  "Single JSON object. No prose. Then exit."
329783
329764
  ].join("\n");
@@ -331918,7 +331899,7 @@ ${(0, inbox_tools_js_1.buildInboxAgentsGuidance)(inboxReg.email, inboxReg.passwo
331918
331899
  log2(`First-pass discovery-result invalid or missing: ${firstError.message}`);
331919
331900
  }
331920
331901
  if (!parsedResult) {
331921
- const errMsg2 = firstError?.message ?? (grokError instanceof Error ? grokError.message : "unknown error");
331902
+ const errMsg3 = firstError?.message ?? (grokError instanceof Error ? grokError.message : "unknown error");
331922
331903
  if (planDebounceTimerRef.current) {
331923
331904
  clearTimeout(planDebounceTimerRef.current);
331924
331905
  planDebounceTimerRef.current = void 0;
@@ -331943,7 +331924,7 @@ ${(0, inbox_tools_js_1.buildInboxAgentsGuidance)(inboxReg.email, inboxReg.passwo
331943
331924
  ""
331944
331925
  ].join("\n");
331945
331926
  const fixPrompt = [
331946
- `Your previous attempt did not produce a valid result file. Error: ${errMsg2}`,
331927
+ `Your previous attempt did not produce a valid result file. Error: ${errMsg3}`,
331947
331928
  "Re-read BRIEF.md.",
331948
331929
  phaseContext,
331949
331930
  `Continue from where you left off \u2014 already verified tests are in ${testsDirPath}/`,
@@ -333572,10 +333553,10 @@ ${(0, inbox_tools_js_1.buildInboxAgentsGuidance)(inboxReg.email, inboxReg.passwo
333572
333553
  onLog: parserLog,
333573
333554
  onProvenCommands: recordProvenCommands
333574
333555
  });
333575
- const errMsg2 = firstError?.message ?? (claudeError instanceof Error ? claudeError.message : "unknown error");
333556
+ const errMsg3 = firstError?.message ?? (claudeError instanceof Error ? claudeError.message : "unknown error");
333576
333557
  const fixPrompt = [
333577
333558
  "Your previous attempt did not leave a valid result file.",
333578
- `Error: ${errMsg2}`,
333559
+ `Error: ${errMsg3}`,
333579
333560
  `Writing a valid SurveyorOutput JSON object to ${resultFilePath} is your ONLY priority now.`,
333580
333561
  "Procedure: navigate to at most 6-8 of the main pages, then IMMEDIATELY use",
333581
333562
  `the Write tool to save the SurveyorOutput JSON to ${resultFilePath}. Do this`,
@@ -339953,8 +339934,8 @@ var require_adm_zip = __commonJS({
339953
339934
  return null;
339954
339935
  }
339955
339936
  function fixPath(zipPath) {
339956
- const { join: join7, normalize, sep } = pth.posix;
339957
- return join7(pth.isAbsolute(zipPath) ? "/" : ".", normalize(sep + zipPath.split("\\").join(sep) + sep));
339937
+ const { join: join8, normalize, sep } = pth.posix;
339938
+ return join8(pth.isAbsolute(zipPath) ? "/" : ".", normalize(sep + zipPath.split("\\").join(sep) + sep));
339958
339939
  }
339959
339940
  function filenameFilter(filterfn) {
339960
339941
  if (filterfn instanceof RegExp) {
@@ -342789,11 +342770,11 @@ var require_winchars = __commonJS({
342789
342770
  var require_strip_absolute_path = __commonJS({
342790
342771
  "../../node_modules/tar/lib/strip-absolute-path.js"(exports2, module2) {
342791
342772
  "use strict";
342792
- var { isAbsolute, parse } = require("path").win32;
342773
+ var { isAbsolute: isAbsolute2, parse } = require("path").win32;
342793
342774
  module2.exports = (path) => {
342794
342775
  let r = "";
342795
342776
  let parsed = parse(path);
342796
- while (isAbsolute(path) || parsed.root) {
342777
+ while (isAbsolute2(path) || parsed.root) {
342797
342778
  const root = path.charAt(0) === "/" && path.slice(0, 4) !== "//?/" ? "/" : parsed.root;
342798
342779
  path = path.slice(root.length);
342799
342780
  r += root;
@@ -345305,7 +345286,7 @@ var require_update = __commonJS({
345305
345286
  var require_opts_arg = __commonJS({
345306
345287
  "../../node_modules/tar/node_modules/mkdirp/lib/opts-arg.js"(exports2, module2) {
345307
345288
  "use strict";
345308
- var { promisify: promisify3 } = require("util");
345289
+ var { promisify: promisify4 } = require("util");
345309
345290
  var fs = require("fs");
345310
345291
  var optsArg = (opts) => {
345311
345292
  if (!opts)
@@ -345319,9 +345300,9 @@ var require_opts_arg = __commonJS({
345319
345300
  else
345320
345301
  throw new TypeError("invalid options argument");
345321
345302
  opts.mkdir = opts.mkdir || opts.fs.mkdir || fs.mkdir;
345322
- opts.mkdirAsync = promisify3(opts.mkdir);
345303
+ opts.mkdirAsync = promisify4(opts.mkdir);
345323
345304
  opts.stat = opts.stat || opts.fs.stat || fs.stat;
345324
- opts.statAsync = promisify3(opts.stat);
345305
+ opts.statAsync = promisify4(opts.stat);
345325
345306
  opts.statSync = opts.statSync || opts.fs.statSync || fs.statSync;
345326
345307
  opts.mkdirSync = opts.mkdirSync || opts.fs.mkdirSync || fs.mkdirSync;
345327
345308
  return opts;
@@ -345574,10 +345555,10 @@ var require_chownr = __commonJS({
345574
345555
  }
345575
345556
  } : (path2, uid, gid) => lchownSync(path2, uid, gid);
345576
345557
  var nodeVersion = process.version;
345577
- var readdir2 = (path2, options, cb) => fs.readdir(path2, options, cb);
345558
+ var readdir3 = (path2, options, cb) => fs.readdir(path2, options, cb);
345578
345559
  var readdirSync = (path2, options) => fs.readdirSync(path2, options);
345579
345560
  if (/^v4\./.test(nodeVersion))
345580
- readdir2 = (path2, options, cb) => fs.readdir(path2, cb);
345561
+ readdir3 = (path2, options, cb) => fs.readdir(path2, cb);
345581
345562
  var chown = (cpath, uid, gid, cb) => {
345582
345563
  fs[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, (er) => {
345583
345564
  cb(er && er.code !== "ENOENT" ? er : null);
@@ -345604,7 +345585,7 @@ var require_chownr = __commonJS({
345604
345585
  }
345605
345586
  };
345606
345587
  var chownr = (p, uid, gid, cb) => {
345607
- readdir2(p, { withFileTypes: true }, (er, children) => {
345588
+ readdir3(p, { withFileTypes: true }, (er, children) => {
345608
345589
  if (er) {
345609
345590
  if (er.code === "ENOENT")
345610
345591
  return cb();
@@ -345878,7 +345859,7 @@ var require_path_reservations = __commonJS({
345878
345859
  var assert = require("assert");
345879
345860
  var normalize = require_normalize_unicode();
345880
345861
  var stripSlashes = require_strip_trailing_slashes();
345881
- var { join: join7 } = require("path");
345862
+ var { join: join8 } = require("path");
345882
345863
  var platform3 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
345883
345864
  var isWindows = platform3 === "win32";
345884
345865
  module2.exports = () => {
@@ -345887,7 +345868,7 @@ var require_path_reservations = __commonJS({
345887
345868
  const getDirs = (path) => {
345888
345869
  const dirs = path.split("/").slice(0, -1).reduce((set, path2) => {
345889
345870
  if (set.length) {
345890
- path2 = join7(set[set.length - 1], path2);
345871
+ path2 = join8(set[set.length - 1], path2);
345891
345872
  }
345892
345873
  set.push(path2 || "/");
345893
345874
  return set;
@@ -345955,7 +345936,7 @@ var require_path_reservations = __commonJS({
345955
345936
  };
345956
345937
  const reserve = (paths, fn) => {
345957
345938
  paths = isWindows ? ["win32 parallelization disabled"] : paths.map((p) => {
345958
- return stripSlashes(join7(normalize(p))).toLowerCase();
345939
+ return stripSlashes(join8(normalize(p))).toLowerCase();
345959
345940
  });
345960
345941
  const dirs = new Set(
345961
345942
  paths.map((path) => getDirs(path)).reduce((a, b) => a.concat(b))
@@ -350170,7 +350151,7 @@ var require_process_inventory = __commonJS({
350170
350151
  var node_child_process_1 = require("child_process");
350171
350152
  var node_os_1 = __importDefault(require("os"));
350172
350153
  var node_util_1 = require("util");
350173
- var execFileAsync3 = (0, node_util_1.promisify)(node_child_process_1.execFile);
350154
+ var execFileAsync4 = (0, node_util_1.promisify)(node_child_process_1.execFile);
350174
350155
  var DEFAULT_LIMIT = 60;
350175
350156
  var DEFAULT_TIMEOUT_MS2 = 800;
350176
350157
  var MAX_COMMAND_LENGTH = 320;
@@ -350232,7 +350213,7 @@ var require_process_inventory = __commonJS({
350232
350213
  const timer = setTimeout(() => controller.abort(), timeoutMs);
350233
350214
  timer.unref?.();
350234
350215
  try {
350235
- const { stdout } = await execFileAsync3(command, args, {
350216
+ const { stdout } = await execFileAsync4(command, args, {
350236
350217
  maxBuffer: 1024 * 1024,
350237
350218
  signal: controller.signal
350238
350219
  });
@@ -351368,7 +351349,7 @@ var require_package4 = __commonJS({
351368
351349
  "package.json"(exports2, module2) {
351369
351350
  module2.exports = {
351370
351351
  name: "@validate.qa/runner",
351371
- version: "1.0.68",
351352
+ version: "1.0.69",
351372
351353
  description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
351373
351354
  bin: {
351374
351355
  "validate-runner": "dist/cli.js"
@@ -351455,7 +351436,7 @@ function getConfigDir() {
351455
351436
  }
351456
351437
 
351457
351438
  // src/runner.ts
351458
- var import_path4 = require("path");
351439
+ var import_path5 = require("path");
351459
351440
  var import_os6 = require("os");
351460
351441
  var import_node_crypto2 = require("crypto");
351461
351442
 
@@ -352050,6 +352031,66 @@ function resetServerState(port = APPIUM_PORT, restartCount = 0) {
352050
352031
  };
352051
352032
  }
352052
352033
 
352034
+ // src/services/mobile/ios-simulator-reset.ts
352035
+ var import_child_process2 = require("child_process");
352036
+ var import_util = require("util");
352037
+ var import_promises = require("fs/promises");
352038
+ var import_path3 = require("path");
352039
+ var execFileAsync = (0, import_util.promisify)(import_child_process2.execFile);
352040
+ var SIMCTL_TIMEOUT_MS = 15e3;
352041
+ 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;
352042
+ function isIosSimulatorUdid(udid) {
352043
+ return typeof udid === "string" && SIMULATOR_UDID_RE.test(udid.trim());
352044
+ }
352045
+ function errMsg(err) {
352046
+ return err instanceof Error ? err.message : String(err);
352047
+ }
352048
+ async function simctl(args) {
352049
+ const { stdout } = await execFileAsync("xcrun", ["simctl", ...args], {
352050
+ timeout: SIMCTL_TIMEOUT_MS,
352051
+ encoding: "utf-8"
352052
+ });
352053
+ return stdout.trim();
352054
+ }
352055
+ async function clearIosSimulatorAppData(udid, bundleId, env = process.env) {
352056
+ if (!isIosSimulatorUdid(udid)) {
352057
+ return { ok: false, detail: `'${udid}' is not a simulator UDID \u2014 physical iOS devices cannot be cleared` };
352058
+ }
352059
+ let container;
352060
+ try {
352061
+ container = await simctl(["get_app_container", udid, bundleId, "data"]);
352062
+ } catch (err) {
352063
+ return { ok: false, detail: `simctl get_app_container failed (app not installed?): ${errMsg(err)}` };
352064
+ }
352065
+ if (!(0, import_path3.isAbsolute)(container) || !container.includes("/Containers/Data/Application/")) {
352066
+ return { ok: false, detail: `unexpected app container path: ${container}` };
352067
+ }
352068
+ try {
352069
+ await simctl(["spawn", udid, "defaults", "delete", bundleId]);
352070
+ } catch {
352071
+ }
352072
+ try {
352073
+ const entries = await (0, import_promises.readdir)(container);
352074
+ for (const entry of entries) {
352075
+ await (0, import_promises.rm)((0, import_path3.join)(container, entry), { recursive: true, force: true });
352076
+ }
352077
+ } catch (err) {
352078
+ return { ok: false, detail: `app container wipe failed: ${errMsg(err)}` };
352079
+ }
352080
+ const keychainPolicy = (env.VALIDATEQA_MOBILE_IOS_KEYCHAIN_RESET ?? "").trim().toLowerCase();
352081
+ if (keychainPolicy !== "never") {
352082
+ try {
352083
+ await simctl(["keychain", udid, "reset"]);
352084
+ } catch (err) {
352085
+ return {
352086
+ ok: false,
352087
+ 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.`
352088
+ };
352089
+ }
352090
+ }
352091
+ return { ok: true };
352092
+ }
352093
+
352053
352094
  // src/services/mobile/appium-actions.ts
352054
352095
  var APPIUM_REQUEST_TIMEOUT_MS = 1e4;
352055
352096
  var APPIUM_WEBVIEW_CONTEXT_TIMEOUT_MS = 12e4;
@@ -352232,9 +352273,12 @@ async function tapAt(sessionPath, bounds) {
352232
352273
  }
352233
352274
  }
352234
352275
  function shouldResetMobileAppData(platform3, deviceId, env = process.env) {
352235
- if (platform3 !== "ANDROID") return false;
352236
352276
  const policy = (env.VALIDATEQA_MOBILE_APP_RESET ?? "").trim().toLowerCase();
352237
352277
  if (policy === "never") return false;
352278
+ if (platform3 === "IOS") {
352279
+ return isIosSimulatorUdid(deviceId);
352280
+ }
352281
+ if (platform3 !== "ANDROID") return false;
352238
352282
  if (policy === "always") return true;
352239
352283
  return !!deviceId && deviceId.startsWith("emulator-");
352240
352284
  }
@@ -353121,11 +353165,11 @@ var import_node_path2 = require("path");
353121
353165
  var import_runner_core8 = __toESM(require_dist2());
353122
353166
 
353123
353167
  // src/services/device-discovery.ts
353124
- var import_child_process2 = require("child_process");
353168
+ var import_child_process3 = require("child_process");
353125
353169
  var import_os3 = require("os");
353126
353170
  function runCommand(command, args, timeout = 1e4) {
353127
353171
  try {
353128
- return (0, import_child_process2.execFileSync)(command, args, {
353172
+ return (0, import_child_process3.execFileSync)(command, args, {
353129
353173
  encoding: "utf-8",
353130
353174
  timeout,
353131
353175
  stdio: ["pipe", "pipe", "pipe"]
@@ -353425,9 +353469,6 @@ function countReadyDevicesByKind(platform3, snapshot = getCachedDeviceSnapshot()
353425
353469
  function selectFreeDevice(platform3, leased, snapshot = getCachedDeviceSnapshot(), requirements = {}) {
353426
353470
  const ready = snapshot.filter((d) => d.platform === platform3 && d.isAvailable && d.state === "BOOTED");
353427
353471
  if (ready.length === 0) return { kind: "busy", reason: `no booted ${platform3} device` };
353428
- if (requirements.resetPolicy === "CLEAR_APP_DATA" && platform3 !== "ANDROID") {
353429
- return { kind: "unsatisfiable", reason: "CLEAR_APP_DATA is unsupported on iOS" };
353430
- }
353431
353472
  let candidates = requirements.resetPolicy === "CLEAR_APP_DATA" ? ready.filter((d) => !d.isPhysical) : [...ready];
353432
353473
  const appId = requirements.appId?.trim();
353433
353474
  if (appId) {
@@ -353477,13 +353518,13 @@ function pickFreeDevice(platform3, leased, snapshot = getCachedDeviceSnapshot(),
353477
353518
  // src/services/mobile/mobile-network-capture.ts
353478
353519
  var import_node_child_process = require("child_process");
353479
353520
  var import_node_crypto = require("crypto");
353480
- var import_promises = require("fs/promises");
353521
+ var import_promises2 = require("fs/promises");
353481
353522
  var import_node_os = require("os");
353482
353523
  var import_node_path = require("path");
353483
353524
  var import_node_util = require("util");
353484
353525
  var import_mockttp = require("mockttp");
353485
353526
  var import_runner_core7 = __toESM(require_dist2());
353486
- var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
353527
+ var execFileAsync2 = (0, import_node_util.promisify)(import_node_child_process.execFile);
353487
353528
  var MAX_CALLS = 1e3;
353488
353529
  var MAX_BODY_CAPTURE_BYTES = 64 * 1024;
353489
353530
  var DEVICE_CMD_TIMEOUT_MS = 15e3;
@@ -353671,7 +353712,7 @@ var MitmProxy = class {
353671
353712
  try {
353672
353713
  await server3.stop();
353673
353714
  } catch (err) {
353674
- this.onLog?.(`[mobile-net] proxy stop error (ignored): ${errMsg(err)}`);
353715
+ this.onLog?.(`[mobile-net] proxy stop error (ignored): ${errMsg2(err)}`);
353675
353716
  }
353676
353717
  this.onLog?.(
353677
353718
  `[mobile-net] proxy closed: ${this.calls.length} calls captured` + (this.dropped > 0 ? ` (${this.dropped} dropped over cap)` : "")
@@ -353696,7 +353737,7 @@ var MitmProxy = class {
353696
353737
  isHttps
353697
353738
  });
353698
353739
  } catch (err) {
353699
- this.onLog?.(`[mobile-net] request capture error (ignored): ${errMsg(err)}`);
353740
+ this.onLog?.(`[mobile-net] request capture error (ignored): ${errMsg2(err)}`);
353700
353741
  }
353701
353742
  }
353702
353743
  // ── response → finalize the matched request ───────────────────────────────
@@ -353739,7 +353780,7 @@ var MitmProxy = class {
353739
353780
  intercepted: true
353740
353781
  });
353741
353782
  } catch (err) {
353742
- this.onLog?.(`[mobile-net] response capture error (ignored): ${errMsg(err)}`);
353783
+ this.onLog?.(`[mobile-net] response capture error (ignored): ${errMsg2(err)}`);
353743
353784
  }
353744
353785
  }
353745
353786
  // ── tls-client-error → record WHY interception failed ─────────────────────
@@ -353817,16 +353858,16 @@ async function generateCaCert(dir, onLog) {
353817
353858
  // rather than on a short expiry.
353818
353859
  });
353819
353860
  const caCertPath = (0, import_node_path.join)(dir, "validateqa-mobile-ca.pem");
353820
- await (0, import_promises.writeFile)(caCertPath, cert, "utf-8");
353861
+ await (0, import_promises2.writeFile)(caCertPath, cert, "utf-8");
353821
353862
  return { caCertPath, caKeyPem: key, caCertPem: cert };
353822
353863
  } catch (err) {
353823
- onLog?.(`[mobile-net] CA generation failed; running plaintext-only: ${errMsg(err)}`);
353864
+ onLog?.(`[mobile-net] CA generation failed; running plaintext-only: ${errMsg2(err)}`);
353824
353865
  return null;
353825
353866
  }
353826
353867
  }
353827
353868
  async function androidReadProxy(deviceId) {
353828
353869
  try {
353829
- const { stdout } = await execFileAsync(
353870
+ const { stdout } = await execFileAsync2(
353830
353871
  "adb",
353831
353872
  ["-s", deviceId, "shell", "settings", "get", "global", "http_proxy"],
353832
353873
  { timeout: DEVICE_CMD_TIMEOUT_MS }
@@ -353839,33 +353880,33 @@ async function androidReadProxy(deviceId) {
353839
353880
  }
353840
353881
  async function androidSetProxy(deviceId, hostPort, onLog) {
353841
353882
  try {
353842
- await execFileAsync(
353883
+ await execFileAsync2(
353843
353884
  "adb",
353844
353885
  ["-s", deviceId, "shell", "settings", "put", "global", "http_proxy", hostPort],
353845
353886
  { timeout: DEVICE_CMD_TIMEOUT_MS }
353846
353887
  );
353847
353888
  return true;
353848
353889
  } catch (err) {
353849
- onLog?.(`[mobile-net] adb set http_proxy failed: ${errMsg(err)}`);
353890
+ onLog?.(`[mobile-net] adb set http_proxy failed: ${errMsg2(err)}`);
353850
353891
  return false;
353851
353892
  }
353852
353893
  }
353853
353894
  async function androidClearProxy(deviceId, originalProxy, onLog) {
353854
353895
  const restoreValue = originalProxy && originalProxy.length > 0 ? originalProxy : ":0";
353855
353896
  try {
353856
- await execFileAsync(
353897
+ await execFileAsync2(
353857
353898
  "adb",
353858
353899
  ["-s", deviceId, "shell", "settings", "put", "global", "http_proxy", restoreValue],
353859
353900
  { timeout: DEVICE_CMD_TIMEOUT_MS }
353860
353901
  );
353861
353902
  } catch (err) {
353862
- onLog?.(`[mobile-net] adb restore http_proxy failed: ${errMsg(err)}`);
353903
+ onLog?.(`[mobile-net] adb restore http_proxy failed: ${errMsg2(err)}`);
353863
353904
  }
353864
353905
  }
353865
353906
  async function androidInstallCa(deviceId, caCertPath) {
353866
353907
  const devicePath = "/sdcard/validateqa-mobile-ca.pem";
353867
353908
  try {
353868
- await execFileAsync("adb", ["-s", deviceId, "push", caCertPath, devicePath], {
353909
+ await execFileAsync2("adb", ["-s", deviceId, "push", caCertPath, devicePath], {
353869
353910
  timeout: DEVICE_CMD_TIMEOUT_MS
353870
353911
  });
353871
353912
  return {
@@ -353873,23 +353914,23 @@ async function androidInstallCa(deviceId, caCertPath) {
353873
353914
  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.`
353874
353915
  };
353875
353916
  } catch (err) {
353876
- return { installed: false, note: `CA push failed: ${errMsg(err)}` };
353917
+ return { installed: false, note: `CA push failed: ${errMsg2(err)}` };
353877
353918
  }
353878
353919
  }
353879
353920
  async function androidRemoveCa(deviceId, onLog) {
353880
353921
  try {
353881
- await execFileAsync(
353922
+ await execFileAsync2(
353882
353923
  "adb",
353883
353924
  ["-s", deviceId, "shell", "rm", "-f", "/sdcard/validateqa-mobile-ca.pem"],
353884
353925
  { timeout: DEVICE_CMD_TIMEOUT_MS }
353885
353926
  );
353886
353927
  } catch (err) {
353887
- onLog?.(`[mobile-net] adb remove CA failed: ${errMsg(err)}`);
353928
+ onLog?.(`[mobile-net] adb remove CA failed: ${errMsg2(err)}`);
353888
353929
  }
353889
353930
  }
353890
353931
  async function iosSimTrustCa(deviceId, caCertPath) {
353891
353932
  try {
353892
- await execFileAsync("xcrun", ["simctl", "keychain", deviceId, "add-root-cert", caCertPath], {
353933
+ await execFileAsync2("xcrun", ["simctl", "keychain", deviceId, "add-root-cert", caCertPath], {
353893
353934
  timeout: DEVICE_CMD_TIMEOUT_MS
353894
353935
  });
353895
353936
  return {
@@ -353897,18 +353938,18 @@ async function iosSimTrustCa(deviceId, caCertPath) {
353897
353938
  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."
353898
353939
  };
353899
353940
  } catch (err) {
353900
- return { trusted: false, note: `simctl add-root-cert failed: ${errMsg(err)}` };
353941
+ return { trusted: false, note: `simctl add-root-cert failed: ${errMsg2(err)}` };
353901
353942
  }
353902
353943
  }
353903
353944
  async function iosSimRemoveCa(deviceId, onLog) {
353904
353945
  if (process.env.VALIDATEQA_MOBILE_SIM_KEYCHAIN_RESET === "1") {
353905
353946
  try {
353906
- await execFileAsync("xcrun", ["simctl", "keychain", deviceId, "reset"], {
353947
+ await execFileAsync2("xcrun", ["simctl", "keychain", deviceId, "reset"], {
353907
353948
  timeout: DEVICE_CMD_TIMEOUT_MS
353908
353949
  });
353909
353950
  onLog?.(`[mobile-net] simctl keychain reset run for ${deviceId} (opt-in; cleared ALL sim certs).`);
353910
353951
  } catch (err) {
353911
- onLog?.(`[mobile-net] simctl keychain reset failed: ${errMsg(err)}`);
353952
+ onLog?.(`[mobile-net] simctl keychain reset failed: ${errMsg2(err)}`);
353912
353953
  }
353913
353954
  return;
353914
353955
  }
@@ -353916,7 +353957,7 @@ async function iosSimRemoveCa(deviceId, onLog) {
353916
353957
  `[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.`
353917
353958
  );
353918
353959
  }
353919
- function errMsg(err) {
353960
+ function errMsg2(err) {
353920
353961
  return err instanceof Error ? err.message : String(err);
353921
353962
  }
353922
353963
  function isProcessAlive(pid) {
@@ -354007,7 +354048,7 @@ async function iosSimConfigureHostProxy(host, port, onLog) {
354007
354048
  let service;
354008
354049
  let prior;
354009
354050
  try {
354010
- const { stdout: order } = await execFileAsync("networksetup", ["-listnetworkserviceorder"], {
354051
+ const { stdout: order } = await execFileAsync2("networksetup", ["-listnetworkserviceorder"], {
354011
354052
  timeout: DEVICE_CMD_TIMEOUT_MS
354012
354053
  });
354013
354054
  const svc = parsePrimaryNetworkService(order);
@@ -354016,26 +354057,26 @@ async function iosSimConfigureHostProxy(host, port, onLog) {
354016
354057
  }
354017
354058
  service = svc;
354018
354059
  const [{ stdout: webOut }, { stdout: secureOut }] = await Promise.all([
354019
- execFileAsync("networksetup", ["-getwebproxy", service], { timeout: DEVICE_CMD_TIMEOUT_MS }),
354020
- execFileAsync("networksetup", ["-getsecurewebproxy", service], { timeout: DEVICE_CMD_TIMEOUT_MS })
354060
+ execFileAsync2("networksetup", ["-getwebproxy", service], { timeout: DEVICE_CMD_TIMEOUT_MS }),
354061
+ execFileAsync2("networksetup", ["-getsecurewebproxy", service], { timeout: DEVICE_CMD_TIMEOUT_MS })
354021
354062
  ]);
354022
354063
  prior = { web: parseWebProxyState(webOut), secure: parseWebProxyState(secureOut) };
354023
354064
  for (const args of buildHostProxySetCommands(service, host, port)) {
354024
- await execFileAsync("networksetup", args, { timeout: DEVICE_CMD_TIMEOUT_MS });
354065
+ await execFileAsync2("networksetup", args, { timeout: DEVICE_CMD_TIMEOUT_MS });
354025
354066
  }
354026
354067
  onLog?.(`[mobile-net] macOS host proxy set on "${service}" \u2192 ${host}:${port} (iOS simulator shares the host network).`);
354027
354068
  return { configured: true, service, prior };
354028
354069
  } catch (err) {
354029
354070
  if (service && prior) {
354030
- onLog?.(`[mobile-net] host proxy set failed mid-way \u2014 rolling back "${service}" to prior state: ${errMsg(err)}`);
354071
+ onLog?.(`[mobile-net] host proxy set failed mid-way \u2014 rolling back "${service}" to prior state: ${errMsg2(err)}`);
354031
354072
  for (const args of buildHostProxyRestoreCommands(service, prior)) {
354032
354073
  try {
354033
- await execFileAsync("networksetup", args, { timeout: DEVICE_CMD_TIMEOUT_MS });
354074
+ await execFileAsync2("networksetup", args, { timeout: DEVICE_CMD_TIMEOUT_MS });
354034
354075
  } catch {
354035
354076
  }
354036
354077
  }
354037
354078
  }
354038
- return { configured: false, reason: `iOS simulator host proxy could not be configured: ${errMsg(err)}` };
354079
+ return { configured: false, reason: `iOS simulator host proxy could not be configured: ${errMsg2(err)}` };
354039
354080
  }
354040
354081
  }
354041
354082
  async function iosSimRestoreHostProxy(service, prior, onLog) {
@@ -354045,9 +354086,9 @@ async function iosSimRestoreHostProxy(service, prior, onLog) {
354045
354086
  };
354046
354087
  for (const args of buildHostProxyRestoreCommands(service, restore)) {
354047
354088
  try {
354048
- await execFileAsync("networksetup", args, { timeout: DEVICE_CMD_TIMEOUT_MS });
354089
+ await execFileAsync2("networksetup", args, { timeout: DEVICE_CMD_TIMEOUT_MS });
354049
354090
  } catch (err) {
354050
- onLog?.(`[mobile-net] networksetup restore (${args[0]}) failed: ${errMsg(err)}`);
354091
+ onLog?.(`[mobile-net] networksetup restore (${args[0]}) failed: ${errMsg2(err)}`);
354051
354092
  }
354052
354093
  }
354053
354094
  onLog?.(`[mobile-net] macOS host proxy restored on "${service}".`);
@@ -354059,14 +354100,14 @@ function markerPath(sessionKey) {
354059
354100
  }
354060
354101
  async function writePendingCleanup(sessionKey, marker) {
354061
354102
  try {
354062
- await (0, import_promises.mkdir)(PENDING_CLEANUP_DIR, { recursive: true });
354063
- await (0, import_promises.writeFile)(markerPath(sessionKey), JSON.stringify(marker), "utf-8");
354103
+ await (0, import_promises2.mkdir)(PENDING_CLEANUP_DIR, { recursive: true });
354104
+ await (0, import_promises2.writeFile)(markerPath(sessionKey), JSON.stringify(marker), "utf-8");
354064
354105
  } catch {
354065
354106
  }
354066
354107
  }
354067
354108
  async function removePendingCleanup(sessionKey) {
354068
354109
  try {
354069
- await (0, import_promises.unlink)(markerPath(sessionKey));
354110
+ await (0, import_promises2.unlink)(markerPath(sessionKey));
354070
354111
  } catch {
354071
354112
  }
354072
354113
  }
@@ -354099,7 +354140,7 @@ function restoreDeviceSync(marker, onLog) {
354099
354140
  async function reconcilePendingCleanups(onLog) {
354100
354141
  let files;
354101
354142
  try {
354102
- files = await (0, import_promises.readdir)(PENDING_CLEANUP_DIR);
354143
+ files = await (0, import_promises2.readdir)(PENDING_CLEANUP_DIR);
354103
354144
  } catch {
354104
354145
  return;
354105
354146
  }
@@ -354109,7 +354150,7 @@ async function reconcilePendingCleanups(onLog) {
354109
354150
  if (activeCleanups.has(sessionKey)) continue;
354110
354151
  const full = (0, import_node_path.join)(PENDING_CLEANUP_DIR, file);
354111
354152
  try {
354112
- const raw = await (0, import_promises.readFile)(full, "utf-8");
354153
+ const raw = await (0, import_promises2.readFile)(full, "utf-8");
354113
354154
  const marker = JSON.parse(raw);
354114
354155
  if (marker && typeof marker.deviceId === "string" && marker.platform) {
354115
354156
  if (!shouldReconcilePendingCleanup(marker)) {
@@ -354122,7 +354163,7 @@ async function reconcilePendingCleanups(onLog) {
354122
354163
  } catch {
354123
354164
  }
354124
354165
  try {
354125
- await (0, import_promises.unlink)(full);
354166
+ await (0, import_promises2.unlink)(full);
354126
354167
  } catch {
354127
354168
  }
354128
354169
  }
@@ -354160,7 +354201,7 @@ async function startMobileNetworkCapture(options) {
354160
354201
  let caKeyPem = null;
354161
354202
  let caCertPem = null;
354162
354203
  try {
354163
- tmpDir = await (0, import_promises.mkdtemp)((0, import_node_path.join)((0, import_node_os.tmpdir)(), "validateqa-mobile-ca-"));
354204
+ tmpDir = await (0, import_promises2.mkdtemp)((0, import_node_path.join)((0, import_node_os.tmpdir)(), "validateqa-mobile-ca-"));
354164
354205
  const ca = await generateCaCert(tmpDir, onLog);
354165
354206
  if (ca) {
354166
354207
  caCertPath = ca.caCertPath;
@@ -354168,7 +354209,7 @@ async function startMobileNetworkCapture(options) {
354168
354209
  caCertPem = ca.caCertPem;
354169
354210
  }
354170
354211
  } catch (err) {
354171
- onLog?.(`[mobile-net] CA setup degraded: ${errMsg(err)}`);
354212
+ onLog?.(`[mobile-net] CA setup degraded: ${errMsg2(err)}`);
354172
354213
  }
354173
354214
  if (!caKeyPem || !caCertPem) {
354174
354215
  try {
@@ -354176,9 +354217,9 @@ async function startMobileNetworkCapture(options) {
354176
354217
  caKeyPem = fallback.key;
354177
354218
  caCertPem = fallback.cert;
354178
354219
  } catch (err) {
354179
- onLog?.(`[mobile-net] fatal CA failure, cannot start proxy: ${errMsg(err)}`);
354220
+ onLog?.(`[mobile-net] fatal CA failure, cannot start proxy: ${errMsg2(err)}`);
354180
354221
  if (tmpDir) {
354181
- await (0, import_promises.rm)(tmpDir, { recursive: true, force: true }).catch(() => void 0);
354222
+ await (0, import_promises2.rm)(tmpDir, { recursive: true, force: true }).catch(() => void 0);
354182
354223
  }
354183
354224
  throw err instanceof Error ? err : new Error(String(err));
354184
354225
  }
@@ -354233,7 +354274,7 @@ async function startMobileNetworkCapture(options) {
354233
354274
  }
354234
354275
  }
354235
354276
  } catch (err) {
354236
- wiringReason = `device wiring degraded: ${errMsg(err)}`;
354277
+ wiringReason = `device wiring degraded: ${errMsg2(err)}`;
354237
354278
  onLog?.(`[mobile-net] ${wiringReason}`);
354238
354279
  }
354239
354280
  if (proxySet || caInstalledOnDevice || hostProxySet) {
@@ -354322,9 +354363,9 @@ async function startMobileNetworkCapture(options) {
354322
354363
  await proxy.close();
354323
354364
  if (capturedTmpDir) {
354324
354365
  try {
354325
- await (0, import_promises.rm)(capturedTmpDir, { recursive: true, force: true });
354366
+ await (0, import_promises2.rm)(capturedTmpDir, { recursive: true, force: true });
354326
354367
  } catch (err) {
354327
- onLog?.(`[mobile-net] temp CA cleanup failed: ${errMsg(err)}`);
354368
+ onLog?.(`[mobile-net] temp CA cleanup failed: ${errMsg2(err)}`);
354328
354369
  }
354329
354370
  }
354330
354371
  const apiCount = finalCalls.filter((c) => c.isApi).length;
@@ -354361,7 +354402,7 @@ var WD_SESSION_CREATE_TIMEOUT_MS = 12e4;
354361
354402
  var WD_ACTION_TIMEOUT_MS = 3e4;
354362
354403
  var APPIUM_DRIVER_DEAD_RE2 = /instrumentation process is not running|cannot be proxied to UiAutomator2 server|socket hang up|ECONNRESET/i;
354363
354404
  var MAX_SESSION_CREATE_ATTEMPTS = 3;
354364
- var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
354405
+ var execFileAsync3 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
354365
354406
  var MOBILE_TEXT_ATTRIBUTE_NAMES = ["text", "content-desc", "contentDescription", "name", "label", "value"];
354366
354407
  var WebDriverTransportError = class extends Error {
354367
354408
  isTransport = true;
@@ -354578,7 +354619,7 @@ async function removeStaleAndroidSystemPortForward(target, error2) {
354578
354619
  const args = androidSystemPortForwardRemovalArgs(target, error2);
354579
354620
  if (!args) return false;
354580
354621
  try {
354581
- await execFileAsync2("adb", args, { timeout: 1e4, windowsHide: true });
354622
+ await execFileAsync3("adb", args, { timeout: 1e4, windowsHide: true });
354582
354623
  return true;
354583
354624
  } catch {
354584
354625
  return false;
@@ -355052,7 +355093,7 @@ async function prepareAndroidInputEnvironment(deviceId) {
355052
355093
  ];
355053
355094
  for (const [namespace, key, value] of settingsToDisable) {
355054
355095
  try {
355055
- await execFileAsync2("adb", ["-s", deviceId, "shell", "settings", "put", namespace, key, value], {
355096
+ await execFileAsync3("adb", ["-s", deviceId, "shell", "settings", "put", namespace, key, value], {
355056
355097
  timeout: 8e3,
355057
355098
  windowsHide: true
355058
355099
  });
@@ -355064,14 +355105,14 @@ async function sendAndroidTextToFocusedInput(deviceId, value) {
355064
355105
  if (!deviceId) {
355065
355106
  throw new Error("Android keyboard fallback requires a device id");
355066
355107
  }
355067
- await execFileAsync2("adb", ["-s", deviceId, "shell", "input", "text", encodeAndroidInputText(value)], {
355108
+ await execFileAsync3("adb", ["-s", deviceId, "shell", "input", "text", encodeAndroidInputText(value)], {
355068
355109
  timeout: 1e4,
355069
355110
  windowsHide: true
355070
355111
  });
355071
355112
  }
355072
355113
  async function pressAndroidKeyboardEnter(deviceId) {
355073
355114
  if (!deviceId) throw new Error("Android keyboard Enter fallback requires a device id");
355074
- await execFileAsync2("adb", ["-s", deviceId, "shell", "input", "keyevent", "66"], {
355115
+ await execFileAsync3("adb", ["-s", deviceId, "shell", "input", "keyevent", "66"], {
355075
355116
  timeout: 1e4,
355076
355117
  windowsHide: true
355077
355118
  });
@@ -355592,7 +355633,14 @@ async function executeMobileTest(options) {
355592
355633
  const resetAllowed = shouldResetMobileAppData(options.target.platform, selectedDevice.id);
355593
355634
  if (resetAllowed) {
355594
355635
  try {
355595
- await executeMobileScript(sessionId, "mobile: clearApp", [{ appId: options.target.appId }]);
355636
+ if (options.target.platform === "IOS") {
355637
+ await terminateTargetApp(sessionId, "IOS", options.target.appId).catch(() => {
355638
+ });
355639
+ const wiped = await clearIosSimulatorAppData(selectedDevice.id, options.target.appId);
355640
+ if (!wiped.ok) throw new Error(wiped.detail);
355641
+ } else {
355642
+ await executeMobileScript(sessionId, "mobile: clearApp", [{ appId: options.target.appId }]);
355643
+ }
355596
355644
  clearedAppDataBeforeLaunch = true;
355597
355645
  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).");
355598
355646
  } catch (err) {
@@ -356250,10 +356298,10 @@ function decideMobileDeviceGate(input) {
356250
356298
 
356251
356299
  // src/services/mobile-bridge.ts
356252
356300
  var import_http = require("http");
356253
- var import_child_process3 = require("child_process");
356301
+ var import_child_process4 = require("child_process");
356254
356302
  var import_crypto = require("crypto");
356255
356303
  var import_os4 = require("os");
356256
- var import_path3 = require("path");
356304
+ var import_path4 = require("path");
356257
356305
  var import_fs3 = require("fs");
356258
356306
  init_dist();
356259
356307
  var MOBILE_BRIDGE_PORT2 = 9515;
@@ -356360,7 +356408,7 @@ async function getWindowSize3() {
356360
356408
  }
356361
356409
  var BRIDGE_PROCESS_MARKER = "validateqa-mobile-bridge";
356362
356410
  var BRIDGE_PORT = MOBILE_BRIDGE_PORT2;
356363
- var BRIDGE_PID_FILE = (0, import_path3.join)((0, import_os4.homedir)(), ".validate.qa", "mobile-bridge.pid");
356411
+ var BRIDGE_PID_FILE = (0, import_path4.join)((0, import_os4.homedir)(), ".validate.qa", "mobile-bridge.pid");
356364
356412
  function readBridgePidFile() {
356365
356413
  try {
356366
356414
  const raw = (0, import_fs3.readFileSync)(BRIDGE_PID_FILE, "utf-8").trim();
@@ -356372,7 +356420,7 @@ function readBridgePidFile() {
356372
356420
  }
356373
356421
  function writeBridgePidFile(pid) {
356374
356422
  try {
356375
- (0, import_fs3.mkdirSync)((0, import_path3.dirname)(BRIDGE_PID_FILE), { recursive: true, mode: 448 });
356423
+ (0, import_fs3.mkdirSync)((0, import_path4.dirname)(BRIDGE_PID_FILE), { recursive: true, mode: 448 });
356376
356424
  (0, import_fs3.writeFileSync)(BRIDGE_PID_FILE, String(pid), { mode: 384 });
356377
356425
  } catch {
356378
356426
  }
@@ -356387,19 +356435,19 @@ function killStaleBridge() {
356387
356435
  const pid = readBridgePidFile();
356388
356436
  if (!pid) return;
356389
356437
  try {
356390
- (0, import_child_process3.execFileSync)("kill", ["-0", String(pid)], { stdio: "ignore" });
356438
+ (0, import_child_process4.execFileSync)("kill", ["-0", String(pid)], { stdio: "ignore" });
356391
356439
  } catch {
356392
356440
  clearBridgePidFile();
356393
356441
  return;
356394
356442
  }
356395
356443
  try {
356396
- const pidsOnPort = (0, import_child_process3.execFileSync)("lsof", ["-ti", `:${BRIDGE_PORT}`], { encoding: "utf-8" }).trim();
356444
+ const pidsOnPort = (0, import_child_process4.execFileSync)("lsof", ["-ti", `:${BRIDGE_PORT}`], { encoding: "utf-8" }).trim();
356397
356445
  const pidList = pidsOnPort.split("\n").filter(Boolean);
356398
356446
  if (!pidList.includes(String(pid))) {
356399
356447
  return;
356400
356448
  }
356401
- (0, import_child_process3.execFileSync)("kill", ["-9", String(pid)], { stdio: "ignore" });
356402
- (0, import_child_process3.execFileSync)("sleep", ["1"], { stdio: "ignore" });
356449
+ (0, import_child_process4.execFileSync)("kill", ["-9", String(pid)], { stdio: "ignore" });
356450
+ (0, import_child_process4.execFileSync)("sleep", ["1"], { stdio: "ignore" });
356403
356451
  } catch {
356404
356452
  } finally {
356405
356453
  clearBridgePidFile();
@@ -357498,6 +357546,7 @@ var MobileBridgeDriver = class {
357498
357546
  sessionId;
357499
357547
  platform;
357500
357548
  appId;
357549
+ deviceId;
357501
357550
  allowAppReset;
357502
357551
  recoverSession;
357503
357552
  maxSessionRecoveries;
@@ -357518,6 +357567,7 @@ var MobileBridgeDriver = class {
357518
357567
  this.sessionId = config.sessionId;
357519
357568
  this.platform = config.platform;
357520
357569
  this.appId = config.appId;
357570
+ this.deviceId = config.deviceId;
357521
357571
  this.allowAppReset = config.allowAppReset === true;
357522
357572
  this.recoverSession = config.recoverSession;
357523
357573
  this.maxSessionRecoveries = Math.max(0, Math.floor(config.maxSessionRecoveries ?? 1));
@@ -357815,17 +357865,34 @@ var MobileBridgeDriver = class {
357815
357865
  });
357816
357866
  }
357817
357867
  /**
357818
- * Deterministic state reset: clear app data + relaunch (Android emulators
357819
- * only, per the runner-computed allowAppReset policy). Returns false when
357820
- * unsupported/disallowed or when clearApp throws — callers fall back to
357821
- * relaunch(), preserving today's behavior. On success, waits for cold-boot
357822
- * content so recording and replay agree on the true entry screen.
357868
+ * Deterministic state reset: clear app data + relaunch, per the
357869
+ * runner-computed allowAppReset policy (Android emulators / iOS simulators
357870
+ * only — a personal physical device is never wiped). Android clears via
357871
+ * `mobile: clearApp`; iOS terminates the app and wipes its simulator data
357872
+ * container + keychain through `simctl` (see ios-simulator-reset.ts).
357873
+ * Returns false when unsupported/disallowed or when the clear fails —
357874
+ * callers fall back to relaunch(), preserving today's behavior. On success,
357875
+ * waits for cold-boot content so recording and replay agree on the true
357876
+ * entry screen.
357823
357877
  */
357824
357878
  async resetAppState() {
357825
- if (!this.allowAppReset || this.platform !== "ANDROID") return false;
357879
+ if (!this.allowAppReset) return false;
357880
+ if (this.platform === "IOS" && !isIosSimulatorUdid(this.deviceId)) return false;
357826
357881
  return this.withSessionRecovery("app data reset", async () => {
357827
357882
  try {
357828
- await this.executeScript("mobile: clearApp", [{ appId: this.appId }]);
357883
+ if (this.platform === "IOS") {
357884
+ try {
357885
+ await this.executeScript("mobile: terminateApp", [{ ...this.appLifecycleArgs(), timeout: 5e3 }]);
357886
+ } catch (err) {
357887
+ if (isRecoverableAppiumSessionFailure(err)) throw err;
357888
+ }
357889
+ const wiped = await clearIosSimulatorAppData(this.deviceId ?? "", this.appId);
357890
+ if (!wiped.ok) {
357891
+ throw new Error(`iOS simulator app-data reset failed: ${wiped.detail}`);
357892
+ }
357893
+ } else {
357894
+ await this.executeScript("mobile: clearApp", [{ appId: this.appId }]);
357895
+ }
357829
357896
  await this.executeScript("mobile: activateApp", [this.appLifecycleArgs()]);
357830
357897
  try {
357831
357898
  await waitForColdBootSettle(this.sessionPath);
@@ -357964,7 +358031,7 @@ function readActiveBundleId2(result) {
357964
358031
  }
357965
358032
 
357966
358033
  // src/services/doctor.ts
357967
- var import_child_process4 = require("child_process");
358034
+ var import_child_process5 = require("child_process");
357968
358035
  var import_fs4 = require("fs");
357969
358036
  var import_os5 = require("os");
357970
358037
  var C = {
@@ -357984,7 +358051,7 @@ function computeDoctorReadiness(web, ios, android) {
357984
358051
  }
357985
358052
  function run(cmd) {
357986
358053
  try {
357987
- return (0, import_child_process4.execSync)(cmd, { encoding: "utf-8", timeout: 15e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
358054
+ return (0, import_child_process5.execSync)(cmd, { encoding: "utf-8", timeout: 15e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
357988
358055
  } catch {
357989
358056
  return null;
357990
358057
  }
@@ -358491,7 +358558,7 @@ var log = {
358491
358558
  dim: (msg) => console.log(`${C2.gray}${msg}${C2.reset}`)
358492
358559
  };
358493
358560
  function getRunsDir() {
358494
- const dir = process.env.RUNS_DIR || (0, import_path4.join)((0, import_os6.homedir)(), ".validate.qa", "runs");
358561
+ const dir = process.env.RUNS_DIR || (0, import_path5.join)((0, import_os6.homedir)(), ".validate.qa", "runs");
358495
358562
  (0, import_runner_core12.ensurePrivateDirectory)(dir);
358496
358563
  return dir;
358497
358564
  }
@@ -360423,9 +360490,11 @@ ${finalVerifyResult.logs ?? ""}`;
360423
360490
  sessionId: session.sessionId,
360424
360491
  platform: platform3,
360425
360492
  appId: session.appId,
360426
- // Deterministic-state authorization: Android emulators only by
360427
- // default (VALIDATEQA_MOBILE_APP_RESET overrides) — a personal
360428
- // physical device must never have its app data wiped.
360493
+ deviceId: session.deviceId,
360494
+ // Deterministic-state authorization: Android emulators / iOS
360495
+ // simulators only (VALIDATEQA_MOBILE_APP_RESET overrides the
360496
+ // Android default) — a personal physical device must never have
360497
+ // its app data wiped.
360429
360498
  allowAppReset: mobileTarget.resetPolicy !== "PRESERVE_APP_DATA" && shouldResetMobileAppData(platform3, session.deviceId),
360430
360499
  maxSessionRecoveries: 1,
360431
360500
  recoverSession: async (cause) => {
@@ -360941,6 +361010,7 @@ ${finalVerifyResult.logs ?? ""}`;
360941
361010
  sessionId: opened.sessionId,
360942
361011
  platform: target.platform,
360943
361012
  appId: opened.appId,
361013
+ deviceId: opened.deviceId,
360944
361014
  allowAppReset: target.resetPolicy !== "PRESERVE_APP_DATA" && shouldResetMobileAppData(target.platform, opened.deviceId)
360945
361015
  });
360946
361016
  };