When installing the Gateway daemon via LaunchAgent (macOS) or systemd (Linux), environment variables defined in config.env.vars were not being included in the service environment. This caused API keys and other env vars configured in clawdbot.json5 to be unavailable when the Gateway ran as a service. The fix adds a configEnvVars parameter to buildGatewayInstallPlan() which merges config.env.vars into the service environment. Service-specific variables (CLAWDBOT_*, HOME, PATH) take precedence over config env vars. Fixes the issue where users had to manually edit the LaunchAgent plist to add environment variables like GOOGLE_API_KEY.
83 lines
3.0 KiB
TypeScript
83 lines
3.0 KiB
TypeScript
import { resolveGatewayLaunchAgentLabel } from "../daemon/constants.js";
|
|
import { resolveGatewayProgramArguments } from "../daemon/program-args.js";
|
|
import {
|
|
renderSystemNodeWarning,
|
|
resolvePreferredNodePath,
|
|
resolveSystemNodeInfo,
|
|
} from "../daemon/runtime-paths.js";
|
|
import { buildServiceEnvironment } from "../daemon/service-env.js";
|
|
import { formatCliCommand } from "../cli/command-format.js";
|
|
import type { GatewayDaemonRuntime } from "./daemon-runtime.js";
|
|
|
|
type WarnFn = (message: string, title?: string) => void;
|
|
|
|
export type GatewayInstallPlan = {
|
|
programArguments: string[];
|
|
workingDirectory?: string;
|
|
environment: Record<string, string | undefined>;
|
|
};
|
|
|
|
export function resolveGatewayDevMode(argv: string[] = process.argv): boolean {
|
|
const entry = argv[1];
|
|
const normalizedEntry = entry?.replaceAll("\\", "/");
|
|
return Boolean(normalizedEntry?.includes("/src/") && normalizedEntry.endsWith(".ts"));
|
|
}
|
|
|
|
export async function buildGatewayInstallPlan(params: {
|
|
env: Record<string, string | undefined>;
|
|
port: number;
|
|
runtime: GatewayDaemonRuntime;
|
|
token?: string;
|
|
devMode?: boolean;
|
|
nodePath?: string;
|
|
warn?: WarnFn;
|
|
/** Environment variables from config.env.vars to include in the service environment */
|
|
configEnvVars?: Record<string, string | undefined>;
|
|
}): Promise<GatewayInstallPlan> {
|
|
const devMode = params.devMode ?? resolveGatewayDevMode();
|
|
const nodePath =
|
|
params.nodePath ??
|
|
(await resolvePreferredNodePath({
|
|
env: params.env,
|
|
runtime: params.runtime,
|
|
}));
|
|
const { programArguments, workingDirectory } = await resolveGatewayProgramArguments({
|
|
port: params.port,
|
|
dev: devMode,
|
|
runtime: params.runtime,
|
|
nodePath,
|
|
});
|
|
if (params.runtime === "node") {
|
|
const systemNode = await resolveSystemNodeInfo({ env: params.env });
|
|
const warning = renderSystemNodeWarning(systemNode, programArguments[0]);
|
|
if (warning) params.warn?.(warning, "Gateway runtime");
|
|
}
|
|
const serviceEnvironment = buildServiceEnvironment({
|
|
env: params.env,
|
|
port: params.port,
|
|
token: params.token,
|
|
launchdLabel:
|
|
process.platform === "darwin"
|
|
? resolveGatewayLaunchAgentLabel(params.env.CLAWDBOT_PROFILE)
|
|
: undefined,
|
|
});
|
|
|
|
// Merge config.env.vars into the service environment.
|
|
// Config env vars are added first so service-specific vars take precedence.
|
|
const environment: Record<string, string | undefined> = {};
|
|
if (params.configEnvVars) {
|
|
for (const [key, value] of Object.entries(params.configEnvVars)) {
|
|
if (value) environment[key] = value;
|
|
}
|
|
}
|
|
Object.assign(environment, serviceEnvironment);
|
|
|
|
return { programArguments, workingDirectory, environment };
|
|
}
|
|
|
|
export function gatewayInstallErrorHint(platform = process.platform): string {
|
|
return platform === "win32"
|
|
? "Tip: rerun from an elevated PowerShell (Start → type PowerShell → right-click → Run as administrator) or skip service install."
|
|
: `Tip: rerun \`${formatCliCommand("clawdbot gateway install")}\` after fixing the error.`;
|
|
}
|