cicy-desktop 2.1.276 → 2.1.278

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.
@@ -68,10 +68,12 @@ jobs:
68
68
  shell: bash
69
69
  env:
70
70
  GH_TOKEN: ${{ github.token }}
71
+ PUBLISH_OWNER: ${{ github.repository_owner }}
72
+ PUBLISH_REPO: ${{ github.event.repository.name }}
71
73
  CICY_OBFUSCATE: "1" # 发版混淆 JS(prebuild 主进程 + Vite 渲染层),仅 release CI
72
74
  run: |
73
75
  set -euo pipefail
74
- npm run build:linux -- --x64 --config.linux.target=AppImage --publish always
76
+ npm run build:linux -- --x64 --config.linux.target=AppImage --config.publish.owner="$PUBLISH_OWNER" --config.publish.repo="$PUBLISH_REPO" --publish always
75
77
 
76
78
  # AppImage + 版本指针传到 OSS —— app-updater 现已全走 OSS(不碰 GitHub),linux 也要在
77
79
  # OSS 上有 versioned AppImage(CiCy-Desktop-<ver>.AppImage)和自己的指针 linux-latest-version.txt。
@@ -139,7 +139,7 @@ jobs:
139
139
  GH_TOKEN: ${{ github.token }}
140
140
  run: |
141
141
  gh release create "${{ steps.meta.outputs.tag }}" \
142
- --repo "cicy-ai/cicy-desktop" \
142
+ --repo "${{ github.repository }}" \
143
143
  --title "CiCy Desktop v${{ steps.meta.outputs.version }}" \
144
144
  --generate-notes \
145
145
  --draft \
@@ -183,7 +183,7 @@ jobs:
183
183
  pkgbuild --root "$ROOT" --component-plist "$PLIST" --scripts "$SCR" \
184
184
  --identifier com.cicy.desktop --version "$VER" --install-location / "$PKG"
185
185
  gh release upload "${{ steps.meta.outputs.tag }}" "$PKG" \
186
- --repo "cicy-ai/cicy-desktop" --clobber
186
+ --repo "${{ github.repository }}" --clobber
187
187
  echo "uploaded $PKG ($(du -h "$PKG" | cut -f1))"
188
188
  done
189
189
 
@@ -75,7 +75,7 @@ jobs:
75
75
  GH_TOKEN: ${{ github.token }}
76
76
  run: |
77
77
  gh release create "${{ steps.meta.outputs.tag }}" \
78
- --repo "cicy-ai/cicy-desktop" \
78
+ --repo "${{ github.repository }}" \
79
79
  --title "CiCy Desktop v${{ steps.meta.outputs.version }}" \
80
80
  --generate-notes \
81
81
  --draft \
@@ -86,14 +86,16 @@ jobs:
86
86
  shell: powershell
87
87
  env:
88
88
  GH_TOKEN: ${{ github.token }}
89
+ PUBLISH_OWNER: ${{ github.repository_owner }}
90
+ PUBLISH_REPO: ${{ github.event.repository.name }}
89
91
  CICY_OBFUSCATE: "1" # 发版混淆 JS(prebuild 主进程 + Vite 渲染层),仅 release CI
90
92
  run: |
91
93
  if (Test-Path dist) { Remove-Item -Recurse -Force dist }
92
94
  if (Test-Path "build\logo.ico") {
93
- npm run build:win -- --config.win.icon=build/logo.ico --publish always
95
+ npm run build:win -- --config.win.icon=build/logo.ico --config.publish.owner="$env:PUBLISH_OWNER" --config.publish.repo="$env:PUBLISH_REPO" --publish always
94
96
  } else {
95
97
  Write-Host "::warning::no icon, using default"
96
- npm run build:win -- --publish always
98
+ npm run build:win -- --config.publish.owner="$env:PUBLISH_OWNER" --config.publish.repo="$env:PUBLISH_REPO" --publish always
97
99
  }
98
100
 
99
101
  # 推 exe 到 OSS。三件事(不再是"永远一个版本"):
@@ -179,6 +181,6 @@ jobs:
179
181
  GH_TOKEN: ${{ github.token }}
180
182
  run: |
181
183
  gh release edit "${{ steps.meta.outputs.tag }}" \
182
- --repo "cicy-ai/cicy-desktop" \
184
+ --repo "${{ github.repository }}" \
183
185
  --draft=false \
