diff --git a/.devcontainer/host/Dockerfile b/.devcontainer/host/Dockerfile index 8572fc152..0c04301ce 100644 --- a/.devcontainer/host/Dockerfile +++ b/.devcontainer/host/Dockerfile @@ -1,6 +1,6 @@ -# Multi-arch Node 24.15.0 bookworm-slim index (amd64 on CI/dogfood, arm64 locally). +# Multi-arch Node 24.21.0 bookworm-slim index (amd64 on CI/dogfood, arm64 locally). # Update the tag AND digest with .nvmrc; the build refuses version drift. -FROM node:24.15.0-bookworm-slim@sha256:4e6b70dd6cbfc88c8157ba19aa3d9f9cce6ba4703576d55459e45efcbc9c5f5d +FROM node:24.21.0-bookworm-slim@sha256:0e0ff40c39bc087845bfb27465a0df4ea419520094bc35842ff83dd8cbe6f9b6 # Development tools for git/worktrees and future Node-ABI native builds, plus # standalone Chromium's runtime libraries (not Electron or a browser binary). diff --git a/.nvmrc b/.nvmrc index 5bf4400f2..df6ae3370 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -24.15.0 +24.21.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 74e01d17b..4ebd216cd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ Volli is a local-first macOS workspace for Tickets, Sessions, worktrees, and rev ## Prerequisites -Use macOS, Node.js `^24.15.0`, and pnpm `11.10.0`. A `.nvmrc` carries the pin (`nvm use`), and the range is enforced rather than advisory: `pnpm install` hard-fails outside it (`engineStrict` in `pnpm-workspace.yaml`), and the desktop dev/start scripts preflight the running Node before anything spawns — an unsupported Node builds native modules (better-sqlite3, node-pty) against the wrong ABI, which surfaces later as a dead database and a greyed-out sign-in. Install dependencies from the repository root: +Use macOS, Node.js `^24.16.0`, and pnpm `11.10.0`. A `.nvmrc` carries the pin (`nvm use`), and the range is enforced rather than advisory: `pnpm install` hard-fails outside it (`engineStrict` in `pnpm-workspace.yaml`), and the desktop dev/start scripts preflight the running Node before anything spawns — an unsupported Node builds native modules (better-sqlite3, node-pty) against the wrong ABI, which surfaces later as a dead database and a greyed-out sign-in. Install dependencies from the repository root: ```bash pnpm install diff --git a/README.md b/README.md index 21ab4967f..9e88e9fb1 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Projects, tickets, and chat history stay on your machine. Model requests go to t ## Build from source -Requirements: macOS, Node.js `^24.15.0` (a `.nvmrc` is provided — `nvm use`), and pnpm `11.10.0`. The Node range is enforced: `pnpm install` and the dev scripts refuse to run under an unsupported Node, because native modules built against the wrong ABI leave the app unable to open its database (and sign-in disabled). +Requirements: macOS, Node.js `^24.16.0` (a `.nvmrc` is provided — `nvm use`), and pnpm `11.10.0`. The Node range is enforced: `pnpm install` and the dev scripts refuse to run under an unsupported Node, because native modules built against the wrong ABI leave the app unable to open its database (and sign-in disabled). ```bash git clone https://github.com/hussainph/volli-code.git diff --git a/apps/docs/src/content/docs/reference/build-from-source.mdx b/apps/docs/src/content/docs/reference/build-from-source.mdx index bb8c035a5..eae8918f5 100644 --- a/apps/docs/src/content/docs/reference/build-from-source.mdx +++ b/apps/docs/src/content/docs/reference/build-from-source.mdx @@ -4,7 +4,7 @@ description: Install Volli's development dependencies, start the desktop develop --- Use these commands to run the desktop app from the repository. You need macOS, -Git, Node `^24.15.0`, and pnpm `11.10.0`, as pinned in the root `package.json`. +Git, Node `^24.16.0`, and pnpm `11.10.0`, as pinned in the root `package.json`. ## Start the development app diff --git a/docs/development/host-linux.md b/docs/development/host-linux.md index febd7b3be..99e41dee8 100644 --- a/docs/development/host-linux.md +++ b/docs/development/host-linux.md @@ -74,7 +74,7 @@ The VC-552 inventory comment records the module lists and test caveats. The image in `.devcontainer/host/Dockerfile` is a **development toolchain**, not a production hostd deployment. Its multi-architecture base is pinned by digest; -its Node version must equal `.nvmrc` (currently 24.15.0), and Corepack installs +its Node version must equal `.nvmrc` (currently 24.21.0), and Corepack installs the root manifest's exact pnpm version. It includes git/SSH, Python/make/g++ for native compilation, fonts and Chromium runtime libraries. It contains neither Electron, Chromium itself, source code, installed workspace dependencies nor @@ -103,11 +103,13 @@ for your own checkout if needed. No production secrets are baked or mounted. Standalone Chromium provisioning belongs to the browser/worker ticket; these libraries support it without installing another browser in every dev image. -The previous 24.13.0 `.nvmrc` pin could not install the current dependency graph: -jsdom 30.0.1 requires Node ^24.15.0 in the 24.x line, and `engineStrict` rejects -older versions. The host lane/image therefore use 24.15.0, and the root -`engines.node` floor is ^24.15.0 too. The host lane's exact-version install -checks the full graph rather than inferring compatibility from the caret range. +The host lane/image use Node 24.21.0, and the root `engines.node` floor is +^24.16.0. Node 24.16.0 added libuv 1.52.1, including the Linux PTY premature-EOF +fix (libuv#4992/#4997); the older 24.15.0 pin could lose a shell's final output +before node-pty delivered exit (VC-639). Electron 44 already includes this fix. +The host lane's exact-version install checks the full graph rather than inferring +compatibility from the caret range. The residual libuv#5165 case needs libuv +1.53.0; revisit once Node ships that version (nodejs/node#66282). When bumping Node, update `.nvmrc`, the Dockerfile's tag and multi-arch digest together; the image build catches drift. A root `packageManager` bump also diff --git a/package.json b/package.json index 54d24a2cf..7b36c1ccc 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "yaml": "2.9.0" }, "engines": { - "node": "^24.15.0" + "node": "^24.16.0" }, "packageManager": "pnpm@11.10.0" } diff --git a/packages/host-core/src/db-open-failure.ts b/packages/host-core/src/db-open-failure.ts index c55cc3109..b272b3b32 100644 --- a/packages/host-core/src/db-open-failure.ts +++ b/packages/host-core/src/db-open-failure.ts @@ -39,7 +39,7 @@ import { DatabaseFromNewerVersionError } from "./db/schema-compatibility"; * — main cannot read the root manifest at runtime (it is not packaged), so * the test is what keeps this string from drifting into a lie. */ -export const REQUIRED_NODE_RANGE = "^24.15.0"; +export const REQUIRED_NODE_RANGE = "^24.16.0"; /** * The signatures a wrong-ABI or missing native build leaves in its error. diff --git a/packages/host-core/src/pty/manager.pty.test.ts b/packages/host-core/src/pty/manager.pty.test.ts index 1a0e60b84..76be94cce 100644 --- a/packages/host-core/src/pty/manager.pty.test.ts +++ b/packages/host-core/src/pty/manager.pty.test.ts @@ -235,6 +235,41 @@ describe("terminal supervisor on node-pty under plain Node (VC-560)", () => { await until(() => ledger.exited.has(0), "the shell to exit after its window closed"); }); + it("delivers a large final output and UTF-8 tail before exit", async () => { + const owner = makeClient("owner"); + const sessionId = await start(owner); + const publish = owner.sink.publish.bind(owner.sink); + owner.sink.publish = (topic, payload) => { + publish(topic, payload); + if (topic === "terminal-data") { + const data = payload as TerminalDataEvent; + // Model a consuming client, after the pipeline has accounted its send. + queueMicrotask(() => manager.ack(owner.sink, sessionId, data.data.length)); + } + }; + manager.write( + owner.sink, + sessionId, + "printf '%0200000d' 0; printf '\\342\\230\\203-%s\\n' $((4+4)); exit 3\r", + ); + await until( + () => owner.events.some((event) => event.topic === "terminal-exit"), + "the exit event", + ); + const exitIndex = owner.events.findIndex((event) => event.topic === "terminal-exit"); + const output = owner.events + .slice(0, exitIndex) + .filter((event) => event.topic === "terminal-data") + .map((event) => (event.payload as TerminalDataEvent).data) + .join(""); + expect(output).toContain("0".repeat(200_000)); + expect(output).toContain("☃-8"); + expect(owner.events[exitIndex]?.payload).toEqual({ sessionId, exitCode: 3 }); + expect(owner.events.slice(exitIndex + 1).some((event) => event.topic === "terminal-data")).toBe( + false, + ); + }); + it("delivers the shell's exit to the attached client after its final output", async () => { const owner = makeClient("owner"); const sessionId = await start(owner); diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 473fc7b6e..a31844f72 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,7 +3,7 @@ packages: - packages/* # Hard-fail `pnpm install` when the running Node is outside the root -# `engines.node` pin (^24.15.0). Without this, an install under a stray older +# `engines.node` pin (^24.16.0). Without this, an install under a stray older # Node (a machine's /usr/local/bin/node beside Homebrew's current, say) # succeeds and quietly builds native modules against the wrong ABI — the # desktop app then boots into a dead database with sign-in greyed out (VC-76).