@swmansion/argent 0.24.1-next.8 → 0.25.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 +16 -191
- 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 +34 -14
- package/dist/installer.mjs +34 -14
- package/dist/tool-server.cjs +133 -118
- package/dylibs/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/libKeyboardPatch.dylib +0 -0
- package/dylibs/libNativeDevtoolsIos.dylib +0 -0
- package/dylibs/tcp/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/tcp/libKeyboardPatch.dylib +0 -0
- package/dylibs/tcp/libNativeDevtoolsIos.dylib +0 -0
- package/dylibs/tvos/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/tvos/libKeyboardPatch.dylib +0 -0
- package/dylibs/tvos/libNativeDevtoolsIos.dylib +0 -0
- package/package.json +1 -1
- package/skills/argent-create-flow/references/flow-yaml.md +1 -1
- package/skills/argent-device-interact/SKILL.md +18 -44
- package/skills/argent-device-interact/references/chromium.md +24 -0
- package/skills/argent-device-interact/references/secrets.md +21 -0
package/README.md
CHANGED
|
@@ -1,215 +1,40 @@
|
|
|
1
1
|
<br/>
|
|
2
|
-
<p align="center">
|
|
3
|
-
<a href="https://argent.swmansion.com">
|
|
4
|
-
<img width="1100" height="382" alt="argent-header" src="https://github.com/user-attachments/assets/6cec01d5-da3c-4b6c-97c3-0374a63c213c" />
|
|
5
|
-
</a>
|
|
6
|
-
</p>
|
|
2
|
+
<p align="center"><a href="https://argent.swmansion.com"><img width="1100" height="382" alt="argent-header" src="https://github.com/user-attachments/assets/6cec01d5-da3c-4b6c-97c3-0374a63c213c" /></a></p>
|
|
7
3
|
|
|
8
4
|
[](https://swm-delivery.com/www/delivery/ck-slug.php?zoneid=zone-gh-argent-1&n=1)
|
|
9
5
|
[](https://swm-delivery.com/www/delivery/ck-slug.php?zoneid=zone-gh-argent-2&n=1)
|
|
10
6
|
[](https://swm-delivery.com/www/delivery/ck-slug.php?zoneid=zone-gh-argent-3&n=1)
|
|
11
7
|
|
|
12
|
-
**[Argent](https://argent.swmansion.com)**
|
|
8
|
+
**[Argent](https://argent.swmansion.com)** gives your AI assistant direct control of iOS simulators, Android emulators and devices, TVs (Apple TV, Android TV, Fire TV) and Electron/Chromium apps. Tap a button, profile a screen, reproduce a bug - without leaving the CLI.
|
|
13
9
|
|
|
14
10
|
```bash
|
|
15
|
-
npx @swmansion/argent@latest init
|
|
16
|
-
# or, in a pnpm project (where npm's devEngines check may refuse to run npx):
|
|
17
|
-
pnpm dlx @swmansion/argent@latest init
|
|
11
|
+
npx @swmansion/argent@latest init # pnpm: pnpm dlx @swmansion/argent@latest init
|
|
18
12
|
```
|
|
19
13
|
|
|
20
|
-
|
|
14
|
+
<p align="center"><img src="https://github.com/software-mansion/argent/blob/main/assets/showcase.gif" alt="argent showcase video gif" width="100%" /></p>
|
|
21
15
|
|
|
22
|
-
|
|
16
|
+
## What it does
|
|
23
17
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
- **Interact** - tap, swipe, gesture, type, hardware buttons; D-pad on TV; mouse and keyboard on desktop.
|
|
19
|
+
- **Flows** - record a path once, replay it deterministically as a repro or smoke test.
|
|
20
|
+
- **Visual regression** - diff a baseline against a live capture, OCR- and font-aware.
|
|
21
|
+
- **Profiling** - Hermes, React DevTools, Xcode Instruments, Perfetto: renders, CPU hotspots, hangs.
|
|
22
|
+
- **Debugging** - logs, network (`fetch` and native), JS evaluation, native and React trees.
|
|
23
|
+
- **React Native** - build, launch and iterate, no extra setup.
|
|
30
24
|
|
|
31
|
-
|
|
25
|
+
Ask your assistant _"What can Argent do?"_ to list every tool.
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
- **UI interaction** - Give your agent the full control toolkit - tapping, swiping, pinching, typing, gestures and hardware buttons on mobile; the directional remote on TV; mouse, scroll and drag on desktop/web. Let it navigate your app exactly as a user would, without lifting a finger.
|
|
35
|
-
- **Record & replay flows** - Capture a sequence of interactions once and let your agent replay it deterministically, so manual repros and smoke tests become repeatable.
|
|
36
|
-
- **Visual regression** - Diff two screenshots (or a saved baseline against a live capture) with OCR- and font-aware comparison to catch unintended UI changes.
|
|
37
|
-
- **Profiling with batteries included** - Argent can perform and analyze React Native (Hermes), React DevTools and native (Xcode Instruments / Android Perfetto) profiling sessions - down to fiber renders, CPU hotspots and cross-correlated commit-vs-hang reports. Get comprehensive summaries and ask to optimise your app where you find fit.
|
|
38
|
-
- **Debugging and diagnostics** - Let your agent inspect logs, capture network traffic (JS `fetch` and native), evaluate JS in the running app, walk the native UIKit and React component trees, and reproduce failing states - so you can jump straight to the fix.
|
|
39
|
-
- **Desktop & web control** - For Electron and Chromium apps your agent can drive tabs, read and write cookies and storage, walk the DOM and inspect network over the Chrome DevTools Protocol.
|
|
40
|
-
- **React Native out of the box** - Argent works with React Native apps natively, so your agent can build, launch, and iterate on your RN project the same way it would any native app - no extra setup required.
|
|
27
|
+
## Documentation
|
|
41
28
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<br/>
|
|
45
|
-
<p align="center">
|
|
46
|
-
<img src="https://github.com/software-mansion/argent/blob/main/assets/showcase.gif" alt="argent showcase video gif" width="100%" />
|
|
47
|
-
</p>
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## Installation
|
|
52
|
-
|
|
53
|
-
#### Prerequisites
|
|
54
|
-
|
|
55
|
-
- **Node.js 20.12** or later
|
|
56
|
-
- For iOS / tvOS: macOS with **Xcode** installed (Apple TV uses tvOS simulators — Xcode downloads the tvOS runtime on demand)
|
|
57
|
-
- For Android / Android TV: **Android SDK Platform Tools** (`adb`) on `PATH`, and the **Android Emulator** package if you want to boot AVDs from Argent. Create AVDs via Android Studio or `avdmanager`.
|
|
58
|
-
- For Fire TV (Vega): the **Vega SDK** (`vega` CLI) on `PATH`
|
|
59
|
-
- For Electron / Chromium: nothing extra to control an already-running app - just launch it with `--remote-debugging-port`, or let Argent spawn your Electron app for you
|
|
60
|
-
|
|
61
|
-
##### Linux host: extra prerequisites for Android emulators
|
|
62
|
-
|
|
63
|
-
Argent runs Android emulators on Linux but the default install can be slow if a few host-side knobs aren't right. Cover these once and the experience matches macOS:
|
|
64
|
-
|
|
65
|
-
- **KVM access.** The emulator falls back to slow software emulation (TCG) without `/dev/kvm`. Make sure virtualization is enabled in BIOS/UEFI (`vmx` for Intel, `svm` for AMD in `/proc/cpuinfo`) and that your user can read/write `/dev/kvm` — on most distros that means joining the `kvm` group:
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
sudo usermod -aG kvm "$USER"
|
|
69
|
-
# log out and back in so the new group takes effect
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
- **GPU mode (`-gpu swiftshader` on Linux, override available).** The Android emulator's Linux GPU story is messy: `-gpu auto` frequently resolves to lavapipe (slow software Vulkan via host libvulkan, ~10× cold-boot regression on flagship hardware), and `-gpu host` silently produces a corrupted or black emulator window on hosts with non-trivial GL stacks — dual-GPU / Optimus laptops, NVIDIA + Mesa coexistence via libglvnd, Wayland sessions on hybrid graphics, headless / containerized hosts. The failure mode is invisible to argent's framebuffer-based screenshot tool, so an agent reports success while the developer sees a black window.
|
|
73
|
-
|
|
74
|
-
Argent picks `-gpu swiftshader` on Linux for universal compatibility: it sidesteps the host GL stack entirely and renders via the emulator's bundled SwiftShader. On modern multi-core machines this is indistinguishably smooth from hardware-accelerated `-gpu host` (and far faster than lavapipe).
|
|
75
|
-
|
|
76
|
-
Override with the `ARGENT_EMULATOR_GPU_MODE` env var if you've verified `-gpu host` works on your machine (typical single-GPU Mesa box with a healthy X session):
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
ARGENT_EMULATOR_GPU_MODE=host argent ...
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Argent's boot-device preflight prints a warning if `/dev/kvm` isn't usable — the condition that causes a 10–50× TCG-vs-KVM slowdown.
|
|
83
|
-
|
|
84
|
-
- **System image.** Prefer the `default` or `google_apis` variants of `x86_64` system images for headless agent workflows; `google_apis_playstore` adds noticeable boot-time CPU churn from Play services. Always pick `x86_64` on Intel/AMD hosts — ARM images run via QEMU translation and are dramatically slower.
|
|
85
|
-
|
|
86
|
-
- **AVD config.** AVDs created via `avdmanager create avd` default to `hw.gpu.enabled=no`. Argent overrides this with an explicit `-gpu` arg at launch (so the on-disk config doesn't need editing). For the smoothest experience under heavy native builds (gradle compilations alongside the AVD), bump the AVD's RAM and CPU count — edit `~/.android/avd/<name>.avd/config.ini`:
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
hw.ramSize = 8192
|
|
90
|
-
hw.cpu.ncore = 6
|
|
91
|
-
vm.heapSize = 512
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Stock 2 GB / 4 vCPU AVDs can be CPU-starved into wedged-system_server states by a concurrent gradle/Kotlin compile.
|
|
95
|
-
|
|
96
|
-
- **Headless / CI mode (`ARGENT_EMULATOR_NO_WINDOW=1`).** Argent shows the emulator window by default so a local developer can see the AVD UI. In a headless context — CI runner, container, or a Wayland-only session where the emulator's bundled Qt has no `wayland` platform plugin and SIGABRTs on the crash-consent dialog — opt out by exporting `ARGENT_EMULATOR_NO_WINDOW=1` before starting the tool-server. This appends `-no-window` to the spawn args, selecting `qemu-system-x86_64-headless` which doesn't need a Qt window. Argent's screencap-based screenshot tool reads the in-memory framebuffer correctly without a visible window.
|
|
97
|
-
|
|
98
|
-
#### Run `init` in your project
|
|
99
|
-
|
|
100
|
-
From your project root:
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
npx @swmansion/argent@latest init
|
|
104
|
-
# or, in a pnpm project (where npm's devEngines check may refuse to run npx):
|
|
105
|
-
pnpm dlx @swmansion/argent@latest init
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
This command triggers an installation wizard which:
|
|
109
|
-
|
|
110
|
-
- Installs `@swmansion/argent` globally
|
|
111
|
-
- Detects your editor and registers the MCP server
|
|
112
|
-
- Copies skills, rules, and agent definitions into your workspace
|
|
113
|
-
|
|
114
|
-
#### Prefer a manual install?
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
npm install -g @swmansion/argent
|
|
118
|
-
argent init
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
#### Share Argent with your team (committable install)
|
|
122
|
-
|
|
123
|
-
By default Argent installs **globally**. To version Argent _with your repo_ so every
|
|
124
|
-
teammate gets the same setup on `npm install` — no per-developer global install, no
|
|
125
|
-
`argent init` — choose the local mode:
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
npx @swmansion/argent@latest init --local
|
|
129
|
-
# or, in a pnpm project:
|
|
130
|
-
pnpm dlx @swmansion/argent@latest init --local
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
> Note: in a freshly `pnpm init`-ed project, `npx` itself may refuse to run
|
|
134
|
-
> (npm's `devEngines` check) — use the `pnpm dlx` form there.
|
|
135
|
-
|
|
136
|
-
This adds `@swmansion/argent` to your project's `devDependencies` and writes MCP
|
|
137
|
-
configs that launch the project-local copy (`node node_modules/@swmansion/argent/dist/cli.js mcp`).
|
|
138
|
-
Commit `package.json` + your lockfile, the generated MCP config (`.mcp.json`,
|
|
139
|
-
`.cursor/mcp.json`, …), `.argent/install.json`, and the skills/rules/agents files.
|
|
140
|
-
Teammates then just run `npm install`.
|
|
141
|
-
|
|
142
|
-
Pass `--global` to force the default mode in scripts; `--local` and `--global` are
|
|
143
|
-
mutually exclusive. A non-interactive (`--yes`) run defaults to global unless the
|
|
144
|
-
project already opted into local mode (a committed `.argent/install.json`, or
|
|
145
|
-
`@swmansion/argent` declared in the project's own `package.json`).
|
|
146
|
-
|
|
147
|
-
> In local mode the committed MCP config runs the project-local copy, so the bare
|
|
148
|
-
> `argent` command is **not** on teammates' `PATH`. Note that `npm install` builds
|
|
149
|
-
> Argent's native deps (`tree-sitter`) on each machine — prebuilt for macOS, Linux
|
|
150
|
-
> x64, and Windows x64; other targets (Linux arm64, Windows arm) compile from source
|
|
151
|
-
> and need a C/C++ toolchain.
|
|
152
|
-
|
|
153
|
-
## CLI Reference
|
|
154
|
-
|
|
155
|
-
| Command | Description |
|
|
156
|
-
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
157
|
-
| `argent init` | Install and configure MCP in the current workspace (`--global` default, `--local` for a committable devDependency) |
|
|
158
|
-
| `argent install` | Alias for `init` command |
|
|
159
|
-
| `argent update` | Pull the latest version and refresh workspace configuration (acts on the present install — both when a global install and a project devDependency coexist; `--global`/`--local` select explicitly) |
|
|
160
|
-
| `argent uninstall` | Unregister the MCP server and uninstall the package (`--global`/`--local` choose which install — and its configs — is removed; non-interactive runs never remove a coexisting global install) |
|
|
161
|
-
| `argent remove` | Alias for `uninstall` command |
|
|
162
|
-
| `argent mcp` | Start MCP server instance, used internally by agent |
|
|
163
|
-
| `argent tools` | List tools exposed by the tool-server (`describe <name>` for details) |
|
|
164
|
-
| `argent run` | Invoke a tool by name |
|
|
165
|
-
| `argent server` | Manage the shared tool-server: `start` / `status` / `stop` / `logs` |
|
|
166
|
-
| `argent lens` | Open Argent Lens bound to a fresh coding-agent session — Claude by default, `--agent` selects codex/gemini/opencode/cursor (macOS; behind the `argent-lens` flag — run `argent enable argent-lens` first) |
|
|
167
|
-
| `argent link` | Route client requests to a remote tool-server |
|
|
168
|
-
| `argent unlink` | Remove the persisted remote tool-server link |
|
|
169
|
-
| `argent enable` | Enable a predefined feature flag (`--scope project` for project-local) |
|
|
170
|
-
| `argent disable` | Disable a feature flag (`--scope project` for project-local) |
|
|
171
|
-
| `argent flags` | List available feature flags and their state |
|
|
172
|
-
| `argent providers` | Inspect and manage external device providers: `list` / `check` / `publish` / `withdraw` / `prune` |
|
|
173
|
-
| `argent telemetry` | Manage telemetry: `status` / `enable` / `disable` |
|
|
174
|
-
|
|
175
|
-
## Supported Editors
|
|
176
|
-
|
|
177
|
-
`argent init` auto-detects and configures MCP for:
|
|
178
|
-
|
|
179
|
-
| Editor | Config location |
|
|
180
|
-
| ----------- | --------------------------------------------------------------------------- |
|
|
181
|
-
| Claude Code | `.mcp.json` (project) or `~/.claude.json` (global) |
|
|
182
|
-
| Cursor | `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global) |
|
|
183
|
-
| VS Code | `.vscode/mcp.json` |
|
|
184
|
-
| Windsurf | `~/.codeium/windsurf/mcp_config.json` (global) |
|
|
185
|
-
| Zed | `.zed/settings.json` (project) or `~/.config/zed/settings.json` (global) |
|
|
186
|
-
| Gemini CLI | `.gemini/settings.json` |
|
|
187
|
-
| Codex CLI | `.codex/config.toml` (project) or `~/.codex/config.toml` (global) |
|
|
188
|
-
| Hermes | `~/.hermes/config.yaml` (global) |
|
|
189
|
-
| opencode | `opencode.json` (project) or `~/.config/opencode/opencode.json` (global) |
|
|
190
|
-
| Kiro | `.kiro/settings/mcp.json` (project) or `~/.kiro/settings/mcp.json` (global) |
|
|
29
|
+
[Installation](https://docs.swmansion.com/argent/docs/fundamentals/installation) · [Platforms](https://docs.swmansion.com/argent/docs/fundamentals/supported-platforms) · [CLI](https://docs.swmansion.com/argent/docs/reference/cli) · [Editors](https://docs.swmansion.com/argent/docs/reference/editors)
|
|
191
30
|
|
|
192
31
|
## Privacy
|
|
193
32
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
You can opt out at any time:
|
|
197
|
-
|
|
198
|
-
```bash
|
|
199
|
-
argent telemetry disable # check status with: argent telemetry status
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
For the full details — see the [Argent Privacy Notice (Telemetry)](https://github.com/software-mansion/argent/blob/main/Telemetry.md).
|
|
33
|
+
Telemetry is opt-out: `argent telemetry disable`. See the [Privacy Notice](https://github.com/software-mansion/argent/blob/main/Telemetry.md).
|
|
203
34
|
|
|
204
35
|
## License
|
|
205
36
|
|
|
206
|
-
Argent uses a mixed licensing model.
|
|
207
|
-
|
|
208
|
-
**Source code** is released under the [Apache License 2.0](LICENSE.txt).
|
|
209
|
-
|
|
210
|
-
**Proprietary binaries** (the per-platform `bin/<platform>/simulator-server` and `bin/darwin/ax-service` executables and the `.dylib` files in `native-devtools-ios`) are the intellectual property of Software Mansion S.A. and are licensed solely for use within this project. Decompiling, reverse-engineering, or redistributing them without explicit written permission is prohibited.
|
|
211
|
-
|
|
212
|
-
By using Argent, you acknowledge and agree to this structure. See [LICENSE](https://github.com/software-mansion/argent/blob/main/LICENSE.txt) for full details.
|
|
37
|
+
Argent uses a mixed licensing model. **Source code** is released under the [Apache License 2.0](LICENSE.txt). **Proprietary binaries** (the per-platform `bin/<platform>/simulator-server` and `bin/darwin/ax-service` executables and the `.dylib` files in `native-devtools-ios`) are the intellectual property of Software Mansion S.A. and are licensed solely for use within this project. Decompiling, reverse-engineering, or redistributing them without explicit written permission is prohibited. By using Argent, you acknowledge and agree to this structure. See [LICENSE](https://github.com/software-mansion/argent/blob/main/LICENSE.txt) for full details.
|
|
213
38
|
|
|
214
39
|
## Argent is created by Software Mansion
|
|
215
40
|
|
|
Binary file
|
package/bin/darwin/ax-service
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/tcp/ax-service
CHANGED
|
Binary file
|
|
Binary file
|
package/dist/cli-cmds.mjs
CHANGED
|
@@ -20976,31 +20976,51 @@ function toAttributes2(record2) {
|
|
|
20976
20976
|
}
|
|
20977
20977
|
return attributes;
|
|
20978
20978
|
}
|
|
20979
|
-
var
|
|
20979
|
+
var CLEARED_OTLP_ENV_VARS = [
|
|
20980
20980
|
"OTEL_EXPORTER_OTLP_HEADERS",
|
|
20981
|
-
"OTEL_EXPORTER_OTLP_LOGS_HEADERS"
|
|
20981
|
+
"OTEL_EXPORTER_OTLP_LOGS_HEADERS",
|
|
20982
|
+
"OTEL_EXPORTER_OTLP_CERTIFICATE",
|
|
20983
|
+
"OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE",
|
|
20984
|
+
"OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE",
|
|
20985
|
+
"OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE",
|
|
20986
|
+
"OTEL_EXPORTER_OTLP_CLIENT_KEY",
|
|
20987
|
+
"OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY"
|
|
20982
20988
|
];
|
|
20989
|
+
var NO_COMPRESSION = "none";
|
|
20983
20990
|
function createExporter(config3) {
|
|
20984
|
-
const saved =
|
|
20985
|
-
for (const name of
|
|
20991
|
+
const saved = CLEARED_OTLP_ENV_VARS.map((name) => [name, process.env[name]]);
|
|
20992
|
+
for (const name of CLEARED_OTLP_ENV_VARS) delete process.env[name];
|
|
20986
20993
|
try {
|
|
20987
20994
|
return new OTLPLogExporter({
|
|
20988
20995
|
url: config3.endpoint,
|
|
20989
20996
|
headers: { authorization: `Bearer ${config3.token}` },
|
|
20990
20997
|
timeoutMillis: EXPORT_TIMEOUT_MS,
|
|
20998
|
+
// Left unset, the SDK takes this from OTEL_EXPORTER_OTLP_COMPRESSION (or
|
|
20999
|
+
// its _LOGS_ variant), so a machine that already runs OpenTelemetry would
|
|
21000
|
+
// gzip argent's batches - a request shaped differently from the one the
|
|
21001
|
+
// ingestion side is sized and tested against, decided by a variable set
|
|
21002
|
+
// for something else. Unlike the variables cleared above, one explicit
|
|
21003
|
+
// value settles it: the SDK takes user-provided over env here rather than
|
|
21004
|
+
// merging the two or reading anything to decide.
|
|
21005
|
+
compression: NO_COMPRESSION,
|
|
20991
21006
|
// The exporter bounds a request with `req.setTimeout()`, which Node only
|
|
20992
21007
|
// arms once the socket is CONNECTED — so a collector whose address drops
|
|
20993
21008
|
// packets (corporate egress filter, dead host behind a firewall) leaves a
|
|
20994
|
-
// socket stuck connecting that no export deadline can reach,
|
|
20995
|
-
// process open for the OS connect timeout
|
|
20996
|
-
// The agent's socket timeout is
|
|
20997
|
-
//
|
|
20998
|
-
// exporter's handler destroys it.
|
|
21009
|
+
// socket stuck in the connecting state that no export deadline can reach,
|
|
21010
|
+
// holding the process open for the OS connect timeout (~75s on macOS)
|
|
21011
|
+
// after shutdown() has already resolved. The agent's socket timeout is
|
|
21012
|
+
// armed when the socket is CREATED, so it also covers connect: it fires,
|
|
21013
|
+
// the request emits 'timeout', and the exporter's handler destroys it.
|
|
20999
21014
|
//
|
|
21000
|
-
// keepAlive is restated
|
|
21001
|
-
//
|
|
21002
|
-
// keepAlive:
|
|
21003
|
-
//
|
|
21015
|
+
// keepAlive is restated to match the agent the SDK would otherwise build,
|
|
21016
|
+
// whose default is keepAlive: true — supplying httpAgentOptions at all
|
|
21017
|
+
// replaces it, and keepAlive: false would open and close a socket per
|
|
21018
|
+
// request. It does NOT save the handshake across the 10s batch cadence:
|
|
21019
|
+
// this same timeout reaps an idle keep-alive socket, so one parked in the
|
|
21020
|
+
// pool between batches is destroyed at EXPORT_TIMEOUT_MS, long before the
|
|
21021
|
+
// next batch reaches for it. Reuse survives only for batches flushed
|
|
21022
|
+
// within the timeout of each other, e.g. a force-flush of a queue that
|
|
21023
|
+
// spans more than one batch.
|
|
21004
21024
|
httpAgentOptions: { timeout: EXPORT_TIMEOUT_MS, keepAlive: true }
|
|
21005
21025
|
});
|
|
21006
21026
|
} finally {
|
|
@@ -21837,7 +21857,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
21837
21857
|
var SESSION_ID2 = randomUUID5();
|
|
21838
21858
|
function readCliVersion() {
|
|
21839
21859
|
if (true) {
|
|
21840
|
-
return "0.
|
|
21860
|
+
return "0.25.0";
|
|
21841
21861
|
}
|
|
21842
21862
|
return "0.0.0";
|
|
21843
21863
|
}
|
package/dist/installer.mjs
CHANGED
|
@@ -15468,31 +15468,51 @@ function toAttributes2(record) {
|
|
|
15468
15468
|
}
|
|
15469
15469
|
return attributes;
|
|
15470
15470
|
}
|
|
15471
|
-
var
|
|
15471
|
+
var CLEARED_OTLP_ENV_VARS = [
|
|
15472
15472
|
"OTEL_EXPORTER_OTLP_HEADERS",
|
|
15473
|
-
"OTEL_EXPORTER_OTLP_LOGS_HEADERS"
|
|
15473
|
+
"OTEL_EXPORTER_OTLP_LOGS_HEADERS",
|
|
15474
|
+
"OTEL_EXPORTER_OTLP_CERTIFICATE",
|
|
15475
|
+
"OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE",
|
|
15476
|
+
"OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE",
|
|
15477
|
+
"OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE",
|
|
15478
|
+
"OTEL_EXPORTER_OTLP_CLIENT_KEY",
|
|
15479
|
+
"OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY"
|
|
15474
15480
|
];
|
|
15481
|
+
var NO_COMPRESSION = "none";
|
|
15475
15482
|
function createExporter(config) {
|
|
15476
|
-
const saved =
|
|
15477
|
-
for (const name of
|
|
15483
|
+
const saved = CLEARED_OTLP_ENV_VARS.map((name) => [name, process.env[name]]);
|
|
15484
|
+
for (const name of CLEARED_OTLP_ENV_VARS) delete process.env[name];
|
|
15478
15485
|
try {
|
|
15479
15486
|
return new OTLPLogExporter({
|
|
15480
15487
|
url: config.endpoint,
|
|
15481
15488
|
headers: { authorization: `Bearer ${config.token}` },
|
|
15482
15489
|
timeoutMillis: EXPORT_TIMEOUT_MS,
|
|
15490
|
+
// Left unset, the SDK takes this from OTEL_EXPORTER_OTLP_COMPRESSION (or
|
|
15491
|
+
// its _LOGS_ variant), so a machine that already runs OpenTelemetry would
|
|
15492
|
+
// gzip argent's batches - a request shaped differently from the one the
|
|
15493
|
+
// ingestion side is sized and tested against, decided by a variable set
|
|
15494
|
+
// for something else. Unlike the variables cleared above, one explicit
|
|
15495
|
+
// value settles it: the SDK takes user-provided over env here rather than
|
|
15496
|
+
// merging the two or reading anything to decide.
|
|
15497
|
+
compression: NO_COMPRESSION,
|
|
15483
15498
|
// The exporter bounds a request with `req.setTimeout()`, which Node only
|
|
15484
15499
|
// arms once the socket is CONNECTED — so a collector whose address drops
|
|
15485
15500
|
// packets (corporate egress filter, dead host behind a firewall) leaves a
|
|
15486
|
-
// socket stuck connecting that no export deadline can reach,
|
|
15487
|
-
// process open for the OS connect timeout
|
|
15488
|
-
// The agent's socket timeout is
|
|
15489
|
-
//
|
|
15490
|
-
// exporter's handler destroys it.
|
|
15501
|
+
// socket stuck in the connecting state that no export deadline can reach,
|
|
15502
|
+
// holding the process open for the OS connect timeout (~75s on macOS)
|
|
15503
|
+
// after shutdown() has already resolved. The agent's socket timeout is
|
|
15504
|
+
// armed when the socket is CREATED, so it also covers connect: it fires,
|
|
15505
|
+
// the request emits 'timeout', and the exporter's handler destroys it.
|
|
15491
15506
|
//
|
|
15492
|
-
// keepAlive is restated
|
|
15493
|
-
//
|
|
15494
|
-
// keepAlive:
|
|
15495
|
-
//
|
|
15507
|
+
// keepAlive is restated to match the agent the SDK would otherwise build,
|
|
15508
|
+
// whose default is keepAlive: true — supplying httpAgentOptions at all
|
|
15509
|
+
// replaces it, and keepAlive: false would open and close a socket per
|
|
15510
|
+
// request. It does NOT save the handshake across the 10s batch cadence:
|
|
15511
|
+
// this same timeout reaps an idle keep-alive socket, so one parked in the
|
|
15512
|
+
// pool between batches is destroyed at EXPORT_TIMEOUT_MS, long before the
|
|
15513
|
+
// next batch reaches for it. Reuse survives only for batches flushed
|
|
15514
|
+
// within the timeout of each other, e.g. a force-flush of a queue that
|
|
15515
|
+
// spans more than one batch.
|
|
15496
15516
|
httpAgentOptions: { timeout: EXPORT_TIMEOUT_MS, keepAlive: true }
|
|
15497
15517
|
});
|
|
15498
15518
|
} finally {
|
|
@@ -16690,7 +16710,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
16690
16710
|
var SESSION_ID = randomUUID4();
|
|
16691
16711
|
function readCliVersion() {
|
|
16692
16712
|
if (true) {
|
|
16693
|
-
return "0.
|
|
16713
|
+
return "0.25.0";
|
|
16694
16714
|
}
|
|
16695
16715
|
return "0.0.0";
|
|
16696
16716
|
}
|
package/dist/tool-server.cjs
CHANGED
|
@@ -93813,31 +93813,51 @@ function toAttributes2(record2) {
|
|
|
93813
93813
|
}
|
|
93814
93814
|
return attributes;
|
|
93815
93815
|
}
|
|
93816
|
-
var
|
|
93816
|
+
var CLEARED_OTLP_ENV_VARS = [
|
|
93817
93817
|
"OTEL_EXPORTER_OTLP_HEADERS",
|
|
93818
|
-
"OTEL_EXPORTER_OTLP_LOGS_HEADERS"
|
|
93818
|
+
"OTEL_EXPORTER_OTLP_LOGS_HEADERS",
|
|
93819
|
+
"OTEL_EXPORTER_OTLP_CERTIFICATE",
|
|
93820
|
+
"OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE",
|
|
93821
|
+
"OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE",
|
|
93822
|
+
"OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE",
|
|
93823
|
+
"OTEL_EXPORTER_OTLP_CLIENT_KEY",
|
|
93824
|
+
"OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY"
|
|
93819
93825
|
];
|
|
93826
|
+
var NO_COMPRESSION = "none";
|
|
93820
93827
|
function createExporter(config2) {
|
|
93821
|
-
const saved =
|
|
93822
|
-
for (const name of
|
|
93828
|
+
const saved = CLEARED_OTLP_ENV_VARS.map((name) => [name, process.env[name]]);
|
|
93829
|
+
for (const name of CLEARED_OTLP_ENV_VARS) delete process.env[name];
|
|
93823
93830
|
try {
|
|
93824
93831
|
return new OTLPLogExporter({
|
|
93825
93832
|
url: config2.endpoint,
|
|
93826
93833
|
headers: { authorization: `Bearer ${config2.token}` },
|
|
93827
93834
|
timeoutMillis: EXPORT_TIMEOUT_MS,
|
|
93835
|
+
// Left unset, the SDK takes this from OTEL_EXPORTER_OTLP_COMPRESSION (or
|
|
93836
|
+
// its _LOGS_ variant), so a machine that already runs OpenTelemetry would
|
|
93837
|
+
// gzip argent's batches - a request shaped differently from the one the
|
|
93838
|
+
// ingestion side is sized and tested against, decided by a variable set
|
|
93839
|
+
// for something else. Unlike the variables cleared above, one explicit
|
|
93840
|
+
// value settles it: the SDK takes user-provided over env here rather than
|
|
93841
|
+
// merging the two or reading anything to decide.
|
|
93842
|
+
compression: NO_COMPRESSION,
|
|
93828
93843
|
// The exporter bounds a request with `req.setTimeout()`, which Node only
|
|
93829
93844
|
// arms once the socket is CONNECTED — so a collector whose address drops
|
|
93830
93845
|
// packets (corporate egress filter, dead host behind a firewall) leaves a
|
|
93831
|
-
// socket stuck connecting that no export deadline can reach,
|
|
93832
|
-
// process open for the OS connect timeout
|
|
93833
|
-
// The agent's socket timeout is
|
|
93834
|
-
//
|
|
93835
|
-
// exporter's handler destroys it.
|
|
93846
|
+
// socket stuck in the connecting state that no export deadline can reach,
|
|
93847
|
+
// holding the process open for the OS connect timeout (~75s on macOS)
|
|
93848
|
+
// after shutdown() has already resolved. The agent's socket timeout is
|
|
93849
|
+
// armed when the socket is CREATED, so it also covers connect: it fires,
|
|
93850
|
+
// the request emits 'timeout', and the exporter's handler destroys it.
|
|
93836
93851
|
//
|
|
93837
|
-
// keepAlive is restated
|
|
93838
|
-
//
|
|
93839
|
-
// keepAlive:
|
|
93840
|
-
//
|
|
93852
|
+
// keepAlive is restated to match the agent the SDK would otherwise build,
|
|
93853
|
+
// whose default is keepAlive: true — supplying httpAgentOptions at all
|
|
93854
|
+
// replaces it, and keepAlive: false would open and close a socket per
|
|
93855
|
+
// request. It does NOT save the handshake across the 10s batch cadence:
|
|
93856
|
+
// this same timeout reaps an idle keep-alive socket, so one parked in the
|
|
93857
|
+
// pool between batches is destroyed at EXPORT_TIMEOUT_MS, long before the
|
|
93858
|
+
// next batch reaches for it. Reuse survives only for batches flushed
|
|
93859
|
+
// within the timeout of each other, e.g. a force-flush of a queue that
|
|
93860
|
+
// spans more than one batch.
|
|
93841
93861
|
httpAgentOptions: { timeout: EXPORT_TIMEOUT_MS, keepAlive: true }
|
|
93842
93862
|
});
|
|
93843
93863
|
} finally {
|
|
@@ -94682,7 +94702,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
94682
94702
|
var SESSION_ID = (0, import_node_crypto3.randomUUID)();
|
|
94683
94703
|
function readCliVersion() {
|
|
94684
94704
|
if (true) {
|
|
94685
|
-
return "0.
|
|
94705
|
+
return "0.25.0";
|
|
94686
94706
|
}
|
|
94687
94707
|
return "0.0.0";
|
|
94688
94708
|
}
|
|
@@ -96039,7 +96059,7 @@ var import_node_path5 = __toESM(require("node:path"));
|
|
|
96039
96059
|
var import_semver2 = __toESM(require_semver2());
|
|
96040
96060
|
|
|
96041
96061
|
// ../tool-server/package.json
|
|
96042
|
-
var version2 = "0.
|
|
96062
|
+
var version2 = "0.25.0";
|
|
96043
96063
|
|
|
96044
96064
|
// ../tool-server/src/utils/update-checker.ts
|
|
96045
96065
|
var import_update_core = __toESM(require_dist4());
|
|
@@ -112176,17 +112196,27 @@ var SCROLL_CLASSES = /* @__PURE__ */ new Set([
|
|
|
112176
112196
|
"androidx.recyclerview.widget.RecyclerView",
|
|
112177
112197
|
"android.widget.ListView"
|
|
112178
112198
|
]);
|
|
112179
|
-
function isUiAutomatorScrollable(attrs) {
|
|
112199
|
+
function isUiAutomatorScrollable(attrs, inWebView = false) {
|
|
112200
|
+
if (inWebView) return attrIsTrue(attrs, "scrollable");
|
|
112180
112201
|
return SCROLL_CLASSES.has(attrs.class ?? "") || attrIsTrue(attrs, "scrollable");
|
|
112181
112202
|
}
|
|
112182
112203
|
var WEBVIEW_CLASSES = /* @__PURE__ */ new Set(["android.webkit.WebView", "android.webkit.WebViewChromium"]);
|
|
112204
|
+
function hasGestureFlag(attrs) {
|
|
112205
|
+
return attrIsTrue(attrs, "clickable") || attrIsTrue(attrs, "long-clickable") || attrIsTrue(attrs, "checkable") || attrIsTrue(attrs, "scrollable");
|
|
112206
|
+
}
|
|
112207
|
+
function deriveWebRole(cls, attrs, label, hasRawChildren) {
|
|
112208
|
+
if (cls === "android.view.View" && label && !hasRawChildren && !hasGestureFlag(attrs)) {
|
|
112209
|
+
return "StaticText";
|
|
112210
|
+
}
|
|
112211
|
+
const role = deriveUiAutomatorRole(cls);
|
|
112212
|
+
if (role === "ScrollView" && !attrIsTrue(attrs, "scrollable")) return "List";
|
|
112213
|
+
return role;
|
|
112214
|
+
}
|
|
112183
112215
|
function attrIsTrue(attrs, key2) {
|
|
112184
112216
|
return attrs[key2] === "true";
|
|
112185
112217
|
}
|
|
112186
112218
|
function isInteractive(attrs) {
|
|
112187
|
-
if (
|
|
112188
|
-
return true;
|
|
112189
|
-
}
|
|
112219
|
+
if (hasGestureFlag(attrs)) return true;
|
|
112190
112220
|
if (attrIsTrue(attrs, "focusable") && labelOf(attrs) !== "") return true;
|
|
112191
112221
|
return false;
|
|
112192
112222
|
}
|
|
@@ -112264,7 +112294,7 @@ function makeUiNode(attrs, role, pixelBounds, label, children) {
|
|
|
112264
112294
|
return out;
|
|
112265
112295
|
}
|
|
112266
112296
|
function pruneSubtree(root, opts) {
|
|
112267
|
-
const stack = [{ parsed: root, scrollClip: null, visited: false }];
|
|
112297
|
+
const stack = [{ parsed: root, scrollClip: null, inWebView: false, visited: false }];
|
|
112268
112298
|
const outputs = /* @__PURE__ */ new Map();
|
|
112269
112299
|
while (stack.length > 0) {
|
|
112270
112300
|
const top = stack[stack.length - 1];
|
|
@@ -112272,22 +112302,31 @@ function pruneSubtree(root, opts) {
|
|
|
112272
112302
|
top.visited = true;
|
|
112273
112303
|
const attrs = top.parsed.attrs;
|
|
112274
112304
|
const myBounds = parseUiAutomatorBounds(attrs.bounds ?? "");
|
|
112275
|
-
const isScroll = isUiAutomatorScrollable(attrs);
|
|
112305
|
+
const isScroll = isUiAutomatorScrollable(attrs, top.inWebView);
|
|
112276
112306
|
const childClip = isScroll && myBounds ? myBounds : top.scrollClip;
|
|
112307
|
+
const childInWebView = top.inWebView || WEBVIEW_CLASSES.has(attrs.class ?? "");
|
|
112277
112308
|
for (let i = top.parsed.children.length - 1; i >= 0; i--) {
|
|
112278
112309
|
const c = top.parsed.children[i];
|
|
112279
112310
|
if (c.tag === "node") {
|
|
112280
|
-
stack.push({
|
|
112311
|
+
stack.push({
|
|
112312
|
+
parsed: c,
|
|
112313
|
+
scrollClip: childClip,
|
|
112314
|
+
inWebView: childInWebView,
|
|
112315
|
+
visited: false
|
|
112316
|
+
});
|
|
112281
112317
|
}
|
|
112282
112318
|
}
|
|
112283
112319
|
} else {
|
|
112284
|
-
outputs.set(
|
|
112320
|
+
outputs.set(
|
|
112321
|
+
top.parsed,
|
|
112322
|
+
computeNodeOutput(top.parsed, top.scrollClip, top.inWebView, outputs, opts)
|
|
112323
|
+
);
|
|
112285
112324
|
stack.pop();
|
|
112286
112325
|
}
|
|
112287
112326
|
}
|
|
112288
112327
|
return outputs.get(root) ?? [];
|
|
112289
112328
|
}
|
|
112290
|
-
function computeNodeOutput(parsed, scrollClip, outputs, opts) {
|
|
112329
|
+
function computeNodeOutput(parsed, scrollClip, inWebView, outputs, opts) {
|
|
112291
112330
|
const attrs = parsed.attrs;
|
|
112292
112331
|
const cls = attrs.class ?? "";
|
|
112293
112332
|
if (NOISY_CLASSES.has(cls)) return [];
|
|
@@ -112309,9 +112348,26 @@ function computeNodeOutput(parsed, scrollClip, outputs, opts) {
|
|
|
112309
112348
|
}
|
|
112310
112349
|
}
|
|
112311
112350
|
if (WEBVIEW_CLASSES.has(cls)) {
|
|
112312
|
-
if (!visible) return
|
|
112313
|
-
|
|
112314
|
-
|
|
112351
|
+
if (!visible) return keptChildren;
|
|
112352
|
+
let label2 = labelOf(attrs);
|
|
112353
|
+
let children = keptChildren;
|
|
112354
|
+
const inner = children.length === 1 && children[0].webHost ? children[0] : void 0;
|
|
112355
|
+
if (inner) {
|
|
112356
|
+
children = inner.children;
|
|
112357
|
+
if (!label2 && inner.label) label2 = inner.label;
|
|
112358
|
+
}
|
|
112359
|
+
if (!label2 && children.length === 0) label2 = "(no web content exposed)";
|
|
112360
|
+
const node2 = makeUiNode(attrs, "WebView", bounds, label2, children);
|
|
112361
|
+
node2.webHost = true;
|
|
112362
|
+
if (inner) {
|
|
112363
|
+
node2.clickable ||= inner.clickable;
|
|
112364
|
+
node2.longClickable ||= inner.longClickable;
|
|
112365
|
+
node2.scrollable ||= inner.scrollable;
|
|
112366
|
+
if (!node2.identifier && inner.identifier) node2.identifier = inner.identifier;
|
|
112367
|
+
}
|
|
112368
|
+
const hidden = hiddenInScroll + (inner?.scrollHidden ?? 0);
|
|
112369
|
+
if (hidden > 0) node2.scrollHidden = hidden;
|
|
112370
|
+
return [node2];
|
|
112315
112371
|
}
|
|
112316
112372
|
const interactive = isInteractive(attrs);
|
|
112317
112373
|
let label = labelOf(attrs);
|
|
@@ -112344,7 +112400,13 @@ function computeNodeOutput(parsed, scrollClip, outputs, opts) {
|
|
|
112344
112400
|
(c) => !(c.role === "StaticText" && c.label && lower.includes(c.label.toLowerCase()) && !c.clickable)
|
|
112345
112401
|
);
|
|
112346
112402
|
}
|
|
112347
|
-
const
|
|
112403
|
+
const role = inWebView ? deriveWebRole(
|
|
112404
|
+
cls,
|
|
112405
|
+
attrs,
|
|
112406
|
+
label,
|
|
112407
|
+
parsed.children.some((c) => c.tag === "node")
|
|
112408
|
+
) : deriveUiAutomatorRole(cls);
|
|
112409
|
+
const node = makeUiNode(attrs, role, bounds, label, keptChildren);
|
|
112348
112410
|
if (hiddenInScroll > 0) node.scrollHidden = hiddenInScroll;
|
|
112349
112411
|
return [node];
|
|
112350
112412
|
}
|
|
@@ -113252,7 +113314,10 @@ async function describeAndroid(registry2, serial, _bundleId, isTv) {
|
|
|
113252
113314
|
devtools.getHierarchy(),
|
|
113253
113315
|
devtools.getScreenSize()
|
|
113254
113316
|
]);
|
|
113255
|
-
const tree2 =
|
|
113317
|
+
const tree2 = await awaitWebViewPublished(
|
|
113318
|
+
parseUiAutomatorDump(xml, size2.width, size2.height),
|
|
113319
|
+
async () => parseUiAutomatorDump((await devtools.getHierarchy()).xml, size2.width, size2.height)
|
|
113320
|
+
);
|
|
113256
113321
|
return { tree: tree2, source: "android-devtools", hint };
|
|
113257
113322
|
} catch (serviceErr) {
|
|
113258
113323
|
console.debug(
|
|
@@ -113260,16 +113325,32 @@ async function describeAndroid(registry2, serial, _bundleId, isTv) {
|
|
|
113260
113325
|
);
|
|
113261
113326
|
}
|
|
113262
113327
|
}
|
|
113328
|
+
const [size, raw] = await Promise.all([getAndroidScreenSize(serial), uiautomatorDump(serial)]);
|
|
113329
|
+
const tree = await awaitWebViewPublished(
|
|
113330
|
+
parseDump(raw, size),
|
|
113331
|
+
async () => parseDump(await uiautomatorDump(serial), size)
|
|
113332
|
+
);
|
|
113333
|
+
return { tree, source: "uiautomator", hint };
|
|
113334
|
+
}
|
|
113335
|
+
var WEBVIEW_PUBLISH_STEP_MS = 250;
|
|
113336
|
+
var WEBVIEW_PUBLISH_BUDGET_MS = 1500;
|
|
113337
|
+
async function awaitWebViewPublished(first, read) {
|
|
113338
|
+
let tree = first;
|
|
113339
|
+
for (let waited = 0; waited < WEBVIEW_PUBLISH_BUDGET_MS && hasUnreadWebView(tree); ) {
|
|
113340
|
+
await new Promise((r) => setTimeout(r, WEBVIEW_PUBLISH_STEP_MS));
|
|
113341
|
+
waited += WEBVIEW_PUBLISH_STEP_MS;
|
|
113342
|
+
tree = await read();
|
|
113343
|
+
}
|
|
113344
|
+
return tree;
|
|
113345
|
+
}
|
|
113346
|
+
async function uiautomatorDump(serial) {
|
|
113263
113347
|
const randomSuffix = `${Date.now().toString(36)}-${Math.floor(Math.random() * 1e9).toString(36)}`;
|
|
113264
113348
|
const dumpPath = `/data/local/tmp/argent-ui-dump-${randomSuffix}.xml`;
|
|
113265
|
-
const
|
|
113266
|
-
|
|
113267
|
-
|
|
113268
|
-
|
|
113269
|
-
|
|
113270
|
-
{ timeoutMs: 2e4 }
|
|
113271
|
-
)
|
|
113272
|
-
]);
|
|
113349
|
+
const rawBuf = await adbExecOutBinary(
|
|
113350
|
+
serial,
|
|
113351
|
+
`uiautomator dump --compressed ${dumpPath} >/dev/null && cat ${dumpPath}; rm -f ${dumpPath}`,
|
|
113352
|
+
{ timeoutMs: 2e4 }
|
|
113353
|
+
);
|
|
113273
113354
|
const raw = rawBuf.toString("utf-8");
|
|
113274
113355
|
const trimmed = raw.trim();
|
|
113275
113356
|
if (/^ERROR:/i.test(trimmed) || !trimmed.includes("<hierarchy") && /error/i.test(trimmed)) {
|
|
@@ -113285,9 +113366,17 @@ async function describeAndroid(registry2, serial, _bundleId, isTv) {
|
|
|
113285
113366
|
}
|
|
113286
113367
|
);
|
|
113287
113368
|
}
|
|
113369
|
+
return raw;
|
|
113370
|
+
}
|
|
113371
|
+
function parseDump(raw, size) {
|
|
113288
113372
|
const oriented = orientScreenSize(size, parseDumpRotation(raw));
|
|
113289
|
-
|
|
113290
|
-
|
|
113373
|
+
return parseUiAutomatorDump(raw, oriented.width, oriented.height);
|
|
113374
|
+
}
|
|
113375
|
+
function hasUnreadWebView(node) {
|
|
113376
|
+
if (node.children.length === 0 && (node.role === "WebView" || node.label === "Web View")) {
|
|
113377
|
+
return true;
|
|
113378
|
+
}
|
|
113379
|
+
return node.children.some(hasUnreadWebView);
|
|
113291
113380
|
}
|
|
113292
113381
|
|
|
113293
113382
|
// ../tool-server/src/preview.ts
|
|
@@ -154280,85 +154369,11 @@ function createRunFlowTool(registry2) {
|
|
|
154280
154369
|
completedMsg: ({ params }) => `Ran flow ${displayFlowName(params)}`,
|
|
154281
154370
|
failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to run flow ${displayFlowName(params)}: ${failureSignal2.error_code}`
|
|
154282
154371
|
},
|
|
154283
|
-
description: `Run a saved flow
|
|
154284
|
-
|
|
154285
|
-
|
|
154286
|
-
|
|
154287
|
-
|
|
154288
|
-
ready (on iOS it also pins later element lookups to that app rather than auto-detecting the frontmost
|
|
154289
|
-
one); \`tool\` calls dispatch through the registry (a raw \`tool\` step ends that iOS pin, so lookups
|
|
154290
|
-
auto-detect again until the next \`launch\`, though a tool that cannot change the foreground app leaves the
|
|
154291
|
-
launched id as a fallback for a timed-out auto-detect, and \`launch-app\`/\`restart-app\` leave the id they
|
|
154292
|
-
started as that fallback instead); \`tap\`/\`long-press\`/\`type\` resolve a selector to an
|
|
154293
|
-
element and act on it (\`tap: { on, times: 2 }\` double-taps; \`long-press: { on, duration }\` presses and
|
|
154294
|
-
holds; \`tap\`/\`long-press\` alternatively take a raw normalized point \u2014 bare \`{ x, y }\` or \`on: { x, y }\`;
|
|
154295
|
-
any selector may scope its matches geometrically, the CSS combinators read off frames: \`within: <selector>\`
|
|
154296
|
-
(descendant \u2014 inside that container's frame), \`after: <selector>\` (CSS \`~\` \u2014 following it in reading
|
|
154297
|
-
order), \`next: <selector>\` (CSS \`+\` \u2014 the nearest such follower, which unlike CSS reaches past a
|
|
154298
|
-
non-matching neighbour rather than failing), plus \`any: true\` (CSS \`*\` \u2014 legal only WITH a scope and
|
|
154299
|
-
never beside text/id/role). Scopes nest to disambiguate \u2014 \`within: { id: card, within: { id: list } }\`
|
|
154300
|
-
reads "inside card inside list", each container's frame inside the next);
|
|
154301
|
-
\`swipe\` performs one finger flick (\`swipe: left\`, or \`swipe: { from?, direction|to|by, momentum?, duration? }\` \u2014
|
|
154302
|
-
direction is the FINGER's travel, the opposite sense of scroll-to's content direction; \`by: { x?, y? }\` \u2014 signed
|
|
154303
|
-
0\u20131 screen fractions, combined length at least 0.03 (a diagonal clears it where neither axis does); duration in ms,
|
|
154304
|
-
default 300, minimum 150, maximum 10000; each bound is a parse error that rejects the file before any step runs;
|
|
154305
|
-
\`momentum: false\` lands exactly where the finger lifts instead of flinging);
|
|
154306
|
-
\`scroll-to\` scrolls (momentum-free) until a target is visible; \`pinch\` zooms
|
|
154307
|
-
(\`pinch: { on?, scale }\` \u2014 scale > 1 in, < 1 out; screen center when \`on\` is omitted); \`rotate\` is the
|
|
154308
|
-
two-finger rotation gesture (\`rotate: { on?, by }\` \u2014 degrees, + clockwise, within \xB13000\xB0; screen center
|
|
154309
|
-
when \`on\` is omitted; distinct from the \`rotate\` tool, which changes device orientation); \`await\` waits
|
|
154310
|
-
for a UI condition, and additionally takes the one condition that has no selector: \`idle: true\` waits
|
|
154311
|
-
until the screen has content and stops moving in BOTH the UI tree and the rendered pixels (it never
|
|
154312
|
-
fails a run \u2014 a screen that never settles passes carrying a \`warning\`, which is what makes it safe to
|
|
154313
|
-
persist; the one idle outcome that does stop the run is an \`error\` for a tree source THIS step could not
|
|
154314
|
-
read at all \u2014 a broken window rather than a verdict about the app, which leaves the run not-ok and skips
|
|
154315
|
-
every later step; it says nothing about WHICH screen settled \u2014 a dropped tap leaves the source screen
|
|
154316
|
-
perfectly idle \u2014 so pair it with the element check that names the destination); \`wait\` pauses for a fixed number of milliseconds; \`assert\` checks one now; \`snapshot\`
|
|
154317
|
-
diffs a screenshot \u2014 or, with \`cropOn: <selector>\`, one element's cropped region \u2014 against a stored
|
|
154318
|
-
baseline (a missing baseline fails the step \u2014 set updateBaselines to adopt the current screen; a
|
|
154319
|
-
cropped element whose size drifted fails on dimensions); \`echo\` annotates; \`run\` executes another flow
|
|
154320
|
-
inline \u2014 a YAML path resolved against the directory of the flow file that references it (co-located
|
|
154321
|
-
runs only); \`script\` runs a local .mjs file in a fresh Node process for setup, cleanup, or any work a device step cannot do
|
|
154322
|
-
(\`script: { path: ../../scripts/seed.mjs, timeout?: <ms> }\` \u2014 always a map, never a bare path; the path
|
|
154323
|
-
is resolved against the flow file that names the step, exactly as a \`run\` target is, so a saved flow
|
|
154324
|
-
climbs out of .argent/flows/ to reach the project's own scripts/ directory. The step needs no device, so
|
|
154325
|
-
a script-only flow runs with nothing booted \u2014 but a \`run\` or a \`when\` step beside it still resolves
|
|
154326
|
-
one, \`when\` even when the only thing it guards is another script, so a platform-gated seed needs a
|
|
154327
|
-
device of that platform. Its stdout and stderr come back on the step report. Co-located runs only.).
|
|
154328
|
-
A selector-less gesture \u2014 a coordinate \`tap\`/\`long-press\`/\`swipe\`, or a \`pinch\`/\`rotate\` with no \`on\` \u2014 resolves
|
|
154329
|
-
no frame out of the tree, so an unreadable tree source does NOT stop it the way it stops \`idle\`: it
|
|
154330
|
-
settles best-effort, dispatches anyway, and the step PASSES carrying a \`warning\` that quotes the source's
|
|
154331
|
-
own error. That green says the gesture was SENT, not that it landed. Restore the tree source (usually
|
|
154332
|
-
relaunch the app so the instrumentation loads), or accept the warning where the app can serve no tree;
|
|
154333
|
-
the first such gesture proves the outage and later ones spend that verdict without paying the settle
|
|
154334
|
-
window again. A tree read that comes back, or a relaunch, retires that verdict \u2014 which only makes the
|
|
154335
|
-
next gesture pay a fresh window, and it warns again if the source is still down.
|
|
154336
|
-
A \`when:\` block (condition + \`steps:\`, no else) runs its steps only if the condition holds \u2014
|
|
154337
|
-
checked once with the short assert grace \u2014 for one-sided divergences like interstitials and coach
|
|
154338
|
-
marks; a skipped block reports distinctly and failures inside an entered block are real failures.
|
|
154339
|
-
A flow is self-contained when its first non-\`echo\`/\`script\` step is \`launch\`; it must not declare
|
|
154340
|
-
\`executionPrerequisite\`. Other flows use the device's current state. Device id is injected by the runner (flows store none) \u2014 pass \`device\` or
|
|
154341
|
-
\`platform\` to pick one, else the single booted device is used. On Chromium a \`launch\` step's value is an
|
|
154342
|
-
Electron app path ({ chromium: <path> | { path, args } }) the runner boots (on the tool-server host) rather
|
|
154343
|
-
than an installed app id it relaunches. With no explicit \`device\`, a run whose leading launch is
|
|
154344
|
-
unambiguously chromium (\`platform: chromium\`, or a lone \`{ chromium: \u2026 }\` target) boots that app and
|
|
154345
|
-
starts there \u2014 following a leading \`run:\`, \`echo:\` or \`script:\`, so a fragment that composes a chromium
|
|
154346
|
-
e2e flow boots too, and so does a flow that seeds a backend before it launches;
|
|
154347
|
-
otherwise the first launch attaches to an already-running instance and never kills it. Every later
|
|
154348
|
-
launch \u2014 a nested e2e flow's own, or a mid-flow relaunch \u2014 boots a fresh instance the run moves onto;
|
|
154349
|
-
an instance the run already owns for that same app is killed first (its exit awaited) so the
|
|
154350
|
-
replacement can't lose the race against its single-instance lock. Instances the runner still owns at
|
|
154351
|
-
run end are torn down then. A launch declaring no id for the run's platform is an error, not a cue to
|
|
154352
|
-
switch platforms. Every step hard-stops the flow on failure; later steps are reported as skipped.
|
|
154353
|
-
Returns a structured report ({ flow, device, executionPrerequisite, ok, aborted?, passed, failed,
|
|
154354
|
-
skipped, errored, steps }) \u2014 \`device\` is the device the run STARTED on; when launches moved it onto
|
|
154355
|
-
runner-booted instances, each names its instance in that step's reason and marks the move \u2014 \`run moved
|
|
154356
|
-
off <id>\`, or \`retired <id> (same app relaunched)\` when the instance it left was the one killed \u2014
|
|
154357
|
-
a relaunch that retired an older owned instance names both.
|
|
154358
|
-
|
|
154359
|
-
If a fragment has an execution prerequisite and prerequisiteAcknowledged is not set to true, the tool
|
|
154360
|
-
returns a notice with the prerequisite instead of running.
|
|
154361
|
-
Pass exactly one flow source: name for a saved flow under project_root, or flow_path for an explicit YAML \u2014 both together, or neither, fails the call.`,
|
|
154372
|
+
description: `Run a saved YAML flow end to end. Use when
|
|
154373
|
+
asked to replay a recorded path, re-run a QA regression, or check that a known journey still passes; for a
|
|
154374
|
+
one-off interaction use the gesture tools instead, and to author a flow use flow-start-recording. Pass
|
|
154375
|
+
exactly one flow source: name (under project_root) or flow_path.
|
|
154376
|
+
Returns a per-step report: the first failure stops the run and the rest report as skipped.`,
|
|
154362
154377
|
longRunning: true,
|
|
154363
154378
|
zodSchema: zodSchema66,
|
|
154364
154379
|
fileInputs: fileInputs3,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -109,7 +109,7 @@ Scopes can combine and nest, with at most six scope keys. Use strict selectors f
|
|
|
109
109
|
|
|
110
110
|
## Directives
|
|
111
111
|
|
|
112
|
-
Directives stop the flow on failure and skip later steps.
|
|
112
|
+
Directives stop the flow on failure and skip later steps. The available directives are `launch`, `tap`, `long-press`, `swipe`, `type`, `scroll-to`, `pinch`, `rotate`, `await`, `assert`, `wait`, `snapshot`, `run`, `script`, `when`, `echo`, and `tool`.
|
|
113
113
|
|
|
114
114
|
Use the launch map for cross-platform flows. A bare launch applies everywhere and becomes an app path on Chromium. The map takes `native:`, `ios:`, `android:`, `vega:`, and `chromium:`. `native:` is one id shared by iOS, Android, and Vega, and a per-platform key overrides it for that platform. `chromium:` accepts a relative or absolute app path. A launch that declares no id for the run's platform is an error, not a cue to switch platforms. On iOS, a successful launch also pins later tree reads to that app until the next raw `tool:` step, so read [The runner tree is not the discovery tree](#the-runner-tree-is-not-the-discovery-tree) when a read describes the wrong screen.
|
|
115
115
|
|
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: argent-device-interact
|
|
3
|
-
description: Interact with an iOS simulator, Android emulator, or Chromium (CDP) app using argent MCP tools. Use when tapping UI elements, performing gestures, scrolling/swiping, typing text, pressing hardware buttons, launching apps, opening URLs, taking screenshots, waiting for an element to appear or disappear, or checking visible app state after interactions.
|
|
3
|
+
description: Interact with an iOS simulator, Android emulator, or Chromium (CDP) app using argent MCP tools. Use when tapping UI elements, performing gestures, scrolling/swiping, typing text, pressing hardware buttons, launching apps, opening URLs, taking screenshots, waiting for an element to appear or disappear, or checking visible app state after interactions. Not for TV targets.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
## Unified tool surface
|
|
7
7
|
|
|
8
8
|
All interaction tools below accept a `udid` parameter and auto-dispatch iOS vs Android based on its shape (UUID → iOS simulator, `chromium-cdp-<port>` → Chromium (CDP) app, anything else → Android adb serial). You use the same tool names on every platform.
|
|
9
9
|
|
|
10
|
-
**Chromium (CDP) app** =
|
|
11
|
-
|
|
12
|
-
**Multi-tab / windows (Chromium only):** a Chromium device may have several tabs / BrowserWindows. Use `chromium-tabs` to `list` them (stable ids `t1`, `t2`, …, optional labels), open a `new` one, `select` which is active, or `close` one. Every other tool (`describe`, `gesture-tap`, `screenshot`, `debugger-evaluate`, `open-url`, …) acts on the **active** tab, so `chromium-tabs action=select` before driving a different tab. Note: a cross-process navigation (some redirects) can swap a tab's underlying CDP target — re-run `chromium-tabs action=list` to pick it up under a fresh id.
|
|
13
|
-
|
|
14
|
-
**Cookies & storage (Chromium only):** `chromium-cookies` reads/writes cookies via the Network domain (so HttpOnly cookies are visible): `action=get` (optionally scoped by `url`), `set` (`name`, `value`, + `url`/`domain`, optional `secure`/`httpOnly`/`sameSite`/`expires`), `delete` (`name`), `clear` (all). `chromium-storage` reads/writes Web Storage for the active page: `store=local|session`, `action=get` (one `key` or all entries), `set`, `remove`, `clear`. Both are per-origin / active-tab. Handy for seeding auth before a flow or asserting app state after one.
|
|
15
|
-
|
|
16
|
-
> **TV targets (Apple TV / Android TV) are not covered by this skill.** A TV target is **focus-driven, not touch-driven** — the `gesture-*` tools are the wrong tools for it. This applies to both Apple TV simulators (UUID-shaped, identical to iOS) and Android TV / leanback devices (serial-shaped, identical to a phone emulator). If `list-devices` tags your target `runtimeKind: "tv"`, stop and use the `argent-tv-interact` skill: `describe` to read focus, `tv-remote` for remote / D-pad presses, and `keyboard` to type.
|
|
17
|
-
|
|
18
|
-
> **Physical iPhones (an iOS entry with kind `"device"`) follow a different contract:** automation is app-scoped and only a subset of these tools exists there. Use the `argent-ios-device-interact` skill instead.
|
|
19
|
-
|
|
20
|
-
For platform-specific caveats (Metro `adb reverse`, locked-screen describe errors, etc.), see § 9 Platform-specific notes at the bottom.
|
|
10
|
+
**Chromium (CDP) app** = an Electron app or a Chromium-family browser (Chrome/Brave/Edge) exposing a Chrome DevTools Protocol endpoint. The same describe/tap/keyboard/screenshot surface drives it, but scrolling, tabs, cookies and storage differ — **read `references/chromium.md` before driving a `chromium` target.**
|
|
21
11
|
|
|
22
12
|
## 1. Before You Start
|
|
23
13
|
|
|
@@ -98,7 +88,7 @@ Point follow-up native diagnostics after you already have a candidate point:
|
|
|
98
88
|
- `native-user-interactable-view-at-point`: deepest native view that would receive touch at a known raw iOS point; requires `bundleId`
|
|
99
89
|
- `native-view-at-point`: deepest visible native view at a known raw iOS point; requires `bundleId`
|
|
100
90
|
|
|
101
|
-
### If `describe` Fails
|
|
91
|
+
### If `describe` Tool Fails
|
|
102
92
|
|
|
103
93
|
Read the exact error and choose the action that matches it:
|
|
104
94
|
|
|
@@ -186,21 +176,7 @@ Special keys: `enter`, `escape`, `backspace`, `tab`, `space`, `arrow-up`, `arrow
|
|
|
186
176
|
{ "udid": "<UDID>", "text": "{{secret:APP_PASSWORD}}" }
|
|
187
177
|
```
|
|
188
178
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
| # | Source | Which keys it exposes |
|
|
192
|
-
| --- | --------------------------------------------- | -------------------------------------------------------------------- |
|
|
193
|
-
| 1 | `ARGENT_SECRET_<NAME>` environment variable | prefixed vars only — the CI-native path |
|
|
194
|
-
| 2 | `<project>/.argent/secrets.env` | every key (`APP_PASSWORD=…`) — gitignore this file |
|
|
195
|
-
| 3 | `<project>/.env.local`, then `<project>/.env` | only `ARGENT_SECRET_`-prefixed keys, so app config stays unreachable |
|
|
196
|
-
| 4 | `~/.argent/secrets.env` | every key — per-user, works in any project |
|
|
197
|
-
|
|
198
|
-
Rules:
|
|
199
|
-
|
|
200
|
-
- 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.
|
|
201
|
-
- 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. To submit, put the text step and the Enter step in **one `run-sequence`**. The skip covers a whole batch that contains the placeholder, but a second bare `keyboard` call gets its own screenshot of the filled field.
|
|
202
|
-
- 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.
|
|
203
|
-
- 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.
|
|
179
|
+
Where the value is read from, and the rules for using a placeholder — including not screenshotting the field afterwards — are in `references/secrets.md`. Read it before typing any credential.
|
|
204
180
|
|
|
205
181
|
### paste — Paste text into the focused field
|
|
206
182
|
|
|
@@ -224,20 +200,18 @@ Values: `Portrait`, `LandscapeLeft`, `LandscapeRight`, `PortraitUpsideDown`
|
|
|
224
200
|
|
|
225
201
|
### await-ui-element — Block until a UI element reaches a state
|
|
226
202
|
|
|
227
|
-
|
|
203
|
+
**Never poll `screenshot`/`describe` in a loop to wait for something.** Use `await-ui-element`: it blocks server-side on the same tree `describe` reads. It has no bare-timer mode by design — for a plain pause, use your own harness sleep.
|
|
228
204
|
|
|
229
205
|
```json
|
|
230
206
|
{ "udid": "<UDID>", "condition": "visible", "selector": { "text": "Continue" } }
|
|
231
207
|
```
|
|
232
208
|
|
|
233
|
-
|
|
234
|
-
- `selector`: `{ text?, identifier?, role? }` — every provided field must match. `text` matches the element's label or value and `role` its element role (e.g. `AXButton`, `button`, `TextView`, `StaticText`), both as case-insensitive substrings; `identifier` matches its accessibility id / resource-id / testID **exactly** (case-insensitive), also accepting the unqualified Android resource-id name (`submit` matches `com.example.app:id/submit`). The synthetic `ROOT` container `describe` prints is never matched, so a `role` like `AXGroup`/`html` won't trivially "match the screen".
|
|
235
|
-
- Prefer a **specific** selector. A loose substring can match several elements, and the tool may then key off one you didn't mean: `text` reads the first **visible** match in **reading order** (top-to-bottom, left-to-right — the same order `describe` lists them, so it's the one you saw first; when no match is visible, the first match overall), while `visible`/`exists` are satisfied by **any** match. Disambiguate with a longer or more exact string, an `identifier`, or a `role` (e.g. pin to a text role like `StaticText` to skip a same-named button). On a `text` timeout the `note` quotes the matched element's text, so you can see which one it landed on.
|
|
236
|
-
- `text` condition also needs `expectedText` (substring the matched element must contain).
|
|
237
|
-
- `hidden` passes when the selector matches nothing. If `note` says it never matched, treat the check as failed and fix the selector. On iOS, a degraded empty tree does not report `hidden` success; the note gives the recovery hint.
|
|
238
|
-
- Optional `timeoutMs` (default 5000) and `pollIntervalMs` (default 400).
|
|
209
|
+
The tool's own description carries the conditions, selector matching, defaults and return shape. What it does not tell you:
|
|
239
210
|
|
|
240
|
-
|
|
211
|
+
- A `hidden` check that succeeds **immediately** may be a false pass — its `note` then says the selector never matched anything at all. Treat that as a failed check and fix the selector; do not read it as "the element went away".
|
|
212
|
+
- The synthetic `ROOT` container `describe` prints is never matched, so a `role` like `AXGroup`/`html` won't trivially "match the screen".
|
|
213
|
+
- To disambiguate a loose selector, pin the `role` to a text role like `StaticText` — that skips a same-named button.
|
|
214
|
+
- On a `text` timeout the `note` quotes the text of the element the check actually read, so you can see which match it landed on.
|
|
241
215
|
|
|
242
216
|
### await-screen-idle — Block until the screen stops changing
|
|
243
217
|
|
|
@@ -297,18 +271,14 @@ For visual regression checks, before/after screenshot comparisons, and detailed
|
|
|
297
271
|
|
|
298
272
|
## 8. Action Sequencing with `run-sequence`
|
|
299
273
|
|
|
300
|
-
Use `run-sequence` to batch multiple interaction steps into **a single tool call**. Only one screenshot is returned — after all steps complete.
|
|
301
|
-
scrolling multiple times, typing and submitting automatically, known sequence of multiple taps, rotating device back and forth.
|
|
274
|
+
Use `run-sequence` to batch multiple interaction steps into **a single tool call**. Only one screenshot is returned — after all steps complete.
|
|
302
275
|
|
|
303
|
-
Do **not** use `run-sequence` when any step depends on observing the result of a previous step
|
|
276
|
+
Do **not** use `run-sequence` when any step depends on observing the result of a previous step.
|
|
304
277
|
|
|
305
278
|
### Use cases
|
|
306
279
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
- Knowing that some action needs multiple steps without necessarily immediate insight of screenshot
|
|
310
|
-
- "scroll to bottom", "scroll to top", "scroll to do X" -> sequence scroll 3-5 times
|
|
311
|
-
- form interactions, "clear and retype field" -> you may use triple-tap to select all, type new value
|
|
280
|
+
- "scroll to bottom", "scroll to top", "scroll until X" -> sequence 3-5 scrolls
|
|
281
|
+
- form interactions, "clear and retype field" -> triple-tap to select all, then type the new value
|
|
312
282
|
- "submit form" → fill all fields in sequence, tap submit
|
|
313
283
|
- "go back to X" → defined tap sequence for the navigation
|
|
314
284
|
|
|
@@ -394,6 +364,10 @@ Stops on the first error (or unmet `await-ui-element` condition) and returns par
|
|
|
394
364
|
- **Locked screen / secure surfaces**: `describe` throws a clear error if it can't capture (keyguard, DRM, Play Integrity). Unlock the device or fall back to `screenshot`.
|
|
395
365
|
- **APK vs .app in `reinstall-app`**: pass `.apk` absolute path on Android; `.app` directory on iOS.
|
|
396
366
|
|
|
367
|
+
### Chromium
|
|
368
|
+
|
|
369
|
+
See `references/chromium.md` — tabs, cookies/storage.
|
|
370
|
+
|
|
397
371
|
### iOS
|
|
398
372
|
|
|
399
373
|
_(no iOS-only gotchas collected here yet — add them as they come up)_
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Chromium (CDP) Targets
|
|
2
|
+
|
|
3
|
+
Everything in the related SKILL.md still applies; this page covers what differs.
|
|
4
|
+
|
|
5
|
+
## Multi-tab / windows
|
|
6
|
+
|
|
7
|
+
A Chromium device may have several tabs / BrowserWindows. Use `chromium-tabs` to `list` them (stable ids `t1`, `t2`, …, optional labels), open a `new` one, `select` which is active, or `close` one.
|
|
8
|
+
|
|
9
|
+
Every other tool (`describe`, `gesture-tap`, `screenshot`, `debugger-evaluate`, `open-url`, …) acts on the **active** tab, so `chromium-tabs action=select` before driving a different tab.
|
|
10
|
+
|
|
11
|
+
Note: a cross-process navigation (some redirects) can swap a tab's underlying CDP target — re-run `chromium-tabs action=list` to pick it up under a fresh id.
|
|
12
|
+
|
|
13
|
+
## Cookies & storage
|
|
14
|
+
|
|
15
|
+
`chromium-cookies` reads/writes cookies via the Network domain (so HttpOnly cookies are visible):
|
|
16
|
+
|
|
17
|
+
- `action=get` (optionally scoped by `url`)
|
|
18
|
+
- `set` (`name`, `value`, + `url`/`domain`, optional `secure`/`httpOnly`/`sameSite`/`expires`)
|
|
19
|
+
- `delete` (`name`)
|
|
20
|
+
- `clear` (all)
|
|
21
|
+
|
|
22
|
+
`chromium-storage` reads/writes Web Storage for the active page: `store=local|session`, `action=get` (one `key` or all entries), `set`, `remove`, `clear`.
|
|
23
|
+
|
|
24
|
+
Both are per-origin / active-tab. Handy for seeding auth before a flow or asserting app state after one.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Typing Secrets
|
|
2
|
+
|
|
3
|
+
Where a `{{secret:<NAME>}}` placeholder gets its value, and the rules for using one. See the `keyboard` section of SKILL.md for the placeholder syntax itself.
|
|
4
|
+
|
|
5
|
+
## Where the value comes from
|
|
6
|
+
|
|
7
|
+
The placeholder is resolved on the machine running the tool-server, from the first of these that defines the name:
|
|
8
|
+
|
|
9
|
+
| # | Source | Which keys it exposes |
|
|
10
|
+
| --- | --------------------------------------------- | -------------------------------------------------------------------- |
|
|
11
|
+
| 1 | `ARGENT_SECRET_<NAME>` environment variable | prefixed vars only — the CI-native path |
|
|
12
|
+
| 2 | `<project>/.argent/secrets.env` | every key (`APP_PASSWORD=…`) — gitignore this file |
|
|
13
|
+
| 3 | `<project>/.env.local`, then `<project>/.env` | only `ARGENT_SECRET_`-prefixed keys, so app config stays unreachable |
|
|
14
|
+
| 4 | `~/.argent/secrets.env` | every key — per-user, works in any project |
|
|
15
|
+
|
|
16
|
+
## Rules
|
|
17
|
+
|
|
18
|
+
- 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.
|
|
19
|
+
- 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. To submit, put the text step and the Enter step in **one `run-sequence`**. The skip covers a whole batch that contains the placeholder, but a second bare `keyboard` call gets its own screenshot of the filled field.
|
|
20
|
+
- 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.
|
|
21
|
+
- 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.
|