chore: require node 22 runtime

This commit is contained in:
Ma
2026-08-14 13:41:28 +08:00
parent 8dee4cb236
commit 493df3a82e
6 changed files with 25 additions and 77 deletions
+1 -1
View File
@@ -35,7 +35,7 @@
"release": "pnpm build && pnpm test"
},
"engines": {
"node": ">=20.0.0",
"node": ">=22.0.0",
"pnpm": ">=9.0.0"
}
}
@@ -552,8 +552,7 @@ describe("CLI integration", () => {
it("checks environment health", () => {
const { stdout } = runStderr(["doctor"]);
expect(stdout).toContain("InkOS Doctor");
expect(stdout).toContain("Node.js >= 20");
expect(stdout).toContain("SQLite memory index");
expect(stdout).toContain("Node.js >= 22");
expect(stdout).toContain("inkos.json");
});
@@ -4,8 +4,7 @@ import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import {
ensureNodeRuntimePinFiles,
evaluateSqliteMemorySupport,
formatSqliteMemorySupportWarning,
evaluateNodeRuntimeSupport,
inspectNodeRuntimePinFiles,
parseNodeMajor,
} from "../runtime-requirements.js";
@@ -26,8 +25,8 @@ describe("runtime requirements", () => {
expect(parseNodeMajor("v22.19.0")).toBe(22);
});
it("marks sqlite memory acceleration unavailable below Node 22", () => {
const result = evaluateSqliteMemorySupport({
it("rejects runtimes below Node 22", () => {
const result = evaluateNodeRuntimeSupport({
nodeVersion: "v20.17.0",
hasNodeSqlite: false,
});
@@ -37,8 +36,8 @@ describe("runtime requirements", () => {
expect(result.detail).toContain("v20.17.0");
});
it("marks sqlite memory acceleration available on supported runtimes", () => {
const result = evaluateSqliteMemorySupport({
it("accepts supported runtimes", () => {
const result = evaluateNodeRuntimeSupport({
nodeVersion: "v22.19.0",
hasNodeSqlite: true,
});
@@ -47,26 +46,6 @@ describe("runtime requirements", () => {
expect(result.detail).toContain("v22.19.0");
});
it("formats an early warning for unsupported sqlite memory runtimes", () => {
const warning = formatSqliteMemorySupportWarning({
nodeVersion: "v20.17.0",
hasNodeSqlite: false,
});
expect(warning).toContain("v20.17.0");
expect(warning).toContain("Node 22+");
expect(warning).toContain("memory.db live sync");
});
it("does not format a warning on supported runtimes", () => {
const warning = formatSqliteMemorySupportWarning({
nodeVersion: "v22.19.0",
hasNodeSqlite: true,
});
expect(warning).toBeNull();
});
it("reports missing node runtime pin files", async () => {
const status = await inspectNodeRuntimePinFiles(tempRoot);
+3 -9
View File
@@ -5,7 +5,7 @@ import { findProjectRoot, log, logError, GLOBAL_ENV_PATH } from "../utils.js";
import { fetchWithProxy } from "@actalk/inkos-core";
import {
ensureNodeRuntimePinFiles,
evaluateSqliteMemorySupport,
evaluateNodeRuntimeSupport,
inspectNodeRuntimePinFiles,
} from "../runtime-requirements.js";
import {
@@ -123,15 +123,9 @@ export const doctorCommand = new Command("doctor")
// 1. Check Node.js version
const nodeVersion = process.version;
const major = parseInt(nodeVersion.slice(1).split(".")[0]!, 10);
checks.push({
name: "Node.js >= 20",
ok: major >= 20,
detail: nodeVersion,
});
checks.push({
name: "SQLite memory index (Node 22+)",
...evaluateSqliteMemorySupport({ nodeVersion }),
name: "Node.js >= 22",
...evaluateNodeRuntimeSupport({ nodeVersion }),
});
checks.push({
name: "Node runtime pin files",
+15 -29
View File
@@ -4,11 +4,11 @@ import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
export const SQLITE_MEMORY_MIN_NODE_MAJOR = 22;
export const SQLITE_MEMORY_PIN_VERSION = String(SQLITE_MEMORY_MIN_NODE_MAJOR);
export const SQLITE_MEMORY_PIN_FILES = [".nvmrc", ".node-version"] as const;
export const MIN_NODE_MAJOR = 22;
export const NODE_PIN_VERSION = String(MIN_NODE_MAJOR);
export const NODE_PIN_FILES = [".nvmrc", ".node-version"] as const;
export interface SqliteMemorySupportResult {
export interface NodeRuntimeSupportResult {
readonly ok: boolean;
readonly detail: string;
}
@@ -24,27 +24,13 @@ export interface NodeRuntimePinRepairResult {
readonly written: ReadonlyArray<string>;
}
export function formatSqliteMemorySupportWarning(options?: {
readonly nodeVersion?: string;
readonly hasNodeSqlite?: boolean;
}): string | null {
const nodeVersion = options?.nodeVersion ?? process.version;
const result = evaluateSqliteMemorySupport({
nodeVersion,
hasNodeSqlite: options?.hasNodeSqlite,
});
if (result.ok) return null;
return `Node ${nodeVersion} does not support SQLite memory index; memory.db live sync will fall back to Markdown. Use Node 22+ or run 'inkos doctor'.`;
}
export async function inspectNodeRuntimePinFiles(root: string): Promise<NodeRuntimePinStatus> {
const missing: string[] = [];
for (const file of SQLITE_MEMORY_PIN_FILES) {
for (const file of NODE_PIN_FILES) {
try {
const content = await readFile(join(root, file), "utf-8");
if (content.trim() !== SQLITE_MEMORY_PIN_VERSION) {
if (content.trim() !== NODE_PIN_VERSION) {
missing.push(file);
}
} catch {
@@ -55,7 +41,7 @@ export async function inspectNodeRuntimePinFiles(root: string): Promise<NodeRunt
if (missing.length === 0) {
return {
ok: true,
detail: `Pinned to Node ${SQLITE_MEMORY_PIN_VERSION} via ${SQLITE_MEMORY_PIN_FILES.join(", ")}.`,
detail: `Pinned to Node ${NODE_PIN_VERSION} via ${NODE_PIN_FILES.join(", ")}.`,
missing,
};
}
@@ -70,7 +56,7 @@ export async function inspectNodeRuntimePinFiles(root: string): Promise<NodeRunt
export async function ensureNodeRuntimePinFiles(root: string): Promise<NodeRuntimePinRepairResult> {
const written: string[] = [];
for (const file of SQLITE_MEMORY_PIN_FILES) {
for (const file of NODE_PIN_FILES) {
const path = join(root, file);
let content = "";
try {
@@ -79,11 +65,11 @@ export async function ensureNodeRuntimePinFiles(root: string): Promise<NodeRunti
content = "";
}
if (content.trim() === SQLITE_MEMORY_PIN_VERSION) {
if (content.trim() === NODE_PIN_VERSION) {
continue;
}
await writeFile(path, `${SQLITE_MEMORY_PIN_VERSION}\n`, "utf-8");
await writeFile(path, `${NODE_PIN_VERSION}\n`, "utf-8");
written.push(file);
}
@@ -106,17 +92,17 @@ function hasNodeSqliteBuiltin(): boolean {
}
}
export function evaluateSqliteMemorySupport(options?: {
export function evaluateNodeRuntimeSupport(options?: {
readonly nodeVersion?: string;
readonly hasNodeSqlite?: boolean;
}): SqliteMemorySupportResult {
}): NodeRuntimeSupportResult {
const nodeVersion = options?.nodeVersion ?? process.version;
const major = parseNodeMajor(nodeVersion);
if (major < SQLITE_MEMORY_MIN_NODE_MAJOR) {
if (major < MIN_NODE_MAJOR) {
return {
ok: false,
detail: `Unavailable on ${nodeVersion}. Long-book memory.db acceleration requires Node ${SQLITE_MEMORY_MIN_NODE_MAJOR}+.`,
detail: `Unsupported runtime ${nodeVersion}. InkOS requires Node ${MIN_NODE_MAJOR}+.`,
};
}
@@ -124,7 +110,7 @@ export function evaluateSqliteMemorySupport(options?: {
if (!hasNodeSqlite) {
return {
ok: false,
detail: `${nodeVersion} detected, but node:sqlite is unavailable on this runtime. memory.db acceleration will stay disabled.`,
detail: `${nodeVersion} detected, but the required node:sqlite module is unavailable.`,
};
}
-10
View File
@@ -1,11 +1,9 @@
import { readFile, stat } from "node:fs/promises";
import { join, resolve } from "node:path";
import { createLLMClient, StateManager, createLogger, createStderrSink, createJsonLineSink, resolveEffectiveLLMConfig, loadLLMEnvLayers, GLOBAL_CONFIG_DIR, GLOBAL_ENV_PATH, type EffectiveLLMConfigResult, type LLMConfigCliOverrides, type ProjectConfig, type PipelineConfig, type LogSink } from "@actalk/inkos-core";
import { formatSqliteMemorySupportWarning } from "./runtime-requirements.js";
export { GLOBAL_CONFIG_DIR, GLOBAL_ENV_PATH };
let sqliteMemorySupportWarned = false;
export async function resolveContext(opts: {
readonly context?: string;
@@ -113,14 +111,6 @@ export function buildPipelineConfig(
readonly logFile?: NodeJS.WritableStream;
},
): PipelineConfig {
if (!extra?.quiet && !sqliteMemorySupportWarned) {
const warning = formatSqliteMemorySupportWarning();
if (warning) {
sqliteMemorySupportWarned = true;
process.stderr.write(`[WARN] ${warning}\n`);
}
}
const sinks: LogSink[] = [];
if (!extra?.quiet) {
sinks.push(createStderrSink({ minLevel: "info" }));