mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-13 22:36:10 +00:00
222 lines
7.0 KiB
TypeScript
222 lines
7.0 KiB
TypeScript
/**
|
|
* External CLI auth selection scoping.
|
|
* Narrows CLI discovery to the provider/profile selected by model auth routing
|
|
* so runtime auth setup avoids broad CLI probing.
|
|
*/
|
|
import {
|
|
findNormalizedProviderValue,
|
|
normalizeProviderId,
|
|
} from "@openclaw/model-catalog-core/provider-id";
|
|
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
|
import { resolveCliRuntimeExecutionProvider } from "../model-runtime-aliases.js";
|
|
import { resolveProviderIdForAuth } from "../provider-auth-aliases.js";
|
|
import { CLAUDE_CLI_PROFILE_ID } from "./constants.js";
|
|
import type { AuthProfileStore } from "./types.js";
|
|
|
|
const CLAUDE_CLI_PROVIDER_ID = "claude-cli";
|
|
|
|
/** Resolve external CLI overlay scope from the user's auth/model selection. */
|
|
export function resolveExternalCliAuthOverlayScopeFromSelection(params: {
|
|
provider: string;
|
|
cfg?: OpenClawConfig;
|
|
agentId?: string;
|
|
modelId?: string;
|
|
workspaceDir?: string;
|
|
store?: AuthProfileStore;
|
|
userLockedAuthProfileId?: string;
|
|
}): {
|
|
providerIds?: readonly string[];
|
|
ignoreAutoPreferredProfile: boolean;
|
|
} {
|
|
const authScope = resolveExternalCliAuthScopeFromAuthSelection({
|
|
provider: params.provider,
|
|
cfg: params.cfg,
|
|
workspaceDir: params.workspaceDir,
|
|
store: params.store,
|
|
userLockedAuthProfileId: params.userLockedAuthProfileId,
|
|
});
|
|
const selectedRuntimeProvider =
|
|
resolveCliRuntimeExecutionProvider({
|
|
provider: params.provider,
|
|
cfg: params.cfg,
|
|
agentId: params.agentId,
|
|
modelId: params.modelId,
|
|
authProfileId: params.userLockedAuthProfileId,
|
|
}) || (params.provider === CLAUDE_CLI_PROVIDER_ID ? CLAUDE_CLI_PROVIDER_ID : undefined);
|
|
const selectedProvider =
|
|
authScope.selectedProviderId ??
|
|
(selectedRuntimeProvider === CLAUDE_CLI_PROVIDER_ID ? CLAUDE_CLI_PROVIDER_ID : undefined);
|
|
const providerIds = [
|
|
...new Set([
|
|
...authScope.providerIds,
|
|
...(selectedRuntimeProvider === CLAUDE_CLI_PROVIDER_ID ? [CLAUDE_CLI_PROVIDER_ID] : []),
|
|
]),
|
|
];
|
|
return {
|
|
...(providerIds.length > 0 ? { providerIds } : {}),
|
|
ignoreAutoPreferredProfile:
|
|
// Claude CLI should not auto-prefer a profile when runtime selection has
|
|
// already chosen Claude CLI and the user did not lock a profile.
|
|
!params.userLockedAuthProfileId && selectedProvider === CLAUDE_CLI_PROVIDER_ID,
|
|
};
|
|
}
|
|
|
|
function resolveExternalCliAuthScopeFromAuthSelection(params: {
|
|
provider: string;
|
|
cfg?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
store?: AuthProfileStore;
|
|
userLockedAuthProfileId?: string;
|
|
}): {
|
|
providerIds: string[];
|
|
selectedProviderId?: string;
|
|
} {
|
|
if (params.userLockedAuthProfileId) {
|
|
// Locked profile id means discovery should be scoped to that exact profile's
|
|
// compatible external CLI provider, if any.
|
|
const providerId = resolveExternalCliProviderIdForCompatibleAuthProfile({
|
|
...params,
|
|
profileId: params.userLockedAuthProfileId,
|
|
})?.externalCliProviderId;
|
|
return {
|
|
providerIds: providerId ? [providerId] : [],
|
|
...(providerId ? { selectedProviderId: providerId } : {}),
|
|
};
|
|
}
|
|
|
|
const providerIds: string[] = [];
|
|
let sawCompatibleOrderedProfile = false;
|
|
let selectedProviderId: string | undefined;
|
|
for (const profileId of resolveConfiguredAuthProfileOrder(params)) {
|
|
const resolved = resolveExternalCliProviderIdForCompatibleAuthProfile({
|
|
...params,
|
|
profileId,
|
|
});
|
|
if (!resolved.compatible) {
|
|
continue;
|
|
}
|
|
if (!sawCompatibleOrderedProfile) {
|
|
selectedProviderId = resolved.externalCliProviderId;
|
|
sawCompatibleOrderedProfile = true;
|
|
}
|
|
if (resolved.externalCliProviderId) {
|
|
providerIds.push(resolved.externalCliProviderId);
|
|
}
|
|
}
|
|
if (sawCompatibleOrderedProfile) {
|
|
return {
|
|
providerIds: [...new Set(providerIds)],
|
|
...(selectedProviderId ? { selectedProviderId } : {}),
|
|
};
|
|
}
|
|
|
|
let compatibleProfileCount = 0;
|
|
const profileIds = [
|
|
...new Set([
|
|
...Object.keys(params.cfg?.auth?.profiles ?? {}),
|
|
...Object.keys(params.store?.profiles ?? {}),
|
|
]),
|
|
];
|
|
for (const profileId of profileIds) {
|
|
const resolved = resolveExternalCliProviderIdForCompatibleAuthProfile({
|
|
...params,
|
|
profileId,
|
|
});
|
|
if (!resolved.compatible) {
|
|
continue;
|
|
}
|
|
compatibleProfileCount += 1;
|
|
if (resolved.externalCliProviderId) {
|
|
providerIds.push(resolved.externalCliProviderId);
|
|
}
|
|
}
|
|
const uniqueProviderIds = [...new Set(providerIds)];
|
|
return {
|
|
providerIds: uniqueProviderIds,
|
|
...(compatibleProfileCount === 1 && uniqueProviderIds[0]
|
|
? // Without explicit order, select only when compatibility is unambiguous.
|
|
{ selectedProviderId: uniqueProviderIds[0] }
|
|
: {}),
|
|
};
|
|
}
|
|
|
|
function resolveConfiguredAuthProfileOrder(params: {
|
|
provider: string;
|
|
cfg?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
store?: AuthProfileStore;
|
|
}): string[] {
|
|
const providerAuthKey = resolveProviderIdForAuth(params.provider, {
|
|
config: params.cfg,
|
|
workspaceDir: params.workspaceDir,
|
|
});
|
|
const orderedProfileIds =
|
|
resolveAuthProfileOrderEntries({
|
|
order: params.store?.order,
|
|
provider: params.provider,
|
|
providerAuthKey,
|
|
}) ??
|
|
resolveAuthProfileOrderEntries({
|
|
order: params.cfg?.auth?.order,
|
|
provider: params.provider,
|
|
providerAuthKey,
|
|
}) ??
|
|
[];
|
|
return [
|
|
...new Set(
|
|
orderedProfileIds
|
|
.map((profileId) => profileId?.trim())
|
|
.filter((profileId): profileId is string => Boolean(profileId)),
|
|
),
|
|
];
|
|
}
|
|
|
|
function resolveAuthProfileOrderEntries(params: {
|
|
order?: Record<string, string[]>;
|
|
provider: string;
|
|
providerAuthKey: string;
|
|
}): string[] | undefined {
|
|
return (
|
|
findNormalizedProviderValue(params.order, params.providerAuthKey) ??
|
|
(normalizeProviderId(params.providerAuthKey) === normalizeProviderId(params.provider)
|
|
? undefined
|
|
: findNormalizedProviderValue(params.order, params.provider))
|
|
);
|
|
}
|
|
|
|
function resolveExternalCliProviderIdForCompatibleAuthProfile(params: {
|
|
provider: string;
|
|
cfg?: OpenClawConfig;
|
|
workspaceDir?: string;
|
|
store?: AuthProfileStore;
|
|
profileId: string;
|
|
}): {
|
|
compatible: boolean;
|
|
externalCliProviderId?: string;
|
|
} {
|
|
const profile = params.cfg?.auth?.profiles?.[params.profileId];
|
|
const credential = params.store?.profiles?.[params.profileId];
|
|
const profileProvider =
|
|
profile?.provider ??
|
|
credential?.provider ??
|
|
(params.profileId === CLAUDE_CLI_PROFILE_ID ? CLAUDE_CLI_PROVIDER_ID : undefined);
|
|
if (!profileProvider) {
|
|
return { compatible: false };
|
|
}
|
|
const authAliasParams = {
|
|
config: params.cfg,
|
|
workspaceDir: params.workspaceDir,
|
|
};
|
|
const providerAuthKey = resolveProviderIdForAuth(params.provider, authAliasParams);
|
|
const profileAuthKey = resolveProviderIdForAuth(profileProvider, authAliasParams);
|
|
if (!providerAuthKey || profileAuthKey !== providerAuthKey) {
|
|
return { compatible: false };
|
|
}
|
|
return {
|
|
compatible: true,
|
|
...(normalizeProviderId(profileProvider) === CLAUDE_CLI_PROVIDER_ID
|
|
? { externalCliProviderId: CLAUDE_CLI_PROVIDER_ID }
|
|
: {}),
|
|
};
|
|
}
|