跳到主要内容
知仓学习社ZHICANG

ego-browser

When you need a browser, read this Skill by default. Use it to open and operate websites, fill forms, click buttons, take screenshots, extract page …

联网无严重或高危命中citrolabs/ego-lite

它会碰到什么

扫了多少20 个文本文件,96 KB
它会碰到什么联网
命中总数10 处
命中统计严重 0 · 高 0 · 中 0 · 低 8

这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。

技能内容

ego-browser

For installation, connection, or runtime problems, read

references/install.md. Use help() or references/api.md for signatures and

uncommon options of APIs named below.

Run browser scripts

Run JavaScript through a heredoc:

ego-browser nodejs <<'EOF'
const task = await taskSpace("inspect example page");
const page = task.page("p1");
await page.goto("https://example.com");

console.log({ taskSpaceId: task.spaceId, page: page.label });
console.log(await page.snapshot());
EOF

In some sandbox environments, heredoc input may not work; use -e instead:

ego-browser nodejs -e '
const task = await taskSpace("inspect example page");
const page = task.page("p1");
await page.goto("https://example.com");
console.log({ taskSpaceId: task.spaceId, page: page.label });
console.log(await page.snapshot());
'

In Bash/Zsh, use single quotes around the code and double quotes for JavaScript

strings. Single quotes within the code require shell quoting.

The script always runs in Node.js, not in the web Page. Browser helpers and

Node.js APIs belong in the script; Page globals such as window, document,

location, and DOM APIs do not. Put browser-side JavaScript inside

page.evaluate(). Do not import Playwright or launch another browser.

The Node.js runtime uses ESM. When a script needs local files, load built-ins

with dynamic imports such as await import("node:fs/promises").

Ego-browser deliberately exposes a small custom API. It is not Playwright, even

where method names and options look similar. Use only the TaskSpace, Page,

FileChooser, mouse, and keyboard APIs explicitly listed in this Skill. Do not

infer Playwright methods such as locator(), getByRole(), context(),

expect(), or route(). When the listed API does not cover an operation, use

the documented page.evaluate() or page.cdp() escape hatches instead of

guessing another method.

Pointer actions accept an optional label with a concise 3-6 word description.

Pass it with clicks, hovers, drags, or scrolling to keep the action text next to

the visible agent cursor in sync with the action.

When the user explicitly asks for ego-browser, start with a real browser command

and diagnose the CLI or installation only if it fails.

Spaces, rounds, and pages

  • Use exactly one TaskSpace for the entire user goal. Create it once, print its

spaceId, and resume that same space in later rounds. Use multiple spaces

only when the user explicitly requests them.

  • Never use a new TaskSpace to recover from a stuck, blocked, timed-out, or

unexpected Page. Recover within the existing space; if it cannot continue,

stop and ask the user.

  • Every invocation starts a new Node.js process. Task spaces, tabs, and Page labels

persist; JavaScript variables do not.

  • A new task space starts with Page p1; navigate it instead of opening

another Page.

  • Reuse a Page with goto() instead of opening a new Page for every URL.
  • All time values are milliseconds.
// Later round: use the space id and Page label printed earlier.
const resumed = await taskSpace(7);
const source = resumed.page("p1");
await source.goto("https://example.com/releases");

Do not inspect or select profiles unless the user explicitly requests a

particular Ego Lite profile. A profileId applies only when creating a space;

use help("profiles") for the exact workflow.

Supported TaskSpace API:

  • State: spaceId, name, ownership, page(label), userPage()
  • Pages: await task.pages(), await task.tabs(), newPage(),

adopt(page, { as? }), release(label)

  • Control: waitForControl(options), handOff(), finish({ keep })
  • Advanced: cdp(method, params, options)

Pages receive permanent labels such as p1, p2, and p3. Prefer these labels

to custom { as } values. Reuse or close Pages as the task proceeds; the runtime

reports the configured Page budget when it is reached.

task.newPage() creates another blank Page when multiple Pages must stay open.

Navigate it separately with page.goto().

await task.pages() returns managed Pages. await task.tabs() returns every tab in the

space as { label?, page, targetId, title, url, active, openedBy }. A tab

without a label is unmanaged; adopt it before operating:

