fix: preserve gateway install env sources

This commit is contained in:
stainlu
2026-05-05 01:03:50 +08:00
committed by Peter Steinberger
parent 2571565c8a
commit bd156fa02e
3 changed files with 64 additions and 17 deletions

View File

@@ -31,7 +31,15 @@ const resolveGatewayAuthMock = vi.hoisted(() =>
const resolveSecretRefValuesMock = vi.hoisted(() => vi.fn());
const randomTokenMock = vi.hoisted(() => vi.fn(() => "generated-token"));
const createInstallPlanFixture = vi.hoisted(() => {
return async (params?: { wrapperPath?: string; env?: Record<string, string | undefined> }) => {
return async (params?: {
wrapperPath?: string;
env?: Record<string, string | undefined>;
}): Promise<{
programArguments: string[];
workingDirectory: string;
environment: Record<string, string | undefined>;
environmentValueSources?: Record<string, string | undefined>;
}> => {
const environment: Record<string, string | undefined> = {};
if (params?.wrapperPath || params?.env?.OPENCLAW_WRAPPER) {
environment.OPENCLAW_WRAPPER = params.wrapperPath ?? params.env?.OPENCLAW_WRAPPER;
@@ -48,7 +56,7 @@ const createInstallPlanFixture = vi.hoisted(() => {
const buildGatewayInstallPlanMock = vi.hoisted(() => vi.fn(createInstallPlanFixture));
const parsePortMock = vi.hoisted(() => vi.fn(() => null));
const isGatewayDaemonRuntimeMock = vi.hoisted(() => vi.fn(() => true));
const installDaemonServiceAndEmitMock = vi.hoisted(() => vi.fn(async () => {}));
const installDaemonServiceAndEmitMock = vi.hoisted(() => vi.fn(async (_params?: unknown) => {}));
const actionState = vi.hoisted(() => ({
warnings: [] as string[],
@@ -224,6 +232,7 @@ describe("runDaemonInstall", () => {
installDaemonServiceAndEmitMock.mockReset();
service.isLoaded.mockReset();
service.stage.mockReset();
service.install.mockReset();
service.readCommand.mockReset();
resetRuntimeCapture();
actionState.warnings.length = 0;
@@ -254,6 +263,7 @@ describe("runDaemonInstall", () => {
installDaemonServiceAndEmitMock.mockResolvedValue(undefined);
service.isLoaded.mockResolvedValue(false);
service.stage.mockResolvedValue(undefined);
service.install.mockResolvedValue(undefined);
service.readCommand.mockResolvedValue(null);
resolveNodeStartupTlsEnvironmentMock.mockReturnValue({
NODE_EXTRA_CA_CERTS: undefined,
@@ -296,6 +306,35 @@ describe("runDaemonInstall", () => {
).toBe(true);
});
it("passes service environment value sources through to service install", async () => {
buildGatewayInstallPlanMock.mockResolvedValueOnce({
programArguments: ["openclaw", "gateway", "run"],
workingDirectory: "/tmp",
environment: {
OPENROUTER_API_KEY: "or-operator-key",
},
environmentValueSources: {
OPENROUTER_API_KEY: "file",
},
});
installDaemonServiceAndEmitMock.mockImplementationOnce(async (params?: unknown) => {
await (params as { install: () => Promise<void> }).install();
});
await runDaemonInstall({ json: true });
expect(service.install).toHaveBeenCalledWith(
expect.objectContaining({
environment: {
OPENROUTER_API_KEY: "or-operator-key",
},
environmentValueSources: {
OPENROUTER_API_KEY: "file",
},
}),
);
});
it("does not treat env-template gateway.auth.token as plaintext during install", async () => {
loadConfigMock.mockReturnValue({
gateway: { auth: { mode: "token", token: "${OPENCLAW_GATEWAY_TOKEN}" } },

View File

@@ -160,6 +160,7 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) {
runtime: runtimeRaw,
wrapperPath,
existingEnvironment: existingServiceEnv,
existingEnvironmentValueSources: existingServiceCommand?.environmentValueSources,
config: cfg,
});
if (autoRefreshMessage) {
@@ -207,21 +208,23 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) {
}
}
const { programArguments, workingDirectory, environment } = await buildGatewayInstallPlan({
env: installEnv,
port,
runtime: runtimeRaw,
wrapperPath,
existingEnvironment: existingServiceEnv,
warn: (message) => {
if (json) {
warnings.push(message);
} else {
defaultRuntime.log(message);
}
},
config: cfg,
});
const { programArguments, workingDirectory, environment, environmentValueSources } =
await buildGatewayInstallPlan({
env: installEnv,
port,
runtime: runtimeRaw,
wrapperPath,
existingEnvironment: existingServiceEnv,
existingEnvironmentValueSources: existingServiceCommand?.environmentValueSources,
warn: (message) => {
if (json) {
warnings.push(message);
} else {
defaultRuntime.log(message);
}
},
config: cfg,
});
await installDaemonServiceAndEmit({
serviceNoun: "Gateway",
@@ -236,6 +239,7 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) {
programArguments,
workingDirectory,
environment,
environmentValueSources,
});
},
});
@@ -249,6 +253,7 @@ async function getGatewayServiceAutoRefreshMessage(params: {
runtime: GatewayDaemonRuntime;
wrapperPath?: string;
existingEnvironment?: Record<string, string | undefined>;
existingEnvironmentValueSources?: GatewayServiceCommandConfig["environmentValueSources"];
config: OpenClawConfig;
}): Promise<string | undefined> {
try {
@@ -264,6 +269,7 @@ async function getGatewayServiceAutoRefreshMessage(params: {
runtime: params.runtime,
wrapperPath: params.wrapperPath,
existingEnvironment: params.existingEnvironment,
existingEnvironmentValueSources: params.existingEnvironmentValueSources,
warn: () => undefined,
config: params.config,
});
@@ -284,6 +290,7 @@ async function getGatewayServiceAutoRefreshMessage(params: {
runtime: params.runtime,
wrapperPath: params.wrapperPath,
existingEnvironment: params.existingEnvironment,
existingEnvironmentValueSources: params.existingEnvironmentValueSources,
warn: () => undefined,
config: params.config,
});