@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.
- package/dist/tool-server.cjs +116 -33
- package/package.json +1 -1
- package/skills/argent-create-flow/SKILL.md +13 -12
package/dist/tool-server.cjs
CHANGED
|
@@ -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
|
-
|
|
126790
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
|
148490
|
-
act on it
|
|
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
|
@@ -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
|
|
24
|
-
|
|
|
25
|
-
| `launch`
|
|
26
|
-
| `tap`
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
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:
|