@swmansion/argent 0.18.1-next.8 → 0.19.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/simulator-server +0 -0
- package/bin/darwin/tvos-ax-service +0 -0
- package/bin/darwin/tvos-hid-daemon +0 -0
- package/bin/linux/simulator-server +0 -0
- package/bin/linux-arm64/simulator-server +0 -0
- package/bin/tcp/ax-service +0 -0
- package/bin/win32/simulator-server.exe +0 -0
- package/dist/cli-cmds.mjs +2087 -876
- package/dist/cli.js +11 -7
- package/dist/cli.js.map +1 -1
- package/dist/installer-help.d.ts +13 -2
- package/dist/installer-help.js +41 -2
- package/dist/installer-help.js.map +1 -1
- package/dist/installer.mjs +838 -464
- package/dist/mcp-server.mjs +1114 -775
- package/dist/tool-server.cjs +2421 -1672
- 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 +4 -1
- package/skills/argent-create-flow/SKILL.md +32 -23
- package/skills/argent-device-interact/SKILL.md +13 -3
- package/skills/argent-test-ui-flow/SKILL.md +1 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swmansion/argent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
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",
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
"benchmark": "node scripts/benchmark.cjs"
|
|
27
27
|
},
|
|
28
28
|
"private": false,
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=20.12.0"
|
|
31
|
+
},
|
|
29
32
|
"publishConfig": {
|
|
30
33
|
"registry": "https://registry.npmjs.org",
|
|
31
34
|
"access": "public"
|
|
@@ -5,7 +5,7 @@ description: Record a reusable flow (scripted sequence of MCP tool calls) that c
|
|
|
5
5
|
|
|
6
6
|
## Overview
|
|
7
7
|
|
|
8
|
-
A flow is a sequence of steps saved to a `.yaml` file in the `.argent/flows/` directory. Each recorded step is **executed live** as you add it, so you verify it works before it becomes part of the flow. Replay a finished flow with `flow-execute`, or — for an e2e flow — headlessly with `argent flow run
|
|
8
|
+
A flow is a sequence of steps saved to a `.yaml` file in the `.argent/flows/` directory. Each recorded step is **executed live** as you add it, so you verify it works before it becomes part of the flow. Replay a finished flow with `flow-execute`, or — for an e2e flow — headlessly with `argent flow run checkout` (a saved flow's name) or `argent flow run path/to/checkout.yaml`.
|
|
9
9
|
|
|
10
10
|
Flows store **no device id**: the runner binds a device (the single booted one, or pass `device`/`platform`). A recorded coordinate `gesture-tap` is captured as a portable `tap: { selector }` step whenever the tapped element has stable text/identifier.
|
|
11
11
|
|
|
@@ -14,27 +14,27 @@ Flows store **no device id**: the runner binds a device (the single booted one,
|
|
|
14
14
|
- **e2e** — begins with a `launch:` step, which starts that app from scratch (terminate + relaunch), so the flow controls its own start state. No `executionPrerequisite`. May `run:` other flows, and (on iOS/Android) may itself be a `run:` target — when nested, its `launch` runs inline, restarting the app for that sub-scenario. **Chromium is the exception:** the runner boots one Electron app per run (the top-level flow's), so a nested chromium e2e flow's `launch` can't boot its own instance and fails the run — keep chromium e2e flows top-level. Record one by adding a `restart-app` of the app under test as the **first** step — it is captured as the `launch` step.
|
|
15
15
|
- **fragment** — doesn't begin with a launch; runs against the device's current state. May declare an `executionPrerequisite` (a documented entry-state contract). Invoked from other flows via a `run:` step, or directly by you at any time.
|
|
16
16
|
|
|
17
|
-
Both run via `argent flow run <
|
|
17
|
+
Both run via `argent flow run <flow|flow.yaml>` — a fragment simply runs against whatever is on screen (its prerequisite is printed as a reminder). A bare name is read from `.argent/flows/<name>.yaml`; anything ending in `.yaml` is a path. Only e2e flows are meaningful CI/suite entries, since only they give a deterministic verdict from a clean start.
|
|
18
18
|
|
|
19
19
|
### Step directives
|
|
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`, `- 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
|
-
| `pinch` | `- pinch: { on: "Map", scale: 3 }` or `- pinch: { scale: 0.5 }` | two-finger zoom in (`scale` > 1) or out (`< 1`); big scales chain gestures; `on` optional — defaults to screen center; open-loop — assert the visible result
|
|
31
|
-
| `rotate` | `- rotate: { on: "Map", by: 90 }` or `- rotate: { by: -45 }` | two-finger rotation by degrees (+ CW, − CCW, within ±3000°; options map only); `on` optional — screen center default; not `tool: rotate` (orientation)
|
|
32
|
-
| `await` | `- await: { visible: Home }` | wait for a UI condition
|
|
33
|
-
| `wait` | `- wait: 500` | pause for a fixed number of milliseconds (last resort — prefer `await`)
|
|
34
|
-
| `assert` | `- assert: { visible: Welcome }` | check a condition, hard-fail if it never holds
|
|
35
|
-
| `snapshot` | `- snapshot: home` or `- snapshot: { name: home, maxMismatch: 0.5, cropOn: { id: order-summary } }` | diff a screenshot — or one element's region — against a stored baseline
|
|
36
|
-
| `run` | `- run: login`
|
|
37
|
-
| `when` | `- when: { visible: "What's new" }` + `steps: [...]` | run a guarded step block only when the condition holds (no else)
|
|
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
|
+
| `pinch` | `- pinch: { on: "Map", scale: 3 }` or `- pinch: { scale: 0.5 }` | two-finger zoom in (`scale` > 1) or out (`< 1`); big scales chain gestures; `on` optional — defaults to screen center; open-loop — assert the visible result |
|
|
31
|
+
| `rotate` | `- rotate: { on: "Map", by: 90 }` or `- rotate: { by: -45 }` | two-finger rotation by degrees (+ CW, − CCW, within ±3000°; options map only); `on` optional — screen center default; not `tool: rotate` (orientation) |
|
|
32
|
+
| `await` | `- await: { visible: Home }` | wait for a UI condition |
|
|
33
|
+
| `wait` | `- wait: 500` | pause for a fixed number of milliseconds (last resort — prefer `await`) |
|
|
34
|
+
| `assert` | `- assert: { visible: Welcome }` | check a condition, hard-fail if it never holds |
|
|
35
|
+
| `snapshot` | `- snapshot: home` or `- snapshot: { name: home, maxMismatch: 0.5, cropOn: { id: order-summary } }` | diff a screenshot — or one element's region — against a stored baseline |
|
|
36
|
+
| `run` | `- run: login.yaml` | execute another flow's steps inline (fragment or e2e); a YAML path resolved relative to the flow file that contains the step (e.g. `../shared/login.yaml`); `.yaml` is optional (`run: login` = `login.yaml` beside the flow) |
|
|
37
|
+
| `when` | `- when: { visible: "What's new" }` + `steps: [...]` | run a guarded step block only when the condition holds (no else) |
|
|
38
38
|
|
|
39
39
|
### Selectors
|
|
40
40
|
|
|
@@ -87,7 +87,12 @@ For a custom poll interval or bundleId, drop to an explicit `- tool: await-ui-el
|
|
|
87
87
|
|
|
88
88
|
`type` presses Enter after typing to commit the value and dismiss the keyboard, so it can't cover later targets. For a chained form whose fields feed one explicit submit — e.g. email then password then a `tap: "Log in"` — set `submit: false` on the intermediate fields so a premature Enter doesn't fire the form early: `type: { into: password, text: "hunter2", submit: false }`.
|
|
89
89
|
|
|
90
|
-
Never record a real credential into a flow — the YAML is committed to the repo. Use a secret placeholder instead: `type: { into: password, text: "{{secret:APP_PASSWORD}}" }`. The placeholder is stored verbatim (the YAML stays secret-free) and is resolved at run time by the tool-server from the `ARGENT_SECRET_APP_PASSWORD` environment variable
|
|
90
|
+
Never record a real credential into a flow — the YAML is committed to the repo. Use a secret placeholder instead: `type: { into: password, text: "{{secret:APP_PASSWORD}}" }`. The placeholder is stored verbatim (the YAML stays secret-free) and is resolved at run time by the tool-server, from the `ARGENT_SECRET_APP_PASSWORD` environment variable or an argent secrets file — so one flow runs unchanged everywhere:
|
|
91
|
+
|
|
92
|
+
- **CI** — the job exports `ARGENT_SECRET_APP_PASSWORD` from its secret store; the environment wins over every file.
|
|
93
|
+
- **A developer's machine** — the value lives in a dotenv file instead, so nothing has to be exported and no session has to be restarted: `APP_PASSWORD=…` in `~/.argent/secrets.env` (per user, any project) or in the project's `.argent/secrets.env` (**gitignore it** — the rest of `.argent/` is committed), or `ARGENT_SECRET_APP_PASSWORD=…` in the project's `.env` / `.env.local`, where only prefixed keys are exposed.
|
|
94
|
+
|
|
95
|
+
Same placeholder for a value that is merely _external_ rather than sensitive (a test account's email, a staging tenant id) — it keeps the flow environment-independent. Note that argent treats every such value as a secret: it is redacted from errors and never echoed back, so don't use it for something a report should show.
|
|
91
96
|
|
|
92
97
|
`scroll-to` takes an optional `direction` (`up` | `down` | `left` | `right`, default `down` — so the common case is just `- scroll-to: <selector>`) and optionally a `within: <selector>` that anchors the scroll inside a specific container — required to drive a **nested** scroller (e.g. a horizontal carousel inside a vertical list), since the device can't be asked which container to scroll. This step-level `within` (a sibling of `target`) anchors the _gesture_, and it is the **only** scope key the step body takes — `after:`/`next:`/`any:` beside `target` are rejected. It is distinct from a selector's scopes (see Selectors), which `target` may itself carry — `scroll-to: { target: { text: Delete, within: { id: cards } }, within: { id: settings-list } }` scrolls the settings list until the Delete button _inside the cards container_ is visible. It scrolls in bounded momentum-free increments, re-checks after each, and stops if a scroll reveals nothing new (end of the container). `tap`/`type` do **not** scroll — add a `scroll-to` before any target that may be off-screen. It's a no-op when the target is already visible, so a defensive `scroll-to` costs nothing on replay and keeps the flow working on smaller screens.
|
|
93
98
|
|
|
@@ -113,7 +118,11 @@ Since a `tv-remote` path is positional (like a coordinate tap), gate each naviga
|
|
|
113
118
|
|
|
114
119
|
### Standalone runner
|
|
115
120
|
|
|
116
|
-
`argent flow run <
|
|
121
|
+
`argent flow run <flow|flow.yaml|dir> [--device <id>] [--platform ios|android|chromium|vega] [--update-baselines] [--output <dir>] [-r|--recursive] [--json]` runs a flow with no LLM in the loop and exits non-zero on any failure — suitable for CI (e2e flows; a fragment runs against the current device state, useful while authoring). The argument is either a saved flow's name, read from `.argent/flows/<name>.yaml` under the current directory, or a `.yaml` file path (relative to the current directory, or absolute) for a flow kept anywhere else. The two never collide: a name carries no separator and no extension, so an argument ending in `.yaml` is always a path and never falls back to the flows directory. Either way the filename (minus `.yaml`) names the run's report and artifacts, so it must contain only letters, numbers, `_`, or `-`. A directory path runs every flow in it sequentially, printing only failing steps plus a final `passed/failed/skipped` flow summary (`--json` prints one aggregate object); `-r`/`--recursive` walks subdirectories too, skipping dot-directories and `node_modules`. An invalid flow file fails alone and the batch continues; an infra error stops the batch, counting the remaining flows skipped. Only a path reaches a directory — a name always resolves to one `.yaml` file. `argent flow list` prints runnable paths for flows saved under `.argent/flows/` — a nested one is addressable by its path only.
|
|
122
|
+
|
|
123
|
+
The standalone command uses only the auto-started local tool server. It is unavailable while `ARGENT_TOOLS_URL` or `argent link` routing is active; unset `ARGENT_TOOLS_URL` or run `argent unlink` first. This restriction applies only to the CLI: an agent may continue calling `flow-execute` with `name` and `project_root`, including through a remote tool server — but a remote call uploads the one YAML into a temp directory on the server, and both `run:` targets (whose referenced files stay behind on the client) and `__baselines__/` resolve beside that copy, so only a self-contained flow replays remotely (see _Replaying_).
|
|
124
|
+
|
|
125
|
+
`snapshot` baselines live beside the **real** (symlink-resolved) **top-level** flow file in `<canonical-root-flow-directory>/__baselines__/<real-root-flow-filename-minus-.yaml>/` — directory _and_ key both come from the resolved file (for a saved flow that is a regular file, simply `.argent/flows/__baselines__/<flow>/`; for one that is a symlink, `.argent/flows/smoke.yaml` → `../vault/a-smoke.yaml` keys `../vault/__baselines__/a-smoke/`, so two projects symlinking their own `smoke.yaml` into one shared vault keep separate baselines — commit baselines beside the real file under the real file's name, and move any set already committed under `.argent/flows/__baselines__/<flow>/` there once) — snapshots inside composed fragments are keyed by and stored with the root flow, wherever the fragment file lives — keyed by platform + resolution; a `snapshot` step **fails** when no baseline exists for the run's device class, so seed baselines with `--update-baselines` and have the user review and commit `__baselines__/` — and pin the device class in CI (`--device`/`--platform`, same simulator model) so runs compare against the committed key. The status bar is pinned (iOS `simctl status_bar`, Android demo mode) for the run so it doesn't drive visual diffs. `--output <dir>` writes each failed snapshot's baseline/current/diff images to `<dir>/<flow>/` — a stable path for CI artifact upload. When two different flow files share a filename and one `--output` dir, the later export lands in `<dir>/<flow>-<pathhash>/` instead (deterministic per flow path, with a warning) so neither flow's evidence overwrites the other's.
|
|
117
126
|
|
|
118
127
|
## Tools
|
|
119
128
|
|
|
@@ -123,8 +132,8 @@ Since a `tv-remote` path is positional (like a coordinate tap), gate each naviga
|
|
|
123
132
|
| `flow-add-step` | Execute a tool call live and record it if it succeeds |
|
|
124
133
|
| `flow-add-echo` | Add a label/comment that prints during replay |
|
|
125
134
|
| `flow-finish-recording` | Stop recording and get a summary |
|
|
126
|
-
| `flow-read-prerequisite` | Read a flow's execution prerequisite without running it
|
|
127
|
-
| `flow-execute` | Replay a saved flow by
|
|
135
|
+
| `flow-read-prerequisite` | Read a flow's execution prerequisite without running it (same `name`/`flow_path` sources) |
|
|
136
|
+
| `flow-execute` | Replay a flow — a saved one by `name`, or any flow YAML by absolute `flow_path` |
|
|
128
137
|
|
|
129
138
|
Every tool during recording returns the current flow file contents, so you can track what has been recorded. Rules:
|
|
130
139
|
|
|
@@ -161,7 +170,7 @@ Record an `await-ui-element` step to **gate** the next step on a screen transiti
|
|
|
161
170
|
- `tool: gesture-pinch` → `pinch: { on: "<target>", scale: … }`, deriving `scale` as `endDistance / startDistance`. Set `on:` to the element under the pinch center when the pinch was aimed at one (the map or image being zoomed); omit it for a screen-center pinch. Don't carry the recorded distances/angle over — the directive re-derives the geometry (finger placement, system-edge avoidance, chaining of large scales) at run time, so the conversion swaps device-specific coordinates for a portable selector with auto-wait. Keep the raw `tool: gesture-pinch` step when the pinch is anchored at a specific point _inside_ a large element (zooming toward a particular map location, not the map's center) or deliberately pans via `endCenterX`/`endCenterY` — `on:` takes only a selector and re-centers the pinch on the element's frame center, so converting would silently move the zoom anchor.
|
|
162
171
|
- `tool: gesture-rotate` → `rotate: { on: "<target>", by: … }`, deriving `by` as `endAngle − startAngle` (the tool's `endAngle` > `startAngle` turns clockwise, matching the directive's positive `by`). Set `on:` to the element under the rotation center when the rotation was aimed at one (the map or image being rotated); omit it for a screen-center rotation. Don't carry the recorded `centerX`/`centerY`, radii (`radius` or `radiusX`/`radiusY`), `startAngle`, or `durationMs` over — the directive re-derives the geometry (finger placement, physical-circle radius, system-edge avoidance) and runs at a fixed pace (~90° per 300 ms), so the conversion swaps device-specific coordinates for a portable selector with auto-wait. Keep the raw `tool: gesture-rotate` step when the rotation is anchored at a specific point _inside_ a large element rather than its center (the directive re-centers on the element's frame center, so converting would silently move the pivot), when the gesture's speed itself matters (the directive's pace is fixed), or when the sweep exceeds the directive's ±3000° bound.
|
|
163
172
|
|
|
164
|
-
Every other recorded tool (a velocity-dependent `gesture-swipe`, a fixed-distance `gesture-scroll` not aimed at an element, `button`, `screenshot`, …) has no directive form — leave it as a `tool:` step. The recorder already handles the rest: coordinate `gesture-tap`s are captured as portable `tap:` selector steps, a `restart-app` is captured as a `launch:` step, a `flow-execute` of a sibling fragment is captured as a `run: <name
|
|
173
|
+
Every other recorded tool (a velocity-dependent `gesture-swipe`, a fixed-distance `gesture-scroll` not aimed at an element, `button`, `screenshot`, …) has no directive form — leave it as a `tool:` step. The recorder already handles the rest: coordinate `gesture-tap`s are captured as portable `tap:` selector steps, a `restart-app` is captured as a `launch:` step, a `flow-execute` of a sibling fragment is captured as a `run: <name>.yaml` composition directive, and device ids are stripped. Captured selectors are emitted in the strict map form (`tap: { text: General }`), never as a loose bare string — the recorder verified the exact element the tap hit, and a bare string would re-parse as loose and route through the identifier-first fallback it was never checked against. After editing, re-run with `flow-execute` to confirm the cleaned flow still passes.
|
|
165
174
|
|
|
166
175
|
### Example session
|
|
167
176
|
|
|
@@ -182,7 +191,7 @@ Then polish the saved file: the two `await-ui-element` steps become `await:` dir
|
|
|
182
191
|
|
|
183
192
|
## Replaying
|
|
184
193
|
|
|
185
|
-
Call `flow-execute` with
|
|
194
|
+
Call `flow-execute` with exactly one flow source: `name` for a flow saved under `.argent/flows/` (this form also works through a remote tool server), or `flow_path` — an absolute path to any flow `.yaml`. A flow's `run:` targets and `__baselines__/` resolve on the **tool server's** filesystem, beside the YAML it actually reads. `flow_path` requires the agent and the tool server to share a filesystem and is refused when they don't; `name` is what still runs then, but it is not a way to keep siblings and baselines — a remote call reaches the server as an upload of that one YAML into a fresh temp directory, so a `run:` target errors as a missing fragment and a `snapshot` step fails for a missing baseline under a temp path (and `updateBaselines` writes the baseline there, to be deleted with the directory). Remotely, replay self-contained flows; a flow that composes or snapshots needs the agent and the tool server on one filesystem. Pass `project_root` too — it is always required here; the stored-for-the-session shortcut applies only to the recording tools. If the flow has an execution prerequisite, the tool returns a **notice** with the prerequisite text instead of running — verify the prerequisite is met (you can also inspect it beforehand with `flow-read-prerequisite`, which takes the same `name`/`flow_path` pair) and call `flow-execute` again with `prerequisiteAcknowledged: true`. A flow without a prerequisite runs immediately. The run executes all steps in order and returns a structured report: `{ ok, passed, failed, skipped, errored, steps }`.
|
|
186
195
|
|
|
187
196
|
**What each step reports.** Raw `tool:` steps include the underlying tool's full `result` (screenshots and other outputs render as usual). The directive steps are summarized: `tap`/`type`/`await`/`assert` report only `status` + `reason`, and `snapshot` adds `artifacts` only when there is something to look at — a failed comparison (baseline/current/diff paths), a missing-baseline failure (`current` only), or a baseline write; a clean pass reports just `status` + `reason`. So converting a `tool: gesture-tap` into a `tap:` directive during cleanup drops only that tap's (uninteresting) raw result — output-bearing tools like `screenshot` have no directive form and stay `tool:` steps, so their results keep flowing through.
|
|
188
197
|
|
|
@@ -177,11 +177,21 @@ Special keys: `enter`, `escape`, `backspace`, `tab`, `space`, `arrow-up`, `arrow
|
|
|
177
177
|
{ "udid": "<UDID>", "text": "{{secret:APP_PASSWORD}}", "key": "enter" }
|
|
178
178
|
```
|
|
179
179
|
|
|
180
|
-
The placeholder is resolved on the machine running the tool-server from the
|
|
180
|
+
The placeholder is resolved on the machine running the tool-server, from the first of these that defines the name:
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
| # | Source | Which keys it exposes |
|
|
183
|
+
| --- | --------------------------------------------- | -------------------------------------------------------------------- |
|
|
184
|
+
| 1 | `ARGENT_SECRET_<NAME>` environment variable | prefixed vars only — the CI-native path |
|
|
185
|
+
| 2 | `<project>/.argent/secrets.env` | every key (`APP_PASSWORD=…`) — gitignore this file |
|
|
186
|
+
| 3 | `<project>/.env.local`, then `<project>/.env` | only `ARGENT_SECRET_`-prefixed keys, so app config stays unreachable |
|
|
187
|
+
| 4 | `~/.argent/secrets.env` | every key — per-user, works in any project |
|
|
188
|
+
|
|
189
|
+
Rules:
|
|
190
|
+
|
|
191
|
+
- The result echoes the placeholder, never the value. An unknown name fails with the list of available secret _names_ and every source it looked in, with paths — read that list before asking the user anything.
|
|
183
192
|
- The auto-screenshot after the call is skipped so the typed value cannot re-enter your context as pixels. Do **not** `describe` or `screenshot` a non-secure field you just filled with a secret — submit or navigate away first, then verify the resulting screen.
|
|
184
|
-
-
|
|
193
|
+
- Nothing outside those sources is reachable; never ask the user to paste a secret value into the conversation. Ask them to put it in a secrets file instead — a file edit applies to the next call, while an exported env var only reaches a tool-server started afterwards.
|
|
194
|
+
- The project sources are found by walking up from the tool-server's working directory. If a project file is not being picked up, the failure's source list shows the paths actually consulted; `~/.argent/secrets.env` needs no project and always applies.
|
|
185
195
|
|
|
186
196
|
### rotate — Change orientation
|
|
187
197
|
|
|
@@ -65,7 +65,7 @@ Steps:
|
|
|
65
65
|
7. screenshot → verify home screen appeared
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
> **Credentials:** never type plaintext credentials — use a `{{secret:<NAME>}}` placeholder in `keyboard`, resolved server-side from the `ARGENT_SECRET_<NAME>` environment variable,
|
|
68
|
+
> **Credentials:** never type plaintext credentials — use a `{{secret:<NAME>}}` placeholder in `keyboard`, resolved server-side so the value never enters agent context. It comes from the `ARGENT_SECRET_<NAME>` environment variable or an argent secrets file (`.argent/secrets.env` in the project, `~/.argent/secrets.env`, or an `ARGENT_SECRET_`-prefixed key in the project's `.env` / `.env.local`). If the name is not defined, the failure lists the available names and every path it checked — ask the user to add it to one of those files (which applies immediately) instead of pasting the secret into the conversation. Never invent credentials or echo secret values into reports or saved files.
|
|
69
69
|
|
|
70
70
|
### Scroll and navigation
|
|
71
71
|
|