cicy-desktop 2.1.100 → 2.1.102

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.100",
3
+ "version": "2.1.102",
4
4
  "description": "CiCy - AI-powered operating system browser",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -8,7 +8,7 @@
8
8
  // https://r2.deepfetch.de5.net/docker/cicy-code-latest.tar.gz
9
9
  //
10
10
  // The container maps :8008 and persists ~/cicy-ai in a named volume.
11
- const { execFile, spawn } = require("child_process");
11
+ const { execFile, execFileSync, spawn } = require("child_process");
12
12
  const https = require("https");
13
13
  const http = require("http");
14
14
  const fs = require("fs");
@@ -28,9 +28,30 @@ const DOCKER_DESKTOP_MIRROR = process.env.CICY_DOCKER_DESKTOP_MIRROR
28
28
  // CICY_* env vars forwarded into the container (team onboarding, version pin…).
29
29
  const PASS_ENV = ["CICY_TEAM_TOKEN", "CICY_CODE_VERSION", "NPM_REGISTRY", "CICY_NPM_REGISTRY", "CICY_AGENTS", "ENABLE_CDN", "CICY_CLOUDFLARED_TOKEN"];
30
30
 
31
+ // Resolve the docker CLI. CRITICAL on Windows: right after Docker Desktop
32
+ // installs, it adds `...\Docker\resources\bin` to the SYSTEM PATH — but the
33
+ // ALREADY-RUNNING cicy-desktop process keeps its stale PATH, so a bare
34
+ // `execFile("docker", …)` ENOENTs and dockerOk() stays false forever (the
35
+ // "已安装但起不来 / 卡在正在启动" bug). Probe the known absolute install paths
36
+ // first; only fall back to PATH (and never cache that fallback, so a later
37
+ // install is picked up).
38
+ let _dockerBin = null;
39
+ function dockerBin() {
40
+ if (_dockerBin) return _dockerBin;
41
+ if (process.platform === "win32") {
42
+ const cands = [
43
+ path.join(process.env["ProgramFiles"] || "C:\\Program Files", "Docker", "Docker", "resources", "bin", "docker.exe"),
44
+ path.join(process.env["ProgramW6432"] || "", "Docker", "Docker", "resources", "bin", "docker.exe"),
45
+ path.join(process.env["LOCALAPPDATA"] || "", "Docker", "Docker", "resources", "bin", "docker.exe"),
46
+ ].filter((c) => c && !c.startsWith("Docker"));
47
+ for (const c of cands) { try { if (fs.existsSync(c)) { _dockerBin = c; return c; } } catch {} }
48
+ }
49
+ return "docker"; // PATH fallback (mac/linux, or before Docker is installed)
50
+ }
51
+
31
52
  function run(args, { timeout = 30000 } = {}) {
32
53
  return new Promise((resolve, reject) => {
33
- execFile("docker", args, { timeout, windowsHide: true }, (err, stdout, stderr) => {
54
+ execFile(dockerBin(), args, { timeout, windowsHide: true }, (err, stdout, stderr) => {
34
55
  if (err) { err.stdout = String(stdout || ""); err.stderr = String(stderr || ""); return reject(err); }
35
56
  resolve({ stdout: String(stdout), stderr: String(stderr) });
36
57
  });
@@ -428,6 +449,35 @@ function launchElevated(exe, args, { emit } = {}) {
428
449
  });
429
450
  }
430
451
 
452
+ // Docker Desktop on Windows needs a WSL2 (or Hyper-V) backend — without it the
453
+ // engine never starts and `docker version` can't reach the daemon, so the card
454
+ // hangs on "正在启动 Docker Desktop". Detect a missing WSL. `wsl` prints UTF-16
455
+ // and a fresh Windows without the feature says "未安装 / not installed / can be
456
+ // installed by running wsl.exe --install".
457
+ function wslMissing() {
458
+ if (process.platform !== "win32") return false;
459
+ try {
460
+ const out = execFileSync("wsl", ["--status"], { timeout: 8000, windowsHide: true, encoding: "utf16le", stdio: ["ignore", "pipe", "pipe"] });
461
+ return /未安装|not installed|--install/i.test(String(out));
462
+ } catch (e) {
463
+ const s = String((e.stdout || "") + (e.stderr || ""));
464
+ if (/未安装|not installed|--install/i.test(s)) return true;
465
+ return false; // wsl present but errored for another reason — assume OK
466
+ }
467
+ }
468
+
469
+ // Ensure the WSL2 backend exists; install it (elevated) if missing. Returns
470
+ // { ok } when present, or { needsReboot } after kicking off `wsl --install`
471
+ // (which requires admin + a Windows reboot before Docker can use it).
472
+ async function ensureWsl({ emit } = {}) {
473
+ if (!wslMissing()) return { ok: true };
474
+ emit && emit({ phase: "install-docker", status: "running", message: "Docker 需要 WSL2 后端,正在安装 WSL(请在管理员授权框点「是」,装完需重启 Windows)…" });
475
+ // --no-distribution: just the WSL2 platform (Docker brings its own distro);
476
+ // falls back to plain `wsl --install` on older builds that reject the flag.
477
+ await launchElevated("wsl", ["--install", "--no-distribution"], { emit });
478
+ return { ok: false, needsReboot: true };
479
+ }
480
+
431
481
  // One-shot, idempotent, resumable bootstrap of the whole local-team stack on
432
482
  // Windows: install Docker (if missing) → load the base image (if missing) →
433
483
  // start the container → wait for :8008. Every step CHECKS first and SKIPS if
@@ -449,10 +499,14 @@ async function bootstrap({ onProgress, port = 8008, container = CONTAINER, volum
449
499
  // re-download/re-run the installer (主人: 装了就别再下 Docker Desktop 了).
450
500
  emit({ phase: "install-docker", status: "running", message: "Docker 已安装,正在启动 Docker Desktop…" });
451
501
  if (needImage) imgDl = downloadImageTarball({ emit }).catch((e) => { emit({ phase: "image", status: "error", message: `镜像下载失败:${e.message}` }); return null; });
502
+ // Docker Desktop installed but the engine needs the WSL2 backend — install
503
+ // it (elevated) if missing; it requires a reboot before the daemon can run.
504
+ const wsl1 = await ensureWsl({ emit });
505
+ if (wsl1.needsReboot) { emit({ phase: "install-docker", status: "error", message: "WSL2 正在安装——装好后请【重启 Windows】,重启后回来点「重试」即可继续。" }); return { ok: false, reason: "wsl_reboot_required" }; }
452
506
  startDockerDesktop();
453
507
  const up = await waitUntil(dockerOk, { totalMs: 300000, everyMs: 5000 });
454
508
  if (!up) {
455
- emit({ phase: "install-docker", status: "error", message: "Docker Desktop 启动超时——手动打开它等图标变绿,再点「重试」" });
509
+ emit({ phase: "install-docker", status: "error", message: "Docker Desktop 启动超时——确认 Docker 图标变绿(首次可能要重启 Windows),再点「重试」" });
456
510
  return { ok: false, reason: "docker_not_ready" };
457
511
  }
458
512
  emit({ phase: "install-docker", status: "done", message: "Docker 就绪" });
@@ -461,6 +515,9 @@ async function bootstrap({ onProgress, port = 8008, container = CONTAINER, volum
461
515
  // running + the daemon coming up (主人: 装 Docker 的同时下载 R2 镜像).
462
516
  if (needImage) imgDl = downloadImageTarball({ emit }).catch((e) => { emit({ phase: "image", status: "error", message: `镜像下载失败:${e.message}` }); return null; });
463
517
  await installDocker({ emit, dest: installDest });
518
+ // Docker Desktop needs WSL2 — install it (elevated) if missing; reboot first.
519
+ const wsl2 = await ensureWsl({ emit });
520
+ if (wsl2.needsReboot) { emit({ phase: "install-docker", status: "error", message: "Docker 和 WSL2 已装好——请【重启 Windows】,重启后回来点「重试」即可继续。" }); return { ok: false, reason: "wsl_reboot_required" }; }
464
521
  // A silent install doesn't auto-launch the daemon — explicitly start Docker
465
522
  // Desktop once its exe lands so the user doesn't have to (主人: 安装启动有问题).
466
523
  emit({ phase: "install-docker", status: "running", message: "启动 Docker Desktop…" });