const active = (await task.tabs()).find((item) => item.active);
if (active && !active.label) {
  const page = await task.adopt(active.page);
  console.log({ page: page.label, url: await page.url() });
}

release(label) returns an unknown-origin Page to the user without closing its

tab. Close Agent-created Pages with page.close(). Treat openedBy: "unknown"

as user-owned when deciding whether a Page may be closed.

Page operations

ego-browser provides the following Page API:

  • State and observation: label, spaceId, openedBy, targetId, url(),

title(), info(), snapshot(), screenshot()

  • Navigation and waits: goto(), reload(), waitForURL(),

waitForEvent(), waitForSelector(), waitForLoadState(),

waitForFunction(), waitForTimeout()

  • Elements: click(), dblclick(), hover(), dragAndDrop(), fill(),

selectOption(), focus(), press(), setInputFiles(),

waitForFileChooser(), close()

  • Dialogs: acceptDialog(promptText?), dismissDialog()
  • Pointer: mouse.click(), move(), down(), up(), wheel()
  • Keyboard: keyboard.down(), up(), press(), type(), insertText(),

paste()

  • Page code and protocols: evaluate(fnOrString, argument),

fetch(url, options), cdp(method, params, options)

page.evaluate() callbacks run only inside the Page; they cannot read variables

or Node.js modules from the surrounding script. Define browser-side helpers

inside the callback or pass one JSON-serializable value as its second argument.

Work efficiently:

  • Each time you observe, collect only the cheapest page state sufficient to

choose the next action. Use a snapshot for semantic or locator ground truth

and a screenshot for visual confirmation; do not request both by default.

  • If an action does not produce the expected result, inspect the current page

before deciding whether to retry. Do not blindly repeat it or immediately

fall back to coordinates or raw CDP.

  • Once the page clearly shows the requested result, stop; do not confirm the

same result through multiple surfaces.

Semantic pages: snapshot and selectors

Prefer snapshots and semantic selectors for ordinary DOM pages. Use screenshots

and coordinates only when useful DOM semantics are unavailable.

Before choosing an unfamiliar target, take a snapshot. When the current state

is sufficient to plan several actions on the same Page, complete them in one

script invocation, then observe the result once. Observe between actions only when an

intermediate result changes what should happen next. Keep the action sequence,

the wait for its final expected state, and the next snapshot in the same

script invocation. Print the snapshot last so the next round can act on it directly.

The final snapshot is the next round's starting view of the changed page;

without it, that round usually has to spend a separate browser call observing

before it can choose the next target, which wastes compute.

Wait for the expected result: use waitForURL() for navigation,

waitForSelector() for element state, or waitForFunction() for application

state. Avoid fixed delays when an observable condition exists. A snapshot

captures the current moment; it does not wait for the page to become stable.

page.snapshot() captures the current viewport. For content outside it, use

page.snapshot({ scope: "full_page" }).

The default viewport snapshot includes visible iframe content returned by the

browser. To focus on a frame's subtree, reuse the ref printed on its iframe

line:

console.log(await page.snapshot({ scope: "subtree", root: "@12" }));

Use the refs returned by the subtree for actions inside the iframe. A subtree

snapshot does not scope later locator actions; they still prefer actionable

matches in the top document before searching frames.

waitForLoadState() defaults to load. waitForFunction() follows the

Playwright argument order; pass undefined before options when there is no Page

argument:

await page.waitForFunction(() => window.appReady, undefined, {
  timeout: 10_000,
});
// Round 1: inspect and choose targets from this output.
const page = task.page("p1");
console.log(await page.snapshot());
// Next round: act using the previous output, verify, then prepare the next round.
const page = task.page("p1");
await page.fill("@21", "user@example.com");
await page.click("loc=role:button[name='Sign in']");
await page.waitForSelector("loc=css:#account-home", { state: "visible" });
console.log(await page.snapshot());

Element actions accept:

  • snapshot refs such as @21 or ref=21
  • text=... for page content
  • loc=css:, loc=role:, and loc=href: locators
  • xpath=...
  • raw CSS selectors

Selector actions require exactly one match. Unquoted text normalizes whitespace,

ignores case, and matches a substring; quoted text such as

