mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-24 10:12:44 +00:00
fix(server): keep the local speech library path out of agent environments (#5987)
The supervisor prepended the sherpa-onnx platform package directory to the daemon worker's loader variable (PATH, LD_LIBRARY_PATH, DYLD_LIBRARY_PATH), so every agent and terminal inherited it. In packaged desktop builds that directory resolves inside app.asar, and on Windows Git Bash truncates the PATH it hands to native executables at that entry. The local speech worker already sets the loader variable on its own environment when it is forked, so the daemon worker does not need it. Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com> Co-authored-by: hanhnt2 <56497031+hanh9898@users.noreply.github.com>
This commit is contained in:
co-authored by
hanhnt2
parent
0d05584d04
commit
749fe3e5ae
@@ -0,0 +1,85 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { expect, test } from "vitest";
|
||||
import {
|
||||
resolveSherpaLoaderEnv,
|
||||
sherpaLoaderEnvKey,
|
||||
} from "../src/server/speech/providers/local/sherpa/sherpa-runtime-env.js";
|
||||
|
||||
const loaderKey = sherpaLoaderEnvKey();
|
||||
const loaderEnv = resolveSherpaLoaderEnv();
|
||||
|
||||
// Agents and terminals inherit the daemon worker's environment, so the local speech
|
||||
// runtime's library directory must stay scoped to the speech worker.
|
||||
test.runIf(loaderKey && loaderEnv)(
|
||||
"daemon worker environment does not carry the local speech library directory",
|
||||
async () => {
|
||||
const root = await mkdtemp(path.join(tmpdir(), "paseo-supervisor-env-"));
|
||||
const reportPath = path.join(root, "worker-env.json");
|
||||
const preloadPath = path.join(root, "report-worker-env.mjs");
|
||||
await writeFile(
|
||||
preloadPath,
|
||||
`
|
||||
import { writeFileSync } from "node:fs";
|
||||
if (/daemon-worker\\.(ts|js)$/.test(process.argv[1] ?? "")) {
|
||||
writeFileSync(${JSON.stringify(reportPath)}, JSON.stringify({ value: process.env[${JSON.stringify(loaderKey)}] ?? "" }));
|
||||
process.exit(0);
|
||||
}
|
||||
`,
|
||||
);
|
||||
const env = Object.fromEntries(
|
||||
Object.entries(process.env).filter(([key]) => !key.startsWith("PASEO_")),
|
||||
);
|
||||
// Start from a parent environment that does not already list the speech library directory.
|
||||
const envLoaderKey =
|
||||
Object.keys(env).find((key) => key.toLowerCase() === loaderKey!.toLowerCase()) ?? loaderKey!;
|
||||
const parentValue = (env[envLoaderKey] ?? "")
|
||||
.split(path.delimiter)
|
||||
.filter((entry) => entry && entry !== loaderEnv!.libDir)
|
||||
.join(path.delimiter);
|
||||
env[envLoaderKey] = parentValue;
|
||||
const child = spawn(
|
||||
process.execPath,
|
||||
["--import", "tsx", fileURLToPath(new URL("./supervisor-entrypoint.ts", import.meta.url))],
|
||||
{
|
||||
cwd: fileURLToPath(new URL("../../../", import.meta.url)),
|
||||
env: {
|
||||
...env,
|
||||
HOME: root,
|
||||
USERPROFILE: root,
|
||||
PASEO_HOME: path.join(root, "home"),
|
||||
NODE_OPTIONS: `--import=${pathToFileURL(preloadPath).href}`,
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
},
|
||||
);
|
||||
let output = "";
|
||||
child.stdout.on("data", (chunk) => {
|
||||
output += chunk;
|
||||
});
|
||||
child.stderr.on("data", (chunk) => {
|
||||
output += chunk;
|
||||
});
|
||||
try {
|
||||
await expect
|
||||
.poll(async () => readFile(reportPath, "utf8").catch(() => null), {
|
||||
timeout: 20_000,
|
||||
message: output,
|
||||
})
|
||||
.not.toBeNull();
|
||||
const report = JSON.parse(await readFile(reportPath, "utf8")) as { value: string };
|
||||
expect(report.value).toBe(parentValue);
|
||||
} finally {
|
||||
if (child.exitCode === null) {
|
||||
const closed = new Promise((resolve) => child.once("close", resolve));
|
||||
child.kill("SIGKILL");
|
||||
await closed;
|
||||
}
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
30_000,
|
||||
);
|
||||
@@ -14,7 +14,6 @@ import { PRIVATE_FILE_MODE } from "../src/server/private-files.js";
|
||||
import { loadPersistedConfig } from "../src/server/persisted-config.js";
|
||||
import { runSupervisor } from "./supervisor.js";
|
||||
import { resolveSupervisorLogFile } from "./supervisor-log-config.js";
|
||||
import { applySherpaLoaderEnv } from "../src/server/speech/providers/local/sherpa/sherpa-runtime-env.js";
|
||||
|
||||
process.title = "Paseo Supervisor";
|
||||
|
||||
@@ -108,8 +107,6 @@ async function main(): Promise<void> {
|
||||
? resolvePackagedNodeEntrypointRunnerPath(fileURLToPath(import.meta.url))
|
||||
: null;
|
||||
|
||||
applySherpaLoaderEnv(workerEnv);
|
||||
|
||||
const paseoHome = resolvePaseoHome(workerEnv);
|
||||
const persistedConfig = loadPersistedConfig(paseoHome);
|
||||
const supervisorLogFile = resolveSupervisorLogFile(paseoHome, persistedConfig, workerEnv);
|
||||
|
||||
Reference in New Issue
Block a user