finch-ego-lite 0.1.2 → 0.1.4

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
@@ -15,12 +15,18 @@ function egoBinary() {
15
15
  }
16
16
  async function processRunning() {
17
17
  if (process.platform !== "darwin") return false;
18
- try {
19
- await execFileAsync("pgrep", ["-f", "/ego lite.app/Contents/MacOS/ego lite"], { timeout: 2e3 });
20
- return true;
21
- } catch {
22
- return false;
23
- }
18
+ const probe = async () => {
19
+ try {
20
+ await execFileAsync("pgrep", ["-f", "/ego lite.app/Contents/MacOS/ego lite"], { timeout: 2e3 });
21
+ return true;
22
+ } catch {
23
+ return false;
24
+ }
25
+ };
26
+ const first = await probe();
27
+ if (first) return true;
28
+ await new Promise((resolve) => setTimeout(resolve, 300));
29
+ return probe();
24
30
  }
25
31
  async function runCommand(file, args, timeout = 2e4) {
26
32
  const execute = () => new Promise((resolve, reject) => {
@@ -121,6 +127,11 @@ async function launchEgo() {
121
127
  if (process.platform !== "darwin") throw new Error("Ego Lite currently supports macOS only.");
122
128
  await execFileAsync("open", ["-a", "ego lite"], { timeout: 5e3 });
123
129
  }
130
+ async function focusEgo() {
131
+ if (process.platform !== "darwin") return;
132
+ await execFileAsync("open", ["-a", "ego lite"], { timeout: 5e3 }).catch(() => {
133
+ });
134
+ }
124
135
  async function openWebsite() {
125
136
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
126
137
  const args = process.platform === "win32" ? ["/c", "start", "", WEBSITE] : [WEBSITE];
@@ -136,6 +147,7 @@ var ACTION_STAGES = {
136
147
  press_key: "acting",
137
148
  scroll: "scrolling",
138
149
  screenshot: "shooting",
150
+ switch_tab: "switching",
139
151
  close_space: "closing",
140
152
  run: "running"
141
153
  };
@@ -159,7 +171,14 @@ function buildScript(action, input, spaceId) {
159
171
  case "open_url": {
160
172
  const name = input.space_name || "web task";
161
173
  const timeout = input.timeout_seconds ?? 20;
174
+ if (spaceId !== void 0) {
175
+ return `await switchTaskSpace(${spaceId});
176
+ await openOrReuseTab(${q(input.url ?? "")}, { wait: true, timeout: ${timeout} });
177
+ const tab = await currentTab();
178
+ cliLog(JSON.stringify({ spaceId: ${spaceId}, url: tab.url, title: tab.title }));`;
179
+ }
162
180
  return `const space = await useOrCreateTaskSpace(${q(name)});
181
+ await switchTaskSpace(space.id);
163
182
  await openOrReuseTab(${q(input.url ?? "")}, { wait: true, timeout: ${timeout} });
164
183
  const tab = await currentTab();
165
184
  cliLog(JSON.stringify({ spaceId: space.id, url: tab.url, title: tab.title }));`;
@@ -206,6 +225,12 @@ cliLog(JSON.stringify({ ok: true, sy: info.sy }));`;
206
225
  return `${spaceLine}
207
226
  const path = await captureScreenshot();
208
227
  cliLog('__EGO_SHOT__' + path);`;
228
+ case "switch_tab":
229
+ return `await switchTaskSpace(${spaceId});
230
+ await switchTab(${q(input.target_id ?? "")});
231
+ await waitForLoad({ timeout: 15 }).catch(() => {});
232
+ const info = await pageInfo();
233
+ cliLog(JSON.stringify({ ok: true, spaceId: ${spaceId}, url: info.url, title: info.title }));`;
209
234
  case "close_space":
210
235
  return `await completeTaskSpace(${spaceId}, { keep: false });
211
236
  cliLog('ok');`;
@@ -274,9 +299,26 @@ function activate(ctx) {
274
299
  }
275
300
  }));
276
301
  let cachedStatus;
302
+ let lastRefreshAt = 0;
277
303
  let refreshInFlight;
278
304
  let pendingDetailedRefresh = false;
279
- let refreshStatus = () => {
305
+ const refreshStatus = (detailed = false) => {
306
+ if (refreshInFlight) {
307
+ if (detailed) pendingDetailedRefresh = true;
308
+ return refreshInFlight;
309
+ }
310
+ refreshInFlight = getStatus(detailed).then((status) => {
311
+ cachedStatus = status;
312
+ lastRefreshAt = Date.now();
313
+ action.notifyUpdate();
314
+ }).catch((error) => ctx.logger.warn("Ego background status refresh failed", error)).finally(() => {
315
+ refreshInFlight = void 0;
316
+ if (pendingDetailedRefresh) {
317
+ pendingDetailedRefresh = false;
318
+ refreshStatus(true);
319
+ }
320
+ });
321
+ return refreshInFlight;
280
322
  };
281
323
  const action = ctx.composerActions.register("ego-browser", {
282
324
  async getBadge() {
@@ -285,7 +327,9 @@ function activate(ctx) {
285
327
  return online ? { text: t("badge.online"), active: true } : { text: t("badge.offline"), active: false };
286
328
  },
287
329
  async getMenu() {
288
- refreshStatus(true);
330
+ if (cachedStatus === void 0 || Date.now() - lastRefreshAt > 3e3) {
331
+ await refreshStatus(true);
332
+ }
289
333
  if (!cachedStatus) {
290
334
  return [{
291
335
  id: "__checking__",
@@ -452,7 +496,6 @@ ${domainOf(tab.url, t("site.unknown"))}`,
452
496
  if (!binary || !await installedVersion(binary)) throw new Error(t("error.notInstalled"));
453
497
  const script = `const result = await completeTaskSpace(${JSON.stringify(spaceId)}, { keep: false }); cliLog(JSON.stringify(result));`;
454
498
  await runCommand(binary, ["nodejs", "-e", script], 15e3);
455
- cachedStatus = void 0;
456
499
  action.notifyUpdate();
457
500
  refreshStatus(true);
458
501
  await ctx.ui.showToast({ title: t("toast.closed"), variant: "success", position: "TC" });
@@ -492,6 +535,7 @@ action:
492
535
  press_key \u2014 press a key: Enter, Tab, Escape, ArrowDown... (space_id?, key)
493
536
  scroll \u2014 scroll down by pixels (space_id?, delta_y?)
494
537
  screenshot \u2014 capture the page and return it as an image (space_id?)
538
+ switch_tab \u2014 switch to a task space and activate one of its pages (space_id, target_id from status)
495
539
  extract \u2014 extract innerText of a CSS selector, or the whole page (space_id?, selector?)
496
540
  close_space \u2014 close a task space (space_id)
497
541
  run \u2014 advanced: run a raw Ego Browser script (script, timeout_seconds?)
@@ -500,10 +544,11 @@ Reuse task spaces, respect user ownership, verify meaningful actions, and comple
500
544
  inputSchema: {
501
545
  type: "object",
502
546
  properties: {
503
- action: { type: "string", enum: ["status", "open_url", "snapshot", "click", "fill", "type", "press_key", "scroll", "screenshot", "extract", "close_space", "run"] },
547
+ action: { type: "string", enum: ["status", "open_url", "snapshot", "click", "fill", "type", "press_key", "scroll", "screenshot", "switch_tab", "extract", "close_space", "run"] },
504
548
  url: { type: "string", description: "URL to open (action=open_url)." },
505
549
  space_id: { type: "number", description: "Task space id from status/open_url. Optional when only one agent space exists." },
506
550
  space_name: { type: "string", description: "Semantic name for the task space (action=open_url)." },
551
+ target_id: { type: "string", description: "Tab target id from action=status (action=switch_tab)." },
507
552
  selector: { type: "string", description: "CSS selector of the target element (click/fill/type/extract)." },
508
553
  text: { type: "string", description: "Text to type (action=type)." },
509
554
  value: { type: "string", description: "Value to fill into the field (action=fill)." },
@@ -514,7 +559,7 @@ Reuse task spaces, respect user ownership, verify meaningful actions, and comple
514
559
  },
515
560
  required: ["action"]
516
561
  },
517
- risk: "high",
562
+ risk: "medium",
518
563
  defaultEnabled: true,
519
564
  async execute(input, exec) {
520
565
  const request = input;
@@ -548,6 +593,9 @@ Reuse task spaces, respect user ownership, verify meaningful actions, and comple
548
593
  if (actionName === "type" && !request.text) {
549
594
  return { content: [{ type: "text", text: "action=type requires text." }], isError: true };
550
595
  }
596
+ if (actionName === "switch_tab" && !request.target_id) {
597
+ return { content: [{ type: "text", text: "action=switch_tab requires target_id from action=status." }], isError: true };
598
+ }
551
599
  if (process.platform !== "darwin") {
552
600
  return { content: [{ type: "text", text: t("status.unsupported", { platform: process.platform }) }], isError: true };
553
601
  }
@@ -560,9 +608,9 @@ Reuse task spaces, respect user ownership, verify meaningful actions, and comple
560
608
  };
561
609
  }
562
610
  let spaceId;
563
- if (actionName !== "open_url" && actionName !== "run") {
611
+ if (actionName !== "run") {
564
612
  spaceId = await resolveSpaceId(request.space_id);
565
- if (spaceId === void 0) {
613
+ if (spaceId === void 0 && actionName !== "open_url") {
566
614
  return {
567
615
  content: [{ type: "text", text: "No task space resolved. Call action=status or action=open_url first, then pass its space_id." }],
568
616
  isError: true
@@ -573,15 +621,16 @@ Reuse task spaces, respect user ownership, verify meaningful actions, and comple
573
621
  exec.progress.report({ stage: "launching", message: pickProgress(t, "launching") });
574
622
  await launchEgo();
575
623
  await new Promise((resolve) => setTimeout(resolve, 1800));
624
+ } else {
625
+ await focusEgo();
576
626
  }
577
627
  const script = buildScript(actionName, request, spaceId);
578
628
  const timeout = Math.min(300, Math.max(1, request.timeout_seconds ?? 60)) * 1e3;
579
629
  exec.progress.report({ stage: ACTION_STAGES[actionName], message: pickProgress(t, ACTION_STAGES[actionName] ?? "browsing") });
580
630
  try {
581
631
  const output = await runCommand(binary, ["nodejs", "-e", script], timeout);
582
- cachedStatus = void 0;
583
632
  action.notifyUpdate();
584
- refreshStatus(false);
633
+ refreshStatus(true);
585
634
  if (actionName === "screenshot") {
586
635
  const path = output.split("__EGO_SHOT__").pop()?.split("\n")[0]?.trim();
587
636
  if (!path || !existsSync(path)) {
@@ -597,32 +646,15 @@ Reuse task spaces, respect user ownership, verify meaningful actions, and comple
597
646
  }
598
647
  return { content: [{ type: "text", text: output || "ok" }] };
599
648
  } catch (error) {
600
- cachedStatus = void 0;
601
649
  action.notifyUpdate();
602
- refreshStatus(false);
650
+ refreshStatus(true);
603
651
  const message = error instanceof Error ? error.message : String(error);
604
652
  return { content: [{ type: "text", text: message }], isError: true };
605
653
  }
606
654
  }
607
655
  }));
608
- refreshStatus = (detailed = false) => {
609
- if (refreshInFlight) {
610
- pendingDetailedRefresh ||= detailed;
611
- return;
612
- }
613
- refreshInFlight = getStatus(detailed).then((status) => {
614
- cachedStatus = status;
615
- action.notifyUpdate();
616
- }).catch((error) => ctx.logger.warn("Ego background status refresh failed", error)).finally(() => {
617
- refreshInFlight = void 0;
618
- if (pendingDetailedRefresh) {
619
- pendingDetailedRefresh = false;
620
- refreshStatus(true);
621
- }
622
- });
623
- };
624
656
  refreshStatus(true);
625
- const timer = setInterval(() => refreshStatus(true), 1e4);
657
+ const timer = setInterval(() => refreshStatus(true), 5e3);
626
658
  ctx.subscriptions.push({ dispose: () => clearInterval(timer) });
627
659
  ctx.logger.info("ego-browser mini tool activated", { cached: Boolean(cachedStatus) });
628
660
  }
package/i18n/en-US.json CHANGED
@@ -104,6 +104,7 @@
104
104
  "acting": "Clicking gently…|Tapping the button…|Filling things in…",
105
105
  "scrolling": "Scrolling down…|Rolling the wheel…",
106
106
  "shooting": "Saying cheese! 📸|Freezing the moment…",
107
+ "switching": "Switching windows…|Jumping to another page…|Ego is changing its view…",
107
108
  "closing": "Tidying up…|Closing the tab circus…|Wrapping up…",
108
109
  "running": "Ego is doing its thing…|Browser magic in progress…",
109
110
  "browsing": "Agent is working in Ego Browser…"
package/i18n/zh-CN.json CHANGED
@@ -104,6 +104,7 @@
104
104
  "acting": "轻轻点了一下…|正在按下按钮…|正在填写…",
105
105
  "scrolling": "正在往下滚动…|鼠标滚轮转起来了…",
106
106
  "shooting": "咔嚓!拍照留念 📸|定格这一刻…",
107
+ "switching": "换个窗口看看…|跳到另一个页面…|Ego 正在切换视野…",
107
108
  "closing": "正在收摊…|正在收拾战场…|任务完成,整理中…",
108
109
  "running": "Ego 正在忙活…|浏览器魔法进行中…",
109
110
  "browsing": "Agent 正在操作 Ego Browser…"
package/icon.png CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "finch-ego-lite",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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",
@@ -2,7 +2,7 @@
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.2"
5
+ version: "0.1.3"
6
6
  ---
7
7
 
8
8
  # Ego Browser 工具使用指南
@@ -31,6 +31,7 @@ metadata:
31
31
  | press_key | space_id?, key | 按键:Enter/Tab/Escape… |
32
32
  | scroll | space_id?, delta_y? | 向下滚动(默认 300px) |
33
33
  | screenshot | space_id? | 截图并作为图片返回 |
34
+ | switch_tab | space_id, target_id | 先切到任务空间,再激活其中某个页面(target_id 来自 status 的 tabs) |
34
35
  | extract | space_id?, selector? | 提取区域 innerText 或整页文本 |
35
36
  | close_space | space_id | 关闭任务空间 |
36
37
  | run | script, timeout_seconds? | 高级:执行原始 Ego 脚本 |