cicy-desktop 2.1.267 → 2.1.269
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cicy-desktop",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.269",
|
|
4
4
|
"description": "CiCy - AI-powered operating system browser",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"bin": {
|
|
@@ -143,10 +143,10 @@
|
|
|
143
143
|
"//optionalDependencies": "(2026-06 回调): mac/linux 改回 native cicy-code(:8008,colima 在 16G mac 上压垮内存)→ 重新内置 cicy-code-<plat> / cicy-mihomo-<plat>,localbin.fromBundle 零网络 seed(npm 仅作更新通道,带 npmmirror→npmjs 回退)。这些由 scripts/sync-runtime-deps.cjs 在 tag-push 时同步到最新版。npm 的 os/cpu 字段保证每个平台只装自己那份。Windows 仍走 docker(WSL :8009),它那份 cicy-code-windows 用不到但 bundle 着无害。",
|
|
144
144
|
"optionalDependencies": {
|
|
145
145
|
"electron": "41.0.3",
|
|
146
|
-
"cicy-code-darwin-x64": "2.3.
|
|
147
|
-
"cicy-code-darwin-arm64": "2.3.
|
|
148
|
-
"cicy-code-linux-x64": "2.3.
|
|
149
|
-
"cicy-code-linux-arm64": "2.3.
|
|
146
|
+
"cicy-code-darwin-x64": "2.3.395",
|
|
147
|
+
"cicy-code-darwin-arm64": "2.3.395",
|
|
148
|
+
"cicy-code-linux-x64": "2.3.395",
|
|
149
|
+
"cicy-code-linux-arm64": "2.3.395",
|
|
150
150
|
"cicy-code-windows-x64": "2.3.193",
|
|
151
151
|
"cicy-mihomo-darwin-x64": "1.10.4",
|
|
152
152
|
"cicy-mihomo-darwin-arm64": "1.10.4",
|
package/src/i18n/locales/en.json
CHANGED
|
@@ -495,7 +495,7 @@
|
|
|
495
495
|
"reload": "Reload",
|
|
496
496
|
"webviewId": "WebviewId: {{id}}",
|
|
497
497
|
"copySkillCmd": "Copy Skill Command",
|
|
498
|
-
"skillPrompt": "Use agent-electron to operate this webview for me: webContent id={{id}}, profile id={{profile}}, url={{url}}, title={{title}}",
|
|
498
|
+
"skillPrompt": "Use the agent-electron skill to operate this webview for me: webContent id={{id}}, profile id={{profile}}, url={{url}}, title={{title}}",
|
|
499
499
|
"cut": "Cut",
|
|
500
500
|
"copy": "Copy",
|
|
501
501
|
"paste": "Paste",
|
package/src/i18n/locales/fr.json
CHANGED
|
@@ -495,7 +495,7 @@
|
|
|
495
495
|
"reload": "Recharger",
|
|
496
496
|
"webviewId": "WebviewId: {{id}}",
|
|
497
497
|
"copySkillCmd": "Copier la commande Skill",
|
|
498
|
-
"skillPrompt": "Utilise agent-electron pour opérer ce webview : webContent id={{id}}, profile id={{profile}}, url={{url}}, title={{title}}",
|
|
498
|
+
"skillPrompt": "Utilise le Skill agent-electron pour opérer ce webview : webContent id={{id}}, profile id={{profile}}, url={{url}}, title={{title}}",
|
|
499
499
|
"cut": "Couper",
|
|
500
500
|
"copy": "Copier",
|
|
501
501
|
"paste": "Coller",
|
package/src/i18n/locales/ja.json
CHANGED
|
@@ -495,7 +495,7 @@
|
|
|
495
495
|
"reload": "再読み込み",
|
|
496
496
|
"webviewId": "WebviewId: {{id}}",
|
|
497
497
|
"copySkillCmd": "Skill コマンドをコピー",
|
|
498
|
-
"skillPrompt": "agent-electron
|
|
498
|
+
"skillPrompt": "agent-electron Skill を使ってこの webview を操作してください:webContent id={{id}}、profile id={{profile}}、url={{url}}、title={{title}}",
|
|
499
499
|
"cut": "切り取り",
|
|
500
500
|
"copy": "コピー",
|
|
501
501
|
"paste": "貼り付け",
|
|
@@ -495,7 +495,7 @@
|
|
|
495
495
|
"reload": "重新加载",
|
|
496
496
|
"webviewId": "WebviewId: {{id}}",
|
|
497
497
|
"copySkillCmd": "复制 Skill 指令",
|
|
498
|
-
"skillPrompt": "
|
|
498
|
+
"skillPrompt": "请使用 agent-electron Skill 帮我操作这个 webview:webContent id={{id}},profile id={{profile}},url={{url}},title={{title}}",
|
|
499
499
|
"cut": "剪切",
|
|
500
500
|
"copy": "复制",
|
|
501
501
|
"paste": "粘贴",
|
|
@@ -21,6 +21,7 @@ const os = require("os");
|
|
|
21
21
|
const { z } = require("zod");
|
|
22
22
|
const { app, BrowserWindow, BrowserView, webContents, ipcMain } = require("electron");
|
|
23
23
|
const { attachContextMenu } = require("../utils/context-menu-options");
|
|
24
|
+
const { sendCDP } = require("../utils/cdp-utils");
|
|
24
25
|
|
|
25
26
|
const SHELL_HTML = path.join(__dirname, "..", "tabbrowser", "tab-shell.html");
|
|
26
27
|
const SHELL_PRELOAD = path.join(__dirname, "..", "tabbrowser", "tab-shell-preload.js");
|
|
@@ -246,6 +247,10 @@ class TabManager {
|
|
|
246
247
|
} catch (e) {}
|
|
247
248
|
}
|
|
248
249
|
this.win = new BrowserWindow(winOpts);
|
|
250
|
+
try {
|
|
251
|
+
this.win.cicyAccountIdx = accountIdx;
|
|
252
|
+
this.win.webContents.cicyAccountIdx = accountIdx;
|
|
253
|
+
} catch (e) {}
|
|
249
254
|
// profile 0 is the system tab window (teams only) — no manual "+" new tab.
|
|
250
255
|
this.win.loadFile(SHELL_HTML, { query: { p: String(accountIdx), noNew: accountIdx === 0 ? "1" : "0", plat: process.platform } });
|
|
251
256
|
managerByHost.set(this.win.webContents.id, this);
|
|
@@ -536,6 +541,33 @@ function findManagerByTab(webContentsId) {
|
|
|
536
541
|
return null;
|
|
537
542
|
}
|
|
538
543
|
|
|
544
|
+
function listTabsForAccount(accountIdx) {
|
|
545
|
+
const m = managers.get(accountIdx);
|
|
546
|
+
const known = new Map(m && !m.win.isDestroyed()
|
|
547
|
+
? m.list().map((tab) => [tab.webContentsId, tab]) : []);
|
|
548
|
+
// Recover BrowserViews that survived a tool-module hot reload. The account
|
|
549
|
+
// tag is stamped in addTab and is also present on split-panel cells.
|
|
550
|
+
for (const wc of webContents.getAllWebContents()) {
|
|
551
|
+
if (wc.isDestroyed() || wc.cicyAccountIdx !== accountIdx || known.has(wc.id)) continue;
|
|
552
|
+
known.set(wc.id, {
|
|
553
|
+
webContentsId: wc.id, title: wc.getTitle(), url: wc.getURL(), active: false,
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
return [...known.values()];
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function profileIdOfWebContents(wc) {
|
|
560
|
+
if (Number.isInteger(wc.cicyAccountIdx)) return wc.cicyAccountIdx;
|
|
561
|
+
try {
|
|
562
|
+
const url = new URL(wc.getURL());
|
|
563
|
+
if (url.pathname.endsWith("/tab-shell.html")) {
|
|
564
|
+
const id = Number(url.searchParams.get("p"));
|
|
565
|
+
if (Number.isInteger(id)) return id;
|
|
566
|
+
}
|
|
567
|
+
} catch (e) {}
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
|
|
539
571
|
// ── programmatic API (team-open reroute / homepage) ──────────────────────────
|
|
540
572
|
// profile 0 is the system tab window. It used to accept tabs ONLY from the
|
|
541
573
|
// homepage (team open, systemOpen:true); that restriction is lifted so the "+"
|
|
@@ -679,6 +711,57 @@ async function tabScreenshot(webContentsId, format) {
|
|
|
679
711
|
}
|
|
680
712
|
}
|
|
681
713
|
|
|
714
|
+
async function tabSnapshot(webContentsId, maxElements = 20, showOverlays = false) {
|
|
715
|
+
const wc = webContents.fromId(webContentsId);
|
|
716
|
+
if (!wc) throw new Error(`tab ${webContentsId} not found`);
|
|
717
|
+
const result = await wc.executeJavaScript(`
|
|
718
|
+
(() => {
|
|
719
|
+
const info = (el) => {
|
|
720
|
+
const rect = el.getBoundingClientRect();
|
|
721
|
+
return {
|
|
722
|
+
tag: el.tagName.toLowerCase(),
|
|
723
|
+
text: (el.textContent || '').substring(0, 100).trim(),
|
|
724
|
+
x: Math.round(rect.x), y: Math.round(rect.y),
|
|
725
|
+
width: Math.round(rect.width), height: Math.round(rect.height)
|
|
726
|
+
};
|
|
727
|
+
};
|
|
728
|
+
const interactive = [];
|
|
729
|
+
document.querySelectorAll('a, button, input, select, textarea, [onclick], [role="button"], [role="link"]').forEach((el) => {
|
|
730
|
+
if (el.offsetParent !== null) interactive.push(info(el));
|
|
731
|
+
});
|
|
732
|
+
return {
|
|
733
|
+
url: location.href, title: document.title,
|
|
734
|
+
viewportWidth: innerWidth, viewportHeight: innerHeight,
|
|
735
|
+
scrollX, scrollY, interactive
|
|
736
|
+
};
|
|
737
|
+
})()
|
|
738
|
+
`, true);
|
|
739
|
+
if (showOverlays) {
|
|
740
|
+
await wc.executeJavaScript(`
|
|
741
|
+
(() => {
|
|
742
|
+
const elements = ${JSON.stringify(result.interactive)};
|
|
743
|
+
elements.forEach((el, i) => {
|
|
744
|
+
const overlay = document.createElement('div');
|
|
745
|
+
overlay.style.cssText = \`position:fixed;left:\${el.x}px;top:\${el.y}px;width:\${el.width}px;height:\${el.height}px;background:rgba(255,0,0,.2);border:2px solid red;z-index:999999;pointer-events:none;box-sizing:border-box\`;
|
|
746
|
+
const label = document.createElement('div');
|
|
747
|
+
label.textContent = i + 1;
|
|
748
|
+
label.style.cssText = 'position:absolute;top:2px;left:2px;background:red;color:white;padding:2px 6px;font-size:12px;font-weight:bold;font-family:monospace;line-height:1';
|
|
749
|
+
overlay.appendChild(label); document.body.appendChild(overlay);
|
|
750
|
+
setTimeout(() => overlay.remove(), 5000);
|
|
751
|
+
});
|
|
752
|
+
})()
|
|
753
|
+
`, true);
|
|
754
|
+
}
|
|
755
|
+
const shown = result.interactive.slice(0, maxElements || 20);
|
|
756
|
+
return "Page Snapshot\n"
|
|
757
|
+
+ `url: ${result.url}\n`
|
|
758
|
+
+ `title: ${result.title}\n`
|
|
759
|
+
+ `viewport: ${result.viewportWidth}x${result.viewportHeight}\n`
|
|
760
|
+
+ `scroll: (${result.scrollX}, ${result.scrollY})\n`
|
|
761
|
+
+ `Interactive Elements (${result.interactive.length}):\n`
|
|
762
|
+
+ shown.map((el, i) => `${i + 1}. [${el.tag}] ${el.text || "(no text)"} @ (${el.x}, ${el.y}) ${el.width}x${el.height}`).join("\n");
|
|
763
|
+
}
|
|
764
|
+
|
|
682
765
|
function registerTabBrowserTools(registerTool) {
|
|
683
766
|
const ok = (obj, isErr = false) => ({
|
|
684
767
|
content: [{ type: "text", text: JSON.stringify(obj, null, 2) }],
|
|
@@ -724,8 +807,31 @@ function registerTabBrowserTools(registerTool) {
|
|
|
724
807
|
z.object({ accountIdx: z.number().describe("账户索引(profile)") }),
|
|
725
808
|
async ({ accountIdx }) => {
|
|
726
809
|
try {
|
|
727
|
-
|
|
728
|
-
|
|
810
|
+
return ok({ accountIdx, tabs: listTabsForAccount(accountIdx) });
|
|
811
|
+
} catch (e) { return ok({ error: e.message }, true); }
|
|
812
|
+
},
|
|
813
|
+
{ tag: "TabBrowser" }
|
|
814
|
+
);
|
|
815
|
+
|
|
816
|
+
registerTool(
|
|
817
|
+
"electron_webcontents",
|
|
818
|
+
"列出 cicy-desktop 当前所有 WebContents(窗口、BrowserView 标签页、webview 等)。",
|
|
819
|
+
z.object({}),
|
|
820
|
+
async () => {
|
|
821
|
+
try {
|
|
822
|
+
const items = webContents.getAllWebContents()
|
|
823
|
+
.filter((wc) => !wc.isDestroyed())
|
|
824
|
+
.map((wc) => ({
|
|
825
|
+
webContentsId: wc.id,
|
|
826
|
+
url: wc.getURL(),
|
|
827
|
+
profileId: profileIdOfWebContents(wc),
|
|
828
|
+
type: wc.getType(),
|
|
829
|
+
title: wc.getTitle(),
|
|
830
|
+
loading: wc.isLoading(),
|
|
831
|
+
crashed: wc.isCrashed(),
|
|
832
|
+
}))
|
|
833
|
+
.sort((a, b) => a.webContentsId - b.webContentsId);
|
|
834
|
+
return ok(items);
|
|
729
835
|
} catch (e) { return ok({ error: e.message }, true); }
|
|
730
836
|
},
|
|
731
837
|
{ tag: "TabBrowser" }
|
|
@@ -761,6 +867,44 @@ function registerTabBrowserTools(registerTool) {
|
|
|
761
867
|
{ tag: "TabBrowser" }
|
|
762
868
|
);
|
|
763
869
|
|
|
870
|
+
registerTool(
|
|
871
|
+
"electron_tab_cdp",
|
|
872
|
+
"对某个标签(按 webContentsId)发送任意 Chrome DevTools Protocol 命令。",
|
|
873
|
+
z.object({
|
|
874
|
+
webContentsId: z.number(),
|
|
875
|
+
method: z.string(),
|
|
876
|
+
params: z.record(z.any()).optional().default({}),
|
|
877
|
+
}),
|
|
878
|
+
async ({ webContentsId, method, params }) => {
|
|
879
|
+
try {
|
|
880
|
+
const wc = webContents.fromId(webContentsId);
|
|
881
|
+
if (!wc) throw new Error(`tab ${webContentsId} not found`);
|
|
882
|
+
const result = await sendCDP(wc, method, params);
|
|
883
|
+
return ok({ success: true, webContentsId, result });
|
|
884
|
+
} catch (e) { return ok({ error: e.message }, true); }
|
|
885
|
+
},
|
|
886
|
+
{ tag: "TabBrowser" }
|
|
887
|
+
);
|
|
888
|
+
|
|
889
|
+
registerTool(
|
|
890
|
+
"electron_tab_snapshot",
|
|
891
|
+
"捕获某个标签(按 webContentsId)的结构快照和交互元素。",
|
|
892
|
+
z.object({
|
|
893
|
+
webContentsId: z.number(),
|
|
894
|
+
max_elements: z.number().optional().default(20),
|
|
895
|
+
show_overlays: z.boolean().optional().default(false),
|
|
896
|
+
}),
|
|
897
|
+
async ({ webContentsId, max_elements, show_overlays }) => {
|
|
898
|
+
try {
|
|
899
|
+
const text = await tabSnapshot(webContentsId, max_elements, show_overlays);
|
|
900
|
+
return { content: [{ type: "text", text }] };
|
|
901
|
+
} catch (e) {
|
|
902
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
{ tag: "TabBrowser" }
|
|
906
|
+
);
|
|
907
|
+
|
|
764
908
|
registerTool(
|
|
765
909
|
"electron_tab_screenshot",
|
|
766
910
|
"截取某个标签(按 webContentsId)。走 CDP,后台标签也能截。",
|