Fix Windows local dependency startup
This commit is contained in:
parent
3274778e40
commit
21248043c7
@ -1,5 +1,10 @@
|
|||||||
# Lumi changelog
|
# 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
|
## 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.
|
- Fixed forms containing fields named `action` or `method` being able to shadow their own submission endpoint and send requests to an invalid URL.
|
||||||
|
|||||||
@ -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
|
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,
|
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
|
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`,
|
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
|
`node run.js --safe-mode`, or `data/recovery/safe-mode.flag`. See
|
||||||
|
|||||||
1
TODO.md
1
TODO.md
@ -677,6 +677,7 @@ This section is for Lumi chat answer feedback and AI Improvement Center work, no
|
|||||||
|
|
||||||
## Done
|
## 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 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 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.
|
- 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.
|
||||||
|
|||||||
@ -14,7 +14,7 @@ editable: false
|
|||||||
Lumi is the core web UI and bot runtime.
|
Lumi is the core web UI and bot runtime.
|
||||||
## Runtime
|
## Runtime
|
||||||
Package: lumi-bot
|
Package: lumi-bot
|
||||||
Version: 0.2.9
|
Version: 0.2.10
|
||||||
## Routes
|
## Routes
|
||||||
- GET /api/events
|
- GET /api/events
|
||||||
- POST /api/destructive-confirmations
|
- POST /api/destructive-confirmations
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "lumi-bot",
|
"name": "lumi-bot",
|
||||||
"version": "0.2.9",
|
"version": "0.2.10",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "lumi-bot",
|
"name": "lumi-bot",
|
||||||
"version": "0.2.9",
|
"version": "0.2.10",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"adm-zip": "^0.5.12",
|
"adm-zip": "^0.5.12",
|
||||||
"better-sqlite3": "^11.5.0",
|
"better-sqlite3": "^11.5.0",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "lumi-bot",
|
"name": "lumi-bot",
|
||||||
"version": "0.2.9",
|
"version": "0.2.10",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -2,6 +2,36 @@
|
|||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
"channel": "stable",
|
"channel": "stable",
|
||||||
"releases": [
|
"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",
|
"version": "0.2.9",
|
||||||
"ref": "refs/tags/v0.2.9",
|
"ref": "refs/tags/v0.2.9",
|
||||||
|
|||||||
@ -4,8 +4,8 @@ const path = require("path");
|
|||||||
const { findSafeTarget } = require("../src/services/versioning");
|
const { findSafeTarget } = require("../src/services/versioning");
|
||||||
|
|
||||||
const root = path.join(__dirname, "..");
|
const root = path.join(__dirname, "..");
|
||||||
const releaseVersion = "0.2.9";
|
const releaseVersion = "0.2.10";
|
||||||
const previousCoreVersion = "0.2.8";
|
const previousCoreVersion = "0.2.9";
|
||||||
const earliestCompatibleCoreVersion = "0.1.9";
|
const earliestCompatibleCoreVersion = "0.1.9";
|
||||||
const changedPlugins = {
|
const changedPlugins = {
|
||||||
"auto-vc": { from: "0.1.5", to: "0.1.6", knowledge: "auto-vc" },
|
"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(webSearch.minimum_lumi_ai_version, "0.8.2");
|
||||||
assert.equal(hasVersionHeading(readText("plugins/lumi_ai_web_search/CHANGELOG.md"), webSearch.version), true);
|
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.");
|
||||||
|
|||||||
@ -4,7 +4,7 @@ const os = require("os");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
const root = path.join(__dirname, "..");
|
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 { targetForRequestedVersion } = require("../src/services/repo-update");
|
||||||
const { buildStatus } = require("../src/services/update-index");
|
const { buildStatus } = require("../src/services/update-index");
|
||||||
const { normalizeRepositoryRef } = require("../src/services/update-repository");
|
const { normalizeRepositoryRef } = require("../src/services/update-repository");
|
||||||
@ -16,7 +16,7 @@ function readJson(relativePath) {
|
|||||||
|
|
||||||
const releaseIndex = readJson("release-index.json");
|
const releaseIndex = readJson("release-index.json");
|
||||||
const releaseVersions = releaseIndex.releases.map((release) => release.version);
|
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");
|
assert.equal(new Set(releaseVersions).size, releaseVersions.length, "release versions must be unique");
|
||||||
for (const release of releaseIndex.releases) {
|
for (const release of releaseIndex.releases) {
|
||||||
assert.equal(normalizeRepositoryRef(release.ref), release.ref);
|
assert.equal(normalizeRepositoryRef(release.ref), release.ref);
|
||||||
@ -37,6 +37,7 @@ for (const [toolId, version] of Object.entries(currentRelease.tools)) {
|
|||||||
const baseTarget = {
|
const baseTarget = {
|
||||||
current_version: "0.2.4",
|
current_version: "0.2.4",
|
||||||
available_versions: [
|
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.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.8", ref: "refs/tags/v0.2.8", rollback_safe: true },
|
||||||
{ version: "0.2.7", ref: "refs/tags/v0.2.7", 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"
|
channel: "stable"
|
||||||
});
|
});
|
||||||
assert.equal(corrected.version_correction, true);
|
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.update_available, true);
|
||||||
assert.equal(corrected.blocked, false);
|
assert.equal(corrected.blocked, false);
|
||||||
|
|
||||||
@ -118,6 +119,20 @@ try {
|
|||||||
fs.rmSync(fixture, { recursive: true, force: true });
|
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-"));
|
const optionalFixture = fs.mkdtempSync(path.join(os.tmpdir(), "lumi-optional-dependency-"));
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(path.join(optionalFixture, "package.json"), JSON.stringify({ optionalDependencies: { optional: "^1.0.0" } }));
|
fs.writeFileSync(path.join(optionalFixture, "package.json"), JSON.stringify({ optionalDependencies: { optional: "^1.0.0" } }));
|
||||||
|
|||||||
@ -49,13 +49,30 @@ function writeState(rootPath, values) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function npmInvocation(options = {}) {
|
function npmInvocation(options = {}) {
|
||||||
if (process.env.npm_execpath && fs.existsSync(process.env.npm_execpath)) {
|
const environment = options.env || process.env;
|
||||||
return { command: process.execPath, args: [process.env.npm_execpath] };
|
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] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
return {
|
||||||
command: options.platform === "win32" || process.platform === "win32" ? "npm.cmd" : "npm",
|
command: String(environment.ComSpec || environment.COMSPEC || "cmd.exe"),
|
||||||
args: []
|
args: ["/d", "/s", "/c", "npm.cmd"]
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
return { command: "npm", args: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureRuntimeDependencies(options = {}) {
|
function ensureRuntimeDependencies(options = {}) {
|
||||||
@ -118,5 +135,6 @@ function ensureRuntimeDependencies(options = {}) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
dependencyFingerprint,
|
dependencyFingerprint,
|
||||||
dependencyIssues,
|
dependencyIssues,
|
||||||
ensureRuntimeDependencies
|
ensureRuntimeDependencies,
|
||||||
|
npmInvocation
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Lumi Core",
|
"name": "Lumi Core",
|
||||||
"version": "0.2.9",
|
"version": "0.2.10",
|
||||||
"channel": "stable",
|
"channel": "stable",
|
||||||
"released_at": "2026-07-18",
|
"released_at": "2026-07-18",
|
||||||
"compatible_from": "0.1.9",
|
"compatible_from": "0.1.9",
|
||||||
@ -8,7 +8,7 @@
|
|||||||
"replaces_versions": [
|
"replaces_versions": [
|
||||||
"1.2.0"
|
"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,
|
"rollback_safe": true,
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"Node.js 18 or newer"
|
"Node.js 18 or newer"
|
||||||
@ -133,6 +133,18 @@
|
|||||||
],
|
],
|
||||||
"rollback_safe": true,
|
"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."
|
"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."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user