@swmansion/argent 0.25.1-next.20 → 0.25.1-next.21

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
@@ -21857,7 +21857,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
21857
21857
  var SESSION_ID2 = randomUUID5();
21858
21858
  function readCliVersion() {
21859
21859
  if (true) {
21860
- return "0.25.1-next.20";
21860
+ return "0.25.1-next.21";
21861
21861
  }
21862
21862
  return "0.0.0";
21863
21863
  }
@@ -16710,7 +16710,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
16710
16710
  var SESSION_ID = randomUUID4();
16711
16711
  function readCliVersion() {
16712
16712
  if (true) {
16713
- return "0.25.1-next.20";
16713
+ return "0.25.1-next.21";
16714
16714
  }
16715
16715
  return "0.0.0";
16716
16716
  }
@@ -94704,7 +94704,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
94704
94704
  var SESSION_ID = (0, import_node_crypto3.randomUUID)();
94705
94705
  function readCliVersion() {
94706
94706
  if (true) {
94707
- return "0.25.1-next.20";
94707
+ return "0.25.1-next.21";
94708
94708
  }
94709
94709
  return "0.0.0";
94710
94710
  }
@@ -147677,6 +147677,7 @@ function adaptFullHierarchy(raw) {
147677
147677
  });
147678
147678
  return screenW > 0 && screenH > 0 ? { tree, screen: { width: screenW, height: screenH } } : { tree };
147679
147679
  }
