@usecanary/browser 0.2.0 → 0.4.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/dist/cli.cjs +190 -197
- package/dist/cli.js +190 -197
- package/dist/cli.js.map +4 -4
- package/package.json +9 -10
package/dist/cli.cjs
CHANGED
|
@@ -7462,15 +7462,15 @@ var require_help = __commonJS({
|
|
|
7462
7462
|
* @return {string}
|
|
7463
7463
|
*
|
|
7464
7464
|
*/
|
|
7465
|
-
wrap(str, width,
|
|
7465
|
+
wrap(str, width, indent2, minColumnWidth = 40) {
|
|
7466
7466
|
const indents = " \\f\\t\\v\xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF";
|
|
7467
7467
|
const manualIndent = new RegExp(`[\\n][${indents}]+`);
|
|
7468
7468
|
if (str.match(manualIndent)) return str;
|
|
7469
|
-
const columnWidth = width -
|
|
7469
|
+
const columnWidth = width - indent2;
|
|
7470
7470
|
if (columnWidth < minColumnWidth) return str;
|
|
7471
|
-
const leadingStr = str.slice(0,
|
|
7472
|
-
const columnText = str.slice(
|
|
7473
|
-
const indentString = " ".repeat(
|
|
7471
|
+
const leadingStr = str.slice(0, indent2);
|
|
7472
|
+
const columnText = str.slice(indent2).replace("\r\n", "\n");
|
|
7473
|
+
const indentString = " ".repeat(indent2);
|
|
7474
7474
|
const zeroWidthSpace = "\u200B";
|
|
7475
7475
|
const breaks = `\\s${zeroWidthSpace}`;
|
|
7476
7476
|
const regex = new RegExp(
|
|
@@ -9967,6 +9967,171 @@ function createLogger(opts = {}) {
|
|
|
9967
9967
|
|
|
9968
9968
|
// ../../packages/cli-kit/src/index.ts
|
|
9969
9969
|
var import_pino_pretty = __toESM(require_pino_pretty(), 1);
|
|
9970
|
+
|
|
9971
|
+
// ../../packages/cli-kit/src/snippets.generated.ts
|
|
9972
|
+
var API_BROWSER = "- `browser.getPage(nameOrId)` \u2014 get-or-create a named page, or attach to an existing tab by the\n `id` from `listPages()`. Named pages persist across steps in a session \u2014 call with the same\n name to reuse the tab.\n- `browser.newPage()` \u2014 an anonymous page, auto-closed when the script ends; does not persist.\n- `browser.listPages()` \u2014 list every open tab: `[{ id, url, title, name }]` (`name` is `null`\n for tabs you never named).\n- `browser.closePage(name)` \u2014 close and forget a named page.";
|
|
9973
|
+
var API_CONSOLE = "- `console.log` / `console.info` write to stdout; `console.warn` / `console.error` write to\n stderr. Top-level `console.log` is your script's output channel.\n- `console.log` inside `page.evaluate(() => \u2026)` runs in the page and is captured into the\n session's console artifact instead.";
|
|
9974
|
+
var API_FILE_HELPERS = 'All file I/O is async (await it), sandboxed to `~/.canary/tmp/` (no filesystem escape), and\nreturns the full path to the file:\n\n- `saveScreenshot(buffer, name)` \u2014 persist a screenshot buffer; buffer first:\n `const path = await saveScreenshot(await page.screenshot(), "home.png");`\n- `writeFile(name, data)` \u2014 write a small file (e.g. JSON state):\n `await writeFile("results.json", JSON.stringify(data));`\n- `readFile(name)` \u2014 read it back (returns the contents as a string):\n `const data = JSON.parse(await readFile("results.json"));`';
|
|
9975
|
+
var API_GLOBALS = "Every script gets these globals:\n\n- `browser` \u2014 pre-connected browser handle (see the script API)\n- `console` \u2014 `log` / `info` / `warn` / `error`, captured per run\n- `setTimeout` / `clearTimeout` \u2014 basic timers\n- `saveScreenshot(buffer, name)` \u2014 save a screenshot buffer (async \u2014 await it)\n- `writeFile(name, data)` / `readFile(name)` \u2014 small-file persistence (async \u2014 await them)";
|
|
9976
|
+
var API_PLAYWRIGHT_METHODS = '- `page.goto(url, { waitUntil: "domcontentloaded" })` \u2014 navigate; `waitUntil` is `"load"` /\n `"domcontentloaded"` / `"networkidle"` (prefer `"domcontentloaded"` on dev servers)\n- `page.title()` / `page.url()` \u2014 current title / URL\n- `page.snapshotForAI(options)` \u2014 AI-optimized page outline; returns `{ full, incremental? }`;\n options `{ track?, depth?, timeout? }`\n- `page.getByRole(role, { name })` / `page.getByText(text)` \u2014 semantic locators (survive re-renders)\n- `page.textContent(sel)` / `page.innerText(sel)` / `page.innerHTML(sel)` /\n `page.getAttribute(sel, name)` \u2014 read by selector\n- `page.inputValue(sel)` / `page.isChecked(sel)` / `page.isVisible(sel)` / `page.isHidden(sel)` \u2014\n input and visibility state\n- `page.fill(sel, value)` / `page.click(sel)` / `page.type(sel, text)` / `page.press(sel, key)` \u2014\n act on elements\n- `page.waitForSelector(sel, { state, timeout })` (`state`: `"attached"` / `"visible"` /\n `"hidden"` / `"detached"`) / `page.waitForURL(pattern)` / `page.waitForLoadState(state)` /\n `page.waitForFunction(fn)` / `page.waitForTimeout(ms)` \u2014 waiting\n- `page.screenshot({ fullPage })` \u2014 capture a screenshot Buffer; save it with `saveScreenshot(...)`\n- `page.evaluate(fn[, arg])` / `page.$eval(sel, fn)` / `page.$$eval(sel, fn)` \u2014 run plain\n JavaScript in the page context (real DOM; args/returns must be serializable)\n- `page.locator(sel)` \u2014 a Locator for chained actions (`.click()`, `.fill(value)`,\n `.textContent()`, `.first()`, \u2026)\n- `page.keyboard.press/type/down/up(...)` / `page.mouse.move/click/down/up(...)` \u2014 low-level input\n- `page.reload()` / `page.goBack()` / `page.goForward()` \u2014 history;\n `page.content()` / `page.setContent(html)` \u2014 full HTML\n- `page.on("console", handler)` \u2014 observe page console events';
|
|
9977
|
+
var API_PLAYWRIGHT_NOTE = "Pages returned by `browser.getPage()` and `browser.newPage()` are full Playwright Page objects \u2014\nthe same API (`goto`, `click`, `fill`, `locator`, `evaluate`, `getByRole`, `waitForSelector`, \u2026):\nhttps://playwright.dev/docs/api/class-page";
|
|
9978
|
+
var API_SANDBOX_ENV = "Scripts execute inside a QuickJS WASM sandbox with no arbitrary access to the host system.\nThis is NOT Node.js \u2014 there is no module system and no Node API:\n\n- `require()` / `import()` \u2014 no module loading; inline any helpers in the script\n- `process`, `fs` / `path` / `os` \u2014 no process or direct filesystem access (use the file helpers)\n- `fetch` / `WebSocket` \u2014 no direct network access (the page does the networking)\n- `__dirname` / `__filename` \u2014 no path globals\n\nMemory and CPU limits are enforced, and both CPU time and wall-clock time are bounded \u2014 infinite\nloops or never-settling promises abort the script. Values crossing `evaluate` / `$eval` must be\nJSON-serializable.";
|
|
9979
|
+
var API_SNAPSHOT = '- `page.snapshotForAI()` returns `{ full, incremental? }` \u2014 `full` is an aria outline of the\n page: roles, accessible names, `[ref=eN]` markers on actionable nodes. Read it to pick a\n semantic selector \u2014 `page.getByRole("button", { name: "Continue" })`,\n `page.getByText("Sign in")` \u2014 then act.\n- Options `{ track?, depth?, timeout? }`: re-run `page.snapshotForAI({ track: "main" })` after\n the page changes to get just the `incremental` diff; `{ depth: N }` caps the tree on huge\n pages; `timeout` bounds the walk.\n- `page.locator("aria-ref=e12")` works for an immediate action in the same script only \u2014 refs go\n stale across steps and after navigations. Prefer re-deriving a semantic selector.';
|
|
9980
|
+
var EX_INSPECT_TABS = "const tabs = await browser.listPages();\nconsole.log(JSON.stringify(tabs, null, 2));";
|
|
9981
|
+
var EX_SNAPSHOT = 'const page = await browser.getPage("main");\nconst snap = await page.snapshotForAI(); // { full, incremental? }\nconsole.log(page.url(), await page.title());\nconsole.log(snap.full); // aria outline \u2014 pick a role/text selector from this\n// then act: await page.getByRole("button", { name: "Continue" }).click();\n// after changes, page.snapshotForAI({ track: "main" }) returns just the incremental diff';
|
|
9982
|
+
var RULE_DEV_SERVER = 'For local dev servers (Next.js, Vite, \u2026) prefer\n`await page.goto(url, { waitUntil: "domcontentloaded" })` \u2014 the default `"load"` wait can hang\non HMR, streaming, or other long-lived dev-server connections. Use `"load"` only when you\nspecifically need every subresource to finish loading.';
|
|
9983
|
+
var RULE_OBSERVE_FIRST = "- Unknown page? Snapshot first, then act: read `(await page.snapshotForAI()).full` to see what\n is there, pick a semantic selector from it (`getByRole`, `getByText`), then interact. Never\n guess selectors blind.\n- Known page or selectors? Skip the snapshot and use direct selectors \u2014 faster and more reliable.";
|
|
9984
|
+
|
|
9985
|
+
// ../../packages/cli-kit/src/scripting-help.ts
|
|
9986
|
+
var indent = (text, prefix) => text.split("\n").map((line) => line.length > 0 ? prefix + line : line).join("\n");
|
|
9987
|
+
var SANDBOX_ENVIRONMENT = `SANDBOX ENVIRONMENT:
|
|
9988
|
+
${indent(API_SANDBOX_ENV, " ")}
|
|
9989
|
+
|
|
9990
|
+
${indent(API_GLOBALS, " ")}`;
|
|
9991
|
+
var SCRIPT_API = `Script API available inside every script:
|
|
9992
|
+
${indent(API_BROWSER, " ")}
|
|
9993
|
+
|
|
9994
|
+
${indent(API_FILE_HELPERS, " ")}
|
|
9995
|
+
|
|
9996
|
+
${indent(API_CONSOLE, " ")}`;
|
|
9997
|
+
var PLAYWRIGHT_PAGE_NOTE = API_PLAYWRIGHT_NOTE;
|
|
9998
|
+
var browserExample = (scriptBody, opts) => [
|
|
9999
|
+
`canary-browser${opts?.connect === true ? " --connect" : ""} <<'EOF'`,
|
|
10000
|
+
scriptBody,
|
|
10001
|
+
"EOF"
|
|
10002
|
+
].join("\n");
|
|
10003
|
+
var INSPECT_PAGE_BODY = `const page = await browser.getPage("TARGET_ID_HERE");
|
|
10004
|
+
console.log(JSON.stringify({
|
|
10005
|
+
url: page.url(),
|
|
10006
|
+
title: await page.title(),
|
|
10007
|
+
}, null, 2));`;
|
|
10008
|
+
var SCREENSHOT_BODY = `const page = await browser.getPage("main");
|
|
10009
|
+
const buf = await page.screenshot();
|
|
10010
|
+
const path = await saveScreenshot(buf, "debug.png");
|
|
10011
|
+
console.log(path);`;
|
|
10012
|
+
var WAITING_BODY = `const page = await browser.getPage("search-results");
|
|
10013
|
+
await page.waitForSelector(".results");
|
|
10014
|
+
await page.waitForURL("**/success");
|
|
10015
|
+
console.log(JSON.stringify({
|
|
10016
|
+
url: page.url(),
|
|
10017
|
+
title: await page.title(),
|
|
10018
|
+
}, null, 2));`;
|
|
10019
|
+
var ERROR_RECOVERY_BODY = `const page = await browser.getPage("checkout");
|
|
10020
|
+
const path = await saveScreenshot(await page.screenshot(), "debug.png");
|
|
10021
|
+
console.log(JSON.stringify({
|
|
10022
|
+
screenshot: path,
|
|
10023
|
+
url: page.url(),
|
|
10024
|
+
title: await page.title(),
|
|
10025
|
+
}, null, 2));`;
|
|
10026
|
+
var POWERSHELL_BLOCK = ` On Windows/PowerShell, use here-strings to pipe multiline scripts:
|
|
10027
|
+
@"
|
|
10028
|
+
const page = await browser.getPage("main");
|
|
10029
|
+
console.log(await page.title());
|
|
10030
|
+
"@ | canary-browser --connect`;
|
|
10031
|
+
var CONNECTING_SECTION = ` Connecting to a running Chrome instance:
|
|
10032
|
+
Auto-discover Chrome with debugging enabled:
|
|
10033
|
+
canary-browser --connect <<'EOF'
|
|
10034
|
+
const page = await browser.getPage("main");
|
|
10035
|
+
console.log(await page.title());
|
|
10036
|
+
EOF
|
|
10037
|
+
|
|
10038
|
+
Connect to a specific CDP endpoint:
|
|
10039
|
+
canary-browser --connect http://localhost:9222 <<'EOF'
|
|
10040
|
+
const page = await browser.getPage("main");
|
|
10041
|
+
console.log(await page.title());
|
|
10042
|
+
EOF
|
|
10043
|
+
|
|
10044
|
+
To launch Chrome with debugging enabled:
|
|
10045
|
+
chrome.exe --remote-debugging-port=9222
|
|
10046
|
+
google-chrome --remote-debugging-port=9222
|
|
10047
|
+
|
|
10048
|
+
Or visit chrome://inspect/#remote-debugging to configure.`;
|
|
10049
|
+
function buildScriptingGuide(options) {
|
|
10050
|
+
const { heading, example } = options;
|
|
10051
|
+
const browserExtras = options.browserExtras === true;
|
|
10052
|
+
const intro = [
|
|
10053
|
+
`${heading}`,
|
|
10054
|
+
" Write small, focused scripts. Each script should do ONE thing: navigate, click, fill, or check.",
|
|
10055
|
+
" End each script by logging the state you need for the next decision.",
|
|
10056
|
+
' Use descriptive page names like "login", "checkout", or "results" instead of "page1".',
|
|
10057
|
+
' Named pages from browser.getPage("name") persist between script runs, so you usually do not need to re-navigate.',
|
|
10058
|
+
" Inside page.evaluate(...), write plain JavaScript only - no TypeScript syntax in the browser context.",
|
|
10059
|
+
...browserExtras ? [POWERSHELL_BLOCK] : []
|
|
10060
|
+
].join("\n");
|
|
10061
|
+
const quickInspection = [
|
|
10062
|
+
" Quick inspection:",
|
|
10063
|
+
indent(
|
|
10064
|
+
example(EX_INSPECT_TABS, { step: "inspect", connect: true }),
|
|
10065
|
+
" "
|
|
10066
|
+
),
|
|
10067
|
+
"",
|
|
10068
|
+
indent(
|
|
10069
|
+
example(INSPECT_PAGE_BODY, { step: "inspect", connect: true }),
|
|
10070
|
+
" "
|
|
10071
|
+
)
|
|
10072
|
+
].join("\n");
|
|
10073
|
+
const aiSnapshots = [
|
|
10074
|
+
" AI snapshots for element discovery:",
|
|
10075
|
+
indent(example(EX_SNAPSHOT, { step: "discover" }), " "),
|
|
10076
|
+
"",
|
|
10077
|
+
indent(API_SNAPSHOT, " ")
|
|
10078
|
+
].join("\n");
|
|
10079
|
+
const choosingApproach = [
|
|
10080
|
+
" Choosing your approach:",
|
|
10081
|
+
indent(RULE_OBSERVE_FIRST, " ")
|
|
10082
|
+
].join("\n");
|
|
10083
|
+
const screenshots = [
|
|
10084
|
+
" Screenshots for visual state:",
|
|
10085
|
+
indent(example(SCREENSHOT_BODY, { step: "capture" }), " ")
|
|
10086
|
+
].join("\n");
|
|
10087
|
+
const waiting = [
|
|
10088
|
+
" Waiting patterns:",
|
|
10089
|
+
indent(example(WAITING_BODY, { step: "wait" }), " ")
|
|
10090
|
+
].join("\n");
|
|
10091
|
+
const devServer = [
|
|
10092
|
+
" Dev server navigation:",
|
|
10093
|
+
indent(RULE_DEV_SERVER, " ")
|
|
10094
|
+
].join("\n");
|
|
10095
|
+
const errorRecovery = [
|
|
10096
|
+
" Error recovery:",
|
|
10097
|
+
" If a script fails, the page usually stays where it stopped.",
|
|
10098
|
+
" Reconnect to the same page name, take a screenshot, and log the URL/title:",
|
|
10099
|
+
indent(example(ERROR_RECOVERY_BODY, { step: "recover" }), " ")
|
|
10100
|
+
].join("\n");
|
|
10101
|
+
const playwrightMethods = [
|
|
10102
|
+
" Common Playwright Page methods:",
|
|
10103
|
+
indent(API_PLAYWRIGHT_METHODS, " ")
|
|
10104
|
+
].join("\n");
|
|
10105
|
+
const tips = [
|
|
10106
|
+
" Tips:",
|
|
10107
|
+
" - Use console.log(JSON.stringify(...)) for structured output.",
|
|
10108
|
+
" - Prefer page.snapshotForAI() for structure; use screenshots when visual layout or styling matters.",
|
|
10109
|
+
" - Keep page names stable across scripts so you can resume work after failures.",
|
|
10110
|
+
...browserExtras ? [
|
|
10111
|
+
" - Each --browser name maps to a separate daemon-managed browser instance.",
|
|
10112
|
+
" - Use --connect to attach to an existing browser; omit the URL to auto-discover Chrome with debugging enabled."
|
|
10113
|
+
] : [],
|
|
10114
|
+
" - Use short timeouts (--timeout 10) so scripts fail fast instead of hanging on missing elements.",
|
|
10115
|
+
...browserExtras ? [
|
|
10116
|
+
" - Add --headless for unattended automation; omit it when you want to watch the browser window."
|
|
10117
|
+
] : []
|
|
10118
|
+
].join("\n");
|
|
10119
|
+
return [
|
|
10120
|
+
intro,
|
|
10121
|
+
quickInspection,
|
|
10122
|
+
aiSnapshots,
|
|
10123
|
+
choosingApproach,
|
|
10124
|
+
screenshots,
|
|
10125
|
+
waiting,
|
|
10126
|
+
devServer,
|
|
10127
|
+
errorRecovery,
|
|
10128
|
+
playwrightMethods,
|
|
10129
|
+
...browserExtras ? [CONNECTING_SECTION] : [],
|
|
10130
|
+
tips
|
|
10131
|
+
].join("\n\n");
|
|
10132
|
+
}
|
|
10133
|
+
|
|
10134
|
+
// ../../packages/cli-kit/src/index.ts
|
|
9970
10135
|
function requestId(prefix) {
|
|
9971
10136
|
return `${prefix}-${Date.now()}-${process.pid}`;
|
|
9972
10137
|
}
|
|
@@ -14820,166 +14985,8 @@ var CONNECT_AUTO_SENTINEL = "auto";
|
|
|
14820
14985
|
var DEFAULT_BROWSER = "default";
|
|
14821
14986
|
var DEFAULT_TIMEOUT_SECS = 30;
|
|
14822
14987
|
|
|
14823
|
-
// src/assets/embedded.generated.ts
|
|
14824
|
-
var LLM_GUIDE = `LLM USAGE GUIDE:
|
|
14825
|
-
Write small, focused scripts. Each script should do ONE thing: navigate, click, fill, or check.
|
|
14826
|
-
End each script by logging the state you need for the next decision.
|
|
14827
|
-
Use descriptive page names like "login", "checkout", or "results" instead of "page1".
|
|
14828
|
-
Named pages from browser.getPage("name") persist between script runs, so you usually do not need to re-navigate.
|
|
14829
|
-
Inside page.evaluate(...), write plain JavaScript only - no TypeScript syntax in the browser context.
|
|
14830
|
-
On Windows/PowerShell, use here-strings to pipe multiline scripts:
|
|
14831
|
-
@"
|
|
14832
|
-
const page = await browser.getPage("main");
|
|
14833
|
-
console.log(await page.title());
|
|
14834
|
-
"@ | canary-browser --connect
|
|
14835
|
-
|
|
14836
|
-
Quick inspection:
|
|
14837
|
-
canary-browser --connect <<'EOF'
|
|
14838
|
-
const tabs = await browser.listPages();
|
|
14839
|
-
console.log(JSON.stringify(tabs, null, 2));
|
|
14840
|
-
EOF
|
|
14841
|
-
|
|
14842
|
-
canary-browser --connect <<'EOF'
|
|
14843
|
-
const page = await browser.getPage("TARGET_ID_HERE");
|
|
14844
|
-
console.log(JSON.stringify({
|
|
14845
|
-
url: page.url(),
|
|
14846
|
-
title: await page.title(),
|
|
14847
|
-
}, null, 2));
|
|
14848
|
-
EOF
|
|
14849
|
-
|
|
14850
|
-
AI snapshots for element discovery:
|
|
14851
|
-
canary-browser <<'EOF'
|
|
14852
|
-
const page = await browser.getPage("main");
|
|
14853
|
-
const result = await page.snapshotForAI();
|
|
14854
|
-
console.log(result.full);
|
|
14855
|
-
// Returns { full: string, incremental?: string }.
|
|
14856
|
-
// Optional args: { track?: string, depth?: number, timeout?: number }.
|
|
14857
|
-
// Read result.full to identify the right element.
|
|
14858
|
-
// Then interact with it using Playwright:
|
|
14859
|
-
// await page.getByRole("button", { name: "Continue" }).click();
|
|
14860
|
-
// Re-run page.snapshotForAI({ track: "main" }) after the page changes.
|
|
14861
|
-
EOF
|
|
14862
|
-
|
|
14863
|
-
Choosing your approach:
|
|
14864
|
-
Unknown pages: use page.snapshotForAI() first to discover the page, then interact based on what you find.
|
|
14865
|
-
Known pages/selectors: skip the snapshot and use direct Playwright selectors like page.click(), page.fill(), or page.locator() for faster, more reliable automation.
|
|
14866
|
-
|
|
14867
|
-
Screenshots for visual state:
|
|
14868
|
-
canary-browser <<'EOF'
|
|
14869
|
-
const page = await browser.getPage("main");
|
|
14870
|
-
const buf = await page.screenshot();
|
|
14871
|
-
const path = await saveScreenshot(buf, "debug.png");
|
|
14872
|
-
console.log(path);
|
|
14873
|
-
EOF
|
|
14874
|
-
|
|
14875
|
-
Waiting patterns:
|
|
14876
|
-
canary-browser <<'EOF'
|
|
14877
|
-
const page = await browser.getPage("search-results");
|
|
14878
|
-
await page.waitForSelector(".results");
|
|
14879
|
-
await page.waitForURL("**/success");
|
|
14880
|
-
console.log(JSON.stringify({
|
|
14881
|
-
url: page.url(),
|
|
14882
|
-
title: await page.title(),
|
|
14883
|
-
}, null, 2));
|
|
14884
|
-
EOF
|
|
14885
|
-
|
|
14886
|
-
Dev server navigation:
|
|
14887
|
-
For local dev servers (Next.js, Vite, etc.), prefer:
|
|
14888
|
-
await page.goto(url, { waitUntil: "domcontentloaded" });
|
|
14889
|
-
The default "load" wait can hang on HMR, streaming, or other long-lived dev-server connections.
|
|
14890
|
-
Use "load" only when you specifically need every subresource to finish loading.
|
|
14891
|
-
|
|
14892
|
-
Error recovery:
|
|
14893
|
-
If a script fails, the page usually stays where it stopped.
|
|
14894
|
-
Reconnect to the same page name, take a screenshot, and log the URL/title:
|
|
14895
|
-
canary-browser <<'EOF'
|
|
14896
|
-
const page = await browser.getPage("checkout");
|
|
14897
|
-
const path = await saveScreenshot(await page.screenshot(), "debug.png");
|
|
14898
|
-
console.log(JSON.stringify({
|
|
14899
|
-
screenshot: path,
|
|
14900
|
-
url: page.url(),
|
|
14901
|
-
title: await page.title(),
|
|
14902
|
-
}, null, 2));
|
|
14903
|
-
EOF
|
|
14904
|
-
|
|
14905
|
-
Common Playwright Page methods:
|
|
14906
|
-
page.goto(url, { waitUntil: "domcontentloaded" })
|
|
14907
|
-
Navigate to a URL; prefer this on dev servers
|
|
14908
|
-
page.title() Get the current page title
|
|
14909
|
-
page.url() Get the current URL
|
|
14910
|
-
page.snapshotForAI(options) Get an AI-optimized snapshot; returns { full, incremental? }
|
|
14911
|
-
Options: { track?: string, depth?: number, timeout?: number }
|
|
14912
|
-
page.getByRole(role, { name }) Target elements discovered from the snapshot
|
|
14913
|
-
page.textContent(selector) Get the text content of an element
|
|
14914
|
-
page.innerHTML(selector) Get the inner HTML of an element
|
|
14915
|
-
page.fill(selector, value) Fill an input field
|
|
14916
|
-
page.click(selector) Click an element
|
|
14917
|
-
page.type(selector, text) Type text character by character
|
|
14918
|
-
page.press(selector, key) Press a key such as Enter or Tab
|
|
14919
|
-
page.waitForSelector(selector) Wait for an element to appear
|
|
14920
|
-
page.waitForURL(url) Wait for navigation to a URL
|
|
14921
|
-
page.screenshot() Capture a screenshot buffer; save it with saveScreenshot(...)
|
|
14922
|
-
page.$$eval(selector, fn) Run a function on all matching elements
|
|
14923
|
-
page.$eval(selector, fn) Run a function on the first matching element
|
|
14924
|
-
page.evaluate(fn) Run JavaScript in the page context (plain JS only)
|
|
14925
|
-
page.locator(selector) Create a locator for chained actions
|
|
14926
|
-
|
|
14927
|
-
Connecting to a running Chrome instance:
|
|
14928
|
-
Auto-discover Chrome with debugging enabled:
|
|
14929
|
-
canary-browser --connect <<'EOF'
|
|
14930
|
-
const page = await browser.getPage("main");
|
|
14931
|
-
console.log(await page.title());
|
|
14932
|
-
EOF
|
|
14933
|
-
|
|
14934
|
-
Connect to a specific CDP endpoint:
|
|
14935
|
-
canary-browser --connect http://localhost:9222 <<'EOF'
|
|
14936
|
-
const page = await browser.getPage("main");
|
|
14937
|
-
console.log(await page.title());
|
|
14938
|
-
EOF
|
|
14939
|
-
|
|
14940
|
-
To launch Chrome with debugging enabled:
|
|
14941
|
-
chrome.exe --remote-debugging-port=9222
|
|
14942
|
-
google-chrome --remote-debugging-port=9222
|
|
14943
|
-
|
|
14944
|
-
Or visit chrome://inspect/#remote-debugging to configure.
|
|
14945
|
-
|
|
14946
|
-
Tips:
|
|
14947
|
-
- Use console.log(JSON.stringify(...)) for structured output.
|
|
14948
|
-
- Prefer page.snapshotForAI() for structure; use screenshots when visual layout or styling matters.
|
|
14949
|
-
- Keep page names stable across scripts so you can resume work after failures.
|
|
14950
|
-
- Each --browser name maps to a separate daemon-managed browser instance.
|
|
14951
|
-
- Use --connect to attach to an existing browser; omit the URL to auto-discover Chrome with debugging enabled.
|
|
14952
|
-
- Use short timeouts (--timeout 10) so scripts fail fast instead of hanging on missing elements.
|
|
14953
|
-
- Add --headless for unattended automation; omit it when you want to watch the browser window.
|
|
14954
|
-
`;
|
|
14955
|
-
|
|
14956
14988
|
// src/commands/help-text.ts
|
|
14957
|
-
var
|
|
14958
|
-
Scripts run in a sandboxed QuickJS runtime (not Node.js). Top-level \`await\` is
|
|
14959
|
-
available, along with a preconnected \`browser\` global and standard \`console\` output.
|
|
14960
|
-
A background daemon starts automatically when needed and manages browser instances,
|
|
14961
|
-
named pages, and CDP connections.
|
|
14962
|
-
|
|
14963
|
-
SANDBOX ENVIRONMENT:
|
|
14964
|
-
Scripts execute inside a QuickJS WASM sandbox with no arbitrary access to the host system.
|
|
14965
|
-
This is NOT Node.js \u2014 the following are NOT available:
|
|
14966
|
-
- require() / import() No module loading
|
|
14967
|
-
- process No process access
|
|
14968
|
-
- fs / path / os No direct filesystem access
|
|
14969
|
-
- fetch / WebSocket No direct network access
|
|
14970
|
-
- __dirname / __filename No path globals
|
|
14971
|
-
|
|
14972
|
-
Available globals:
|
|
14973
|
-
browser Pre-connected browser handle (see API below)
|
|
14974
|
-
console log, warn, error, info (routed to CLI output)
|
|
14975
|
-
setTimeout / clearTimeout Basic timers
|
|
14976
|
-
saveScreenshot(buf, name) Save a screenshot buffer (async, must be awaited)
|
|
14977
|
-
writeFile(name, data) Write a file to temp dir (async, must be awaited)
|
|
14978
|
-
readFile(name) Read a file from temp dir (async, must be awaited)
|
|
14979
|
-
|
|
14980
|
-
Memory and CPU limits are enforced. Infinite loops will be interrupted.
|
|
14981
|
-
|
|
14982
|
-
Primary invocation styles:
|
|
14989
|
+
var INVOCATION_STYLES = `Primary invocation styles:
|
|
14983
14990
|
canary-browser <<'EOF'
|
|
14984
14991
|
const page = await browser.getPage("main");
|
|
14985
14992
|
await page.goto("https://example.com");
|
|
@@ -14995,40 +15002,26 @@ Primary invocation styles:
|
|
|
14995
15002
|
canary-browser --connect <<'EOF'
|
|
14996
15003
|
const page = await browser.getPage("main");
|
|
14997
15004
|
console.log(await page.title());
|
|
14998
|
-
EOF
|
|
15005
|
+
EOF`;
|
|
15006
|
+
var CLI_LONG_ABOUT = `Dev Browser is a CLI for controlling local or external browsers with JavaScript scripts.
|
|
15007
|
+
Scripts run in a sandboxed QuickJS runtime (not Node.js). Top-level \`await\` is
|
|
15008
|
+
available, along with a preconnected \`browser\` global and standard \`console\` output.
|
|
15009
|
+
A background daemon starts automatically when needed and manages browser instances,
|
|
15010
|
+
named pages, and CDP connections.
|
|
14999
15011
|
|
|
15000
|
-
|
|
15001
|
-
|
|
15002
|
-
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
|
|
15009
|
-
|
|
15010
|
-
|
|
15011
|
-
|
|
15012
|
-
|
|
15013
|
-
Write data to ~/.canary/tmp/<name>.
|
|
15014
|
-
Returns the full path to the written file.
|
|
15015
|
-
Example: const path = await writeFile("results.json", JSON.stringify(data));
|
|
15016
|
-
|
|
15017
|
-
await readFile(name: string): Promise<string>
|
|
15018
|
-
Read a file from ~/.canary/tmp/<name>.
|
|
15019
|
-
Returns the file content as a string.
|
|
15020
|
-
Example: const data = JSON.parse(await readFile("results.json"));
|
|
15021
|
-
|
|
15022
|
-
console.log/info(...) Write output to stdout.
|
|
15023
|
-
console.warn/error(...) Write output to stderr.
|
|
15024
|
-
|
|
15025
|
-
All file I/O functions are async and must be awaited.
|
|
15026
|
-
All paths are restricted to ~/.canary/tmp/ \u2014 no filesystem escape.
|
|
15027
|
-
|
|
15028
|
-
Pages returned by \`browser.getPage()\` and \`browser.newPage()\` are full Playwright
|
|
15029
|
-
Page objects \u2014 you get the same API (goto, click, fill, locator, evaluate, etc.):
|
|
15030
|
-
https://playwright.dev/docs/api/class-page`;
|
|
15031
|
-
var CLI_AFTER_LONG_HELP = LLM_GUIDE;
|
|
15012
|
+
${SANDBOX_ENVIRONMENT}
|
|
15013
|
+
|
|
15014
|
+
${INVOCATION_STYLES}
|
|
15015
|
+
|
|
15016
|
+
${SCRIPT_API}
|
|
15017
|
+
|
|
15018
|
+
${PLAYWRIGHT_PAGE_NOTE}`;
|
|
15019
|
+
var CLI_AFTER_LONG_HELP = `${buildScriptingGuide({
|
|
15020
|
+
browserExtras: true,
|
|
15021
|
+
example: browserExample,
|
|
15022
|
+
heading: "LLM USAGE GUIDE:"
|
|
15023
|
+
})}
|
|
15024
|
+
`;
|
|
15032
15025
|
var RUN_LONG_ABOUT = "Run a script file against the browser.\n\nThe file is executed the same way as stdin input: as top-level JavaScript with `await`, `browser`, and `console` available.\n\nUse top-level flags before `run`, for example `canary-browser --browser my-project run script.js`.";
|
|
15033
15026
|
var INSTALL_LONG_ABOUT = "Install Playwright browsers (Chromium).\n\nDownloads the Chromium build used for daemon-managed browser instances.";
|
|
15034
15027
|
var BROWSERS_LONG_ABOUT = "List all managed browser instances.\n\nShows the browser name, whether it is daemon-launched or externally connected, its status, and any named pages currently registered.";
|