Files
openclaw/src/plugins/inspect-shape.ts
2026-05-02 07:29:15 +01:00

131 lines
4.1 KiB
TypeScript

import type { PluginRegistry } from "./registry.js";
import { hasKind } from "./slots.js";
export type PluginCapabilityKind =
| "cli-backend"
| "text-inference"
| "speech"
| "realtime-transcription"
| "realtime-voice"
| "media-understanding"
| "image-generation"
| "web-search"
| "agent-harness"
| "context-engine"
| "channel";
export type PluginInspectShape =
| "hook-only"
| "plain-capability"
| "hybrid-capability"
| "non-capability";
export type PluginCapabilityEntry = {
kind: PluginCapabilityKind;
ids: string[];
};
export type PluginShapeSummary = {
shape: PluginInspectShape;
capabilityMode: "none" | "plain" | "hybrid";
capabilityCount: number;
capabilities: PluginCapabilityEntry[];
usesLegacyBeforeAgentStart: boolean;
};
function buildPluginCapabilityEntries(
plugin: PluginRegistry["plugins"][number],
): PluginCapabilityEntry[] {
return [
{ kind: "cli-backend" as const, ids: plugin.cliBackendIds ?? [] },
{ kind: "text-inference" as const, ids: plugin.providerIds },
{ kind: "speech" as const, ids: plugin.speechProviderIds },
{ kind: "realtime-transcription" as const, ids: plugin.realtimeTranscriptionProviderIds },
{ kind: "realtime-voice" as const, ids: plugin.realtimeVoiceProviderIds },
{ kind: "media-understanding" as const, ids: plugin.mediaUnderstandingProviderIds },
{ kind: "image-generation" as const, ids: plugin.imageGenerationProviderIds },
{ kind: "web-search" as const, ids: plugin.webSearchProviderIds },
{ kind: "agent-harness" as const, ids: plugin.agentHarnessIds },
{
kind: "context-engine" as const,
ids:
plugin.status === "loaded" && hasKind(plugin.kind, "context-engine")
? (plugin.contextEngineIds ?? [])
: [],
},
{ kind: "channel" as const, ids: plugin.channelIds },
].filter((entry) => entry.ids.length > 0);
}
function derivePluginInspectShape(params: {
capabilityCount: number;
typedHookCount: number;
customHookCount: number;
toolCount: number;
commandCount: number;
cliCount: number;
serviceCount: number;
gatewayDiscoveryServiceCount: number;
gatewayMethodCount: number;
httpRouteCount: number;
}): PluginInspectShape {
if (params.capabilityCount > 1) {
return "hybrid-capability";
}
if (params.capabilityCount === 1) {
return "plain-capability";
}
const hasOnlyHooks =
params.typedHookCount + params.customHookCount > 0 &&
params.toolCount === 0 &&
params.commandCount === 0 &&
params.cliCount === 0 &&
params.serviceCount === 0 &&
params.gatewayDiscoveryServiceCount === 0 &&
params.gatewayMethodCount === 0 &&
params.httpRouteCount === 0;
if (hasOnlyHooks) {
return "hook-only";
}
return "non-capability";
}
export function buildPluginShapeSummary(params: {
plugin: PluginRegistry["plugins"][number];
report: Pick<PluginRegistry, "hooks" | "typedHooks" | "tools">;
}): PluginShapeSummary {
const capabilities = buildPluginCapabilityEntries(params.plugin);
const typedHookCount = params.report.typedHooks.filter(
(entry) => entry.pluginId === params.plugin.id,
).length;
const customHookCount = params.report.hooks.filter(
(entry) => entry.pluginId === params.plugin.id,
).length;
const toolCount = params.report.tools.filter(
(entry) => entry.pluginId === params.plugin.id,
).length;
const capabilityCount = capabilities.length;
const shape = derivePluginInspectShape({
capabilityCount,
typedHookCount,
customHookCount,
toolCount,
commandCount: params.plugin.commands.length,
cliCount: params.plugin.cliCommands.length,
serviceCount: params.plugin.services.length,
gatewayDiscoveryServiceCount: params.plugin.gatewayDiscoveryServiceIds.length,
gatewayMethodCount: params.plugin.gatewayMethods.length,
httpRouteCount: params.plugin.httpRoutes,
});
return {
shape,
capabilityMode: capabilityCount === 0 ? "none" : capabilityCount === 1 ? "plain" : "hybrid",
capabilityCount,
capabilities,
usesLegacyBeforeAgentStart: params.report.typedHooks.some(
(entry) => entry.pluginId === params.plugin.id && entry.hookName === "before_agent_start",
),
};
}