live: heartbeat sync 2026-10-21T17:49:41-07:00 (Refs #11)
This commit is contained in:
1 parent
c0d840a8bb
commit
017404aa51
30 files changed
+568
-109
No files matched your search
@@ -1,47 +1,48 @@
|
||||
---
|
||||
name: browser-screenshot-disk-and-cookies
|
||||
description: Save omp managed-browser screenshots to real files (tab.screenshot path is silently ignored) and reset the shared Chromium cookie jar between persona captures.
|
||||
description: "Save omp managed-browser screenshots to real files (tab.screenshot path is silently ignored), prove/clear cookie state (clearCookies is domain-scoped), and get embedded-image commits past the secret gate."
|
||||
---
|
||||
|
||||
# Screenshots to disk with the omp `browser` eval helper
|
||||
# omp managed browser from Eval: screenshots, cookies, page reads
|
||||
|
||||
## Traps (both cost a full re-capture to discover)
|
||||
1. `tab.screenshot({ path })` **ignores `path` silently** — the option is not in
|
||||
its schema (`{selector, fullPage, silent, annotate, format, quality, ifChanged, threshold}`).
|
||||
The image attaches inline to the tool result and **nothing is written to disk**;
|
||||
`ls` shows an empty dir. Inline images may also be dropped from transcript
|
||||
archives later, so "attached to chat" is not "saved".
|
||||
2. The shared managed Chromium keeps **cookies across tabs and turns** (profile
|
||||
persists after `tab.close()`). A second capture script that expects the
|
||||
anonymous view lands signed-in and `waitForSelector("testid/…")` fails fast.
|
||||
Friction-earned 2026-10-21 (cookie-state proof on a local stack), same day (static HTML guide verification — four wasted API calls from signature guesses), re-earned same day on the franksign screenshot-walkthrough campaign (19 embedded PNGs, two cookie traps, one secret-gate false positive).
|
||||
|
||||
## Procedure
|
||||
Write files via `tab.run`, which exposes the raw Puppeteer `page` — its
|
||||
`screenshot({path})` does write:
|
||||
## Screenshots to real files
|
||||
- `tab.screenshot({ path })` returns the image to the model but the `path` option is **silently ignored** on the managed-browser path — no file appears on disk.
|
||||
- Working recipe: re-capture raw and decode base64 to disk:
|
||||
```js
|
||||
await tab.run(async ({ page }, p) => {
|
||||
const r = await page.screenshot({ encoding: "base64", type: "png" });
|
||||
await Bun.write(p, Buffer.from(typeof r === "string" ? r : r.data, "base64"));
|
||||
}, { args: ["/tmp/x.png"] });
|
||||
```
|
||||
This puppeteer variant returns the base64 **string directly**, not `{data}` — handle both or you write `undefined`.
|
||||
- `computer.window("Firefox").screenshot()` fails (`captureWindow: unavailable` under niri) — don't go that way.
|
||||
- Downscale before embedding: `magick "$f" -resize 1000x "$f"` keeps dark-theme UI PNGs at 20–80 kB each.
|
||||
|
||||
```js
|
||||
const dir = "/tmp/<session-dir>";
|
||||
const tab = await browser.open({ name: "caps", url });
|
||||
const shot = (file) =>
|
||||
tab.run(async ({ page }, f) => { await page.screenshot({ path: f }); return f; },
|
||||
{ args: [`${dir}/${file}.png`] });
|
||||
## Cookie state (corrected 2026-10-21 — the old claim was wrong)
|
||||
- Cookies DO cross `browser.open` calls: the headless browser is one shared process with one jar (a stale persona session from an earlier campaign hijacked an "anonymous" walkthrough tab).
|
||||
- `tab.clearCookies()` is **domain-scoped to the current page origin** — running it on `about:blank` clears nothing and silently leaves the previous session live (a "signer" tab showed the admin's dashboard). Correct sequence: `goto(app-origin)` → `clearCookies()` → `reload()`, then verify with `page.cookies()`, not with page text.
|
||||
- Cookie proof (ground truth):
|
||||
```js
|
||||
const cs = await page.cookies()
|
||||
return cs.map(c => `${c.name}=${c.value.slice(0,12)}… dom=${c.domain} exp=${c.expires}`).join("\n")
|
||||
```
|
||||
|
||||
await tab.clearCookies(); // ALWAYS before an anonymous capture
|
||||
await tab.goto(url);
|
||||
await tab.waitForSelector("testid/first-state");
|
||||
await shot("1-anonymous");
|
||||
// ...drive states with direct helpers: click/fill("testid/<id>", ...) ...
|
||||
await shot("2-next-state");
|
||||
await tab.close();
|
||||
```
|
||||
## Committing screenshots: the secret-gate false positive
|
||||
- Data-URI PNGs in a diff WILL trip shape-based secret gates (`ASIA[A-Z0-9]{16}` etc. appear as random substrings in base64 streams, worse with case-insensitive grep).
|
||||
- Prove noise, don't assume: run the gate's own pattern with `grep -oEi` over `git diff --cached` and eyeball the matches (image-byte substrings, not credentials; confirm screenshots show passwords masked).
|
||||
- Then use the gate's sanctioned override with the evidence in the commit body: `ALLOW_SECRETS=1 git commit -m "... ALLOW_SECRETS: the N hits are ASIA-shape coincidences inside PNG base64 ..."`. Never `--no-verify`.
|
||||
|
||||
Verify with `ls -la <dir> && file <dir>/*.png` — do not trust silent success.
|
||||
## API signatures (guesses cost calls)
|
||||
- `browser.open({ url, name })` — options object; `browser.open(url)` → TypeError.
|
||||
- `tab.wait(ms)` does not exist → `await Bun.sleep(ms)`.
|
||||
- `tab.run(fnOrCode)` executes in an isolated **Node/Puppeteer realm** (`{ tab, page, browser }`, plus `args` as trailing params), NOT the page — `document is not defined` means you wanted `tab.evaluate(exprString)` (page-global expression; no top-level `return`; wrap an IIFE when you need one).
|
||||
- `tab.text("svg")` etc. are direct page helpers — prefer them over evaluate for plain reads.
|
||||
- For app UIs with labelled forms, `role/textbox[name="Exact label *"]` beats `label=...`: visible labels often include a required-marker the source selector lacks.
|
||||
|
||||
## Notes
|
||||
- Selectors accept `testid/<id>` handler; prefer it over CSS on `[data-testid]`.
|
||||
- `tab.run(fn, { args })`: functions receive `{tab, page, browser, wait, assert}`
|
||||
first, then each arg; a bare code string runs in an **isolated runtime, not the
|
||||
page** — `document` is undefined there. Use direct helpers or `tab.evaluate`
|
||||
(page-global expression; top-level `return` invalid) for page access.
|
||||
- Byte-identical PNG sizes between two frames = same view; fine as proof of a
|
||||
round-trip (state A → B → A).
|
||||
## Eval cell output
|
||||
- `log(...)` inside a JS eval cell can be swallowed (printed nothing across several runs); **`return` from the cell always shows**. Put every value you need to see into the return string.
|
||||
|
||||
## Static page verification loop
|
||||
Serve with `python3 -m http.server <port> -d <dir>` spawned via `Bun.spawn`, open, `tab.evaluate` anchor cross-check (`hrefs` vs `ids`), `img` census (`complete && naturalWidth > 0`), screenshots, `tab.close()`, `proc.kill(9)`. If a rendered label looks wrong in a low-res screenshot, read the real DOM text before "fixing" markup — `<id>` renders as `<id>` and misreads as `</id>` at thumbnail size.
|
||||
Reference in new issue
Block a user