mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
fix: align manifest media availability with runtime
This commit is contained in:
@@ -393,6 +393,140 @@ describe("optional media tool factory planning", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not expose manifest-backed generation providers when plugins are globally disabled", () => {
|
||||
const config: OpenClawConfig = {
|
||||
plugins: {
|
||||
enabled: false,
|
||||
entries: {
|
||||
comfy: {
|
||||
config: {
|
||||
mode: "local",
|
||||
workflow: { "1": { inputs: {} } },
|
||||
promptNodeId: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const configSignals = [
|
||||
{
|
||||
rootPath: "plugins.entries.comfy.config",
|
||||
mode: {
|
||||
path: "mode",
|
||||
default: "local",
|
||||
allowed: ["local"],
|
||||
},
|
||||
requiredAny: ["workflow", "workflowPath"],
|
||||
required: ["promptNodeId"],
|
||||
},
|
||||
];
|
||||
installSnapshot(config, [
|
||||
createPlugin({
|
||||
id: "comfy",
|
||||
contracts: {
|
||||
imageGenerationProviders: ["comfy"],
|
||||
videoGenerationProviders: ["comfy"],
|
||||
musicGenerationProviders: ["comfy"],
|
||||
},
|
||||
imageGenerationProviderMetadata: {
|
||||
comfy: { configSignals },
|
||||
},
|
||||
videoGenerationProviderMetadata: {
|
||||
comfy: { configSignals },
|
||||
},
|
||||
musicGenerationProviderMetadata: {
|
||||
comfy: { configSignals },
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
expect(
|
||||
__testing.resolveOptionalMediaToolFactoryPlan({
|
||||
config,
|
||||
authStore: createAuthStore(),
|
||||
}),
|
||||
).toEqual({
|
||||
imageGenerate: false,
|
||||
videoGenerate: false,
|
||||
musicGenerate: false,
|
||||
pdf: false,
|
||||
});
|
||||
expect(
|
||||
createOpenClawTools({
|
||||
config,
|
||||
authProfileStore: createAuthStore(),
|
||||
pluginToolAllowlist: ["image_generate", "video_generate", "music_generate"],
|
||||
}).map((tool) => tool.name),
|
||||
).not.toEqual(expect.arrayContaining(["image_generate", "video_generate", "music_generate"]));
|
||||
});
|
||||
|
||||
it("does not count unresolved SecretRef config signals as configured", () => {
|
||||
vi.stubEnv("COMFY_TEST_API_KEY", "");
|
||||
const config: OpenClawConfig = {
|
||||
plugins: {
|
||||
entries: {
|
||||
comfy: {
|
||||
config: {
|
||||
mode: "cloud",
|
||||
apiKey: { source: "env", provider: "default", id: "COMFY_TEST_API_KEY" },
|
||||
workflow: { "1": { inputs: {} } },
|
||||
promptNodeId: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const configSignals = [
|
||||
{
|
||||
rootPath: "plugins.entries.comfy.config",
|
||||
mode: {
|
||||
path: "mode",
|
||||
allowed: ["cloud"],
|
||||
},
|
||||
requiredAny: ["workflow", "workflowPath"],
|
||||
required: ["promptNodeId", "apiKey"],
|
||||
},
|
||||
];
|
||||
installSnapshot(config, [
|
||||
createPlugin({
|
||||
id: "comfy",
|
||||
contracts: {
|
||||
imageGenerationProviders: ["comfy"],
|
||||
videoGenerationProviders: ["comfy"],
|
||||
musicGenerationProviders: ["comfy"],
|
||||
},
|
||||
imageGenerationProviderMetadata: {
|
||||
comfy: { configSignals },
|
||||
},
|
||||
videoGenerationProviderMetadata: {
|
||||
comfy: { configSignals },
|
||||
},
|
||||
musicGenerationProviderMetadata: {
|
||||
comfy: { configSignals },
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
expect(
|
||||
__testing.resolveOptionalMediaToolFactoryPlan({
|
||||
config,
|
||||
authStore: createAuthStore(),
|
||||
}),
|
||||
).toEqual({
|
||||
imageGenerate: false,
|
||||
videoGenerate: false,
|
||||
musicGenerate: false,
|
||||
pdf: false,
|
||||
});
|
||||
expect(
|
||||
createOpenClawTools({
|
||||
config,
|
||||
authProfileStore: createAuthStore(),
|
||||
pluginToolAllowlist: ["image_generate", "video_generate", "music_generate"],
|
||||
}).map((tool) => tool.name),
|
||||
).not.toEqual(expect.arrayContaining(["image_generate", "video_generate", "music_generate"]));
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: "legacy local provider config",
|
||||
|
||||
@@ -147,6 +147,14 @@ function resolveOptionalMediaToolFactoryPlan(params: {
|
||||
musicGenerate: allowMusicGenerate,
|
||||
pdf: allowPdf,
|
||||
};
|
||||
if (params.config?.plugins?.enabled === false) {
|
||||
return {
|
||||
imageGenerate: false,
|
||||
videoGenerate: false,
|
||||
musicGenerate: false,
|
||||
pdf: false,
|
||||
};
|
||||
}
|
||||
if (!params.authStore) {
|
||||
return fallbackPlan;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { coerceSecretRef, type SecretRef } from "../../config/types.secrets.js";
|
||||
import { getCurrentPluginMetadataSnapshot } from "../../plugins/current-plugin-metadata-snapshot.js";
|
||||
import { isManifestPluginAvailableForControlPlane } from "../../plugins/manifest-contract-eligibility.js";
|
||||
import type { PluginManifestRecord } from "../../plugins/manifest-registry.js";
|
||||
import type { PluginMetadataSnapshot } from "../../plugins/plugin-metadata-snapshot.types.js";
|
||||
import { resolveDefaultSecretProviderAlias } from "../../secrets/ref-contract.js";
|
||||
import { listProfilesForProvider } from "../auth-profiles.js";
|
||||
import type { AuthProfileStore } from "../auth-profiles/types.js";
|
||||
|
||||
@@ -57,17 +59,44 @@ function readEffectiveConfig(params: {
|
||||
return isRecord(overlay) ? { ...root, ...overlay } : root;
|
||||
}
|
||||
|
||||
function hasConfiguredValue(value: unknown): boolean {
|
||||
if (typeof value === "string") {
|
||||
return value.trim().length > 0;
|
||||
function canResolveEnvSecretRefInConfigPath(params: {
|
||||
config?: OpenClawConfig;
|
||||
ref: SecretRef;
|
||||
}): boolean {
|
||||
if (params.ref.source !== "env") {
|
||||
return false;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.length > 0;
|
||||
const providerConfig = params.config?.secrets?.providers?.[params.ref.provider];
|
||||
if (!providerConfig) {
|
||||
return params.ref.provider === resolveDefaultSecretProviderAlias(params.config ?? {}, "env");
|
||||
}
|
||||
if (isRecord(value)) {
|
||||
return Object.keys(value).length > 0;
|
||||
if (providerConfig.source !== "env") {
|
||||
return false;
|
||||
}
|
||||
return value !== undefined && value !== null;
|
||||
const allowlist = providerConfig.allowlist;
|
||||
return !allowlist || allowlist.includes(params.ref.id);
|
||||
}
|
||||
|
||||
function hasConfiguredValue(params: { config?: OpenClawConfig; value: unknown }): boolean {
|
||||
const secretRef = coerceSecretRef(params.value, params.config?.secrets?.defaults);
|
||||
if (secretRef) {
|
||||
return (
|
||||
canResolveEnvSecretRefInConfigPath({
|
||||
config: params.config,
|
||||
ref: secretRef,
|
||||
}) && Boolean(process.env[secretRef.id]?.trim())
|
||||
);
|
||||
}
|
||||
if (typeof params.value === "string") {
|
||||
return params.value.trim().length > 0;
|
||||
}
|
||||
if (Array.isArray(params.value)) {
|
||||
return params.value.length > 0;
|
||||
}
|
||||
if (isRecord(params.value)) {
|
||||
return Object.keys(params.value).length > 0;
|
||||
}
|
||||
return params.value !== undefined && params.value !== null;
|
||||
}
|
||||
|
||||
function configSignalPasses(params: {
|
||||
@@ -99,14 +128,24 @@ function configSignalPasses(params: {
|
||||
}
|
||||
}
|
||||
for (const requiredPath of params.signal.required ?? []) {
|
||||
if (!hasConfiguredValue(readPath(effectiveConfig, requiredPath))) {
|
||||
if (
|
||||
!hasConfiguredValue({
|
||||
config: params.config,
|
||||
value: readPath(effectiveConfig, requiredPath),
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const requiredAny = params.signal.requiredAny ?? [];
|
||||
if (
|
||||
requiredAny.length > 0 &&
|
||||
!requiredAny.some((path) => hasConfiguredValue(readPath(effectiveConfig, path)))
|
||||
!requiredAny.some((path) =>
|
||||
hasConfiguredValue({
|
||||
config: params.config,
|
||||
value: readPath(effectiveConfig, path),
|
||||
}),
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -210,6 +249,9 @@ export function hasSnapshotCapabilityAvailability(params: {
|
||||
config?: OpenClawConfig;
|
||||
authStore?: AuthProfileStore;
|
||||
}): boolean {
|
||||
if (params.config?.plugins?.enabled === false) {
|
||||
return false;
|
||||
}
|
||||
for (const plugin of params.snapshot.plugins) {
|
||||
if (
|
||||
!isManifestPluginAvailableForControlPlane({
|
||||
@@ -266,6 +308,9 @@ export function hasSnapshotProviderEnvAvailability(params: {
|
||||
providerId: string;
|
||||
config?: OpenClawConfig;
|
||||
}): boolean {
|
||||
if (params.config?.plugins?.enabled === false) {
|
||||
return false;
|
||||
}
|
||||
for (const plugin of params.snapshot.plugins) {
|
||||
if (
|
||||
!isManifestPluginAvailableForControlPlane({
|
||||
|
||||
@@ -307,6 +307,9 @@ export function hasGenerationToolAvailability(params: {
|
||||
providers?: CapabilityProvider[] | (() => CapabilityProvider[]);
|
||||
providerKey: GenerationCapabilityProviderKey;
|
||||
}): boolean {
|
||||
if (params.cfg?.plugins?.enabled === false) {
|
||||
return false;
|
||||
}
|
||||
if (hasToolModelConfig(coerceToolModelConfig(params.modelConfig))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user