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 +66 -34
- package/i18n/en-US.json +1 -0
- package/i18n/zh-CN.json +1 -0
- package/icon.png +0 -0
- package/package.json +1 -1
- package/skills/ego-browser/SKILL.md +2 -1
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: "
|
|
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 !== "
|
|
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(
|
|
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(
|
|
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),
|
|
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
|
@@ -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.
|
|
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 脚本 |
|