cicy-desktop 2.1.321 → 2.1.323

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/bin/preinstall.sh CHANGED
@@ -1,32 +1,32 @@
1
- #!/bin/bash
2
-
3
- set -e
4
-
5
- echo "Installing electron globally..."
6
-
7
- if [ "$(uname)" = "Linux" ]; then
8
- sudo npm install -g electron
9
- else
10
- npm install -g electron
11
- fi
12
-
13
- if [ "$(uname)" = "Linux" ]; then
14
- echo "Fixing chrome-sandbox permissions on Linux..."
15
- ELECTRON_PATH=$(which electron)
16
- ELECTRON_DIR=$(dirname "$ELECTRON_PATH")
17
- SANDBOX_PATH="$ELECTRON_DIR/../lib/node_modules/electron/dist/chrome-sandbox"
18
-
19
- if [ -f "$SANDBOX_PATH" ]; then
20
- if command -v sudo &> /dev/null; then
21
- sudo chown root:root "$SANDBOX_PATH"
22
- sudo chmod 4755 "$SANDBOX_PATH"
23
- echo "chrome-sandbox permissions fixed"
24
- else
25
- echo "Warning: sudo not available, skipping chrome-sandbox permissions"
26
- fi
27
- else
28
- echo "Warning: chrome-sandbox not found at $SANDBOX_PATH"
29
- fi
30
- fi
31
-
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ echo "Installing electron globally..."
6
+
7
+ if [ "$(uname)" = "Linux" ]; then
8
+ sudo npm install -g electron
9
+ else
10
+ npm install -g electron
11
+ fi
12
+
13
+ if [ "$(uname)" = "Linux" ]; then
14
+ echo "Fixing chrome-sandbox permissions on Linux..."
15
+ ELECTRON_PATH=$(which electron)
16
+ ELECTRON_DIR=$(dirname "$ELECTRON_PATH")
17
+ SANDBOX_PATH="$ELECTRON_DIR/../lib/node_modules/electron/dist/chrome-sandbox"
18
+
19
+ if [ -f "$SANDBOX_PATH" ]; then
20
+ if command -v sudo &> /dev/null; then
21
+ sudo chown root:root "$SANDBOX_PATH"
22
+ sudo chmod 4755 "$SANDBOX_PATH"
23
+ echo "chrome-sandbox permissions fixed"
24
+ else
25
+ echo "Warning: sudo not available, skipping chrome-sandbox permissions"
26
+ fi
27
+ else
28
+ echo "Warning: chrome-sandbox not found at $SANDBOX_PATH"
29
+ fi
30
+ fi
31
+
32
32
  echo "Electron setup complete"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cicy-desktop",
3
- "version": "2.1.321",
3
+ "version": "2.1.323",
4
4
  "description": "CiCy - AI-powered operating system browser",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -144,10 +144,10 @@
144
144
  "//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 着无害。",
