mirror of https://github.com/openclaw/openclaw.git
fix: persist auth profile env refs for daemon install
This commit is contained in:
parent
549cb65ba4
commit
ba9fb4d994
|
|
@ -1,6 +1,7 @@
|
|||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
loadAuthProfileStoreForSecretsRuntime: vi.fn(),
|
||||
resolvePreferredNodePath: vi.fn(),
|
||||
resolveGatewayProgramArguments: vi.fn(),
|
||||
resolveSystemNodeInfo: vi.fn(),
|
||||
|
|
@ -8,6 +9,10 @@ const mocks = vi.hoisted(() => ({
|
|||
buildServiceEnvironment: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../agents/auth-profiles.js", () => ({
|
||||
loadAuthProfileStoreForSecretsRuntime: mocks.loadAuthProfileStoreForSecretsRuntime,
|
||||
}));
|
||||
|
||||
vi.mock("../daemon/runtime-paths.js", () => ({
|
||||
resolvePreferredNodePath: mocks.resolvePreferredNodePath,
|
||||
resolveSystemNodeInfo: mocks.resolveSystemNodeInfo,
|
||||
|
|
@ -63,6 +68,10 @@ function mockNodeGatewayPlanFixture(
|
|||
programArguments: ["node", "gateway"],
|
||||
workingDirectory,
|
||||
});
|
||||
mocks.loadAuthProfileStoreForSecretsRuntime.mockReturnValue({
|
||||
version: 1,
|
||||
profiles: {},
|
||||
});
|
||||
mocks.resolveSystemNodeInfo.mockResolvedValue({
|
||||
path: "/opt/node",
|
||||
version,
|
||||
|
|
@ -232,6 +241,67 @@ describe("buildGatewayInstallPlan", () => {
|
|||
expect(plan.environment.HOME).toBe("/Users/service");
|
||||
expect(plan.environment.OPENCLAW_PORT).toBe("3000");
|
||||
});
|
||||
|
||||
it("merges env-backed auth-profile refs into the service environment", async () => {
|
||||
mockNodeGatewayPlanFixture({
|
||||
serviceEnvironment: {
|
||||
OPENCLAW_PORT: "3000",
|
||||
},
|
||||
});
|
||||
mocks.loadAuthProfileStoreForSecretsRuntime.mockReturnValue({
|
||||
version: 1,
|
||||
profiles: {
|
||||
"openai:default": {
|
||||
type: "api_key",
|
||||
provider: "openai",
|
||||
keyRef: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
||||
},
|
||||
"anthropic:default": {
|
||||
type: "token",
|
||||
provider: "anthropic",
|
||||
tokenRef: { source: "env", provider: "default", id: "ANTHROPIC_TOKEN" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const plan = await buildGatewayInstallPlan({
|
||||
env: {
|
||||
OPENAI_API_KEY: "sk-openai-test", // pragma: allowlist secret
|
||||
ANTHROPIC_TOKEN: "ant-test-token",
|
||||
},
|
||||
port: 3000,
|
||||
runtime: "node",
|
||||
});
|
||||
|
||||
expect(plan.environment.OPENAI_API_KEY).toBe("sk-openai-test");
|
||||
expect(plan.environment.ANTHROPIC_TOKEN).toBe("ant-test-token");
|
||||
});
|
||||
|
||||
it("skips unresolved auth-profile env refs", async () => {
|
||||
mockNodeGatewayPlanFixture({
|
||||
serviceEnvironment: {
|
||||
OPENCLAW_PORT: "3000",
|
||||
},
|
||||
});
|
||||
mocks.loadAuthProfileStoreForSecretsRuntime.mockReturnValue({
|
||||
version: 1,
|
||||
profiles: {
|
||||
"openai:default": {
|
||||
type: "api_key",
|
||||
provider: "openai",
|
||||
keyRef: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const plan = await buildGatewayInstallPlan({
|
||||
env: {},
|
||||
port: 3000,
|
||||
runtime: "node",
|
||||
});
|
||||
|
||||
expect(plan.environment.OPENAI_API_KEY).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("gatewayInstallErrorHint", () => {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
import {
|
||||
loadAuthProfileStoreForSecretsRuntime,
|
||||
type AuthProfileStore,
|
||||
} from "../agents/auth-profiles.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { collectConfigServiceEnvVars } from "../config/env-vars.js";
|
||||
import type { OpenClawConfig } from "../config/types.js";
|
||||
|
|
@ -19,6 +23,33 @@ export type GatewayInstallPlan = {
|
|||
environment: Record<string, string | undefined>;
|
||||
};
|
||||
|
||||
function collectAuthProfileServiceEnvVars(params: {
|
||||
env: Record<string, string | undefined>;
|
||||
authStore?: AuthProfileStore;
|
||||
}): Record<string, string> {
|
||||
const authStore = params.authStore ?? loadAuthProfileStoreForSecretsRuntime();
|
||||
const entries: Record<string, string> = {};
|
||||
|
||||
for (const credential of Object.values(authStore.profiles)) {
|
||||
const ref =
|
||||
credential.type === "api_key"
|
||||
? credential.keyRef
|
||||
: credential.type === "token"
|
||||
? credential.tokenRef
|
||||
: undefined;
|
||||
if (!ref || ref.source !== "env") {
|
||||
continue;
|
||||
}
|
||||
const value = params.env[ref.id]?.trim();
|
||||
if (!value) {
|
||||
continue;
|
||||
}
|
||||
entries[ref.id] = value;
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
export async function buildGatewayInstallPlan(params: {
|
||||
env: Record<string, string | undefined>;
|
||||
port: number;
|
||||
|
|
@ -28,6 +59,7 @@ export async function buildGatewayInstallPlan(params: {
|
|||
warn?: DaemonInstallWarnFn;
|
||||
/** Full config to extract env vars from (env vars + inline env keys). */
|
||||
config?: OpenClawConfig;
|
||||
authStore?: AuthProfileStore;
|
||||
}): Promise<GatewayInstallPlan> {
|
||||
const { devMode, nodePath } = await resolveDaemonInstallRuntimeInputs({
|
||||
env: params.env,
|
||||
|
|
@ -61,6 +93,10 @@ export async function buildGatewayInstallPlan(params: {
|
|||
// Config env vars are added first so service-specific vars take precedence.
|
||||
const environment: Record<string, string | undefined> = {
|
||||
...collectConfigServiceEnvVars(params.config),
|
||||
...collectAuthProfileServiceEnvVars({
|
||||
env: params.env,
|
||||
authStore: params.authStore,
|
||||
}),
|
||||
};
|
||||
Object.assign(environment, serviceEnvironment);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue