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.267",
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.394",
147
- "cicy-code-darwin-arm64": "2.3.394",
148
- "cicy-code-linux-x64": "2.3.394",
149
- "cicy-code-linux-arm64": "2.3.394",
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",
@@ -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",
@@ -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",
@@ -495,7 +495,7 @@
495
495
  "reload": "再読み込み",
496
496
  "webviewId": "WebviewId: {{id}}",
497
497
  "copySkillCmd": "Skill コマンドをコピー",
498
- "skillPrompt": "agent-electron でこの webview を操作してください:webContent id={{id}}、profile id={{profile}}、url={{url}}、title={{title}}",
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": "请用 agent-electron 帮我操作这个 webview:webContent id={{id}},profile id={{profile}},url={{url}},title={{title}}",
498
+ "skillPrompt": "请使用 agent-electron Skill 帮我操作这个 webviewwebContent 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
- const m = managers.get(accountIdx);
728
- return ok({ accountIdx, tabs: m && !m.win.isDestroyed() ? m.list() : [] });
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,后台标签也能截。",