184
186
  --latest
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cicy-desktop",
3
- "version": "2.1.276",
3
+ "version": "2.1.278",
4
4
  "description": "CiCy - AI-powered operating system browser",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -143,10 +143,10 @@
143
143
  "//optionalDependencies": "(2026-06 回调): mac/linux 改回 native cicy-code(:8008,colima 在 16G mac 上压垮内存)→ 重新内置 cicy-code-<plat> / cicy-mihomo-<plat>,localbin.fromBundle 零网络 seed(npm 仅作更新通道,带 npmmirror→npmjs 回退)。这些由 scripts/sync-runtime-deps.cjs 在 tag-push 时同步到最新版。npm 的 os/cpu 字段保证每个平台只装自己那份。Windows 仍走 docker(WSL :8009),它那份 cicy-code-windows 用不到但 bundle 着无害。",
144
144
  "optionalDependencies": {
145
145
  "electron": "41.0.3",
146
- "cicy-code-darwin-x64": "2.3.558",
147
- "cicy-code-darwin-arm64": "2.3.558",
148
- "cicy-code-linux-x64": "2.3.558",
149
- "cicy-code-linux-arm64": "2.3.558",
146
+ "cicy-code-darwin-x64": "2.3.563",
147
+ "cicy-code-darwin-arm64": "2.3.563",
148
+ "cicy-code-linux-x64": "2.3.563",
149
+ "cicy-code-linux-arm64": "2.3.563",
150
150
  "cicy-code-windows-x64": "2.3.193",
151
151
  "cicy-mihomo-darwin-x64": "1.10.4",
152
152
  "cicy-mihomo-darwin-arm64": "1.10.4",
@@ -0,0 +1,8 @@
1
+ // Copyright 2026 CiCy AI
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ function shouldSkipCicyUpdate({ latest, current, platformReady }) {
5
+ return Boolean(latest && current && latest === current && platformReady);
6
+ }
7
+
8
+ module.exports = { shouldSkipCicyUpdate };
@@ -71,16 +71,46 @@ fi
71
71
  [ -n "$ver" ] || { log "could not resolve cicy-code@${want}"; exit 1; }
72
72
  dest="$RT/$ver"
73
73
 
74
- if [ -x "$dest/bin/cicy-code" ]; then
75
- log "v$ver already installed → repointing"
76
- else
74
+ case "$(uname -s):$(uname -m)" in
75
+ Linux:x86_64) platform_pkg="cicy-code-linux-x64" ;;
76
+ Linux:aarch64|Linux:arm64) platform_pkg="cicy-code-linux-arm64" ;;
77
+ *) log "unsupported platform: $(uname -s)-$(uname -m)"; exit 1 ;;
78
+ esac
79
+
80
+ platform_installed() {
81
+ [ -f "$dest/lib/node_modules/$platform_pkg/package.json" ] ||
82
+ [ -f "$dest/lib/node_modules/cicy-code/node_modules/$platform_pkg/package.json" ]
83
+ }
84
+
85
+ install_package() {
86
+ spec="$1"
87
+ if npm install -g "$spec" --prefix "$dest" --registry "$REG" \
88
+ --fetch-retries=2 --fetch-timeout=60000 --fetch-retry-maxtimeout=30000; then
89
+ return 0
90
+ fi
91
+ alt="$NPM_CN"; [ "$REG" = "$NPM_CN" ] && alt="$NPM_OFFICIAL"
92
+ log "install from $REG failed → falling back to $alt"
93
+ npm install -g "$spec" --prefix "$dest" --registry "$alt" \
94
+ --fetch-retries=2 --fetch-timeout=60000 --fetch-retry-maxtimeout=30000
95
+ REG="$alt"
96
+ }
97
+
98
+ if [ ! -x "$dest/bin/cicy-code" ]; then
77
99
  log "installing v$ver from $REG"
78
100
  rm -rf "$dest"
79
101
  mkdir -p "$dest"
80
- npm install -g "cicy-code@$ver" --prefix "$dest" --registry "$REG" \
81
- --fetch-retries=2 --fetch-timeout=60000 --fetch-retry-maxtimeout=30000
102
+ install_package "cicy-code@$ver"
82
103
  fi
83
104
 
105
+ if ! platform_installed; then
106
+ log "repairing missing $platform_pkg@$ver"
107
+ install_package "$platform_pkg@$ver"
108
+ fi
109
+
110
+ [ -x "$dest/bin/cicy-code" ] || { log "incomplete runtime: missing bin/cicy-code"; exit 1; }
111
+ platform_installed || { log "incomplete runtime: missing $platform_pkg@$ver"; exit 1; }
112
+ log "v$ver runtime complete → repointing"
113
+
84
114
  mkdir -p "$(dirname "$LINK")"
