mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:40:44 +00:00
fix: hide bundled runtime npm windows
This commit is contained in:
@@ -21,6 +21,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/bootstrap: refresh cached workspace bootstrap snapshots on long-lived main-session turns when `AGENTS.md`, `SOUL.md`, `MEMORY.md`, or `TOOLS.md` change on disk, while preserving unchanged snapshot identity through the workspace file cache. (#64871; related #43901, #26497, #28594, #30896) Thanks @aimqwest and @mikejuyoon.
|
||||
- macOS Gateway: detect installed-but-unloaded LaunchAgent split-brain states during status, doctor, and restart, and re-bootstrap launchd supervision before falling back to unmanaged listener restarts. Fixes #67335, #53475, and #71060; refs #58890, #60885, and #70801. Thanks @ze1tgeist88, @dafacto, and @vishutdhar.
|
||||
- Plugins/install: treat mirrored core logger dependencies as staged bundled runtime deps so packaged Gateway starts do not crash when the external plugin-runtime-deps root is missing `tslog`. Fixes #72228; supersedes #72493. Thanks @deepujain.
|
||||
- Plugins/install: hide bundled runtime-dependency npm child windows on Windows across Gateway startup, postinstall, and packaged staging paths so Telegram/Anthropic dependency repair no longer flashes shell windows. Fixes #72315. Thanks @athuljayaram and @joshfeng.
|
||||
- Plugins/install: stage bundled plugin runtime dependencies before Gateway startup, drain update restarts, and materialize plugin-owned root chunks in external mirrors so staged deps resolve under native ESM. Fixes #72058; supersedes #72084. Thanks @amnesia106 and @drvoss.
|
||||
- TTS/SecretRef: resolve `messages.tts.providers.*.apiKey` from the active runtime snapshot so SecretRef-backed MiniMax and other TTS provider keys work in runtime reply/audio paths. Fixes #68690. Thanks @joshavant.
|
||||
- Gateway/install: surface systemd user-bus recovery hints during Linux service activation and retry via the machine user scope when `systemctl --user` reports no-medium bus failures. Fixes #39673; refs #44417 and #63561. Thanks @Arbor4, @myrsu, and @mssteuer.
|
||||
|
||||
@@ -817,6 +817,7 @@ export function runBundledPluginPostinstall(params = {}) {
|
||||
encoding: "utf8",
|
||||
env: npmRunner.env ?? installEnv,
|
||||
stdio: "pipe",
|
||||
windowsHide: true,
|
||||
shell: npmRunner.shell,
|
||||
windowsVerbatimArguments: npmRunner.windowsVerbatimArguments,
|
||||
});
|
||||
|
||||
@@ -877,13 +877,15 @@ function runNpmInstall(params) {
|
||||
npm_config_save: "false",
|
||||
npm_config_yes: "true",
|
||||
};
|
||||
const result = spawnSync(params.npmRunner.command, params.npmRunner.args, {
|
||||
const runSpawnSync = params.spawnSyncImpl ?? spawnSync;
|
||||
const result = runSpawnSync(params.npmRunner.command, params.npmRunner.args, {
|
||||
cwd: params.cwd,
|
||||
encoding: "utf8",
|
||||
env: npmEnv,
|
||||
shell: params.npmRunner.shell,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
timeout: params.timeoutMs ?? 5 * 60 * 1000,
|
||||
windowsHide: true,
|
||||
windowsVerbatimArguments: params.npmRunner.windowsVerbatimArguments,
|
||||
});
|
||||
if (result.status === 0) {
|
||||
@@ -1240,6 +1242,10 @@ export function stageBundledPluginRuntimeDeps(params = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
export const __testing = {
|
||||
runNpmInstall,
|
||||
};
|
||||
|
||||
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
||||
stageBundledPluginRuntimeDeps();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { spawn, spawnSync } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
import { EventEmitter } from "node:events";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
@@ -27,9 +28,11 @@ import {
|
||||
|
||||
vi.mock("node:child_process", async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import("node:child_process")>()),
|
||||
spawn: vi.fn(),
|
||||
spawnSync: vi.fn(),
|
||||
}));
|
||||
|
||||
const spawnMock = vi.mocked(spawn);
|
||||
const spawnSyncMock = vi.mocked(spawnSync);
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
@@ -91,6 +94,7 @@ function statfsFixture(params: {
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
spawnMock.mockReset();
|
||||
spawnSyncMock.mockReset();
|
||||
bundledRuntimeDepsActivityTesting.resetBundledRuntimeDepsInstallActivity();
|
||||
for (const dir of tempDirs.splice(0)) {
|
||||
@@ -312,6 +316,7 @@ describe("installBundledRuntimeDeps", () => {
|
||||
["C:\\node\\node_modules\\npm\\bin\\npm-cli.js", "install", "--ignore-scripts", "acpx@0.5.3"],
|
||||
expect.objectContaining({
|
||||
cwd: installRoot,
|
||||
windowsHide: true,
|
||||
env: expect.objectContaining({
|
||||
npm_config_legacy_peer_deps: "true",
|
||||
npm_config_package_lock: "false",
|
||||
@@ -330,6 +335,36 @@ describe("installBundledRuntimeDeps", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("hides async npm child windows for startup repair installs", async () => {
|
||||
const installRoot = makeTempDir();
|
||||
spawnMock.mockImplementation((_command, _args, options) => {
|
||||
writeInstalledPackage(String(options?.cwd ?? ""), "acpx", "0.5.3");
|
||||
const child = new EventEmitter() as ReturnType<typeof spawn>;
|
||||
Object.assign(child, {
|
||||
stdout: new EventEmitter(),
|
||||
stderr: new EventEmitter(),
|
||||
});
|
||||
queueMicrotask(() => child.emit("close", 0, null));
|
||||
return child;
|
||||
});
|
||||
|
||||
await repairBundledRuntimeDepsInstallRootAsync({
|
||||
installRoot,
|
||||
missingSpecs: ["acpx@0.5.3"],
|
||||
installSpecs: ["acpx@0.5.3"],
|
||||
env: {},
|
||||
});
|
||||
|
||||
expect(spawnMock).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.any(Array),
|
||||
expect.objectContaining({
|
||||
cwd: installRoot,
|
||||
windowsHide: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("anchors non-isolated external install roots with a package manifest", () => {
|
||||
const parentRoot = makeTempDir();
|
||||
const installRoot = path.join(parentRoot, ".openclaw", "plugin-runtime-deps", "openclaw-test");
|
||||
|
||||
@@ -1409,6 +1409,7 @@ async function spawnBundledRuntimeDepsInstall(params: {
|
||||
cwd: params.cwd,
|
||||
env: params.env,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
windowsHide: true,
|
||||
});
|
||||
const stdout: Buffer[] = [];
|
||||
const stderr: Buffer[] = [];
|
||||
@@ -1480,6 +1481,7 @@ export function installBundledRuntimeDeps(params: {
|
||||
encoding: "utf8",
|
||||
env: npmRunner.env ?? installEnv,
|
||||
stdio: "pipe",
|
||||
windowsHide: true,
|
||||
});
|
||||
if (result.status !== 0 || result.error) {
|
||||
throw new Error(formatBundledRuntimeDepsInstallError(result));
|
||||
|
||||
@@ -79,6 +79,7 @@ describe("bundled plugin postinstall", () => {
|
||||
},
|
||||
shell: false,
|
||||
stdio: "pipe",
|
||||
windowsHide: true,
|
||||
windowsVerbatimArguments: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
__testing as stageBundledPluginRuntimeDepsTesting,
|
||||
collectRuntimeDependencyInstallManifest,
|
||||
collectRuntimeDependencyInstallSpecs,
|
||||
stageBundledPluginRuntimeDeps,
|
||||
@@ -129,6 +130,31 @@ describe("stageBundledPluginRuntimeDeps", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("hides npm child windows during fallback runtime installs", () => {
|
||||
const spawnSyncImpl = vi.fn(() => ({ status: 0, stderr: "", stdout: "" }));
|
||||
|
||||
stageBundledPluginRuntimeDepsTesting.runNpmInstall({
|
||||
cwd: "C:\\openclaw\\dist\\extensions\\telegram\\.openclaw-install-stage",
|
||||
npmRunner: {
|
||||
command: "npm.cmd",
|
||||
args: ["install", "--silent"],
|
||||
env: { PATH: "C:\\node" },
|
||||
shell: false,
|
||||
windowsVerbatimArguments: true,
|
||||
},
|
||||
spawnSyncImpl,
|
||||
});
|
||||
|
||||
expect(spawnSyncImpl).toHaveBeenCalledWith(
|
||||
"npm.cmd",
|
||||
["install", "--silent"],
|
||||
expect.objectContaining({
|
||||
windowsHide: true,
|
||||
windowsVerbatimArguments: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("skips restaging when runtime deps stamp matches the sanitized manifest", () => {
|
||||
const { pluginDir, repoRoot } = createBundledPluginFixture({
|
||||
packageJson: {
|
||||
|
||||
Reference in New Issue
Block a user