cicy-desktop 2.1.295 → 2.1.296
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
|
@@ -148,7 +148,7 @@ function register({ sidecarLogPath } = {}) {
|
|
|
148
148
|
let _autoBootstrapRetryAt = 0; // 硬失败后的自动重试时间点(退避,不是永久暂停:用户不会自己修)
|
|
149
149
|
// 退避:子进程被拦(安全软件开机后一段时间会自己放行)2 分钟一试;其余 15 分钟一试。
|
|
150
150
|
const AUTO_RETRY_MS = { spawn_blocked: 2 * 60 * 1000, default: 15 * 60 * 1000 };
|
|
151
|
-
const HARD_BOOTSTRAP_REASONS = new Set(["spawn_blocked", "virtualization_disabled", "wsl_enable_failed", "wsl_reboot_required"]);
|
|
151
|
+
const HARD_BOOTSTRAP_REASONS = new Set(["spawn_blocked", "virtualization_disabled", "wsl_enable_failed", "wsl_kernel_missing", "wsl_reboot_required"]);
|
|
152
152
|
function recordBootstrapResult(result, err) {
|
|
153
153
|
if (err) {
|
|
154
154
|
const code = err && err.code;
|
package/src/sidecar/docker.js
CHANGED
|
@@ -667,6 +667,18 @@ function featureEnabled(feature) {
|
|
|
667
667
|
});
|
|
668
668
|
}
|
|
669
669
|
|
|
670
|
+
const WSL_KERNEL_MSI_URL = process.env.CICY_WSL_KERNEL_URL || "https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi";
|
|
671
|
+
// WSL2 kernel present? The inbox (feature-based) WSL keeps it at System32\lxss\tools\kernel;
|
|
672
|
+
// the Store WSL bundles its own, in which case the file is absent but `wsl --status`
|
|
673
|
+
// reports a kernel version — callers treat "functional + version" as present too.
|
|
674
|
+
function wslKernelPresent() {
|
|
675
|
+
if (process.platform !== "win32") return true;
|
|
676
|
+
const sysroot = process.env.SystemRoot || process.env.windir || "C:\\Windows";
|
|
677
|
+
try { if (fs.existsSync(path.join(sysroot, "System32", "lxss", "tools", "kernel"))) return true; } catch {}
|
|
678
|
+
try { if (fs.existsSync(path.join(process.env.LOCALAPPDATA || "", "Microsoft", "WindowsApps", "wsl.exe")) && fs.existsSync(path.join(process.env.ProgramFiles || "C:\\Program Files", "WSL", "wsl.exe"))) return true; } catch {}
|
|
679
|
+
return false;
|
|
680
|
+
}
|
|
681
|
+
|
|
670
682
|
// Functional WSL probe that needs no elevation: `wsl -l -v` on a machine with
|
|
671
683
|
// the features enabled (and rebooted) either lists distros or says "no installed
|
|
672
684
|
// distributions"; with the features off it prints the "--install" help text.
|
|
@@ -719,17 +731,18 @@ function elevatedWslSetup({ emit, need = {} } = {}) {
|
|
|
719
731
|
`$R = "${resultFile.replace(/"/g, '""')}"`,
|
|
720
732
|
'function Say($m) { Add-Content -Path $R -Value ("[" + (Get-Date -Format "HH:mm:ss") + "] " + $m) }',
|
|
721
733
|
'function St($n) { try { (Get-CimInstance Win32_OptionalFeature -Filter ("Name=\'" + $n + "\'")).InstallState } catch { 0 } }',
|
|
722
|
-
'
|
|
734
|
+
'$S32 = Join-Path $env:SystemRoot "System32"; $DISM = Join-Path $S32 "dism.exe"; $SFC = Join-Path $S32 "sfc.exe"; $MSIEXEC = Join-Path $S32 "msiexec.exe"',
|
|
735
|
+
'function En($n) { & $DISM /online /enable-feature /featurename:$n /all /norestart | Out-Null; $c = $LASTEXITCODE; Say ("enable " + $n + " exit=" + $c); return $c }',
|
|
723
736
|
'Say "start admin=$(([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(\'Administrators\'))"',
|
|
724
737
|
'$feats = @("Microsoft-Windows-Subsystem-Linux","VirtualMachinePlatform")',
|
|
725
738
|
'$bad = @(); foreach ($f in $feats) { if ((St $f) -ne 1) { $c = En $f; if ($c -ne 0 -and $c -ne 3010) { $bad += $f } } }',
|
|
726
739
|
'if ($bad.Count -gt 0) {',
|
|
727
740
|
' Say "repair: DISM /RestoreHealth (component store refused: $($bad -join \',\')) — this can take 10-30 min"',
|
|
728
|
-
'
|
|
729
|
-
'
|
|
741
|
+
' & $DISM /online /cleanup-image /restorehealth /norestart | Out-Null; Say ("restorehealth exit=" + $LASTEXITCODE)',
|
|
742
|
+
' & $SFC /scannow | Out-Null; Say ("sfc exit=" + $LASTEXITCODE)',
|
|
730
743
|
' foreach ($f in $bad) { $c = En $f }',
|
|
731
744
|
'}',
|
|
732
|
-
`if (Test-Path "${msi.replace(/"/g, '""')}") { Start-Process
|
|
745
|
+
`if (Test-Path "${msi.replace(/"/g, '""')}") { $p = Start-Process $MSIEXEC -ArgumentList "/i","\`"${msi.replace(/"/g, '""')}\`"","/qn","/norestart" -Wait -PassThru; Say ("kernel msi exit=" + $p.ExitCode) }`,
|
|
733
746
|
'Say ("final subsystem=" + (St "Microsoft-Windows-Subsystem-Linux") + " vmPlatform=" + (St "VirtualMachinePlatform"))',
|
|
734
747
|
'Say "DONE"',
|
|
735
748
|
].join("\r\n");
|
|
@@ -819,10 +832,27 @@ async function ensureWsl({ emit } = {}) {
|
|
|
819
832
|
if (state.ready) {
|
|
820
833
|
// 功能都已启用、wsl.exe 也在,但 WSL 本身还不能用(`wsl -l -v` 只打印帮助)= 启用后还没
|
|
821
834
|
// 重启。别去 --import(必失败),直接进自动重启流程。
|
|
822
|
-
if (await wslFunctional())
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
835
|
+
if (!(await wslFunctional())) {
|
|
836
|
+
log.warn("[bootstrap] ensure-wsl: features enabled but WSL not functional yet (pending reboot)");
|
|
837
|
+
emit && emit({ phase: "install-docker", status: "running", message: "WSL 功能已启用但尚未生效,需要重启 Windows…" });
|
|
838
|
+
return { ok: false, needsReboot: true };
|
|
839
|
+
}
|
|
840
|
+
// WSL2 内核(lxss\tools\kernel)缺失时 --import 也必失败。先把 MSI 下到 Downloads,再用
|
|
841
|
+
// 同一个提权脚本装(功能已启用的话脚本只做装内核这一件事)。
|
|
842
|
+
if (!wslKernelPresent()) {
|
|
843
|
+
const msi = path.join(os.homedir(), "Downloads", "wsl_update_x64.msi");
|
|
844
|
+
try { await ensureDownloaded(WSL_KERNEL_MSI_URL, msi, null, { emit, phase: "install-docker", label: "下载 WSL2 内核" }); }
|
|
845
|
+
catch (e) { log.warn(`[bootstrap] kernel msi download failed: ${e.message}`); }
|
|
846
|
+
emit && emit({ phase: "install-docker", status: "running", message: "WSL2 内核未安装,开始安装(会弹一次 UAC,请点「是」)…" });
|
|
847
|
+
const r = await elevatedWslSetup({ emit, need: {} });
|
|
848
|
+
if (!wslKernelPresent()) {
|
|
849
|
+
const message = `WSL2 内核未能安装${r.detail ? `(${r.detail})` : "(UAC 未确认或 MSI 安装失败)"}——会自动重试。`;
|
|
850
|
+
log.error(`[bootstrap] ✗ ensure-wsl reason=wsl_kernel_missing ${r.detail || ""}`);
|
|
851
|
+
emit && emit({ phase: "done", status: "error", message });
|
|
852
|
+
return { ok: false, needsReboot: false, failed: true, reason: "wsl_kernel_missing", message };
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
return { ok: true };
|
|
826
856
|
}
|
|
827
857
|
|
|
828
858
|
emit && emit({ phase: "install-docker", status: "running", message: "Docker 需要 WSL2 后端,开始启用所需的 Windows 功能(会弹一次 UAC,请点「是」)…" });
|
|
@@ -939,7 +969,7 @@ module.exports = {
|
|
|
939
969
|
start, stop, stopContainer, restart, checkStatus, loadImage, loadImageFromTarball,
|
|
940
970
|
downloadImageTarball, imagePresent, dockerOk, installDocker,
|
|
941
971
|
bootstrap, probeHealth, readContainerToken, dockerDesktopExe, desktopDir, downloadsDir, imageTarballPath,
|
|
942
|
-
launchElevated, elevatedWslSetup, spawnProbe, wslFunctional, wslExe, wslMissing, ensureWsl, virtualizationStatus,
|
|
972
|
+
launchElevated, elevatedWslSetup, spawnProbe, wslFunctional, wslKernelPresent, wslExe, wslMissing, ensureWsl, virtualizationStatus,
|
|
943
973
|
// platform-agnostic download/retry primitives, reused by native.js
|
|
944
974
|
ensureDownloaded, curlDownload, withRetry, waitUntil, run, headSize,
|
|
945
975
|
// image freshness (修「重建仍用旧镜像」—— 校验 OSS ETag 变了才重下重载)
|
|
@@ -67,8 +67,15 @@ test("a missing wsl.exe (ENOENT, features enabled but not yet rebooted) counts a
|
|
|
67
67
|
|
|
68
68
|
test("features enabled but WSL still the pre-reboot stub → needsReboot; auto-reboot capped at 2", () => {
|
|
69
69
|
const d = read("src/sidecar/docker.js");
|
|
70
|
-
assert.match(d, /if \(await wslFunctional\(\)\)
|
|
70
|
+
assert.match(d, /if \(!\(await wslFunctional\(\)\)\) \{/);
|
|
71
71
|
assert.match(d, /\[Argument\\\]/);
|
|
72
72
|
const ipc = read("src/backends/sidecar-ipc.js");
|
|
73
73
|
assert.match(ipc, /rebootCount\(\) >= 2/);
|
|
74
74
|
});
|
|
75
|
+
|
|
76
|
+
test("missing WSL2 kernel is installed through the same single elevation", () => {
|
|
77
|
+
const d = read("src/sidecar/docker.js");
|
|
78
|
+
assert.match(d, /function wslKernelPresent\(\)/);
|
|
79
|
+
assert.match(d, /if \(!wslKernelPresent\(\)\) \{[^]*elevatedWslSetup\(\{ emit, need: \{\} \}\)/);
|
|
80
|
+
assert.match(d, /reason: "wsl_kernel_missing"/);
|
|
81
|
+
});
|