fix(plugins): align provider auth metadata

This commit is contained in:
Vincent Koc
2026-04-23 16:03:42 -07:00
parent 8129ac0f26
commit c0a7b6a510
10 changed files with 185 additions and 3 deletions

View File

@@ -23,6 +23,7 @@ Docs: https://docs.openclaw.ai
- Providers/Google: honor the private-network SSRF opt-in for Gemini image generation requests, so trusted proxy setups that resolve Google API hosts to private addresses can use `image_generate`. Fixes #67216.
- Agents/transport: stop embedded runs from lowering the process-wide undici stream timeouts, so slow Gemini image generation and other long-running provider requests no longer inherit short run-attempt headers timeouts. Fixes #70423. Thanks @giangthb.
- Providers/OpenRouter: send image-understanding prompts as user text before image parts, restoring non-empty vision responses for OpenRouter multimodal models. Fixes #70410.
- Plugins/providers: mirror runtime auth choices in bundled provider manifests and detect `KIMI_API_KEY` for Moonshot/Kimi web search before plugin runtime loads. Thanks @vincentkoc.
- Memory/QMD: recreate stale managed QMD collections when startup repair finds the collection name already exists, so root memory narrows back to `MEMORY.md` instead of staying on broad workspace markdown indexing.
- Agents/OpenAI: surface selected-model capacity failures from PI, Codex, and auto-reply harness paths with a model-switch hint instead of the generic empty-response error. Thanks @vincentkoc.
- Providers/OpenAI: route `openai/gpt-image-2` through configured Codex OAuth directly when an `openai-codex` profile is active, instead of probing `OPENAI_API_KEY` first.

View File

