finch-ego-lite 0.1.1 → 0.1.2

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/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
2
  import { execFile } from "node:child_process";
3
- import { existsSync } from "node:fs";
3
+ import { existsSync, readFileSync } from "node:fs";
4
4
  import { homedir } from "node:os";
5
5
  import { join } from "node:path";
6
6
  import { promisify } from "node:util";
@@ -126,6 +126,94 @@ async function openWebsite() {
126
126
  const args = process.platform === "win32" ? ["/c", "start", "", WEBSITE] : [WEBSITE];
127
127
  await execFileAsync(command, args, { timeout: 5e3 });
128
128
  }
129
+ var ACTION_STAGES = {
130
+ open_url: "opening",
131
+ snapshot: "reading",
132
+ extract: "reading",
133
+ click: "acting",
134
+ fill: "acting",
135
+ type: "acting",
136
+ press_key: "acting",
137
+ scroll: "scrolling",
138
+ screenshot: "shooting",
139
+ close_space: "closing",
140
+ run: "running"
141
+ };
142
+ function pickProgress(t, stage) {
143
+ const pool = t(`progress.${stage}`);
144
+ const list = pool.split("|").map((s) => s.trim()).filter(Boolean);
145
+ return list[Math.floor(Math.random() * list.length)] ?? pool;
146
+ }
147
+ async function resolveSpaceId(spaceId) {
148
+ if (spaceId !== void 0) return spaceId;
149
+ const status = await getStatus(false);
150
+ const agentSpaces = status.spaces.filter((space) => space.ownership === "agent");
151
+ if (agentSpaces.length === 1) return agentSpaces[0].id;
152
+ return void 0;
153
+ }
154
+ function buildScript(action, input, spaceId) {
155
+ const q = JSON.stringify;
156
+ const sel = input.selector ? q(input.selector) : "null";
157
+ const spaceLine = spaceId !== void 0 ? `await switchTaskSpace(${spaceId});` : "";
158
+ switch (action) {
159
+ case "open_url": {
160
+ const name = input.space_name || "web task";
161
+ const timeout = input.timeout_seconds ?? 20;
162
+ return `const space = await useOrCreateTaskSpace(${q(name)});
163
+ await openOrReuseTab(${q(input.url ?? "")}, { wait: true, timeout: ${timeout} });
164
+ const tab = await currentTab();
165
+ cliLog(JSON.stringify({ spaceId: space.id, url: tab.url, title: tab.title }));`;
166
+ }
167
+ case "snapshot":
168
+ return `${spaceLine}
169
+ const text = await snapshotText();
170
+ cliLog(String(text));`;
171
+ case "extract":
172
+ return `${spaceLine}
173
+ const sel = ${sel};
174
+ let text;
175
+ if (sel) {
176
+ text = String(await js('(() => { const el = document.querySelector(' + JSON.stringify(sel) + '); return el ? el.innerText : ""; })()'));
177
+ } else {
178
+ text = String(await snapshotText());
179
+ }
180
+ cliLog(text);`;
181
+ case "click":
182
+ return `${spaceLine}
183
+ await click(${sel});
184
+ await waitForLoad({ timeout: 15 }).catch(() => {});
185
+ const info = await pageInfo();
186
+ cliLog(JSON.stringify({ ok: true, url: info.url, title: info.title }));`;
187
+ case "fill":
188
+ return `${spaceLine}
189
+ await fillInput(${sel}, ${q(input.value ?? "")});
190
+ cliLog('ok');`;
191
+ case "type":
192
+ return `${spaceLine}
193
+ ${sel !== "null" ? `await click(${sel});` : ""}
194
+ await typeText(${q(input.text ?? "")});
195
+ cliLog('ok');`;
196
+ case "press_key":
197
+ return `${spaceLine}
198
+ await pressKey(${q(input.key ?? "Enter")});
199
+ cliLog('ok');`;
200
+ case "scroll":
201
+ return `${spaceLine}
202
+ await scrollBy({ y: ${input.delta_y ?? 300} });
203
+ const info = await pageInfo();
204
+ cliLog(JSON.stringify({ ok: true, sy: info.sy }));`;
205
+ case "screenshot":
206
+ return `${spaceLine}
207
+ const path = await captureScreenshot();
208
+ cliLog('__EGO_SHOT__' + path);`;
209
+ case "close_space":
210
+ return `await completeTaskSpace(${spaceId}, { keep: false });
211
+ cliLog('ok');`;
212
+ case "run":
213
+ default:
214
+ return input.script ?? "";
215
+ }
216
+ }
129
217
  function key(value) {
130
218
  return Buffer.from(JSON.stringify(value)).toString("base64url");
131
219
  }
