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 +1 -1
- package/src/sidecar/docker.js +60 -3
package/package.json
CHANGED
package/src/sidecar/docker.js
CHANGED
|
@@ -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(
|
|
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…" });
|