Download src/commands/agent-exec.auth.test.ts from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 13.9 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/commands/agent-exec.auth.test.ts
- Command line
-
hf download hf://SaylorTwift/openclaw/src/commands/agent-exec.auth.test.ts
-
curl -L -o agent-exec.auth.test.ts https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/commands/agent-exec.auth.test.ts
13.9 kB
| import fs from "node:fs/promises"; | |
| import path from "node:path"; | |
| import { afterEach, describe, expect, it, vi } from "vitest"; | |
| import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; | |
| import { | |
| ensureAuthProfileStore, | |
| findPersistedAuthProfileCredential, | |
| loadAuthProfileStoreForRuntime, | |
| resolveAuthProfileOrder, | |
| resolvePersistedAuthProfileOwnerAgentDir, | |
| } from "../agents/auth-profiles.js"; | |
| import { | |
| clearRuntimeAuthProfileStoreSnapshots, | |
| setRuntimeAuthProfileStoreSnapshot, | |
| } from "../agents/auth-profiles/runtime-snapshots.js"; | |
| import { | |
| inspectPersistedAuthProfileStoreRaw, | |
| readPersistedAuthProfileStoreRaw, | |
| writePersistedAuthProfileStoreRaw, | |
| } from "../agents/auth-profiles/sqlite.js"; | |
| import type { RuntimeEnv } from "../runtime.js"; | |
| import { writeConfigMachineState } from "../state/config-machine-state-write.js"; | |
| import { withOpenClawTestState } from "../test-utils/openclaw-test-state.js"; | |
| import { agentExecCommand } from "./agent-exec.js"; | |
| const tempDirs = useAutoCleanupTempDirTracker(afterEach); | |
| function createRuntime() { | |
| return { runtime: { log: vi.fn(), error: vi.fn(), exit: vi.fn() } satisfies RuntimeEnv }; | |
| } | |
| function successResult() { | |
| return { | |
| payloads: [{ text: "done" }], | |
| meta: { durationMs: 1, finalAssistantVisibleText: "done" }, | |
| }; | |
| } | |
| afterEach(() => { | |
| vi.restoreAllMocks(); | |
| clearRuntimeAuthProfileStoreSnapshots(); | |
| }); | |
| describe("agent exec stored auth", () => { | |
| it("skips external Codex CLI credentials under --auth-env-only", async () => { | |
| const codexHome = tempDirs.make("openclaw-agent-exec-codex-home-"); | |
| await fs.writeFile( | |
| path.join(codexHome, "auth.json"), | |
| JSON.stringify({ | |
| auth_mode: "chatgpt", | |
| tokens: { access_token: "test-access", refresh_token: "test-refresh" }, | |
| }), | |
| "utf8", | |
| ); | |
| const previousCodexHome = process.env.CODEX_HOME; | |
| const previousOpenAiApiKey = process.env.OPENAI_API_KEY; | |
| const previousDatabaseUrl = process.env.DATABASE_URL; | |
| process.env.CODEX_HOME = codexHome; | |
| process.env.OPENAI_API_KEY = "test-openai-key"; | |
| process.env.DATABASE_URL = "postgres://test.invalid/database"; | |
| const { runtime } = createRuntime(); | |
| let profileIds: string[] = []; | |
| let runtimeProfileIds: string[] = []; | |
| let hostExecApiKey: string | undefined; | |
| let hostExecDatabaseUrl: string | undefined; | |
| try { | |
| const { withHostExecInheritedEnvOmitted } = await import("../infra/host-env-security.js"); | |
| await withHostExecInheritedEnvOmitted(["DATABASE_URL"], () => | |
| agentExecCommand("inspect", { authEnvOnly: true }, runtime, { | |
| runAgent: vi.fn(async () => { | |
| profileIds = Object.keys( | |
| ensureAuthProfileStore(undefined, { | |
| allowKeychainPrompt: false, | |
| externalCliProviderIds: ["openai"], | |
| }).profiles, | |
| ); | |
| runtimeProfileIds = Object.keys( | |
| loadAuthProfileStoreForRuntime(undefined, { | |
| allowKeychainPrompt: false, | |
| externalCliProviderIds: ["openai"], | |
| }).profiles, | |
| ); | |
| const { sanitizeHostExecEnv } = await import("../infra/host-env-security.js"); | |
| const hostExecEnv = sanitizeHostExecEnv({ baseEnv: process.env }); | |
| hostExecApiKey = hostExecEnv.OPENAI_API_KEY; | |
| hostExecDatabaseUrl = hostExecEnv.DATABASE_URL; | |
| return successResult(); | |
| }), | |
| }), | |
| ); | |
| } finally { | |
| if (previousCodexHome === undefined) { | |
| delete process.env.CODEX_HOME; | |
| } else { | |
| process.env.CODEX_HOME = previousCodexHome; | |
| } | |
| if (previousOpenAiApiKey === undefined) { | |
| delete process.env.OPENAI_API_KEY; | |
| } else { | |
| process.env.OPENAI_API_KEY = previousOpenAiApiKey; | |
| } | |
| if (previousDatabaseUrl === undefined) { | |
| delete process.env.DATABASE_URL; | |
| } else { | |
| process.env.DATABASE_URL = previousDatabaseUrl; | |
| } | |
| } | |
| expect(profileIds).toEqual([]); | |
| expect(runtimeProfileIds).toEqual([]); | |
| expect(hostExecApiKey).toBeUndefined(); | |
| expect(hostExecDatabaseUrl).toBeUndefined(); | |
| }); | |
| it.each([false, true])( | |
| "reads portable shared credentials across temporary state (local override: %s)", | |
| async (localOverride) => { | |
| await withOpenClawTestState( | |
| { scenario: "minimal", env: { OPENAI_API_KEY: undefined } }, | |
| async (state) => { | |
| writeConfigMachineState("auth.sharedStore", { location: "state-db" }); | |
| const sharedStore = { | |
| version: 1, | |
| order: { openai: ["openai:token", "openai:shared", "openai:private"] }, | |
| usageStats: { | |
| "openai:token": { disabledUntil: Date.now() + 3_600_000 }, | |
| "openai:private": { lastUsed: 123 }, | |
| }, | |
| profiles: { | |
| "openai:shared": { | |
| type: "api_key" as const, | |
| provider: "openai", | |
| key: "shared-test-key", | |
| }, | |
| "openai:token": { | |
| type: "token" as const, | |
| provider: "openai", | |
| token: "shared-test-token", | |
| }, | |
| "openai:private": { | |
| type: "api_key" as const, | |
| provider: "openai", | |
| key: "private-test-key", | |
| copyToAgents: false, | |
| }, | |
| "openai:oauth": { | |
| type: "oauth" as const, | |
| provider: "openai", | |
| access: "test-access", | |
| refresh: "test-refresh", | |
| expires: Date.now() + 60_000, | |
| copyToAgents: true, | |
| }, | |
| }, | |
| }; | |
| writePersistedAuthProfileStoreRaw(sharedStore); | |
| if (localOverride) { | |
| writePersistedAuthProfileStoreRaw( | |
| { | |
| version: 1, | |
| profiles: { | |
| "openai:shared": { type: "api_key", provider: "openai", key: "local-test-key" }, | |
| }, | |
| }, | |
| state.agentDir(), | |
| ); | |
| } | |
| setRuntimeAuthProfileStoreSnapshot(sharedStore, state.agentDir()); | |
| const { runtime } = createRuntime(); | |
| let resolvedKey: string | undefined; | |
| const result = await agentExecCommand("inspect", {}, runtime, { | |
| runAgent: async () => { | |
| expect(process.env.OPENCLAW_STATE_DIR).not.toBe(state.stateDir); | |
| const store = ensureAuthProfileStore(undefined, { | |
| externalCli: { mode: "none" }, | |
| syncExternalCli: false, | |
| }); | |
| expect(Object.keys(store.profiles).toSorted()).toEqual([ | |
| "openai:shared", | |
| "openai:token", | |
| ]); | |
| expect( | |
| loadAuthProfileStoreForRuntime(undefined, { | |
| externalCli: { mode: "none" }, | |
| syncExternalCli: false, | |
| }).profiles, | |
| ).toEqual(store.profiles); | |
| expect(findPersistedAuthProfileCredential({ profileId: "openai:token" })).toEqual( | |
| sharedStore.profiles["openai:token"], | |
| ); | |
| expect(store).toMatchObject({ | |
| runtimeLocalProfileIds: localOverride ? ["openai:shared"] : [], | |
| }); | |
| expect(resolveAuthProfileOrder({ store, provider: "openai", cfg: {} })).toEqual([ | |
| "openai:shared", | |
| "openai:token", | |
| ]); | |
| expect(store.usageStats).toEqual({ | |
| "openai:token": sharedStore.usageStats["openai:token"], | |
| }); | |
| const { resolveApiKeyForProfile, saveAuthProfileStore } = | |
| await import("../agents/auth-profiles.js"); | |
| resolvedKey = ( | |
| await resolveApiKeyForProfile({ store, profileId: "openai:shared", cfg: {} }) | |
| )?.apiKey; | |
| expect(inspectPersistedAuthProfileStoreRaw(state.agentDir()).status).toBe( | |
| localOverride ? "readable" : "missing", | |
| ); | |
| saveAuthProfileStore(store); | |
| return successResult(); | |
| }, | |
| }); | |
| expect(result.envelope.error).toBeUndefined(); | |
| expect(resolvedKey).toBe(localOverride ? "local-test-key" : "shared-test-key"); | |
| expect(readPersistedAuthProfileStoreRaw()).toEqual(sharedStore); | |
| expect(readPersistedAuthProfileStoreRaw(state.agentDir())).toEqual({ | |
| version: 1, | |
| profiles: localOverride | |
| ? { | |
| "openai:shared": { type: "api_key", provider: "openai", key: "local-test-key" }, | |
| } | |
| : {}, | |
| }); | |
| }, | |
| ); | |
| }, | |
| ); | |
| it("rejects an unreadable original shared store before entering temporary exec", async () => { | |
| await withOpenClawTestState({ scenario: "minimal", layout: "split" }, async (state) => { | |
| writeConfigMachineState("auth.sharedStore", { location: "state-db" }); | |
| writePersistedAuthProfileStoreRaw({ version: 1, profiles: "invalid" }); | |
| const { runtime } = createRuntime(); | |
| const runAgent = vi.fn(async () => successResult()); | |
| const result = await agentExecCommand("inspect", {}, runtime, { runAgent }); | |
| expect(result.envelope.error?.message).toContain( | |
| path.join(state.stateDir, "state", "openclaw.sqlite"), | |
| ); | |
| expect(result.envelope.error?.message).toContain("is unreadable; run openclaw doctor --fix"); | |
| expect(runAgent).not.toHaveBeenCalled(); | |
| }); | |
| }); | |
| it("reads stored credentials from the configured agent directory", async () => { | |
| const stateDir = tempDirs.make("openclaw-agent-exec-cfg-auth-"); | |
| const customAgentDir = path.join(stateDir, "custom-home"); | |
| await fs.mkdir(customAgentDir, { recursive: true }); | |
| const seedPath = path.join(stateDir, "openclaw.json"); | |
| await fs.writeFile( | |
| seedPath, | |
| JSON.stringify({ | |
| agents: { entries: { main: { agentDir: customAgentDir } } }, | |
| }), | |
| "utf8", | |
| ); | |
| const { saveAuthProfileStore } = await import("../agents/auth-profiles.js"); | |
| saveAuthProfileStore( | |
| { | |
| version: 1, | |
| profiles: { "openai:stored": { type: "api_key", provider: "openai", key: "test-key" } }, | |
| }, | |
| customAgentDir, | |
| ); | |
| const { runtime } = createRuntime(); | |
| let scopedProfileIds: string[] = []; | |
| await agentExecCommand("inspect", { config: seedPath }, runtime, { | |
| runAgent: vi.fn(async () => { | |
| scopedProfileIds = Object.keys(loadAuthProfileStoreForRuntime()?.profiles ?? {}); | |
| return successResult(); | |
| }), | |
| }); | |
| // The run config strips agentDir to keep run state ephemeral, but credential | |
| // ownership must still follow the operator's configured directory. | |
| expect(scopedProfileIds).toContain("openai:stored"); | |
| }); | |
| it("blocks direct persisted credential reads under --auth-env-only", async () => { | |
| const normalStateDir = tempDirs.make("openclaw-agent-exec-hidden-auth-"); | |
| const normalAgentDir = path.join(normalStateDir, "agents", "main", "agent"); | |
| const previousStateDir = process.env.OPENCLAW_STATE_DIR; | |
| process.env.OPENCLAW_STATE_DIR = normalStateDir; | |
| const { saveAuthProfileStore } = await import("../agents/auth-profiles.js"); | |
| saveAuthProfileStore( | |
| { | |
| version: 1, | |
| profiles: { | |
| "openai:stored": { type: "api_key", provider: "openai", key: "test-key" }, | |
| }, | |
| }, | |
| normalAgentDir, | |
| ); | |
| const { runtime } = createRuntime(); | |
| let persistedCredential: unknown; | |
| let ownerAgentDir: string | undefined; | |
| try { | |
| await agentExecCommand("inspect", { authEnvOnly: true }, runtime, { | |
| runAgent: vi.fn(async () => { | |
| persistedCredential = findPersistedAuthProfileCredential({ | |
| agentDir: normalAgentDir, | |
| profileId: "openai:stored", | |
| }); | |
| ownerAgentDir = resolvePersistedAuthProfileOwnerAgentDir({ | |
| agentDir: normalAgentDir, | |
| profileId: "openai:stored", | |
| }); | |
| return successResult(); | |
| }), | |
| }); | |
| } finally { | |
| if (previousStateDir === undefined) { | |
| delete process.env.OPENCLAW_STATE_DIR; | |
| } else { | |
| process.env.OPENCLAW_STATE_DIR = previousStateDir; | |
| } | |
| } | |
| expect(persistedCredential).toBeUndefined(); | |
| expect(ownerAgentDir).toBeUndefined(); | |
| }); | |
| it("uses the normal stored auth profile when auth-env-only is disabled", async () => { | |
| const normalStateDir = tempDirs.make("openclaw-agent-exec-normal-state-"); | |
| const normalAgentDir = path.join(normalStateDir, "agents", "main", "agent"); | |
| const previousStateDir = process.env.OPENCLAW_STATE_DIR; | |
| process.env.OPENCLAW_STATE_DIR = normalStateDir; | |
| const { saveAuthProfileStore } = await import("../agents/auth-profiles.js"); | |
| saveAuthProfileStore( | |
| { | |
| version: 1, | |
| profiles: { | |
| "openai:stored": { type: "api_key", provider: "openai", key: "test-key" }, | |
| }, | |
| }, | |
| normalAgentDir, | |
| ); | |
| const { runtime } = createRuntime(); | |
| let profileIds: string[] = []; | |
| try { | |
| await agentExecCommand("inspect", { authEnvOnly: false }, runtime, { | |
| runAgent: vi.fn(async () => { | |
| expect(process.env.OPENCLAW_STATE_DIR).not.toBe(normalStateDir); | |
| profileIds = Object.keys( | |
| ensureAuthProfileStore(undefined, { | |
| allowKeychainPrompt: false, | |
| syncExternalCli: false, | |
| }).profiles, | |
| ); | |
| return successResult(); | |
| }), | |
| }); | |
| } finally { | |
| if (previousStateDir === undefined) { | |
| delete process.env.OPENCLAW_STATE_DIR; | |
| } else { | |
| process.env.OPENCLAW_STATE_DIR = previousStateDir; | |
| } | |
| } | |
| expect(profileIds).toContain("openai:stored"); | |
| }); | |
| }); | |