cicy-desktop 2.1.90 → 2.1.92

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/cicy-desktop CHANGED
@@ -13,10 +13,13 @@ const GLOBAL_CONFIG_FILE = path.join(HOME, "global.json");
13
13
  const STATE_FILE = path.join(HOME, ".cicy-cluster.json");
14
14
  const LOGS_DIR = path.join(HOME, "logs");
15
15
  const PACKAGE_ROOT = path.join(__dirname, "..");
16
- // CN defaults: a fresh machine has no cached electron binary, so npx's electron
17
- // postinstall would hit GitHub releases and fail (ECOMPROMISED). Mirror it +
18
- // the npm registry to npmmirror (overridable via the same-named env vars).
19
- const ELECTRON_MIRROR = process.env.ELECTRON_MIRROR || "https://npmmirror.com/mirrors/electron/";
16
+ // CN/global defaults: a fresh machine has no cached electron binary, so the
17
+ // electron postinstall (and our own self-provisioning below) would otherwise
18
+ // hit GitHub releases, which is slow/blocked on many networks. Default to our
19
+ // own Cloudflare R2 mirror — the three platform zips we ship are mirrored at
20
+ // <ELECTRON_MIRROR>v<version>/electron-v<version>-<platform>.zip (+ SHASUMS256.txt),
21
+ // the exact layout @electron/get expects. Overridable via the env var.
22
+ const ELECTRON_MIRROR = process.env.ELECTRON_MIRROR || "https://r2.deepfetch.de5.net/electron/";
20
23
  const NPM_REGISTRY = process.env.CICY_NPM_REGISTRY || process.env.npm_config_registry || "https://registry.npmmirror.com";
21
24
  const MASTER_ENTRY = path.join(PACKAGE_ROOT, "src", "master", "master-main.js");
22
25
 
@@ -73,32 +76,40 @@ function globalElectronDir() {
73
76
  } catch { return null; }
74
77
  }
75
78
 
76
- // Make sure a USABLE electron exists before spawning the worker. Prefer a shared
77
- // GLOBAL electron (downloaded once, survives npx-cache eviction, and
78
- // resolveElectronSpawn() picks it first). If nothing healthy is found anywhere,
79
- // auto-install the PINNED version GLOBALLY with the npmmirror mirror — i.e. do the
80
- // `npm i -g electron@<pin>` step for the user, so a fresh `npx cicy-desktop`
81
- // self-provisions electron and starts first-try. Runs once per process.
79
+ // Make sure a USABLE electron exists before spawning the worker. A pre-existing
80
+ // GLOBAL electron is reused if present (downloaded once, shared). Otherwise we
81
+ // provision the electron binary LOCALLY into the bundled npx copy by re-running
82
+ // electron's own install.js against our R2 mirror — NO `npm i -g`, NO GitHub.
83
+ // This is what lets a single `npx cicy-desktop` self-provision electron and
84
+ // start first-try even when the install-time postinstall couldn't fetch the
85
+ // binary (GitHub blocked/slow). Runs once per process.
82
86
  let _electronEnsured = false;
