@swmansion/argent 0.23.1-next.1 → 0.24.0
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/README.md +1 -1
- package/bin/argent-android-devtools-0.1.0.apk +0 -0
- package/bin/darwin/ax-service +0 -0
- package/bin/darwin/tvos-ax-service +0 -0
- package/bin/darwin/tvos-hid-daemon +0 -0
- package/bin/tcp/ax-service +0 -0
- package/dist/cli-cmds.mjs +1 -1
- package/dist/installer.mjs +1 -1
- package/dist/tool-server.cjs +15 -7
- package/dylibs/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/libKeyboardPatch.dylib +0 -0
- package/dylibs/libNativeDevtoolsIos.dylib +0 -0
- package/dylibs/tcp/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/tcp/libKeyboardPatch.dylib +0 -0
- package/dylibs/tcp/libNativeDevtoolsIos.dylib +0 -0
- package/dylibs/tvos/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/tvos/libKeyboardPatch.dylib +0 -0
- package/dylibs/tvos/libNativeDevtoolsIos.dylib +0 -0
- package/package.json +1 -1
- package/rules/argent.md +2 -2
- package/skills/argent-create-flow/SKILL.md +1 -1
- package/skills/argent-ios-device-interact/SKILL.md +6 -4
- package/skills/argent-ios-device-setup/SKILL.md +2 -2
- package/skills/argent-ios-simulator-setup/SKILL.md +1 -1
- package/skills/argent-metro-debugger/SKILL.md +2 -0
- package/skills/argent-native-profiler/SKILL.md +1 -0
- package/skills/argent-qa-flows/SKILL.md +1 -1
- package/skills/argent-react-native-app-workflow/SKILL.md +2 -0
- package/skills/argent-react-native-profiler/SKILL.md +2 -0
- package/skills/argent-test-ui-flow/SKILL.md +2 -0
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Argent drives a growing set of targets through a single toolkit, each with the r
|
|
|
23
23
|
|
|
24
24
|
| Platform | Targets | Interaction |
|
|
25
25
|
| ----------------- | ----------------------------------------------------------------------- | ---------------- |
|
|
26
|
-
| **iOS** | Simulators and physical iPhones
|
|
26
|
+
| **iOS** | Simulators and physical iPhones | Touch / gesture |
|
|
27
27
|
| **Android** | Emulators (AVDs) and physical devices over adb | Touch / gesture |
|
|
28
28
|
| **TV** | Apple TV (tvOS), Android TV / Google TV, Amazon Fire TV (Vega) | D-pad / remote |
|
|
29
29
|
| **Desktop & web** | Electron and Chromium apps (incl. React Native Web / Expo web) over CDP | Mouse / keyboard |
|
|
Binary file
|
package/bin/darwin/ax-service
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/tcp/ax-service
CHANGED
|
Binary file
|
package/dist/cli-cmds.mjs
CHANGED
package/dist/installer.mjs
CHANGED
|
@@ -16625,7 +16625,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
16625
16625
|
var SESSION_ID = randomUUID4();
|
|
16626
16626
|
function readCliVersion() {
|
|
16627
16627
|
if (true) {
|
|
16628
|
-
return "0.
|
|
16628
|
+
return "0.24.0";
|
|
16629
16629
|
}
|
|
16630
16630
|
return "0.0.0";
|
|
16631
16631
|
}
|
package/dist/tool-server.cjs
CHANGED
|
@@ -93916,7 +93916,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
93916
93916
|
var SESSION_ID = (0, import_node_crypto3.randomUUID)();
|
|
93917
93917
|
function readCliVersion() {
|
|
93918
93918
|
if (true) {
|
|
93919
|
-
return "0.
|
|
93919
|
+
return "0.24.0";
|
|
93920
93920
|
}
|
|
93921
93921
|
return "0.0.0";
|
|
93922
93922
|
}
|
|
@@ -95451,6 +95451,11 @@ function subjectValue(subject, key2) {
|
|
|
95451
95451
|
}
|
|
95452
95452
|
return null;
|
|
95453
95453
|
}
|
|
95454
|
+
function signingLabel(commonName) {
|
|
95455
|
+
const match = /^([^:]+):.*\(([A-Za-z0-9]+)\)\s*$/.exec(commonName);
|
|
95456
|
+
if (match) return `${match[1].trim()} (${match[2]})`;
|
|
95457
|
+
return commonName.split(":")[0].trim();
|
|
95458
|
+
}
|
|
95454
95459
|
function parseSigningTeams(pem) {
|
|
95455
95460
|
const newestByTeam = /* @__PURE__ */ new Map();
|
|
95456
95461
|
for (const block of pem.match(PEM_BLOCK_RE) ?? []) {
|
|
@@ -95461,11 +95466,12 @@ function parseSigningTeams(pem) {
|
|
|
95461
95466
|
continue;
|
|
95462
95467
|
}
|
|
95463
95468
|
const teamId = subjectValue(cert.subject, "OU");
|
|
95464
|
-
const
|
|
95469
|
+
const commonName = subjectValue(cert.subject, "CN");
|
|
95465
95470
|
const issuedAtMs = Date.parse(cert.validFrom);
|
|
95466
|
-
if (!teamId || !
|
|
95471
|
+
if (!teamId || !commonName || Number.isNaN(issuedAtMs)) {
|
|
95467
95472
|
continue;
|
|
95468
95473
|
}
|
|
95474
|
+
const label = signingLabel(commonName);
|
|
95469
95475
|
const known = newestByTeam.get(teamId);
|
|
95470
95476
|
if (!known || issuedAtMs > known.issuedAtMs) {
|
|
95471
95477
|
newestByTeam.set(teamId, { teamId, label, issuedAtMs });
|
|
@@ -113260,7 +113266,9 @@ function extractDeviceArg(data) {
|
|
|
113260
113266
|
return null;
|
|
113261
113267
|
}
|
|
113262
113268
|
function targetsIosPhysicalDevice(data) {
|
|
113263
|
-
const
|
|
113269
|
+
const record2 = data && typeof data === "object" ? data : null;
|
|
113270
|
+
const flowDevice = typeof record2?.device === "string" ? record2.device : null;
|
|
113271
|
+
const deviceArg = extractDeviceArg(data) ?? flowDevice;
|
|
113264
113272
|
return deviceArg !== null && isIosPhysicalDevice(resolveDevice(deviceArg));
|
|
113265
113273
|
}
|
|
113266
113274
|
function inferPlatform(deviceId) {
|
|
@@ -122048,7 +122056,7 @@ var gestureSwipeTool = {
|
|
|
122048
122056
|
Generates interpolated Move events for a natural feel (~60fps).
|
|
122049
122057
|
Swipe up (fromY > toY) to scroll content down.
|
|
122050
122058
|
Use when you need to scroll a list, dismiss a modal, drag an element, or navigate between pages. Not supported on Chromium \u2014 use gesture-scroll there instead.
|
|
122051
|
-
Physical iOS: an edge gesture (back-swipe) needs fromX 0 exactly.
|
|
122059
|
+
Physical iOS: an edge gesture (back-swipe) needs fromX 0 exactly; durationMs sets drag speed, not time; momentum:false only rests 300ms at the end and does not damp.
|
|
122052
122060
|
Pass momentum:false for a momentum-free swipe that lands where the finger lifts (little to no fling at the 300 default), when you need a deterministic scroll distance; it needs durationMs >= 150 and is rejected below that, a shorter ease-out leaving the OS too little wall clock to read the deceleration as a stop. At 150 it lands short of the lift point instead, and 2 of 47 runs still flung backwards. A plain swipe takes any duration up to 10000ms and is delivered as close to the speed it was authored as a 16ms frame allows: below ~32ms the whole travel lands in one or two frames, which the OS flings as hard as it flings anything. Returns { swiped: true, timestampMs }. On physical iOS, reactivated: true = app was re-fronted; re-describe. Fails if the simulator-server / emulator backend is not reachable for the given device.`,
|
|
122053
122061
|
alwaysLoad: true,
|
|
122054
122062
|
searchHint: "swipe scroll drag pan gesture device simulator emulator touch move",
|
|
@@ -133313,7 +133321,7 @@ The tree carries no z-order or occlusion information: an element listed at a poi
|
|
|
133313
133321
|
by an overlay (e.g. a toolbar over list rows), so check a screenshot when a tap lands unexpectedly.
|
|
133314
133322
|
|
|
133315
133323
|
For app-scoped inspection with full UIKit properties (accessibilityIdentifier, viewClassName),
|
|
133316
|
-
use native-describe-screen with an explicit bundleId instead (iOS only).
|
|
133324
|
+
use native-describe-screen with an explicit bundleId instead (iOS simulator only).
|
|
133317
133325
|
For React Native apps, debugger-component-tree returns React component names with tap coordinates.
|
|
133318
133326
|
|
|
133319
133327
|
On a TV target (Apple TV / Android TV \u2014 a \`list-devices\` entry with runtimeKind 'tv') this returns
|
|
@@ -145637,7 +145645,7 @@ function connectedIosDeviceHint(devices, platform) {
|
|
|
145637
145645
|
if (platform && platform !== "ios") return "";
|
|
145638
145646
|
const ids = devices.filter((d) => d.platform === "ios" && d.kind === "device" && d.state === "connected").map((d) => d.udid).filter((id) => Boolean(id));
|
|
145639
145647
|
if (ids.length === 0) return "";
|
|
145640
|
-
return ` A physical iPhone is connected (${ids.join(", ")}); hardware is never picked automatically, pass
|
|
145648
|
+
return ` A physical iPhone is connected (${ids.join(", ")}); hardware is never picked automatically, pass device <udid> (--device on the CLI) to run on it.`;
|
|
145641
145649
|
}
|
|
145642
145650
|
function describeDevice(d) {
|
|
145643
145651
|
return `${deviceEntryId(d) ?? "?"} (${d.platform}${d.state ? `, ${d.state}` : ""})`;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/rules/argent.md
CHANGED
|
@@ -83,7 +83,7 @@ Decision order:
|
|
|
83
83
|
argent install, so an unscoped call tears down their devices too; reserve that form for a deliberate
|
|
84
84
|
machine-wide cleanup.
|
|
85
85
|
If the user started Metro separately, ask whether to call `stop-metro` (specify the port if not 8081).
|
|
86
|
-
- If tools provided by mcp-server are not sufficient and action can be done using `xcrun`, `adb`, or other commands, use the command. Examples: changing device options, performing a device action such as lock, shake, etc.
|
|
86
|
+
- If tools provided by mcp-server are not sufficient and action can be done using `xcrun`, `adb`, or other commands, use the command. Examples: changing device options, performing a device action such as lock, shake, etc. Not on a physical iPhone.
|
|
87
87
|
- When waiting for an action, do not call `screenshot` repeatedly without a proper wait mechanism. Use the `await-ui-element` tool to block until the UI settles (e.g. wait for an element to become `visible`/`hidden`, or to contain expected `text`) instead of polling.
|
|
88
88
|
</general_rules>
|
|
89
89
|
|
|
@@ -114,7 +114,7 @@ When: Beginning a task that involves the Android emulator, no emulator running y
|
|
|
114
114
|
|
|
115
115
|
PHYSICAL iPHONE (USB)
|
|
116
116
|
Skills: `argent-ios-device-setup` (cable, trust, signing), then `argent-ios-device-interact` (the app-scoped interaction contract)
|
|
117
|
-
When: The user names a physical iPhone, a real device, or hardware, or the target `list-devices` iOS entry has kind `"device"`. Never for a simulator, and never because a cabled phone is listed first. On hardware every interaction starts with `launch-app`; `paste`, `settings-permissions`, two-finger gestures, `rotate`
|
|
117
|
+
When: The user names a physical iPhone, a real device, or hardware, or the target `list-devices` iOS entry has kind `"device"`. Never for a simulator, and never because a cabled phone is listed first. On hardware every interaction starts with `launch-app`; `paste`, `settings-permissions`, two-finger gestures, `rotate`, `shake`, screen recording, `debugger-*`, `react-profiler-*`, `native-profiler-*` and `native-*` do not exist there.
|
|
118
118
|
Prompt keywords: physical iPhone, real device, on my phone, USB, hardware
|
|
119
119
|
|
|
120
120
|
TAPPING, SWIPING, TYPING, GESTURES, SCREENSHOTS, SCROLLING
|
|
@@ -13,7 +13,7 @@ For a saved QA test case, ticket, or acceptance criterion, load `argent-qa-flows
|
|
|
13
13
|
|
|
14
14
|
- Before creating or changing a flow, read [Live authoring](references/live-authoring.md) completely.
|
|
15
15
|
- When polishing, composing, or manually reviewing YAML, read [Flow YAML](references/flow-yaml.md). For Vega, read its platform limits before recording remote or keyboard tools.
|
|
16
|
-
- Flows run on physical iPhones (an iOS `list-devices` entry with kind `"device"`), but replay never auto-binds one, even when no simulator is booted: pass the phone's udid as `device` (CLI `--device`), and only a `connected` phone can run. `pinch`/`rotate` steps fail there like the live tools. See `argent-ios-device-interact` for the hardware contract.
|
|
16
|
+
- Flows run on physical iPhones (an iOS `list-devices` entry with kind `"device"`), but replay never auto-binds one, even when no simulator is booted: pass the phone's udid as `device` (CLI `--device`), and only a `connected` phone can run. `pinch`/`rotate` steps fail there like the live tools. On hardware the flow tree is the `describe` tree: same ids and roles, no UIView hierarchy. See `argent-ios-device-interact` for the hardware contract.
|
|
17
17
|
- On capture warnings, raw coordinates, unavailable trees, mistimed transitions, overlays, or replay failures, read [Reliability and recovery](references/reliability-and-recovery.md).
|
|
18
18
|
|
|
19
19
|
## Non-negotiable rules
|
|
@@ -9,10 +9,12 @@ Read this only for a physical iPhone. `argent-device-interact` still applies for
|
|
|
9
9
|
|
|
10
10
|
## Contract
|
|
11
11
|
|
|
12
|
-
- Observation never changes the screen; mutation may. `describe` and `await-ui-element` fail on a backgrounded target instead of re-fronting it. Gestures and `keyboard` re-front it and return `reactivated: true`; re-describe before the next step.
|
|
13
|
-
-
|
|
12
|
+
- Observation never changes the screen; mutation may. `describe` and `await-ui-element` fail on a backgrounded target instead of re-fronting it; `await-screen-idle` returns `settled: false` with no reason. The first `describe` within a second of `button home` can still return the old tree; describe again. Gestures and `keyboard` re-front it and return `reactivated: true`; re-describe before the next step.
|
|
13
|
+
- `launch-app`, `open-url` and `restart-app` set the app under automation; `reinstall-app` clears it; a tool-server restart forgets it: launch again.
|
|
14
|
+
- Each tool's description states its own hardware limits (named keys, `gesture-custom` shapes, buttons, edge swipes, tap counts). A gated tool fails with a bare `not supported on ios device`: the fix is in the list below, not in the error.
|
|
14
15
|
|
|
15
16
|
## Tools on hardware
|
|
16
17
|
|
|
17
|
-
- Only these exist: `list-devices`, `launch-app`, `restart-app`, `reinstall-app`, `open-url`, `describe`, `screenshot`, `screenshot-diff`, `gesture-tap`, `gesture-swipe`, `gesture-custom`, `button`, `keyboard`, `await-ui-element`, `await-screen-idle`, `run-sequence`, the flow tools, and `stop-simulator-
|
|
18
|
-
- No two-finger gestures, `rotate`, `shake`, `paste`,
|
|
18
|
+
- Only these exist: `list-devices`, `launch-app`, `restart-app`, `reinstall-app`, `open-url`, `describe`, `screenshot`, `screenshot-diff`, `gesture-tap`, `gesture-swipe`, `gesture-custom`, `button` (`home`, `volumeUp`, `volumeDown`, `actionButton` on models that have one), `keyboard`, `await-ui-element`, `await-screen-idle`, `run-sequence`, the flow tools, `stop-simulator-server` and `stop-all-simulator-servers` (session end). Every other device tool fails with `not supported on ios device`.
|
|
19
|
+
- No two-finger gestures, `rotate`, `shake`, `paste`, `settings-permissions`, screen recording, `debugger-*`, `react-profiler-*`, `native-profiler-*`, `native-*`, `boot-device`: drive the app's own zoom and rotate UI with taps and drags, move the phone by hand, type with `keyboard`, change permissions in the phone's Settings, and debug, profile or record on a simulator.
|
|
20
|
+
- `gesture-swipe`: `durationMs` sets drag speed, not time; `momentum:false` only rests 300 ms at the end, no damping. `open-url` https lands in Safari, never in the app that owns the link: pass `bundleId`.
|
|
@@ -10,11 +10,11 @@ Read this only for a physical iPhone. Simulators use `argent-ios-simulator-setup
|
|
|
10
10
|
## First run
|
|
11
11
|
|
|
12
12
|
1. Cable the phone, unlock it, keep the screen awake, and turn on Developer Mode (Settings > Privacy & Security > Developer Mode). `list-devices` must show it `connected`.
|
|
13
|
-
2.
|
|
13
|
+
2. `launch-app` only registers the app. The first `describe`, gesture or `screenshot` builds, signs and starts the on-device runner: minutes cold, tens of seconds from cache or after a tool-server restart. Build cap 15 min, runner ready 120 s.
|
|
14
14
|
3. On the first install the phone asks to trust the developer (Settings > General > VPN & Device Management), and an **ArgentRunner** app appears on the home screen. Tell the user it is argent's automation runner and must stay installed.
|
|
15
15
|
|
|
16
16
|
## Signing and failures
|
|
17
17
|
|
|
18
|
-
Signing needs no configuration. The
|
|
18
|
+
Signing needs no configuration. The first phone call of a tool-server process carries a note naming the team picked and the `ARGENT_IOS_TEAM_ID` override (tool-server environment; a change forces a cold rebuild). Cable, lock, trust, expired-profile and missing-certificate errors name their fix: apply it, retry the same call. Also: `errSecInternalComponent`: run `security set-key-partition-list -S apple-tool:,apple:,codesign: -s ~/Library/Keychains/login.keychain-db` (asks the user's login password), retry. `team has no devices`: keep the phone cabled, retry. Bundle id registration failed: free-team app-id cap, wait days or sign under a paid team. Any other xcodebuild failure prints raw `error:` lines: read `~/.argent/ios-device-runner/logs/runner-<udid8>.log`. `RUNNER_WEDGED`: `stop-simulator-server` for the udid, retry.
|
|
19
19
|
|
|
20
20
|
Then read `argent-ios-device-interact`.
|
|
@@ -8,7 +8,7 @@ description: Set up and connect to an iOS simulator using argent MCP tools. Use
|
|
|
8
8
|
If you delegate simulator tasks to sub-agents, make sure they have MCP permissions.
|
|
9
9
|
|
|
10
10
|
1. **Find a booted simulator**
|
|
11
|
-
Use `list-devices`. Filter for entries with `platform: "ios"`
|
|
11
|
+
Use `list-devices`. Filter for entries with `platform: "ios"` and skip any with `kind: "device"` (a physical iPhone, never a simulator target): booted simulators are listed first.
|
|
12
12
|
If none are booted, call `boot-device` with `udid: <chosen UDID>`.
|
|
13
13
|
|
|
14
14
|
2. **Verify connection**
|
|
@@ -5,6 +5,8 @@ description: Debug a JS runtime via CDP using argent debugger tools. Primary pat
|
|
|
5
5
|
|
|
6
6
|
## 1. Prerequisites
|
|
7
7
|
|
|
8
|
+
Physical iPhone: not supported; every `debugger-*` tool rejects `kind: "device"`. Use a simulator.
|
|
9
|
+
|
|
8
10
|
For **React Native (iOS / Android)**: requires **Metro dev server running** (default `localhost:8081`) and **a React Native app connected to Metro** (at least one CDP target). Verify via `debugger-status` — it returns `status: "connected"` or `status: "not_connected"` with a `reason` and `guidance` (it does not fail when the debugger is unreachable).
|
|
9
11
|
|
|
10
12
|
For **Vega (Fire TV)**: requires a **Debug `.vpkg`** (a Release build never attaches) and **Metro reachable from the device** (`vega device start-port-forwarding --port 8081 --forward false`). Verify via `debugger-status`. `debugger-component-tree`, `debugger-inspect-element`, `debugger-reload-metro` and the `react-profiler-*` / `profiler-*` tools are unavailable there — see the `argent-tv-interact` skill.
|
|
@@ -10,6 +10,7 @@ description: Native profiling for CPU hotspots, UI hangs, memory issues. iOS via
|
|
|
10
10
|
- `native-profiler-analyze` — parse exported trace data and return a structured bottleneck payload.
|
|
11
11
|
- `profiler-stack-query` — drill into parsed data: hang stacks, function callers, thread breakdown, leak details.
|
|
12
12
|
- `profiler-load` — list and reload previous trace sessions from disk for re-investigation.
|
|
13
|
+
- Physical iPhone: not supported; use a simulator.
|
|
13
14
|
|
|
14
15
|
---
|
|
15
16
|
|
|
@@ -11,7 +11,7 @@ Load `argent-create-flow` as the authoring engine. Follow its required reference
|
|
|
11
11
|
|
|
12
12
|
**Apple TV and Android TV are out of scope.** The runner does not reject touch directives there, so they fail at the gesture layer instead of with authoring guidance. Use `argent-tv-interact` and report the limitation.
|
|
13
13
|
|
|
14
|
-
**Physical iPhones** run QA flows, with
|
|
14
|
+
**Physical iPhones** run QA flows, with three hardware limits: replay never auto-binds a phone, so pass its udid as `device` (CLI `--device`) and keep it `connected`; `pinch`/`rotate` steps fail there like the live tools, so drive the app's own zoom UI instead; the flow tree is the `describe` tree (same ids and roles), so a selector authored on a simulator can miss there. Read `argent-ios-device-interact` for the app-scoped contract before recording.
|
|
15
15
|
|
|
16
16
|
## Definition of done
|
|
17
17
|
|
|
@@ -3,6 +3,8 @@ name: argent-react-native-app-workflow
|
|
|
3
3
|
description: Step-by-step workflows for developing or debugging React Native apps on iOS simulator or Android emulator. Use when starting the app, debugging Metro, fixing builds, diagnosing runtime errors, or running tests.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
Physical iPhone (`kind: "device"`): Metro debugging and profiling tools reject it. Use a simulator.
|
|
7
|
+
|
|
6
8
|
## 1. Starting the React Native App
|
|
7
9
|
|
|
8
10
|
### 1.1 Explore Configuration (MANDATORY — Do This First)
|
|
@@ -5,6 +5,8 @@ description: Profile a React Native Hermes app to measure re-render and CPU perf
|
|
|
5
5
|
|
|
6
6
|
This skill is complementary to `argent-react-native-optimization`, not a replacement for it.
|
|
7
7
|
|
|
8
|
+
Physical iPhone: not supported; `react-profiler-*` reject `kind: "device"`. Profile on a simulator.
|
|
9
|
+
|
|
8
10
|
## 2. Tool Overview
|
|
9
11
|
|
|
10
12
|
### React Profiler (Hermes / React commits)
|
|
@@ -5,6 +5,8 @@ description: Autonomously test an app UI (iOS or Android) by running interact-sc
|
|
|
5
5
|
|
|
6
6
|
## Platform-agnostic
|
|
7
7
|
|
|
8
|
+
Physical iPhone (`kind: "device"`): read `argent-ios-device-interact` first. `launch-app` before anything; `describe` fails while the app is backgrounded.
|
|
9
|
+
|
|
8
10
|
The interaction tool names are identical on iOS and Android — `gesture-tap`, `gesture-swipe`, `describe`, `screenshot`, `launch-app`, etc. — and the tool-server auto-dispatches based on the `udid` you pass (UUID-shape → iOS, adb serial → Android).
|
|
9
11
|
|
|
10
12
|
**Before testing, resolve which device to test on.** Call `list-devices` and follow `<device_selection_rule>`: prefer a running device on any platform;
|