From ac8c1f19bce97b3fd56ca077d3a4e06dc60aa0b7 Mon Sep 17 00:00:00 2001 From: Dave Lauer Date: Tue, 27 Jan 2026 11:30:36 -0500 Subject: [PATCH] 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 --- src/daemon/program-args.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/daemon/program-args.ts b/src/daemon/program-args.ts index 0b3d52b54..d2abffd89 100644 --- a/src/daemon/program-args.ts +++ b/src/daemon/program-args.ts @@ -68,6 +68,20 @@ async function resolveRealpathSafe(inputPath: string): Promise { } } +/** + * 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(); @@ -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