@swmansion/argent 0.15.1-next.2 → 0.15.1-next.4

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.
@@ -126784,10 +126784,11 @@ async function typeSimulatorServer(registry2, device, params) {
126784
126784
  }
126785
126785
  keysPressed++;
126786
126786
  };
126787
+ let namedKeyCode;
126787
126788
  if (params.key) {
126788
126789
  const lower = params.key.toLowerCase();
126789
- const code = Object.hasOwn(NAMED_KEYS, lower) ? NAMED_KEYS[lower] : void 0;
126790
- if (code == null) {
126790
+ namedKeyCode = Object.hasOwn(NAMED_KEYS, lower) ? NAMED_KEYS[lower] : void 0;
126791
+ if (namedKeyCode == null) {
126791
126792
  throw new InvalidToolInputError(
126792
126793
  `Unknown key "${params.key}". Supported: ${Object.keys(NAMED_KEYS).join(", ")}`,
126793
126794
  {
@@ -126797,7 +126798,6 @@ async function typeSimulatorServer(registry2, device, params) {
126797
126798
  }
126798
126799
  );
126799
126800
  }
126800
- await pressKeyCode(code);
126801
126801
  }
126802
126802
  if (params.text) {
126803
126803
  for (const char of params.text) {
@@ -126812,6 +126812,9 @@ async function typeSimulatorServer(registry2, device, params) {
126812
126812
  await sleep8(delay2);
126813
126813
  }
126814
126814
  }
126815
+ if (namedKeyCode != null) {
126816
+ await pressKeyCode(namedKeyCode);
126817
+ }
126815
126818
  return { typed: params.text ?? params.key ?? "", keys: keysPressed };
126816
126819
  }
126817
126820
 
@@ -126955,9 +126958,10 @@ var sleep9 = (ms) => new Promise((r) => setTimeout(r, ms));
126955
126958
  async function runChromium(api, params) {
126956
126959
  const delay2 = params.delayMs ?? 50;
126957
126960
  let keysPressed = 0;
126961
+ let named;
126958
126962
  if (params.key) {
126959
126963
  const lower = params.key.toLowerCase();
126960
- const named = Object.hasOwn(CHROMIUM_NAMED_KEYS, lower) ? CHROMIUM_NAMED_KEYS[lower] : void 0;
126964
+ named = Object.hasOwn(CHROMIUM_NAMED_KEYS, lower) ? CHROMIUM_NAMED_KEYS[lower] : void 0;
126961
126965
  if (!named) {
126962
126966
  throw new InvalidToolInputError(
126963
126967
  `Unknown key "${params.key}". Supported: ${Object.keys(CHROMIUM_NAMED_KEYS).join(", ")}`,
@@ -126968,20 +126972,6 @@ async function runChromium(api, params) {
126968
126972
  }
126969
126973
  );
126970
126974
  }
126971
- await api.dispatchKeyEvent({
126972
- type: "keyDown",
126973
- key: named.key,
126974
- code: named.code,
126975
- windowsVirtualKeyCode: named.windowsVirtualKeyCode
126976
- });
126977
- await sleep9(delay2);
126978
- await api.dispatchKeyEvent({
126979
- type: "keyUp",
126980
- key: named.key,
126981
- code: named.code,
126982
- windowsVirtualKeyCode: named.windowsVirtualKeyCode
126983
- });
126984
- keysPressed++;
126985
126975
  }
126986
126976
  if (params.text) {
126987
126977
  for (const char of params.text) {
@@ -127010,6 +127000,22 @@ async function runChromium(api, params) {
127010
127000
  await sleep9(delay2);
127011
127001
  }
127012
127002
  }
127003
+ if (named) {
127004
+ await api.dispatchKeyEvent({
127005
+ type: "keyDown",
127006
+ key: named.key,
127007
+ code: named.code,
127008
+ windowsVirtualKeyCode: named.windowsVirtualKeyCode
127009
+ });
127010
+ await sleep9(delay2);
127011
+ await api.dispatchKeyEvent({
127012
+ type: "keyUp",
127013
+ key: named.key,
127014
+ code: named.code,
127015
+ windowsVirtualKeyCode: named.windowsVirtualKeyCode
127016
+ });
127017
+ keysPressed++;
127018
+ }
127013
127019
  return { typed: params.text ?? params.key ?? "", keys: keysPressed };
127014
127020
  }
127015
127021
  function makeChromiumImpl(registry2) {
@@ -127093,7 +127099,7 @@ async function injectViaInputd(subcommands) {
127093
127099
  async function injectVegaButtons(buttons) {
127094
127100
  await injectViaInputd(remoteButtonsToKeycodes(buttons).map((code) => `button_press ${code}`));
127095
127101
  }
127096
- async function injectVegaNamedKey(name) {
127102
+ function resolveVegaNamedKeycode(name) {
127097
127103
  const lower = name.toLowerCase();
127098
127104
  const code = Object.hasOwn(NAMED_KEYCODES, lower) ? NAMED_KEYCODES[lower] : void 0;
127099
127105
  if (!code) {
@@ -127106,7 +127112,10 @@ async function injectVegaNamedKey(name) {
127106
127112
  }
127107
127113
  );
127108
127114
  }
127109
- await injectViaInputd([`button_press ${code}`]);
127115
+ return code;
127116
+ }
127117
+ async function injectVegaNamedKey(name) {
127118
+ await injectViaInputd([`button_press ${resolveVegaNamedKeycode(name)}`]);
127110
127119
  }
127111
127120
  async function injectVegaText(text) {
127112
127121
  if (/[\n\r]/.test(text)) {
@@ -127122,14 +127131,15 @@ async function injectVegaText(text) {
127122
127131
  // ../tool-server/src/tools/keyboard/platforms/vega.ts
127123
127132
  async function runVega2(params) {
127124
127133
  let keysPressed = 0;
127125
- if (params.key) {
127126
- await injectVegaNamedKey(params.key);
127127
- keysPressed++;
127128
- }
127134
+ if (params.key) resolveVegaNamedKeycode(params.key);
127129
127135
  if (params.text) {
127130
127136
  await injectVegaText(params.text);
127131
127137
  keysPressed += [...params.text].length;
127132
127138
  }
127139
+ if (params.key) {
127140
+ await injectVegaNamedKey(params.key);
127141
+ keysPressed++;
127142
+ }
127133
127143
  return { typed: params.text ?? params.key ?? "", keys: keysPressed };
127134
127144
  }
127135
127145
  var vegaImpl4 = {
@@ -127144,7 +127154,7 @@ var zodSchema24 = external_exports.object({
127144
127154
  ),
127145
127155
  text: external_exports.string().optional().describe("Text to type character by character. Handles uppercase and common punctuation."),
127146
127156
  key: external_exports.string().optional().describe(
127147
- "Named key to press: enter, escape, backspace, tab, space, arrow-up, arrow-down, arrow-left, arrow-right, f1\u2013f12. Not supported on TV targets \u2014 move focus with `tv-remote` (up/down/left/right) instead."
127157
+ "Named key to press: enter, escape, backspace, tab, space, arrow-up, arrow-down, arrow-left, arrow-right, f1\u2013f12. When combined with `text`, the key is pressed AFTER the text is typed (so text + enter types and submits). Not supported on TV targets \u2014 move focus with `tv-remote` (up/down/left/right) instead."
127148
127158
  ),
127149
127159
  delayMs: external_exports.number().optional().describe(
127150
127160
  "Delay in ms between key presses (default 50). Ignored on Android phones/tablets (typed via `adb input text`, which has no per-key cadence), on Vega (text/keys injected in a single shot), and on TV targets (Apple TV / Android TV type the whole string at the daemon's own cadence)."
@@ -127166,7 +127176,7 @@ Returns { typed: string, keys: number }. Fails if an unsupported key name is pro
127166
127176
  - text: types a string (supports uppercase, digits, common punctuation)
127167
127177
  - key: presses a single named key (enter, escape, backspace, tab, arrow-up/down/left/right, f1\u2013f12) \u2014 NOT supported on TV targets; move focus with \`tv-remote\` instead.
127168
127178
  On a TV target (runtimeKind 'tv') only \`text\` applies \u2014 focus a text field first (with \`tv-remote\`), then type into it (injected HID keyboard on Apple TV, \`adb input text\` on Android TV).
127169
- Provide text, key, or both.`,
127179
+ Provide text, key, or both \u2014 when both are given, the text is typed first and the key is pressed after it (text + key:"enter" types and submits).`,
127170
127180
  zodSchema: zodSchema24,
127171
127181
  capability: capability16,
127172
127182
  searchHint: "type text keyboard input named key enter escape arrow tv vega fire tv search field hid leanback",
@@ -133565,7 +133575,7 @@ Allowed tools and their args (udid is auto-injected, do NOT include it in args):
133565
133575
  gesture-pinch: { centerX: number, centerY: number, startDistance: number, endDistance: number, angle?: number, durationMs?: number } [ios only]
133566
133576
  gesture-rotate: { centerX: number, centerY: number, radius: number, startAngle: number, endAngle: number, durationMs?: number } [ios only]
133567
133577
  button: { button: "home"|"back"|"power"|"volumeUp"|"volumeDown"|"appSwitch"|"actionButton" } [ios/android]
133568
- keyboard: { text?: string, key?: string, delayMs?: number } (TV: text only) [ios/android/chromium/vega/tv]
133578
+ keyboard: { text?: string, key?: string, delayMs?: number } (key pressed after text; TV: text only) [ios/android/chromium/vega/tv]
133569
133579
  rotate: { orientation: "Portrait"|"LandscapeLeft"|"LandscapeRight"|"PortraitUpsideDown" } [ios/android]
133570
133580
  tv-remote: { button: <remote button | array of them>, repeat?: number } [apple tv/android tv/vega]
133571
133581
  buttons: up/down/left/right/select/back/home/menu/playPause (+ rewind/fastForward/next/previous/volumeUp/volumeDown/mute \u2014 work on Android TV and Vega; rejected on the Apple TV simulator)
@@ -133578,10 +133588,9 @@ Example \u2014 scroll down three times (use gesture-scroll with positive deltaY
133578
133588
  { "tool": "gesture-swipe", "args": { "fromX": 0.5, "fromY": 0.7, "toX": 0.5, "toY": 0.3 } }
133579
133589
  ]}
133580
133590
 
133581
- Example \u2014 type text and submit:
133591
+ Example \u2014 type text and submit (one step: the key is pressed after the text is typed):
133582
133592
  { "udid": "<UDID>", "steps": [
133583
- { "tool": "keyboard", "args": { "text": "hello world" } },
133584
- { "tool": "keyboard", "args": { "key": "enter" } }
133593
+ { "tool": "keyboard", "args": { "text": "hello world", "key": "enter" } }
133585
133594
  ]}
133586
133595
 
133587
133596
  Example \u2014 TV: move focus right twice then activate (one tv-remote step with a path is cheaper):
@@ -144538,6 +144547,12 @@ function toYamlStep(step) {
144538
144547
  const body = step.selector ? selectorToYaml(step.selector) : { x: step.x, y: step.y };
144539
144548
  return { tap: body };
144540
144549
  }
144550
+ case "long-press": {
144551
+ const sel = selectorToYaml(step.selector);
144552
+ return {
144553
+ "long-press": step.duration !== void 0 ? { on: sel, duration: step.duration } : sel
144554
+ };
144555
+ }
144541
144556
  case "type": {
144542
144557
  const body = {
144543
144558
  into: selectorToYaml(step.into),
@@ -144787,6 +144802,7 @@ var STEP_DIRECTIVE_KEYS = [
144787
144802
  "run",
144788
144803
  "tool",
144789
144804
  "tap",
144805
+ "long-press",
144790
144806
  "type",
144791
144807
  "await",
144792
144808
  "assert",
@@ -144794,6 +144810,35 @@ var STEP_DIRECTIVE_KEYS = [
144794
144810
  "scroll-to",
144795
144811
  "snapshot"
144796
144812
  ];
144813
+ function parseLongPress(body, entry) {
144814
+ const obj = body !== null && typeof body === "object" ? body : {};
144815
+ if (obj.on !== void 0 || obj.duration !== void 0) {
144816
+ if (obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
144817
+ badEntry(
144818
+ entry,
144819
+ 'the long-press options form takes a nested selector \u2014 e.g. long-press: { on: { text: "Row" }, duration: 1200 }'
144820
+ );
144821
+ }
144822
+ if (!Object.keys(obj).every((k) => k === "on" || k === "duration")) {
144823
+ badEntry(entry, "the long-press options form accepts only { on, duration }");
144824
+ }
144825
+ if (obj.on === void 0) {
144826
+ badEntry(entry, 'long-press needs a target \u2014 e.g. long-press: { on: "Row", duration: 1200 }');
144827
+ }
144828
+ const step = { kind: "long-press", selector: parseSelector(obj.on, "long-press.on") };
144829
+ if (obj.duration !== void 0) {
144830
+ if (typeof obj.duration !== "number" || !Number.isFinite(obj.duration) || obj.duration <= 0) {
144831
+ badEntry(
144832
+ entry,
144833
+ "long-press.duration needs a positive number of milliseconds (e.g. `duration: 1200`)"
144834
+ );
144835
+ }
144836
+ step.duration = obj.duration;
144837
+ }
144838
+ return step;
144839
+ }
144840
+ return { kind: "long-press", selector: parseSelector(body, "long-press") };
144841
+ }
144797
144842
  function fromYamlStep(raw) {
144798
144843
  const entry = raw;
144799
144844
  const kinds = STEP_DIRECTIVE_KEYS.filter((k) => k in entry);
@@ -144836,6 +144881,9 @@ function fromYamlStep(raw) {
144836
144881
  }
144837
144882
  return { kind: "tap", selector: parseSelector(body, "tap") };
144838
144883
  }
144884
+ if ("long-press" in raw) {
144885
+ return parseLongPress(raw["long-press"], raw);
144886
+ }
144839
144887
  if ("type" in raw) {
144840
144888
  const body = raw.type;
144841
144889
  if (!body || typeof body !== "object") badEntry(raw, "type needs { into, text }");
@@ -145767,6 +145815,8 @@ You can still edit the .yaml file directly afterwards to remove or reorder steps
145767
145815
  return `${n}. run: ${step.flow}`;
145768
145816
  case "tap":
145769
145817
  return `${n}. tap: ${step.selector ? selectorLabel(step.selector) : `(${step.x}, ${step.y})`}`;
145818
+ case "long-press":
145819
+ return `${n}. long-press: ${selectorLabel(step.selector)}`;
145770
145820
  case "type":
145771
145821
  return `${n}. type: ${selectorLabel(step.into)} \u2190 "${step.text}"`;
145772
145822
  case "await":
@@ -146013,7 +146063,7 @@ function offscreenHint(sel) {
146013
146063
  return `no visible element matched selector ${describeSelector(sel)} \u2014 if it is off-screen, add a scroll-to step before this one`;
146014
146064
  }
146015
146065
  async function runDirective(env, step) {
146016
- if (env.device.platform === "vega" && (step.kind === "tap" || step.kind === "type" || step.kind === "scroll-to")) {
146066
+ if (env.device.platform === "vega" && (step.kind === "tap" || step.kind === "long-press" || step.kind === "type" || step.kind === "scroll-to")) {
146017
146067
  return {
146018
146068
  ok: false,
146019
146069
  reason: `${step.kind} is a touch directive and Vega is remote-driven \u2014 move focus with \`tool: tv-remote\` steps (and type via \`tool: keyboard\`) instead`
@@ -146022,6 +146072,8 @@ async function runDirective(env, step) {
146022
146072
  switch (step.kind) {
146023
146073
  case "tap":
146024
146074
  return runTap(env, step);
146075
+ case "long-press":
146076
+ return runLongPress(env, step);
146025
146077
  case "type":
146026
146078
  return runType(env, step);
146027
146079
  case "await":
@@ -146052,6 +146104,33 @@ async function runTap(env, target) {
146052
146104
  await invokeOnDevice(env, "gesture-tap", point);
146053
146105
  return { ok: true };
146054
146106
  }
146107
+ var DEFAULT_LONG_PRESS_MS = 800;
146108
+ async function runLongPress(env, step) {
146109
+ const frame = await waitForFrame(env, step.selector);
146110
+ if (frame === "aborted") return ABORTED_OUTCOME;
146111
+ if (!frame) {
146112
+ return { ok: false, reason: offscreenHint(step.selector) };
146113
+ }
146114
+ const point = getDescribeTapPoint(frame);
146115
+ const duration3 = step.duration ?? DEFAULT_LONG_PRESS_MS;
146116
+ if (env.device.platform === "chromium") {
146117
+ await invokeOnDevice(env, "gesture-drag", {
146118
+ fromX: point.x,
146119
+ fromY: point.y,
146120
+ toX: point.x,
146121
+ toY: point.y,
146122
+ durationMs: duration3
146123
+ });
146124
+ } else {
146125
+ await invokeOnDevice(env, "gesture-custom", {
146126
+ events: [
146127
+ { type: "Down", x: point.x, y: point.y, delayMs: 0 },
146128
+ { type: "Up", x: point.x, y: point.y, delayMs: duration3 }
146129
+ ]
146130
+ });
146131
+ }
146132
+ return { ok: true };
146133
+ }
146055
146134
  async function runType(env, step) {
146056
146135
  const frame = await waitForFrame(env, step.into);
146057
146136
  if (frame === "aborted") return ABORTED_OUTCOME;
@@ -148486,8 +148565,9 @@ function createRunFlowTool(registry2) {
148486
148565
  id: "flow-execute",
148487
148566
  description: `Run a saved flow from the .argent/flows/ directory.
148488
148567
  Steps run in order: \`launch\` starts an app from scratch (terminate + relaunch) and waits until it is
148489
- ready; \`tool\` calls dispatch through the registry; \`tap\`/\`type\` resolve a selector to an element and
148490
- act on it; \`scroll-to\` scrolls (momentum-free) until a target is visible; \`await\` waits for a UI
148568
+ ready; \`tool\` calls dispatch through the registry; \`tap\`/\`long-press\`/\`type\` resolve a selector to an
148569
+ element and act on it (\`long-press: { on, duration }\` presses and holds); \`scroll-to\` scrolls
148570
+ (momentum-free) until a target is visible; \`await\` waits for a UI
148491
148571
  condition; \`wait\` pauses for a fixed number of milliseconds; \`assert\` checks one now; \`snapshot\`
148492
148572
  diffs a screenshot against a stored baseline (a missing baseline fails the step \u2014 set updateBaselines
148493
148573
  to adopt the current screen); \`echo\` annotates; \`run\` executes a referenced fragment inline.
@@ -148642,6 +148722,8 @@ function stepTarget(step) {
148642
148722
  if (step.selector) return selectorLabel2(step.selector);
148643
148723
  if (step.x !== void 0 && step.y !== void 0) return `(${step.x}, ${step.y})`;
148644
148724
  return void 0;
148725
+ case "long-press":
148726
+ return selectorLabel2(step.selector);
148645
148727
  case "type":
148646
148728
  return `into ${selectorLabel2(step.into)}`;
148647
148729
  case "await":
@@ -148737,6 +148819,7 @@ async function execLeafStep(state3, step, index, sourceFlow) {
148737
148819
  return { ...base, status: r.ok ? "pass" : "error", reason: r.reason };
148738
148820
  }
148739
148821
  case "tap":
148822
+ case "long-press":
148740
148823
  case "type":
148741
148824
  case "await":
148742
148825
  case "assert":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swmansion/argent",
3
- "version": "0.15.1-next.2",
3
+ "version": "0.15.1-next.4",
4
4
  "description": "MCP server for iOS Simulator and Android Emulator control",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -20,17 +20,18 @@ Both run via `argent flow run <name>` — a fragment simply runs against whateve
20
20
 
21
21
  Beyond raw `tool:` steps and `echo:`, flows support declarative directives interpreted by the runner (they are **not** agent-callable tools). **Every directive hard-stops the flow on failure**; later steps are reported `skip`.
22
22
 
23
- | Directive | YAML | Meaning |
24
- | ----------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
25
- | `launch` | `- launch: com.acme.app` or `- launch: { ios: …, android: … }` | start the app from scratch (terminate + relaunch) and wait until ready |
26
- | `tap` | `- tap: Login` or `- tap: { x: 0.5, y: 0.57 }` | tap an element by selector (auto-waits), or a raw normalized point |
27
- | `type` | `- type: { into: email, text: "a@b.com" }` | focus a field, type, then press Enter to submit + dismiss the keyboard |
28
- | `scroll-to` | `- scroll-to: "Order #1234"` (scrolls down) or `- scroll-to: { target: …, direction: right, within: }` | momentum-free scroll until the target is visible |
29
- | `await` | `- await: { visible: Home }` | wait for a UI condition |
30
- | `wait` | `- wait: 500` | pause for a fixed number of milliseconds (last resort — prefer `await`) |
31
- | `assert` | `- assert: { visible: Welcome }` | check a condition, hard-fail if it never holds |
32
- | `snapshot` | `- snapshot: home` or `- snapshot: { name: home, maxMismatch: 0.5 }` | diff a screenshot against a stored baseline |
33
- | `run` | `- run: login` | execute a fragment's steps inline |
23
+ | Directive | YAML | Meaning |
24
+ | ------------ | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
25
+ | `launch` | `- launch: com.acme.app` or `- launch: { ios: …, android: … }` | start the app from scratch (terminate + relaunch) and wait until ready |
26
+ | `tap` | `- tap: Login` or `- tap: { x: 0.5, y: 0.57 }` | tap an element by selector (auto-waits), or a raw normalized point |
27
+ | `long-press` | `- long-press: Row 3` or `- long-press: { on: <sel>, duration: 1200 }` | press and hold an element (default 800ms; Chromium: mouse press-hold) |
28
+ | `type` | `- type: { into: email, text: "a@b.com" }` | focus a field, type, then press Enter to submit + dismiss the keyboard |
29
+ | `scroll-to` | `- scroll-to: "Order #1234"` (scrolls down) or `- scroll-to: { target: …, direction: right, within: … }` | momentum-free scroll until the target is visible |
30
+ | `await` | `- await: { visible: Home }` | wait for a UI condition |
31
+ | `wait` | `- wait: 500` | pause for a fixed number of milliseconds (last resort — prefer `await`) |
32
+ | `assert` | `- assert: { visible: Welcome }` | check a condition, hard-fail if it never holds |
33
+ | `snapshot` | `- snapshot: home` or `- snapshot: { name: home, maxMismatch: 0.5 }` | diff a screenshot against a stored baseline |
34
+ | `run` | `- run: login` | execute a fragment's steps inline |
34
35
 
35
36
  ### Selectors
36
37
 
@@ -60,7 +61,7 @@ For a custom poll interval or bundleId, drop to an explicit `- tool: await-ui-el
60
61
 
61
62
  ### TV targets (Vega)
62
63
 
63
- A Vega (Fire TV) device is remote-driven — there is no touch input, so the touch directives (`tap`, `type`, `scroll-to`) fail on it with guidance. Drive focus with `tool: tv-remote` steps and type with `tool: keyboard` instead; everything else (`launch`, `await`, `assert`, `wait`, `snapshot`, `echo`, `run`, selectors) works unchanged — the tree comes from the on-device automation toolkit, which attaches at app launch (the `launch` step waits for it, so a leading `launch` also guarantees selectors resolve).
64
+ A Vega (Fire TV) device is remote-driven — there is no touch input, so the touch directives (`tap`, `long-press`, `type`, `scroll-to`) fail on it with guidance. Drive focus with `tool: tv-remote` steps and type with `tool: keyboard` instead; everything else (`launch`, `await`, `assert`, `wait`, `snapshot`, `echo`, `run`, selectors) works unchanged — the tree comes from the on-device automation toolkit, which attaches at app launch (the `launch` step waits for it, so a leading `launch` also guarantees selectors resolve).
64
65
 
65
66
  ```yaml
66
67
  steps: