mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-19 15:52:49 +00:00
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>
86 lines
3.1 KiB
TypeScript
86 lines
3.1 KiB
TypeScript
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,
|
|
);
|