text="Save changes" is exact and case-sensitive.

A small Playwright-compatible selector subset is also accepted: css=...,

terminal :has-text("...") and :text-is("..."), >> nth=N after a CSS,

text, or href selector (N is -1 or non-negative), plus

loc=role:...[name*="..."] for accessible-name substrings. Other Playwright

selector syntax is not supported.

When a selector identifies a wrapper, focus() and press() may use its

interactive ancestor or unique editable descendant; fill() and

setInputFiles() only continue to a unique compatible control.

click(), fill(), hover(), and dragAndDrop() automatically bring their

target into view with browser wheel input. Do not pre-scroll solely to make a

DOM target actionable.

Snapshot node names are accessibility roles. Use a ref now or loc=... to find

the element again. After the page changes, take a new snapshot. When a useful

node has no ref, construct a selector from its role, text, or surrounding

context. CSS searches nested open shadow roots. Actions use an actionable match

in the top document first, then search frames when the top document has none.

Multiple actionable matches in the selected document or frame are ambiguous.

Select options by value, visible label, or zero-based index. A string matches

either value or label; pass an array for a multiple select:

await page.selectOption("select[name=month]", { label: "October" });

Pass null or [] to clear the current selection.

Visual pages: screenshot, mouse, and keyboard

Use a screenshot with mouse and keyboard operations for canvas, rich-text,

spreadsheets, maps, and other interfaces that lack useful DOM semantics:

const path = await page.screenshot({ path: "/absolute/path/before.png" });
await page.mouse.click(420, 260, { label: "open spreadsheet cell" });
await page.mouse.wheel(0, 600, { label: "scroll project board" });
await page.keyboard.paste("hello\tworld");
console.log({ screenshot: path });

Inspect the screenshot with an image-viewing tool. Coordinates use CSS pixels;

keyboard names and +-separated chords follow Playwright syntax. Use

ControlOrMeta for portable shortcuts and verify the resulting page state.

mouse.wheel() performs a short wheel-input motion at the current mouse

position and resolves when that motion completes. In each script invocation, move or

click over the intended scrollable area before using it.

On macOS, keyboard.paste() sends the native paste shortcut and then restores

the user's clipboard. Pass { text, html } when a rich editor needs structured

clipboard content; text is the plain-text fallback. On other platforms, use

keyboard.insertText() for plain text.

await page.keyboard.paste({
  text: "Name\tStatus",
  html: "<table><tr><td>Name</td><td>Status</td></tr></table>",
});

For rich-text editors and editable grids, validate a small edit before repeating

it at scale. Canvas-backed editors may not expose visible content through DOM

text or selectors; verify those results with a screenshot or an

application-specific visible state.

Page JavaScript and CDP

Use page.evaluate() for bulk extraction or complex in-page work. It accepts

one JSON-serializable argument and returns a JSON-serializable value:

const rows = await page.evaluate(
  ({ selector, limit }) =>
    [...document.querySelectorAll(selector)].slice(0, limit).map((node) => ({
      text: node.textContent?.trim(),
      href: node.querySelector("a")?.href,
    })),
  { selector: "article", limit: 20 },
);

page.evaluate() has no timeout option. Keep long work in bounded calls; on a

safety timeout, use executionStopped and mayHaveLateEffects to decide

whether an unsafe follow-up requires reloading or closing the Page first.

Use documented Page methods first. If a wrapper is missing or does not work

reliably on the current page, use page.cdp() as a lower-level path for

diagnosis or control. It accepts Page, Runtime, DOM, Network, Input, and similar

commands; use task.cdp() for Target and Browser commands. Raw CDP invalidates

refs. Do not persist page.targetId across rounds.

Action receipts, popups, and dialogs

When an action is expected to open a new Page, start the wait before the action:

const popupPromise = page.waitForEvent("popup");
await page.click('a[target="_blank"]');
const popupPage = await popupPromise;
await popupPage.waitForLoadState();

High-level actions also report immediately observed popups in receipt.popups

as { label, targetId }. Resolve the Page with

task.page(receipt.popups[0].label) and continue there; wait for its URL when

the destination matters.

For uncommon protocol-event workflows, await page.events() returns and clears

the buffered event array; it is not an EventEmitter.

