fix: select CLI auth profile for runtime prep

This commit is contained in:
Shakker
2026-06-16 17:30:51 +01:00
committed by Shakker
parent c6478defba
commit 045a7148d4
4 changed files with 139 additions and 19 deletions
@@ -109,4 +109,20 @@ describe("external CLI auth scope", () => {
expect(scope?.providerIds).toContain("claude-cli");
});
it("includes Gemini CLI when it is the configured Google model runtime", () => {
const scope = resolveExternalCliAuthScopeFromConfig({
agents: {
defaults: {
models: {
"google/gemini-3.1-pro-preview": {
agentRuntime: { id: "google-gemini-cli" },
},
},
},
},
});
expect(scope?.providerIds).toContain("google-gemini-cli");
});
});
@@ -61,6 +61,7 @@ function addExternalCliRuntimeScope(out: Set<string>, value: string | undefined)
normalized === "codex" ||
normalized === "codex-cli" ||
normalized === "codex-app-server" ||
normalized === "google-gemini-cli" ||
normalized === "openai" ||
normalized === "minimax" ||
normalized === "minimax-cli" ||
+102
View File
@@ -509,6 +509,108 @@ describe("shouldSkipLocalCliCredentialEpoch", () => {
}
});
it("selects the configured Gemini CLI OAuth profile when no explicit profile is passed", async () => {
const { dir, sessionFile } = createSessionFile();
const agentDir = path.join(dir, "agents", "main", "agent");
const authProfileId = "google-gemini-cli:user@example.test";
const prepareExecution = vi.fn(async () => ({
env: { GEMINI_CLI_HOME: path.join(agentDir, "gemini-home") },
}));
const resolveApiKeyForProfile = vi.fn(async () => ({
apiKey: JSON.stringify({ token: "provider-formatted-access", projectId: "project-1" }),
profileId: authProfileId,
profileType: "oauth" as const,
provider: "google-gemini-cli",
email: "user@example.test",
}));
fs.mkdirSync(agentDir, { recursive: true });
saveAuthProfileStore(
{
version: 1,
profiles: {
[authProfileId]: {
type: "oauth",
provider: "google-gemini-cli",
access: "raw-access-token",
refresh: "raw-refresh-token",
expires: 1_800_000_000_000,
projectId: "project-1",
email: "user@example.test",
},
},
},
agentDir,
);
cliBackendsTesting.setDepsForTest({
resolvePluginSetupCliBackend: () => undefined,
resolveRuntimeCliBackends: () => [
{
id: "google-gemini-cli",
pluginId: "google",
bundleMcp: false,
authEpochMode: "profile-only",
prepareExecution,
config: {
command: "gemini",
args: ["--prompt", "{prompt}"],
output: "json",
input: "arg",
sessionMode: "existing",
},
},
],
});
setCliRunnerPrepareTestDeps({
resolveApiKeyForProfile,
});
try {
await prepareCliRunContext({
sessionId: "session-test",
sessionKey: "agent:main:main",
sessionFile,
workspaceDir: dir,
prompt: "latest ask",
provider: "google-gemini-cli",
model: "gemini-3.1-pro-preview",
timeoutMs: 1_000,
runId: "run-test-gemini-oauth-default-profile",
config: {
auth: {
profiles: {
[authProfileId]: {
provider: "google-gemini-cli",
mode: "oauth",
email: "user@example.test",
},
},
},
} as OpenClawConfig,
});
expect(resolveApiKeyForProfile).toHaveBeenCalledWith(
expect.objectContaining({
profileId: authProfileId,
agentDir,
}),
);
expect(prepareExecution).toHaveBeenCalledWith(
expect.objectContaining({
authProfileId,
authCredential: expect.objectContaining({
type: "oauth",
provider: "google-gemini-cli",
access: "raw-access-token",
refresh: "raw-refresh-token",
expires: 1_800_000_000_000,
}),
}),
);
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});
it("stages adopted OAuth credentials for Gemini CLI preparation", async () => {
const { dir, sessionFile } = createSessionFile();
const agentDir = path.join(dir, "agents", "main", "agent");
+20 -19
View File
@@ -33,6 +33,7 @@ import { normalizeMessageChannel } from "../../utils/message-channel.js";
import { resolveAgentDir, resolveSessionAgentIds } from "../agent-scope.js";
import { externalCliDiscoveryForProviderAuth } from "../auth-profiles/external-cli-discovery.js";
import { resolveApiKeyForProfile } from "../auth-profiles/oauth.js";
import { resolveAuthProfileOrder } from "../auth-profiles/order.js";
import { loadAuthProfileStoreForRuntime } from "../auth-profiles/store.js";
import type { AuthProfileCredential, AuthProfileStore } from "../auth-profiles/types.js";
import {
@@ -290,16 +291,29 @@ export async function prepareCliRunContext(
requestedAuthProfileId ?? backendResolved.defaultAuthProfileId?.trim() ?? undefined;
let authStore: AuthProfileStore | undefined;
let authCredential: AuthProfileCredential | undefined;
if (effectiveAuthProfileId) {
authStore = loadAuthProfileStoreForRuntime(agentDir, {
readOnly: true,
const loadScopedAuthStore = (options: { profileId?: string; readOnly?: boolean } = {}) =>
loadAuthProfileStoreForRuntime(agentDir, {
readOnly: options.readOnly ?? true,
externalCli: externalCliDiscoveryForProviderAuth({
cfg: params.config,
provider: params.provider,
profileId: effectiveAuthProfileId,
...(options.profileId ? { profileId: options.profileId } : {}),
}),
});
if (effectiveAuthProfileId) {
authStore = loadScopedAuthStore({ profileId: effectiveAuthProfileId });
authCredential = authStore.profiles[effectiveAuthProfileId];
} else if (backendResolved.prepareExecution || backendResolved.authEpochMode === "profile-only") {
authStore = loadScopedAuthStore();
effectiveAuthProfileId =
resolveAuthProfileOrder({
cfg: params.config,
store: authStore,
provider: params.provider,
})[0]?.trim() || undefined;
if (effectiveAuthProfileId) {
authCredential = authStore.profiles[effectiveAuthProfileId];
}
}
if (
effectiveAuthProfileId &&
@@ -310,13 +324,7 @@ export async function prepareCliRunContext(
})
) {
const authProfileId = effectiveAuthProfileId;
const writableAuthStore = loadAuthProfileStoreForRuntime(agentDir, {
externalCli: externalCliDiscoveryForProviderAuth({
cfg: params.config,
provider: params.provider,
profileId: authProfileId,
}),
});
const writableAuthStore = loadScopedAuthStore({ profileId: authProfileId, readOnly: false });
const resolvedAuth = await prepareDeps.resolveApiKeyForProfile({
cfg: params.config,
store: writableAuthStore,
@@ -325,14 +333,7 @@ export async function prepareCliRunContext(
});
const resolvedAuthProfileId = resolvedAuth?.profileId ?? authProfileId;
const resolvedAuthCredential = resolvedAuth?.credential;
authStore = loadAuthProfileStoreForRuntime(agentDir, {
readOnly: true,
externalCli: externalCliDiscoveryForProviderAuth({
cfg: params.config,
provider: params.provider,
profileId: resolvedAuthProfileId,
}),
});
authStore = loadScopedAuthStore({ profileId: resolvedAuthProfileId });
authCredential = resolvedAuthCredential ?? authStore.profiles[resolvedAuthProfileId];
if (resolvedAuth && authCredential) {
effectiveAuthProfileId = resolvedAuthProfileId;