@petukhovart/agent-view 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +2 -2
- package/README.md +38 -76
- package/dist/cli/commands/launch.d.ts.map +1 -1
- package/dist/cli/commands/launch.js +37 -6
- package/dist/cli/commands/launch.js.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/server/launcher.d.ts +14 -1
- package/dist/server/launcher.d.ts.map +1 -1
- package/dist/server/launcher.js +126 -13
- package/dist/server/launcher.js.map +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +17 -3
- package/dist/server/server.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -1
- package/package.json +60 -62
- package/skills/verify/SKILL.md +49 -36
- package/skills/verify-recipe/SKILL.md +286 -258
- package/agents/design-conformance-runner.md +0 -107
- package/agents/verify-runner.md +0 -187
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-view",
|
|
3
|
-
"description": "Visual verification CLI for desktop apps (Electron/Tauri/Browser) via Chrome DevTools Protocol. Ships two skills (
|
|
4
|
-
"version": "0.8.
|
|
3
|
+
"description": "Visual verification CLI for desktop apps (Electron/Tauri/Browser) via Chrome DevTools Protocol. Ships two skills: verify (run DOM, screenshot, eval, watch, console checks against a live app) and verify-recipe (author a reusable verification plan as a .md file).",
|
|
4
|
+
"version": "0.8.2",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cdp",
|
|
7
7
|
"electron",
|
package/README.md
CHANGED
|
@@ -29,8 +29,7 @@ Each step links to the full explanation below.
|
|
|
29
29
|
- **Worker access** — SharedWorker, ServiceWorker, dedicated Worker visible alongside pages; fuzzy `--target` resolution everywhere (id → title → URL)
|
|
30
30
|
- **Canvas / WebGL scene graph** — PixiJS today, engine-pluggable; `--compact` mirrors the DOM mode
|
|
31
31
|
- **Design-conformance verification** — pair screenshot commands with local design references (Figma export, hand-off
|
|
32
|
-
PNGs, any image on disk) inside a verify-recipe; the `
|
|
33
|
-
against the mockup
|
|
32
|
+
PNGs, any image on disk) inside a verify-recipe; the `verify` skill compares screenshots against the references inline
|
|
34
33
|
|
|
35
34
|
## Why CLI, not MCP?
|
|
36
35
|
|
|
@@ -57,8 +56,7 @@ npm install -g @petukhovart/agent-view
|
|
|
57
56
|
|
|
58
57
|
The plugin ships two skills. **`verify`** executes visual and runtime checks against a running app. **`verify-recipe`** generates a `.claude/verify-recipes/<slug>.md` file — a disciplined, cheapest-first command sequence for a feature or bugfix — that you or any AI agent can run later. Trigger it with phrases like "write a verify-recipe for the login fix" or "generate a verification plan for this feature".
|
|
59
58
|
|
|
60
|
-
For
|
|
61
|
-
see [Recommended workflow with Claude Code](#recommended-workflow-with-claude-code) below.
|
|
59
|
+
For the canonical author-once / re-run flow, see [Recommended workflow with Claude Code](#recommended-workflow-with-claude-code) below.
|
|
62
60
|
|
|
63
61
|
Verify:
|
|
64
62
|
|
|
@@ -194,13 +192,34 @@ Full surface in [Commands](#commands) below.
|
|
|
194
192
|
|
|
195
193
|
## Recommended workflow with Claude Code
|
|
196
194
|
|
|
197
|
-
A repeatable, token-efficient flow for "I shipped a feature/fix → confirm it actually works visually and at runtime".
|
|
198
|
-
Three phases, each driven by a focused prompt.
|
|
195
|
+
A repeatable, token-efficient flow for "I shipped a feature/fix → confirm it actually works visually and at runtime". Two phases, each driven by a focused prompt.
|
|
199
196
|
|
|
200
|
-
|
|
197
|
+
```mermaid
|
|
198
|
+
flowchart TD
|
|
199
|
+
Dev["Developer<br/>in Claude Code"] -->|prompt| Agent["Main Claude agent<br/>(Opus / Sonnet)"]
|
|
201
200
|
|
|
202
|
-
|
|
203
|
-
|
|
201
|
+
Agent -->|"verify ad-hoc"| Verify["verify skill"]
|
|
202
|
+
Agent -->|"write recipe"| Recipe["verify-recipe skill"]
|
|
203
|
+
|
|
204
|
+
Recipe -->|interview| Dev
|
|
205
|
+
Recipe -->|writes| File[".claude/verify-recipes/<slug>.md"]
|
|
206
|
+
File -.->|read on next run| Verify
|
|
207
|
+
|
|
208
|
+
Verify -->|"agent-view dom / eval / click /<br/>screenshot / watch / console"| CLI["agent-view CLI"]
|
|
209
|
+
CLI -->|CDP| App["Live app<br/>(Electron / Tauri / Browser)"]
|
|
210
|
+
App -->|stdout / image paths| Verify
|
|
211
|
+
|
|
212
|
+
Verify -->|"pass / fail summary<br/>+ design conformance verdict"| Dev
|
|
213
|
+
|
|
214
|
+
classDef skill fill:#e8f0ff,stroke:#3060a0,color:#0a1f3d
|
|
215
|
+
classDef tool fill:#fff4d6,stroke:#a07020,color:#3d2a05
|
|
216
|
+
class Verify,Recipe skill
|
|
217
|
+
class CLI tool
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Phase 1 — Author the verification plan (once)
|
|
221
|
+
|
|
222
|
+
Generate the recipe **once**, from a PRD / plan file / Jira ticket / commit range. The recipe is reusable — re-run after every iteration on the same feature.
|
|
204
223
|
|
|
205
224
|
```text
|
|
206
225
|
Generate a verify-recipe for the changes in commits <hash1>..<hash2>.
|
|
@@ -212,41 +231,24 @@ Design references (if any):
|
|
|
212
231
|
- /abs/path/figma-exports/error-state.png → label "invalid creds error"
|
|
213
232
|
```
|
|
214
233
|
|
|
215
|
-
What this triggers: the `verify-recipe` skill interviews you (if more context needed), then writes a
|
|
216
|
-
`.claude/verify-recipes/<slug>.md` with `Repro Steps`, `Evidence Commands` (cheapest-first: `eval` / `dom --filter`
|
|
217
|
-
before `screenshot`), `Regression Checks`, and — if you provided design refs — a `Design Conformance` table mapping
|
|
218
|
-
screenshot commands to expected reference images.
|
|
234
|
+
What this triggers: the `verify-recipe` skill interviews you (if more context needed), then writes a `.claude/verify-recipes/<slug>.md` with `Repro Steps`, `Evidence Commands` (cheapest-first: `eval` / `dom --filter` before `screenshot`), `Regression Checks`, and — if you provided design refs — a `Design Conformance` table mapping screenshot commands to expected reference images.
|
|
219
235
|
|
|
220
|
-
**Tip:** if you implemented from a Figma file via the `figma-implement-design` skill, it likely already saved exports
|
|
221
|
-
somewhere on disk — pass those paths. agent-view does NOT fetch from Figma URLs; provide local files only.
|
|
236
|
+
**Tip:** if you implemented from a Figma file via the `figma-implement-design` skill, it likely already saved exports somewhere on disk — pass those paths. agent-view does NOT fetch from Figma URLs; provide local files only.
|
|
222
237
|
|
|
223
|
-
### Phase 2 — Run the recipe
|
|
238
|
+
### Phase 2 — Run the recipe
|
|
224
239
|
|
|
225
240
|
```text
|
|
226
241
|
Run the verify-recipe at .claude/verify-recipes/<slug>.md.
|
|
227
242
|
```
|
|
228
243
|
|
|
229
|
-
What this triggers: the `verify` skill
|
|
230
|
-
subagent (Haiku) to execute every step and return a compact JSON report. If the recipe has a `Design Conformance`
|
|
231
|
-
section, `design-conformance-runner` (also Haiku) runs in parallel and visually compares screenshots against the
|
|
232
|
-
references.
|
|
233
|
-
|
|
234
|
-
**Effect on cost:** raw output (DOM dumps, screenshots, eval values) stays in the subagent. The main agent only sees the
|
|
235
|
-
merged JSON report — typically <2k tokens vs ~50k if Opus ran the recipe directly.
|
|
244
|
+
What this triggers: the `verify` skill reads the recipe, performs the `Repro Steps` setup, runs each `Evidence Command` against the live app, compares output to the recipe's `Expected:` lines, and reports pass/fail per step. If the recipe has a `Design Conformance` section, the same skill captures the screenshots, opens both actual and expected images, and reports `match` / `minor_mismatch` / `major_mismatch` per pair — all inline, in the same conversation.
|
|
236
245
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
The merged report lists `failed`, `requires_visual_review`, and `major_mismatch` items with one-sentence diagnoses. Hand
|
|
240
|
-
any subset back to the main agent:
|
|
246
|
+
When something fails, ask the main agent to fix and re-run only the affected steps:
|
|
241
247
|
|
|
242
248
|
```text
|
|
243
|
-
|
|
244
|
-
and step 7 (selective filter heading missing). Re-run only those two steps after the fix.
|
|
249
|
+
Step 4 failed (zone filter not mutating store). Fix and re-run that step plus step 7.
|
|
245
250
|
```
|
|
246
251
|
|
|
247
|
-
The main agent re-runs only the failing commands itself for richer evidence, makes the fix, and re-spawns
|
|
248
|
-
`verify-runner` for the targeted re-check — not the whole recipe.
|
|
249
|
-
|
|
250
252
|
### One-shot prompt (when there's no plan to convert)
|
|
251
253
|
|
|
252
254
|
For small fixes where you don't want a persistent recipe file:
|
|
@@ -259,53 +261,13 @@ I added a `saving` ref and bound it to :disabled. Verify it works:
|
|
|
259
261
|
- visual: button greys out (compare to /abs/path/saving-state.png if one is provided)
|
|
260
262
|
```
|
|
261
263
|
|
|
262
|
-
Claude will pick the right skill (usually `verify` ad-hoc mode), run a handful of `eval` / `dom --filter` / `console`
|
|
263
|
-
calls, and only screenshot if the visual claim needs it.
|
|
264
|
-
|
|
265
|
-
### Three-phase preconditions (0.8.0+)
|
|
266
|
-
|
|
267
|
-
Recipes split setup into three phases, each with a different runner:
|
|
268
|
-
|
|
269
|
-
| Phase | Who runs it | Purpose | Idempotent? |
|
|
270
|
-
|---|---|---|---|
|
|
271
|
-
| `## Manual Preconditions` | Human (or main agent) | Only what `agent-view` physically can't (USB token, hardware, multi-machine state). Usually empty. | n/a |
|
|
272
|
-
| `## Bringup` | verify-runner | Conditional + idempotent setup: `if <state-check> is falsy → run actions → wait for state to settle`. Logins, mounting widgets, navigation, anything `agent-view` can drive deterministically. | YES per step |
|
|
273
|
-
| `## Machine Preconditions` | verify-runner | Pure state queries that confirm Bringup actually landed. No actions here. | trivially |
|
|
274
|
-
|
|
275
|
-
Bringup steps look like:
|
|
276
|
-
|
|
277
|
-
```markdown
|
|
278
|
-
### B1. Login if on auth screen
|
|
279
|
-
- if `agent-view eval "typeof window.__dev"` is not `"object"`:
|
|
280
|
-
agent-view fill --filter "Логин" "root"
|
|
281
|
-
agent-view fill --filter "Пароль" "$AGENTVIEW_PASSWORD"
|
|
282
|
-
agent-view click --filter "Войти"
|
|
283
|
-
wait for `agent-view eval "typeof window.__dev"` to be `"object"`, timeout 15s
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
The runner always evaluates the IF first (cheap). If the system is already in the target state → skip the actions, advance. Otherwise → run actions in order, then poll the post-condition until truthy or timeout. **Re-running a recipe when the app is ready costs ~3 seconds** (only state-checks, zero actions). Starting from auth screen costs ~15-25 seconds (one-time login + mount + navigate). Same recipe, both starting states, no manual intervention.
|
|
287
|
-
|
|
288
|
-
Failure modes are distinct:
|
|
289
|
-
- `bringup_failed` → bringup spec is wrong (the action commands ran but the post-condition didn't land — likely the programmatic API doesn't exist or the button text changed). Author error.
|
|
290
|
-
- `precondition_failed` → bringup ran AND post-conditions landed, but Machine Preconditions still false. Real environment issue (degraded mode, wrong user role, missing data).
|
|
291
|
-
- `cascading_failures` → bringup + preconditions OK, but Evidence Commands fail. Verified feature is broken or recipe is stale.
|
|
292
|
-
|
|
293
|
-
Without the three-phase split, all three look like "step N failed" and you can't tell them apart.
|
|
294
|
-
|
|
295
|
-
**Credentials in Bringup:** always use env-var references (`$AGENTVIEW_PASSWORD`), never inline a literal password. Recipes get committed to git. The `verify-recipe` skill warns when it sees a literal-looking password and offers to convert.
|
|
264
|
+
Claude will pick the right skill (usually `verify` ad-hoc mode), run a handful of `eval` / `dom --filter` / `console` calls, and only screenshot if the visual claim needs it.
|
|
296
265
|
|
|
297
266
|
### Anti-patterns to avoid
|
|
298
267
|
|
|
299
|
-
- "Just verify the feature" with no plan or symptom — the recipe author can't pick the cheapest signal without knowing
|
|
300
|
-
|
|
301
|
-
-
|
|
302
|
-
first.
|
|
303
|
-
- Running the recipe directly with Opus when a recipe file exists — that's exactly the case `verify-runner` is for.
|
|
304
|
-
Prefer Phase 2's prompt.
|
|
305
|
-
- Stuffing 50 assertions into one recipe — split per-feature. A recipe should run in <2 minutes and produce a report you
|
|
306
|
-
can read in 30 seconds.
|
|
307
|
-
- Manual Preconditions without Machine Precondition counterparts — the runner can't catch the user skipping setup. The
|
|
308
|
-
skill warns during authoring; either add a check or accept the gap explicitly in the recipe.
|
|
268
|
+
- "Just verify the feature" with no plan or symptom — the recipe author can't pick the cheapest signal without knowing what "works" means. Give it the symptom that motivated the fix.
|
|
269
|
+
- Pasting Figma URLs and expecting agent-view to download them — it won't. Export the frames you care about to PNG first.
|
|
270
|
+
- Stuffing 50 assertions into one recipe — split per-feature. A recipe should run in <2 minutes and produce a report you can read in 30 seconds.
|
|
309
271
|
|
|
310
272
|
## How it works
|
|
311
273
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/launch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/launch.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAwB5D,wBAAsB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAgCtE"}
|
|
@@ -1,15 +1,46 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
1
2
|
import { sendCommand } from '../client.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
process.exit(1);
|
|
6
|
-
}
|
|
7
|
-
const response = await sendCommand({
|
|
3
|
+
import { ServerErrorCode } from '../../types.js';
|
|
4
|
+
function sendLaunch(config) {
|
|
5
|
+
return sendCommand({
|
|
8
6
|
command: 'launch',
|
|
9
7
|
port: config.port,
|
|
10
8
|
runtime: config.runtime,
|
|
11
9
|
args: { launch: config.launch, cwd: process.cwd() },
|
|
12
10
|
});
|
|
11
|
+
}
|
|
12
|
+
function promptRetry() {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
15
|
+
rl.question('Close the conflicting process or start the app manually, then press Enter to retry (or type "q" + Enter to abort): ', (answer) => {
|
|
16
|
+
rl.close();
|
|
17
|
+
resolve(answer.trim().toLowerCase() !== 'q');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export async function runLaunch(config) {
|
|
22
|
+
if (!config.launch) {
|
|
23
|
+
console.error('No launch command configured. Add "launch" to agent-view.config.json.');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
let response = await sendLaunch(config);
|
|
27
|
+
while (!response.ok && response.code === ServerErrorCode.PortConflict) {
|
|
28
|
+
const conflict = response.data;
|
|
29
|
+
const who = conflict.processName && conflict.pid
|
|
30
|
+
? `${conflict.processName} (PID ${conflict.pid})`
|
|
31
|
+
: conflict.pid
|
|
32
|
+
? `PID ${conflict.pid}`
|
|
33
|
+
: 'unknown process';
|
|
34
|
+
console.error(`Port ${conflict.port} is occupied by ${who} and does not expose CDP.`);
|
|
35
|
+
console.error('Options: (1) close that process so agent-view can auto-launch the app, or (2) start the app manually.');
|
|
36
|
+
if (!process.stdin.isTTY) {
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
const retry = await promptRetry();
|
|
40
|
+
if (!retry)
|
|
41
|
+
process.exit(1);
|
|
42
|
+
response = await sendLaunch(config);
|
|
43
|
+
}
|
|
13
44
|
if (response.ok) {
|
|
14
45
|
console.log(response.data);
|
|
15
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launch.js","sourceRoot":"","sources":["../../../src/cli/commands/launch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"launch.js","sourceRoot":"","sources":["../../../src/cli/commands/launch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,eAAe,EAA8C,MAAM,gBAAgB,CAAA;AAG5F,SAAS,UAAU,CAAC,MAAuB;IACzC,OAAO,WAAW,CAAC;QACjB,OAAO,EAAE,QAAQ;QACjB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE;KACpD,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;QAC5E,EAAE,CAAC,QAAQ,CACT,qHAAqH,EACrH,CAAC,MAAM,EAAE,EAAE;YACT,EAAE,CAAC,KAAK,EAAE,CAAA;YACV,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,CAAA;QAC9C,CAAC,CACF,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAuB;IACrD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAA;QACtF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,IAAI,QAAQ,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,CAAA;IAEvC,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,IAAI,KAAK,eAAe,CAAC,YAAY,EAAE,CAAC;QACtE,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAwB,CAAA;QAClD,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,GAAG;YAC9C,CAAC,CAAC,GAAG,QAAQ,CAAC,WAAW,SAAS,QAAQ,CAAC,GAAG,GAAG;YACjD,CAAC,CAAC,QAAQ,CAAC,GAAG;gBACZ,CAAC,CAAC,OAAO,QAAQ,CAAC,GAAG,EAAE;gBACvB,CAAC,CAAC,iBAAiB,CAAA;QACvB,OAAO,CAAC,KAAK,CAAC,QAAQ,QAAQ,CAAC,IAAI,mBAAmB,GAAG,2BAA2B,CAAC,CAAA;QACrF,OAAO,CAAC,KAAK,CAAC,uGAAuG,CAAC,CAAA;QAEtH,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,WAAW,EAAE,CAAA;QACjC,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC3B,QAAQ,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,CAAA;IACrC,CAAC;IAED,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -25,7 +25,7 @@ import { runWatch } from './commands/watch.js';
|
|
|
25
25
|
const program = new Command()
|
|
26
26
|
.name('agent-view')
|
|
27
27
|
.description('Visual verification CLI for desktop apps')
|
|
28
|
-
.version('0.
|
|
28
|
+
.version('0.8.2');
|
|
29
29
|
program
|
|
30
30
|
.command('init')
|
|
31
31
|
.description('Auto-generate agent-view.config.json')
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
+
import { RuntimeType, type PortConflictData } from '../types.js';
|
|
2
|
+
export declare class PortConflictError extends Error {
|
|
3
|
+
readonly conflict: PortConflictData;
|
|
4
|
+
constructor(conflict: PortConflictData);
|
|
5
|
+
}
|
|
1
6
|
export declare function parseCommand(cmd: string): [string, string[]];
|
|
2
7
|
export declare function isRunning(port: number): Promise<boolean>;
|
|
3
|
-
export declare function
|
|
8
|
+
export declare function detectPortConflict(port: number): Promise<PortConflictData | null>;
|
|
9
|
+
export declare function launch(command: string, port: number, cwd?: string, runtime?: RuntimeType): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Installs a global guard that swallows async socket errors leaking from
|
|
12
|
+
* chrome-remote-interface / Node http during CDP probing (ECONNRESET on a
|
|
13
|
+
* closing keep-alive socket, ECONNREFUSED races, EPIPE). Anything else is
|
|
14
|
+
* re-thrown so we don't hide real bugs.
|
|
15
|
+
*/
|
|
16
|
+
export declare function installCDPErrorGuard(): void;
|
|
4
17
|
//# sourceMappingURL=launcher.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../../src/server/launcher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../../src/server/launcher.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAWhE,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAA;gBACvB,QAAQ,EAAE,gBAAgB;CAavC;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAQ5D;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO9D;AAwCD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAMvF;AAED,wBAAsB,MAAM,CAC1B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CA8B3C"}
|
package/dist/server/launcher.js
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
1
|
+
import { spawn, execFile } from 'node:child_process';
|
|
2
|
+
import { createConnection } from 'node:net';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
2
4
|
import { listTargets } from '../cdp/transport.js';
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import { RuntimeType } from '../types.js';
|
|
6
|
+
const DEFAULT_LAUNCH_TIMEOUT_MS = 60_000;
|
|
7
|
+
const TAURI_LAUNCH_TIMEOUT_MS = 10 * 60_000;
|
|
8
|
+
const POLL_INTERVAL_MS = 2_000;
|
|
9
|
+
const PORT_PROBE_TIMEOUT_MS = 500;
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
7
11
|
const WIN_CMD_EXECUTABLES = new Set(['npm', 'npx', 'pnpm', 'yarn', 'ng', 'vite', 'tsc']);
|
|
12
|
+
export class PortConflictError extends Error {
|
|
13
|
+
conflict;
|
|
14
|
+
constructor(conflict) {
|
|
15
|
+
const who = conflict.processName && conflict.pid
|
|
16
|
+
? `${conflict.processName} (PID ${conflict.pid})`
|
|
17
|
+
: conflict.pid
|
|
18
|
+
? `PID ${conflict.pid}`
|
|
19
|
+
: 'an unknown process';
|
|
20
|
+
super(`Port ${conflict.port} is occupied by ${who}, but it does not expose CDP. `
|
|
21
|
+
+ `Either stop that process to let agent-view auto-launch the app, or start the app manually so it listens on port ${conflict.port}.`);
|
|
22
|
+
this.name = 'PortConflictError';
|
|
23
|
+
this.conflict = conflict;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
8
26
|
export function parseCommand(cmd) {
|
|
9
27
|
const parts = cmd.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? [];
|
|
10
28
|
const cleaned = parts.map(p => p.replace(/^["']|["']$/g, ''));
|
|
@@ -15,12 +33,70 @@ export function parseCommand(cmd) {
|
|
|
15
33
|
return [exe, cleaned.slice(1)];
|
|
16
34
|
}
|
|
17
35
|
export async function isRunning(port) {
|
|
18
|
-
|
|
19
|
-
|
|
36
|
+
try {
|
|
37
|
+
const targets = await listTargets(port);
|
|
38
|
+
return targets.length > 0;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function probePortOpen(port) {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
const socket = createConnection({ host: '127.0.0.1', port });
|
|
47
|
+
const done = (open) => {
|
|
48
|
+
socket.destroy();
|
|
49
|
+
resolve(open);
|
|
50
|
+
};
|
|
51
|
+
socket.setTimeout(PORT_PROBE_TIMEOUT_MS);
|
|
52
|
+
socket.once('connect', () => done(true));
|
|
53
|
+
socket.once('error', () => done(false));
|
|
54
|
+
socket.once('timeout', () => done(false));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function resolvePortOwner(port) {
|
|
58
|
+
try {
|
|
59
|
+
if (process.platform === 'win32') {
|
|
60
|
+
const ps = `$c = Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue | Select-Object -First 1; if ($c) { $p = Get-Process -Id $c.OwningProcess -ErrorAction SilentlyContinue; \"$($c.OwningProcess)|$($p.ProcessName)\" }`;
|
|
61
|
+
const { stdout } = await execFileAsync('powershell.exe', ['-NoProfile', '-Command', ps], { timeout: 3000 });
|
|
62
|
+
const line = stdout.trim();
|
|
63
|
+
if (!line)
|
|
64
|
+
return {};
|
|
65
|
+
const [pidStr, name] = line.split('|');
|
|
66
|
+
const pid = Number(pidStr);
|
|
67
|
+
return { pid: Number.isFinite(pid) ? pid : undefined, processName: name || undefined };
|
|
68
|
+
}
|
|
69
|
+
const { stdout } = await execFileAsync('lsof', ['-iTCP:' + port, '-sTCP:LISTEN', '-Fpc', '-n', '-P'], { timeout: 3000 });
|
|
70
|
+
let pid;
|
|
71
|
+
let processName;
|
|
72
|
+
for (const line of stdout.split('\n')) {
|
|
73
|
+
if (line.startsWith('p'))
|
|
74
|
+
pid = Number(line.slice(1));
|
|
75
|
+
else if (line.startsWith('c'))
|
|
76
|
+
processName = line.slice(1);
|
|
77
|
+
}
|
|
78
|
+
return { pid, processName };
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export async function detectPortConflict(port) {
|
|
85
|
+
if (await isRunning(port))
|
|
86
|
+
return null;
|
|
87
|
+
const open = await probePortOpen(port);
|
|
88
|
+
if (!open)
|
|
89
|
+
return null;
|
|
90
|
+
const owner = await resolvePortOwner(port);
|
|
91
|
+
return { port, ...owner };
|
|
20
92
|
}
|
|
21
|
-
export async function launch(command, port, cwd) {
|
|
22
|
-
if (await isRunning(port))
|
|
93
|
+
export async function launch(command, port, cwd, runtime) {
|
|
94
|
+
if (await isRunning(port))
|
|
23
95
|
return;
|
|
96
|
+
const open = await probePortOpen(port);
|
|
97
|
+
if (open) {
|
|
98
|
+
const owner = await resolvePortOwner(port);
|
|
99
|
+
throw new PortConflictError({ port, ...owner });
|
|
24
100
|
}
|
|
25
101
|
const [exe, args] = parseCommand(command);
|
|
26
102
|
const child = spawn(exe, args, {
|
|
@@ -31,13 +107,50 @@ export async function launch(command, port, cwd) {
|
|
|
31
107
|
windowsHide: true,
|
|
32
108
|
});
|
|
33
109
|
child.unref();
|
|
110
|
+
const timeout = runtime === RuntimeType.Tauri ? TAURI_LAUNCH_TIMEOUT_MS : DEFAULT_LAUNCH_TIMEOUT_MS;
|
|
34
111
|
const start = Date.now();
|
|
35
|
-
while (Date.now() - start <
|
|
112
|
+
while (Date.now() - start < timeout) {
|
|
36
113
|
await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
37
|
-
if (await isRunning(port))
|
|
114
|
+
if (await isRunning(port))
|
|
38
115
|
return;
|
|
39
|
-
}
|
|
40
116
|
}
|
|
41
|
-
throw new Error(`Application did not start within ${
|
|
117
|
+
throw new Error(`Application did not start within ${timeout / 1000}s. Check your config.launch command.`);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Installs a global guard that swallows async socket errors leaking from
|
|
121
|
+
* chrome-remote-interface / Node http during CDP probing (ECONNRESET on a
|
|
122
|
+
* closing keep-alive socket, ECONNREFUSED races, EPIPE). Anything else is
|
|
123
|
+
* re-thrown so we don't hide real bugs.
|
|
124
|
+
*/
|
|
125
|
+
export function installCDPErrorGuard() {
|
|
126
|
+
const isCDPProbeError = (err) => {
|
|
127
|
+
if (!err || typeof err !== 'object')
|
|
128
|
+
return false;
|
|
129
|
+
const code = err.code;
|
|
130
|
+
if (code === 'ECONNRESET' || code === 'ECONNREFUSED' || code === 'EPIPE')
|
|
131
|
+
return true;
|
|
132
|
+
const message = err.message ?? '';
|
|
133
|
+
return /ECONNRESET|ECONNREFUSED|EPIPE/.test(message);
|
|
134
|
+
};
|
|
135
|
+
process.on('uncaughtException', (err) => {
|
|
136
|
+
if (isCDPProbeError(err)) {
|
|
137
|
+
if (process.env.AV_DEBUG_CONSOLE) {
|
|
138
|
+
// eslint-disable-next-line no-console
|
|
139
|
+
console.error('[av-debug] suppressed CDP probe error:', err.message);
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
throw err;
|
|
144
|
+
});
|
|
145
|
+
process.on('unhandledRejection', (reason) => {
|
|
146
|
+
if (isCDPProbeError(reason)) {
|
|
147
|
+
if (process.env.AV_DEBUG_CONSOLE) {
|
|
148
|
+
// eslint-disable-next-line no-console
|
|
149
|
+
console.error('[av-debug] suppressed CDP probe rejection:', String(reason));
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
throw reason;
|
|
154
|
+
});
|
|
42
155
|
}
|
|
43
156
|
//# sourceMappingURL=launcher.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launcher.js","sourceRoot":"","sources":["../../src/server/launcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"launcher.js","sourceRoot":"","sources":["../../src/server/launcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"AAaA,OAAO,EAKL,KAAK,WAAW,EAEhB,KAAK,UAAU,EAGhB,MAAM,iBAAiB,CAAA;AAmDxB,KAAK,YAAY,GACb;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtC,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAIzG;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6B5F;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAaxD;AAMD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,aAAa,CAA0B;IAE/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAe2D;IAEpF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA0C;IAC5E,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2F;IAEnH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"AAaA,OAAO,EAKL,KAAK,WAAW,EAEhB,KAAK,UAAU,EAGhB,MAAM,iBAAiB,CAAA;AAmDxB,KAAK,YAAY,GACb;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtC,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAIzG;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6B5F;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAaxD;AAMD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,aAAa,CAA0B;IAE/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAe2D;IAEpF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA0C;IAC5E,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2F;IAEnH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB5B,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,YAAY;YAmBN,cAAc;YAwCd,aAAa;YAMb,aAAa;YA0Bb,cAAc;YAad,iBAAiB;YAgBjB,cAAc;YASd,YAAY;YA+BZ,SAAS;YAmDT,YAAY;YAqGZ,WAAW;YAyCX,UAAU;YAwBV,gBAAgB;YA0BhB,UAAU;YAqCV,UAAU;YA+BV,uBAAuB;YAYvB,gBAAgB;YAuBhB,WAAW;YA2CX,UAAU;YAsCV,aAAa;YASb,aAAa;YA2Bb,UAAU;YAwCV,oBAAoB;YAsFpB,aAAa;YA2Hb,UAAU;YAKV,QAAQ;CAgBvB;AAID,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAOjG"}
|
package/dist/server/server.js
CHANGED
|
@@ -8,9 +8,9 @@ import { getAdapter } from '../adapters/registry.js';
|
|
|
8
8
|
import { formatAccessibilityTree, countAccessibilityNodes, diffDomText } from '../inspectors/dom/index.js';
|
|
9
9
|
import { getSceneGraph, getRawScene, diffScenes } from '../inspectors/scene/index.js';
|
|
10
10
|
import { RefStore } from './ref-store.js';
|
|
11
|
-
import { launch, isRunning } from './launcher.js';
|
|
11
|
+
import { launch, isRunning, installCDPErrorGuard, PortConflictError } from './launcher.js';
|
|
12
12
|
import { readConfig } from '../config/manager.js';
|
|
13
|
-
import { RuntimeType, WebGLEngine } from '../types.js';
|
|
13
|
+
import { RuntimeType, WebGLEngine, ServerErrorCode } from '../types.js';
|
|
14
14
|
import { TargetType, ConsoleLevel, EvaluationError, MouseButton, } from '../cdp/types.js';
|
|
15
15
|
import { listSupportedTargets, connectToRuntime } from '../cdp/transport.js';
|
|
16
16
|
import { ConsoleStream } from '../cdp/_tests/console-stream.js';
|
|
@@ -134,6 +134,7 @@ export class AgentViewServer {
|
|
|
134
134
|
streamingCommands = new Set(['watch']);
|
|
135
135
|
validCommands = new Set([...Object.keys(this.handlers), ...this.streamingCommands]);
|
|
136
136
|
async start() {
|
|
137
|
+
installCDPErrorGuard();
|
|
137
138
|
mkdirSync(TOKEN_DIR, { recursive: true });
|
|
138
139
|
this.token = randomBytes(32).toString('hex');
|
|
139
140
|
await writeFile(TOKEN_PATH, this.token, { mode: 0o600 });
|
|
@@ -288,7 +289,20 @@ export class AgentViewServer {
|
|
|
288
289
|
if (await isRunning(req.port)) {
|
|
289
290
|
return { ok: true, data: 'Application already running' };
|
|
290
291
|
}
|
|
291
|
-
|
|
292
|
+
try {
|
|
293
|
+
await launch(launchCmd, req.port, cwd, req.runtime);
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
if (err instanceof PortConflictError) {
|
|
297
|
+
return {
|
|
298
|
+
ok: false,
|
|
299
|
+
error: err.message,
|
|
300
|
+
code: ServerErrorCode.PortConflict,
|
|
301
|
+
data: err.conflict,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
throw err;
|
|
305
|
+
}
|
|
292
306
|
return { ok: true, data: 'Application launched and ready' };
|
|
293
307
|
}
|
|
294
308
|
async handleDom(req) {
|