@sightmap/agent-browser 0.9.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/LICENSE +21 -0
- package/README.md +138 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +395 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fullstory, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# @sightmap/agent-browser
|
|
2
|
+
|
|
3
|
+
Sightmap-aware wrapper around Vercel's [`agent-browser`](https://agent-browser.dev) CLI. Adds four sightmap-aware commands (`snapshot`, `match`, `act`, `network`) that consume your `.sightmap/` corpus; all other commands forward verbatim to `agent-browser` — including the `-p <provider>` flag for hosted Chrome providers.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
pnpm add -D @sightmap/agent-browser agent-browser
|
|
8
|
+
agent-browser install # downloads Chrome on first run
|
|
9
|
+
|
|
10
|
+
`agent-browser` is a peer dependency. `sightmap-agent-browser` shells out to it for browser primitives, so it has to be on `PATH` (or installed in the same `node_modules`).
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
sightmap-agent-browser [global flags] <command> [args...]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Global flags
|
|
19
|
+
|
|
20
|
+
| Flag | Default | Meaning |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| `-s`, `--session NAME` | `default` | Named agent-browser session |
|
|
23
|
+
| `--sightmap-dir DIR` | `.sightmap` | Path to `.sightmap/` directory |
|
|
24
|
+
| `--json` | `false` | Emit JSON instead of human-readable text |
|
|
25
|
+
|
|
26
|
+
The `.sightmap/` directory is loaded **per invocation** from `--sightmap-dir`, resolved against the current working directory. There is no daemon and no cache — every command re-reads the corpus, so edits to `.sightmap/` are picked up immediately.
|
|
27
|
+
|
|
28
|
+
## Sightmap-specific commands
|
|
29
|
+
|
|
30
|
+
Four commands consume `.sightmap/` and produce enriched output. Everything else falls through to `agent-browser`.
|
|
31
|
+
|
|
32
|
+
### `snapshot` — enriched a11y snapshot
|
|
33
|
+
|
|
34
|
+
Captures an ARIA snapshot from the current page and annotates it with the matched sightmap view, view memory, and the sightmap components present on the page.
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
$ sightmap-agent-browser -s demo snapshot
|
|
38
|
+
View: Example (/**)
|
|
39
|
+
memory:
|
|
40
|
+
- IANA-reserved domain used for documentation examples
|
|
41
|
+
Components:
|
|
42
|
+
- Heading (view-scoped) — 1 match
|
|
43
|
+
|
|
44
|
+
--- ARIA snapshot ---
|
|
45
|
+
- heading "Example Domain" [level=1, ref=e1]
|
|
46
|
+
- paragraph
|
|
47
|
+
- link "Learn more" [ref=e2]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Pass `--json` for a structured object including an `ariaSnapshot` field carrying the raw a11y text (parity with `@sightmap/mcp`).
|
|
51
|
+
|
|
52
|
+
### `match URL` — show the matching sightmap view
|
|
53
|
+
|
|
54
|
+
Pure query: resolves a URL (or pathname) against the loaded sightmap and prints the matched view, applicable components, and known requests. No browser is launched.
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
$ sightmap-agent-browser match /list/abc123
|
|
58
|
+
View: ListDetailScreen (route /list/*)
|
|
59
|
+
memory: id is opaque; do not parse
|
|
60
|
+
Components:
|
|
61
|
+
- BottomTabBar (global)
|
|
62
|
+
- ListCard (view)
|
|
63
|
+
Requests:
|
|
64
|
+
- GET /api/list/:id → fetchList
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### `act NAME` — resolve a component name to a selector
|
|
68
|
+
|
|
69
|
+
Resolves a sightmap component name to its primary CSS selector (plus any fallbacks). Prints the selector to stdout so it can be piped into another `agent-browser` command. Exits non-zero with a reason for unknown components.
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
$ sightmap-agent-browser act SubmitButton
|
|
73
|
+
[data-testid="submit"]
|
|
74
|
+
# fallbacks: button[type="submit"]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Pass `--json` for the full resolution result.
|
|
78
|
+
|
|
79
|
+
### `network` — annotated network requests
|
|
80
|
+
|
|
81
|
+
Pulls recent network requests from the current session and tags any that match a `requests:` entry in the sightmap, attaching request memory.
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
$ sightmap-agent-browser network
|
|
85
|
+
[GET] https://example.test/api/list/abc → 200 [fetchList]
|
|
86
|
+
memory: returns 404 for archived lists; treat as "not found"
|
|
87
|
+
[POST] https://example.test/api/track → 204
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Passthrough
|
|
91
|
+
|
|
92
|
+
Any command not listed above forwards to `agent-browser` verbatim, with `--session NAME` prepended. This makes `sightmap-agent-browser` a drop-in upgrade for `agent-browser`:
|
|
93
|
+
|
|
94
|
+
sightmap-agent-browser open https://example.com
|
|
95
|
+
sightmap-agent-browser click @e3
|
|
96
|
+
sightmap-agent-browser screenshot page.png
|
|
97
|
+
|
|
98
|
+
You only opt into the sightmap-aware behavior on the four commands above; everything else still works exactly as `agent-browser` documents it.
|
|
99
|
+
|
|
100
|
+
## Hosted browser providers
|
|
101
|
+
|
|
102
|
+
`agent-browser`'s `-p <provider>` flag flows through unchanged. The provider binds to the session on `open`, and all subsequent sightmap-aware commands inherit it:
|
|
103
|
+
|
|
104
|
+
sightmap-agent-browser -p browserless -s demo open https://example.com
|
|
105
|
+
sightmap-agent-browser -s demo snapshot # runs against the browserless-hosted browser
|
|
106
|
+
|
|
107
|
+
Sightmap layers above whichever provider you pick (Browserless, Browserbase, Kernel, …) — no extra wrapper code needed, no extra cost.
|
|
108
|
+
|
|
109
|
+
## Sessions
|
|
110
|
+
|
|
111
|
+
`agent-browser` uses **named sessions** to keep a browser context alive across invocations. `sightmap-agent-browser` honors the same flag:
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
sightmap-agent-browser -s checkout open https://shop.test
|
|
115
|
+
sightmap-agent-browser -s checkout snapshot
|
|
116
|
+
sightmap-agent-browser -s checkout network
|
|
117
|
+
sightmap-agent-browser -s checkout close
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
If you don't pass `-s`/`--session`, the session is `"default"`. Run parallel flows by giving them distinct session names.
|
|
121
|
+
|
|
122
|
+
## `.sightmap/` resolution
|
|
123
|
+
|
|
124
|
+
Resolved per-invocation, from CWD by default. Override with `--sightmap-dir` when invoking from outside the project root:
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
sightmap-agent-browser --sightmap-dir /path/to/repo/.sightmap snapshot
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Integration tests
|
|
131
|
+
|
|
132
|
+
Set `SIGHTMAP_AB_INTEGRATION=1` to enable the real-browser smoke test in this package's `test/integration/`. The smoke test runs `open` → `snapshot` → `close` against `https://example.com` and verifies enrichment fires end-to-end.
|
|
133
|
+
|
|
134
|
+
## See also
|
|
135
|
+
|
|
136
|
+
- [`@sightmap/playwright`](../playwright) — sister wrapper for `@playwright/cli`. Same four enriched commands, different browser backend.
|
|
137
|
+
- [`@sightmap/mcp`](../mcp) — the MCP-server alternative with the curation tool family.
|
|
138
|
+
- [`@sightmap/sightmap`](../sightmap) — the underlying matcher, lint, and validate library.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/commands/act.ts
|
|
4
|
+
import {
|
|
5
|
+
loadDirectory,
|
|
6
|
+
resolveSightmapAct
|
|
7
|
+
} from "@sightmap/sightmap";
|
|
8
|
+
async function runActCommand(opts) {
|
|
9
|
+
const sightmap = await loadDirectory(opts.sightmapDir);
|
|
10
|
+
const r = resolveSightmapAct(sightmap, { componentName: opts.componentName });
|
|
11
|
+
if (r.kind === "error") {
|
|
12
|
+
process.stderr.write(r.message + "\n");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (opts.json) {
|
|
17
|
+
process.stdout.write(JSON.stringify(r, null, 2) + "\n");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
process.stdout.write(`${r.selector}
|
|
21
|
+
`);
|
|
22
|
+
if (r.allSelectors.length > 1) {
|
|
23
|
+
process.stdout.write(`# fallbacks: ${r.allSelectors.slice(1).join(", ")}
|
|
24
|
+
`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/commands/match.ts
|
|
29
|
+
import { loadDirectory as loadDirectory2, match } from "@sightmap/sightmap";
|
|
30
|
+
async function runMatchCommand(opts) {
|
|
31
|
+
const sightmap = await loadDirectory2(opts.sightmapDir);
|
|
32
|
+
const result = match(sightmap, { url: opts.url });
|
|
33
|
+
if (opts.json) {
|
|
34
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const lines = [];
|
|
38
|
+
if (result.view) {
|
|
39
|
+
lines.push(`View: ${result.view.name} (${result.view.route})`);
|
|
40
|
+
for (const m of result.view.memory ?? []) lines.push(` memory: ${m}`);
|
|
41
|
+
} else {
|
|
42
|
+
lines.push(`View: (no match)`);
|
|
43
|
+
}
|
|
44
|
+
if (result.components.length > 0) {
|
|
45
|
+
lines.push(`Components:`);
|
|
46
|
+
for (const c of result.components) {
|
|
47
|
+
lines.push(` - ${c.name} (${c.scope})`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (result.requests.length > 0) {
|
|
51
|
+
lines.push(`Requests:`);
|
|
52
|
+
for (const r of result.requests) {
|
|
53
|
+
lines.push(` - ${r.method ?? "ANY"} ${r.route} \u2192 ${r.name}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/commands/network.ts
|
|
60
|
+
import {
|
|
61
|
+
loadDirectory as loadDirectory3,
|
|
62
|
+
annotateNetworkRequests
|
|
63
|
+
} from "@sightmap/sightmap";
|
|
64
|
+
|
|
65
|
+
// src/exec.ts
|
|
66
|
+
import { spawn } from "child_process";
|
|
67
|
+
async function execAgentBrowser(args, options = {}) {
|
|
68
|
+
const binary = options.binary ?? "agent-browser";
|
|
69
|
+
const timeoutMs = options.timeoutMs ?? 6e4;
|
|
70
|
+
const wantsStdin = options.stdinInput !== void 0;
|
|
71
|
+
return await new Promise((resolve, reject) => {
|
|
72
|
+
const child = spawn(binary, args, {
|
|
73
|
+
cwd: options.cwd ?? process.cwd(),
|
|
74
|
+
env: options.env ?? process.env,
|
|
75
|
+
stdio: [wantsStdin ? "pipe" : "ignore", "pipe", "pipe"]
|
|
76
|
+
});
|
|
77
|
+
const stdoutChunks = [];
|
|
78
|
+
const stderrChunks = [];
|
|
79
|
+
child.stdout.on("data", (c) => stdoutChunks.push(c));
|
|
80
|
+
child.stderr.on("data", (c) => stderrChunks.push(c));
|
|
81
|
+
const killTimer = setTimeout(() => {
|
|
82
|
+
child.kill("SIGTERM");
|
|
83
|
+
}, timeoutMs);
|
|
84
|
+
child.on("error", (err) => {
|
|
85
|
+
clearTimeout(killTimer);
|
|
86
|
+
reject(err);
|
|
87
|
+
});
|
|
88
|
+
child.on("close", (code) => {
|
|
89
|
+
clearTimeout(killTimer);
|
|
90
|
+
resolve({
|
|
91
|
+
exitCode: code ?? -1,
|
|
92
|
+
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
93
|
+
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
if (wantsStdin && child.stdin) {
|
|
97
|
+
child.stdin.write(options.stdinInput);
|
|
98
|
+
child.stdin.end();
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async function streamAgentBrowser(args, options = {}) {
|
|
103
|
+
const binary = options.binary ?? "agent-browser";
|
|
104
|
+
return await new Promise((resolve, reject) => {
|
|
105
|
+
const child = spawn(binary, args, {
|
|
106
|
+
cwd: options.cwd ?? process.cwd(),
|
|
107
|
+
env: options.env ?? process.env,
|
|
108
|
+
stdio: "inherit"
|
|
109
|
+
});
|
|
110
|
+
child.on("error", reject);
|
|
111
|
+
child.on("close", (code) => {
|
|
112
|
+
resolve({ exitCode: code ?? -1 });
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/parse-network.ts
|
|
118
|
+
function parseAgentBrowserNetwork(text) {
|
|
119
|
+
const env = JSON.parse(text);
|
|
120
|
+
if (env.success !== true) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`agent-browser network --json: ${env.error ?? "unknown error"}`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
const data = env.data ?? [];
|
|
126
|
+
const entries = Array.isArray(data) ? data : Array.isArray(data.requests) ? data.requests : [];
|
|
127
|
+
return entries.map((e) => ({
|
|
128
|
+
method: e.method ?? "",
|
|
129
|
+
url: e.url ?? "",
|
|
130
|
+
status: typeof e.status === "number" ? e.status : 0,
|
|
131
|
+
statusText: e.statusText ?? ""
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/commands/network.ts
|
|
136
|
+
async function runNetworkCommand(opts) {
|
|
137
|
+
const sightmap = await loadDirectory3(opts.sightmapDir);
|
|
138
|
+
const result = await execAgentBrowser([
|
|
139
|
+
"--session",
|
|
140
|
+
opts.sessionName,
|
|
141
|
+
"network",
|
|
142
|
+
"requests",
|
|
143
|
+
"--json"
|
|
144
|
+
]);
|
|
145
|
+
if (result.exitCode !== 0) {
|
|
146
|
+
process.stderr.write(result.stderr);
|
|
147
|
+
process.exit(result.exitCode);
|
|
148
|
+
}
|
|
149
|
+
const parsed = parseAgentBrowserNetwork(result.stdout);
|
|
150
|
+
const annotated = annotateNetworkRequests(sightmap, parsed);
|
|
151
|
+
if (opts.json) {
|
|
152
|
+
process.stdout.write(JSON.stringify(annotated, null, 2) + "\n");
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
for (const r of annotated) {
|
|
156
|
+
const tag = r.sightmapName ? ` [${r.sightmapName}]` : "";
|
|
157
|
+
process.stdout.write(`[${r.method}] ${r.url} \u2192 ${r.status}${tag}
|
|
158
|
+
`);
|
|
159
|
+
if (r.sightmapMemory) {
|
|
160
|
+
for (const m of r.sightmapMemory) {
|
|
161
|
+
process.stdout.write(` memory: ${m}
|
|
162
|
+
`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/commands/passthrough.ts
|
|
169
|
+
async function runPassthroughCommand(opts) {
|
|
170
|
+
const { exitCode } = await streamAgentBrowser([
|
|
171
|
+
"--session",
|
|
172
|
+
opts.sessionName,
|
|
173
|
+
...opts.rest
|
|
174
|
+
]);
|
|
175
|
+
if (exitCode !== 0) process.exit(exitCode);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/commands/snapshot.ts
|
|
179
|
+
import {
|
|
180
|
+
enrichSnapshot,
|
|
181
|
+
loadDirectory as loadDirectory4,
|
|
182
|
+
match as sightmapMatch
|
|
183
|
+
} from "@sightmap/sightmap";
|
|
184
|
+
|
|
185
|
+
// src/parse-snapshot.ts
|
|
186
|
+
function parseAgentBrowserSnapshot(text) {
|
|
187
|
+
const env = JSON.parse(text);
|
|
188
|
+
if (env.success !== true) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
`agent-browser snapshot --json: ${env.error ?? "unknown error"}`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
const data = env.data ?? {};
|
|
194
|
+
return {
|
|
195
|
+
url: data.origin ?? "",
|
|
196
|
+
ariaText: data.snapshot ?? "",
|
|
197
|
+
refs: data.refs ?? {}
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/in-page-runner.ts
|
|
202
|
+
import {
|
|
203
|
+
buildInPageEvalFunction
|
|
204
|
+
} from "@sightmap/sightmap";
|
|
205
|
+
function parseAgentBrowserEvalEnvelope(stdout) {
|
|
206
|
+
const env = JSON.parse(stdout);
|
|
207
|
+
if (env.success !== true) {
|
|
208
|
+
throw new Error(
|
|
209
|
+
`agent-browser eval --json: ${env.error ?? "unknown error"}`
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
const matches = env.data?.result?.matches;
|
|
213
|
+
return Array.isArray(matches) ? matches : [];
|
|
214
|
+
}
|
|
215
|
+
async function runInPageMatcher(components, sessionName, options = {}) {
|
|
216
|
+
if (components.length === 0) return [];
|
|
217
|
+
const js = buildInPageEvalFunction(components);
|
|
218
|
+
const iife = `(${js})()`;
|
|
219
|
+
const result = await execAgentBrowser(
|
|
220
|
+
["--session", sessionName, "eval", "--stdin", "--json"],
|
|
221
|
+
{ ...options, stdinInput: iife }
|
|
222
|
+
);
|
|
223
|
+
if (result.exitCode !== 0) {
|
|
224
|
+
throw new Error(`agent-browser eval failed: ${result.stderr}`);
|
|
225
|
+
}
|
|
226
|
+
return parseAgentBrowserEvalEnvelope(result.stdout);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/commands/snapshot.ts
|
|
230
|
+
async function runSnapshotCommand(opts) {
|
|
231
|
+
const sightmap = await loadDirectory4(opts.sightmapDir);
|
|
232
|
+
const snapResult = await execAgentBrowser([
|
|
233
|
+
"--session",
|
|
234
|
+
opts.sessionName,
|
|
235
|
+
"snapshot",
|
|
236
|
+
"--json"
|
|
237
|
+
]);
|
|
238
|
+
if (snapResult.exitCode !== 0) {
|
|
239
|
+
process.stderr.write(snapResult.stderr);
|
|
240
|
+
process.exit(snapResult.exitCode);
|
|
241
|
+
}
|
|
242
|
+
const parsed = parseAgentBrowserSnapshot(snapResult.stdout);
|
|
243
|
+
const matchResult = sightmapMatch(sightmap, { url: parsed.url });
|
|
244
|
+
const allComponents = matchResult.components.map((c) => ({
|
|
245
|
+
name: c.name,
|
|
246
|
+
selector: c.selector
|
|
247
|
+
}));
|
|
248
|
+
const inPage = await runInPageMatcher(allComponents, opts.sessionName);
|
|
249
|
+
const enriched = enrichSnapshot({
|
|
250
|
+
sightmap,
|
|
251
|
+
currentUrl: parsed.url,
|
|
252
|
+
inPageMatches: inPage
|
|
253
|
+
});
|
|
254
|
+
if (opts.json) {
|
|
255
|
+
process.stdout.write(
|
|
256
|
+
JSON.stringify(
|
|
257
|
+
{ ...enriched, ariaSnapshot: parsed.ariaText },
|
|
258
|
+
null,
|
|
259
|
+
2
|
|
260
|
+
) + "\n"
|
|
261
|
+
);
|
|
262
|
+
} else {
|
|
263
|
+
process.stdout.write(formatHuman(enriched, parsed.ariaText));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function formatHuman(enriched, ariaText) {
|
|
267
|
+
const lines = [];
|
|
268
|
+
if (enriched.view) {
|
|
269
|
+
lines.push(`View: ${enriched.view.name} (${enriched.view.route})`);
|
|
270
|
+
if (enriched.view.memory.length > 0) {
|
|
271
|
+
lines.push(` memory:`);
|
|
272
|
+
for (const m of enriched.view.memory) lines.push(` - ${m}`);
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
lines.push(`View: (no match)`);
|
|
276
|
+
}
|
|
277
|
+
if (enriched.components.length > 0) {
|
|
278
|
+
lines.push(`Components:`);
|
|
279
|
+
for (const c of enriched.components) {
|
|
280
|
+
lines.push(
|
|
281
|
+
` - ${c.name} (${c.scope}) \u2014 ${c.matchCount} match${c.matchCount === 1 ? "" : "es"}`
|
|
282
|
+
);
|
|
283
|
+
for (const m of c.memory) lines.push(` memory: ${m}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
lines.push("");
|
|
287
|
+
lines.push("--- ARIA snapshot ---");
|
|
288
|
+
lines.push(ariaText);
|
|
289
|
+
return lines.join("\n");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/cli.ts
|
|
293
|
+
function requireValue(flag, next) {
|
|
294
|
+
if (next === void 0 || next.startsWith("-")) {
|
|
295
|
+
console.error(
|
|
296
|
+
`${flag}: expected a value, got "${next ?? "(end of args)"}"`
|
|
297
|
+
);
|
|
298
|
+
process.exit(2);
|
|
299
|
+
}
|
|
300
|
+
return next;
|
|
301
|
+
}
|
|
302
|
+
function parseArgs(argv) {
|
|
303
|
+
let session = "default";
|
|
304
|
+
let sightmapDir = ".sightmap";
|
|
305
|
+
let json = false;
|
|
306
|
+
const positional = [];
|
|
307
|
+
for (let i = 0; i < argv.length; i++) {
|
|
308
|
+
const a = argv[i];
|
|
309
|
+
if (a === "-s" || a === "--session") {
|
|
310
|
+
session = requireValue(a, argv[++i]);
|
|
311
|
+
} else if (a === "--sightmap-dir") {
|
|
312
|
+
sightmapDir = requireValue(a, argv[++i]);
|
|
313
|
+
} else if (a === "--json") {
|
|
314
|
+
json = true;
|
|
315
|
+
} else {
|
|
316
|
+
positional.push(a);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return {
|
|
320
|
+
command: positional[0] ?? "",
|
|
321
|
+
session,
|
|
322
|
+
sightmapDir,
|
|
323
|
+
json,
|
|
324
|
+
rest: positional.slice(1)
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
function printHelp() {
|
|
328
|
+
console.log(
|
|
329
|
+
`sightmap-agent-browser \u2014 sightmap-aware wrapper around Vercel's agent-browser CLI
|
|
330
|
+
|
|
331
|
+
Usage:
|
|
332
|
+
sightmap-agent-browser [global flags] <command> [args...]
|
|
333
|
+
|
|
334
|
+
Global flags:
|
|
335
|
+
-s, --session NAME Named agent-browser session (default: "default")
|
|
336
|
+
--sightmap-dir DIR Path to .sightmap/ directory (default: ".sightmap")
|
|
337
|
+
--json Emit JSON instead of human-readable text
|
|
338
|
+
|
|
339
|
+
Sightmap-specific commands:
|
|
340
|
+
snapshot Enriched a11y snapshot with sightmap component names
|
|
341
|
+
match URL Show the matching sightmap view for URL
|
|
342
|
+
act NAME Resolve a sightmap component name to a selector
|
|
343
|
+
network Network requests annotated with sightmap memory
|
|
344
|
+
|
|
345
|
+
All other commands are forwarded to agent-browser verbatim, including
|
|
346
|
+
the -p <provider> flag for hosted browser providers.
|
|
347
|
+
`
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
async function main() {
|
|
351
|
+
const argv = process.argv.slice(2);
|
|
352
|
+
if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
|
|
353
|
+
printHelp();
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const { command, session, sightmapDir, json, rest } = parseArgs(argv);
|
|
357
|
+
switch (command) {
|
|
358
|
+
case "snapshot":
|
|
359
|
+
await runSnapshotCommand({ sessionName: session, sightmapDir, json });
|
|
360
|
+
return;
|
|
361
|
+
case "match": {
|
|
362
|
+
const url = rest[0];
|
|
363
|
+
if (url === void 0) {
|
|
364
|
+
console.error("sightmap-agent-browser match: URL required");
|
|
365
|
+
process.exit(2);
|
|
366
|
+
}
|
|
367
|
+
await runMatchCommand({ url, sightmapDir, json });
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
case "act": {
|
|
371
|
+
const name = rest[0];
|
|
372
|
+
if (name === void 0) {
|
|
373
|
+
console.error("sightmap-agent-browser act: component name required");
|
|
374
|
+
process.exit(2);
|
|
375
|
+
}
|
|
376
|
+
await runActCommand({ componentName: name, sightmapDir, json });
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
case "network":
|
|
380
|
+
await runNetworkCommand({ sessionName: session, sightmapDir, json });
|
|
381
|
+
return;
|
|
382
|
+
default: {
|
|
383
|
+
const passArgs = json ? ["--json", command, ...rest] : [command, ...rest];
|
|
384
|
+
await runPassthroughCommand({
|
|
385
|
+
sessionName: session,
|
|
386
|
+
rest: passArgs.filter((s) => s.length > 0)
|
|
387
|
+
});
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
main().catch((err) => {
|
|
393
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
394
|
+
process.exit(1);
|
|
395
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sightmap/agent-browser",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Sightmap-aware wrapper around Vercel's agent-browser CLI. Adds sightmap-* commands and enriches snapshot output with component names from .sightmap/.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/sightmap/sightmap-js.git",
|
|
9
|
+
"directory": "packages/agent-browser"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://sightmap.org",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"sightmap",
|
|
14
|
+
"agent-browser",
|
|
15
|
+
"cli",
|
|
16
|
+
"agent",
|
|
17
|
+
"browser"
|
|
18
|
+
],
|
|
19
|
+
"type": "module",
|
|
20
|
+
"bin": {
|
|
21
|
+
"sightmap-agent-browser": "./dist/cli.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"agent-browser": "^0.27.0",
|
|
33
|
+
"@types/node": "^20.19.39",
|
|
34
|
+
"tsup": "^8.5.1",
|
|
35
|
+
"typescript": "~5.7.3",
|
|
36
|
+
"vitest": "^3.0.0"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@sightmap/sightmap": "^0.9.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"agent-browser": "^0.27.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependenciesMeta": {
|
|
45
|
+
"agent-browser": {
|
|
46
|
+
"optional": false
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsup",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"test:watch": "vitest",
|
|
53
|
+
"typecheck": "tsc --noEmit"
|
|
54
|
+
}
|
|
55
|
+
}
|