@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.
@@ -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 (verify, verify-recipe) and two Haiku-tier subagents (verify-runner, design-conformance-runner) so recipe execution and visual mockup-conformance run cheaply outside the main agent's context.",
4
- "version": "0.8.0",
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 `design-conformance-runner` subagent reports visual deviations
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 Haiku-cost recipe execution and visual conformance against design mockups,
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
- ### Phase 1 — Author the verification plan (Opus / Sonnet)
197
+ ```mermaid
198
+ flowchart TD
199
+ Dev["Developer<br/>in Claude Code"] -->|prompt| Agent["Main Claude agent<br/>(Opus / Sonnet)"]
201
200
 
202
- Generate the recipe **once**, from a PRD / plan file / Jira ticket / commit range. The recipe is reusable — re-run after
203
- every iteration on the same feature.
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/&lt;slug&gt;.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 (delegated to Haiku)
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 resolves the window via `agent-view discover`, then spawns the `verify-runner`
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
- ### Phase 3 Iterate on failures (Opus / Sonnet)
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
- Three steps failed in the verify run. Fix step 4 (zone filter not mutating store)
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
- what "works" means. Give it the symptom that motivated the fix.
301
- - Pasting Figma URLs and expecting agent-view to download them it won't. Export the frames you care about to PNG
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":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAE5D,wBAAsB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBtE"}
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
- export async function runLaunch(config) {
3
- if (!config.launch) {
4
- console.error('No launch command configured. Add "launch" to agent-view.config.json.');
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;AAG1C,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,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC;QACjC,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;IAEF,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"}
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.5.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 launch(command: string, port: number, cwd?: string): Promise<void>;
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":"AAUA,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,CAG9D;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBvF"}
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"}
@@ -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
- const LAUNCH_TIMEOUT_MS = 60_000;
4
- const POLL_INTERVAL_MS = 1_000;
5
- // Commands that are .cmd scripts on Windows and need explicit extension
6
- // when spawned without shell
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
- const targets = await listTargets(port);
19
- return targets.length > 0;
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 < LAUNCH_TIMEOUT_MS) {
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 ${LAUNCH_TIMEOUT_MS / 1000}s. Check your config.launch command.`);
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;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEjD,MAAM,iBAAiB,GAAG,MAAM,CAAA;AAChC,MAAM,gBAAgB,GAAG,KAAK,CAAA;AAE9B,wEAAwE;AACxE,6BAA6B;AAC7B,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;AAExF,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,gCAAgC,CAAC,IAAI,EAAE,CAAA;IAC/D,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAA;IAC7D,IAAI,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IACpB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACjE,GAAG,GAAG,GAAG,GAAG,MAAM,CAAA;IACpB,CAAC;IACD,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY;IAC1C,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAA;IACvC,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,OAAe,EAAE,IAAY,EAAE,GAAY;IACtE,IAAI,MAAM,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,OAAM;IACR,CAAC;IAED,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IACzC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE;QAC7B,KAAK,EAAE,KAAK;QACZ,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,QAAQ;QACf,GAAG;QACH,WAAW,EAAE,IAAI;KAClB,CAAC,CAAA;IACF,KAAK,CAAC,KAAK,EAAE,CAAA;IAEb,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACxB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,iBAAiB,EAAE,CAAC;QAC9C,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAA;QACnE,IAAI,MAAM,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,OAAM;QACR,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,oCAAoC,iBAAiB,GAAG,IAAI,sCAAsC,CAAC,CAAA;AACrH,CAAC"}
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;IAe5B,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,YAAY;YAmBN,cAAc;YAwCd,aAAa;YAMb,aAAa;YA0Bb,cAAc;YAad,iBAAiB;YAgBjB,cAAc;YASd,YAAY;YAmBZ,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"}
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"}
@@ -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
- await launch(launchCmd, req.port, cwd);
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) {