A synchronous JavaScript dialog may appear as receipt.dialog or in

page.info(). Handle it before continuing:

await page.acceptDialog("prompt response");
// Or: await page.dismissDialog();

A receipt describes only the dispatched action and immediate popup or dialog

observations; it does not verify the resulting application state.

Files and requests

Set an existing file input with absolute paths:

await page.setInputFiles("input[type=file]", ["/absolute/path/report.pdf"]);

If a click creates the file input, start waiting before the click:

const chooserPromise = page.waitForFileChooser({ timeout: 10_000 });
await page.click("button.upload");
const chooser = await chooserPromise;
const result = await chooser.setFiles("/absolute/path/report.pdf");

An upload-triggered JavaScript dialog may be returned as result.dialog; when

present, handle it with the dialog methods above.

For a browser download, arm the event before the triggering action and save the

returned artifact to an absolute path in the same script:

const downloadPromise = page.waitForEvent("download", { timeout: 30_000 });
await page.click("button.download");
const download = await downloadPromise;
console.log({
  url: download.url(),
  suggestedFilename: download.suggestedFilename(),
});
await download.saveAs("/absolute/path/report.pdf");

download.saveAs() waits for completion and creates missing parent

directories. download.path() returns the round-local temporary file;

failure(), cancel(), and delete() manage its lifecycle. Temporary download

files are removed when the SDK round is disposed, so call saveAs() before the

script ends. Do not set a global download directory with raw CDP; each download

wait configures and restores only the addressed Page session.

page.fetch() runs window.fetch() in the Page: relative URLs, cookies, and

service workers use that Page, and browser CORS still applies. It returns

{ ok, status, statusText, url, headers, body }:

const response = await page.fetch("/api/items", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ limit: 20 }),
  timeout: 10_000,
});

Save binary responses without converting them to text:

await page.fetch("/image.png", { saveAs: "/absolute/path/image.png" });

Use standard Node.js fetch() for background requests that do not need Page

browser semantics.

User control and completion

Stop when the user takes control or the space is inactive or unassigned. Do not

retry or route around the stop. Permission prompts, device choosers, and

other browser-owned prompts require the user to handle them.

When the user must act in the browser, call await task.handOff(), end the

round, and explain what they should do. After the user confirms, resume the

same space:

const task = await takeOverTaskSpace(7);
const userPage = task.userPage();

Adopt userPage if it is unmanaged. Use waitForControl() only when the current

script must wait in place. Claim a user-owned or inactive space only when the

user explicitly asks. Find its numeric id first; names may be duplicated:

const spaces = await listTaskSpaces();
console.log(spaces.filter((space) => space.ownership === "user"));

const task = await claimTaskSpace(7);
const userPage = task.userPage();

When the task succeeds, close the TaskSpace by default with

await task.finish({ keep: [] }). Call finish() exactly once and wait for it

to resolve before reporting completion.

Keeping Pages is a rare exception: retain only necessary Pages when the user

explicitly asks, or when the result must remain in the browser for the user to

view or continue working with. Pages merely visited, search results, and

intermediate steps do not need to remain open.

await task.finish({ keep: [] }); // Default: keep no Agent-managed Pages.
await task.finish({ keep: ["p2"] }); // Exception: keep only the result Page for the user.

User-created and unmanaged tabs are protected; if any remain, keep: [] does

not close the whole space. Do not close unwanted Pages one by one at completion;

list the Pages to keep instead.

Use page.close() only while the task is still in progress. Do not call

finish() when the task stops for user control or an error.

If the final output contains [ego-browser:notice], finish the current browser

task, tell the user an Ego Lite update is available, and run

ego-browser upgrade only with their approval. Re-read this Skill after the

upgrade.

References

  • [Installation and connection](references/install.md)
  • [API signatures and options](references/api.md)
  • [Clearing cookies, cache, and storage](references/clearing-state.md) — read

before clearing any cookie, cache, or storage; some clears reach the whole

browser profile.

想直接用这个技能?

本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。

它属于哪个仓库

星标★ 16,028
本站分层T2
该仓技能数2
原文件路径skills/ego-browser/SKILL.md

同一个仓库里的其他技能

看这个仓库的全部 2 个技能