mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 20:35:39 -06:00
fix: select CLI auth profile for runtime prep
This commit is contained in:
@@ -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" ||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user