mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 21:20:43 +00:00
fix: accept aws-sdk auth profiles
This commit is contained in:
@@ -145,6 +145,7 @@ Docs: https://docs.openclaw.ai
|
||||
- CLI backends: keep versioned OAuth identity matches reusable when auth profile ids rotate, so Claude CLI sessions do not reset and lose continuity during same-account OAuth refresh/profile alias changes. Fixes #78541.
|
||||
- Model providers: normalize APNG sniffed PNG uploads, preserve Gemini 3 tool-call thought-signature replay with documented fallback signatures, accept legacy `__env__:VAR` custom-provider keys, and repair snake_case tool-call transcript sanitization. Fixes #51881, #48915, #77566, and #42858.
|
||||
- Telegram/models: parse provider ids containing dots in `/models` callback buttons so `hf.co` model lists render as inline keyboard buttons. Fixes #38745.
|
||||
- Auth profiles/Bedrock: accept persisted `type: "aws-sdk"` auth profiles so EC2/IMDS and shared AWS credential-chain Bedrock setups are not dropped as `invalid_type`. Fixes #69708.
|
||||
- Amazon Bedrock: refresh shared AWS profile/config file credentials before Bedrock model, discovery, and embedding requests so long-running Gateway processes pick up renewed profile credentials without restart. Fixes #77551.
|
||||
- Anthropic: reject uppercase provider-prefixed forward-compat model ids locally instead of sending malformed dynamic ids upstream. Fixes #73715.
|
||||
- OpenAI/embeddings: pass configured output dimensionality through single and batched embedding requests so memory embedding indexes can request smaller vectors. Fixes #55126.
|
||||
|
||||
@@ -17,7 +17,7 @@ export type AuthProfileHealthStatus = "ok" | "expiring" | "expired" | "missing"
|
||||
type AuthProfileHealth = {
|
||||
profileId: string;
|
||||
provider: string;
|
||||
type: "oauth" | "token" | "api_key";
|
||||
type: "oauth" | "token" | "api_key" | "aws-sdk";
|
||||
status: AuthProfileHealthStatus;
|
||||
reasonCode?: AuthCredentialReasonCode;
|
||||
expiresAt?: number;
|
||||
@@ -127,6 +127,17 @@ function buildProfileHealth(params: {
|
||||
};
|
||||
}
|
||||
|
||||
if (healthCredential.type === "aws-sdk") {
|
||||
return {
|
||||
profileId,
|
||||
provider,
|
||||
type: "aws-sdk",
|
||||
status: "static",
|
||||
source,
|
||||
label,
|
||||
};
|
||||
}
|
||||
|
||||
if (healthCredential.type === "token") {
|
||||
const eligibility = evaluateStoredCredentialEligibility({
|
||||
credential: healthCredential,
|
||||
|
||||
@@ -996,6 +996,25 @@ describe("ensureAuthProfileStore", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts aws-sdk auth profiles without static credential material (#69708)", () => {
|
||||
withTempAgentDir("openclaw-auth-aws-sdk-", (agentDir) => {
|
||||
writeAuthProfileStore(agentDir, {
|
||||
"amazon-bedrock:default": {
|
||||
type: "aws-sdk",
|
||||
provider: "amazon-bedrock",
|
||||
createdAt: "2026-03-15T10:00:00.000Z",
|
||||
},
|
||||
});
|
||||
|
||||
const profile = loadAuthProfile(agentDir, "amazon-bedrock:default");
|
||||
|
||||
expect(profile).toMatchObject({
|
||||
type: "aws-sdk",
|
||||
provider: "amazon-bedrock",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: "migrates SecretRef object in `key` to `keyRef` and clears `key`",
|
||||
|
||||
@@ -85,6 +85,10 @@ export function evaluateStoredCredentialEligibility(params: {
|
||||
return { eligible: true, reasonCode: "ok" };
|
||||
}
|
||||
|
||||
if (credential.type === "aws-sdk") {
|
||||
return { eligible: true, reasonCode: "ok" };
|
||||
}
|
||||
|
||||
if (credential.type === "token") {
|
||||
const hasToken = hasConfiguredSecretString(credential.token);
|
||||
const hasTokenRef = hasConfiguredSecretRef(credential.tokenRef);
|
||||
|
||||
@@ -80,7 +80,7 @@ function isProfileConfigCompatible(params: {
|
||||
cfg?: OpenClawConfig;
|
||||
profileId: string;
|
||||
provider: string;
|
||||
mode: "api_key" | "token" | "oauth";
|
||||
mode: "api_key" | "aws-sdk" | "token" | "oauth";
|
||||
allowOAuthTokenCompatibility?: boolean;
|
||||
}): boolean {
|
||||
const profileConfig = params.cfg?.auth?.profiles?.[params.profileId];
|
||||
@@ -311,6 +311,9 @@ export async function resolveApiKeyForProfile(
|
||||
}
|
||||
return buildApiKeyProfileResult({ apiKey: key, provider: cred.provider, email: cred.email });
|
||||
}
|
||||
if (cred.type === "aws-sdk") {
|
||||
return null;
|
||||
}
|
||||
if (cred.type === "token") {
|
||||
const expiryState = resolveTokenExpiryState(cred.expires);
|
||||
if (expiryState === "expired" || expiryState === "invalid_expires") {
|
||||
|
||||
@@ -31,7 +31,12 @@ export type LegacyAuthStore = Record<string, AuthProfileCredential>;
|
||||
type CredentialRejectReason = "non_object" | "invalid_type" | "missing_provider";
|
||||
type RejectedCredentialEntry = { key: string; reason: CredentialRejectReason };
|
||||
|
||||
const AUTH_PROFILE_TYPES = new Set<AuthProfileCredential["type"]>(["api_key", "oauth", "token"]);
|
||||
const AUTH_PROFILE_TYPES = new Set<AuthProfileCredential["type"]>([
|
||||
"api_key",
|
||||
"aws-sdk",
|
||||
"oauth",
|
||||
"token",
|
||||
]);
|
||||
|
||||
function normalizeSecretBackedField(params: {
|
||||
entry: Record<string, unknown>;
|
||||
@@ -539,6 +544,14 @@ export function applyLegacyAuthStore(store: AuthProfileStore, legacy: LegacyAuth
|
||||
};
|
||||
continue;
|
||||
}
|
||||
if (cred.type === "aws-sdk") {
|
||||
store.profiles[profileId] = {
|
||||
type: "aws-sdk",
|
||||
provider: credentialProvider,
|
||||
...(cred.email ? { email: cred.email } : {}),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
if (cred.type === "token") {
|
||||
store.profiles[profileId] = {
|
||||
type: "token",
|
||||
|
||||
@@ -61,7 +61,7 @@ function collectOAuthModeSecretRefViolations(params: {
|
||||
profileId: string;
|
||||
credential: AuthProfileCredential;
|
||||
defaults: SecretDefaults | undefined;
|
||||
configuredMode?: "api_key" | "oauth" | "token";
|
||||
configuredMode?: "api_key" | "aws-sdk" | "oauth" | "token";
|
||||
violations: OAuthSecretRefPolicyViolation[];
|
||||
}): void {
|
||||
if (params.configuredMode !== "oauth") {
|
||||
|
||||
@@ -29,6 +29,17 @@ export type ApiKeyCredential = {
|
||||
metadata?: Record<string, string>;
|
||||
};
|
||||
|
||||
export type AwsSdkCredential = {
|
||||
type: "aws-sdk";
|
||||
provider: string;
|
||||
/** Explicit opt-out for copying this profile when creating another agent. */
|
||||
copyToAgents?: boolean;
|
||||
email?: string;
|
||||
displayName?: string;
|
||||
/** Optional provider-specific metadata (e.g., account IDs, regions). */
|
||||
metadata?: Record<string, string>;
|
||||
};
|
||||
|
||||
export type TokenCredential = {
|
||||
/**
|
||||
* Static bearer-style token (often OAuth access token / PAT).
|
||||
@@ -59,7 +70,11 @@ export type OAuthCredential = OAuthCredentials & {
|
||||
displayName?: string;
|
||||
};
|
||||
|
||||
export type AuthProfileCredential = ApiKeyCredential | TokenCredential | OAuthCredential;
|
||||
export type AuthProfileCredential =
|
||||
| ApiKeyCredential
|
||||
| AwsSdkCredential
|
||||
| TokenCredential
|
||||
| OAuthCredential;
|
||||
|
||||
export type AuthProfileFailureReason =
|
||||
| "auth"
|
||||
|
||||
@@ -99,6 +99,14 @@ function encodeAuthProfileCredential(credential: AuthProfileCredential): string
|
||||
credential.displayName ?? null,
|
||||
encodeUnknown(credential.metadata),
|
||||
]);
|
||||
case "aws-sdk":
|
||||
return JSON.stringify([
|
||||
"aws-sdk",
|
||||
credential.provider,
|
||||
credential.email ?? null,
|
||||
credential.displayName ?? null,
|
||||
encodeUnknown(credential.metadata),
|
||||
]);
|
||||
case "token":
|
||||
return JSON.stringify([
|
||||
"token",
|
||||
|
||||
@@ -290,6 +290,61 @@ async function expectBedrockAuthSource(params: {
|
||||
});
|
||||
}
|
||||
|
||||
it("resolves persisted aws-sdk auth profiles without static keys (#69708)", async () => {
|
||||
const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-bedrock-auth-profile-"));
|
||||
try {
|
||||
await fs.writeFile(
|
||||
path.join(agentDir, "auth-profiles.json"),
|
||||
`${JSON.stringify(
|
||||
{
|
||||
version: 1,
|
||||
profiles: {
|
||||
"amazon-bedrock:default": {
|
||||
type: "aws-sdk",
|
||||
provider: "amazon-bedrock",
|
||||
createdAt: "2026-03-15T10:00:00.000Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
clearRuntimeAuthProfileStoreSnapshots();
|
||||
const store = ensureAuthProfileStore(agentDir);
|
||||
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "amazon-bedrock",
|
||||
profileId: "amazon-bedrock:default",
|
||||
cfg: BEDROCK_PROVIDER_CFG as never,
|
||||
store,
|
||||
agentDir,
|
||||
});
|
||||
|
||||
expect(resolved).toMatchObject({
|
||||
mode: "aws-sdk",
|
||||
profileId: "amazon-bedrock:default",
|
||||
source: "profile:amazon-bedrock:default",
|
||||
});
|
||||
expect(resolved.apiKey).toBeUndefined();
|
||||
await expect(
|
||||
hasAvailableAuthForProvider({
|
||||
provider: "amazon-bedrock",
|
||||
cfg: BEDROCK_PROVIDER_CFG as never,
|
||||
store,
|
||||
agentDir,
|
||||
}),
|
||||
).resolves.toBe(true);
|
||||
expect(resolveModelAuthMode("amazon-bedrock", BEDROCK_PROVIDER_CFG as never, store)).toBe(
|
||||
"aws-sdk",
|
||||
);
|
||||
} finally {
|
||||
await fs.rm(agentDir, { recursive: true, force: true });
|
||||
clearRuntimeAuthProfileStoreSnapshots();
|
||||
}
|
||||
});
|
||||
|
||||
function buildDemoLocalStore(keys: string[]) {
|
||||
return {
|
||||
version: 1 as const,
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
} from "../shared/string-coerce.js";
|
||||
import { normalizeOptionalSecretInput } from "../utils/normalize-secret-input.js";
|
||||
import {
|
||||
type AuthProfileCredential,
|
||||
type AuthProfileStore,
|
||||
externalCliDiscoveryForProviderAuth,
|
||||
ensureAuthProfileStore,
|
||||
@@ -43,6 +44,7 @@ import {
|
||||
type ResolvedProviderAuth,
|
||||
} from "./model-auth-runtime-shared.js";
|
||||
import { normalizeProviderId } from "./model-selection.js";
|
||||
import { resolveProviderIdForAuth } from "./provider-auth-aliases.js";
|
||||
|
||||
export {
|
||||
ensureAuthProfileStore,
|
||||
@@ -234,6 +236,58 @@ function resolveProviderAuthOverride(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function profileTypeToAuthMode(type: AuthProfileCredential["type"]): ResolvedProviderAuth["mode"] {
|
||||
return type === "oauth"
|
||||
? "oauth"
|
||||
: type === "token"
|
||||
? "token"
|
||||
: type === "aws-sdk"
|
||||
? "aws-sdk"
|
||||
: "api-key";
|
||||
}
|
||||
|
||||
function isProfileForProvider(params: {
|
||||
cfg?: OpenClawConfig;
|
||||
credential: AuthProfileCredential;
|
||||
provider: string;
|
||||
}): boolean {
|
||||
return (
|
||||
resolveProviderIdForAuth(params.credential.provider, { config: params.cfg }) ===
|
||||
resolveProviderIdForAuth(params.provider, { config: params.cfg })
|
||||
);
|
||||
}
|
||||
|
||||
function resolveAwsSdkProfileAuth(params: {
|
||||
cfg?: OpenClawConfig;
|
||||
provider: string;
|
||||
profileId: string;
|
||||
credential: AuthProfileCredential | undefined;
|
||||
}): ResolvedProviderAuth | null {
|
||||
if (params.credential?.type !== "aws-sdk") {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
!isProfileForProvider({
|
||||
cfg: params.cfg,
|
||||
credential: params.credential,
|
||||
provider: params.provider,
|
||||
})
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const profileConfig = params.cfg?.auth?.profiles?.[params.profileId];
|
||||
if (profileConfig) {
|
||||
if (profileConfig.provider !== params.credential.provider || profileConfig.mode !== "aws-sdk") {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return {
|
||||
...resolveAwsSdkAuthInfo(),
|
||||
profileId: params.profileId,
|
||||
source: `profile:${params.profileId}`,
|
||||
};
|
||||
}
|
||||
|
||||
function isLocalBaseUrl(baseUrl: string): boolean {
|
||||
try {
|
||||
let host = normalizeLowercaseStringOrEmpty(new URL(baseUrl).hostname);
|
||||
@@ -539,6 +593,15 @@ export async function resolveApiKeyForProvider(params: {
|
||||
profileId,
|
||||
preferredProfile,
|
||||
});
|
||||
const awsSdkProfileAuth = resolveAwsSdkProfileAuth({
|
||||
cfg,
|
||||
provider,
|
||||
profileId,
|
||||
credential: store.profiles[profileId],
|
||||
});
|
||||
if (awsSdkProfileAuth) {
|
||||
return awsSdkProfileAuth;
|
||||
}
|
||||
const resolved = await resolveApiKeyForProfile({
|
||||
cfg,
|
||||
store,
|
||||
@@ -553,7 +616,7 @@ export async function resolveApiKeyForProvider(params: {
|
||||
apiKey: resolved.apiKey,
|
||||
profileId,
|
||||
source: `profile:${profileId}`,
|
||||
mode: mode === "oauth" ? "oauth" : mode === "token" ? "token" : "api-key",
|
||||
mode: mode ? profileTypeToAuthMode(mode) : "api-key",
|
||||
};
|
||||
// When the resolved key is a provider-owned synthetic profile marker and
|
||||
// the caller has not locked this profile, fall through to env/config
|
||||
@@ -641,6 +704,15 @@ export async function resolveApiKeyForProvider(params: {
|
||||
let deferredAuthProfileResult: ResolvedProviderAuth | null = null;
|
||||
for (const candidate of order) {
|
||||
try {
|
||||
const awsSdkProfileAuth = resolveAwsSdkProfileAuth({
|
||||
cfg,
|
||||
provider,
|
||||
profileId: candidate,
|
||||
credential: store.profiles[candidate],
|
||||
});
|
||||
if (awsSdkProfileAuth) {
|
||||
return awsSdkProfileAuth;
|
||||
}
|
||||
const resolved = await resolveApiKeyForProfile({
|
||||
cfg,
|
||||
store,
|
||||
@@ -649,8 +721,9 @@ export async function resolveApiKeyForProvider(params: {
|
||||
});
|
||||
if (resolved) {
|
||||
const mode = store.profiles[candidate]?.type;
|
||||
const resolvedMode: ResolvedProviderAuth["mode"] =
|
||||
mode === "oauth" ? "oauth" : mode === "token" ? "token" : "api-key";
|
||||
const resolvedMode: ResolvedProviderAuth["mode"] = mode
|
||||
? profileTypeToAuthMode(mode)
|
||||
: "api-key";
|
||||
const result: ResolvedProviderAuth = {
|
||||
apiKey: resolved.apiKey,
|
||||
profileId: candidate,
|
||||
@@ -770,10 +843,10 @@ export function resolveModelAuthMode(
|
||||
const modes = new Set(
|
||||
profiles
|
||||
.map((id) => authStore.profiles[id]?.type)
|
||||
.filter((mode): mode is "api_key" | "oauth" | "token" => Boolean(mode)),
|
||||
.filter((mode): mode is "api_key" | "aws-sdk" | "oauth" | "token" => Boolean(mode)),
|
||||
);
|
||||
const distinct = ["oauth", "token", "api_key"].filter((k) =>
|
||||
modes.has(k as "oauth" | "token" | "api_key"),
|
||||
const distinct = ["oauth", "token", "api_key", "aws-sdk"].filter((k) =>
|
||||
modes.has(k as "oauth" | "token" | "api_key" | "aws-sdk"),
|
||||
);
|
||||
if (distinct.length >= 2) {
|
||||
return "mixed";
|
||||
@@ -787,6 +860,9 @@ export function resolveModelAuthMode(
|
||||
if (modes.has("api_key")) {
|
||||
return "api-key";
|
||||
}
|
||||
if (modes.has("aws-sdk")) {
|
||||
return "aws-sdk";
|
||||
}
|
||||
}
|
||||
|
||||
if (authOverride === undefined && normalizeProviderId(resolved) === "amazon-bedrock") {
|
||||
@@ -855,6 +931,16 @@ export async function hasAvailableAuthForProvider(params: {
|
||||
});
|
||||
for (const candidate of order) {
|
||||
try {
|
||||
if (
|
||||
resolveAwsSdkProfileAuth({
|
||||
cfg,
|
||||
provider,
|
||||
profileId: candidate,
|
||||
credential: store.profiles[candidate],
|
||||
})
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
const resolved = await resolveApiKeyForProfile({
|
||||
cfg,
|
||||
store,
|
||||
|
||||
@@ -39,7 +39,7 @@ export type ProviderAuthResolver = (
|
||||
) => {
|
||||
apiKey: string | undefined;
|
||||
discoveryApiKey?: string;
|
||||
mode: "api_key" | "oauth" | "token" | "none";
|
||||
mode: "api_key" | "aws-sdk" | "oauth" | "token" | "none";
|
||||
source: "env" | "profile" | "none";
|
||||
profileId?: string;
|
||||
};
|
||||
|
||||
@@ -113,6 +113,12 @@ export const resolveAuthLabel = async (
|
||||
source: "",
|
||||
};
|
||||
}
|
||||
if (profile.type === "aws-sdk") {
|
||||
return {
|
||||
label: `${profileId} aws-sdk${more}`,
|
||||
source: "",
|
||||
};
|
||||
}
|
||||
const display = resolveAuthProfileDisplayLabel({ cfg, store, profileId });
|
||||
const label = display === profileId ? profileId : display;
|
||||
const exp = formatExpirationLabel(profile.expires, now, formatUntil);
|
||||
@@ -169,6 +175,10 @@ export const resolveAuthLabel = async (
|
||||
const suffix = formatFlagsSuffix(flags);
|
||||
return `${profileId}=token:${tokenLabel}${suffix}`;
|
||||
}
|
||||
if (profile.type === "aws-sdk") {
|
||||
const suffix = formatFlagsSuffix(flags);
|
||||
return `${profileId}=aws-sdk${suffix}`;
|
||||
}
|
||||
const display = resolveAuthProfileDisplayLabel({
|
||||
cfg,
|
||||
store,
|
||||
|
||||
@@ -98,7 +98,7 @@ vi.mock("../plugins/provider-auth-helpers.js", () => ({
|
||||
params: {
|
||||
profileId: string;
|
||||
provider: string;
|
||||
mode: "api_key" | "oauth" | "token";
|
||||
mode: "api_key" | "aws-sdk" | "oauth" | "token";
|
||||
email?: string;
|
||||
displayName?: string;
|
||||
},
|
||||
|
||||
@@ -51,7 +51,12 @@ function inferLegacyCredentialType(
|
||||
record: Record<string, unknown>,
|
||||
): AuthProfileCredential["type"] | undefined {
|
||||
const explicit = readNonEmptyString(record.type) ?? readNonEmptyString(record.mode);
|
||||
if (explicit === "api_key" || explicit === "token" || explicit === "oauth") {
|
||||
if (
|
||||
explicit === "api_key" ||
|
||||
explicit === "aws-sdk" ||
|
||||
explicit === "token" ||
|
||||
explicit === "oauth"
|
||||
) {
|
||||
return explicit;
|
||||
}
|
||||
if (readNonEmptyString(record.key) ?? readNonEmptyString(record.apiKey)) {
|
||||
|
||||
@@ -8,7 +8,12 @@ import {
|
||||
} from "../shared/string-coerce.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
|
||||
const AUTH_PROFILE_MODES = new Set<AuthProfileConfig["mode"]>(["api_key", "oauth", "token"]);
|
||||
const AUTH_PROFILE_MODES = new Set<AuthProfileConfig["mode"]>([
|
||||
"api_key",
|
||||
"aws-sdk",
|
||||
"oauth",
|
||||
"token",
|
||||
]);
|
||||
|
||||
export type AuthProfileConfigProtectionResult = {
|
||||
config: OpenClawConfig;
|
||||
|
||||
@@ -46,7 +46,9 @@ function formatTimestamp(value: number | undefined): string | undefined {
|
||||
}
|
||||
|
||||
function resolveProfileExpiry(profile: AuthProfileCredential): string | undefined {
|
||||
return profile.type === "api_key" ? undefined : formatTimestamp(profile.expires);
|
||||
return profile.type === "oauth" || profile.type === "token"
|
||||
? formatTimestamp(profile.expires)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function summarizeProfile(params: {
|
||||
|
||||
@@ -47,7 +47,7 @@ vi.mock("../../plugins/provider-auth-helpers.js", () => ({
|
||||
params: {
|
||||
profileId: string;
|
||||
provider: string;
|
||||
mode: "api_key" | "oauth" | "token";
|
||||
mode: "api_key" | "aws-sdk" | "oauth" | "token";
|
||||
email?: string;
|
||||
displayName?: string;
|
||||
},
|
||||
|
||||
@@ -588,10 +588,13 @@ export function resolveRequestedLoginProviderOrThrow(
|
||||
return resolveRequestedProviderOrThrow(providers, rawProvider);
|
||||
}
|
||||
|
||||
function credentialMode(credential: AuthProfileCredential): "api_key" | "oauth" | "token" {
|
||||
function credentialMode(credential: AuthProfileCredential): AuthProfileCredential["type"] {
|
||||
if (credential.type === "api_key") {
|
||||
return "api_key";
|
||||
}
|
||||
if (credential.type === "aws-sdk") {
|
||||
return "aws-sdk";
|
||||
}
|
||||
if (credential.type === "token") {
|
||||
return "token";
|
||||
}
|
||||
|
||||
@@ -110,6 +110,9 @@ export function resolveProviderAuthOverview(params: {
|
||||
profileId,
|
||||
);
|
||||
}
|
||||
if (profile.type === "aws-sdk") {
|
||||
return withUnusableSuffix(`${profileId}=AWS SDK`, profileId);
|
||||
}
|
||||
const display = resolveAuthProfileDisplayLabel({ cfg, store, profileId });
|
||||
const suffix =
|
||||
display === profileId
|
||||
@@ -123,6 +126,7 @@ export function resolveProviderAuthOverview(params: {
|
||||
const oauthCount = profiles.filter((id) => store.profiles[id]?.type === "oauth").length;
|
||||
const tokenCount = profiles.filter((id) => store.profiles[id]?.type === "token").length;
|
||||
const apiKeyCount = profiles.filter((id) => store.profiles[id]?.type === "api_key").length;
|
||||
const awsSdkCount = profiles.filter((id) => store.profiles[id]?.type === "aws-sdk").length;
|
||||
|
||||
const envKey = resolveEnvApiKey(provider, process.env, {
|
||||
config: cfg,
|
||||
@@ -171,6 +175,7 @@ export function resolveProviderAuthOverview(params: {
|
||||
oauth: oauthCount,
|
||||
token: tokenCount,
|
||||
apiKey: apiKeyCount,
|
||||
awsSdk: awsSdkCount,
|
||||
labels,
|
||||
},
|
||||
...(envKey
|
||||
|
||||
@@ -671,7 +671,7 @@ export async function modelsStatusCommand(
|
||||
bits.push(
|
||||
formatKeyValue(
|
||||
"profiles",
|
||||
`${entry.profiles.count} (oauth=${entry.profiles.oauth}, token=${entry.profiles.token}, api_key=${entry.profiles.apiKey})`,
|
||||
`${entry.profiles.count} (oauth=${entry.profiles.oauth}, token=${entry.profiles.token}, api_key=${entry.profiles.apiKey}, aws-sdk=${entry.profiles.awsSdk})`,
|
||||
),
|
||||
);
|
||||
if (entry.profiles.labels.length > 0) {
|
||||
|
||||
@@ -28,6 +28,7 @@ export type ProviderAuthOverview = {
|
||||
oauth: number;
|
||||
token: number;
|
||||
apiKey: number;
|
||||
awsSdk: number;
|
||||
labels: string[];
|
||||
};
|
||||
env?: { value: string; source: string };
|
||||
|
||||
@@ -5,8 +5,9 @@ export type AuthProfileConfig = {
|
||||
* - api_key: static provider API key
|
||||
* - oauth: refreshable OAuth credentials (access+refresh+expires)
|
||||
* - token: static bearer-style token (optionally expiring; no refresh)
|
||||
* - aws-sdk: AWS SDK default credential chain (no secret in auth-profiles.json)
|
||||
*/
|
||||
mode: "api_key" | "oauth" | "token";
|
||||
mode: "api_key" | "aws-sdk" | "oauth" | "token";
|
||||
email?: string;
|
||||
displayName?: string;
|
||||
};
|
||||
|
||||
@@ -560,7 +560,12 @@ export const OpenClawSchema = z
|
||||
z
|
||||
.object({
|
||||
provider: z.string(),
|
||||
mode: z.union([z.literal("api_key"), z.literal("oauth"), z.literal("token")]),
|
||||
mode: z.union([
|
||||
z.literal("api_key"),
|
||||
z.literal("aws-sdk"),
|
||||
z.literal("oauth"),
|
||||
z.literal("token"),
|
||||
]),
|
||||
email: z.string().optional(),
|
||||
displayName: z.string().optional(),
|
||||
})
|
||||
|
||||
@@ -42,7 +42,7 @@ export type ModelAuthExpiry = {
|
||||
|
||||
export type ModelAuthStatusProfile = {
|
||||
profileId: string;
|
||||
type: "oauth" | "token" | "api_key";
|
||||
type: "oauth" | "token" | "api_key" | "aws-sdk";
|
||||
status: AuthProfileHealthStatus;
|
||||
expiry?: ModelAuthExpiry;
|
||||
};
|
||||
|
||||
@@ -337,7 +337,12 @@ function hasAuthProfileCredentialSource(params: {
|
||||
if (
|
||||
dedupeProfileIds(order).some((profileId) => {
|
||||
const cred = store.profiles[profileId];
|
||||
return cred?.type === "api_key" || cred?.type === "oauth" || cred?.type === "token";
|
||||
return (
|
||||
cred?.type === "api_key" ||
|
||||
cred?.type === "aws-sdk" ||
|
||||
cred?.type === "oauth" ||
|
||||
cred?.type === "token"
|
||||
);
|
||||
})
|
||||
) {
|
||||
return true;
|
||||
|
||||
@@ -90,7 +90,7 @@ function runCatalog(
|
||||
) => {
|
||||
apiKey: string | undefined;
|
||||
discoveryApiKey?: string;
|
||||
mode: "api_key" | "oauth" | "token" | "none";
|
||||
mode: "api_key" | "aws-sdk" | "oauth" | "token" | "none";
|
||||
source: "env" | "profile" | "none";
|
||||
profileId?: string;
|
||||
};
|
||||
|
||||
@@ -138,7 +138,7 @@ export function applyAuthProfileConfig(
|
||||
params: {
|
||||
profileId: string;
|
||||
provider: string;
|
||||
mode: "api_key" | "oauth" | "token";
|
||||
mode: "api_key" | "aws-sdk" | "oauth" | "token";
|
||||
email?: string;
|
||||
displayName?: string;
|
||||
preferProfileFirst?: boolean;
|
||||
|
||||
@@ -155,7 +155,7 @@ export function runProviderCatalog(params: {
|
||||
) => {
|
||||
apiKey: string | undefined;
|
||||
discoveryApiKey?: string;
|
||||
mode: "api_key" | "oauth" | "token" | "none";
|
||||
mode: "api_key" | "aws-sdk" | "oauth" | "token" | "none";
|
||||
source: "env" | "profile" | "none";
|
||||
profileId?: string;
|
||||
};
|
||||
|
||||
@@ -441,7 +441,7 @@ export type ProviderCatalogContext = {
|
||||
) => {
|
||||
apiKey: string | undefined;
|
||||
discoveryApiKey?: string;
|
||||
mode: "api_key" | "oauth" | "token" | "none";
|
||||
mode: "api_key" | "aws-sdk" | "oauth" | "token" | "none";
|
||||
source: "env" | "profile" | "none";
|
||||
profileId?: string;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
|
||||
const { prepareSecretsRuntimeSnapshot } = setupSecretsRuntimeSnapshotTestHooks();
|
||||
|
||||
function withAuthProfileMode(mode: "api_key" | "oauth" | "token"): OpenClawConfig {
|
||||
function withAuthProfileMode(mode: "api_key" | "aws-sdk" | "oauth" | "token"): OpenClawConfig {
|
||||
return {
|
||||
auth: {
|
||||
profiles: {
|
||||
|
||||
Reference in New Issue
Block a user