cicy-desktop 2.1.208 → 2.1.209

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.208",
3
+ "version": "2.1.209",
4
4
  "description": "CiCy - AI-powered operating system browser",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -140,11 +140,11 @@
140
140
  "//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 着无害。",
141
141
  "optionalDependencies": {
142
142
  "electron": "41.0.3",
143
- "cicy-code-darwin-x64": "2.3.53",
144
- "cicy-code-darwin-arm64": "2.3.53",
145
- "cicy-code-linux-x64": "2.3.53",
146
- "cicy-code-linux-arm64": "2.3.53",
147
- "cicy-code-windows-x64": "2.3.53",
143
+ "cicy-code-darwin-x64": "2.3.54",
144
+ "cicy-code-darwin-arm64": "2.3.54",
145
+ "cicy-code-linux-x64": "2.3.54",
146
+ "cicy-code-linux-arm64": "2.3.54",
147
+ "cicy-code-windows-x64": "2.3.54",
148
148
  "cicy-mihomo-darwin-x64": "1.10.4",
149
149
  "cicy-mihomo-darwin-arm64": "1.10.4",
150
150
  "cicy-mihomo-linux-x64": "1.10.4",
package/src/main.js CHANGED
@@ -1095,34 +1095,26 @@ electronApp.whenReady().then(async () => {
1095
1095
  if (global.__cicyTitleSyncTimer.unref) global.__cicyTitleSyncTimer.unref();
1096
1096
  }
1097
1097
 
1098
- // Periodic per-window thumbnails → ~/cicy-files/window-thumbs (chrome-style
1099
- // small previews on disk; override dir via CICY_THUMB_DIR). Best-effort.
1100
- if (!global.__cicyThumbStarted) {
1098
+ // 窗口缩略图 & 桌面快照:默认**不再周期截图**(资源占用大头;消费端 cicy-code 已改
1099
+ // live RPC /ui/snapshot 与按需 desktop_snapshot,见各文件)。改成「手动触发→按需截」:
1100
+ // - 窗口截图:cicy-code 走 electron_tab_screenshot / Page.captureScreenshot(live)
1101
+ // - 桌面截图:desktop_snapshot 工具按需截(win=一次性 --disable-gpu 子进程,mac/linux=live)
1102
+ // 想恢复后台常驻连续截图(给一直盯屏幕的 agent)再用 CICY_DESKTOP_SNAPSHOT=1 显式开。
1103
+ if (process.env.CICY_DESKTOP_SNAPSHOT === "1" && !global.__cicyDesktopSnapStarted) {
1104
+ global.__cicyDesktopSnapStarted = true;
1105
+ try {
1106
+ const info = require("./utils/desktop-snapshot").startDesktopSnapshots();
1107
+ log.info(`[desktop-snap] continuous desktop snapshots → ${info.dir} (every ${info.intervalMs}ms, mode ${info.mode})`);
1108
+ } catch (e) { log.warn(`[desktop-snap] start failed: ${e.message}`); }
1109
+ }
1110
+ if (process.env.CICY_WINDOW_THUMBS === "1" && !global.__cicyThumbStarted) {
1101
1111
  global.__cicyThumbStarted = true;
1102
1112
  try {
1103
1113
  const info = require("./utils/window-thumbnails").startWindowThumbnails();
1104
- log.info(`[thumbs] window thumbnails → ${info.dir} (every ${info.intervalMs}ms, maxW ${info.maxWidth})`);
1114
+ log.info(`[thumbs] window thumbnails → ${info.dir} (every ${info.intervalMs}ms)`);
1105
1115
  } catch (e) { log.warn(`[thumbs] start failed: ${e.message}`); }
1106
1116
  }
1107
1117
 
1108
- // Periodic WHOLE-desktop snapshot → ~/cicy-files/desktop-snapshot/desktop.b64
1109
- // so the cloud (cicy-code) can read the screen via the `desktop_snapshot` tool.
1110
- // Windows uses a --disable-gpu child (GDI, no prompt); linux uses scrot.
1111
- //
1112
- // macOS: this spawns `screencapture`, which DOES trigger the Screen-Recording TCC
1113
- // prompt — and on an ad-hoc-signed build (no Apple cert) the grant never sticks, so
1114
- // the prompt fires over and over. So it's OFF by default on macOS (most users —
1115
- // docker/team management — don't need the agent to watch their screen). Opt in with
1116
- // CICY_DESKTOP_SNAPSHOT=1. Other platforms stay on (opt out with =0).
1117
- const __snap = require("./utils/desktop-snapshot");
1118
- if (!global.__cicyDesktopSnapStarted && __snap.snapshotEnabled()) {
1119
- global.__cicyDesktopSnapStarted = true;
1120
- try {
1121
- const info = __snap.startDesktopSnapshots();
1122
- log.info(`[desktop-snap] desktop snapshots → ${info.dir} (every ${info.intervalMs}ms, maxW ${info.maxWidth}, mode ${info.mode})`);
1123
- } catch (e) { log.warn(`[desktop-snap] start failed: ${e.message}`); }
1124
- }
1125
-
1126
1118
  // Local-team discovery — reads ~/cicy-ai/global.json's cicyDesktopNodes
1127
1119
  // and probes each via /api/health. Pure local, never talks to the cloud
1128
1120
  // and never runs docker shells.
@@ -54,6 +54,7 @@ module.exports = (registerTool) => {
54
54
  // BUT honor snapshotEnabled(): on macOS capture is off by default, and a live
55
55
  // `screencapture` here is exactly what pops the Screen-Recording prompt (just
56
56
  // less often than the daemon did) — so when disabled we must NOT live-capture.
57
+ // mac/linux:进程内即时抓屏(honor snapshotEnabled — mac 默认关避免授权弹窗)。
57
58
  if (process.platform !== "win32" && snap.snapshotEnabled()) {
58
59
  try {
59
60
  const r = await snap.captureB64(maxWidth);
@@ -63,12 +64,25 @@ module.exports = (registerTool) => {
63
64
  throw e;
64
65
  }
65
66
  }
67
+ // win32:进程内截不了(需 --disable-gpu)。按需起一个一次性 --disable-gpu 子进程
68
+ // 截一张、写盘,再读回来 —— 这样没有常驻 daemon 也能手动截图(手动触发→写盘→拉图)。
69
+ if (process.platform === "win32") {
70
+ try {
71
+ await snap.captureOnceWin();
72
+ const after = readDaemonB64();
73
+ if (after) return { content: [{ type: "text", text: after.b64 }] };
74
+ } catch (e) {
75
+ if (fresh) return { content: [{ type: "text", text: fresh.b64 }] };
76
+ throw e;
77
+ }
78
+ }
66
79
 
67
80
  if (fresh) return { content: [{ type: "text", text: fresh.b64 }] }; // stale is better than nothing
81
+ // macOS 默认关(避免反复弹屏幕录制授权)。
68
82
  if (process.platform === "darwin" && !snap.snapshotEnabled()) {
69
- throw new Error("桌面截图在 macOS 默认关闭(避免反复弹屏幕录制授权)。需要 agent 看屏幕时,启动 app 前设 CICY_DESKTOP_SNAPSHOT=1。");
83
+ throw new Error("桌面截图在 macOS 默认关闭(避免反复弹屏幕录制授权)。需要 agent 看屏幕时,启动 app 前设环境变量 CICY_DESKTOP_SNAPSHOT=1。");
70
84
  }
71
- throw new Error("no desktop snapshot yet (daemon warming up?)");
85
+ throw new Error("no desktop snapshot yet");
72
86
  } catch (error) {
73
87
  return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
74
88
  }
@@ -26,7 +26,7 @@ const electron = require("electron");
26
26
 
27
27
  const MAX_W = 600; // 压到 600 以下宽度
28
28
  const QUALITY = 60;
29
- const DEFAULT_INTERVAL_MS = 8000;
29
+ const DEFAULT_INTERVAL_MS = 30000; // 8s→30s:降全屏截图频率(资源占用)
30
30
 
31
31
  function snapDir() {
32
32
  const fromEnv = (process.env.CICY_DESKTOP_SNAP_DIR || "").trim();
@@ -35,13 +35,16 @@ function snapDir() {
35
35
 
36
36
  // Is desktop screen capture allowed here? Single source of truth shared by the
37
37
  // periodic daemon (main.js) AND the on-demand `desktop_snapshot` tool's live fallback.
38
- // OFF by default on macOS: any capture trips the Screen-Recording prompt, and macOS 15
39
- // won't persist the grant for a non-Apple-Team-ID signature (ad-hoc AND a self-signed
40
- // cert both re-prompt — verified). So unless you ship an Apple Developer-ID + notarized
41
- // build, the only way to "no prompts" is to not capture. Opt in with CICY_DESKTOP_SNAPSHOT=1.
42
- // win/linux are ON by default (no such prompt); opt out with =0.
38
+ // OFF by default on macOS AND Windows (opt in with CICY_DESKTOP_SNAPSHOT=1):
39
+ // - macOS: any capture trips the Screen-Recording prompt that won't persist for a
40
+ // non-Apple-Team-ID signature (re-prompts forever).
41
+ // - Windows: the periodic whole-desktop capture spawns an always-on --disable-gpu
42
+ // child + writes every 30s pure overhead for users who only manage docker/teams
43
+ // and aren't being driven by a screen-watching cloud agent (资源占用). The on-demand
44
+ // `desktop_snapshot` tool still has its own live fallback when actually requested.
45
+ // linux stays ON by default (no prompt, cheap scrot); opt out with =0.
43
46
  function snapshotEnabled() {
44
- return process.platform === "darwin"
47
+ return (process.platform === "darwin" || process.platform === "win32")
45
48
  ? process.env.CICY_DESKTOP_SNAPSHOT === "1"
46
49
  : process.env.CICY_DESKTOP_SNAPSHOT !== "0";
47
50
  }
@@ -169,6 +172,29 @@ function stopDesktopSnapshots() {
169
172
  if (child) { try { child.kill(); } catch (_) {} child = null; }
170
173
  }
171
174
 
175
+ // On-demand ONE-SHOT capture (no persistent daemon). win32: spawn a --disable-gpu
176
+ // child that captures once, writes desktop.b64, and exits — so manual「立即截图」
177
+ // works without the periodic daemon running. mac/linux: capture in-process once.
178
+ // Resolves after the file is written; rejects on timeout/error.
179
+ function captureOnceWin(timeoutMs = 20000) {
180
+ if (process.platform !== "win32") return captureOnce();
181
+ return new Promise((resolve, reject) => {
182
+ const dir = snapDir();
183
+ try { fs.mkdirSync(dir, { recursive: true }); } catch (_) {}
184
+ let c;
185
+ try {
186
+ c = spawn(process.execPath, ["--disable-gpu", "--disable-logging", __filename], {
187
+ env: { ...process.env, CICY_SNAP_DAEMON: "1", CICY_SNAP_ONESHOT: "1" },
188
+ stdio: "ignore",
189
+ windowsHide: true,
190
+ });
191
+ } catch (e) { return reject(e); }
192
+ const timer = setTimeout(() => { try { c.kill(); } catch (_) {} reject(new Error("oneshot capture timeout")); }, timeoutMs);
193
+ c.on("exit", (code) => { clearTimeout(timer); resolve({ dir, code }); });
194
+ c.on("error", (e) => { clearTimeout(timer); reject(e); });
195
+ });
196
+ }
197
+
172
198
  // ── daemon mode ───────────────────────────────────────────────────────────────
173
199
  // Entered when this file is the entry of `electron --disable-gpu
174
200
  // desktop-snapshot.js` (Windows capture child). Detected purely via the env flag
@@ -190,7 +216,13 @@ if (process.env.CICY_SNAP_DAEMON === "1") {
190
216
  }
191
217
  };
192
218
  console.error("[snap-daemon] booting, dir=" + snapDir());
193
- app.whenReady().then(() => {
219
+ app.whenReady().then(async () => {
220
+ // ONESHOT:截一张、写盘、退出(给 captureOnceWin 的按需手动截图用,不常驻)。
221
+ if (process.env.CICY_SNAP_ONESHOT === "1") {
222
+ await once();
223
+ try { app.quit(); } catch (_) {}
224
+ return;
225
+ }
194
226
  once();
195
227
  const t = setInterval(once, intervalMs());
196
228
  if (t.unref) t.unref();
@@ -200,4 +232,4 @@ if (process.env.CICY_SNAP_DAEMON === "1") {
200
232
  app.on("render-process-gone", () => app.quit());
201
233
  }
202
234
 
203
- module.exports = { startDesktopSnapshots, stopDesktopSnapshots, snapDir, captureOnce, captureB64, snapshotEnabled, MAX_W };
235
+ module.exports = { startDesktopSnapshots, stopDesktopSnapshots, snapDir, captureOnce, captureOnceWin, captureB64, snapshotEnabled, MAX_W };
@@ -88,6 +88,10 @@ async function tick(opts) {
88
88
  const manifest = [];
89
89
  for (const win of wins) {
90
90
  try {
91
+ // 跳过最小化/隐藏的窗口:capturePage 即使对不可见窗口也强制离屏重绘+GPU 回读
92
+ // (高占用主因)。看不见的没必要刷预览,留用上次的缩略图即可。
93
+ if (win.isMinimized && win.isMinimized()) continue;
94
+ if (win.isVisible && !win.isVisible()) continue;
91
95
  const entry = await captureOne(win, dir, opts);
92
96
  if (entry) manifest.push(entry);
93
97
  } catch (_) {
@@ -104,7 +108,7 @@ async function tick(opts) {
104
108
 
105
109
  function startWindowThumbnails(options = {}) {
106
110
  const opts = {
107
- intervalMs: options.intervalMs || 4000,
111
+ intervalMs: options.intervalMs || 20000, // 4s→20s:降 capturePage 频率(资源占用大头)
108
112
  maxWidth: options.maxWidth || 320, // small rect, chrome-ish
109
113
  quality: options.quality || 60,
110
114
  };