diff --git a/CHANGELOG.md b/CHANGELOG.md index e6bd1fe..b10d7c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Lumi changelog +## 0.2.10 + +- Fixed local Windows startup under Node.js 24 by invoking npm through its JavaScript CLI instead of directly spawning `npm.cmd`. +- Added a safe command-shell fallback and focused Windows dependency-launch regression coverage. + ## 0.2.9 - Fixed forms containing fields named `action` or `method` being able to shadow their own submission endpoint and send requests to an invalid URL. diff --git a/README.md b/README.md index d6bc1a4..a0d1e32 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,9 @@ Admins may also install or repair a specific core or plugin release. These choices use immutable version tags and verify the selected version before files are replaced. `npm start` and `npm run run` launch the update-aware wrapper, which repairs required packages from the lockfile after a core update while -allowing unavailable optional integrations to remain disabled. +allowing unavailable optional integrations to remain disabled. On Windows the +wrapper invokes npm through the active Node installation, including Node.js 24, +so dependency repair does not rely on spawning a command script directly. Recovery mode can be started with `LUMI_SAFE_MODE=1 npm run run`, `node run.js --safe-mode`, or `data/recovery/safe-mode.flag`. See diff --git a/TODO.md b/TODO.md index 5b56225..4813bf2 100644 --- a/TODO.md +++ b/TODO.md @@ -677,6 +677,7 @@ This section is for Lumi chat answer feedback and AI Improvement Center work, no ## Done +- 2026-07-18: Fixed core 0.2.10 local Windows startup under Node.js 24 by resolving npm's JavaScript CLI through the active Node installation, retaining a command-shell fallback, and verifying automatic dependency repair through the real launcher on port 3000. - 2026-07-18: Fixed core 0.2.9 and Lumi AI 0.8.5 forms whose `action` or `method` fields could shadow the form endpoint, added a shared clobber-safe resolver, and migrated the affected feedback/settings requests to it. - 2026-07-18: Fixed Lumi AI 0.8.4 Improvement Center state changes for no-change, dismiss, edit, and restore actions with an explicit authenticated JSON contract, actionable errors, safe diagnostics, and focused regression coverage. - 2026-07-18: Fixed private reverse-proxy HTTPS recognition for production diagnostics in core 0.2.5 without globally trusting client forwarding headers, and taught the local client to consume the repository's env-style `.secrets` file and short diagnostic variable names directly. diff --git a/knowledge/core/lumi-core.md b/knowledge/core/lumi-core.md index 595c35c..71f68e1 100644 --- a/knowledge/core/lumi-core.md +++ b/knowledge/core/lumi-core.md @@ -14,7 +14,7 @@ editable: false Lumi is the core web UI and bot runtime. ## Runtime Package: lumi-bot -Version: 0.2.9 +Version: 0.2.10 ## Routes - GET /api/events - POST /api/destructive-confirmations diff --git a/package-lock.json b/package-lock.json index 7f5800e..7244e75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "lumi-bot", - "version": "0.2.9", + "version": "0.2.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "lumi-bot", - "version": "0.2.9", + "version": "0.2.10", "dependencies": { "adm-zip": "^0.5.12", "better-sqlite3": "^11.5.0", diff --git a/package.json b/package.json index 9b1155a..81c96b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lumi-bot", - "version": "0.2.9", + "version": "0.2.10", "private": true, "type": "commonjs", "scripts": { diff --git a/release-index.json b/release-index.json index 977051b..270f4cc 100644 --- a/release-index.json +++ b/release-index.json @@ -2,6 +2,36 @@ "schema_version": 1, "channel": "stable", "releases": [ + { + "version": "0.2.10", + "ref": "refs/tags/v0.2.10", + "released_at": "2026-07-18", + "installable": true, + "rollback_safe": true, + "replaces_versions": [ + "1.2.0" + ], + "data_policy": "preserve", + "dependency_policy": "sync_on_restart", + "migration_notes": "Fixes local Windows startup and automatic dependency repair under Node.js 24 while preserving all settings, databases, plugin data, models, uploads, logs, feedback, and secrets.", + "plugins": { + "auto-vc": "0.1.6", + "birthday": "0.1.3", + "economy-framework": "0.2.10", + "economy-games": "0.1.7", + "expression-interaction": "0.2.1", + "lumi_ai": "0.8.5", + "moderation": "0.1.5", + "okf": "0.1.1", + "quotes": "0.1.2", + "sample-plugin": "0.1.0", + "throne_wishlist": "0.1.2", + "welcome_messages": "0.1.1" + }, + "tools": { + "lumi_ai_web_search": "0.1.1" + } + }, { "version": "0.2.9", "ref": "refs/tags/v0.2.9", diff --git a/scripts/verify-release-metadata.js b/scripts/verify-release-metadata.js index 6c2ea20..757a89c 100644 --- a/scripts/verify-release-metadata.js +++ b/scripts/verify-release-metadata.js @@ -4,8 +4,8 @@ const path = require("path"); const { findSafeTarget } = require("../src/services/versioning"); const root = path.join(__dirname, ".."); -const releaseVersion = "0.2.9"; -const previousCoreVersion = "0.2.8"; +const releaseVersion = "0.2.10"; +const previousCoreVersion = "0.2.9"; const earliestCompatibleCoreVersion = "0.1.9"; const changedPlugins = { "auto-vc": { from: "0.1.5", to: "0.1.6", knowledge: "auto-vc" }, @@ -87,4 +87,4 @@ assert.equal(webSearch.minimum_lumi_version, "0.2.0"); assert.equal(webSearch.minimum_lumi_ai_version, "0.8.2"); assert.equal(hasVersionHeading(readText("plugins/lumi_ai_web_search/CHANGELOG.md"), webSearch.version), true); -console.log("Release metadata verification passed: core 0.2.9, Lumi AI 0.8.5, and synchronized package metadata."); +console.log("Release metadata verification passed: core 0.2.10, Lumi AI 0.8.5, and synchronized package metadata."); diff --git a/scripts/verify-update-system.js b/scripts/verify-update-system.js index 919e852..60d7cf3 100644 --- a/scripts/verify-update-system.js +++ b/scripts/verify-update-system.js @@ -4,7 +4,7 @@ const os = require("os"); const path = require("path"); const root = path.join(__dirname, ".."); -const { dependencyIssues, ensureRuntimeDependencies } = require("../src/services/dependency-manager"); +const { dependencyIssues, ensureRuntimeDependencies, npmInvocation } = require("../src/services/dependency-manager"); const { targetForRequestedVersion } = require("../src/services/repo-update"); const { buildStatus } = require("../src/services/update-index"); const { normalizeRepositoryRef } = require("../src/services/update-repository"); @@ -16,7 +16,7 @@ function readJson(relativePath) { const releaseIndex = readJson("release-index.json"); const releaseVersions = releaseIndex.releases.map((release) => release.version); -assert.deepEqual(releaseVersions, ["0.2.9", "0.2.8", "0.2.7", "0.2.6", "0.2.5", "0.2.4", "0.2.3", "0.2.2", "0.2.1", "0.2.0", "0.1.9"]); +assert.deepEqual(releaseVersions, ["0.2.10", "0.2.9", "0.2.8", "0.2.7", "0.2.6", "0.2.5", "0.2.4", "0.2.3", "0.2.2", "0.2.1", "0.2.0", "0.1.9"]); assert.equal(new Set(releaseVersions).size, releaseVersions.length, "release versions must be unique"); for (const release of releaseIndex.releases) { assert.equal(normalizeRepositoryRef(release.ref), release.ref); @@ -37,6 +37,7 @@ for (const [toolId, version] of Object.entries(currentRelease.tools)) { const baseTarget = { current_version: "0.2.4", available_versions: [ + { version: "0.2.10", ref: "refs/tags/v0.2.10", rollback_safe: true }, { version: "0.2.9", ref: "refs/tags/v0.2.9", rollback_safe: true }, { version: "0.2.8", ref: "refs/tags/v0.2.8", rollback_safe: true }, { version: "0.2.7", ref: "refs/tags/v0.2.7", rollback_safe: true }, @@ -69,7 +70,7 @@ const corrected = buildStatus({ channel: "stable" }); assert.equal(corrected.version_correction, true); -assert.equal(corrected.safe_target_version, "0.2.9"); +assert.equal(corrected.safe_target_version, "0.2.10"); assert.equal(corrected.update_available, true); assert.equal(corrected.blocked, false); @@ -118,6 +119,20 @@ try { fs.rmSync(fixture, { recursive: true, force: true }); } +const windowsNpmFixture = fs.mkdtempSync(path.join(os.tmpdir(), "lumi-windows-npm-")); +try { + const fakeNode = path.join(windowsNpmFixture, "node.exe"); + const fakeNpmCli = path.join(windowsNpmFixture, "node_modules", "npm", "bin", "npm-cli.js"); + fs.mkdirSync(path.dirname(fakeNpmCli), { recursive: true }); + fs.writeFileSync(fakeNpmCli, ""); + const invocation = npmInvocation({ platform: "win32", execPath: fakeNode, env: {} }); + assert.equal(invocation.command, fakeNode); + assert.deepEqual(invocation.args, [fakeNpmCli]); + assert.equal(invocation.args.includes("npm.cmd"), false); +} finally { + fs.rmSync(windowsNpmFixture, { recursive: true, force: true }); +} + const optionalFixture = fs.mkdtempSync(path.join(os.tmpdir(), "lumi-optional-dependency-")); try { fs.writeFileSync(path.join(optionalFixture, "package.json"), JSON.stringify({ optionalDependencies: { optional: "^1.0.0" } })); diff --git a/src/services/dependency-manager.js b/src/services/dependency-manager.js index b65f7dd..5d34cc7 100644 --- a/src/services/dependency-manager.js +++ b/src/services/dependency-manager.js @@ -49,13 +49,30 @@ function writeState(rootPath, values) { } function npmInvocation(options = {}) { - if (process.env.npm_execpath && fs.existsSync(process.env.npm_execpath)) { - return { command: process.execPath, args: [process.env.npm_execpath] }; + const environment = options.env || process.env; + const execPath = path.resolve(options.execPath || process.execPath); + const configuredCli = String(environment.npm_execpath || "").trim(); + if (configuredCli && fs.existsSync(configuredCli)) { + return { command: execPath, args: [configuredCli] }; } - return { - command: options.platform === "win32" || process.platform === "win32" ? "npm.cmd" : "npm", - args: [] - }; + + const platform = options.platform || process.platform; + if (platform === "win32") { + const candidates = [ + path.join(path.dirname(execPath), "node_modules", "npm", "bin", "npm-cli.js"), + environment.ProgramFiles + ? path.join(environment.ProgramFiles, "nodejs", "node_modules", "npm", "bin", "npm-cli.js") + : null + ].filter(Boolean); + const npmCli = candidates.find((candidate) => fs.existsSync(candidate)); + if (npmCli) return { command: execPath, args: [npmCli] }; + + return { + command: String(environment.ComSpec || environment.COMSPEC || "cmd.exe"), + args: ["/d", "/s", "/c", "npm.cmd"] + }; + } + return { command: "npm", args: [] }; } function ensureRuntimeDependencies(options = {}) { @@ -118,5 +135,6 @@ function ensureRuntimeDependencies(options = {}) { module.exports = { dependencyFingerprint, dependencyIssues, - ensureRuntimeDependencies + ensureRuntimeDependencies, + npmInvocation }; diff --git a/update-manifest.json b/update-manifest.json index e70181e..6f100e6 100644 --- a/update-manifest.json +++ b/update-manifest.json @@ -1,6 +1,6 @@ { "name": "Lumi Core", - "version": "0.2.9", + "version": "0.2.10", "channel": "stable", "released_at": "2026-07-18", "compatible_from": "0.1.9", @@ -8,7 +8,7 @@ "replaces_versions": [ "1.2.0" ], - "migration_notes": "Includes the 1.2.0 version correction, production plugin-update diagnostics, secured read-only production diagnostics, Windows/network-share-safe plugin code replacement, and clobber-safe shared form submission. Lumi synchronizes runtime dependencies on restart and preserves settings, databases, plugin data, community knowledge, AI models, runtimes, uploads, logs, feedback, and secrets.", + "migration_notes": "Includes the 1.2.0 version correction, production plugin-update diagnostics, secured read-only production diagnostics, Windows/network-share-safe plugin replacement, clobber-safe shared form submission, and Node.js 24-compatible Windows dependency startup. Lumi synchronizes runtime dependencies on restart and preserves settings, databases, plugin data, community knowledge, AI models, runtimes, uploads, logs, feedback, and secrets.", "rollback_safe": true, "requirements": [ "Node.js 18 or newer" @@ -133,6 +133,18 @@ ], "rollback_safe": true, "migration_notes": "Fixes shared browser form endpoint shadowing and Lumi AI feedback 404 responses; existing feedback, settings, models, corrections, and plugin data are preserved." + }, + { + "version": "0.2.10", + "channel": "stable", + "released_at": "2026-07-18", + "compatible_from": "0.1.9", + "migration_kind": "patch", + "replaces_versions": [ + "1.2.0" + ], + "rollback_safe": true, + "migration_notes": "Fixes local Windows startup and automatic dependency repair under Node.js 24; all existing data remains preserved." } ] }