@swmansion/argent 0.25.1-next.33 → 0.25.1-next.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli-cmds.mjs CHANGED
@@ -2372,6 +2372,8 @@ var FAILURE_CODES = {
2372
2372
  ANDROID_DEVTOOLS_WRONG_PLATFORM: "ANDROID_DEVTOOLS_WRONG_PLATFORM",
2373
2373
  ANDROID_DEVTOOLS_DEVICE_ID_INVALID: "ANDROID_DEVTOOLS_DEVICE_ID_INVALID",
2374
2374
  ANDROID_DEVTOOLS_HELPER_TERMINATED: "ANDROID_DEVTOOLS_HELPER_TERMINATED",
2375
+ ANDROID_DEVTOOLS_HELPER_INSTALL_FAILED: "ANDROID_DEVTOOLS_HELPER_INSTALL_FAILED",
2376
+ ANDROID_DEVTOOLS_HELPER_REPAIR_FAILED: "ANDROID_DEVTOOLS_HELPER_REPAIR_FAILED",
2375
2377
  ANDROID_DEVTOOLS_RPC_CLIENT_CLOSED: "ANDROID_DEVTOOLS_RPC_CLIENT_CLOSED",
2376
2378
  ANDROID_DEVTOOLS_RPC_ERROR: "ANDROID_DEVTOOLS_RPC_ERROR",
2377
2379
  ANDROID_DEVTOOLS_RPC_TIMEOUT: "ANDROID_DEVTOOLS_RPC_TIMEOUT",
@@ -21857,7 +21859,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
21857
21859
  var SESSION_ID2 = randomUUID5();
21858
21860
  function readCliVersion() {
21859
21861
  if (true) {
21860
- return "0.25.1-next.33";
21862
+ return "0.25.1-next.35";
21861
21863
  }
21862
21864
  return "0.0.0";
21863
21865
  }
@@ -15744,6 +15744,8 @@ var FAILURE_CODES = {
15744
15744
  ANDROID_DEVTOOLS_WRONG_PLATFORM: "ANDROID_DEVTOOLS_WRONG_PLATFORM",
15745
15745
  ANDROID_DEVTOOLS_DEVICE_ID_INVALID: "ANDROID_DEVTOOLS_DEVICE_ID_INVALID",
15746
15746
  ANDROID_DEVTOOLS_HELPER_TERMINATED: "ANDROID_DEVTOOLS_HELPER_TERMINATED",
15747
+ ANDROID_DEVTOOLS_HELPER_INSTALL_FAILED: "ANDROID_DEVTOOLS_HELPER_INSTALL_FAILED",
15748
+ ANDROID_DEVTOOLS_HELPER_REPAIR_FAILED: "ANDROID_DEVTOOLS_HELPER_REPAIR_FAILED",
15747
15749
  ANDROID_DEVTOOLS_RPC_CLIENT_CLOSED: "ANDROID_DEVTOOLS_RPC_CLIENT_CLOSED",
15748
15750
  ANDROID_DEVTOOLS_RPC_ERROR: "ANDROID_DEVTOOLS_RPC_ERROR",
15749
15751
  ANDROID_DEVTOOLS_RPC_TIMEOUT: "ANDROID_DEVTOOLS_RPC_TIMEOUT",
@@ -16710,7 +16712,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
16710
16712
  var SESSION_ID = randomUUID4();
16711
16713
  function readCliVersion() {
16712
16714
  if (true) {
16713
- return "0.25.1-next.33";
16715
+ return "0.25.1-next.35";
16714
16716
  }
16715
16717
  return "0.0.0";
16716
16718
  }
@@ -16552,6 +16552,8 @@ var FAILURE_CODES = {
16552
16552
  ANDROID_DEVTOOLS_WRONG_PLATFORM: "ANDROID_DEVTOOLS_WRONG_PLATFORM",
16553
16553
  ANDROID_DEVTOOLS_DEVICE_ID_INVALID: "ANDROID_DEVTOOLS_DEVICE_ID_INVALID",
16554
16554
  ANDROID_DEVTOOLS_HELPER_TERMINATED: "ANDROID_DEVTOOLS_HELPER_TERMINATED",
16555
+ ANDROID_DEVTOOLS_HELPER_INSTALL_FAILED: "ANDROID_DEVTOOLS_HELPER_INSTALL_FAILED",
16556
+ ANDROID_DEVTOOLS_HELPER_REPAIR_FAILED: "ANDROID_DEVTOOLS_HELPER_REPAIR_FAILED",
16555
16557
  ANDROID_DEVTOOLS_RPC_CLIENT_CLOSED: "ANDROID_DEVTOOLS_RPC_CLIENT_CLOSED",
16556
16558
  ANDROID_DEVTOOLS_RPC_ERROR: "ANDROID_DEVTOOLS_RPC_ERROR",
16557
16559
  ANDROID_DEVTOOLS_RPC_TIMEOUT: "ANDROID_DEVTOOLS_RPC_TIMEOUT",
@@ -698,6 +698,8 @@ var init_failure_codes = __esm({
698
698
  ANDROID_DEVTOOLS_WRONG_PLATFORM: "ANDROID_DEVTOOLS_WRONG_PLATFORM",
699
699
  ANDROID_DEVTOOLS_DEVICE_ID_INVALID: "ANDROID_DEVTOOLS_DEVICE_ID_INVALID",
700
700
  ANDROID_DEVTOOLS_HELPER_TERMINATED: "ANDROID_DEVTOOLS_HELPER_TERMINATED",
701
+ ANDROID_DEVTOOLS_HELPER_INSTALL_FAILED: "ANDROID_DEVTOOLS_HELPER_INSTALL_FAILED",
702
+ ANDROID_DEVTOOLS_HELPER_REPAIR_FAILED: "ANDROID_DEVTOOLS_HELPER_REPAIR_FAILED",
701
703
  ANDROID_DEVTOOLS_RPC_CLIENT_CLOSED: "ANDROID_DEVTOOLS_RPC_CLIENT_CLOSED",
702
704
  ANDROID_DEVTOOLS_RPC_ERROR: "ANDROID_DEVTOOLS_RPC_ERROR",
703
705
  ANDROID_DEVTOOLS_RPC_TIMEOUT: "ANDROID_DEVTOOLS_RPC_TIMEOUT",
@@ -77006,8 +77008,8 @@ async function emulatorSupportsFlag(flag, options = {}) {
77006
77008
  } catch {
77007
77009
  return false;
77008
77010
  }
77009
- const cacheKey3 = `${emulatorPath}|${flag}`;
77010
- const cached3 = emulatorFlagSupportCache.get(cacheKey3);
77011
+ const cacheKey2 = `${emulatorPath}|${flag}`;
77012
+ const cached3 = emulatorFlagSupportCache.get(cacheKey2);
77011
77013
  if (cached3 !== void 0) return cached3;
77012
77014
  let output;
77013
77015
  let failure = null;
@@ -77034,7 +77036,7 @@ async function emulatorSupportsFlag(flag, options = {}) {
77034
77036
  return false;
77035
77037
  }
77036
77038
  const supported = output.includes(flag);
77037
- emulatorFlagSupportCache.set(cacheKey3, supported);
77039
+ emulatorFlagSupportCache.set(cacheKey2, supported);
77038
77040
  return supported;
77039
77041
  }
77040
77042
  function describeAdbFailure(args, err) {
@@ -94704,7 +94706,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
94704
94706
  var SESSION_ID = (0, import_node_crypto3.randomUUID)();
94705
94707
  function readCliVersion() {
94706
94708
  if (true) {
94707
- return "0.25.1-next.33";
94709
+ return "0.25.1-next.35";
94708
94710
  }
94709
94711
  return "0.0.0";
94710
94712
  }
@@ -107264,6 +107266,7 @@ async function openMoqClient(udid) {
107264
107266
  async function openMoqClientFromInfo(info) {
107265
107267
  await installNodeWebTransport();
107266
107268
  const session = new MoqDeviceSession(await connectMoq(info), { publishPath: PUBLISH_PATH });
107269
+ disposeWhenTransportDies(session);
107267
107270
  return {
107268
107271
  sendControl: (payload) => session.sendControl(payload),
107269
107272
  async screenshot(opts) {
@@ -107274,6 +107277,15 @@ async function openMoqClientFromInfo(info) {
107274
107277
  }
107275
107278
  };
107276
107279
  }
107280
+ function disposeWhenTransportDies(session) {
107281
+ const dispose = () => {
107282
+ try {
107283
+ session.close();
107284
+ } catch {
107285
+ }
107286
+ };
107287
+ void session.closed.then(dispose, dispose);
107288
+ }
107277
107289
 
107278
107290
  // ../tool-server/src/utils/simulator-client.ts
107279
107291
  init_wrapper();
@@ -107342,17 +107354,26 @@ function failAllPending(conn, makeError) {
107342
107354
  conn.pending.clear();
107343
107355
  for (const entry of entries) entry.settle(makeError(entry.cmd));
107344
107356
  }
107357
+ var COMMAND_TRANSPORT_FAILURE = {
107358
+ error_code: FAILURE_CODES.SIMULATOR_COMMAND_TRANSPORT_FAILED,
107359
+ failure_stage: "simulator_command_transport",
107360
+ failure_area: "tool_server",
107361
+ error_kind: "network",
107362
+ network_failure: "connection_reset",
107363
+ failure_command: "simulator_server"
107364
+ };
107345
107365
  function transportError(cmd, apiUrl, detail) {
107346
107366
  return new FailureError(
107347
107367
  `simulator-server did not accept the '${cmd}' command: ${detail}. The command was NOT delivered to the device. Check that the simulator is still booted and the simulator-server for ${apiUrl} is running.`,
107348
- {
107349
- error_code: FAILURE_CODES.SIMULATOR_COMMAND_TRANSPORT_FAILED,
107350
- failure_stage: "simulator_command_transport",
107351
- failure_area: "tool_server",
107352
- error_kind: "network",
107353
- network_failure: "connection_reset",
107354
- failure_command: "simulator_server"
107355
- }
107368
+ COMMAND_TRANSPORT_FAILURE
107369
+ );
107370
+ }
107371
+ function remoteTransportError(cmd, cause) {
107372
+ const detail = cause instanceof Error ? cause.message : String(cause);
107373
+ return wrapFailure(
107374
+ cause,
107375
+ COMMAND_TRANSPORT_FAILURE,
107376
+ `The remote simulator did not accept the '${cmd}' command: ${detail}. The command was NOT delivered to the device. Call stop-simulator-server for this device and retry - the next call opens a new cloud session. If the machine itself was released, acquire one again before retrying.`
107356
107377
  );
107357
107378
  }
107358
107379
  function getOrCreateConnection(api) {
@@ -107408,13 +107429,10 @@ function getOrCreateConnection(api) {
107408
107429
  return conn;
107409
107430
  }
107410
107431
  function sendCommand(api, cmd) {
107411
- if (api.transport) {
107412
- routeViaTransport(api.transport, cmd);
107413
- return Promise.resolve();
107414
- }
107432
+ const cmdName = typeof cmd.cmd === "string" ? cmd.cmd : "unknown";
107433
+ if (api.transport) return sendViaTransport(api.transport, cmd, cmdName);
107415
107434
  const conn = getOrCreateConnection(api);
107416
107435
  const id = String(++cmdId);
107417
- const cmdName = typeof cmd.cmd === "string" ? cmd.cmd : "unknown";
107418
107436
  const payload = JSON.stringify({ id, ...cmd });
107419
107437
  return new Promise((resolve14, reject) => {
107420
107438
  let done = false;
@@ -107616,32 +107634,36 @@ async function httpScreenshot(api, rotation, signal, scale) {
107616
107634
  );
107617
107635
  }
107618
107636
  }
107637
+ async function sendViaTransport(transport, cmd, cmdName) {
107638
+ const sent = routeViaTransport(transport, cmd);
107639
+ try {
107640
+ await sent;
107641
+ } catch (cause) {
107642
+ throw remoteTransportError(cmdName, cause);
107643
+ }
107644
+ }
107619
107645
  function routeViaTransport(transport, cmd) {
107620
107646
  switch (cmd.cmd) {
107621
107647
  case "touch": {
107622
107648
  const sx = cmd.second_x ?? cmd.secondX;
107623
107649
  const sy = cmd.second_y ?? cmd.secondY;
107624
- transport.touch({
107650
+ return transport.touch({
107625
107651
  type: cmd.type,
107626
107652
  x: cmd.x,
107627
107653
  y: cmd.y,
107628
107654
  secondX: sx == null ? void 0 : sx,
107629
107655
  secondY: sy == null ? void 0 : sy
107630
107656
  });
107631
- return;
107632
107657
  }
107633
107658
  case "button":
107634
- transport.button({
107659
+ return transport.button({
107635
107660
  direction: cmd.direction,
107636
107661
  button: cmd.button
107637
107662
  });
107638
- return;
107639
107663
  case "rotate":
107640
- transport.rotate(cmd.direction);
107641
- return;
107664
+ return transport.rotate(cmd.direction);
107642
107665
  case "key":
107643
- transport.pressKey(cmd.direction, cmd.code);
107644
- return;
107666
+ return transport.pressKey(cmd.direction, cmd.code);
107645
107667
  default:
107646
107668
  throw new Error(`MoQ transport does not implement sendCommand cmd '${String(cmd.cmd)}'`);
107647
107669
  }
@@ -107655,8 +107677,8 @@ function createMoqTransport(moq, options) {
107655
107677
  return { url: (0, import_node_url.pathToFileURL)(file2).toString(), path: file2 };
107656
107678
  };
107657
107679
  return {
107658
- touch(opts) {
107659
- void moq.sendControl(
107680
+ async touch(opts) {
107681
+ await moq.sendControl(
107660
107682
  encodeTouch({
107661
107683
  action: opts.type,
107662
107684
  x: opts.x,
@@ -107666,17 +107688,17 @@ function createMoqTransport(moq, options) {
107666
107688
  })
107667
107689
  );
107668
107690
  },
107669
- button(opts) {
107670
- void moq.sendControl(encodeButton({ action: opts.direction, button: opts.button }));
107691
+ async button(opts) {
107692
+ await moq.sendControl(encodeButton({ action: opts.direction, button: opts.button }));
107671
107693
  },
107672
- rotate(direction) {
107673
- void moq.sendControl(encodeRotate(direction));
107694
+ async rotate(direction) {
107695
+ await moq.sendControl(encodeRotate(direction));
107674
107696
  },
107675
107697
  async paste(text) {
107676
107698
  await options.pasteText(text);
107677
107699
  },
107678
- pressKey(direction, keyCode) {
107679
- void moq.sendControl(encodeKey({ action: direction, code: keyCode }));
107700
+ async pressKey(direction, keyCode) {
107701
+ await moq.sendControl(encodeKey({ action: direction, code: keyCode }));
107680
107702
  },
107681
107703
  async screenshot(opts) {
107682
107704
  const scale = opts?.scale ?? getScreenshotScale();
@@ -107709,17 +107731,20 @@ async function buildRemoteInstance(device) {
107709
107731
  await simctlPbcopy(device.id, text);
107710
107732
  const CMD = 227;
107711
107733
  const V = 25;
107712
- await moq.sendControl(encodeKey({ action: "Down", code: CMD }));
107713
- await moq.sendControl(encodeKey({ action: "Down", code: V }));
107714
- await moq.sendControl(encodeKey({ action: "Up", code: V }));
107715
- await moq.sendControl(encodeKey({ action: "Up", code: CMD }));
107734
+ await api.pressKey("Down", CMD);
107735
+ await api.pressKey("Down", V);
107736
+ await api.pressKey("Up", V);
107737
+ await api.pressKey("Up", CMD);
107716
107738
  }
107717
107739
  });
107718
107740
  const stubUrl = `moq+remote://${device.id}`;
107719
107741
  const api = {
107720
107742
  apiUrl: stubUrl,
107721
107743
  streamUrl: stubUrl,
107722
- pressKey: (direction, keyCode) => moq.sendControl(encodeKey({ action: direction, code: keyCode })),
107744
+ // Through `sendCommand`, as the attached instance below does, so a key the
107745
+ // session refuses reports the same failure as a refused touch instead of a
107746
+ // bare SDK error the caller cannot classify.
107747
+ pressKey: (direction, keyCode) => sendCommand(api, { cmd: "key", code: keyCode, direction }),
107723
107748
  transport
107724
107749
  };
107725
107750
  return {
@@ -112874,10 +112899,6 @@ init_android_binary();
112874
112899
 
112875
112900
  // ../tool-server/src/utils/android-helper-install.ts
112876
112901
  init_adb();
112877
- var installedHelpers = /* @__PURE__ */ new Map();
112878
- function cacheKey(serial, versionCode) {
112879
- return `${serial}|${versionCode}`;
112880
- }
112881
112902
  async function probeInstalledVersion(serial, packageName) {
112882
112903
  let out;
112883
112904
  try {
@@ -112900,17 +112921,17 @@ async function probeInstalledVersion(serial, packageName) {
112900
112921
  }
112901
112922
  return { installed: false, versionCode: null };
112902
112923
  }
112903
- async function ensureAndroidDevtoolsInstalled(serial) {
112924
+ async function ensureAndroidDevtoolsInstalled(serial, options = {}) {
112904
112925
  const manifest = helperManifest();
112905
- const key2 = cacheKey(serial, manifest.versionCode);
112906
- if (installedHelpers.has(key2)) return;
112907
- const probe3 = await probeInstalledVersion(serial, manifest.packageName);
112908
- if (probe3.installed && probe3.versionCode !== null && probe3.versionCode >= manifest.versionCode) {
112909
- installedHelpers.set(key2, true);
112910
- return;
112926
+ if (!options.force) {
112927
+ const probe3 = await probeInstalledVersion(serial, manifest.packageName);
112928
+ if (probe3.installed && (probe3.versionCode === null || probe3.versionCode >= manifest.versionCode)) {
112929
+ return;
112930
+ }
112911
112931
  }
112912
112932
  const apkPath = bundledHelperApkPath();
112913
- const args = ["-s", serial, "install", ...manifest.installFlags, apkPath];
112933
+ const flags = options.force ? [...manifest.installFlags, "-d"] : manifest.installFlags;
112934
+ const args = ["-s", serial, "install", ...flags, apkPath];
112914
112935
  try {
112915
112936
  await runAdb(args, { timeoutMs: 6e4 });
112916
112937
  } catch (err) {
@@ -112925,7 +112946,6 @@ async function ensureAndroidDevtoolsInstalled(serial) {
112925
112946
  throw err;
112926
112947
  }
112927
112948
  }
112928
- installedHelpers.set(key2, true);
112929
112949
  }
112930
112950
 
112931
112951
  // ../tool-server/src/utils/android-devtools-client.ts
@@ -113069,14 +113089,28 @@ function androidDevtoolsRef(device) {
113069
113089
  };
113070
113090
  }
113071
113091
  var READY_TIMEOUT_MS2 = 3e4;
113092
+ var EXIT_GRACE_MS = 200;
113072
113093
  var HELPER_PORT_MARKER = /^INSTRUMENTATION_STATUS:\s*port=(\d+)/;
113073
113094
  var ADB_FORWARD_PORT_MARKER = /^(\d+)\s*$/;
113095
+ var HELPER_STATUS_MARKER = /^INSTRUMENTATION_STATUS:\s*Error=/;
113096
+ var HELPER_STATUS_MAX_CHARS = 400;
113097
+ function classifyHelperSpawnFault(text) {
113098
+ return /Unable to find instrumentation (?:info|target package)/i.test(text) ? "instrumentation-missing" : "unknown";
113099
+ }
113100
+ var HelperSpawnError = class extends FailureError {
113101
+ fault;
113102
+ constructor(message, signal, fault) {
113103
+ super(message, signal);
113104
+ this.name = "HelperSpawnError";
113105
+ this.fault = fault;
113106
+ }
113107
+ };
113074
113108
  async function spawnHelper(serial) {
113075
113109
  const manifest = helperManifest();
113076
113110
  const adbPath = await resolveAndroidBinary("adb");
113077
113111
  if (!adbPath) {
113078
113112
  throw new FailureError(
113079
- "`adb` not found on PATH or under `$ANDROID_HOME/platform-tools` while spawning android-devtools helper.",
113113
+ "`adb` not found on PATH or under `$ANDROID_HOME/platform-tools` while spawning the argent android helper.",
113080
113114
  {
113081
113115
  error_code: FAILURE_CODES.ANDROID_DEVTOOLS_ADB_NOT_FOUND,
113082
113116
  failure_stage: "android_devtools_spawn_helper",
@@ -113095,10 +113129,13 @@ async function spawnHelper(serial) {
113095
113129
  let localPort = null;
113096
113130
  let settled = false;
113097
113131
  let stderrBuf = "";
113132
+ let statusBuf = "";
113133
+ let exitGrace;
113098
113134
  const settle = (fn, cleanup) => {
113099
113135
  if (settled) return;
113100
113136
  settled = true;
113101
113137
  clearTimeout(timer);
113138
+ clearTimeout(exitGrace);
113102
113139
  rl.close();
113103
113140
  cleanup?.();
113104
113141
  fn();
@@ -113106,6 +113143,9 @@ async function spawnHelper(serial) {
113106
113143
  const rl = readline2.createInterface({ input: proc.stdout });
113107
113144
  rl.on("line", async (rawLine) => {
113108
113145
  const line = rawLine.trim();
113146
+ if (statusBuf.length < HELPER_STATUS_MAX_CHARS && HELPER_STATUS_MARKER.test(line)) {
113147
+ statusBuf = (statusBuf ? `${statusBuf} ${line}` : line).slice(0, HELPER_STATUS_MAX_CHARS);
113148
+ }
113109
113149
  const portMatch = HELPER_PORT_MARKER.exec(line);
113110
113150
  if (!portMatch || devicePort !== null) return;
113111
113151
  devicePort = parseInt(portMatch[1], 10);
@@ -113135,25 +113175,32 @@ async function spawnHelper(serial) {
113135
113175
  stderrBuf += data.toString("utf-8");
113136
113176
  if (stderrBuf.length > 4 * 1024) stderrBuf = stderrBuf.slice(-4 * 1024);
113137
113177
  });
113138
- proc.on("exit", (code, signal) => {
113139
- const detail = stderrBuf.trim() ? ` stderr=${stderrBuf.trim().slice(0, 400)}` : "";
113140
- settle(
113141
- () => reject(
113142
- new FailureError(
113143
- `am instrument exited before becoming ready (code=${code} signal=${signal}).${detail}`,
113144
- {
113145
- error_code: FAILURE_CODES.ANDROID_DEVTOOLS_HELPER_EXITED_BEFORE_READY,
113146
- failure_stage: "android_devtools_helper_ready",
113147
- failure_area: "tool_server",
113148
- error_kind: "subprocess",
113149
- failure_command: "android_devtools",
113150
- ...typeof code === "number" ? { failure_exit_code: code } : {},
113151
- ...signal === "SIGABRT" || signal === "SIGHUP" || signal === "SIGINT" || signal === "SIGKILL" || signal === "SIGQUIT" || signal === "SIGTERM" ? { failure_signal: signal } : {}
113152
- }
113153
- )
113178
+ let exitCode = null;
113179
+ let exitSignal = null;
113180
+ const rejectExited = () => {
113181
+ const detail = statusBuf ? statusBuf : stderrBuf.trim() ? `code=${exitCode} signal=${exitSignal}. stderr=${stderrBuf.trim().slice(0, 200)}` : `code=${exitCode} signal=${exitSignal}`;
113182
+ reject(
113183
+ new HelperSpawnError(
113184
+ `am instrument exited before becoming ready: ${detail}`,
113185
+ {
113186
+ error_code: FAILURE_CODES.ANDROID_DEVTOOLS_HELPER_EXITED_BEFORE_READY,
113187
+ failure_stage: "android_devtools_helper_ready",
113188
+ failure_area: "tool_server",
113189
+ error_kind: "subprocess",
113190
+ failure_command: "android_devtools",
113191
+ ...typeof exitCode === "number" ? { failure_exit_code: exitCode } : {},
113192
+ ...exitSignal === "SIGABRT" || exitSignal === "SIGHUP" || exitSignal === "SIGINT" || exitSignal === "SIGKILL" || exitSignal === "SIGQUIT" || exitSignal === "SIGTERM" ? { failure_signal: exitSignal } : {}
113193
+ },
113194
+ classifyHelperSpawnFault(statusBuf)
113154
113195
  )
113155
113196
  );
113197
+ };
113198
+ proc.on("exit", (code, signal) => {
113199
+ exitCode = code;
113200
+ exitSignal = signal;
113201
+ exitGrace = setTimeout(() => settle(rejectExited), EXIT_GRACE_MS);
113156
113202
  });
113203
+ proc.on("close", () => settle(rejectExited));
113157
113204
  proc.on("error", (err) => {
113158
113205
  settle(
113159
113206
  () => reject(
@@ -113188,6 +113235,50 @@ async function spawnHelper(serial) {
113188
113235
  }, READY_TIMEOUT_MS2);
113189
113236
  });
113190
113237
  }
113238
+ async function installHelper(serial, options) {
113239
+ try {
113240
+ await ensureAndroidDevtoolsInstalled(serial, options);
113241
+ } catch (err) {
113242
+ const cause = err instanceof Error ? err : new Error(String(err));
113243
+ const reason = cause.message.replace(/^.*? failed: /s, "").replace(/\s+/g, " ").trim().slice(0, 200);
113244
+ throw new FailureError(
113245
+ `the argent android helper is not installed on ${serial} and could not be installed: ${reason}`,
113246
+ {
113247
+ error_code: FAILURE_CODES.ANDROID_DEVTOOLS_HELPER_INSTALL_FAILED,
113248
+ failure_stage: "android_devtools_helper_install",
113249
+ failure_area: "tool_server",
113250
+ error_kind: "subprocess",
113251
+ failure_command: "adb"
113252
+ },
113253
+ { cause }
113254
+ );
113255
+ }
113256
+ }
113257
+ async function spawnHelperWithRepair(serial) {
113258
+ await installHelper(serial, {});
113259
+ try {
113260
+ return await spawnHelper(serial);
113261
+ } catch (err) {
113262
+ if (!(err instanceof HelperSpawnError) || err.fault !== "instrumentation-missing") throw err;
113263
+ await installHelper(serial, { force: true });
113264
+ try {
113265
+ return await spawnHelper(serial);
113266
+ } catch (repairErr) {
113267
+ if (!(repairErr instanceof HelperSpawnError)) throw repairErr;
113268
+ throw new FailureError(
113269
+ `the argent android helper could not start on ${serial} even after reinstalling it: ${repairErr.message}`,
113270
+ {
113271
+ error_code: FAILURE_CODES.ANDROID_DEVTOOLS_HELPER_REPAIR_FAILED,
113272
+ failure_stage: "android_devtools_helper_repair",
113273
+ failure_area: "tool_server",
113274
+ error_kind: "subprocess",
113275
+ failure_command: "android_devtools"
113276
+ },
113277
+ { cause: repairErr }
113278
+ );
113279
+ }
113280
+ }
113281
+ }
113191
113282
  async function removeAdbForward(serial, localPort) {
113192
113283
  try {
113193
113284
  await runAdb(["-s", serial, "forward", "--remove", `tcp:${localPort}`], { timeoutMs: 5e3 });
@@ -113237,8 +113328,7 @@ var androidDevtoolsBlueprint = {
113237
113328
  }
113238
113329
  const serial = device.id;
113239
113330
  const events = new TypedEventEmitter();
113240
- await ensureAndroidDevtoolsInstalled(serial);
113241
- const spawned = await spawnHelper(serial);
113331
+ const spawned = await spawnHelperWithRepair(serial);
113242
113332
  let ready2 = false;
113243
113333
  let disposed = false;
113244
113334
  let client2;
@@ -115505,13 +115595,13 @@ async function ensureRunnerArtifact(config2, opts = {}) {
115505
115595
  xcodeVersionFingerprint()
115506
115596
  ]);
115507
115597
  const staticArgs = runnerBuildStaticArgs(projectPath, config2);
115508
- const cacheKey3 = computeRunnerCacheKey(sourcesHash, xcodeVersion, staticArgs);
115598
+ const cacheKey2 = computeRunnerCacheKey(sourcesHash, xcodeVersion, staticArgs);
115509
115599
  const derivedDataPath = derivedDir();
115510
115600
  const build = opts.build ?? buildRunnerArtifact;
115511
115601
  return withRunnerBuildLock(async () => {
115512
115602
  const stampPath = path21.join(derivedDataPath, CACHE_KEY_FILE);
115513
115603
  const stamped = await fsp.readFile(stampPath, "utf8").catch(() => null);
115514
- if (!opts.force && stamped === cacheKey3) {
115604
+ if (!opts.force && stamped === cacheKey2) {
115515
115605
  const cached3 = findBaseXctestrun(derivedDataPath);
115516
115606
  const expiresAt = await readRunnerExpiryDate(derivedDataPath);
115517
115607
  const needsRenew = expiresAt !== null && profileNeedsRenewal(expiresAt, /* @__PURE__ */ new Date());
@@ -115523,14 +115613,14 @@ async function ensureRunnerArtifact(config2, opts = {}) {
115523
115613
  };
115524
115614
  }
115525
115615
  }
115526
- if (stamped !== cacheKey3) {
115616
+ if (stamped !== cacheKey2) {
115527
115617
  await fsp.rm(derivedDataPath, {
115528
115618
  recursive: true,
115529
115619
  force: true
115530
115620
  });
115531
115621
  }
115532
115622
  const built = await build(derivedDataPath, staticArgs, opts.destinationUdid);
115533
- await fsp.writeFile(stampPath, cacheKey3);
115623
+ await fsp.writeFile(stampPath, cacheKey2);
115534
115624
  return built;
115535
115625
  });
115536
115626
  }
@@ -119829,17 +119919,17 @@ var reactProfilerSessionBlueprint = {
119829
119919
  }
119830
119920
  };
119831
119921
  var profilerPathsCache = /* @__PURE__ */ new Map();
119832
- function cacheKey2(port, deviceId) {
119922
+ function cacheKey(port, deviceId) {
119833
119923
  return `${port}:${deviceId}`;
119834
119924
  }
119835
119925
  function cacheProfilerPaths(port, paths, deviceId) {
119836
- profilerPathsCache.set(cacheKey2(port, deviceId), paths);
119926
+ profilerPathsCache.set(cacheKey(port, deviceId), paths);
119837
119927
  }
119838
119928
  function getCachedProfilerPaths(port, deviceId) {
119839
- return profilerPathsCache.get(cacheKey2(port, deviceId));
119929
+ return profilerPathsCache.get(cacheKey(port, deviceId));
119840
119930
  }
119841
119931
  function clearCachedProfilerPaths(port, deviceId) {
119842
- profilerPathsCache.delete(cacheKey2(port, deviceId));
119932
+ profilerPathsCache.delete(cacheKey(port, deviceId));
119843
119933
  }
119844
119934
 
119845
119935
  // ../tool-server/src/tools/devices/boot-device.ts
@@ -148067,10 +148157,7 @@ async function queryAndroidFullHierarchy(registry2, device) {
148067
148157
  devtools = await registry2.resolveService(ref.urn, ref.options);
148068
148158
  } catch (err) {
148069
148159
  const msg = err instanceof Error ? err.message : String(err);
148070
- throw new Error(
148071
- `the android devtools helper is unavailable (${msg}) \u2014 flows resolve testID selectors against the full hierarchy it serves; confirm the device is unlocked and the helper can be installed (\`adb install -t\`)`,
148072
- { cause: err }
148073
- );
148160
+ throw new Error(`the argent android helper is unavailable: ${msg}`, { cause: err });
148074
148161
  }
148075
148162
  const [{ xml }, size] = await Promise.all([
148076
148163
  // clearCache: await/assert polls must see text changes, not cached reads.
@@ -149661,6 +149748,10 @@ function roleOnlySelectorWarning(selector) {
149661
149748
  if (selector.text !== void 0 || selector.textMatches !== void 0) return void 0;
149662
149749
  return `selector ${describeSelector(selector)} matches by role alone (the tapped element has no id or visible text) \u2014 replay takes whichever element of that role ranks first, so re-record against a labelled element if that is not reliably this one`;
149663
149750
  }
149751
+ function innermostTreeReason(message) {
149752
+ const unwrapped = /helper is unavailable:\s*(.+)/s.exec(message)?.[1] ?? message;
149753
+ return unwrapped.replace(/^\[[^\]]+\]\s*/, "").trim();
149754
+ }
149664
149755
  async function captureTapSelector(registry2, session, udid, point) {
149665
149756
  try {
149666
149757
  const device = resolveDevice(udid);
@@ -149692,8 +149783,9 @@ async function captureTapSelector(registry2, session, udid, point) {
149692
149783
  ].filter((w) => w !== void 0);
149693
149784
  return { selector, ...warnings.length > 0 ? { warning: warnings.join("; ") } : {} };
149694
149785
  } catch (err) {
149786
+ const message = err instanceof Error ? err.message : String(err);
149695
149787
  return {
149696
- warning: `selector capture failed (${err instanceof Error ? err.message : String(err)}); kept coordinates`
149788
+ warning: `selector capture failed (${innermostTreeReason(message)}); kept coordinates`
149697
149789
  };
149698
149790
  }
149699
149791
  }
@@ -154201,9 +154293,9 @@ async function androidDevtoolsReady(registry2, device) {
154201
154293
  try {
154202
154294
  const ref = androidDevtoolsRef(device);
154203
154295
  const api = await registry2.resolveService(ref.urn, ref.options);
154204
- return api.isReady();
154205
- } catch {
154206
- return false;
154296
+ return { ready: api.isReady() };
154297
+ } catch (err) {
154298
+ return { ready: false, reason: err instanceof Error ? err.message : String(err) };
154207
154299
  }
154208
154300
  }
154209
154301
  async function treeSourceGate(registry2, device, bundleId, signal) {
@@ -154223,9 +154315,9 @@ async function treeSourceGate(registry2, device, bundleId, signal) {
154223
154315
  }
154224
154316
  }
154225
154317
  if (device.platform === "android" && !signal?.aborted) {
154226
- const ready2 = await androidDevtoolsReady(registry2, device);
154318
+ const { ready: ready2, reason } = await androidDevtoolsReady(registry2, device);
154227
154319
  if (!ready2 && !signal?.aborted) {
154228
- return `could not reach the Android devtools helper (full-hierarchy source for testID selectors). Confirm the device is unlocked and the argent helper can be installed (\`adb install -t\`); a locked device or a blocked install is the usual cause. Re-run once resolved.`;
154320
+ return reason ? `the argent android helper is unavailable: ${reason}` : `the argent android helper is unavailable (full-hierarchy source for testID selectors).`;
154229
154321
  }
154230
154322
  }
154231
154323
  if (device.platform === "vega" && !signal?.aborted) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swmansion/argent",
3
- "version": "0.25.1-next.33",
3
+ "version": "0.25.1-next.35",
4
4
  "mcpName": "io.github.software-mansion/argent",
5
5
  "description": "MCP server for iOS Simulator and Android Emulator control",
6
6
  "license": "Apache-2.0",
@@ -74,11 +74,11 @@ A normally injectable app that is broken in the environment gets the same coordi
74
74
 
75
75
  While the required source is down, selector failures and raw-point capture are void. Restore the source and re-record affected taps.
76
76
 
77
- | Platform | Symptom | Recovery |
78
- | -------- | -------------------------------- | ------------------------------------------------------ |
79
- | Android | Cannot reach the devtools helper | Unlock the device, allow `adb install -t`, and rerun |
80
- | Chromium | No reachable CDP session | Boot again with `electronAppPath` and remote debugging |
81
- | Vega | Toolkit returns no page source | Relaunch an app built with automation support |
77
+ | Platform | Symptom | Recovery |
78
+ | -------- | -------------------------------------- | -------------------------------------------------------------------------------- |
79
+ | Android | Cannot reach the argent android helper | Argent reinstalls it once; if it persists, read the device's reason in the error |
80
+ | Chromium | No reachable CDP session | Boot again with `electronAppPath` and remote debugging |
81
+ | Vega | Toolkit returns no page source | Relaunch an app built with automation support |
82
82
 
83
83
  On Android, healthy `describe` output does not prove the flow tree is available. It can fall back to legacy `uiautomator`, while the runner refuses that trimmed fallback.
84
84