fix: set WorkingDirectory in systemd service for control UI assets

The systemd service file was generated without a WorkingDirectory directive,
causing the control UI to fail with "Control UI assets not found" because
the gateway process ran with cwd set to the user's home directory instead
of the repo/package root.

The resolveControlUiRoot() function uses process.cwd() as a fallback for
finding dist/control-ui, which failed when cwd was wrong.

This fix derives workingDirectory from the CLI entrypoint path for all
production runtime paths (node, bun, auto) so that `moltbot gateway install`
and `moltbot doctor --repair` generate service files with the correct
WorkingDirectory.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Dave Lauer 2026-01-27 11:30:36 -05:00
parent 3f83afe4a6
commit ac8c1f19bc

View File

@ -68,6 +68,20 @@ async function resolveRealpathSafe(inputPath: string): Promise<string> {
}
}
/**
* Derive working directory from a dist entrypoint path.
* E.g., /path/to/moltbot/dist/entry.js -> /path/to/moltbot
*/
function resolveWorkingDirectoryFromEntrypoint(entrypointPath: string): string | undefined {
const dir = path.dirname(entrypointPath);
const parts = dir.split(path.sep);
const distIndex = parts.lastIndexOf("dist");
if (distIndex > 0) {
return parts.slice(0, distIndex).join(path.sep);
}
return undefined;
}
function buildDistCandidates(...inputs: string[]): string[] {
const candidates: string[] = [];
const seen = new Set<string>();
@ -167,6 +181,7 @@ async function resolveCliProgramArguments(params: {
const cliEntrypointPath = await resolveCliEntrypointPathForService();
return {
programArguments: [nodePath, cliEntrypointPath, ...params.args],
workingDirectory: resolveWorkingDirectoryFromEntrypoint(cliEntrypointPath),
};
}
@ -186,6 +201,7 @@ async function resolveCliProgramArguments(params: {
const cliEntrypointPath = await resolveCliEntrypointPathForService();
return {
programArguments: [bunPath, cliEntrypointPath, ...params.args],
workingDirectory: resolveWorkingDirectoryFromEntrypoint(cliEntrypointPath),
};
}
@ -194,6 +210,7 @@ async function resolveCliProgramArguments(params: {
const cliEntrypointPath = await resolveCliEntrypointPathForService();
return {
programArguments: [execPath, cliEntrypointPath, ...params.args],
workingDirectory: resolveWorkingDirectoryFromEntrypoint(cliEntrypointPath),
};
} catch (error) {
// If running under bun or another runtime that can execute TS directly