canary-automate
Drive a real browser for a one-off task with Canary — navigate, click, fill, scrape, screenshot — and return the result. Nothing is recorded. Use when the user…
The Canary sandbox scripting API for browser automation. Use when writing or debugging a Canary script — looking up how to open a page, click, fill, extract text, observe an unknown page with snapshotForAI, evaluate in the page, take a screenshot, persist data between steps, or
$ npx -y skills add 0xnyn/canary --skill canary-scripting --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/canary-scriptingContext preview
The summary Claude sees to decide when to auto-load this skill.
The Canary sandbox scripting API for browser automation. Use when writing or debugging a Canary script — looking up how to open a page, click, fill, extract text, observe an unknown page with snapshotForAI, evaluate in the page, take a screenshot, persist data between steps, or
name: canary-scripting
description: The Canary sandbox scripting API for browser automation. Use when writing or debugging a Canary script — looking up how to open a page, click, fill, extract text, observe an unknown page with snapshotForAI, evaluate in the page, take a screenshot, persist data between steps, or understand sandbox limits (no imports, timeouts). Trigger phrases — "how do I click in canary", "canary page API", "what's on this page", "explore a page in canary", "snapshotForAI", "saveScreenshot signature", "get text from the page", "why is my canary script timing out", "open a new tab in canary".
license: MIT
metadata:
author: usecanary
version: 0.4.4
category: reference
tags:
- canary
- browser-automation
- playwright
- scriptingCanary scripts are plain **async JavaScript** run in a QuickJS sandbox with a Playwright-like API. Both `canary-browser run` (one-off) and `canary run --session` (recorded step) execute the same way: top-level `await`, with `browser`, `console`, and the file helpers available as globals.
User says: "navigate to a site and get the title in canary" or "how do I read text off the page?" Use a **named** page so it persists across steps, then `goto` and `evaluate`/`locator`. See *Quick start*.
User says: "click the login button", "fill the search box", "scrape the headlines" `page.locator(selector)` then `.click()` / `.fill(value)` / `.textContent()`; or `page.evaluate(fn)` to pull structured data in one round-trip.
User says: "take a screenshot" or "what's the saveScreenshot signature?" `const buf = await page.screenshot({ fullPage: true }); await saveScreenshot(buf, "home.png");` — note **buffer first**, and that `saveScreenshot` is a top-level global, not `browser.saveScreenshot`.
User says: "I don't know the selectors", "what's on this page?", "explore before acting" `(await page.snapshotForAI()).full` → an aria outline of the page. Read it to pick a role/text selector, then act. See *Observing the page*.
<!-- canary:snippet ex-quickstart fenced=js -->
const page = await browser.getPage("main"); // named, persistent page
await page.goto("https://example.com", { waitUntil: "domcontentloaded" });
console.log(await page.title());
const headings = await page.evaluate(() =>
[...document.querySelectorAll("h1, h2")].map((h) => h.textContent.trim())
);
console.log(JSON.stringify(headings));
await page.locator("a.more").click();
const buf = await page.screenshot({ fullPage: false });
await saveScreenshot(buf, "page.png"); // saveScreenshot(buffer, name)<!-- canary:end ex-quickstart -->
<!-- canary:snippet ex-snapshot fenced=js -->
const page = await browser.getPage("main");
const snap = await page.snapshotForAI(); // { full, incremental? }
console.log(page.url(), await page.title());
console.log(snap.full); // aria outline — pick a role/text selector from this
// then act: await page.getByRole("button", { name: "Continue" }).click();
// after changes, page.snapshotForAI({ track: "main" }) returns just the incremental diff<!-- canary:end ex-snapshot -->
<!-- canary:snippet api-snapshot -->
page: roles, accessible names, `[ref=eN]` markers on actionable nodes. Read it to pick a semantic selector — `page.getByRole("button", { name: "Continue" })`, `page.getByText("Sign in")` — then act.
the page changes to get just the `incremental` diff; `{ depth: N }` caps the tree on huge pages; `timeout` bounds the walk.
stale across steps and after navigations. Prefer re-deriving a semantic selector. <!-- canary:end api-snapshot -->
<!-- canary:snippet rule-observe-first -->
is there, pick a semantic selector from it (`getByRole`, `getByText`), then interact. Never guess selectors blind.
<!-- canary:end rule-observe-first -->
channel.
<!-- canary:snippet api-globals --> Every script gets these globals:
<!-- canary:end api-globals -->
<!-- canary:snippet rule-data-passing -->
within a session — reuse the same page name so each step picks up where the last left off.
`JSON.parse(await readFile("state.json"))` in the next. <!-- canary:end rule-data-passing -->
is in [`references/REFERENCE.md`
QA harness built for Claude Code | E2E testing with screen recordings, console logs, network HARs, and Playwright traces
Repo: 0xnyn/canary
Drive a real browser for a one-off task with Canary — navigate, click, fill, scrape, screenshot — and return the result. Nothing is recorded. Use when the user…
Open and triage recorded Canary sessions in the local viewer. Use when the user wants to look at, replay, compare, or triage a recorded session — or asks what…
Record a verifiable QA session with Canary — explore a flow step by step against one persistent browser, each script a recorded step that captures a Playwright…
Turn a code change into a prioritized browser-QA plan with Canary — read the git diff, infer which user-facing workflows it touches, suggest the concrete flows…