Skip to content

Commit 9db04a2

Browse files
committed
fix(openai): scope external codex auth to realtime
1 parent ffb02a5 commit 9db04a2

4 files changed

Lines changed: 46 additions & 15 deletions

File tree

extensions/openai/realtime-voice-provider.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ describe("buildOpenAIRealtimeVoiceProvider", () => {
302302
expect(resolveProviderAuthProfileApiKeyMock).toHaveBeenCalledWith({
303303
provider: "openai-codex",
304304
cfg: {},
305+
includeExternalCliAuth: true,
305306
});
306307
const request = requireFetchRequest();
307308
expectRecordFields(request, "fetch request", {
@@ -589,6 +590,7 @@ describe("buildOpenAIRealtimeVoiceProvider", () => {
589590
expect(isProviderAuthProfileConfiguredMock).toHaveBeenCalledWith({
590591
provider: "openai-codex",
591592
cfg,
593+
includeExternalCliAuth: true,
592594
});
593595
});
594596

@@ -632,6 +634,7 @@ describe("buildOpenAIRealtimeVoiceProvider", () => {
632634
expect(resolveProviderAuthProfileApiKeyMock).toHaveBeenCalledWith({
633635
provider: "openai-codex",
634636
cfg,
637+
includeExternalCliAuth: true,
635638
});
636639
expectRecordFields(requireFetchHeaders(), "fetch headers", {
637640
Authorization: "Bearer oauth-realtime-token", // pragma: allowlist secret
@@ -663,6 +666,7 @@ describe("buildOpenAIRealtimeVoiceProvider", () => {
663666
expect(resolveProviderAuthProfileApiKeyMock).toHaveBeenCalledWith({
664667
provider: "openai-codex",
665668
cfg,
669+
includeExternalCliAuth: true,
666670
});
667671
expectRecordFields(requireFetchHeaders(), "fetch headers", {
668672
Authorization: "Bearer oauth-realtime-token", // pragma: allowlist secret

extensions/openai/realtime-voice-provider.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ async function resolveOpenAIRealtimeDefaultAuth(params: {
349349
const codexToken = await resolveProviderAuthProfileApiKey({
350350
provider: "openai-codex",
351351
cfg: params.cfg,
352+
includeExternalCliAuth: true,
352353
});
353354
if (codexToken) {
354355
return { status: "available", kind: "codex-oauth", value: codexToken };
@@ -387,6 +388,7 @@ function hasOpenAIRealtimeBrowserAuthInput(params: {
387388
isProviderAuthProfileConfigured({
388389
provider: "openai-codex",
389390
cfg: params.cfg,
391+
includeExternalCliAuth: true,
390392
}) || hasOpenAIRealtimeApiKeyInput(undefined)
391393
);
392394
}
@@ -725,6 +727,7 @@ class OpenAIRealtimeVoiceBridge implements RealtimeVoiceBridge {
725727
!isProviderAuthProfileConfigured({
726728
provider: "openai-codex",
727729
cfg: cfg.cfg,
730+
includeExternalCliAuth: true,
728731
})
729732
) {
730733
const directApiKey = resolveOpenAIRealtimeEnvApiKey();

src/plugin-sdk/provider-auth.test.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,14 @@ describe("provider auth profile helpers", () => {
8686
);
8787
});
8888

89-
it("scopes external CLI auth discovery to provider profile resolution", async () => {
89+
it("only discovers external CLI auth when provider resolution opts in", async () => {
9090
vi.resetModules();
9191

92-
const store: AuthProfileStore = {
92+
const primaryStore: AuthProfileStore = {
93+
version: 1,
94+
profiles: {},
95+
};
96+
const externalStore: AuthProfileStore = {
9397
version: 1,
9498
profiles: {
9599
"openai-codex:default": {
@@ -102,7 +106,10 @@ describe("provider auth profile helpers", () => {
102106
},
103107
};
104108
const externalCli = { mode: "scoped", providerIds: ["openai-codex"] };
105-
const loadAuthProfileStoreForSecretsRuntime = vi.fn(() => store);
109+
const loadAuthProfileStoreForSecretsRuntime = vi.fn(
110+
(_agentDir?: string, options?: { externalCli?: unknown }) =>
111+
options?.externalCli ? externalStore : primaryStore,
112+
);
106113

107114
vi.doMock("../agents/agent-scope-config.js", () => ({
108115
resolveDefaultAgentDir: () => "/tmp/openclaw-agent",
@@ -126,18 +133,27 @@ describe("provider auth profile helpers", () => {
126133
.map(([profileId]) => profileId),
127134
}));
128135
vi.doMock("../agents/auth-profiles/store.js", () => ({
129-
ensureAuthProfileStore: vi.fn(() => store),
130-
ensureAuthProfileStoreForLocalUpdate: vi.fn(() => store),
136+
ensureAuthProfileStore: vi.fn(() => primaryStore),
137+
ensureAuthProfileStoreForLocalUpdate: vi.fn(() => primaryStore),
131138
loadAuthProfileStoreForSecretsRuntime,
132139
loadAuthProfileStoreWithoutExternalProfiles: vi.fn(() => ({ version: 1, profiles: {} })),
133140
updateAuthProfileStoreWithLock: vi.fn(),
134141
}));
135142

136143
const { isProviderAuthProfileConfigured } = await import("./provider-auth.js");
137144

138-
expect(isProviderAuthProfileConfigured({ provider: "openai-codex" })).toBe(true);
139-
expect(loadAuthProfileStoreForSecretsRuntime).toHaveBeenCalledWith("/tmp/openclaw-agent", {
140-
externalCli,
141-
});
145+
expect(isProviderAuthProfileConfigured({ provider: "openai-codex" })).toBe(false);
146+
expect(
147+
isProviderAuthProfileConfigured({
148+
provider: "openai-codex",
149+
includeExternalCliAuth: true,
150+
}),
151+
).toBe(true);
152+
expect(loadAuthProfileStoreForSecretsRuntime).toHaveBeenNthCalledWith(1, "/tmp/openclaw-agent");
153+
expect(loadAuthProfileStoreForSecretsRuntime).toHaveBeenNthCalledWith(
154+
2,
155+
"/tmp/openclaw-agent",
156+
{ externalCli },
157+
);
142158
});
143159
});

src/plugin-sdk/provider-auth.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ export function listUsableProviderAuthProfileIds(params: {
289289
cfg?: OpenClawConfig;
290290
agentDir?: string;
291291
allowKeychainPrompt?: boolean;
292+
includeExternalCliAuth?: boolean;
292293
}): { agentDir: string; profileIds: string[] } {
293294
try {
294295
const { agentDir, profileIds } = resolveUsableProviderAuthProfiles(params);
@@ -303,6 +304,7 @@ export function isProviderAuthProfileConfigured(params: {
303304
cfg?: OpenClawConfig;
304305
agentDir?: string;
305306
allowKeychainPrompt?: boolean;
307+
includeExternalCliAuth?: boolean;
306308
}): boolean {
307309
return listUsableProviderAuthProfileIds(params).profileIds.length > 0;
308310
}
@@ -312,6 +314,7 @@ export async function resolveProviderAuthProfileApiKey(params: {
312314
cfg?: OpenClawConfig;
313315
agentDir?: string;
314316
allowKeychainPrompt?: boolean;
317+
includeExternalCliAuth?: boolean;
315318
}): Promise<string | undefined> {
316319
const { agentDir, profileIds, store } = resolveUsableProviderAuthProfiles(params);
317320
if (!agentDir || profileIds.length === 0) {
@@ -336,14 +339,19 @@ function resolveUsableProviderAuthProfiles(params: {
336339
cfg?: OpenClawConfig;
337340
agentDir?: string;
338341
allowKeychainPrompt?: boolean;
342+
includeExternalCliAuth?: boolean;
339343
}): { agentDir: string; profileIds: string[]; store: AuthProfileStore } {
340344
const agentDir = params.agentDir?.trim() || resolveDefaultAgentDir(params.cfg ?? {});
341-
const externalCli = externalCliDiscoveryForProviderAuth({
342-
cfg: params.cfg,
343-
provider: params.provider,
344-
allowKeychainPrompt: params.allowKeychainPrompt,
345-
});
346-
const store = loadAuthProfileStoreForSecretsRuntime(agentDir, { externalCli });
345+
const externalCli = params.includeExternalCliAuth
346+
? externalCliDiscoveryForProviderAuth({
347+
cfg: params.cfg,
348+
provider: params.provider,
349+
allowKeychainPrompt: params.allowKeychainPrompt,
350+
})
351+
: undefined;
352+
const store = externalCli
353+
? loadAuthProfileStoreForSecretsRuntime(agentDir, { externalCli })
354+
: loadAuthProfileStoreForSecretsRuntime(agentDir);
347355
const profileIds = resolveAuthProfileOrder({
348356
cfg: params.cfg,
349357
store,

0 commit comments

Comments
 (0)