@petukhovart/agent-view 0.12.0 → 0.13.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/.claude-plugin/plugin.json +1 -1
- package/README.md +40 -0
- package/dist/cdp/transport.d.ts.map +1 -1
- package/dist/cdp/transport.js +262 -1
- package/dist/cdp/transport.js.map +1 -1
- package/dist/cdp/types.d.ts +93 -0
- package/dist/cdp/types.d.ts.map +1 -1
- package/dist/cdp/types.js +15 -0
- package/dist/cdp/types.js.map +1 -1
- package/dist/cli/commands/dialog.d.ts +11 -0
- package/dist/cli/commands/dialog.d.ts.map +1 -0
- package/dist/cli/commands/dialog.js +30 -0
- package/dist/cli/commands/dialog.js.map +1 -0
- package/dist/cli/commands/upload.d.ts +10 -0
- package/dist/cli/commands/upload.d.ts.map +1 -0
- package/dist/cli/commands/upload.js +35 -0
- package/dist/cli/commands/upload.js.map +1 -0
- package/dist/cli/index.js +71 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/inspectors/dialog/index.d.ts +20 -0
- package/dist/inspectors/dialog/index.d.ts.map +1 -0
- package/dist/inspectors/dialog/index.js +58 -0
- package/dist/inspectors/dialog/index.js.map +1 -0
- package/dist/server/server.d.ts +32 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +184 -0
- package/dist/server/server.js.map +1 -1
- package/dist/server/tauri-dialog-shim.d.ts +27 -0
- package/dist/server/tauri-dialog-shim.d.ts.map +1 -0
- package/dist/server/tauri-dialog-shim.js +55 -0
- package/dist/server/tauri-dialog-shim.js.map +1 -0
- package/package.json +1 -1
- package/skills/verify/SKILL.md +117 -2
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** What the arm script reports back from the page. */
|
|
2
|
+
export var TauriShimResult;
|
|
3
|
+
(function (TauriShimResult) {
|
|
4
|
+
TauriShimResult["Armed"] = "armed";
|
|
5
|
+
/** The window is not a Tauri webview — nothing to patch, and nothing is wrong. */
|
|
6
|
+
TauriShimResult["NoTauri"] = "no-tauri";
|
|
7
|
+
})(TauriShimResult || (TauriShimResult = {}));
|
|
8
|
+
const STORE_KEY = '__agentViewTauriDialog__';
|
|
9
|
+
/**
|
|
10
|
+
* Tauri opens its file dialog in Rust, past the webview, so CDP cannot see it
|
|
11
|
+
* and cannot close it. The one seam is the JS side of the call:
|
|
12
|
+
* `@tauri-apps/plugin-dialog` resolves `window.__TAURI_INTERNALS__.invoke` at
|
|
13
|
+
* call time, so replacing that function answers the dialog before the Rust side
|
|
14
|
+
* ever runs. The patch survives repeated arming but not a navigation — a fresh
|
|
15
|
+
* document has a fresh `window`, and the window must be armed again.
|
|
16
|
+
*/
|
|
17
|
+
export function buildTauriArmScript(arm) {
|
|
18
|
+
return `(() => {
|
|
19
|
+
const internals = window.__TAURI_INTERNALS__;
|
|
20
|
+
if (!internals || typeof internals.invoke !== 'function') return ${JSON.stringify(TauriShimResult.NoTauri)};
|
|
21
|
+
let store = window[${JSON.stringify(STORE_KEY)}];
|
|
22
|
+
// Disarming a window that was never armed must not install the patch: a
|
|
23
|
+
// command whose whole purpose is to remove interference cannot add some.
|
|
24
|
+
if (!store && ${JSON.stringify(arm === null)}) return ${JSON.stringify(TauriShimResult.NoTauri)};
|
|
25
|
+
if (!store) {
|
|
26
|
+
store = window[${JSON.stringify(STORE_KEY)}] = { arm: null, fired: [] };
|
|
27
|
+
const original = internals.invoke.bind(internals);
|
|
28
|
+
internals.invoke = function (cmd, args, opts) {
|
|
29
|
+
const arm = store.arm;
|
|
30
|
+
if (arm && (cmd === 'plugin:dialog|open' || cmd === 'plugin:dialog|save')) {
|
|
31
|
+
store.arm = null;
|
|
32
|
+
store.fired.push({ ts: Date.now(), cmd: cmd, cancel: arm.kind === 'cancel' });
|
|
33
|
+
if (store.fired.length > 20) store.fired.shift();
|
|
34
|
+
if (arm.kind === 'cancel') return Promise.resolve(null);
|
|
35
|
+
const multiple = cmd === 'plugin:dialog|open' && !!(args && args.options && args.options.multiple);
|
|
36
|
+
return Promise.resolve(multiple ? arm.files : (arm.files[0] === undefined ? null : arm.files[0]));
|
|
37
|
+
}
|
|
38
|
+
return original(cmd, args, opts);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
store.arm = ${JSON.stringify(arm)};
|
|
42
|
+
return ${JSON.stringify(TauriShimResult.Armed)};
|
|
43
|
+
})()`;
|
|
44
|
+
}
|
|
45
|
+
export function buildTauriStatusScript() {
|
|
46
|
+
return `(() => {
|
|
47
|
+
const store = window[${JSON.stringify(STORE_KEY)}];
|
|
48
|
+
return {
|
|
49
|
+
patched: !!store,
|
|
50
|
+
armed: !!(store && store.arm),
|
|
51
|
+
fired: store ? store.fired : [],
|
|
52
|
+
};
|
|
53
|
+
})()`;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=tauri-dialog-shim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tauri-dialog-shim.js","sourceRoot":"","sources":["../../src/server/tauri-dialog-shim.ts"],"names":[],"mappings":"AAEA,sDAAsD;AACtD,MAAM,CAAN,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,kCAAe,CAAA;IACf,kFAAkF;IAClF,uCAAoB,CAAA;AACtB,CAAC,EAJW,eAAe,KAAf,eAAe,QAI1B;AAQD,MAAM,SAAS,GAAG,0BAA0B,CAAA;AAE5C;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAA0B;IAC5D,OAAO;;qEAE4D,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,OAAO,CAAC;uBACrF,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;;;kBAG9B,IAAI,CAAC,SAAS,CAAC,GAAG,KAAK,IAAI,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,OAAO,CAAC;;qBAE5E,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;gBAe9B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;WACxB,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,CAAC;KAC3C,CAAA;AACL,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,OAAO;yBACgB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;;;;;;KAM7C,CAAA;AACL,CAAC"}
|
package/package.json
CHANGED
package/skills/verify/SKILL.md
CHANGED
|
@@ -47,7 +47,8 @@ agent-view dom --diff # Lines changed since last dom call (+ a
|
|
|
47
47
|
### Interaction
|
|
48
48
|
```bash
|
|
49
49
|
agent-view click <ref> # Click element by ref from dom output
|
|
50
|
-
agent-view click --
|
|
50
|
+
agent-view click --filter "Save" # Find element by text and click
|
|
51
|
+
agent-view click --pos 100,200 # Click by coordinates — CANVAS ONLY, see below
|
|
51
52
|
agent-view click <ref> --double # Double-click (fires dblclick handlers); works with --filter / --pos too
|
|
52
53
|
agent-view fill <ref> "text" # Type into input field
|
|
53
54
|
agent-view drag --from <ref> --to <ref> # Drag element to another element by ref
|
|
@@ -61,6 +62,100 @@ ref and coordinate (e.g. `--from <ref> --to-pos 400,300`). For canvas/Pixi targe
|
|
|
61
62
|
Refs are resolved fresh on each call, so window resizes between snapshots are tolerated.
|
|
62
63
|
Increase `--steps` for handlers using `globalpointermove` so intermediate frames are not skipped.
|
|
63
64
|
|
|
65
|
+
**Coordinates are a last resort.** `--pos` / `--from-pos` / `--to-pos` exist for canvas and WebGL,
|
|
66
|
+
where no ref exists. On DOM the correct order is `dom --filter "<text>"` → take the `[ref=N]` →
|
|
67
|
+
`click <ref>`, or `click --filter "<text>"` in one step. A coordinate pair breaks on any layout
|
|
68
|
+
shift, scroll, zoom, or window resize, and it clicks whatever now sits at that point — silently.
|
|
69
|
+
If you reach for `--pos` on a DOM element, first say why the ref was not usable.
|
|
70
|
+
|
|
71
|
+
### Modals & file pickers (`dialog`, `upload`)
|
|
72
|
+
|
|
73
|
+
A modal that agent-view cannot answer stops a run dead: the window looks frozen, every
|
|
74
|
+
later command times out, and nothing says why. Two kinds, handled differently.
|
|
75
|
+
|
|
76
|
+
**JS modals — `alert` / `confirm` / `prompt` / `beforeunload` — are answered for you.**
|
|
77
|
+
Nothing to set up. While agent-view is attached these never block the page: the default
|
|
78
|
+
standing answer is *dismiss*, and each one is recorded. `agent-view dialog` is the
|
|
79
|
+
reliable place to read that record; the console feed carries the same line
|
|
80
|
+
(`[agent-view] confirm auto-dismissed: <message>`) but only from the moment console
|
|
81
|
+
attaches, so a modal answered before your first `console` call reaches `dialog` alone.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
agent-view dialog # standing answer + every modal this window has seen
|
|
85
|
+
agent-view dialog policy accept # confirm() → true from now on
|
|
86
|
+
agent-view dialog policy accept --text "name" # prompt() → "name"
|
|
87
|
+
agent-view dialog policy dismiss # back to the default
|
|
88
|
+
agent-view dialog dismiss # answer one that is open right now
|
|
89
|
+
agent-view dialog accept --text "x" # …ditto, accepting
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`dialog accept` / `dialog dismiss` exist for a modal that was already open **before**
|
|
93
|
+
agent-view attached — that one produced no event, so no policy applied to it.
|
|
94
|
+
|
|
95
|
+
**Native file pickers never open — you answer them in advance.** Which mechanism applies
|
|
96
|
+
depends on how the app opens the picker, and `dialog arm` sets up all of them at once,
|
|
97
|
+
so you do not have to know:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# The input already exists in the DOM (even hidden) — no picker at all, cheapest path
|
|
101
|
+
agent-view upload --selector "#file-input" --file ./fixtures/a.png
|
|
102
|
+
agent-view upload --selector "#imgs" --file ./a.png --file ./b.png # multi-select
|
|
103
|
+
agent-view upload --ref 12 --file ./a.png # if the AX tree exposes it
|
|
104
|
+
|
|
105
|
+
# The input is created inside the click handler, or the app calls a native dialog API
|
|
106
|
+
agent-view dialog arm --file ./fixtures/a.png # then click the button that opens it
|
|
107
|
+
agent-view dialog arm --cancel # act as if the user pressed Cancel
|
|
108
|
+
agent-view dialog disarm # let real pickers open again
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Rules that actually bite:
|
|
112
|
+
|
|
113
|
+
- **Arm before the click.** A picker cannot be caught once it is open. `arm` is one-shot —
|
|
114
|
+
it is spent by the first picker and interception turns itself off, so a later click
|
|
115
|
+
opens a real OS dialog.
|
|
116
|
+
- **Click through `agent-view click`, never `eval "el.click()"`.** Chromium refuses to
|
|
117
|
+
open a file picker without user activation, and an eval-driven click carries none: the
|
|
118
|
+
picker is silently dropped and never intercepted.
|
|
119
|
+
- **Hidden inputs have no ref.** `display:none` / `v-show="false"` keeps them out of the
|
|
120
|
+
AX tree, so `dom` never prints one. Use `--selector`. `upload` has no `--filter` on
|
|
121
|
+
purpose: an accessible name lands on the label, not on the input behind it.
|
|
122
|
+
- **`beforeunload` is always dismissed**, whatever the policy — accepting it navigates
|
|
123
|
+
away and loses the state you are checking.
|
|
124
|
+
- Paths are resolved against your cwd and must exist — CDP accepts a bad path silently and
|
|
125
|
+
the app then reads an empty file.
|
|
126
|
+
- `agent-view dialog` after the fact shows what was intercepted and what was answered.
|
|
127
|
+
|
|
128
|
+
Known limits: `showOpenFilePicker()` (File System Access API) exposes no input to fill, so
|
|
129
|
+
it can only be cancelled. Electron does not implement `window.prompt` at all. Native
|
|
130
|
+
dialogs opened straight from an Electron **main** process (`dialog.showOpenDialog` behind
|
|
131
|
+
an IPC channel) are out of reach — CDP does not see the main process.
|
|
132
|
+
|
|
133
|
+
### Waiting (`wait`)
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
agent-view wait --filter "Saved" # until the text appears in the AX tree
|
|
137
|
+
agent-view wait --filter "Saved" --timeout 20 # max wait in seconds (default 10)
|
|
138
|
+
agent-view wait --filter "Row 5" --window "Main" # specific window
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Exits as soon as the element appears; exits non-zero on timeout — so `&&` after it is a real gate.
|
|
142
|
+
|
|
143
|
+
**Never sleep for a fixed time.** No `sleep`, no `timeout`, no `ping -n N 127.0.0.1` as a delay
|
|
144
|
+
(agents reach for `ping` when the harness blocks `sleep` — it is the same mistake in worse
|
|
145
|
+
clothing). A fixed pause is either too short, and you assert against a half-rendered UI, or too
|
|
146
|
+
long, and you burn wall-clock on every step. agent-view has a condition-based wait for every kind
|
|
147
|
+
of signal:
|
|
148
|
+
|
|
149
|
+
| You are waiting for… | Use |
|
|
150
|
+
|---|---|
|
|
151
|
+
| An element to render | `wait --filter "<text>"` |
|
|
152
|
+
| A store/state value | `watch "<expr>" --until "<expr>"` |
|
|
153
|
+
| A log line | `console --follow --until "<pattern>"` |
|
|
154
|
+
| A request to fire | `network --follow --until "<url>"` |
|
|
155
|
+
|
|
156
|
+
If none of these fits, poll: `dom --filter X --count` in a loop with an explicit attempt cap, and
|
|
157
|
+
report how many attempts it took.
|
|
158
|
+
|
|
64
159
|
### Screenshots
|
|
65
160
|
```bash
|
|
66
161
|
agent-view screenshot --scale 0.5 # Recommended: JPEG at half-res (~3× fewer vision tokens)
|
|
@@ -239,6 +334,7 @@ Verifications cost very different amounts. Pick the cheapest tool that can actua
|
|
|
239
334
|
| Count of matching elements | `dom --filter X --count` | Single integer, no tree output, no ref mutations |
|
|
240
335
|
| App state, store contents, computed values | `eval "expr"` | DOM doesn't expose JS state; reading the tree to infer it is wasteful and unreliable |
|
|
241
336
|
| Does `window.X` / a globally-exposed API exist? | `eval "typeof window.X"` | DOM doesn't show JS globals; only authoritative check |
|
|
337
|
+
| An element that has not rendered yet | `wait --filter "<text>"` | Exits on appearance and non-zero on timeout — a real gate, unlike a fixed pause |
|
|
242
338
|
| State *trajectory* — what changed during/after an action | `watch "expr" --until …` or `--max-changes 1` | `eval` shows the final snapshot only; `watch` shows the diffs in order |
|
|
243
339
|
| Worker logic (SharedWorker / ServiceWorker) | `eval --target <name>` | Workers have no DOM at all |
|
|
244
340
|
| Did the last action throw or warn? | `console --clear` before, `console --level error,warn` after | Catches errors that don't surface in the DOM |
|
|
@@ -247,6 +343,9 @@ Verifications cost very different amounts. Pick the cheapest tool that can actua
|
|
|
247
343
|
| Layout, spacing, full-window visual regression | `screenshot --scale 0.5` | The only tool that sees pixels — but expensive (~6k tokens), use last |
|
|
248
344
|
| Canvas/WebGL scene contents | `scene --diff` | DOM is empty for canvas apps |
|
|
249
345
|
| What DOM nodes changed after an interaction | `dom --diff` | Returns only `+`/`-` lines; much cheaper than re-reading the full tree |
|
|
346
|
+
| Selecting a file for an input that exists in the DOM | `upload --selector` | No picker opens at all; works on hidden inputs, which have no ref |
|
|
347
|
+
| Selecting a file when the input appears only mid-click | `dialog arm --file` then `click` | The only way — the input does not exist before the click and is gone after |
|
|
348
|
+
| The window stopped responding after a click | `dialog` | Shows whether a modal was answered, and what the app was told |
|
|
250
349
|
|
|
251
350
|
When two tools could answer the same question, prefer the one higher up the table. A common mistake is screenshotting to check "is the count = 5?" when `eval "store.counter"` returns the number directly for ~50 tokens.
|
|
252
351
|
|
|
@@ -256,6 +355,22 @@ When two tools could answer the same question, prefer the one higher up the tabl
|
|
|
256
355
|
|
|
257
356
|
A run can produce one of three outcomes per step: **pass**, **fail**, **requires_visual_review**. There is no fourth bucket called "actually fine, here's why". A failed `Expected:` line is FAIL.
|
|
258
357
|
|
|
358
|
+
**How to run the commands themselves:**
|
|
359
|
+
|
|
360
|
+
- **Never discard output.** No `>/dev/null`, no `2>&1` to nowhere, no `| head -1` on a command whose
|
|
361
|
+
failure you have not yet read. Every agent-view command prints either the evidence or the reason
|
|
362
|
+
it failed; a suppressed `click` that matched nothing looks exactly like a successful one.
|
|
363
|
+
- **Chain with `&&`, not newlines.** Newline-separated commands keep running after a failure, so a
|
|
364
|
+
broken first step is followed by three steps acting on the wrong state. `&&` stops at the first
|
|
365
|
+
non-zero exit.
|
|
366
|
+
- **One action, then one check.** `click` is not evidence. The evidence is the `dom --diff`,
|
|
367
|
+
`dom --filter … --count`, `eval`, or `console --level error` you run after it. A block of three
|
|
368
|
+
clicks in a row with no check between them proves nothing about any of them.
|
|
369
|
+
- **Call the `agent-view` binary.** Install it once in the target project
|
|
370
|
+
(`pnpm add -D @petukhovart/agent-view`) and call `agent-view …` or `pnpm exec agent-view …`.
|
|
371
|
+
Do not prefix every call with `npx <package>`: it re-resolves the package on each invocation and
|
|
372
|
+
falls outside this skill's `allowed-tools`, so each call needs a fresh permission prompt.
|
|
373
|
+
|
|
259
374
|
These heuristics catch real bugs. Skipping them is how a run silently passes while the bug sits in plain sight in the same data:
|
|
260
375
|
|
|
261
376
|
1. **A failed expectation is FAIL.** If output disagrees with what the step expected, mark `fail` and continue. Do not soften the expectation. Do not invent prose explanations inline ("label reuse", "convention", "arithmetic off"). Justifications belong in the bug report after the run, never in the per-step log.
|
|
@@ -321,7 +436,7 @@ Tolerance default: a designer's code-review level — flag what they'd notice, i
|
|
|
321
436
|
## Resilience
|
|
322
437
|
|
|
323
438
|
- **Stale refs:** After HMR, navigation, or state change — re-run `dom` for fresh refs before interacting
|
|
324
|
-
- **Element not found:**
|
|
439
|
+
- **Element not found:** `agent-view wait --filter "<text>" --timeout 5` (covers the render delay after HMR). If it times out — report FAIL. Do not insert a blind pause and retry.
|
|
325
440
|
- **CDP disconnect:** Run `agent-view discover` to check. If no windows — `agent-view launch` (auto-starts Electron/Browser/Tauri). On `PORT_CONFLICT` — surface PID/process and ask user.
|
|
326
441
|
- **`CDP_TIMEOUT` error:** the command hit the server-side deadline; cached CDP sessions for that port were dropped, so retry once. Repeated timeouts mean the app's DevTools endpoint is wedged — restart the app.
|
|
327
442
|
- **Max retries per command:** 2. After that — SKIP scenario step with warning
|