mirror of https://github.com/openclaw/openclaw.git
xAI: honor config-backed auth during provider bootstrap
This commit is contained in:
parent
2d919cf63d
commit
d5fafbe3ce
|
|
@ -137,6 +137,32 @@ describe("xai web search config resolution", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("reuses the legacy grok web search api key for provider auth fallback", () => {
|
||||
const captured = capturePluginRegistration(xaiPlugin);
|
||||
const provider = captured.providers[0];
|
||||
expect(
|
||||
provider?.resolveSyntheticAuth?.({
|
||||
config: {
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
grok: {
|
||||
apiKey: "xai-legacy-fallback", // pragma: allowlist secret
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
provider: "xai",
|
||||
providerConfig: undefined,
|
||||
}),
|
||||
).toEqual({
|
||||
apiKey: "xai-legacy-fallback",
|
||||
source: "plugins.entries.xai.config.webSearch.apiKey",
|
||||
mode: "api-key",
|
||||
});
|
||||
});
|
||||
|
||||
it("uses default model when not specified", () => {
|
||||
expect(resolveXaiWebSearchModel({})).toBe("grok-4-1-fast");
|
||||
expect(resolveXaiWebSearchModel(undefined)).toBe("grok-4-1-fast");
|
||||
|
|
|
|||
|
|
@ -113,4 +113,50 @@ describe("provider discovery auth marker guardrails", () => {
|
|||
const request = vllmCall?.[1] as { headers?: Record<string, string> } | undefined;
|
||||
expect(request?.headers?.Authorization).toBe("Bearer ALLCAPS_SAMPLE");
|
||||
});
|
||||
|
||||
it("surfaces xai provider auth from plugin web search config without persisting plaintext", async () => {
|
||||
const agentDir = await createAgentDirWithAuthProfiles({});
|
||||
|
||||
const providers = await resolveImplicitProvidersForTest({
|
||||
agentDir,
|
||||
env: {},
|
||||
config: {
|
||||
plugins: {
|
||||
entries: {
|
||||
xai: {
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: "xai-plugin-config-key", // pragma: allowlist secret
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(providers?.xai?.apiKey).toBe(NON_ENV_SECRETREF_MARKER);
|
||||
});
|
||||
|
||||
it("surfaces xai provider auth from legacy grok web search config without persisting plaintext", async () => {
|
||||
const agentDir = await createAgentDirWithAuthProfiles({});
|
||||
|
||||
const providers = await resolveImplicitProvidersForTest({
|
||||
agentDir,
|
||||
env: {},
|
||||
config: {
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
grok: {
|
||||
apiKey: "xai-legacy-config-key", // pragma: allowlist secret
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(providers?.xai?.apiKey).toBe(NON_ENV_SECRETREF_MARKER);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -305,8 +305,8 @@ export async function resolveImplicitProviders(
|
|||
...params,
|
||||
authStore,
|
||||
env,
|
||||
resolveProviderApiKey: createProviderApiKeyResolver(env, authStore),
|
||||
resolveProviderAuth: createProviderAuthResolver(env, authStore),
|
||||
resolveProviderApiKey: createProviderApiKeyResolver(env, authStore, params.config),
|
||||
resolveProviderAuth: createProviderAuthResolver(env, authStore, params.config),
|
||||
};
|
||||
|
||||
for (const order of PLUGIN_DISCOVERY_ORDERS) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { coerceSecretRef, resolveSecretInputRef } from "../config/types.secrets.js";
|
||||
import { resolveProviderSyntheticAuthWithPlugin } from "../plugins/provider-runtime.js";
|
||||
import { normalizeOptionalSecretInput } from "../utils/normalize-secret-input.js";
|
||||
import { listProfilesForProvider } from "./auth-profiles/profiles.js";
|
||||
import { ensureAuthProfileStore } from "./auth-profiles/store.js";
|
||||
|
|
@ -304,6 +305,7 @@ export function resolveMissingProviderApiKey(params: {
|
|||
export function createProviderApiKeyResolver(
|
||||
env: NodeJS.ProcessEnv,
|
||||
authStore: ReturnType<typeof ensureAuthProfileStore>,
|
||||
config?: OpenClawConfig,
|
||||
): ProviderApiKeyResolver {
|
||||
return (provider: string): { apiKey: string | undefined; discoveryApiKey?: string } => {
|
||||
const envVar = resolveEnvApiKeyVarName(provider, env);
|
||||
|
|
@ -314,9 +316,19 @@ export function createProviderApiKeyResolver(
|
|||
};
|
||||
}
|
||||
const fromProfiles = resolveApiKeyFromProfiles({ provider, store: authStore, env });
|
||||
if (fromProfiles?.apiKey) {
|
||||
return {
|
||||
apiKey: fromProfiles.apiKey,
|
||||
discoveryApiKey: fromProfiles.discoveryApiKey,
|
||||
};
|
||||
}
|
||||
const fromConfig = resolveConfigBackedProviderAuth({
|
||||
provider,
|
||||
config,
|
||||
});
|
||||
return {
|
||||
apiKey: fromProfiles?.apiKey,
|
||||
discoveryApiKey: fromProfiles?.discoveryApiKey,
|
||||
apiKey: fromConfig?.apiKey,
|
||||
discoveryApiKey: fromConfig?.discoveryApiKey,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -324,6 +336,7 @@ export function createProviderApiKeyResolver(
|
|||
export function createProviderAuthResolver(
|
||||
env: NodeJS.ProcessEnv,
|
||||
authStore: ReturnType<typeof ensureAuthProfileStore>,
|
||||
config?: OpenClawConfig,
|
||||
): ProviderAuthResolver {
|
||||
return (provider: string, options?: { oauthMarker?: string }) => {
|
||||
const ids = listProfilesForProvider(authStore, provider);
|
||||
|
|
@ -377,6 +390,19 @@ export function createProviderAuthResolver(
|
|||
};
|
||||
}
|
||||
|
||||
const fromConfig = resolveConfigBackedProviderAuth({
|
||||
provider,
|
||||
config,
|
||||
});
|
||||
if (fromConfig) {
|
||||
return {
|
||||
apiKey: fromConfig.apiKey,
|
||||
discoveryApiKey: fromConfig.discoveryApiKey,
|
||||
mode: fromConfig.mode,
|
||||
source: "none",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
apiKey: undefined,
|
||||
discoveryApiKey: undefined,
|
||||
|
|
@ -385,3 +411,39 @@ export function createProviderAuthResolver(
|
|||
};
|
||||
};
|
||||
}
|
||||
|
||||
function resolveConfigBackedProviderAuth(params: { provider: string; config?: OpenClawConfig }):
|
||||
| {
|
||||
apiKey: string;
|
||||
discoveryApiKey?: string;
|
||||
mode: "api_key";
|
||||
source: "config";
|
||||
}
|
||||
| undefined {
|
||||
const synthetic = resolveProviderSyntheticAuthWithPlugin({
|
||||
provider: params.provider,
|
||||
config: params.config,
|
||||
context: {
|
||||
config: params.config,
|
||||
provider: params.provider,
|
||||
providerConfig: params.config?.models?.providers?.[params.provider],
|
||||
},
|
||||
});
|
||||
const apiKey = synthetic?.apiKey?.trim();
|
||||
if (!apiKey) {
|
||||
return undefined;
|
||||
}
|
||||
return isNonSecretApiKeyMarker(apiKey)
|
||||
? {
|
||||
apiKey,
|
||||
discoveryApiKey: toDiscoveryApiKey(apiKey),
|
||||
mode: "api_key",
|
||||
source: "config",
|
||||
}
|
||||
: {
|
||||
apiKey: resolveNonEnvSecretRefApiKeyMarker("file"),
|
||||
discoveryApiKey: toDiscoveryApiKey(apiKey),
|
||||
mode: "api_key",
|
||||
source: "config",
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue