@swmansion/argent 0.16.1-next.1 → 0.16.1-next.2
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 +112 -76
- package/package.json +1 -1
- package/skills/argent-create-flow/SKILL.md +12 -12
package/dist/tool-server.cjs
CHANGED
|
@@ -144726,6 +144726,28 @@ function textWaitToYaml(selector, expectedText, textMatch) {
|
|
|
144726
144726
|
}
|
|
144727
144727
|
}
|
|
144728
144728
|
}
|
|
144729
|
+
function targetToYaml(step) {
|
|
144730
|
+
const hasPointField = step.x !== void 0 || step.y !== void 0;
|
|
144731
|
+
if (step.selector !== void 0) {
|
|
144732
|
+
if (hasPointField) {
|
|
144733
|
+
throw new Error(
|
|
144734
|
+
"Cannot serialize flow gesture target: use a selector or x/y coordinates, not both"
|
|
144735
|
+
);
|
|
144736
|
+
}
|
|
144737
|
+
return selectorToYaml(step.selector);
|
|
144738
|
+
}
|
|
144739
|
+
if (typeof step.x !== "number" || typeof step.y !== "number") {
|
|
144740
|
+
throw new Error(
|
|
144741
|
+
"Cannot serialize flow gesture target: a coordinate target needs numeric x and y"
|
|
144742
|
+
);
|
|
144743
|
+
}
|
|
144744
|
+
if (!(step.x >= 0 && step.x <= 1) || !(step.y >= 0 && step.y <= 1)) {
|
|
144745
|
+
throw new Error(
|
|
144746
|
+
"Cannot serialize flow gesture target: coordinates are normalized 0\u20131 fractions of the screen, not pixels"
|
|
144747
|
+
);
|
|
144748
|
+
}
|
|
144749
|
+
return { x: step.x, y: step.y };
|
|
144750
|
+
}
|
|
144729
144751
|
function waitToYaml(condition, selector, expectedText, textMatch, timeoutMs) {
|
|
144730
144752
|
const sel = selectorToYaml(selector);
|
|
144731
144753
|
let body;
|
|
@@ -144755,18 +144777,13 @@ function toYamlStep(step) {
|
|
|
144755
144777
|
case "run":
|
|
144756
144778
|
return { run: step.flow };
|
|
144757
144779
|
case "tap": {
|
|
144758
|
-
|
|
144759
|
-
|
|
144760
|
-
return { tap: step.times !== void 0 ? { on: sel, times: step.times } : sel };
|
|
144761
|
-
}
|
|
144762
|
-
const body = { x: step.x, y: step.y };
|
|
144763
|
-
if (step.times !== void 0) body.times = step.times;
|
|
144764
|
-
return { tap: body };
|
|
144780
|
+
const target = targetToYaml(step);
|
|
144781
|
+
return { tap: step.times !== void 0 ? { on: target, times: step.times } : target };
|
|
144765
144782
|
}
|
|
144766
144783
|
case "long-press": {
|
|
144767
|
-
const
|
|
144784
|
+
const target = targetToYaml(step);
|
|
144768
144785
|
return {
|
|
144769
|
-
"long-press": step.duration !== void 0 ? { on:
|
|
144786
|
+
"long-press": step.duration !== void 0 ? { on: target, duration: step.duration } : target
|
|
144770
144787
|
};
|
|
144771
144788
|
}
|
|
144772
144789
|
case "type": {
|
|
@@ -145079,35 +145096,6 @@ var STEP_DIRECTIVE_KEYS = [
|
|
|
145079
145096
|
"scroll-to",
|
|
145080
145097
|
"snapshot"
|
|
145081
145098
|
];
|
|
145082
|
-
function parseLongPress(body, entry) {
|
|
145083
|
-
const obj = body !== null && typeof body === "object" ? body : {};
|
|
145084
|
-
if (obj.on !== void 0 || obj.duration !== void 0) {
|
|
145085
|
-
if (obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
|
|
145086
|
-
badEntry(
|
|
145087
|
-
entry,
|
|
145088
|
-
'the long-press options form takes a nested selector \u2014 e.g. long-press: { on: { text: "Row" }, duration: 1200 }'
|
|
145089
|
-
);
|
|
145090
|
-
}
|
|
145091
|
-
if (!Object.keys(obj).every((k) => k === "on" || k === "duration")) {
|
|
145092
|
-
badEntry(entry, "the long-press options form accepts only { on, duration }");
|
|
145093
|
-
}
|
|
145094
|
-
if (obj.on === void 0) {
|
|
145095
|
-
badEntry(entry, 'long-press needs a target \u2014 e.g. long-press: { on: "Row", duration: 1200 }');
|
|
145096
|
-
}
|
|
145097
|
-
const step = { kind: "long-press", selector: parseSelector(obj.on, "long-press.on") };
|
|
145098
|
-
if (obj.duration !== void 0) {
|
|
145099
|
-
if (typeof obj.duration !== "number" || !Number.isFinite(obj.duration) || obj.duration <= 0) {
|
|
145100
|
-
badEntry(
|
|
145101
|
-
entry,
|
|
145102
|
-
"long-press.duration needs a positive number of milliseconds (e.g. `duration: 1200`)"
|
|
145103
|
-
);
|
|
145104
|
-
}
|
|
145105
|
-
step.duration = obj.duration;
|
|
145106
|
-
}
|
|
145107
|
-
return step;
|
|
145108
|
-
}
|
|
145109
|
-
return { kind: "long-press", selector: parseSelector(body, "long-press") };
|
|
145110
|
-
}
|
|
145111
145099
|
function parseTapTimes(raw, entry) {
|
|
145112
145100
|
if (raw === void 0) return void 0;
|
|
145113
145101
|
if (typeof raw !== "number" || !Number.isInteger(raw) || raw < 1 || raw > 10) {
|
|
@@ -145115,23 +145103,32 @@ function parseTapTimes(raw, entry) {
|
|
|
145115
145103
|
}
|
|
145116
145104
|
return raw === 1 ? void 0 : raw;
|
|
145117
145105
|
}
|
|
145118
|
-
function
|
|
145119
|
-
|
|
145120
|
-
|
|
145121
|
-
if (obj.
|
|
145122
|
-
|
|
145123
|
-
|
|
145124
|
-
|
|
145125
|
-
|
|
145126
|
-
|
|
145127
|
-
|
|
145128
|
-
|
|
145106
|
+
function parseTarget(raw, where) {
|
|
145107
|
+
if (raw !== null && typeof raw === "object") {
|
|
145108
|
+
const obj = raw;
|
|
145109
|
+
if (obj.x !== void 0 || obj.y !== void 0) {
|
|
145110
|
+
if (obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
|
|
145111
|
+
badEntry(raw, `${where} takes a selector or x/y coordinates, not both`);
|
|
145112
|
+
}
|
|
145113
|
+
if (typeof obj.x !== "number" || typeof obj.y !== "number") {
|
|
145114
|
+
badEntry(raw, `${where}: a coordinate target needs numeric x and y`);
|
|
145115
|
+
}
|
|
145116
|
+
if (!(obj.x >= 0 && obj.x <= 1) || !(obj.y >= 0 && obj.y <= 1)) {
|
|
145117
|
+
badEntry(
|
|
145118
|
+
raw,
|
|
145119
|
+
`${where}: coordinates are normalized 0\u20131 fractions of the screen, not pixels`
|
|
145120
|
+
);
|
|
145121
|
+
}
|
|
145122
|
+
if (!Object.keys(obj).every((k) => k === "x" || k === "y")) {
|
|
145123
|
+
badEntry(raw, `${where}: a coordinate target takes only { x, y }`);
|
|
145124
|
+
}
|
|
145125
|
+
return { x: obj.x, y: obj.y };
|
|
145129
145126
|
}
|
|
145130
|
-
const step = { kind: "tap", x: obj.x, y: obj.y };
|
|
145131
|
-
const times = parseTapTimes(obj.times, entry);
|
|
145132
|
-
if (times !== void 0) step.times = times;
|
|
145133
|
-
return step;
|
|
145134
145127
|
}
|
|
145128
|
+
return { selector: parseSelector(raw, where) };
|
|
145129
|
+
}
|
|
145130
|
+
function parseTap(body, entry) {
|
|
145131
|
+
const obj = body !== null && typeof body === "object" ? body : {};
|
|
145135
145132
|
if (obj.on !== void 0 || obj.times !== void 0) {
|
|
145136
145133
|
if (obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
|
|
145137
145134
|
badEntry(
|
|
@@ -145139,18 +145136,59 @@ function parseTap(body, entry) {
|
|
|
145139
145136
|
'the tap options form takes a nested selector \u2014 e.g. tap: { on: { text: "Photo" }, times: 2 }'
|
|
145140
145137
|
);
|
|
145141
145138
|
}
|
|
145139
|
+
if (obj.x !== void 0 || obj.y !== void 0) {
|
|
145140
|
+
badEntry(
|
|
145141
|
+
entry,
|
|
145142
|
+
"the tap options form takes a nested point \u2014 e.g. tap: { on: { x: 0.5, y: 0.5 }, times: 2 }"
|
|
145143
|
+
);
|
|
145144
|
+
}
|
|
145142
145145
|
if (!Object.keys(obj).every((k) => k === "on" || k === "times")) {
|
|
145143
145146
|
badEntry(entry, "the tap options form accepts only { on, times }");
|
|
145144
145147
|
}
|
|
145145
145148
|
if (obj.on === void 0) {
|
|
145146
145149
|
badEntry(entry, 'tap with times needs a target \u2014 e.g. tap: { on: "Photo", times: 2 }');
|
|
145147
145150
|
}
|
|
145148
|
-
const step = { kind: "tap",
|
|
145151
|
+
const step = { kind: "tap", ...parseTarget(obj.on, "tap.on") };
|
|
145149
145152
|
const times = parseTapTimes(obj.times, entry);
|
|
145150
145153
|
if (times !== void 0) step.times = times;
|
|
145151
145154
|
return step;
|
|
145152
145155
|
}
|
|
145153
|
-
return { kind: "tap",
|
|
145156
|
+
return { kind: "tap", ...parseTarget(body, "tap") };
|
|
145157
|
+
}
|
|
145158
|
+
function parseLongPress(body, entry) {
|
|
145159
|
+
const obj = body !== null && typeof body === "object" ? body : {};
|
|
145160
|
+
if (obj.on !== void 0 || obj.duration !== void 0) {
|
|
145161
|
+
if (obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
|
|
145162
|
+
badEntry(
|
|
145163
|
+
entry,
|
|
145164
|
+
'the long-press options form takes a nested selector \u2014 e.g. long-press: { on: { text: "Row" }, duration: 1200 }'
|
|
145165
|
+
);
|
|
145166
|
+
}
|
|
145167
|
+
if (obj.x !== void 0 || obj.y !== void 0) {
|
|
145168
|
+
badEntry(
|
|
145169
|
+
entry,
|
|
145170
|
+
"the long-press options form takes a nested point \u2014 e.g. long-press: { on: { x: 0.5, y: 0.5 }, duration: 1200 }"
|
|
145171
|
+
);
|
|
145172
|
+
}
|
|
145173
|
+
if (!Object.keys(obj).every((k) => k === "on" || k === "duration")) {
|
|
145174
|
+
badEntry(entry, "the long-press options form accepts only { on, duration }");
|
|
145175
|
+
}
|
|
145176
|
+
if (obj.on === void 0) {
|
|
145177
|
+
badEntry(entry, 'long-press needs a target \u2014 e.g. long-press: { on: "Row", duration: 1200 }');
|
|
145178
|
+
}
|
|
145179
|
+
const step = { kind: "long-press", ...parseTarget(obj.on, "long-press.on") };
|
|
145180
|
+
if (obj.duration !== void 0) {
|
|
145181
|
+
if (typeof obj.duration !== "number" || !Number.isFinite(obj.duration) || obj.duration <= 0) {
|
|
145182
|
+
badEntry(
|
|
145183
|
+
entry,
|
|
145184
|
+
"long-press.duration needs a positive number of milliseconds (e.g. `duration: 1200`)"
|
|
145185
|
+
);
|
|
145186
|
+
}
|
|
145187
|
+
step.duration = obj.duration;
|
|
145188
|
+
}
|
|
145189
|
+
return step;
|
|
145190
|
+
}
|
|
145191
|
+
return { kind: "long-press", ...parseTarget(body, "long-press") };
|
|
145154
145192
|
}
|
|
145155
145193
|
function fromYamlStep(raw) {
|
|
145156
145194
|
const entry = raw;
|
|
@@ -146108,9 +146146,8 @@ You can still edit the .yaml file directly afterwards to remove or reorder steps
|
|
|
146108
146146
|
case "run":
|
|
146109
146147
|
return `${n}. run: ${step.flow}`;
|
|
146110
146148
|
case "tap":
|
|
146111
|
-
return `${n}. tap: ${step.selector ? selectorLabel(step.selector) : `(${step.x}, ${step.y})`}`;
|
|
146112
146149
|
case "long-press":
|
|
146113
|
-
return `${n}.
|
|
146150
|
+
return `${n}. ${step.kind}: ${step.selector ? selectorLabel(step.selector) : `(${step.x}, ${step.y})`}`;
|
|
146114
146151
|
case "type":
|
|
146115
146152
|
return `${n}. type: ${selectorLabel(step.into)} \u2190 "${step.text}"`;
|
|
146116
146153
|
case "await":
|
|
@@ -146388,20 +146425,23 @@ async function runDirective(env, step) {
|
|
|
146388
146425
|
}
|
|
146389
146426
|
}
|
|
146390
146427
|
}
|
|
146391
|
-
async function
|
|
146392
|
-
let point;
|
|
146428
|
+
async function resolveTargetPoint(env, target) {
|
|
146393
146429
|
if (target.selector) {
|
|
146394
146430
|
const frame = await waitForFrame(env, target.selector);
|
|
146395
|
-
if (frame === "aborted") return ABORTED_OUTCOME;
|
|
146431
|
+
if (frame === "aborted") return { fail: ABORTED_OUTCOME };
|
|
146396
146432
|
if (!frame) {
|
|
146397
|
-
return { ok: false, reason: offscreenHint(target.selector) };
|
|
146433
|
+
return { fail: { ok: false, reason: offscreenHint(target.selector) } };
|
|
146398
146434
|
}
|
|
146399
|
-
|
|
146400
|
-
}
|
|
146401
|
-
|
|
146402
|
-
|
|
146403
|
-
return { ok: false, reason: "tap needs a selector or x/y coordinates" };
|
|
146435
|
+
return getDescribeTapPoint(frame);
|
|
146436
|
+
}
|
|
146437
|
+
if (typeof target.x === "number" && typeof target.y === "number") {
|
|
146438
|
+
return { x: target.x, y: target.y };
|
|
146404
146439
|
}
|
|
146440
|
+
return { fail: { ok: false, reason: "gesture needs a selector or x/y coordinates" } };
|
|
146441
|
+
}
|
|
146442
|
+
async function runTap(env, target) {
|
|
146443
|
+
const point = await resolveTargetPoint(env, target);
|
|
146444
|
+
if ("fail" in point) return point.fail;
|
|
146405
146445
|
await invokeOnDevice(env, "gesture-tap", {
|
|
146406
146446
|
...point,
|
|
146407
146447
|
...target.times !== void 0 ? { clickCount: target.times } : {}
|
|
@@ -146410,12 +146450,8 @@ async function runTap(env, target) {
|
|
|
146410
146450
|
}
|
|
146411
146451
|
var DEFAULT_LONG_PRESS_MS = 800;
|
|
146412
146452
|
async function runLongPress(env, step) {
|
|
146413
|
-
const
|
|
146414
|
-
if (
|
|
146415
|
-
if (!frame) {
|
|
146416
|
-
return { ok: false, reason: offscreenHint(step.selector) };
|
|
146417
|
-
}
|
|
146418
|
-
const point = getDescribeTapPoint(frame);
|
|
146453
|
+
const point = await resolveTargetPoint(env, step);
|
|
146454
|
+
if ("fail" in point) return point.fail;
|
|
146419
146455
|
const duration3 = step.duration ?? DEFAULT_LONG_PRESS_MS;
|
|
146420
146456
|
if (env.device.platform === "chromium") {
|
|
146421
146457
|
await invokeOnDevice(env, "gesture-drag", {
|
|
@@ -148877,8 +148913,9 @@ function createRunFlowTool(registry2) {
|
|
|
148877
148913
|
description: `Run a saved flow from the .argent/flows/ directory.
|
|
148878
148914
|
Steps run in order: \`launch\` starts an app from scratch (terminate + relaunch) and waits until it is
|
|
148879
148915
|
ready; \`tool\` calls dispatch through the registry; \`tap\`/\`long-press\`/\`type\` resolve a selector to an
|
|
148880
|
-
element and act on it (\`long-press: { on, duration }\` presses and
|
|
148881
|
-
|
|
148916
|
+
element and act on it (\`tap: { on, times: 2 }\` double-taps; \`long-press: { on, duration }\` presses and
|
|
148917
|
+
holds; \`tap\`/\`long-press\` alternatively take a raw normalized point \u2014 bare \`{ x, y }\` or \`on: { x, y }\`);
|
|
148918
|
+
\`scroll-to\` scrolls (momentum-free) until a target is visible; \`await\` waits for a UI
|
|
148882
148919
|
condition; \`wait\` pauses for a fixed number of milliseconds; \`assert\` checks one now; \`snapshot\`
|
|
148883
148920
|
diffs a screenshot against a stored baseline (a missing baseline fails the step \u2014 set updateBaselines
|
|
148884
148921
|
to adopt the current screen); \`echo\` annotates; \`run\` executes a referenced fragment inline.
|
|
@@ -149032,11 +149069,10 @@ function selectorLabel2(sel) {
|
|
|
149032
149069
|
function stepTarget(step) {
|
|
149033
149070
|
switch (step.kind) {
|
|
149034
149071
|
case "tap":
|
|
149072
|
+
case "long-press":
|
|
149035
149073
|
if (step.selector) return selectorLabel2(step.selector);
|
|
149036
149074
|
if (step.x !== void 0 && step.y !== void 0) return `(${step.x}, ${step.y})`;
|
|
149037
149075
|
return void 0;
|
|
149038
|
-
case "long-press":
|
|
149039
|
-
return selectorLabel2(step.selector);
|
|
149040
149076
|
case "type":
|
|
149041
149077
|
return `into ${selectorLabel2(step.into)}`;
|
|
149042
149078
|
case "await":
|
package/package.json
CHANGED
|
@@ -20,18 +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
|
|
24
|
-
| ------------ |
|
|
25
|
-
| `launch` | `- launch: com.acme.app` or `- launch: { ios: …, android: … }`
|
|
26
|
-
| `tap` | `- tap: Login`, `- tap: { x: 0.5, y: 0.57 }`, `- tap: { on: Login, times: 2 }`
|
|
27
|
-
| `long-press` | `- long-press: Row 3
|
|
28
|
-
| `type` | `- type: { into: email, text: "a@b.com" }`
|
|
29
|
-
| `scroll-to` | `- scroll-to: "Order #1234"` (scrolls down) or `- scroll-to: { target: …, direction: right, within: … }`
|
|
30
|
-
| `await` | `- await: { visible: Home }`
|
|
31
|
-
| `wait` | `- wait: 500`
|
|
32
|
-
| `assert` | `- assert: { visible: Welcome }`
|
|
33
|
-
| `snapshot` | `- snapshot: home` or `- snapshot: { name: home, maxMismatch: 0.5 }`
|
|
34
|
-
| `run` | `- run: login`
|
|
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`, `- tap: { x: 0.5, y: 0.57 }`, `- tap: { on: Login, times: 2 }`, `- tap: { on: { x: 0.5, y: 0.57 }, times: 2 }` | tap by selector (auto-waits) or raw point; `times` (2 = double-tap) needs the target nested under `on:` — a selector or a point (`{ x, y, times }` is rejected) |
|
|
27
|
+
| `long-press` | `- long-press: Row 3`, `- long-press: { x: 0.5, y: 0.6 }`, `- long-press: { on: <sel>, duration: 1200 }`, `- long-press: { on: { x: 0.5, y: 0.6 }, duration: 1200 }` | press and hold an element or raw point (default 800ms; Chromium: mouse press-hold); `duration` needs the target nested under `on:` — a selector or a point |
|
|
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 another flow's steps inline (fragment or e2e) |
|
|
35
35
|
|
|
36
36
|
### Selectors
|
|
37
37
|
|