145
145
  "optionalDependencies": {
146
146
  "electron": "41.0.3",
147
- "cicy-code-darwin-x64": "2.3.589",
148
- "cicy-code-darwin-arm64": "2.3.589",
149
- "cicy-code-linux-x64": "2.3.589",
150
- "cicy-code-linux-arm64": "2.3.589",
147
+ "cicy-code-darwin-x64": "2.3.592",
148
+ "cicy-code-darwin-arm64": "2.3.592",
149
+ "cicy-code-linux-x64": "2.3.592",
150
+ "cicy-code-linux-arm64": "2.3.592",
151
151
  "cicy-code-windows-x64": "2.3.193",
152
152
  "cicy-mihomo-darwin-x64": "1.10.4",
153
153
  "cicy-mihomo-darwin-arm64": "1.10.4",
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env bash
2
+ # Superseded by the cicy-exe-deploy skill (cicy-skills/skills/cicy-exe-deploy) — kept as a plain-ssh fallback.
2
3
  # Push a locally built CiCy Desktop installer to every Windows node and install
3
4
  # it there — no CDN, no auto-update wait.
4
5
  #
5
6
  # scripts/deploy-exe-fleet.sh [path/to/CiCy Desktop Setup X.Y.Z.exe] [node...]
6
7
  #
7
8
  # Per node (ssh alias from ~/.ssh/config, e.g. xs-1001 — frp via ws-hub):
8
- # 1. scp the exe into the node's ~/projects (= C:\ of its Windows host)
9
+ # 1. scp the exe into the node's ~/projects (= C:\projects of its Windows host)
9
10
  # 2. ssh -L tunnel to the node's cicy-code :8008, ask it for connected
10
11
  # cicy-desktop clients (agent-desktop clients)
11
12
  # 3. agent-desktop exec-file a tiny .bat on each Windows client that runs the
@@ -24,12 +25,12 @@ fi
24
25
  shift || true
25
26
  VER=$(basename "$EXE" | sed -E 's/.*Setup ([0-9.]+)\.exe/\1/')
26
27
  REMOTE_NAME="CiCy-Desktop-Setup-$VER.exe"
27
- WIN_PATH="C:\\$REMOTE_NAME"
28
+ WIN_PATH="C:\\projects\\$REMOTE_NAME"
28
29
 
29
30
  if [ $# -gt 0 ]; then NODES="$*"; fi
30
31
  if [ -z "${NODES:-}" ]; then
31
32
  # every xs-* alias in ~/.ssh/config (Windows hosts); mac-local etc. are not exe targets
32
- NODES=$(awk '/^Host xs-/{print $2}' ~/.ssh/config | sort -V)
33
+ NODES=$(awk '/^Host xs-/ && $2 !~ /[*?]/{print $2}' ~/.ssh/config | sort -V)
33
34
  fi
34
35
  PORT=${LOCAL_PORT_BASE:-18100}
35
36
  SCRATCH=$(mktemp -d)
@@ -62,20 +62,72 @@ function desktopInstanceId() {
62
62
  return id;
63
63
  }
64
64
 
65
- async function hubFetch(route, { method = "GET", token = "", body = null } = {}) {
65
+ // Electron's net.fetch goes through Chromium's network stack it honours the
66
+ // OS proxy settings (PAC / system proxy) like the renderer does. Node's global
67
+ // fetch ignores them, which showed up as "fetch failed" on PCs that can only
68
+ // reach the hub through a proxy. Falls back to global fetch outside Electron.
69
+ function pickFetch() {
70
+ try { const { net, app } = require("electron"); if (net && typeof net.fetch === "function" && app && app.isReady()) return net.fetch.bind(net); } catch {}
71
+ return fetch;
72
+ }
73
+
74
+ async function hubFetch(route, { method = "GET", token = "", body = null, tries = 2 } = {}) {
75
+ let lastErr;
76
+ for (let attempt = 0; attempt < tries; attempt++) {
77
+ const ctrl = new AbortController();
78
+ const t = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
79
+ try {
80
+ const headers = { accept: "application/json" };
81
+ if (token) headers.authorization = "Bearer " + token;
82
+ if (body != null) headers["content-type"] = "application/json";
83
+ const r = await pickFetch()(hubOrigin() + route, { method, headers, body: body == null ? undefined : JSON.stringify(body), signal: ctrl.signal, cache: "no-store" });
84
+ const text = await r.text();
85
+ let json = null;
86
+ try { json = text ? JSON.parse(text) : null; } catch {}
87
+ return { status: r.status, ok: r.ok, json, text };
88
+ } catch (e) {
89
+ lastErr = e;
90
+ log.warn(`[hub] ${method} ${route} failed (${attempt + 1}/${tries}): ${e.message}`);
91
+ if (attempt + 1 < tries) await new Promise((r) => setTimeout(r, 1500));
92
+ } finally { clearTimeout(t); }
93
+ }
94
+ const msg = String((lastErr && lastErr.message) || lastErr || "fetch failed");
95
+ throw new Error(/fetch failed|ECONN|ENOTFOUND|abort/i.test(msg) ? `hub unreachable (${hubOrigin()}): ${msg}` : msg);
96
+ }
97
+
98
+ // ── fallback: the local cicy-code sidecar ───────────────────────────────────
99
+ // Some PCs cannot reach the hub directly at all (no proxy on Windows) while the
100
+ // cicy-code container on the same PC can (it carries its own proxy) and is
101
+ // signed in to the same hub account. Its /api/im/cicy-cloud/* routes give us
102
+ // the same instance list and one-time open grants, so use them when the direct
103
+ // call dies with a network error.
104
+ const SIDECAR_PORT = Number(process.env.CICY_SIDECAR_PORT || 8008);
105
+ let _sidecarTok = { value: "", at: 0 };
106
+ async function sidecarToken() {
107
+ if (_sidecarTok.value && Date.now() - _sidecarTok.at < 5 * 60 * 1000) return _sidecarTok.value;
108
+ let tok = "";
109
+ try { tok = String(readGlobalConfig(GLOBAL_JSON)?.api_token || "").trim(); } catch {}
110
+ if (!tok && process.platform === "win32") {
111
+ try { tok = String(await require("../sidecar/wsl-docker").readContainerToken(SIDECAR_PORT) || "").trim(); } catch (e) { log.warn(`[hub] sidecar token: ${e.message}`); }
112
+ }
113
+ if (tok) _sidecarTok = { value: tok, at: Date.now() };
114
+ return tok;
115
+ }
116
+ async function sidecarFetch(route, { method = "GET", body = null } = {}) {
117
+ const tok = await sidecarToken();
118
+ if (!tok) throw new Error("local cicy-code token unavailable");
66
119
  const ctrl = new AbortController();
67
120
  const t = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
68
121
  try {
69
- const headers = { accept: "application/json" };
70
- if (token) headers.authorization = "Bearer " + token;
122
+ const headers = { accept: "application/json", authorization: "Bearer " + tok };
71
123
  if (body != null) headers["content-type"] = "application/json";
72
- const r = await fetch(hubOrigin() + route, { method, headers, body: body == null ? undefined : JSON.stringify(body), signal: ctrl.signal, cache: "no-store" });
124
+ const r = await fetch(`http://127.0.0.1:${SIDECAR_PORT}${route}`, { method, headers, body: body == null ? undefined : JSON.stringify(body), signal: ctrl.signal, cache: "no-store" });
73
125
  const text = await r.text();
74
- let json = null;
75
- try { json = text ? JSON.parse(text) : null; } catch {}
126
+ let json = null; try { json = text ? JSON.parse(text) : null; } catch {}
76
127
  return { status: r.status, ok: r.ok, json, text };
77
128
  } finally { clearTimeout(t); }
78
129
  }
130
+ const isNetErr = (e) => /unreachable|fetch failed|ECONN|ENOTFOUND|abort/i.test(String((e && e.message) || e));
79
131
 
80
132
  function errorOf(res, fallback) {
81
133
  return (res && res.json && (res.json.error || res.json.message)) || fallback || `HTTP ${res && res.status}`;
@@ -168,18 +220,27 @@ function clearAuth() {
168
220
  async function instances() {
169
221
  const a = readAuth();
170
222
  if (!a) return { ok: false, error: "not_logged_in", instances: [] };
171
- const res = await hubFetch("/api/instances", { token: a.token });
172
- if (res.status === 401) { clearAuth(); return { ok: false, error: "unauthorized", instances: [] }; }
223
+ let res, viaSidecar = false;
224
+ try { res = await hubFetch("/api/instances", { token: a.token }); }
225
+ catch (e) {
226
+ if (!isNetErr(e)) return { ok: false, error: e.message, instances: [] };
227
+ log.warn(`[hub] direct instance list failed (${e.message}); trying the local cicy-code`);
228
+ try { res = await sidecarFetch("/api/im/cicy-cloud/instances"); viaSidecar = true; }
229
+ catch (e2) { return { ok: false, error: `${e.message}; sidecar: ${e2.message}`, instances: [] }; }
230
+ }
231
+ if (res.status === 401 && !viaSidecar) { clearAuth(); return { ok: false, error: "unauthorized", instances: [] }; }
173
232
  if (!res.ok || !res.json) return { ok: false, error: errorOf(res, "instances_failed"), instances: [] };
174
233
  const list = Array.isArray(res.json.instances) ? res.json.instances : [];
175
234
  const out = list
176
- .filter((i) => !i.self && !String(i.platform || "").startsWith("desktop"))
235
+ // direct: `self` is this desktop's hidden pseudo-instance; via sidecar: `self` is the
236
+ // local node itself, which IS a real instance the user may want to open.
237
+ .filter((i) => (viaSidecar || !i.self) && !String(i.platform || "").startsWith("desktop"))
177
238
  .map((i) => ({
178
239
  id: i.instanceId,
179
- name: i.name || i.proxyHost || i.instanceId,
240
+ name: i.name || (i.proxyHost ? String(i.proxyHost).split(".")[0] : i.instanceId),
180
241
  host: i.proxyHost || "",
181
242
  url: i.proxyHost ? "https://" + i.proxyHost : "",
182
- online: !!i.online,
243
+ online: i.status ? i.status === "online" : !!i.online,
183
244
  reachable: !!i.proxyAvailable,
184
245
  version: i.version || "",
185
246
  platform: i.platform || "",
@@ -192,14 +253,22 @@ async function instances() {
192
253
  agents: Array.isArray(i.agents) ? i.agents.length : undefined,
193
254
  }))
194
255
  .sort((x, y) => Number(y.online) - Number(x.online) || x.name.localeCompare(y.name));
195
- return { ok: true, owner: res.json.owner || a.owner, instances: out };
256
+ return { ok: true, owner: res.json.owner || a.owner, viaSidecar, instances: out };
196
257
  }
197
258
 
198
259
  // One-time hand-off URL for an instance (optionally one of its local ports).
199
260
  async function grantUrl({ id, port = 0, next = "/" } = {}) {
200
261
  const a = readAuth();
201
262
  if (!a) throw new Error("not_logged_in");
202
- const res = await hubFetch("/api/gateway/grant", { method: "POST", token: a.token, body: { instanceId: String(id || ""), port: Number(port) || 0, next: String(next || "/") } });
263
+ let res;
264
+ try { res = await hubFetch("/api/gateway/grant", { method: "POST", token: a.token, body: { instanceId: String(id || ""), port: Number(port) || 0, next: String(next || "/") } }); }
265
+ catch (e) {
266
+ if (!isNetErr(e)) throw e;
267
+ log.warn(`[hub] direct grant failed (${e.message}); trying the local cicy-code`);
268
+ res = await sidecarFetch("/api/im/cicy-cloud/open", { method: "POST", body: { instance_id: String(id || ""), port: Number(port) || 0, next: String(next || "/") } });
269
+ if (!res.ok || !res.json || !res.json.url) throw new Error(errorOf(res, "grant_failed"));
270
+ return { url: res.json.url, host: res.json.host };
271
+ }
203
272
  if (res.status === 401) { clearAuth(); throw new Error("unauthorized"); }
204
273
  if (!res.ok || !res.json || !res.json.url) throw new Error(errorOf(res, "grant_failed"));
205
274
  return { url: res.json.url, host: res.json.host };
package/src/config.js CHANGED
@@ -1,11 +1,11 @@
1
1
  // Copyright 2026 CiCy AI
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- module.exports = {
5
- config: {
6
- port: null,
7
- logsDir: null,
8
- logFilePath: null,
9
- oneWindow: false,
10
- },
11
- };
4
+ module.exports = {
5
+ config: {
6
+ port: null,
7
+ logsDir: null,
8
+ logFilePath: null,
9
+ oneWindow: false,
10
+ },
11
+ };