fix(plugins): discover setup provider env vars (#81542)

Discover provider plugins from setup.providers[].envVars credentials during provider discovery while keeping the deprecated providerAuthEnvVars fallback.

Co-authored-by: JARVIS-Glasses <whatsskilll@gmail.com>
This commit is contained in:
WhatsSkiLL
2026-05-14 07:58:05 +02:00
committed by GitHub
parent 9518d12e13
commit eefa6ecea0
3 changed files with 41 additions and 6 deletions

View File

@@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Plugins: discover provider plugins from `setup.providers[].envVars` credentials during provider discovery while keeping the deprecated `providerAuthEnvVars` fallback. (#81542) Thanks @JARVIS-Glasses.
- Docs/Codex harness: clarify that per-agent `CODEX_HOME` isolates `~/.codex` while inherited `HOME` intentionally keeps `.agents` discovery and subprocess user-home state available.
- CLI tables: preserve muted/color styling on wrapped continuation lines after multiline cells, keeping `openclaw plugins list` descriptions readable.
- iOS: restore first-use Contacts, Calendar, and Reminders permission prompts and add Privacy & Access status/actions in Settings. Thanks @BunsDev.

View File

@@ -51,12 +51,14 @@ function createManifestPlugin(id: string): PluginManifestRecord {
function createManifestPluginWithoutDiscovery(params: {
id: string;
providerAuthEnvVars?: Record<string, string[]>;
setupProviders?: NonNullable<PluginManifestRecord["setup"]>["providers"];
}): PluginManifestRecord {
const { providerDiscoverySource: _providerDiscoverySource, ...plugin } = createManifestPlugin(
params.id,
);
return {
...plugin,
...(params.setupProviders ? { setup: { providers: params.setupProviders } } : {}),
...(params.providerAuthEnvVars ? { providerAuthEnvVars: params.providerAuthEnvVars } : {}),
};
}
@@ -184,6 +186,36 @@ describe("resolvePluginDiscoveryProvidersRuntime", () => {
expect(params.onlyPluginIds).toEqual(["deepseek", "kilocode"]);
});
it("falls back to full provider plugins when setup provider env vars are configured", () => {
const codexEntryProvider = createProvider({ id: "codex", mode: "catalog" });
const fullProviders = [createProvider({ id: "kilocode", mode: "catalog" })];
mocks.resolveDiscoveredProviderPluginIds.mockReturnValue(["codex", "kilocode"]);
mocks.loadPluginMetadataSnapshot.mockReturnValue({
index: { plugins: [] },
manifestRegistry: {
plugins: [
createManifestPlugin("codex"),
createManifestPluginWithoutDiscovery({
id: "kilocode",
setupProviders: [{ id: "kilocode", envVars: ["KILOCODE_API_KEY"] }],
}),
],
diagnostics: [],
},
});
mocks.loadSource.mockReturnValue(codexEntryProvider);
mocks.resolvePluginProviders.mockReturnValue(fullProviders);
expect(
resolvePluginDiscoveryProvidersRuntime({
env: { KILOCODE_API_KEY: "sk-test" } as NodeJS.ProcessEnv,
}),
).toEqual([{ ...codexEntryProvider, pluginId: "codex" }, ...fullProviders]);
expect(mocks.resolvePluginProviders).toHaveBeenCalledTimes(1);
const params = requireResolvePluginProvidersParams();
expect(params.onlyPluginIds).toEqual(["kilocode"]);
});
it("shares one metadata snapshot between provider id discovery and entry loading", () => {
const registry = { plugins: [] };
const manifestRegistry = {

View File

@@ -56,12 +56,14 @@ function hasProviderAuthEnvCredential(
plugin: PluginManifestRecord,
env: NodeJS.ProcessEnv,
): boolean {
return Object.values(plugin.providerAuthEnvVars ?? {}).some((envVars) =>
envVars.some((name) => {
const value = env[name]?.trim();
return value !== undefined && value !== "";
}),
);
const envVars = [
...(plugin.setup?.providers ?? []).flatMap((provider) => provider.envVars ?? []),
...Object.values(plugin.providerAuthEnvVars ?? {}).flat(),
];
return envVars.some((name) => {
const value = env[name]?.trim();
return value !== undefined && value !== "";
});
}
function dedupeSorted(values: Iterable<string>): string[] {