@@ -393,17 +481,36 @@ ${domainOf(tab.url, t("site.unknown"))}`,
393
481
  ctx.subscriptions.push(ctx.tools.register({
394
482
  name: "ego_browser",
395
483
  title: "Ego Browser",
396
- description: `Operate Ego Browser through its preloaded JavaScript helpers.
484
+ description: `Operate Ego Browser with declarative actions. Everyday browsing needs no scripts.
397
485
  action:
398
- status \u2014 check installation, runtime, task-space ownership, and open pages
399
- run \u2014 execute one coherent Ego Browser JavaScript program (requires script)
400
- Use cliLog(...) for output. Reuse task spaces, respect user control, verify actions, and complete finished spaces.`,
486
+ status \u2014 check installation, runtime, task spaces and open pages
487
+ open_url \u2014 open a URL in a task space (url, space_name?) and return its spaceId
488
+ snapshot \u2014 read the current page as text (space_id?)
489
+ click \u2014 click an element by CSS selector (space_id?, selector)
490
+ fill \u2014 fill a form field (space_id?, selector, value)
491
+ type \u2014 type text; pass selector to focus the field first (space_id?, selector?, text)
492
+ press_key \u2014 press a key: Enter, Tab, Escape, ArrowDown... (space_id?, key)
493
+ scroll \u2014 scroll down by pixels (space_id?, delta_y?)
494
+ screenshot \u2014 capture the page and return it as an image (space_id?)
495
+ extract \u2014 extract innerText of a CSS selector, or the whole page (space_id?, selector?)
496
+ close_space \u2014 close a task space (space_id)
497
+ run \u2014 advanced: run a raw Ego Browser script (script, timeout_seconds?)
498
+ Most actions accept space_id; when omitted and exactly one agent-owned task space exists, it is used.
499
+ Reuse task spaces, respect user ownership, verify meaningful actions, and complete spaces when done.`,
401
500
  inputSchema: {
402
501
  type: "object",
403
502
  properties: {
404
- action: { type: "string", enum: ["status", "run"] },
405
- script: { type: "string", description: "JavaScript program for action=run. Ego Browser helpers are preloaded." },
406
- timeout_seconds: { type: "number", description: "Execution timeout for action=run, from 1 to 300 seconds. Default 60." }
503
+ action: { type: "string", enum: ["status", "open_url", "snapshot", "click", "fill", "type", "press_key", "scroll", "screenshot", "extract", "close_space", "run"] },
504
+ url: { type: "string", description: "URL to open (action=open_url)." },
505
+ space_id: { type: "number", description: "Task space id from status/open_url. Optional when only one agent space exists." },
506
+ space_name: { type: "string", description: "Semantic name for the task space (action=open_url)." },
507
+ selector: { type: "string", description: "CSS selector of the target element (click/fill/type/extract)." },
508
+ text: { type: "string", description: "Text to type (action=type)." },
509
+ value: { type: "string", description: "Value to fill into the field (action=fill)." },
510
+ key: { type: "string", description: "Key to press (action=press_key): Enter, Tab, Escape..." },
511
+ delta_y: { type: "number", description: "Pixels to scroll down (action=scroll, default 300)." },
512
+ script: { type: "string", description: "Raw Ego Browser script (action=run only)." },
513
+ timeout_seconds: { type: "number", description: "Execution timeout, 1 to 300 seconds. Default 60." }
407
514
  },
408
515
  required: ["action"]
409
516
  },
@@ -411,17 +518,35 @@ Use cliLog(...) for output. Reuse task spaces, respect user control, verify acti
411
518
  defaultEnabled: true,
412
519
  async execute(input, exec) {
413
520
  const request = input;
414
- if (request.action === "status") {
521
+ const actionName = request.action ?? "";
522
+ if (actionName === "status") {
415
523
  const status = await getStatus(true);
416
524
  cachedStatus = status;
417
525
  action.notifyUpdate();
418
526
  return { content: [{ type: "text", text: statusText(status, t) }], isError: !status.supported || Boolean(status.error) };
419
527
  }
420
- if (request.action !== "run" || typeof request.script !== "string" || !request.script.trim()) {
421
- return { content: [{ type: "text", text: "action=run requires a non-empty script." }], isError: true };
528
+ if (!ACTION_STAGES[actionName]) {
529
+ return { content: [{ type: "text", text: `Unknown action: ${actionName}.` }], isError: true };
530
+ }
531
+ if (actionName === "run") {
532
+ if (typeof request.script !== "string" || !request.script.trim()) {
533
+ return { content: [{ type: "text", text: "action=run requires a non-empty script." }], isError: true };
534
+ }
535
+ if (request.script.length > 1e5 || request.script.includes("\0")) {
536
+ return { content: [{ type: "text", text: "The Ego Browser script is invalid or too large." }], isError: true };
537
+ }
538
+ }
539
+ if (actionName === "open_url" && !request.url) {
540
+ return { content: [{ type: "text", text: "action=open_url requires a url." }], isError: true };
541
+ }
542
+ if ((actionName === "click" || actionName === "fill" || actionName === "extract") && !request.selector) {
543
+ return { content: [{ type: "text", text: `action=${actionName} requires a CSS selector.` }], isError: true };
422
544
  }
423
- if (request.script.length > 1e5 || request.script.includes("\0")) {
424
- return { content: [{ type: "text", text: "The Ego Browser script is invalid or too large." }], isError: true };
545
+ if (actionName === "fill" && request.value === void 0) {
546
+ return { content: [{ type: "text", text: "action=fill requires a value." }], isError: true };
547
+ }
548
+ if (actionName === "type" && !request.text) {
549
+ return { content: [{ type: "text", text: "action=type requires text." }], isError: true };
425
550
  }
426
551
  if (process.platform !== "darwin") {
427
552
  return { content: [{ type: "text", text: t("status.unsupported", { platform: process.platform }) }], isError: true };
@@ -434,19 +559,43 @@ Use cliLog(...) for output. Reuse task spaces, respect user control, verify acti
434
559
  isError: true
435
560
  };
436
561
  }
562
+ let spaceId;
563
+ if (actionName !== "open_url" && actionName !== "run") {
564
+ spaceId = await resolveSpaceId(request.space_id);
565
+ if (spaceId === void 0) {
566
+ return {
567
+ content: [{ type: "text", text: "No task space resolved. Call action=status or action=open_url first, then pass its space_id." }],
568
+ isError: true
569
+ };
570
+ }
571
+ }
437
572
  if (!await processRunning()) {
438
- exec.progress.report({ stage: "launching", message: t("progress.launching") });
573
+ exec.progress.report({ stage: "launching", message: pickProgress(t, "launching") });
439
574
  await launchEgo();
440
575
  await new Promise((resolve) => setTimeout(resolve, 1800));
441
576
  }
577
+ const script = buildScript(actionName, request, spaceId);
442
578
  const timeout = Math.min(300, Math.max(1, request.timeout_seconds ?? 60)) * 1e3;
443
- exec.progress.report({ stage: "browsing", message: t("progress.browsing") });
579
+ exec.progress.report({ stage: ACTION_STAGES[actionName], message: pickProgress(t, ACTION_STAGES[actionName] ?? "browsing") });
444
580
  try {
445
- const output = await runCommand(binary, ["nodejs", "-e", request.script], timeout);
581
+ const output = await runCommand(binary, ["nodejs", "-e", script], timeout);
446
582
  cachedStatus = void 0;
447
583
  action.notifyUpdate();
448
584
  refreshStatus(false);
449
- return { content: [{ type: "text", text: output || "Ego Browser script completed." }] };
585
+ if (actionName === "screenshot") {
586
+ const path = output.split("__EGO_SHOT__").pop()?.split("\n")[0]?.trim();
587
+ if (!path || !existsSync(path)) {
588
+ return { content: [{ type: "text", text: output || "Screenshot failed." }], isError: true };
589
+ }
590
+ const data = readFileSync(path).toString("base64");
591
+ return {
592
+ content: [
593
+ { type: "text", text: `Screenshot: ${path}` },
594
+ { type: "image", data: `data:image/png;base64,${data}`, mimeType: "image/png" }
595
+ ]
596
+ };
597
+ }
598
+ return { content: [{ type: "text", text: output || "ok" }] };
450
599
  } catch (error) {
451
600
  cachedStatus = void 0;
452
601
  action.notifyUpdate();
package/i18n/en-US.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "Ego Browser",
3
3
  "description": "Let Finch browse the web in isolated Ego Lite task spaces while you follow every page it opens.",
4
- "systemPrompt": "Use ego_browser for browser automation. Call action=status to inspect Ego availability and task spaces. Call action=run with one coherent ego-browser JavaScript program for browser work. Reuse task spaces, respect user ownership, verify meaningful actions, and complete spaces when done.",
4
+ "systemPrompt": "Use ego_browser for browser automation with declarative actions. Call action=status first to inspect Ego and task spaces. Use open_url, snapshot, click, fill, type, press_key, scroll, screenshot, extract and close_space for everyday tasks, passing space_id from open_url/status when needed. Fall back to action=run only for advanced cases. Reuse task spaces, respect user ownership, verify meaningful actions, and complete spaces when done.",
5
5
  "composerActions": {
6
6
  "ego-browser": {
7
7
  "tooltip": "Ego Browser"
@@ -98,7 +98,14 @@
98
98
  "notInstalled": "Ego Browser is not installed"
99
99
  },
100
100
  "progress": {
101
- "launching": "Launching Ego Lite…",
101
+ "launching": "Waking up Ego Browser…|Pouring a coffee for the browser…|Ego is stretching…",
102
+ "opening": "Opening a new door…|Loading the page…|Knocking on the web…",
103
+ "reading": "Taking a look around…|Reading the page…|Scanning the content…",
104
+ "acting": "Clicking gently…|Tapping the button…|Filling things in…",
105
+ "scrolling": "Scrolling down…|Rolling the wheel…",
106
+ "shooting": "Saying cheese! 📸|Freezing the moment…",
107
+ "closing": "Tidying up…|Closing the tab circus…|Wrapping up…",
108
+ "running": "Ego is doing its thing…|Browser magic in progress…",
102
109
  "browsing": "Agent is working in Ego Browser…"
103
110
  }
104
111
  }
package/i18n/zh-CN.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "Ego 浏览器",
3
3
  "description": "让 Finch 在独立的 Ego Lite 任务空间中浏览网页,你可以随时查看它打开的每个页面。",
4
- "systemPrompt": "使用 ego_browser 执行浏览器自动化。先用 action=status 检查 Ego 状态和任务空间;用 action=run 执行一段完整的 Ego Browser JavaScript。复用任务空间、尊重用户控制权、验证关键操作,并在完成后关闭任务空间。",
4
+ "systemPrompt": "使用 ego_browser 执行浏览器自动化。先用 action=status 检查 Ego 状态和任务空间;日常操作优先用 open_url/snapshot/click/fill/type/press_key/scroll/screenshot/extract/close_space 等声明式 action,必要时传 space_id;高级场景才用 action=run 执行脚本。复用任务空间、尊重用户控制权、验证关键操作,并在完成后关闭任务空间。",
5
5
  "composerActions": {
6
6
  "ego-browser": {
7
7
  "tooltip": "Ego 浏览器"
@@ -98,7 +98,14 @@
98
98
  "notInstalled": "Ego Browser 未安装"
99
99
  },
100
100
  "progress": {
101
- "launching": "正在启动 Ego Lite…",
101
+ "launching": "正在唤醒 Ego 浏览器…|给浏览器泡杯咖啡…|Ego 正在伸懒腰…",
102
+ "opening": "正在推开新世界的大门…|网页加载中…|正在敲响网络的大门…",
103
+ "reading": "正在环顾页面…|正在阅读页面…|正在扫描内容…",
104
+ "acting": "轻轻点了一下…|正在按下按钮…|正在填写…",
105
+ "scrolling": "正在往下滚动…|鼠标滚轮转起来了…",
106
+ "shooting": "咔嚓!拍照留念 📸|定格这一刻…",
107
+ "closing": "正在收摊…|正在收拾战场…|任务完成,整理中…",
108
+ "running": "Ego 正在忙活…|浏览器魔法进行中…",
102
109
  "browsing": "Agent 正在操作 Ego Browser…"
103
110
  }
104
111
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "finch-ego-lite",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Let Finch browse the web in isolated Ego Lite task spaces while you follow every page it opens.",
5
5
  "author": {
6
6
  "name": "PuterJam",
@@ -28,7 +28,7 @@
28
28
  "id": "ego-browser",
29
29
  "name": "Ego Browser",
30
30
  "description": "Control Ego Browser and see the pages your Agent is working on.",
31
- "systemPrompt": "Use ego_browser for browser automation. Call action=status to inspect Ego availability and task spaces. Call action=run with one coherent ego-browser JavaScript program for browser work. Reuse task spaces, respect user ownership, verify meaningful actions, and complete spaces when done.",
31
+ "systemPrompt": "Use ego_browser for browser automation with declarative actions. Call action=status first to inspect Ego and task spaces. Use open_url, snapshot, click, fill, type, press_key, scroll, screenshot, extract and close_space for everyday tasks, passing space_id from open_url/status when needed. Fall back to action=run only for advanced cases. Reuse task spaces, respect user ownership, verify meaningful actions, and complete spaces when done.",
32
32
  "main": "dist/index.js",
33
33
  "activationEvents": [
34
34
  "onStartup"
@@ -2,50 +2,45 @@
2
2
  name: ego-browser
3
3
  description: Use Ego Browser whenever the user needs to open a website, navigate pages, click, type, upload, take screenshots, extract page data, test web apps, log in, or automate browser actions. Ego gives Agents isolated task spaces that reuse the user's login state without competing with normal browser windows.
4
4
  metadata:
5
- version: "0.1.0"
5
+ version: "0.1.2"
6
6
  ---
7
7
 
8
- # Ego Browser
9
-
10
- Use the `ego_browser` Agent tool instead of Bash.
11
-
12
- ## Start
13
-
14
- Call `ego_browser` with `action: "status"` when availability or current task spaces matter. For browser work call `action: "run"` with one coherent JavaScript program in `script`.
15
-
16
- All Ego helpers are preloaded in the script. Print model-visible output with `cliLog(...)`.
17
-
18
- ```js
19
- const task = await useOrCreateTaskSpace('inspect example page')
20
- await openOrReuseTab('https://example.com', { wait: true, timeout: 20 })
21
- cliLog(await snapshotText())
22
- ```
23
-
24
- ## Main helpers
25
-
26
- - Spaces: `listTaskSpaces`, `useOrCreateTaskSpace`, `claimTaskSpace`, `handOffTaskSpace`, `takeOverTaskSpace`, `waitForAgentControl`, `completeTaskSpace`
27
- - Tabs: `listTabs`, `openOrReuseTab`, `closeTab`, `gotoAndWait`, `currentTab`, `switchTab`, `pageInfo`, `ensureRealTab`
28
- - Observe: `snapshotText`, `captureScreenshot`, `drainEvents`
29
- - Act: `click`, `doubleClick`, `hover`, `dragMouse`, `scrollBy`, `scroll`, `fillInput`, `typeText`, `pressKey`, `uploadFile`
30
- - Wait: `wait`, `waitForLoad`, `waitForElement`, `waitForNetworkIdle`
31
- - Advanced: `js`, `cdp`, `serverFetch`, `browserFetch`
32
-
33
- ## Task-space rules
34
-
35
- Reuse the same short, goal-named task space across follow-ups. Prefer its numeric `id` after creation. Respect `ownership`:
36
-
37
- - `agent`: the Agent may act.
38
- - `agentDelegatedToUser`: the user controls it; stop and wait.
39
- - `user`: claim only after explicit user confirmation.
40
-
41
- When login, captcha, or manual confirmation is required, call `handOffTaskSpace(id)` and tell the user what to do. Call `takeOverTaskSpace(id)` only after the user explicitly says to continue.
42
-
43
- Finish in a dedicated final call with `completeTaskSpace(id, { keep: false })`. Use `keep: true` only when the user explicitly wants the live page left open or must continue manually.
44
-
45
- ## Workflow
46
-
47
- 1. Semantic pages: `snapshotText()` then act with `@N`, stable `loc=...`, or CSS selectors.
48
- 2. Canvas/rich editors: screenshot, coordinates, and real keyboard input; test a tiny write first.
49
- 3. Extraction/state: use one `js(String.raw\`(() => { ... })()\`) expression.
50
-
51
- After meaningful actions, verify with a new snapshot, `pageInfo()`, or screenshot. Refs from `snapshotText()` are valid only for the latest snapshot. Wait and timeout values are seconds unless the name ends in `Ms`.
8
+ # Ego Browser 工具使用指南
9
+
10
+ 本 skill 说明如何通过 `ego_browser` 工具完成浏览器自动化。日常浏览**不需要写脚本**——工具提供声明式 action,直接传参数即可。只有高级场景(CDP、自定义 JS、复杂逻辑)才使用 `action=run`。
11
+
12
+ ## 使用流程
13
+
14
+ 1. **`action: "status"`** — 检查 Ego 是否可用、任务空间与页面。任务开始时先调一次。
15
+ 2. **`action: "open_url"`** — 打开网址:传 `url`(可选 `space_name` 给任务空间起个语义名)。返回 `spaceId`,后续操作复用它。
16
+ 3. **按需执行操作**(见下表),关键操作后验证结果。
17
+ 4. **`action: "close_space"`** — 任务完成,关闭任务空间(`space_id`)。
18
+
19
+ `space_id` 规则:多数 action 接受 `space_id`;省略时若恰好只有一个 agent 空间会自动使用,否则需要先从 `status`/`open_url` 拿到。
20
+
21
+ ## Action 一览
22
+
23
+ | action | 参数 | 说明 |
24
+ |---|---|---|
25
+ | status | - | 环境、任务空间、页面状态 |
26
+ | open_url | url, space_name? | 打开网址,返回 spaceId |
27
+ | snapshot | space_id? | 读取页面为文本(语义树) |
28
+ | click | space_id?, selector | 按 CSS selector 点击 |
29
+ | fill | space_id?, selector, value | 填写表单字段 |
30
+ | type | space_id?, selector?, text | 输入文本;传 selector 先聚焦 |
31
+ | press_key | space_id?, key | 按键:Enter/Tab/Escape… |
32
+ | scroll | space_id?, delta_y? | 向下滚动(默认 300px) |
33
+ | screenshot | space_id? | 截图并作为图片返回 |
34
+ | extract | space_id?, selector? | 提取区域 innerText 或整页文本 |
35
+ | close_space | space_id | 关闭任务空间 |
36
+ | run | script, timeout_seconds? | 高级:执行原始 Ego 脚本 |
37
+
38
+ ## 规则
39
+
40
+ - **复用任务空间**:同一目标的多步操作复用同一个 `space_id`,不要每次新建。
41
+ - **尊重归属**:`ownership` 为 `agent` 时可操作;`agentDelegatedToUser` 时用户正控制,停下等待;`user` 时必须先征得用户明确同意再接管。
42
+ - **登录/验证码/人工确认**:交给用户处理,明确告诉用户需要做什么;用户说继续后再接管。
43
+ - **验证关键操作**:点击、提交、填表后,用 `snapshot` 或 `pageInfo` 确认结果,不要盲目继续。
44
+ - **选择器优先**:定位元素用 CSS selector;`snapshot` 输出的语义树可帮助挑选 selector。
45
+ - **完成后关闭**:任务结束用 `close_space`;仅当用户明确要求保留页面时留着。
46
+ - **超时控制**:复杂操作适当调大 `timeout_seconds`(默认 60 秒)。