cicy-desktop 2.1.294 → 2.1.295
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
|
@@ -155,7 +155,7 @@ function register({ sidecarLogPath } = {}) {
|
|
|
155
155
|
const reason = code === "EPERM" || /spawn EPERM/.test(err.message || "") ? "spawn_blocked" : "bootstrap_exception";
|
|
156
156
|
_lastBootstrapError = { reason, message: err.message, ts: Date.now() };
|
|
157
157
|
} else if (result && result.ok) {
|
|
158
|
-
_lastBootstrapError = null; _autoBootstrapPaused = null; return;
|
|
158
|
+
_lastBootstrapError = null; _autoBootstrapPaused = null; try { if (rebootCount() > 0) rebootCount(-99); } catch {} return;
|
|
159
159
|
} else if (result) {
|
|
160
160
|
_lastBootstrapError = { reason: result.reason || result.error || "bootstrap_failed", message: result.message || "", ts: Date.now() };
|
|
161
161
|
} else return;
|
|
@@ -169,9 +169,19 @@ function register({ sidecarLogPath } = {}) {
|
|
|
169
169
|
// WSL 功能启用后必须重启 Windows 才能继续。不等人:自动安排 90 秒后重启(桌面上会有
|
|
170
170
|
// 系统倒计时提示),UI 可「取消」或「立即重启」。每次开机只安排一次。
|
|
171
171
|
let _rebootScheduled = false;
|
|
172
|
+
// 防重启循环:WSL 始终起不来时不能每次开机都重启。计数存 db/wsl-reboots.json,最多自动重启 2 次
|
|
173
|
+
// (成功后 :8008 起来时清零)。
|
|
174
|
+
const REBOOT_COUNT_FILE = path.join(os.homedir(), "cicy-ai", "db", "wsl-reboots.json");
|
|
175
|
+
function rebootCount(delta) {
|
|
176
|
+
let c = 0; try { c = Number(JSON.parse(fs.readFileSync(REBOOT_COUNT_FILE, "utf8")).count) || 0; } catch {}
|
|
177
|
+
if (delta) { c = Math.max(0, c + delta); try { fs.mkdirSync(path.dirname(REBOOT_COUNT_FILE), { recursive: true }); fs.writeFileSync(REBOOT_COUNT_FILE, JSON.stringify({ count: c, ts: Date.now() })); } catch {} }
|
|
178
|
+
return c;
|
|
179
|
+
}
|
|
172
180
|
function scheduleReboot(delaySec = 90) {
|
|
173
181
|
if (_rebootScheduled || process.platform !== "win32") return false;
|
|
182
|
+
if (delaySec > 10 && rebootCount() >= 2) { log.warn("[docker] auto-reboot skipped: already rebooted 2× for WSL without success"); return false; }
|
|
174
183
|
_rebootScheduled = true;
|
|
184
|
+
rebootCount(+1);
|
|
175
185
|
try {
|
|
176
186
|
require("child_process").execFile("shutdown", ["/r", "/t", String(delaySec), "/c", "CiCy Desktop: WSL2 功能已启用,需要重启 Windows 才能继续安装 Docker;登录后会自动继续。可在 CiCy Desktop 里取消。"], { windowsHide: true }, (err) => {
|
|
177
187
|
if (err) { _rebootScheduled = false; log.warn(`[docker] schedule reboot failed: ${err.message}`); }
|
package/src/sidecar/docker.js
CHANGED
|
@@ -640,7 +640,9 @@ async function wslMissing() {
|
|
|
640
640
|
// wsl.exe 不存在(ENOENT):功能刚启用但还没重启 Windows 时就是这样 —— 按「缺失」处理,
|
|
641
641
|
// 让 ensureWsl 走 needsReboot,而不是当成已就绪去 --import 然后 spawn ENOENT。
|
|
642
642
|
if (err && (err.code === "ENOENT" || /ENOENT/.test(err.message || ""))) return resolve(true);
|
|
643
|
-
|
|
643
|
+
// 旧版内置 wsl.exe 存根:任何子命令都只打印用法帮助("wsl.exe [Argument]" / "--install")。
|
|
644
|
+
// 功能刚启用还没重启时就是这个状态 → 按缺失处理(ensureWsl 走 needsReboot)。
|
|
645
|
+
if (/未安装|not installed|--install|\[Argument\]|\[参数\]/i.test(s)) return resolve(true);
|
|
644
646
|
if (err && (err.killed || err.signal || err.code === "ETIMEDOUT")) return resolve(null); // timed out → unknown
|
|
645
647
|
resolve(false); // wsl present (errored for another reason → assume OK)
|
|
646
648
|
});
|
|
@@ -673,7 +675,7 @@ function wslFunctional() {
|
|
|
673
675
|
return new Promise((resolve) => {
|
|
674
676
|
execFile(wslExe(), ["-l", "-v"], { timeout: 25000, windowsHide: true, encoding: "utf16le" }, (err, stdout, stderr) => {
|
|
675
677
|
const s = String((stdout || "") + (stderr || "")).replace(/\u0000/g, "");
|
|
676
|
-
if (/NAME\s+STATE|没有已安装的分发版|no installed distributions|aka\.ms\/wslstore
|
|
678
|
+
if (/NAME\s+STATE|没有已安装的分发版|no installed distributions|aka\.ms\/wslstore|名称\s+状态/i.test(s)) return resolve(true);
|
|
677
679
|
resolve(false);
|
|
678
680
|
});
|
|
679
681
|
});
|
|
@@ -814,7 +816,14 @@ async function ensureWsl({ emit } = {}) {
|
|
|
814
816
|
const subsystemEnabled = await featureEnabled("Microsoft-Windows-Subsystem-Linux");
|
|
815
817
|
const vmPlatformEnabled = await featureEnabled("VirtualMachinePlatform");
|
|
816
818
|
const state = classifyWslPrerequisites({ executableMissing, subsystemEnabled, vmPlatformEnabled });
|
|
817
|
-
if (state.ready)
|
|
819
|
+
if (state.ready) {
|
|
820
|
+
// 功能都已启用、wsl.exe 也在,但 WSL 本身还不能用(`wsl -l -v` 只打印帮助)= 启用后还没
|
|
821
|
+
// 重启。别去 --import(必失败),直接进自动重启流程。
|
|
822
|
+
if (await wslFunctional()) return { ok: true };
|
|
823
|
+
log.warn("[bootstrap] ensure-wsl: features enabled but WSL not functional yet (pending reboot)");
|
|
824
|
+
emit && emit({ phase: "install-docker", status: "running", message: "WSL 功能已启用但尚未生效,需要重启 Windows…" });
|
|
825
|
+
return { ok: false, needsReboot: true };
|
|
826
|
+
}
|
|
818
827
|
|
|
819
828
|
emit && emit({ phase: "install-docker", status: "running", message: "Docker 需要 WSL2 后端,开始启用所需的 Windows 功能(会弹一次 UAC,请点「是」)…" });
|
|
820
829
|
const r = await elevatedWslSetup({ emit, need: { subsystem: !subsystemEnabled, vmPlatform: !vmPlatformEnabled } });
|
|
@@ -241,12 +241,14 @@ function importTarball(dest, installDir) {
|
|
|
241
241
|
// and the caller can `wsl --shutdown` + retry instead of hanging 10 minutes.
|
|
242
242
|
execFile(docker.wslExe(), ["--import", DISTRO, installDir, dest, "--version", "2"],
|
|
243
243
|
{ timeout: 240000, windowsHide: true, encoding: "buffer" },
|
|
244
|
-
(err,
|
|
244
|
+
(err, so, se) => {
|
|
245
245
|
if (err) {
|
|
246
|
-
// wsl.exe 的报错是 UTF-16
|
|
247
|
-
//
|
|
246
|
+
// wsl.exe 的报错是 UTF-16 输出,而且多半写在 stdout(不是 stderr)(常见:「请启用'虚拟机
|
|
247
|
+
// 平台'Windows 功能并确保在 BIOS 中启用虚拟化」);两路都解码后拼进 message,抽屉才能显示
|
|
248
|
+
// 真正原因而不是「Command failed」。
|
|
248
249
|
let text = "";
|
|
249
|
-
try {
|
|
250
|
+
const dec = (b) => { try { return Buffer.isBuffer(b) ? b.toString("utf16le") : String(b || ""); } catch { return ""; } };
|
|
251
|
+
text = (dec(so) + " " + dec(se));
|
|
250
252
|
text = text.replace(/\u0000/g, "").replace(/\s+/g, " ").trim();
|
|
251
253
|
if (text) err.message = `${err.message.split("\n")[0]} — ${text.slice(0, 300)}`;
|
|
252
254
|
err.stderr = text;
|
|
@@ -28,7 +28,7 @@ test("featureEnabled reads Win32_OptionalFeature (works unelevated), dism only a
|
|
|
28
28
|
test("wsl --import failures carry wsl.exe's decoded message into the drawer", () => {
|
|
29
29
|
const src = read("src/sidecar/wsl-docker.js");
|
|
30
30
|
assert.match(src, /encoding: "buffer"/);
|
|
31
|
-
assert.match(src, /
|
|
31
|
+
assert.match(src, /toString\("utf16le"\)/);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
test("keepalive logon task degrades to LeastPrivilege / HKCU Run when not elevated", () => {
|
|
@@ -64,3 +64,11 @@ test("auto-update defaults to on", () => {
|
|
|
64
64
|
test("a missing wsl.exe (ENOENT, features enabled but not yet rebooted) counts as WSL missing", () => {
|
|
65
65
|
assert.match(read("src/sidecar/docker.js"), /err\.code === "ENOENT"[^\n]*return resolve\(true\)/);
|
|
66
66
|
});
|
|
67
|
+
|
|
68
|
+
test("features enabled but WSL still the pre-reboot stub → needsReboot; auto-reboot capped at 2", () => {
|
|
69
|
+
const d = read("src/sidecar/docker.js");
|
|
70
|
+
assert.match(d, /if \(await wslFunctional\(\)\) return \{ ok: true \};/);
|
|
71
|
+
assert.match(d, /\[Argument\\\]/);
|
|
72
|
+
const ipc = read("src/backends/sidecar-ipc.js");
|
|
73
|
+
assert.match(ipc, /rebootCount\(\) >= 2/);
|
|
74
|
+
});
|