From 428144bd2086b150a6aabdcd873f86bf3c19c9f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=81=82=E6=A9=99?= Date: Wed, 14 Oct 2026 19:27:34 +0800 Subject: [PATCH] fix(ci): finish workspace isolation on current main --- .github/workflows/ci.yml | 2 +- packages/beauticode-desktop/README.md | 4 ++-- packages/beauticode-desktop/package.json | 2 +- packages/beauticode-desktop/src/index.mjs | 3 +++ .../beauticode-desktop/test/contract.test.mjs | 2 +- .../beauticode-desktop/test/route.test.mjs | 16 ++++++++++++++++ packages/core/test/process-liveness.test.js | 19 +++++++++++++++++-- scripts/pack-desktop-aggregate.mjs | 1 + 8 files changed, 42 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 52c1401..5d29d3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,9 +23,9 @@ jobs: cache: npm - run: npm ci - run: npm audit --audit-level=high + - run: npm run build - run: npm run typecheck - run: npm test - - run: npm run build - name: Parse JavaScript entry points run: | node --check apps/tray/session-host.mjs diff --git a/packages/beauticode-desktop/README.md b/packages/beauticode-desktop/README.md index c2a51e2..67c33e1 100644 --- a/packages/beauticode-desktop/README.md +++ b/packages/beauticode-desktop/README.md @@ -2,8 +2,8 @@ Windows-only aggregate package for the beautiCode desktop hosts. Requires Node.js 22 or newer. -The source workspace installs on Linux for CI validation. `npm run desktop:pack` -adds the Windows-only npm platform restriction to the distributable; running +The private source workspace installs on Linux for CI validation. `npm run desktop:pack` +creates the publishable distributable with the Windows-only npm platform restriction; running Windows installation commands on another platform remains unsupported. Codex MSIX safety notice: direct launching an executable inside WindowsApps diff --git a/packages/beauticode-desktop/package.json b/packages/beauticode-desktop/package.json index ca01a34..2fa73b5 100644 --- a/packages/beauticode-desktop/package.json +++ b/packages/beauticode-desktop/package.json @@ -1,7 +1,7 @@ { "name": "beauticode-desktop", "version": "0.1.0-test.12", - "private": false, + "private": true, "description": "beautiCode desktop host aggregate for DSH, Codex, WorkBuddy, Cursor, and Doubao.", "type": "module", "bin": { diff --git a/packages/beauticode-desktop/src/index.mjs b/packages/beauticode-desktop/src/index.mjs index b49f768..05d6fd7 100644 --- a/packages/beauticode-desktop/src/index.mjs +++ b/packages/beauticode-desktop/src/index.mjs @@ -441,6 +441,9 @@ export function resolveRoute(host, command, options = {}) { } export function runHostCommand(host, command, options = {}) { + if ((command === "install" || command === "uninstall") && (options.platform || process.platform) !== "win32") { + throw new Error("Desktop host installation is only supported on Windows."); + } const route = resolveRoute(host, command, options); if (command === "health") throw new Error("Use the asynchronous runHostHealth API for health checks."); if (route.readOnly) return getHostStatus(host, options); diff --git a/packages/beauticode-desktop/test/contract.test.mjs b/packages/beauticode-desktop/test/contract.test.mjs index 53c90e2..22e1df8 100644 --- a/packages/beauticode-desktop/test/contract.test.mjs +++ b/packages/beauticode-desktop/test/contract.test.mjs @@ -12,7 +12,7 @@ test("desktop source workspace can be installed for cross-platform validation", const manifest = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8")); assert.equal(manifest.name, "beauticode-desktop"); assert.equal(manifest.version, "0.1.0-test.12"); - assert.equal(manifest.private, false); + assert.equal(manifest.private, true); assert.equal(manifest.os, undefined); assert.equal(manifest.bin["beauticode-desktop"], "./bin/beauticode-desktop.mjs"); assert.ok(manifest.files.includes("runtime")); diff --git a/packages/beauticode-desktop/test/route.test.mjs b/packages/beauticode-desktop/test/route.test.mjs index bfd4143..09a9f1d 100644 --- a/packages/beauticode-desktop/test/route.test.mjs +++ b/packages/beauticode-desktop/test/route.test.mjs @@ -36,6 +36,7 @@ test("status has no child-process route", () => { test("install route is executable through an injected child-process seam", () => { let call; const result = runHostCommand("workbuddy", "install", { + platform: "win32", runtimeRoot: "C:/package/runtime", env: { TEST_ENV: "1" }, spawnSync(...args) { @@ -49,3 +50,18 @@ test("install route is executable through an injected child-process seam", () => assert.deepEqual(call[1].slice(1), ["install"]); assert.equal(call[2].env.BEAUTICODE_PACKAGED_RUNTIME, "1"); }); + +test("non-Windows install and uninstall reject before invoking a host script", () => { + for (const platform of ["linux", "darwin"]) { + for (const host of HOSTS) { + for (const command of ["install", "uninstall"]) { + let called = false; + assert.throws(() => runHostCommand(host, command, { + platform, + spawnSync() { called = true; return { status: 0 }; }, + }), /only supported on Windows/); + assert.equal(called, false); + } + } + } +}); diff --git a/packages/core/test/process-liveness.test.js b/packages/core/test/process-liveness.test.js index 08944c7..c09148f 100644 --- a/packages/core/test/process-liveness.test.js +++ b/packages/core/test/process-liveness.test.js @@ -1,5 +1,7 @@ import assert from "node:assert/strict"; import test from "node:test"; +import childProcess from "node:child_process"; +import { syncBuiltinESMExports } from "node:module"; import { isRecordedPidLive } from "../dist/process-liveness.js"; test("a live process may create its claim long after it starts", async () => { @@ -8,7 +10,20 @@ test("a live process may create its claim long after it starts", async () => { assert.equal(await isRecordedPidLive(process.pid, claimTime), true); }); -test("a claim from before this process started cannot identify this PID", async () => { - const oldClaim = new Date(Date.now() - Math.max(120_000, process.uptime() * 1_000 + 120_000)).toISOString(); +test("a claim from before this process started cannot identify this PID", async (t) => { + const start = Date.now(); + t.mock.method(childProcess, "execFile", (_command, _args, _options, callback) => callback(null, new Date(start).toISOString())); + // Refresh the source's named ESM import after mocking the builtin's CJS export. + // A real PowerShell probe can time out on CI and deliberately fail open. + syncBuiltinESMExports(); + t.after(() => { t.mock.restoreAll(); syncBuiltinESMExports(); }); + const oldClaim = new Date(start - 120_000).toISOString(); assert.equal(await isRecordedPidLive(process.pid, oldClaim), false); }); + +test("a live process is retained when its platform start-time probe is unavailable", async (t) => { + t.mock.method(childProcess, "execFile", (_command, _args, _options, callback) => callback(new Error("probe unavailable"), "")); + syncBuiltinESMExports(); + t.after(() => { t.mock.restoreAll(); syncBuiltinESMExports(); }); + assert.equal(await isRecordedPidLive(process.pid, new Date(0).toISOString()), true); +}); diff --git a/scripts/pack-desktop-aggregate.mjs b/scripts/pack-desktop-aggregate.mjs index 7df4ca4..e821c2a 100644 --- a/scripts/pack-desktop-aggregate.mjs +++ b/scripts/pack-desktop-aggregate.mjs @@ -168,6 +168,7 @@ export async function stageDesktopAggregate(destRoot = defaultStageDir(), opts = await fsp.mkdir(destRoot, { recursive: true }); const manifest = JSON.parse(await fsp.readFile(path.join(sourceRoot, "package.json"), "utf8")); // Source workspaces must install on Linux CI; only the distributable has a Windows-only runtime contract. + manifest.private = false; manifest.os = ["win32"]; await fsp.writeFile(path.join(destRoot, "package.json"), `${JSON.stringify(manifest, null, 2)}\n`, "utf8"); for (const name of ["README.md", "LICENSE"]) {