147680
+ var FLOW_TREE_MAX_DEPTH = 100;
147680
147681
  var FULL_HIERARCHY_FIELDS = [
147681
147682
  "className",
147682
147683
  "identifier",
@@ -147708,9 +147709,9 @@ async function queryFullHierarchyTree(registry2, device, target) {
147708
147709
  const ndRef = nativeDevtoolsRef(device);
147709
147710
  nativeApi = await registry2.resolveService(ndRef.urn, ndRef.options);
147710
147711
  } catch (err) {
147711
- throw new Error(
147712
+ throw wrapPreservingFailure(
147712
147713
  `native devtools is unavailable (${errMsg2(err)}) \u2014 flows resolve selectors against the full view hierarchy it serves`,
147713
- { cause: err }
147714
+ err
147714
147715
  );
147715
147716
  }
147716
147717
  let bundleId;
@@ -147779,7 +147780,7 @@ async function queryFullHierarchyTree(registry2, device, target) {
147779
147780
  resolved = await resolveNativeTargetApp(nativeApi, void 0);
147780
147781
  } catch (err) {
147781
147782
  const timedOut2 = getFailureSignal(err)?.error_code === FAILURE_CODES.NATIVE_DEVTOOLS_RPC_TIMEOUT;
147782
- if (!timedOut2 || !target) throw err;
147783
+ if (!timedOut2 || !target) throw await explainTargetingFailure(err, nativeApi, device, target);
147783
147784
  if (!isInjectableBundleId(target.bundleId)) {
147784
147785
  throw new FailureError(
147785
147786
  systemAppFlowTargetRefusal(target.bundleId),
@@ -147792,13 +147793,15 @@ async function queryFullHierarchyTree(registry2, device, target) {
147792
147793
  err instanceof Error ? { cause: err } : void 0
147793
147794
  );
147794
147795
  }
147795
- if (!nativeApi.listConnectedBundleIds().includes(target.bundleId)) throw err;
147796
+ if (!nativeApi.listConnectedBundleIds().includes(target.bundleId)) {
147797
+ throw await explainTargetingFailure(err, nativeApi, device, target);
147798
+ }
147796
147799
  let hintState;
147797
147800
  try {
147798
147801
  hintState = await nativeApi.getAppState(target.bundleId);
147799
147802
  } catch (probeErr) {
147800
147803
  if (getFailureSignal(probeErr)?.error_code === FAILURE_CODES.NATIVE_DEVTOOLS_RPC_TIMEOUT) {
147801
- throw err;
147804
+ throw await explainTargetingFailure(err, nativeApi, device, target);
147802
147805
  }
147803
147806
  throw probeErr;
147804
147807
  }
@@ -147817,13 +147820,18 @@ async function queryFullHierarchyTree(registry2, device, target) {
147817
147820
  resolved = { bundleId: target.bundleId };
147818
147821
  }
147819
147822
  bundleId = resolved.bundleId;
147823
+ if (!nativeApi.listConnectedBundleIds().includes(bundleId)) {
147824
+ throw new Error(
147825
+ `${bundleId} answered the target probe and then dropped its native-devtools connection before the view hierarchy could be read. It was instrumented, so a retry may ride this out; if the connection does not come back, relaunch with restart-app (or a flow \`launch\` step) \u2014 launch-app would only foreground the process that just lost it.`
147826
+ );
147827
+ }
147820
147828
  }
147821
147829
  const rawResult = await nativeApi.queryViewHierarchy(
147822
147830
  bundleId,
147823
147831
  "ViewHierarchy.getFullHierarchy",
147824
147832
  {
147825
147833
  fields: FULL_HIERARCHY_FIELDS,
147826
- maxDepth: 40
147834
+ maxDepth: FLOW_TREE_MAX_DEPTH
147827
147835
  }
147828
147836
  );
147829
147837
  if (rawResult.error) {
@@ -147840,6 +147848,86 @@ async function queryFullHierarchyTree(registry2, device, target) {
147840
147848
  function errMsg2(err) {
147841
147849
  return err instanceof Error ? err.message : String(err);
147842
147850
  }
147851
+ async function explainTargetingFailure(err, nativeApi, device, launched) {
147852
+ const failureCode = getFailureSignal(err)?.error_code;
147853
+ if (failureCode === FAILURE_CODES.NATIVE_TARGET_MULTIPLE_APPS_AMBIGUOUS) {
147854
+ const terminate = await terminateCommand(device);
147855
+ const clearOthers = terminate ? `; clear the others with \`${terminate}\` (argent has no terminate tool, and restart-app would just bring that app back to the front).` : `.`;
147856
+ return wrapPreservingFailure(
147857
+ // Short header: the embedded diagnostic already says the set is
147858
+ // ambiguous, and the per-app entries need those 90 characters.
147859
+ `could not target an app to read the view hierarchy from:
147860
+ ${cappedAppDiagnostic(withoutExplicitBundleIdAdvice(errMsg2(err)))}
147861
+ Flow selectors auto-target and cannot name a bundleId. Foreground the intended app with launch-app (it does not terminate), then retry${clearOthers}`,
147862
+ err
147863
+ );
147864
+ }
147865
+ if (failureCode === FAILURE_CODES.NATIVE_TARGET_SINGLE_APP_NOT_FOREGROUND) {
147866
+ return wrapPreservingFailure(
147867
+ `the only native-devtools-connected app is not foreground, so it cannot be auto-targeted:
147868
+ ${withoutExplicitBundleIdAdvice(errMsg2(err))}
147869
+ Flow selector steps auto-target and cannot provide a bundleId. Bring that app to the foreground with launch-app (it does not terminate \u2014 the app is already instrumented, just not frontmost), then retry.`,
147870
+ err
147871
+ );
147872
+ }
147873
+ const stillConnected = nativeApi.listConnectedBundleIds();
147874
+ if (stillConnected.length > 0) {
147875
+ const launchedGone = launched !== void 0 && !stillConnected.includes(launched.bundleId);
147876
+ const terminate = stillConnected.length > (launchedGone ? 0 : 1) ? await terminateCommand(device) : void 0;
147877
+ const clearOthers = terminate ? ` To clear the others use \`${terminate}\` \u2014 argent exposes no terminate tool, and restart-app would bring the app you cleared back to the front instead.` : ``;
147878
+ return wrapPreservingFailure(
147879
+ `could not read the state of the native-devtools-connected apps, so none could be auto-targeted (${firstClause(err)}). Connected: ${cappedList(stillConnected)}. ` + (launchedGone ? `${launched.bundleId} \u2014 the app this flow launched \u2014 is NOT among them, so relaunch it with restart-app (or a flow \`launch\` step); launch-app does not terminate, so it would only foreground the same uninstrumented process.` : `They are instrumented \u2014 do not relaunch. A suspended app stops answering: foreground the app the flow drives with launch-app (it does not terminate), then retry.`) + clearOthers,
147880
+ err
147881
+ );
147882
+ }
147883
+ return wrapPreservingFailure(
147884
+ `no app is connected to native devtools, so flow selectors have no instrumented process to read the view hierarchy from (${firstClause(err)}). Relaunch with restart-app (or a flow \`launch\` step): launch-app does not terminate, so on an app already running from Metro/Expo, Xcode, or its icon it only foregrounds that uninstrumented process. Argent treats an Apple system app (com.apple.*) as non-injectable \u2014 the native-devtools feature tools refuse it too \u2014 so if one never connects, drive it with raw point taps and tool: await-ui-element steps.`,
147885
+ err
147886
+ );
147887
+ }
147888
+ async function terminateCommand(device) {
147889
+ try {
147890
+ const { prefix } = await simctlTargetForUdid(device.id);
147891
+ return `xcrun ${prefix.join(" ")} terminate <udid> <bundleId>`;
147892
+ } catch {
147893
+ return void 0;
147894
+ }
147895
+ }
147896
+ function firstClause(err) {
147897
+ const firstLine2 = errMsg2(err).split("\n", 1)[0];
147898
+ const sentenceEnd = /\.(?=\s|$)/.exec(firstLine2);
147899
+ return sentenceEnd === null ? firstLine2 : firstLine2.slice(0, sentenceEnd.index + 1);
147900
+ }
147901
+ var MAX_LISTED_APPS = 2;
147902
+ function cappedList(bundleIds) {
147903
+ if (bundleIds.length <= MAX_LISTED_APPS) return bundleIds.join(", ");
147904
+ const dropped = bundleIds.length - MAX_LISTED_APPS;
147905
+ return `${bundleIds.slice(0, MAX_LISTED_APPS).join(", ")} (+${dropped} more)`;
147906
+ }
147907
+ function cappedAppDiagnostic(message) {
147908
+ const lines = message.split("\n");
147909
+ const isEntry = (line) => line.startsWith("- ");
147910
+ const firstEntry = lines.findIndex(isEntry);
147911
+ if (firstEntry === -1) return message;
147912
+ const entries = lines.filter(isEntry);
147913
+ if (entries.length <= MAX_LISTED_APPS) return message;
147914
+ const kept = entries.slice(0, MAX_LISTED_APPS);
147915
+ const dropped = entries.length - MAX_LISTED_APPS;
147916
+ return [
147917
+ ...lines.slice(0, firstEntry),
147918
+ ...kept,
147919
+ `- (+${dropped} more connected app${dropped === 1 ? "" : "s"})`,
147920
+ ...lines.slice(firstEntry + entries.length).filter((line) => !isEntry(line))
147921
+ ].join("\n");
147922
+ }
147923
+ function withoutExplicitBundleIdAdvice(message) {
147924
+ return message.replace(/\nProvide bundleId explicitly[^\n]*$/, "");
147925
+ }
147926
+ function wrapPreservingFailure(message, err) {
147927
+ const cause = err instanceof Error ? err : new Error(String(err));
147928
+ const signal = getFailureSignal(err);
147929
+ return signal ? new FailureError(message, signal, { cause }) : new Error(message, { cause });
147930
+ }
147843
147931
  function projectIosDeviceNode(node) {
147844
147932
  const onScreen = node.frame.width > 0 && node.frame.height > 0;
147845
147933
  return {
@@ -149496,7 +149584,7 @@ function unmetWaitWarningFor(cause) {
149496
149584
  }
149497
149585
  function indeterminateReasonCaveat(udid) {
149498
149586
  if (platformOf(udid) !== "ios") return "";
149499
- return ". That reason may tell you to pass `bundleId` \u2014 it is quoted from the shared native-target error, and it does not apply here: the probe predicts an `await:`/`assert:` directive, and no directive takes a bundleId, so neither this probe nor the runner accepts one (the `bundleId` on this step reached the live wait only). What the runner's iOS tree needs is an app with argent's instrumentation loaded \u2014 relaunch it with `launch-app` or a flow `launch:` step. An app that cannot load it at all, such as a `com.apple.*` system app, can never be probed or converted: keep the check as a raw `tool:` step";
149587
+ return ". One thing that reason cannot see is this step: the probe predicts an `await:`/`assert:` directive, and no directive takes a bundleId, so neither this probe nor the runner accepts one (the `bundleId` on this step reached the live wait only)";
149500
149588
  }
149501
149589
  var CANCELLED_PROBE_WARNING = "recorded, but the re-probe against the tree the RUNNER reads was cancelled before it answered. The step itself ran and is written to the flow; only the verdict is missing, so whether it would convert to `await:`/`assert:` is UNKNOWN, not known-bad \u2014 record the wait again, uncancelled, before trusting the conversion";
149502
149590
  var PROBE_MAX_TREE_READ_MS = 2500;
@@ -149570,6 +149658,11 @@ async function probeAgainstRunnerTree(registry2, ctx, args) {
149570
149658
  (condition === "text" ? textTieClause(args.udid) : "") + " " + SPELLING_CLAUSE + ` ${treeDivergenceFor(args.udid, condition)} ${runnerSideReadClause(args.udid, condition)}`
149571
149659
  };
149572
149660
  }
149661
+ function roleOnlySelectorWarning(selector) {
149662
+ if (selector.role === void 0 || selector.identifier !== void 0) return void 0;
149663
+ if (selector.text !== void 0 || selector.textMatches !== void 0) return void 0;
149664
+ 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`;
149665
+ }
149573
149666
  async function captureTapSelector(registry2, session, udid, point) {
149574
149667
  try {
149575
149668
  const device = resolveDevice(udid);
@@ -149595,7 +149688,11 @@ async function captureTapSelector(registry2, session, udid, point) {
149595
149688
  warning: `selector ${describeSelector(selector)} resolves to a different element on this screen; kept coordinates (brittle)`
149596
149689
  };
149597
149690
  }
149598
- return { selector, warning: fallbackSourceWarning(source, device.platform) };
149691
+ const warnings = [
149692
+ roleOnlySelectorWarning(selector),
149693
+ fallbackSourceWarning(source, device.platform)
149694
+ ].filter((w) => w !== void 0);
149695
+ return { selector, ...warnings.length > 0 ? { warning: warnings.join("; ") } : {} };
149599
149696
  } catch (err) {
149600
149697
  return {
149601
149698
  warning: `selector capture failed (${err instanceof Error ? err.message : String(err)}); kept coordinates`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swmansion/argent",
3
- "version": "0.25.1-next.20",
3
+ "version": "0.25.1-next.21",
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",