cicy-desktop 2.1.273 → 2.1.274

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.273",
3
+ "version": "2.1.274",
4
4
  "description": "CiCy - AI-powered operating system browser",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -52,11 +52,13 @@ module.exports = (registerTool) => {
52
52
  return { content: [{ type: "text", text: fresh.b64 }] };
53
53
  }
54
54
 
55
- // Stale/missing. A desktop_snapshot RPC is an explicit one-shot request,
56
- // so mac/linux capture immediately even when the periodic daemon is off.
57
- // CICY_DESKTOP_SNAPSHOT only controls the background capture loop in
58
- // main.js; it must not make the user's “立即截图” button unusable.
59
- if (process.platform !== "win32") {
55
+ // Stale/missing. mac/linux can capture live in-process; win32 cannot (needs
56
+ // the --disable-gpu daemon), so there we fall back to whatever file exists.
57
+ // BUT honor snapshotEnabled(): on macOS capture is off by default, and a live
58
+ // `screencapture` here is exactly what pops the Screen-Recording prompt (just
59
+ // less often than the daemon did) — so when disabled we must NOT live-capture.
60
+ // mac/linux:进程内即时抓屏(honor snapshotEnabled — mac 默认关避免授权弹窗)。
61
+ if (process.platform !== "win32" && snap.snapshotEnabled()) {
60
62
  try {
61
63
  const r = await snap.captureB64(maxWidth);
62
64
  return { content: [{ type: "text", text: r.b64 }] };
@@ -79,6 +81,10 @@ module.exports = (registerTool) => {
79
81
  }
80
82
 
81
83
  if (fresh) return { content: [{ type: "text", text: fresh.b64 }] }; // stale is better than nothing
84
+ // macOS 默认关(避免反复弹屏幕录制授权)。
85
+ if (process.platform === "darwin" && !snap.snapshotEnabled()) {
86
+ throw new Error("桌面截图在 macOS 默认关闭(避免反复弹屏幕录制授权)。需要 agent 看屏幕时,启动 app 前设环境变量 CICY_DESKTOP_SNAPSHOT=1。");
87
+ }
82
88
  throw new Error("no desktop snapshot yet");
83
89
  } catch (error) {
84
90
  return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
@@ -37,15 +37,14 @@ function snapDir() {
37
37
  }
38
38
 
39
39
  // Is desktop screen capture allowed here? Single source of truth shared by the
40
- // periodic daemon (main.js). One-shot `desktop_snapshot` RPC requests bypass this
41
- // switch: clicking “立即截图” must work without enabling background capture.
40
+ // periodic daemon (main.js) AND the on-demand `desktop_snapshot` tool's live fallback.
42
41
  // OFF by default on macOS AND Windows (opt in with CICY_DESKTOP_SNAPSHOT=1):
43
42
  // - macOS: any capture trips the Screen-Recording prompt that won't persist for a
44
43
  // non-Apple-Team-ID signature (re-prompts forever).
45
44
  // - Windows: the periodic whole-desktop capture spawns an always-on --disable-gpu
46
45
  // child + writes every 30s — pure overhead for users who only manage docker/teams
47
46
  // and aren't being driven by a screen-watching cloud agent (资源占用). The on-demand
48
- // `desktop_snapshot` still captures once when explicitly requested.
47
+ // `desktop_snapshot` tool still has its own live fallback when actually requested.
49
48
  // linux stays ON by default (no prompt, cheap scrot); opt out with =0.
50
49
  function snapshotEnabled() {
51
50
  return (process.platform === "darwin" || process.platform === "win32")
@@ -0,0 +1,58 @@
1
+ // Copyright 2026 CiCy AI
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ const assert = require("node:assert/strict");
5
+ const fs = require("node:fs");
6
+ const os = require("node:os");
7
+ const path = require("node:path");
8
+ const test = require("node:test");
9
+
10
+ test("desktop_snapshot does not capture live when desktop snapshots are disabled", async (t) => {
11
+ if (process.platform === "win32") {
12
+ t.skip("the Windows one-shot capture path is intentionally separate");
13
+ return;
14
+ }
15
+
16
+ const emptySnapshotDir = fs.mkdtempSync(path.join(os.tmpdir(), "cicy-snapshot-test-"));
17
+ t.after(() => fs.rmSync(emptySnapshotDir, { recursive: true, force: true }));
18
+
19
+ const snapshotPath = require.resolve("../src/utils/desktop-snapshot");
20
+ const toolsPath = require.resolve("../src/tools/desktop-snapshot-tools");
21
+ const originalSnapshotModule = require.cache[snapshotPath];
22
+ const originalToolsModule = require.cache[toolsPath];
23
+ let captureCalls = 0;
24
+
25
+ require.cache[snapshotPath] = {
26
+ id: snapshotPath,
27
+ filename: snapshotPath,
28
+ loaded: true,
29
+ exports: {
30
+ snapDir: () => emptySnapshotDir,
31
+ snapshotEnabled: () => false,
32
+ captureB64: async () => {
33
+ captureCalls += 1;
34
+ return { b64: "captured" };
35
+ },
36
+ },
37
+ };
38
+ delete require.cache[toolsPath];
39
+
40
+ t.after(() => {
41
+ if (originalSnapshotModule) require.cache[snapshotPath] = originalSnapshotModule;
42
+ else delete require.cache[snapshotPath];
43
+ if (originalToolsModule) require.cache[toolsPath] = originalToolsModule;
44
+ else delete require.cache[toolsPath];
45
+ });
46
+
47
+ let handler;
48
+ require(toolsPath)((name, _description, _schema, registeredHandler) => {
49
+ if (name === "desktop_snapshot") handler = registeredHandler;
50
+ });
51
+
52
+ assert.equal(typeof handler, "function");
53
+ const result = await handler({ maxWidth: 600 });
54
+
55
+ assert.equal(captureCalls, 0);
56
+ assert.equal(result.isError, true);
57
+ assert.match(result.content[0].text, /^Error:/);
58
+ });