botprint 0.2.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/PRIVACY.md +94 -0
- package/README.md +221 -0
- package/docs/agent-usage.md +109 -0
- package/package.json +56 -0
- package/src/cdp.ts +267 -0
- package/src/cli.ts +758 -0
- package/src/coerce.ts +19 -0
- package/src/compare.ts +363 -0
- package/src/errors.ts +15 -0
- package/src/exit-codes.ts +10 -0
- package/src/findings.ts +342 -0
- package/src/guide.ts +152 -0
- package/src/probe.ts +700 -0
- package/src/profile.ts +188 -0
- package/src/render.ts +268 -0
- package/src/run.ts +432 -0
- package/src/user-agent.ts +50 -0
- package/src/version.ts +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Botprint contributors
|
|
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/PRIVACY.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Privacy
|
|
2
|
+
|
|
3
|
+
Botprint is local-first diagnostic tooling.
|
|
4
|
+
|
|
5
|
+
Botprint does not upload profile data, reports, compare artifacts, auth headers, or browser metadata. Artifacts stay on disk unless you choose to share them.
|
|
6
|
+
|
|
7
|
+
## What Profiles Contain
|
|
8
|
+
|
|
9
|
+
Botprint profiles can contain browser-environment data, including:
|
|
10
|
+
|
|
11
|
+
- user agent and client hint brands, platform, and mobile flag
|
|
12
|
+
- platform, language, and languages
|
|
13
|
+
- timezone
|
|
14
|
+
- viewport, outer window, and screen metrics including color depth
|
|
15
|
+
- device scale factor, hardware concurrency, device memory, speech voice count
|
|
16
|
+
- plugin and MIME type counts, PDF viewer availability
|
|
17
|
+
- `navigator.webdriver`, `window.chrome` presence, known automation globals
|
|
18
|
+
- WebGL vendor and renderer, unmasked when available
|
|
19
|
+
- canvas and audio sample hashes
|
|
20
|
+
- which of a fixed list of common fonts are installed
|
|
21
|
+
- cookie, localStorage, sessionStorage, IndexedDB, and service worker availability
|
|
22
|
+
- selected Permissions API states and `Notification.permission`
|
|
23
|
+
- WebRTC API availability and host-candidate gathering presence (no STUN servers are contacted)
|
|
24
|
+
- request headers the browser sent to the local diagnostic page, from a fixed allowlist: Accept, Accept-Encoding, Accept-Language, DNT, Priority, Sec-CH-UA*, Sec-Fetch-*, Sec-GPC, Upgrade-Insecure-Requests, User-Agent. Cookies, Authorization, Host, and any other header are never recorded.
|
|
25
|
+
- CDP browser product and protocol version
|
|
26
|
+
- profile label, target URL, probe context, and browser context ID
|
|
27
|
+
|
|
28
|
+
Existing-target profiles may also include:
|
|
29
|
+
|
|
30
|
+
- CDP target ID
|
|
31
|
+
- target type
|
|
32
|
+
- a `page-contamination-possible` probe warning
|
|
33
|
+
|
|
34
|
+
These fields are useful for diagnostics, but they can reveal details about a browser environment. Review artifacts before sharing them publicly.
|
|
35
|
+
|
|
36
|
+
## Fresh Diagnostic Page
|
|
37
|
+
|
|
38
|
+
By default, Botprint creates a fresh local diagnostic page inside the browser context of the first open page target and runs the probe there. The diagnostic page is static HTML served from a local socket that exists only for the duration of the run. The page runs in the same context as your open pages but does not read them.
|
|
39
|
+
|
|
40
|
+
The diagnostic server binds to `127.0.0.1` by default.
|
|
41
|
+
|
|
42
|
+
For Docker or cross-namespace browser setups, users may explicitly choose:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
--diagnostic-bind 0.0.0.0
|
|
46
|
+
--diagnostic-host host.docker.internal
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Only use broader binding when the browser cannot reach the default localhost page.
|
|
50
|
+
|
|
51
|
+
## Existing Target Mode
|
|
52
|
+
|
|
53
|
+
Existing-target mode runs the probe inside an already-open CDP page target.
|
|
54
|
+
|
|
55
|
+
This is useful when you need to measure a specific page or framework context, but it is less isolated than the default fresh diagnostic page. Existing-target output may reflect:
|
|
56
|
+
|
|
57
|
+
- page scripts
|
|
58
|
+
- framework state
|
|
59
|
+
- service workers
|
|
60
|
+
- content security policy
|
|
61
|
+
- cookies
|
|
62
|
+
- storage state
|
|
63
|
+
- application state
|
|
64
|
+
- extensions or browser policy affecting that page
|
|
65
|
+
|
|
66
|
+
The probe performs short-lived browser API checks. It attempts to clean up storage keys it creates, but a page crash, timeout, or browser-level interruption could leave small local artifacts such as a temporary storage key or IndexedDB database name.
|
|
67
|
+
|
|
68
|
+
Do not run existing-target probes on sensitive logged-in pages unless you are comfortable with the page observing normal in-page JavaScript execution.
|
|
69
|
+
|
|
70
|
+
## Auth Headers
|
|
71
|
+
|
|
72
|
+
`--auth-header` values are used only for the CDP connection path.
|
|
73
|
+
|
|
74
|
+
Botprint does not persist auth-header values in profile artifacts.
|
|
75
|
+
|
|
76
|
+
If you quote commands in issues, pull requests, or agent prompts, redact header values:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
--auth-header "Authorization: Bearer <redacted>"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Sharing Checklist
|
|
83
|
+
|
|
84
|
+
Before sharing a profile or report publicly:
|
|
85
|
+
|
|
86
|
+
- confirm it does not include sensitive labels or target URLs
|
|
87
|
+
- review timezone and locale information
|
|
88
|
+
- review screen, hardware, and WebGL details
|
|
89
|
+
- avoid sharing auth headers or shell history
|
|
90
|
+
- prefer synthetic examples from `examples/synthetic` for public docs and tests
|
|
91
|
+
|
|
92
|
+
## No Telemetry
|
|
93
|
+
|
|
94
|
+
Botprint has no telemetry pipeline. It does not phone home.
|
package/README.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Botprint
|
|
2
|
+
|
|
3
|
+
Botprint shows what your automated browser exposes to the web, and what changed from your baseline.
|
|
4
|
+
|
|
5
|
+
It connects to a running Chromium-based browser over the Chrome DevTools Protocol (CDP), runs a probe in a page inside the browser context you are debugging, and writes a schema-versioned JSON profile. Profiles can be rendered as reports, compared against a baseline, and condensed into short explanations for coding agents.
|
|
6
|
+
|
|
7
|
+
Botprint is a diagnostic tool. It reports what the browser exposes, what differs between two environments, and which launch option or environment difference is most likely responsible. It does not ship spoofing presets or patches.
|
|
8
|
+
|
|
9
|
+
Typical questions it answers:
|
|
10
|
+
|
|
11
|
+
- Why does my Playwright or Puppeteer flow behave differently in Docker or CI than on my laptop?
|
|
12
|
+
- What does the site actually see from this browser agent: user agent, client hints, Accept-Language, timezone, fonts, GPU, storage, permissions?
|
|
13
|
+
- Is this difference real environment drift, or an artifact of how I measured it?
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
Release binaries (no runtime needed):
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
curl -fsSL https://github.com/oh-meh/botprint/releases/latest/download/botprint-linux-x64 -o botprint
|
|
21
|
+
chmod +x botprint && sudo mv botprint /usr/local/bin/
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Replace `linux-x64` with `linux-arm64`, `darwin-arm64`, `darwin-x64`, or `windows-x64.exe`.
|
|
25
|
+
|
|
26
|
+
With [Bun](https://bun.sh):
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bunx botprint help # run without installing
|
|
30
|
+
bun install -g botprint # or install globally
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
From source:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git clone https://github.com/oh-meh/botprint && cd botprint
|
|
37
|
+
bun install && bun run build # writes dist/botprint
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quickstart
|
|
41
|
+
|
|
42
|
+
Start the browser with a CDP port and collect a profile:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
botprint run --cdp http://127.0.0.1:9222 --out local.profile.json --label local
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Compare two environments:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
botprint compare local.profile.json docker.profile.json # Markdown summary
|
|
52
|
+
botprint compare local.profile.json docker.profile.json --json # full diff list
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Render a report or a compact explanation:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
botprint report docker.profile.json
|
|
59
|
+
botprint explain docker.profile.json --for-agent
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
A profile report looks like this (synthetic Docker example):
|
|
63
|
+
|
|
64
|
+
````markdown
|
|
65
|
+
# Botprint Profile: synthetic-docker
|
|
66
|
+
|
|
67
|
+
## Key Signals
|
|
68
|
+
- WebDriver: false; window.chrome: false
|
|
69
|
+
- WebGL: SwiftShader Device (Synthetic)
|
|
70
|
+
- Canvas: stable; audio: available
|
|
71
|
+
- Fonts: 0 of 31 common fonts detected
|
|
72
|
+
- Storage: cookies=true, indexedDB=error
|
|
73
|
+
- Accept-Language: (not sent)
|
|
74
|
+
|
|
75
|
+
## Top Findings
|
|
76
|
+
_Showing top 5 of 9 findings. Use `--json` for the full finding list._
|
|
77
|
+
- **IndexedDB is unavailable** (compatibility_risk): IndexedDB status is error.
|
|
78
|
+
- **Browser identifies itself as headless** (warning): User agent reports HeadlessChrome/145. Client hint brands include HeadlessChrome.
|
|
79
|
+
- **Software WebGL renderer detected** (warning): Renderer evidence: WebKit WebGL SwiftShader Device (Synthetic).
|
|
80
|
+
- **No common fonts detected** (warning): None of 31 common fonts rendered differently from the generic fallback families.
|
|
81
|
+
- **No Accept-Language header is sent** (warning): The browser's request for the diagnostic page carried no Accept-Language header.
|
|
82
|
+
````
|
|
83
|
+
|
|
84
|
+
And a compare:
|
|
85
|
+
|
|
86
|
+
````markdown
|
|
87
|
+
# Botprint Compare: synthetic-local -> synthetic-docker
|
|
88
|
+
|
|
89
|
+
## Summary
|
|
90
|
+
- Verdict: material
|
|
91
|
+
- Material: yes
|
|
92
|
+
- Differences: 28
|
|
93
|
+
- Categories: automation, cdp, environment, http, identity, permissions, rendering, storage
|
|
94
|
+
- Warnings: comparison.browser_binary_mismatch, comparison.headless_mode_mismatch
|
|
95
|
+
|
|
96
|
+
## Top Differences
|
|
97
|
+
_Showing top 5 of 28 differences. Use `--json` for the full diff list._
|
|
98
|
+
- **window.chrome presence changed** (material): signals.automation.chrome_object true -> false
|
|
99
|
+
- **Timezone changed** (material): signals.environment.timezone "America/New_York" -> "UTC"
|
|
100
|
+
- **WebGL renderer changed** (material): signals.rendering.webgl.unmasked_renderer "ANGLE Metal Renderer: Synthetic GPU" -> "SwiftShader Device (Synthetic)"
|
|
101
|
+
- **Detected font set changed** (material): signals.rendering.fonts.detected ["Arial","Courier New","Georgia","Helvetica","Helvetica Neue","Menlo","Monaco","Times New Roman","Verdana"] -> []
|
|
102
|
+
- **IndexedDB availability changed** (material): signals.storage.indexed_db "available" -> "error"
|
|
103
|
+
````
|
|
104
|
+
|
|
105
|
+
Full synthetic examples live in [`examples/synthetic`](examples/synthetic).
|
|
106
|
+
|
|
107
|
+
## What It Measures
|
|
108
|
+
|
|
109
|
+
| Category | Signals |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| `identity` | user agent, client hint brands/platform/mobile, platform, language and languages, plugin and MIME counts, PDF viewer |
|
|
112
|
+
| `automation` | `navigator.webdriver`, `window.chrome` presence, known automation globals |
|
|
113
|
+
| `environment` | timezone, viewport, outer window size, screen metrics and color depth, hardware concurrency, device memory, speech voice count |
|
|
114
|
+
| `rendering` | WebGL vendor/renderer (masked and unmasked), canvas hash, audio hash, detected common fonts |
|
|
115
|
+
| `storage` | cookies, localStorage, sessionStorage, IndexedDB, service worker |
|
|
116
|
+
| `permissions` | Permissions API states and `Notification.permission` |
|
|
117
|
+
| `webrtc` | API availability and local host-candidate gathering (no STUN) |
|
|
118
|
+
| `http` | request headers the browser sent for the diagnostic page: User-Agent, Accept-Language, Sec-CH-UA client hints, Sec-Fetch, Accept-Encoding |
|
|
119
|
+
| `cdp` | browser product and protocol version |
|
|
120
|
+
|
|
121
|
+
Findings are generated from these signals with stable IDs. Each finding names the launch option or environment difference that commonly causes it, for example `--enable-automation` for `navigator.webdriver`, or the headless shell binary for a missing `window.chrome`.
|
|
122
|
+
|
|
123
|
+
## Browser Contexts
|
|
124
|
+
|
|
125
|
+
Playwright and Puppeteer apply locale, timezone, viewport, permissions, and init scripts per browser context, not per browser. By default `botprint run` creates its fresh diagnostic page inside the browser context of the first open page target, so those options are measured. The profile records which context was used in `source.browser_context_id` and `source.browser_context_source`.
|
|
126
|
+
|
|
127
|
+
- `--context default` measures the browser's default context instead (process-level defaults only).
|
|
128
|
+
- `--context <id>` picks a specific context; IDs are listed by `botprint targets --cdp ... --json`.
|
|
129
|
+
- If no page is open, the fresh page falls back to the default context and the profile carries the `default-browser-context` note.
|
|
130
|
+
- The diagnostic page is a real page in that context for the length of the probe: Playwright's `context.on("page")` fires, init scripts and route handlers apply to it, and it is closed when the probe ends.
|
|
131
|
+
|
|
132
|
+
## Existing Pages
|
|
133
|
+
|
|
134
|
+
To measure an already-open page in place, including its scripts, service workers, CSP, and storage state:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
botprint targets --cdp http://127.0.0.1:9222 --json
|
|
138
|
+
botprint run --cdp http://127.0.0.1:9222 --target <targetId> --out site.profile.json
|
|
139
|
+
botprint run --cdp http://127.0.0.1:9222 --target-url https://app.example.test/ --out site.profile.json
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
These profiles carry the `page-contamination-possible` note, and HTTP headers are not captured in this mode.
|
|
143
|
+
|
|
144
|
+
## Docker
|
|
145
|
+
|
|
146
|
+
When the browser runs in a container, it needs a host-reachable diagnostic URL:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
botprint run --cdp http://127.0.0.1:9334 --out docker.profile.json --label docker \
|
|
150
|
+
--diagnostic-bind 0.0.0.0 --diagnostic-host host.docker.internal
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Omit both flags otherwise so the diagnostic page stays bound to `127.0.0.1`.
|
|
154
|
+
|
|
155
|
+
## Playwright
|
|
156
|
+
|
|
157
|
+
Expose CDP from the browser you want to measure:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
import { chromium } from "playwright";
|
|
161
|
+
|
|
162
|
+
const browser = await chromium.launch({ headless: true, args: ["--remote-debugging-port=9333"] });
|
|
163
|
+
const page = await browser.newPage();
|
|
164
|
+
await page.goto("about:blank");
|
|
165
|
+
// Keep the process alive while Botprint connects to http://127.0.0.1:9333.
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from playwright.sync_api import sync_playwright
|
|
170
|
+
|
|
171
|
+
with sync_playwright() as p:
|
|
172
|
+
browser = p.chromium.launch(headless=True, args=["--remote-debugging-port=9333"])
|
|
173
|
+
page = browser.new_context().new_page()
|
|
174
|
+
page.goto("about:blank")
|
|
175
|
+
input("Botprint can attach to http://127.0.0.1:9333. Press Enter to quit.")
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Use the headless mode and browser binary that the environment under investigation really runs. Playwright's default `chromium` is the headless shell; `channel: "chromium"` runs new headless mode. Both report `HeadlessChrome` in the user agent; only the shell also drops `window.chrome`, the PDF viewer plugins, and the outer window size.
|
|
179
|
+
|
|
180
|
+
## Commands
|
|
181
|
+
|
|
182
|
+
| Command | Purpose |
|
|
183
|
+
| --- | --- |
|
|
184
|
+
| `run` | Collect a profile from a CDP endpoint. |
|
|
185
|
+
| `targets` | List page targets and their browser context IDs. `--json` for scripts. |
|
|
186
|
+
| `report` | Render a Markdown report from a profile. |
|
|
187
|
+
| `compare` | Compare a baseline profile against a candidate. |
|
|
188
|
+
| `explain` | Render a compact explanation of a profile or compare result. |
|
|
189
|
+
| `guide` | Guided terminal flow over the same commands. Alias: `tui`. |
|
|
190
|
+
|
|
191
|
+
`botprint help <command>` lists flags. Notable `run` flags: `--probe-timeout <ms>` (per-check timeout inside the browser, default 2000), `--timeout <ms>` (CDP operations, default 10000), `--auth-header` (repeatable, for remote endpoints).
|
|
192
|
+
|
|
193
|
+
## Exit Codes
|
|
194
|
+
|
|
195
|
+
| Code | Meaning |
|
|
196
|
+
| --- | --- |
|
|
197
|
+
| `0` | Success, or compare found no material drift. |
|
|
198
|
+
| `1` | Compare found material or high-confidence automation drift. |
|
|
199
|
+
| `2` | Usage error. |
|
|
200
|
+
| `3` | CDP connectivity failure. |
|
|
201
|
+
| `4` | Probe failure. |
|
|
202
|
+
| `5` | Schema validation failure. |
|
|
203
|
+
|
|
204
|
+
## Output Contract
|
|
205
|
+
|
|
206
|
+
Profile JSON and compare JSON are the stable surfaces for scripts and agents. Markdown and `explain` output are prose. Profiles from an older `schema_version` fail validation with a hint to regenerate, so re-collect baselines with the Botprint version that compares them. The contract, finding IDs, warning IDs, and category glossary are documented in [`docs/agent-usage.md`](docs/agent-usage.md). Drop-in agent presets (a Claude Code skill and an `AGENTS.md` snippet) live in [`agent-presets/`](agent-presets).
|
|
207
|
+
|
|
208
|
+
## Validated On
|
|
209
|
+
|
|
210
|
+
- Linux x64: Chromium headless, Playwright Chromium headless shell (source run and compiled binary)
|
|
211
|
+
- macOS arm64: Google Chrome new headless (source run and compiled binary); headed Chrome, Playwright Chromium, and Docker Chrome via `chromedp/headless-shell` last checked on v0.1
|
|
212
|
+
|
|
213
|
+
Not yet validated: Windows binary, remote authenticated `wss://` CDP endpoints.
|
|
214
|
+
|
|
215
|
+
## Privacy
|
|
216
|
+
|
|
217
|
+
Botprint does not upload anything. Profiles stay on disk. See [`PRIVACY.md`](PRIVACY.md) for what they contain.
|
|
218
|
+
|
|
219
|
+
## Contributing and License
|
|
220
|
+
|
|
221
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md). MIT, see [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Agent Usage
|
|
2
|
+
|
|
3
|
+
This is the reference for coding agents (Claude Code, Codex, Cursor, browser-use, and similar) that run Botprint or interpret its output. The README covers installation and human workflows.
|
|
4
|
+
|
|
5
|
+
Botprint is a diagnostic tool: it reports what a browser exposes, what differs from a baseline, and which launch option or environment difference is likely responsible. Report findings as causes to investigate. Botprint does not ship spoofing presets or patches.
|
|
6
|
+
|
|
7
|
+
## When To Use
|
|
8
|
+
|
|
9
|
+
- A Playwright, Puppeteer, browser-use, Stagehand, or raw CDP flow behaves differently across local, Docker, CI, or remote browsers.
|
|
10
|
+
- The user wants to know what a site sees from a given browser: identity, headers, locale, fonts, GPU, storage, permissions.
|
|
11
|
+
- The user provides a Botprint profile, compare JSON, report, or `explain` output.
|
|
12
|
+
|
|
13
|
+
## Workflow
|
|
14
|
+
|
|
15
|
+
1. Expose CDP on the browser under investigation (`--remote-debugging-port=9222`).
|
|
16
|
+
2. Collect a profile per environment with `botprint run`. The fresh page is created inside the browser context of the first open page target, so framework context options are measured. Pass `--context default` to measure process defaults, or `--target`/`--target-url` to probe an open page in place.
|
|
17
|
+
3. If a known-good baseline exists, `botprint compare baseline.json candidate.json --json`.
|
|
18
|
+
4. Decide from JSON and exit codes. Use `report` and `explain --for-agent` as prose context.
|
|
19
|
+
5. Read `verdict.warnings[]` before interpreting diffs: they say when a difference is a measurement artifact rather than drift.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
botprint run --cdp http://127.0.0.1:9222 --out local.profile.json --label local
|
|
23
|
+
botprint targets --cdp http://127.0.0.1:9222 --json
|
|
24
|
+
botprint run --cdp http://127.0.0.1:9222 --target-url https://app.example.test/ --out site.profile.json --label site
|
|
25
|
+
botprint compare local.profile.json docker.profile.json --json
|
|
26
|
+
botprint explain compare.json --for-agent
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Docker browsers need `--diagnostic-bind 0.0.0.0 --diagnostic-host host.docker.internal`. Remote endpoints take `--auth-header "Authorization: Bearer <redacted>"` (repeatable). Redact header values whenever you quote a command.
|
|
30
|
+
|
|
31
|
+
## Stable Contract
|
|
32
|
+
|
|
33
|
+
Parse and rely on:
|
|
34
|
+
|
|
35
|
+
- profile JSON and compare JSON, keyed by `schema_version` (currently `0.3`)
|
|
36
|
+
- exit codes
|
|
37
|
+
- profile `source.probe_context`, `source.browser_context_id`, `source.browser_context_source`, `source.probe_warnings[]`, `source.target_id`, `source.target_type`
|
|
38
|
+
- profile `findings[]`: `id`, `severity`, `category`
|
|
39
|
+
- compare diff `id`, `level`, `category`, `path`, `baseline`, `candidate`; absent values are `{ "kind": "absent" }`
|
|
40
|
+
- compare `verdict.level`, `verdict.material`, `verdict.categories[]`, `verdict.warnings[].id`, `verdict.warnings[].evidence`
|
|
41
|
+
|
|
42
|
+
Treat as prose only: Markdown report bodies, `explain` wording, finding `title`/`evidence`/`explanation`/`likely_implications`/`suggested_investigation`, and warning `summary`/`implications`.
|
|
43
|
+
|
|
44
|
+
## Exit Codes
|
|
45
|
+
|
|
46
|
+
| Code | Meaning |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `0` | Success, or compare found no material drift. |
|
|
49
|
+
| `1` | Compare found material or high-confidence automation drift. Not a crash. |
|
|
50
|
+
| `2` | Usage error. |
|
|
51
|
+
| `3` | CDP connectivity failure. Check the endpoint and `--remote-debugging-port`. |
|
|
52
|
+
| `4` | Probe failure. The in-browser probe threw; the message includes the exception. |
|
|
53
|
+
| `5` | Schema validation failure. Regenerate the profile; do not hand-edit JSON. |
|
|
54
|
+
|
|
55
|
+
## Findings
|
|
56
|
+
|
|
57
|
+
Severity values: `high_confidence_automation_signal`, `compatibility_risk`, `warning`, `notable`, `normal`.
|
|
58
|
+
|
|
59
|
+
| ID | Severity | Common cause |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| `automation.webdriver_exposed` | high confidence | `--enable-automation`, passed by default by Puppeteer and Playwright, always by Selenium. |
|
|
62
|
+
| `automation.common_globals_present` | high confidence | ChromeDriver (`cdc_*`), legacy headless stacks. |
|
|
63
|
+
| `automation.chrome_object_missing` | notable | Headless shell binary (Playwright's default `chromium`) rather than new headless mode. |
|
|
64
|
+
| `identity.headless_user_agent` | warning | Any Chromium headless mode; the headless shell and `--headless=new` both report `HeadlessChrome`. Only a headed browser reports `Chrome`. |
|
|
65
|
+
| `identity.empty_plugins` | notable | The headless shell exposes no PDF viewer plugins; headed and new headless expose a fixed set. |
|
|
66
|
+
| `environment.default_viewport` | notable | No viewport configured; Chromium default is 800x600. |
|
|
67
|
+
| `environment.zero_outer_window` | notable | Headless shell reports no window. |
|
|
68
|
+
| `rendering.swiftshader_webgl` | warning | No GPU, Docker, CI, or `--disable-gpu`. |
|
|
69
|
+
| `rendering.webgl_unavailable` | compatibility risk | GPU disabled or unsupported flags. |
|
|
70
|
+
| `rendering.unmasked_webgl_unavailable` | notable | Browser withholds `WEBGL_debug_renderer_info`. |
|
|
71
|
+
| `rendering.canvas_unstable` | notable / compatibility risk | Canvas output varied between two draws. |
|
|
72
|
+
| `rendering.no_common_fonts` | warning | Minimal container image with no fonts installed (`fc-list` is empty). |
|
|
73
|
+
| `storage.cookies_disabled` | compatibility risk | Cookies disabled by policy or context option. |
|
|
74
|
+
| `storage.indexeddb_unavailable` | compatibility risk / notable | Storage policy or restricted origin; `timeout` means the probe hit `--probe-timeout`. |
|
|
75
|
+
| `permissions.unexpected_permissions_behavior` | notable | Permissions API query errored. |
|
|
76
|
+
| `permissions.notifications_denied` | notable | The headless shell reports `Notification.permission` as denied; headed and new headless report prompt. |
|
|
77
|
+
| `webrtc.host_candidate_error` | notable | WebRTC blocked by policy or sandboxing; `timeout` means the probe hit `--probe-timeout`. |
|
|
78
|
+
| `http.accept_language_missing` | warning | No locale configured (headless shell, minimal containers). Set Playwright `locale` or `--lang`. |
|
|
79
|
+
|
|
80
|
+
## Compare Levels and Warnings
|
|
81
|
+
|
|
82
|
+
Diff `level` and `verdict.level`: `advisory`, `material`, `high_confidence_automation` (`verdict.level` can also be `none`). Loss of a storage capability, timezone, WebGL renderer, `window.chrome`, Accept-Language, and lost fonts are material; most other drift is advisory. User agent diffs ignore build-number noise and fire only on family, major version, or platform changes.
|
|
83
|
+
|
|
84
|
+
Warnings never change the verdict or exit code. They tell you how much to trust the diffs:
|
|
85
|
+
|
|
86
|
+
- `comparison.browser_binary_mismatch`: different binary families or majors two or more apart. Diffs may be binary artifacts.
|
|
87
|
+
- `comparison.headless_mode_mismatch`: one profile is headless and the other is not, judged from the CDP product and the user agent. When the headless shell is involved, plugins, `window.chrome`, outer window, and notifications change too; with new headless mode only the user agent does.
|
|
88
|
+
- `comparison.probe_context_mismatch`: one profile is a fresh page, the other an existing target.
|
|
89
|
+
- `comparison.browser_context_mismatch`: one profile measured a page-target context and the other the browser default context. Locale, timezone, viewport, and permission diffs may be context configuration.
|
|
90
|
+
- `comparison.http_not_captured`: headers exist on only one side; header diffs are skipped.
|
|
91
|
+
|
|
92
|
+
Profile `source.probe_warnings[]`:
|
|
93
|
+
|
|
94
|
+
- `page-contamination-possible`: existing-target profile; page scripts, service workers, CSP, cookies, and app state may affect signals.
|
|
95
|
+
- `default-browser-context`: no page target was open, so the fresh page used the browser default context.
|
|
96
|
+
- `multiple-browser-contexts`: page targets span several contexts; the first was used. Pass `--context <id>` to choose.
|
|
97
|
+
- `http-not-captured`: the diagnostic page request was not observed.
|
|
98
|
+
|
|
99
|
+
## Category Glossary
|
|
100
|
+
|
|
101
|
+
- `automation`: WebDriver, `window.chrome`, known automation globals.
|
|
102
|
+
- `identity`: user agent, client hints, platform, language(s), plugins, MIME types, PDF viewer.
|
|
103
|
+
- `environment`: timezone, viewport, outer window, screen, hardware concurrency, device memory, speech voices.
|
|
104
|
+
- `rendering`: WebGL, canvas, audio, fonts.
|
|
105
|
+
- `storage`: cookies, localStorage, sessionStorage, IndexedDB, service worker.
|
|
106
|
+
- `permissions`: Permissions API states and `Notification.permission` (`notification_api`).
|
|
107
|
+
- `webrtc`: WebRTC API and host-candidate gathering.
|
|
108
|
+
- `http`: request headers sent for the diagnostic page (fresh-page mode only).
|
|
109
|
+
- `cdp`: browser product and protocol metadata.
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "botprint",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Botprint shows what your automated browser exposes to the web, and what changed from your baseline.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"cdp",
|
|
9
|
+
"chrome-devtools-protocol",
|
|
10
|
+
"browser-automation",
|
|
11
|
+
"playwright",
|
|
12
|
+
"puppeteer",
|
|
13
|
+
"headless-chrome",
|
|
14
|
+
"diagnostics",
|
|
15
|
+
"agent-tools",
|
|
16
|
+
"debugging"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/oh-meh/botprint.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/oh-meh/botprint/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/oh-meh/botprint#readme",
|
|
26
|
+
"engines": {
|
|
27
|
+
"bun": ">=1.2.0"
|
|
28
|
+
},
|
|
29
|
+
"bin": {
|
|
30
|
+
"botprint": "src/cli.ts"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"src",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE",
|
|
36
|
+
"PRIVACY.md",
|
|
37
|
+
"docs/agent-usage.md"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"botprint": "bun run src/cli.ts",
|
|
41
|
+
"build": "bun build --compile src/cli.ts --outfile dist/botprint",
|
|
42
|
+
"build:release": "bun run scripts/build-release.ts",
|
|
43
|
+
"examples:generate": "bun run scripts/generate-examples.ts",
|
|
44
|
+
"smoke:local": "bun run scripts/smoke-local.ts",
|
|
45
|
+
"test": "bun test",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"check": "bun run typecheck && bun test"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/bun": "^1.3.13",
|
|
51
|
+
"typescript": "^6.0.3"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"zod": "^4.3.6"
|
|
55
|
+
}
|
|
56
|
+
}
|