83
87
  function ensureElectron() {
84
88
  if (_electronEnsured) return;
85
89
  _electronEnsured = true;
86
- // Already usable (global, or the resolvable bundled/npx copy)? nothing to do.
90
+ // Already usable (a shared global, or the resolvable bundled/npx copy)? done.
87
91
  if (electronBinaryHealthy(globalElectronDir())) return;
88
92
  let bundledDir = null;
89
93
  try { bundledDir = path.dirname(require.resolve("electron/package.json", { paths: [PACKAGE_ROOT] })); } catch {}
90
94
  if (electronBinaryHealthy(bundledDir)) return;
91
- // None usable → install the pinned electron globally (with the mirror).
92
- console.log(`⚙️ No usable Electron installing electron@${ELECTRON_VERSION} -g (mirror), one-time…`);
95
+ if (!bundledDir) {
96
+ console.warn(`⚠️ electron package not resolvable under ${PACKAGE_ROOT}; cannot self-provision.`);
97
+ return;
98
+ }
99
+ // The electron package is installed but its binary wasn't downloaded. Fetch it
100
+ // INTO node_modules/electron from the mirror (R2) by invoking electron's own
101
+ // installer — @electron/get reads ELECTRON_MIRROR and pulls
102
+ // <mirror>v<version>/electron-v<version>-<platform>.zip. Local, no global.
103
+ console.log(`⚙️ Fetching Electron ${ELECTRON_VERSION} from mirror (one-time)…`);
93
104
  try {
94
- execSync(`npm i -g electron@${ELECTRON_VERSION} --registry=${NPM_REGISTRY}`, {
105
+ execSync(`node "${path.join(bundledDir, "install.js")}"`, {
95
106
  stdio: "inherit",
107
+ cwd: bundledDir,
96
108
  env: { ...process.env, ELECTRON_MIRROR, npm_config_registry: NPM_REGISTRY },
97
109
  });
98
- _globalElectronBin = undefined; // bust the memo so resolveElectronSpawn re-finds the new global bin
99
110
  } catch (e) {
100
- console.warn(`⚠️ Global electron install failed: ${e.message}`);
101
- console.warn(` Fix manually: set ELECTRON_MIRROR=${ELECTRON_MIRROR} && npm i -g electron@${ELECTRON_VERSION}`);
111
+ console.warn(`⚠️ Electron download from mirror failed: ${e.message}`);
112
+ console.warn(` Retry: ELECTRON_MIRROR=${ELECTRON_MIRROR} node "${path.join(bundledDir, "install.js")}"`);
102
113
  }
103
114
  }
104
115
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cicy-desktop",
3
- "version": "2.1.90",
3
+ "version": "2.1.92",
4
4
  "description": "CiCy - AI-powered operating system browser",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -128,16 +128,16 @@
128
128
  "swagger-jsdoc": "^6.2.8",
129
129
  "swagger-ui-express": "^5.0.1",
130
130
  "zod": "3.25",
131
- "electron-updater": "6.6.2",
132
- "electron": "41.0.3"
131
+ "electron-updater": "6.6.2"
133
132
  },
134
133
  "//optionalDependencies": "Runtime Bundle v1 (主人指令): platform binaries delivered by `npm i -g cicy-desktop` itself — npm installs only the current-platform subpackage (os/cpu pinned in each), so first start seeds the runtime store with ZERO network, ZERO npx. Windows packages are named *-windows-* (npm spam filter 403s new names containing win32). cicy-msys2 added once published.",
135
134
  "optionalDependencies": {
136
- "cicy-code-darwin-x64": "2.3.11",
137
- "cicy-code-darwin-arm64": "2.3.11",
138
- "cicy-code-linux-x64": "2.3.11",
139
- "cicy-code-linux-arm64": "2.3.11",
140
- "cicy-code-windows-x64": "2.3.11",
135
+ "electron": "41.0.3",
136
+ "cicy-code-darwin-x64": "2.3.12",
137
+ "cicy-code-darwin-arm64": "2.3.12",
138
+ "cicy-code-linux-x64": "2.3.12",
139
+ "cicy-code-linux-arm64": "2.3.12",
140
+ "cicy-code-windows-x64": "2.3.12",
141
141
  "cicy-mihomo-darwin-x64": "1.10.4",
142
142
  "cicy-mihomo-darwin-arm64": "1.10.4",
143
143
  "cicy-mihomo-linux-x64": "1.10.4",
@@ -149,6 +149,7 @@
149
149
  "@babel/core": "^7.29.0",
150
150
  "@babel/preset-env": "^7.29.0",
151
151
  "axios": "^1.13.5",
152
+ "electron": "41.0.3",
152
153
  "electron-builder": "^26.7.0",
153
154
  "jest": "^29.7.0",
154
155
  "prettier": "^3.8.1",
@@ -64,12 +64,26 @@ async function execShell(command, opts = {}) {
64
64
 
65
65
  contextBridge.exposeInMainWorld("electronRPC", (tool, args) => rpc(tool, args));
66
66
 
67
- // i18n: expose t(key, opts) and current locale to render. Resources are
68
- // loaded once in the main process and shipped synchronously over IPC. Since
69
- // preload runs in the main process for context-isolated windows, we can
70
- // require the i18n module directly.
67
+ // i18n: expose t(key, opts) and current locale to render.
68
+ //
69
+ // IMPORTANT: this preload runs in the RENDERER process, so `require("../i18n")`
70
+ // gives a SEPARATE i18n instance from the main process — it has no idea what
71
+ // locale the app resolved. Left to its own lazy init() it would fall back to
72
+ // English (pickLocale(undefined) → FALLBACK), which is exactly why the homepage
73
+ // (e.g. the first-run terms gate) showed English even on zh-CN systems. So we
74
+ // pull the resolved locale from main over a synchronous IPC and init THIS
75
+ // instance with it. i18next.init() with inline resources is synchronous, so the
76
+ // language is set immediately and `locale` below reads the correct value.
71
77
  let __i18n;
72
- try { __i18n = require("../i18n"); } catch (e) { __i18n = null; }
78
+ try {
79
+ __i18n = require("../i18n");
80
+ let mainLng = "";
81
+ try { mainLng = ipcRenderer.sendSync("i18n:locale"); } catch (_) {}
82
+ __i18n.init(mainLng || undefined);
83
+ if (mainLng && __i18n.i18next.language !== __i18n.pickLocale(mainLng)) {
84
+ __i18n.i18next.changeLanguage(__i18n.pickLocale(mainLng));
85
+ }
86
+ } catch (e) { __i18n = null; }
73
87
  contextBridge.exposeInMainWorld("cicyI18n", {
74
88
  t: (key, opts) => {
75
89
  if (!__i18n) return key;
@@ -16,7 +16,7 @@ const { ipcMain } = require("electron");
16
16
  const sidecar = require("../sidecar/cicy-code");
17
17
  const docker = require("../sidecar/docker");
18
18
 
19
- const PORT = Number(process.env.CICY_CODE_PORT || (process.platform === "win32" ? 8007 : 8008));
19
+ const PORT = Number(process.env.CICY_CODE_PORT || 8008);
20
20
  let registered = false;
21
21
 
22
22
  function register({ sidecarLogPath } = {}) {
@@ -11,7 +11,7 @@ const sidecar = require("../sidecar/cicy-code");
11
11
  const { createWindow } = require("../utils/window-utils");
12
12
  const registry = require("./registry");
13
13
 
14
- const LOCAL_PORT = Number(process.env.CICY_CODE_PORT || (process.platform === "win32" ? 8007 : 8008));
14
+ const LOCAL_PORT = Number(process.env.CICY_CODE_PORT || 8008);
15
15
  const LOCAL_HOST = "127.0.0.1";
16
16
 
17
17
  // On a typical install cicy-code runs as the same user as cicy-desktop,
package/src/main.js CHANGED
@@ -81,6 +81,18 @@ const __initialLocale = (() => {
81
81
  })();
82
82
  i18n.init(__initialLocale);
83
83
 
84
+ // Synchronous bridge so the homepage preload — which runs in the RENDERER
85
+ // process and therefore holds its OWN separate i18n module instance — can
86
+ // resolve the same locale the main process picked. Without this the preload's
87
+ // lazy init() falls back to English regardless of OS language, so
88
+ // window.cicyI18n.t() returned English even on zh-CN systems. Read live at call
89
+ // time, so it reflects the ready-time changeLanguage() below.
90
+ try {
91
+ require("electron").ipcMain.on("i18n:locale", (e) => {
92
+ e.returnValue = (i18n.i18next && i18n.i18next.language) || i18n.FALLBACK || "en";
93
+ });
94
+ } catch {}
95
+
84
96
  // Single-instance lock: only one cicy-desktop process can hold the primary
85
97
  // instance. A second launch sends `second-instance` with argv to the primary
86
98
  // and exits itself. The primary focuses its homepage so the user sees the
@@ -867,7 +879,7 @@ electronApp.whenReady().then(async () => {
867
879
  // cloud team register + gateway-key injection when logged in. A fresh boot
868
880
  // may npm-seed the runtime first, so probe for up to ~90s before giving up.
869
881
  (async () => {
870
- const sidecarPort = Number(process.env.CICY_CODE_PORT || (process.platform === "win32" ? 8007 : 8008));
882
+ const sidecarPort = Number(process.env.CICY_CODE_PORT || 8008);
871
883
  const lt = require("./backends/local-teams");
872
884
  for (let i = 0; i < 30; i++) {
873
885
  try {
@@ -20,9 +20,7 @@ const net = require("net");
20
20
  const path = require("path");
21
21
  const { spawn, execFileSync } = require("child_process");
22
22
 
23
- // Default cicy-code port: 8007 on Windows, 8008 elsewhere (主人令). CICY_CODE_PORT
24
- // overrides on every platform.
25
- const DEFAULT_PORT = Number(process.env.CICY_CODE_PORT || (process.platform === "win32" ? 8007 : 8008));
23
+ const DEFAULT_PORT = Number(process.env.CICY_CODE_PORT || 8008);
26
24
 
27
25
  // Liveness = "is something LISTENING on :port", via a raw TCP connect — NOT an
28
26
  // HTTP GET. /health can block (mid-boot, busy, hung) and time out even while the
@@ -5,8 +5,7 @@
5
5
  // installed() → 磁盘 binary 版本(localbin manifest,诊断用)
6
6
  const http = require("http");
7
7
 
8
- // Match the sidecar default: 8007 on Windows, 8008 elsewhere (CICY_CODE_PORT wins).
9
- const DEFAULT_PORT = Number(process.env.CICY_CODE_PORT || (process.platform === "win32" ? 8007 : 8008));
8
+ const DEFAULT_PORT = 8008;
10
9
 
11
10
  // The ONE running-version reader. GET /api/health → version. Returns the version
12
11
  // string, or null on any failure / missing field. Used by the update flow's