Skip to content

Commit 3dfb9b3

Browse files
committed
fix service env placeholder collection
Signed-off-by: sallyom <somalley@redhat.com>
1 parent 725ddd1 commit 3dfb9b3

3 files changed

Lines changed: 124 additions & 0 deletions

File tree

src/commands/daemon-install-helpers.test.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,6 +1146,79 @@ describe("buildGatewayInstallPlan — dotenv merge", () => {
11461146
);
11471147
});
11481148

1149+
it("retains .env values for macOS LaunchAgent env SecretRefs", async () => {
1150+
await writeStateDirDotEnv("MINIMAX_API_KEY=minimax-dotenv-key\n", {
1151+
stateDir: path.join(tmpDir, ".openclaw"),
1152+
});
1153+
mockNodeGatewayPlanFixture({
1154+
serviceEnvironment: {
1155+
HOME: "/from-service",
1156+
OPENCLAW_LAUNCHD_LABEL: "ai.openclaw.gateway",
1157+
OPENCLAW_PORT: "3000",
1158+
},
1159+
});
1160+
1161+
const plan = await buildGatewayInstallPlan({
1162+
env: { HOME: tmpDir },
1163+
port: 3000,
1164+
runtime: "node",
1165+
platform: "darwin",
1166+
config: {
1167+
models: {
1168+
providers: {
1169+
"minimax-openai": {
1170+
baseUrl: "https://api.minimax.io/v1",
1171+
apiKey: { source: "env", provider: "default", id: "MINIMAX_API_KEY" },
1172+
models: [],
1173+
},
1174+
},
1175+
},
1176+
},
1177+
});
1178+
1179+
expect(plan.environment.MINIMAX_API_KEY).toBe("minimax-dotenv-key");
1180+
expect(plan.environment.OPENCLAW_SERVICE_MANAGED_ENV_KEYS).toBe("MINIMAX_API_KEY");
1181+
});
1182+
1183+
it("retains .env values when config env has an unresolved self reference", async () => {
1184+
await writeStateDirDotEnv("MINIMAX_API_KEY=minimax-dotenv-key\n", {
1185+
stateDir: path.join(tmpDir, ".openclaw"),
1186+
});
1187+
mockNodeGatewayPlanFixture({
1188+
serviceEnvironment: {
1189+
HOME: "/from-service",
1190+
OPENCLAW_LAUNCHD_LABEL: "ai.openclaw.gateway",
1191+
OPENCLAW_PORT: "3000",
1192+
},
1193+
});
1194+
1195+
const plan = await buildGatewayInstallPlan({
1196+
env: { HOME: tmpDir },
1197+
port: 3000,
1198+
runtime: "node",
1199+
platform: "darwin",
1200+
config: {
1201+
env: {
1202+
vars: {
1203+
MINIMAX_API_KEY: "${MINIMAX_API_KEY}",
1204+
},
1205+
},
1206+
models: {
1207+
providers: {
1208+
"minimax-openai": {
1209+
baseUrl: "https://api.minimax.io/v1",
1210+
apiKey: { source: "env", provider: "default", id: "MINIMAX_API_KEY" },
1211+
models: [],
1212+
},
1213+
},
1214+
},
1215+
},
1216+
});
1217+
1218+
expect(plan.environment.MINIMAX_API_KEY).toBe("minimax-dotenv-key");
1219+
expect(plan.environment.OPENCLAW_SERVICE_MANAGED_ENV_KEYS).toBe("MINIMAX_API_KEY");
1220+
});
1221+
11491222
it("does not retain config env values for macOS LaunchAgent env files", async () => {
11501223
await writeStateDirDotEnv("OPENROUTER_API_KEY=or-dotenv\nTAVILY_API_KEY=dotenv-tavily\n", {
11511224
stateDir: path.join(tmpDir, ".openclaw"),

src/config/config-env-vars.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ function collectConfigEnvVarsByTarget(cfg?: OpenClawConfig): Record<string, stri
3131
if (isBlockedConfigEnvVar(key)) {
3232
continue;
3333
}
34+
if (containsEnvVarReference(value)) {
35+
continue;
36+
}
3437
entries[key] = value;
3538
}
3639
}
@@ -49,6 +52,9 @@ function collectConfigEnvVarsByTarget(cfg?: OpenClawConfig): Record<string, stri
4952
if (isBlockedConfigEnvVar(key)) {
5053
continue;
5154
}
55+
if (containsEnvVarReference(value)) {
56+
continue;
57+
}
5258
entries[key] = value;
5359
}
5460

src/config/config.env-vars.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,51 @@ describe("config env vars", () => {
121121
});
122122
});
123123

124+
it("drops unresolved env references from config env", async () => {
125+
const entries = collectConfigRuntimeEnvVars({
126+
env: {
127+
vars: {
128+
OPENROUTER_API_KEY: "${OPENROUTER_API_KEY}",
129+
BRAVE_API_KEY: "config-key",
130+
},
131+
},
132+
} as OpenClawConfig);
133+
134+
expect(entries.OPENROUTER_API_KEY).toBeUndefined();
135+
expect(entries.BRAVE_API_KEY).toBe("config-key");
136+
});
137+
138+
it("drops unresolved env references from top-level config env", async () => {
139+
const entries = collectConfigRuntimeEnvVars({
140+
env: {
141+
OPENROUTER_API_KEY: "${OPENROUTER_API_KEY}",
142+
BRAVE_API_KEY: "config-key",
143+
},
144+
} as OpenClawConfig);
145+
146+
expect(entries.OPENROUTER_API_KEY).toBeUndefined();
147+
expect(entries.BRAVE_API_KEY).toBe("config-key");
148+
});
149+
150+
it("keeps resolved env references from config env", async () => {
151+
const resolvedConfig = resolveConfigEnvVars(
152+
{
153+
env: {
154+
vars: {
155+
OPENROUTER_API_KEY: "${OPENROUTER_API_KEY}",
156+
BRAVE_API_KEY: "config-key",
157+
},
158+
},
159+
},
160+
{ OPENROUTER_API_KEY: "resolved-key" },
161+
) as OpenClawConfig;
162+
163+
const entries = collectConfigRuntimeEnvVars(resolvedConfig);
164+
165+
expect(entries.OPENROUTER_API_KEY).toBe("resolved-key");
166+
expect(entries.BRAVE_API_KEY).toBe("config-key");
167+
});
168+
124169
it("loads ${VAR} substitutions from ~/.openclaw/.env on repeated runtime loads", async () => {
125170
await withTempHome(async (_home) => {
126171
await withEnvOverride({ BRAVE_API_KEY: undefined }, async () => {

0 commit comments

Comments
 (0)