@@ -23,6 +23,17 @@
"groupLabel": "Anthropic",
"groupHint": "Claude CLI + API key"
},
{
"provider": "anthropic",
"method": "setup-token",
"choiceId": "setup-token",
"choiceLabel": "Anthropic setup-token",
"choiceHint": "Manual token path",
"assistantPriority": 40,
"groupId": "anthropic",
"groupLabel": "Anthropic",
"groupHint": "Claude CLI + API key + token"
},
{
"provider": "anthropic",
"method": "api-key",

View File

@@ -0,0 +1,41 @@
import fs from "node:fs";
import { describe, expect, it } from "vitest";
import { resolveProviderPluginChoice } from "../../src/plugins/provider-auth-choice.runtime.js";
import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js";
import plugin from "./index.js";
type ComfyManifest = {
providerAuthChoices?: Array<{ choiceId?: string; method?: string; provider?: string }>;
};
function readManifest(): ComfyManifest {
return JSON.parse(
fs.readFileSync(new URL("./openclaw.plugin.json", import.meta.url), "utf8"),
) as ComfyManifest;
}
describe("comfy provider plugin", () => {
it("registers cloud API-key auth metadata", async () => {
const provider = await registerSingleProviderPlugin(plugin);
expect(provider.id).toBe("comfy");
expect(provider.envVars).toEqual(["COMFY_API_KEY", "COMFY_CLOUD_API_KEY"]);
expect(provider.auth?.map((method) => method.id)).toEqual(["cloud-api-key"]);
const choice = resolveProviderPluginChoice({
providers: [provider],
choice: "comfy-cloud-api-key",
});
expect(choice?.provider.id).toBe("comfy");
expect(choice?.method.id).toBe("cloud-api-key");
expect(readManifest().providerAuthChoices).toEqual(
expect.arrayContaining([
expect.objectContaining({
provider: "comfy",
method: "cloud-api-key",
choiceId: "comfy-cloud-api-key",
}),
]),
);
});
});

View File

@@ -1,4 +1,5 @@
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { buildComfyImageGenerationProvider } from "./image-generation-provider.js";
import { buildComfyMusicGenerationProvider } from "./music-generation-provider.js";
import { buildComfyVideoGenerationProvider } from "./video-generation-provider.js";
@@ -15,7 +16,27 @@ export default definePluginEntry({
label: "ComfyUI",
docsPath: "/providers/comfy",
envVars: ["COMFY_API_KEY", "COMFY_CLOUD_API_KEY"],
auth: [],
auth: [
createProviderApiKeyAuthMethod({
providerId: PROVIDER_ID,
methodId: "cloud-api-key",
label: "Comfy Cloud API key",
hint: "API key for Comfy Cloud workflow runs",
optionKey: "comfyApiKey",
flagName: "--comfy-api-key",
envVar: "COMFY_API_KEY",
promptMessage: "Enter Comfy Cloud API key",
wizard: {
choiceId: "comfy-cloud-api-key",
choiceLabel: "Comfy Cloud API key",
choiceHint: "Required for cloud workflows",
groupId: "comfy",
groupLabel: "ComfyUI",
groupHint: "Local or cloud workflows",
onboardingScopes: ["image-generation"],
},
}),
],
});
api.registerImageGenerationProvider(buildComfyImageGenerationProvider());
api.registerMusicGenerationProvider(buildComfyMusicGenerationProvider());

View File

@@ -5,6 +5,23 @@
"providerAuthEnvVars": {
"comfy": ["COMFY_API_KEY", "COMFY_CLOUD_API_KEY"]
},
"providerAuthChoices": [
{
"provider": "comfy",
"method": "cloud-api-key",
"choiceId": "comfy-cloud-api-key",
"choiceLabel": "Comfy Cloud API key",
"choiceHint": "Required for cloud workflows",
"groupId": "comfy",
"groupLabel": "ComfyUI",
"groupHint": "Local or cloud workflows",
"optionKey": "comfyApiKey",
"cliFlag": "--comfy-api-key",
"cliOption": "--comfy-api-key <key>",
"cliDescription": "Comfy Cloud API key",
"onboardingScopes": ["image-generation"]
}
],
"contracts": {
"imageGenerationProviders": ["comfy"],
"musicGenerationProviders": ["comfy"],

View File

@@ -1,10 +1,28 @@
import fs from "node:fs";
import type { Context, Model } from "@mariozechner/pi-ai";
import { describe, expect, it } from "vitest";
import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js";
import { createCapturedThinkingConfigStream } from "../../test/helpers/plugins/stream-hooks.js";
import plugin from "./index.js";
import { createKimiWebSearchProvider } from "./src/kimi-web-search-provider.js";
type MoonshotManifest = {
providerAuthEnvVars?: Record<string, string[]>;
};
function readManifest(): MoonshotManifest {
return JSON.parse(
fs.readFileSync(new URL("./openclaw.plugin.json", import.meta.url), "utf8"),
) as MoonshotManifest;
}
describe("moonshot provider plugin", () => {
it("mirrors Kimi web-search env credentials in manifest metadata", () => {
const manifestEnvVars = readManifest().providerAuthEnvVars?.moonshot ?? [];
expect(manifestEnvVars).toEqual(expect.arrayContaining(createKimiWebSearchProvider().envVars));
});
it("owns replay policy for OpenAI-compatible Moonshot transports without mangling native Kimi tool_call IDs", async () => {
const provider = await registerSingleProviderPlugin(plugin);

View File

@@ -3,7 +3,7 @@
"enabledByDefault": true,
"providers": ["moonshot"],
"providerAuthEnvVars": {
"moonshot": ["MOONSHOT_API_KEY"]
"moonshot": ["MOONSHOT_API_KEY", "KIMI_API_KEY"]
},
"providerAuthChoices": [
{

View File

@@ -0,0 +1,41 @@
import fs from "node:fs";
import { describe, expect, it } from "vitest";
import { resolveProviderPluginChoice } from "../../src/plugins/provider-auth-choice.runtime.js";
import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js";
import plugin from "./index.js";
type NvidiaManifest = {
providerAuthChoices?: Array<{ choiceId?: string; method?: string; provider?: string }>;
};
function readManifest(): NvidiaManifest {
return JSON.parse(
fs.readFileSync(new URL("./openclaw.plugin.json", import.meta.url), "utf8"),
) as NvidiaManifest;
}
describe("nvidia provider plugin", () => {
it("registers API-key auth metadata", async () => {
const provider = await registerSingleProviderPlugin(plugin);
expect(provider.id).toBe("nvidia");
expect(provider.envVars).toEqual(["NVIDIA_API_KEY"]);
expect(provider.auth?.map((method) => method.id)).toEqual(["api-key"]);
const choice = resolveProviderPluginChoice({
providers: [provider],
choice: "nvidia-api-key",
});
expect(choice?.provider.id).toBe("nvidia");
expect(choice?.method.id).toBe("api-key");
expect(readManifest().providerAuthChoices).toEqual(
expect.arrayContaining([
expect.objectContaining({
provider: "nvidia",
method: "api-key",
choiceId: "nvidia-api-key",
}),
]),
);
});
});

View File

@@ -11,7 +11,24 @@ export default defineSingleProviderPluginEntry({
label: "NVIDIA",
docsPath: "/providers/nvidia",
envVars: ["NVIDIA_API_KEY"],
auth: [],
auth: [
{
methodId: "api-key",
label: "NVIDIA API key",
hint: "API key",
optionKey: "nvidiaApiKey",
flagName: "--nvidia-api-key",
envVar: "NVIDIA_API_KEY",
promptMessage: "Enter NVIDIA API key",
wizard: {
choiceId: "nvidia-api-key",
choiceLabel: "NVIDIA API key",
groupId: "nvidia",
groupLabel: "NVIDIA",
groupHint: "API key",
},
},
],
catalog: {
buildProvider: buildNvidiaProvider,
},

View File

@@ -5,6 +5,21 @@
"providerAuthEnvVars": {
"nvidia": ["NVIDIA_API_KEY"]
},
"providerAuthChoices": [
{
"provider": "nvidia",
"method": "api-key",
"choiceId": "nvidia-api-key",
"choiceLabel": "NVIDIA API key",
"groupId": "nvidia",
"groupLabel": "NVIDIA",
"groupHint": "API key",
"optionKey": "nvidiaApiKey",
"cliFlag": "--nvidia-api-key",
"cliOption": "--nvidia-api-key <key>",
"cliDescription": "NVIDIA API key"
}
],
"configSchema": {
"type": "object",
"additionalProperties": false,