cicy-desktop 2.1.307 → 2.1.309
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.
|
@@ -132,6 +132,21 @@ jobs:
|
|
|
132
132
|
if ($kfile) { & $oss cp "$($kfile.FullName)" "$kernelKey" -f --acl public-read } else { Write-Warning "kernel not found in msi extract" }
|
|
133
133
|
} else { Write-Host "WSL2 kernel already on OSS, skip" }
|
|
134
134
|
|
|
135
|
+
# 0b) 新版 WSL 的 MSI(一次性,已存在就跳过):老 Win10 自带旧版 WSL 反复死锁,桌面端改为装新版。
|
|
136
|
+
$wslKey = "$base/wsl/wsl.x64.msi"
|
|
137
|
+
& $oss stat "$wslKey" 2>$null | Out-Null
|
|
138
|
+
if ($LASTEXITCODE -ne 0) {
|
|
139
|
+
$rel = Invoke-RestMethod -Uri "https://api.github.com/repos/microsoft/WSL/releases/latest" -Headers @{ "User-Agent" = "cicy-desktop-ci" }
|
|
140
|
+
$asset = $rel.assets | Where-Object { $_.name -match 'x64\.msi$' } | Select-Object -First 1
|
|
141
|
+
if ($asset) {
|
|
142
|
+
Write-Host "Publishing modern WSL $($rel.tag_name) -> $wslKey"
|
|
143
|
+
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile wsl.x64.msi
|
|
144
|
+
& $oss cp "wsl.x64.msi" "$wslKey" -f --acl public-read
|
|
145
|
+
Set-Content -Path wsl-version.txt -Value $rel.tag_name
|
|
146
|
+
& $oss cp "wsl-version.txt" "$base/wsl/version.txt" -f --acl public-read
|
|
147
|
+
} else { Write-Warning "no x64 msi asset in microsoft/WSL latest" }
|
|
148
|
+
} else { Write-Host "modern WSL msi already on OSS, skip" }
|
|
149
|
+
|
|
135
150
|
# 1) 永久版本化副本(unique key — 不会被后续发版覆盖)
|
|
136
151
|
$verKey = "$base/cicy-desktop-$version.exe"
|
|
137
152
|
Write-Host "Uploading versioned -> $verKey"
|
package/package.json
CHANGED
|
@@ -159,6 +159,7 @@ function register({ sidecarLogPath } = {}) {
|
|
|
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;
|
|
162
|
+
_softRetryAt = Date.now() + 2 * 60 * 1000;
|
|
162
163
|
if (_lastBootstrapError.reason === "wsl_reboot_required") scheduleReboot(90);
|
|
163
164
|
if (HARD_BOOTSTRAP_REASONS.has(_lastBootstrapError.reason)) {
|
|
164
165
|
_autoBootstrapPaused = _lastBootstrapError.reason;
|
|
@@ -171,6 +172,7 @@ function register({ sidecarLogPath } = {}) {
|
|
|
171
172
|
let _rebootScheduled = false;
|
|
172
173
|
let _relayMisses = 0, _relayResetDone = false; // WSL localhost 转发自愈(见 daemon 循环)
|
|
173
174
|
let _unknownStreak = 0, _wslRepairDone = false; // WSL 卡死自愈(见 daemon 循环)
|
|
175
|
+
let _softRetryAt = 0; // 非硬失败(如 docker_install_failed)也退避 2 分钟,别每 35 秒重跑一遍
|
|
174
176
|
// 防重启循环:WSL 始终起不来时不能每次开机都重启。计数存 db/wsl-reboots.json,最多自动重启 2 次
|
|
175
177
|
// (成功后 :8008 起来时清零)。
|
|
176
178
|
const REBOOT_COUNT_FILE = path.join(os.homedir(), "cicy-ai", "db", "wsl-reboots.json");
|
|
@@ -265,6 +267,8 @@ function register({ sidecarLogPath } = {}) {
|
|
|
265
267
|
} else if (s.running) { _relayMisses = 0; }
|
|
266
268
|
if (!s.running && !s.unknown && _autoBootstrapPaused && Date.now() < _autoBootstrapRetryAt) {
|
|
267
269
|
// 硬失败后退避(见 recordBootstrapResult);卡片显示 lastError,到点自动再试,用户点「重试」也行。
|
|
270
|
+
} else if (!s.running && !s.unknown && Date.now() < _softRetryAt) {
|
|
271
|
+
// 上一轮刚失败,2 分钟内不重跑
|
|
268
272
|
} else if (!s.running && !s.unknown) {
|
|
269
273
|
if (_autoBootstrapPaused) log.info(`[docker-daemon] backoff elapsed after ${_autoBootstrapPaused} → auto-retrying bootstrap`);
|
|
270
274
|
log.info(`[docker-daemon] :8008 down (${s.installed ? "installed" : "not installed"}) → auto-bootstrapping WSL cicy-code`);
|
package/src/sidecar/docker.js
CHANGED
|
@@ -612,6 +612,7 @@ function wslExe() {
|
|
|
612
612
|
if (process.platform !== "win32") return "wsl";
|
|
613
613
|
const sysroot = process.env.SystemRoot || process.env.windir || "C:\\Windows";
|
|
614
614
|
const cands = [
|
|
615
|
+
path.join(process.env.ProgramFiles || "C:\\Program Files", "WSL", "wsl.exe"), // 新版 WSL(MSI),优先
|
|
615
616
|
path.join(sysroot, "System32", "wsl.exe"),
|
|
616
617
|
path.join(process.env.LOCALAPPDATA || "", "Microsoft", "WindowsApps", "wsl.exe"),
|
|
617
618
|
];
|
|
@@ -670,6 +671,18 @@ function featureEnabled(feature) {
|
|
|
670
671
|
});
|
|
671
672
|
}
|
|
672
673
|
|
|
674
|
+
// 新版 WSL(微软独立发布的 MSI,Store 版同源):不走旧的 LxssManager、内核内置、`--status` 等命令
|
|
675
|
+
// 齐全、localhost 转发稳定。老 Win10 自带的旧版 WSL 反复死锁/半死/缺内核,全部由它一次解决。
|
|
676
|
+
// MSI 由发布工作流从 GitHub 镜像到 OSS(国内可下)。
|
|
677
|
+
const WSL_MODERN_MSI_URL = process.env.CICY_WSL_MSI_URL || (require("./mirrors").OSS_RELEASES_BASE + "/wsl/wsl.x64.msi");
|
|
678
|
+
function modernWslInstalled() {
|
|
679
|
+
if (process.platform !== "win32") return true;
|
|
680
|
+
try { return fs.existsSync(path.join(process.env.ProgramFiles || "C:\\Program Files", "WSL", "wsl.exe")); } catch { return false; }
|
|
681
|
+
}
|
|
682
|
+
// 这台机器的旧版 WSL 出过死锁/被自动重启过(db/wsl-reboots.json 有计数)→ 下次提权时顺手换新版。
|
|
683
|
+
function legacyWslTroubleSeen() {
|
|
684
|
+
try { const j = JSON.parse(fs.readFileSync(path.join(os.homedir(), "cicy-ai", "db", "wsl-reboots.json"), "utf8")); return (Number(j.count) || 0) > 0; } catch { return false; }
|
|
685
|
+
}
|
|
673
686
|
const WSL_KERNEL_MSI_URL = process.env.CICY_WSL_KERNEL_URL || "https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi";
|
|
674
687
|
// 裸内核文件(由发布工作流从 MSI 解出后放到 OSS):精简版 Windows 没有 msiexec 时直接放到位。
|
|
675
688
|
const WSL_KERNEL_RAW_URL = process.env.CICY_WSL_KERNEL_RAW_URL || (require("./mirrors").OSS_RELEASES_BASE + "/wsl-kernel/kernel");
|
|
@@ -750,6 +763,12 @@ function elevatedWslSetup({ emit, need = {} } = {}) {
|
|
|
750
763
|
' & $SFC /scannow | Out-Null; Say ("sfc exit=" + $LASTEXITCODE)',
|
|
751
764
|
' foreach ($f in $bad) { $c = En $f }',
|
|
752
765
|
'}',
|
|
766
|
+
`$MODERN = Join-Path $env:ProgramFiles "WSL\\wsl.exe"`,
|
|
767
|
+
`if (-not (Test-Path $MODERN) -and (Test-Path $MSIEXEC)) {`,
|
|
768
|
+
` $wm = Join-Path $env:USERPROFILE "Downloads\\wsl.x64.msi"`,
|
|
769
|
+
` Say "installing modern WSL (replaces the legacy inbox WSL: no LxssManager deadlocks, kernel built in) — download ~250MB"`,
|
|
770
|
+
` try { if (-not (Test-Path $wm) -or (Get-Item $wm).Length -lt 100000000) { Invoke-WebRequest -UseBasicParsing -Uri "${WSL_MODERN_MSI_URL}" -OutFile $wm }; $p = Start-Process $MSIEXEC -ArgumentList "/i","\`"$wm\`"","/qn","/norestart" -Wait -PassThru; Say ("modern wsl msi exit=" + $p.ExitCode) } catch { Say ("modern wsl install failed: " + $_.Exception.Message) }`,
|
|
771
|
+
`}`,
|
|
753
772
|
`$KDIR = Join-Path $S32 "lxss\\tools"; $KFILE = Join-Path $KDIR "kernel"`,
|
|
754
773
|
`if (-not (Test-Path $KFILE)) {`,
|
|
755
774
|
` if ((Test-Path $MSIEXEC) -and (Test-Path "${msi.replace(/"/g, '""')}")) { $p = Start-Process $MSIEXEC -ArgumentList "/i","\`"${msi.replace(/"/g, '""')}\`"","/qn","/norestart" -Wait -PassThru; Say ("kernel msi exit=" + $p.ExitCode) }`,
|
|
@@ -849,10 +868,20 @@ async function ensureWsl({ emit } = {}) {
|
|
|
849
868
|
// 功能都已启用、wsl.exe 也在,但 WSL 本身还不能用(`wsl -l -v` 只打印帮助)= 启用后还没
|
|
850
869
|
// 重启。别去 --import(必失败),直接进自动重启流程。
|
|
851
870
|
if (!(await wslFunctional())) {
|
|
871
|
+
if (!modernWslInstalled() && legacyWslTroubleSeen()) {
|
|
872
|
+
// 旧版 WSL 已经死锁/重启过 → 别再等它,换新版 WSL(一次 UAC),装完重启。
|
|
873
|
+
emit && emit({ phase: "install-docker", status: "running", message: "旧版 WSL 反复无响应,改为安装微软新版 WSL(会弹一次 UAC,请点「是」,下载约 250MB)…" });
|
|
874
|
+
await elevatedWslSetup({ emit, need: {} });
|
|
875
|
+
}
|
|
852
876
|
log.warn("[bootstrap] ensure-wsl: features enabled but WSL not functional yet (pending reboot)");
|
|
853
877
|
emit && emit({ phase: "install-docker", status: "running", message: "WSL 功能已启用但尚未生效,需要重启 Windows…" });
|
|
854
878
|
return { ok: false, needsReboot: true };
|
|
855
879
|
}
|
|
880
|
+
if (!modernWslInstalled() && legacyWslTroubleSeen()) {
|
|
881
|
+
emit && emit({ phase: "install-docker", status: "running", message: "这台机器的旧版 WSL 出过死锁,升级为微软新版 WSL(会弹一次 UAC,下载约 250MB)…" });
|
|
882
|
+
await elevatedWslSetup({ emit, need: {} });
|
|
883
|
+
if (modernWslInstalled()) { emit && emit({ phase: "install-docker", status: "running", message: "新版 WSL 已安装,需要重启 Windows 生效…" }); return { ok: false, needsReboot: true }; }
|
|
884
|
+
}
|
|
856
885
|
// WSL2 内核(lxss\tools\kernel)缺失时 --import 也必失败。先把 MSI 下到 Downloads,再用
|
|
857
886
|
// 同一个提权脚本装(功能已启用的话脚本只做装内核这一件事)。
|
|
858
887
|
if (!wslKernelPresent()) {
|
|
@@ -988,7 +1017,7 @@ module.exports = {
|
|
|
988
1017
|
start, stop, stopContainer, restart, checkStatus, loadImage, loadImageFromTarball,
|
|
989
1018
|
downloadImageTarball, imagePresent, dockerOk, installDocker,
|
|
990
1019
|
bootstrap, probeHealth, readContainerToken, dockerDesktopExe, desktopDir, downloadsDir, imageTarballPath,
|
|
991
|
-
launchElevated, elevatedWslSetup, spawnProbe, wslFunctional, wslKernelPresent, wslExe, wslMissing, ensureWsl, virtualizationStatus,
|
|
1020
|
+
launchElevated, elevatedWslSetup, modernWslInstalled, spawnProbe, wslFunctional, wslKernelPresent, wslExe, wslMissing, ensureWsl, virtualizationStatus,
|
|
992
1021
|
// platform-agnostic download/retry primitives, reused by native.js
|
|
993
1022
|
ensureDownloaded, curlDownload, withRetry, waitUntil, run, headSize,
|
|
994
1023
|
// image freshness (修「重建仍用旧镜像」—— 校验 OSS ETag 变了才重下重载)
|
|
@@ -95,7 +95,9 @@ async function ensureWslKernel({ emit } = {}) {
|
|
|
95
95
|
// …连接尝试失败」。实测 `wsl --shutdown` 后恢复。据此标记,bootstrap 遇到就重置一次再重试。
|
|
96
96
|
function isWslVmUnreachable(err) {
|
|
97
97
|
if (!err) return false;
|
|
98
|
-
|
|
98
|
+
// bash 的退出码只会是 0-255;>255 / -1 / 4294967295 只可能来自 wsl.exe 自己(连不上 VM)。
|
|
99
|
+
const code = err.code === 4294967295 || err.code === -1 || err.code === 0xffffffff || (typeof err.code === "number" && err.code > 255);
|
|
100
|
+
if (code) return true;
|
|
99
101
|
let text = "";
|
|
100
102
|
try { const raw = err.stdout || ""; text = Buffer.isBuffer(raw) ? raw.toString("utf16le") : String(raw); } catch {}
|
|
101
103
|
try { text += Buffer.from(String(err.stdout || ""), "binary").toString("utf16le"); } catch {}
|
|
@@ -139,7 +141,13 @@ function wslRunStream(cmd, { emit, phase = "install-docker", timeout = 900000, d
|
|
|
139
141
|
child.stderr.on("data", pump);
|
|
140
142
|
const timer = setTimeout(() => { try { child.kill("SIGKILL"); } catch {} reject(Object.assign(new Error("timeout"), { stdout: tail })); }, timeout);
|
|
141
143
|
child.on("error", (e) => { clearTimeout(timer); reject(e); });
|
|
142
|
-
child.on("close", (code) => {
|
|
144
|
+
child.on("close", (code) => {
|
|
145
|
+
clearTimeout(timer);
|
|
146
|
+
if (code === 0) return resolve({ stdout: tail });
|
|
147
|
+
const err = Object.assign(new Error(`exit ${code}`), { stdout: tail, code });
|
|
148
|
+
err.wslVmUnreachable = isWslVmUnreachable(err);
|
|
149
|
+
reject(err);
|
|
150
|
+
});
|
|
143
151
|
});
|
|
144
152
|
}
|
|
145
153
|
|
|
@@ -139,3 +139,20 @@ test("the container update script is pushed with LF endings even from a CRLF che
|
|
|
139
139
|
assert.match(read("src/sidecar/wsl-docker.js"), /cicy-code-update\.sh"\), "utf8"\)\.replace\(\/\\r\\n\?\/g, "\\n"\)/);
|
|
140
140
|
assert.match(read(".gitattributes"), /\*\.sh text eol=lf/);
|
|
141
141
|
});
|
|
142
|
+
|
|
143
|
+
test("streamed wsl runs flag an unreachable VM too, and soft failures back off 2 min", () => {
|
|
144
|
+
const w = read("src/sidecar/wsl-docker.js");
|
|
145
|
+
assert.match(w, /const err = Object\.assign\(new Error\(`exit \$\{code\}`\), \{ stdout: tail, code \}\);\n\s+err\.wslVmUnreachable = isWslVmUnreachable\(err\);/);
|
|
146
|
+
assert.match(w, /err\.code > 255\)/);
|
|
147
|
+
assert.match(read("src/backends/sidecar-ipc.js"), /_softRetryAt = Date\.now\(\) \+ 2 \* 60 \* 1000;/);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("legacy WSL trouble → install modern WSL (MSI mirrored to OSS) in the single elevation; wslExe prefers it", () => {
|
|
151
|
+
const d = read("src/sidecar/docker.js");
|
|
152
|
+
assert.match(d, /function modernWslInstalled\(\)/);
|
|
153
|
+
assert.match(d, /function legacyWslTroubleSeen\(\)/);
|
|
154
|
+
assert.match(d, /"WSL", "wsl\.exe"\), \/\/ 新版 WSL/);
|
|
155
|
+
assert.match(d, /installing modern WSL/);
|
|
156
|
+
assert.match(d, /if \(!modernWslInstalled\(\) && legacyWslTroubleSeen\(\)\) \{/);
|
|
157
|
+
assert.match(read(".github/workflows/windows-exe-release.yml"), /wsl\/wsl\.x64\.msi/);
|
|
158
|
+
});
|