85
115
  ln -sfn "$dest/bin/cicy-code" "$LINK"
86
116
  # Keep the npm-global bin (first on PATH) following the canonical link too, so
@@ -17,6 +17,7 @@ const http = require("http");
17
17
  const fs = require("fs");
18
18
  const os = require("os");
19
19
  const path = require("path");
20
+ const { classifyWslPrerequisites } = require("./wsl-prerequisites");
20
21
 
21
22
  const IMAGE = process.env.CICY_DOCKER_IMAGE || "cicybot/cicy-code:latest";
22
23
  // Image tarball on Aliyun OSS (oss-cn-shanghai, public-read) — CN-domestic and
@@ -660,7 +661,12 @@ async function dismEnableFeature(feature, label, { emit } = {}) {
660
661
  // features are verified-enabled (a Windows reboot is then needed before Docker
661
662
  // can use WSL2), or { failed } if a feature couldn't be enabled.
662
663
  async function ensureWsl({ emit } = {}) {
663
- if (!(await wslMissing())) return { ok: true };
664
+ const executableMissing = await wslMissing();
665
+ const subsystemEnabled = await featureEnabled("Microsoft-Windows-Subsystem-Linux");
666
+ const vmPlatformEnabled = await featureEnabled("VirtualMachinePlatform");
667
+ const state = classifyWslPrerequisites({ executableMissing, subsystemEnabled, vmPlatformEnabled });
668
+ if (state.ready) return { ok: true };
669
+
664
670
  emit && emit({ phase: "install-docker", status: "running", message: "Docker 需要 WSL2 后端,开始启用所需的 Windows 功能…" });
665
671
  const a = await dismEnableFeature("Microsoft-Windows-Subsystem-Linux", "启用 WSL 功能 1/2 · Linux 子系统", { emit });
666
672
  const b = await dismEnableFeature("VirtualMachinePlatform", "启用 WSL 功能 2/2 · 虚拟机平台", { emit });
@@ -668,10 +674,12 @@ async function ensureWsl({ emit } = {}) {
668
674
  emit && emit({ phase: "done", status: "error", message: "WSL 功能未能全部启用——请点「重试」" });
669
675
  return { ok: false, needsReboot: false, failed: true };
670
676
  }
671
- // Best-effort: also pull the WSL2 kernel/plumbing (no-op until reboot on some
672
- // builds; harmless if it errors).
673
- await launchElevated("wsl", ["--install", "--no-distribution"], { emit }).catch(() => {});
674
- emit && emit({ phase: "install-docker", status: "running", message: "WSL2 功能已启用 ✓,需【重启 Windows】后回来点「重试」继续。" });
677
+ // Best-effort: also pull the WSL2 kernel/plumbing when the executable itself
678
+ // is missing. Feature-only repairs must stop here and wait for reboot.
679
+ if (state.installWsl) {
680
+ await launchElevated("wsl", ["--install", "--no-distribution"], { emit }).catch(() => {});
681
+ }
682
+ emit && emit({ phase: "install-docker", status: "running", message: "WSL2 功能已启用 ✓,请【重启 Windows】;重启后回来点「重试」继续。" });
675
683
  return { ok: false, needsReboot: true };
676
684
  }
677
685
 
@@ -21,6 +21,7 @@ const fs = require("fs");
21
21
  const docker = require("./docker"); // shared: downloads, waitUntil, probeHealth, launchElevated, ensureWsl…
22
22
  const log = require("electron-log"); // persisted main.log — bootstrap timing/failures land here
23
23
  const { t } = require("../i18n"); // 打开/读 token 的可见日志走 i18n
24
+ const { shouldSkipCicyUpdate } = require("./cicy-runtime-health");
24
25
 
25
26
  // Dedicated distro name — NEVER reuse/clobber a user's own "Ubuntu" distro.
26
27
  const DISTRO = process.env.CICY_WSL_DISTRO || "cicy-code-wsl";
@@ -929,11 +930,10 @@ async function _bootstrap({ onProgress, port = 8008, container = "cicy-code-dock
929
930
 
930
931
  // 1) WSL2 platform
931
932
  begin("ensure-wsl");
932
- if (await docker.wslMissing()) {
933
- const w = await docker.ensureWsl({ emit });
934
- if (w.needsReboot) { fail("wsl_reboot_required"); emit({ phase: "done", status: "reboot", message: "WSL2 正在安装——请【重启 Windows】后回来点「重试」继续。" }); finish(false, "wsl_reboot_required"); return { ok: false, reason: "wsl_reboot_required" }; }
935
- done();
936
- } else done(true);
933
+ const w = await docker.ensureWsl({ emit });
934
+ if (w.needsReboot) { fail("wsl_reboot_required"); emit({ phase: "done", status: "reboot", message: "WSL2 所需功能已启用——请【重启 Windows】后回来点「重试」继续。" }); finish(false, "wsl_reboot_required"); return { ok: false, reason: "wsl_reboot_required" }; }
935
+ if (!w.ok) { fail("wsl_enable_failed"); finish(false, "wsl_enable_failed"); return { ok: false, reason: "wsl_enable_failed" }; }
936
+ done(true);
937
937
 
938
938
  // 2) Ubuntu distro
939
939
  begin("ensure-distro");
@@ -986,13 +986,36 @@ async function _bootstrap({ onProgress, port = 8008, container = "cicy-code-dock
986
986
  done();
987
987
  } else done(true);
988
988
 
989
- // 7) Health — the ONLY path to ok:true.
989
+ // 7) Health — the ONLY path to ok:true. A short failed probe triggers the
990
+ // runtime updater once: it repairs the common partial-install state where the
991
+ // JS launcher exists but cicy-code-linux-<arch> is missing from the volume.
990
992
  begin("wait-health");
991
993
  emit({ phase: "container", status: "running", message: "等待 cicy-code 就绪…" });
992
- const healthy = await docker.waitUntil(() => probeHealth(port), { totalMs: 120000, everyMs: 3000 });
994
+ let healthy = await docker.waitUntil(() => probeHealth(port), { totalMs: 15000, everyMs: 3000 });
995
+ if (!healthy) {
996
+ emit({ phase: "container", status: "running", message: "服务尚未响应,正在检查并修复运行时完整性…" });
997
+ try {
998
+ const repaired = await update({ onProgress: emit, container, port });
999
+ healthy = Boolean(repaired && repaired.ok && await probeHealth(port));
1000
+ } catch (e) {
1001
+ log.warn(`[bootstrap] runtime auto-repair failed: ${e.message}`);
1002
+ }
1003
+ }
1004
+ if (!healthy) healthy = await docker.waitUntil(() => probeHealth(port), { totalMs: 105000, everyMs: 3000 });
993
1005
  if (healthy) { done(); await ensureAutostart(); await ensureDesktopShortcut(volume, port); } // survive reboot + desktop shortcut
994
1006
  else fail("health_timeout");
995
- emit({ phase: healthy ? "done" : "container", status: healthy ? "done" : "error", message: healthy ? "Docker cicy-code 已就绪 🎉" : `服务起来了但 :${port} 还没响应——稍等或点「重试」` });
1007
+ let healthMessage = "Docker cicy-code 已就绪 🎉";
1008
+ if (!healthy) {
1009
+ let tail = "";
1010
+ try {
1011
+ const r = await wslRun(`docker logs --tail 60 ${container}`, { timeout: 15000 });
1012
+ tail = String((r.stderr || "") + (r.stdout || "")).trim().slice(-1200);
1013
+ } catch (e) {
1014
+ tail = String((e && (e.stderr || e.stdout)) || "").trim().slice(-1200);
1015
+ }
1016
+ healthMessage = `服务未能响应 :${port}。${tail ? `\n\n容器日志:\n${tail}` : "请点「重试」。"}`;
1017
+ }
1018
+ emit({ phase: healthy ? "done" : "container", status: healthy ? "done" : "error", message: healthMessage });
996
1019
  finish(healthy, healthy ? null : "health_timeout");
997
1020
  return { ok: healthy, container };
998
1021
  }
@@ -1061,6 +1084,18 @@ async function installedCicyVersion(container) {
1061
1084
  } catch { return null; }
1062
1085
  }
1063
1086
 
1087
+ // True only when the launcher AND this Linux architecture's optional binary
1088
+ // package are present. The version pointer alone is insufficient: npm can leave
1089
+ // a partial global install that has bin/cicy-code but cannot start.
1090
+ async function cicyRuntimePlatformReady(container) {
1091
+ const check = `target=$(readlink -f "$HOME/.local/bin/cicy-code" 2>/dev/null || true); ` +
1092
+ `dest=$(dirname "$(dirname "$target")" 2>/dev/null); ` +
1093
+ `case "$(uname -m)" in x86_64) p=cicy-code-linux-x64;; aarch64|arm64) p=cicy-code-linux-arm64;; *) exit 1;; esac; ` +
1094
+ `[ -x "$target" ] && { [ -f "$dest/lib/node_modules/$p/package.json" ] || [ -f "$dest/lib/node_modules/cicy-code/node_modules/$p/package.json" ]; }`;
1095
+ try { await wslRun(`docker exec ${container} bash -lc '${check}'`, { timeout: 15000 }); return true; }
1096
+ catch { return false; }
1097
+ }
1098
+
1064
1099
  // Update cicy-code IN PLACE. SMART path (避免容器内 npm view 卡 ~2min):
1065
1100
  // 1. 在宿主机解析最新版本号(host 网络,不过容器代理/DNS)
1066
1101
  // 2. 和容器里已装版本比对——一样就直接「已是最新」,根本不 docker exec
@@ -1080,10 +1115,12 @@ async function update({ onProgress, container = "cicy-code-docker", port = 8008
1080
1115
  emit({ phase: "image", status: "running", message: t("docker.updating.checking") });
1081
1116
  let net = "unknown";
1082
1117
  try { net = await require("./net-detect").detect(); } catch {}
1083
- const [latest, current] = await Promise.all([resolveLatestCicy(net), installedCicyVersion(container)]);
1084
- log.info(`[wsl-docker] update: net=${net} latest=${latest} current=${current}`);
1118
+ const [latest, current, platformReady] = await Promise.all([
1119
+ resolveLatestCicy(net), installedCicyVersion(container), cicyRuntimePlatformReady(container),
1120
+ ]);
1121
+ log.info(`[wsl-docker] update: net=${net} latest=${latest} current=${current} platformReady=${platformReady}`);
1085
1122
  // 2) 已是最新 → 秒回,根本不进容器装(你点更新等 2 分钟的就是这种「其实已最新」的空跑)。
1086
- if (latest && current && latest === current) {
1123
+ if (shouldSkipCicyUpdate({ latest, current, platformReady })) {
1087
1124
  emit({ phase: "done", status: "done", message: t("docker.updating.alreadyLatest", { v: current }) });
1088
1125
  return { ok: true, alreadyLatest: true, version: current };
1089
1126
  }
@@ -0,0 +1,15 @@
1
+ // Copyright 2026 CiCy AI
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ function classifyWslPrerequisites({ executableMissing, subsystemEnabled, vmPlatformEnabled }) {
5
+ const featuresReady = subsystemEnabled && vmPlatformEnabled;
6
+ const ready = !executableMissing && featuresReady;
7
+ return {
8
+ ready,
9
+ enableFeatures: !featuresReady,
10
+ installWsl: executableMissing,
11
+ needsReboot: !ready,
12
+ };
13
+ }
14
+
15
+ module.exports = { classifyWslPrerequisites };
@@ -0,0 +1,69 @@
1
+ const test = require("node:test");
2
+ const assert = require("node:assert/strict");
3
+ const fs = require("node:fs");
4
+ const os = require("node:os");
5
+ const path = require("node:path");
6
+ const { spawnSync } = require("node:child_process");
7
+
8
+ const script = path.join(__dirname, "..", "src", "sidecar", "container-scripts", "cicy-code-update.sh");
9
+
10
+ function writeExecutable(file, content) {
11
+ fs.writeFileSync(file, content, { mode: 0o755 });
12
+ }
13
+
14
+ function runUpdate({ complete = false } = {}) {
15
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "cicy-code-update-"));
16
+ const home = path.join(root, "home");
17
+ const store = path.join(root, "store");
18
+ const bin = path.join(root, "bin");
19
+ const dest = path.join(store, "2.3.563");
20
+ fs.mkdirSync(path.join(dest, "bin"), { recursive: true });
21
+ fs.mkdirSync(bin, { recursive: true });
22
+ writeExecutable(path.join(dest, "bin", "cicy-code"), "#!/bin/sh\nexit 0\n");
23
+ if (complete) {
24
+ fs.mkdirSync(path.join(dest, "lib", "node_modules", "cicy-code-linux-x64"), { recursive: true });
25
+ fs.writeFileSync(path.join(dest, "lib", "node_modules", "cicy-code-linux-x64", "package.json"), "{}\n");
26
+ }
27
+ writeExecutable(path.join(bin, "supervisorctl"), "#!/bin/sh\nexit 1\n");
28
+ writeExecutable(path.join(bin, "npm"), `#!/bin/sh
29
+ set -eu
30
+ printf '%s\\n' "$*" >> "${root}/npm.log"
31
+ prefix=""
32
+ prev=""
33
+ for arg in "$@"; do
34
+ if [ "$prev" = "--prefix" ]; then prefix="$arg"; break; fi
35
+ prev="$arg"
36
+ done
37
+ [ -n "$prefix" ]
38
+ mkdir -p "$prefix/lib/node_modules/cicy-code-linux-x64"
39
+ printf '{}\\n' > "$prefix/lib/node_modules/cicy-code-linux-x64/package.json"
40
+ `);
41
+ const result = spawnSync("bash", [script, "2.3.563"], {
42
+ encoding: "utf8",
43
+ env: {
44
+ ...process.env,
45
+ HOME: home,
46
+ CICY_CODE_STORE: store,
47
+ NPM_REGISTRY: "https://registry.npmjs.org",
48
+ PATH: `${bin}:${process.env.PATH}`,
49
+ },
50
+ });
51
+ return {
52
+ ...result,
53
+ platformPackage: path.join(dest, "lib", "node_modules", "cicy-code-linux-x64", "package.json"),
54
+ npmLog: fs.existsSync(path.join(root, "npm.log")) ? fs.readFileSync(path.join(root, "npm.log"), "utf8") : "",
55
+ };
56
+ }
57
+
58
+ test("repairs an existing cicy-code runtime when its linux-x64 package is missing", () => {
59
+ const result = runUpdate();
60
+ assert.equal(result.status, 0, result.stderr);
61
+ assert.equal(fs.existsSync(result.platformPackage), true);
62
+ assert.match(result.npmLog, /cicy-code-linux-x64@2\.3\.563/);
63
+ });
64
+
65
+ test("does not reinstall an existing runtime when its platform package is present", () => {
66
+ const result = runUpdate({ complete: true });
67
+ assert.equal(result.status, 0, result.stderr);
68
+ assert.equal(result.npmLog, "");
69
+ });
@@ -0,0 +1,12 @@
1
+ const test = require("node:test");
2
+ const assert = require("node:assert/strict");
3
+
4
+ const { shouldSkipCicyUpdate } = require("../src/sidecar/cicy-runtime-health");
5
+
6
+ test("does not skip an update when the recorded version matches but the platform package is missing", () => {
7
+ assert.equal(shouldSkipCicyUpdate({ latest: "2.3.563", current: "2.3.563", platformReady: false }), false);
8
+ });
9
+
10
+ test("skips an update only when the recorded version matches and the runtime is complete", () => {
11
+ assert.equal(shouldSkipCicyUpdate({ latest: "2.3.563", current: "2.3.563", platformReady: true }), true);
12
+ });
@@ -0,0 +1,37 @@
1
+ const test = require("node:test");
2
+ const assert = require("node:assert/strict");
3
+
4
+ const { classifyWslPrerequisites } = require("../src/sidecar/wsl-prerequisites");
5
+
6
+ test("requires feature enablement and reboot when wsl.exe exists but Windows features are disabled", () => {
7
+ assert.deepEqual(
8
+ classifyWslPrerequisites({
9
+ executableMissing: false,
10
+ subsystemEnabled: false,
11
+ vmPlatformEnabled: false,
12
+ }),
13
+ { ready: false, enableFeatures: true, installWsl: false, needsReboot: true },
14
+ );
15
+ });
16
+
17
+ test("is ready only when wsl.exe and both Windows features are available", () => {
18
+ assert.deepEqual(
19
+ classifyWslPrerequisites({
20
+ executableMissing: false,
21
+ subsystemEnabled: true,
22
+ vmPlatformEnabled: true,
23
+ }),
24
+ { ready: true, enableFeatures: false, installWsl: false, needsReboot: false },
25
+ );
26
+ });
27
+
28
+ test("installs WSL and enables features when wsl.exe is missing", () => {
29
+ assert.deepEqual(
30
+ classifyWslPrerequisites({
31
+ executableMissing: true,
32
+ subsystemEnabled: false,
33
+ vmPlatformEnabled: false,
34
+ }),
35
+ { ready: false, enableFeatures: true, installWsl: true, needsReboot: true },
36
+ );
37
+ });