cicy-desktop 2.1.318 → 2.1.319

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.318",
3
+ "version": "2.1.319",
4
4
  "description": "CiCy - AI-powered operating system browser",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -172,7 +172,15 @@ function register({ sidecarLogPath } = {}) {
172
172
  let _rebootScheduled = false;
173
173
  let _tcpRepairDone = false; // Windows 端口修复每次进程只做一次(需要 UAC)
174
174
  let _relayMissesAfterRepair = 0; // 端口修复后仍连不上的轮数 → 3 轮自动重启
175
- let _relayMisses = 0, _relayResetDone = false; // WSL localhost 转发自愈(见 daemon 循环)
175
+ let _relayMisses = 0; // WSL localhost 转发自愈(见 daemon 循环)
176
+ // 「每次开机只 wsl --shutdown 一次」必须跨进程持久化:桌面端自动更新/崩溃重开后内存标记归零,
177
+ // 实测 XS-MASTER 上每隔几分钟就 shutdown 一次,把容器和所有 agent 一起杀掉,还引发 :8008 的
178
+ // 重连风暴(14k TIME_WAIT → 端口耗尽 → 误判不通 → 再 shutdown)的死循环。按开机时间戳存文件。
179
+ const RELAY_RESET_FILE = path.join(os.homedir(), "cicy-ai", "db", "wsl-relay-reset.json");
180
+ const bootStamp = () => Math.round((Date.now() - os.uptime() * 1000) / 60000); // 开机分钟戳(±1 分钟抖动无所谓)
181
+ const relayResetDoneThisBoot = () => { try { const j = JSON.parse(fs.readFileSync(RELAY_RESET_FILE, "utf8")); return Math.abs(Number(j.boot) - bootStamp()) <= 2; } catch { return false; } };
182
+ const markRelayReset = () => { try { fs.mkdirSync(path.dirname(RELAY_RESET_FILE), { recursive: true }); fs.writeFileSync(RELAY_RESET_FILE, JSON.stringify({ boot: bootStamp(), ts: Date.now() })); } catch {} };
183
+ let _relayResetDone = relayResetDoneThisBoot();
176
184
  let _unknownStreak = 0, _wslRepairDone = false; // WSL 卡死自愈(见 daemon 循环)
177
185
  let _softRetryAt = 0; // 非硬失败(如 docker_install_failed)也退避 2 分钟,别每 35 秒重跑一遍
178
186
  // 防重启循环:WSL 始终起不来时不能每次开机都重启。计数存 db/wsl-reboots.json,最多自动重启 2 次
@@ -271,7 +279,7 @@ function register({ sidecarLogPath } = {}) {
271
279
  if (inside) {
272
280
  _relayMisses += 1;
273
281
  if (_relayMisses >= 2 && !_relayResetDone) {
274
- _relayResetDone = true;
282
+ _relayResetDone = true; markRelayReset();
275
283
  auditDestructiveIpc(log, "docker:self-heal-localhost-relay", null, { container: APP_CONTAINER, misses: _relayMisses });
276
284
  log.warn("[docker-daemon] container healthy inside WSL but 127.0.0.1:8008 unreachable from Windows → wsl --shutdown once to rebuild the localhost relay");
277
285
  try { await appDocker.wslShutdown(); } catch (e) { log.warn(`[docker-daemon] wsl --shutdown failed: ${e.message}`); }
@@ -55,3 +55,10 @@ test("cloud title reconcile is rate-limited (no TIME_WAIT storm on Windows)", ()
55
55
  const main = readSrc("src/main.js");
56
56
  assert.equal((main.match(/syncAllLocalTeams\(\{ force: true \}\)/g) || []).length, 2);
57
57
  });
58
+
59
+ test("the once-per-boot wsl --shutdown relay reset is persisted across desktop restarts", () => {
60
+ const s = readSrc("src/backends/sidecar-ipc.js");
61
+ assert.match(s, /wsl-relay-reset\.json/);
62
+ assert.match(s, /let _relayResetDone = relayResetDoneThisBoot\(\);/);
63
+ assert.match(s, /_relayResetDone = true; markRelayReset\(\);/);
64
+ });