mirror of https://github.com/openclaw/openclaw.git
Plugins: add host-owned tool and provider storage
This commit is contained in:
parent
d34a5aa870
commit
2be54e9861
|
|
@ -12,6 +12,7 @@ import {
|
|||
resolveExtensionCommandRegistration,
|
||||
resolveExtensionProviderRegistration,
|
||||
} from "./runtime-registrations.js";
|
||||
import { listExtensionHostProviderRegistrations } from "./runtime-registry.js";
|
||||
|
||||
export function pushExtensionHostRegistryDiagnostic(params: {
|
||||
registry: PluginRegistry;
|
||||
|
|
@ -54,7 +55,7 @@ export function resolveExtensionHostProviderCompatibility(params: {
|
|||
}
|
||||
|
||||
const result = resolveExtensionProviderRegistration({
|
||||
existing: params.registry.providers,
|
||||
existing: [...listExtensionHostProviderRegistrations(params.registry)],
|
||||
ownerPluginId: params.record.id,
|
||||
ownerSource: params.record.source,
|
||||
provider: normalizedProvider,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
||||
import { resolveExtensionHostProviders } from "./provider-runtime.js";
|
||||
import { addExtensionHostProviderRegistration } from "./runtime-registry.js";
|
||||
|
||||
describe("resolveExtensionHostProviders", () => {
|
||||
it("projects provider registrations into provider plugins with plugin ids", () => {
|
||||
const registry = createEmptyPluginRegistry();
|
||||
registry.providers.push({
|
||||
addExtensionHostProviderRegistration(registry, {
|
||||
pluginId: "demo-plugin",
|
||||
source: "bundled",
|
||||
provider: {
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ describe("extension host registry writes", () => {
|
|||
expect(registry.httpRoutes).toHaveLength(1);
|
||||
expect(registry.channels).toHaveLength(1);
|
||||
expect(registry.providers).toHaveLength(1);
|
||||
expect(registry.providers[0]?.pluginId).toBe("demo");
|
||||
});
|
||||
|
||||
it("writes legacy hooks, typed hooks, and context engines through host helpers", () => {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ import type {
|
|||
import {
|
||||
addExtensionHostCliRegistration,
|
||||
addExtensionHostHttpRoute,
|
||||
addExtensionHostProviderRegistration,
|
||||
addExtensionHostServiceRegistration,
|
||||
addExtensionHostToolRegistration,
|
||||
replaceExtensionHostHttpRoute,
|
||||
setExtensionHostGatewayHandler,
|
||||
} from "./runtime-registry.js";
|
||||
|
|
@ -86,7 +88,7 @@ export function addExtensionProviderRegistration(params: {
|
|||
entry: ExtensionHostProviderRegistration;
|
||||
}): void {
|
||||
params.record.providerIds.push(params.providerId);
|
||||
params.registry.providers.push(params.entry as PluginProviderRegistration);
|
||||
addExtensionHostProviderRegistration(params.registry, params.entry as PluginProviderRegistration);
|
||||
}
|
||||
|
||||
export function addExtensionLegacyHookRegistration(params: {
|
||||
|
|
@ -123,7 +125,7 @@ export function addExtensionToolRegistration(params: {
|
|||
if (params.names.length > 0) {
|
||||
params.record.toolNames.push(...params.names);
|
||||
}
|
||||
params.registry.tools.push(params.entry as PluginToolRegistration);
|
||||
addExtensionHostToolRegistration(params.registry, params.entry as PluginToolRegistration);
|
||||
}
|
||||
|
||||
export function addExtensionCliRegistration(params: {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
|||
import {
|
||||
addExtensionHostCliRegistration,
|
||||
addExtensionHostHttpRoute,
|
||||
addExtensionHostProviderRegistration,
|
||||
addExtensionHostServiceRegistration,
|
||||
addExtensionHostToolRegistration,
|
||||
getExtensionHostGatewayHandlers,
|
||||
hasExtensionHostRuntimeEntries,
|
||||
listExtensionHostCliRegistrations,
|
||||
|
|
@ -19,7 +21,7 @@ import {
|
|||
describe("extension host runtime registry accessors", () => {
|
||||
it("detects runtime entries across non-tool surfaces", () => {
|
||||
const providerRegistry = createEmptyPluginRegistry();
|
||||
providerRegistry.providers.push({
|
||||
addExtensionHostProviderRegistration(providerRegistry, {
|
||||
pluginId: "provider-demo",
|
||||
source: "test",
|
||||
provider: {
|
||||
|
|
@ -82,7 +84,7 @@ describe("extension host runtime registry accessors", () => {
|
|||
|
||||
it("projects existing registry collections without copying them", () => {
|
||||
const registry = createEmptyPluginRegistry();
|
||||
registry.tools.push({
|
||||
addExtensionHostToolRegistration(registry, {
|
||||
pluginId: "tool-demo",
|
||||
optional: false,
|
||||
source: "test",
|
||||
|
|
@ -96,6 +98,15 @@ describe("extension host runtime registry accessors", () => {
|
|||
},
|
||||
}),
|
||||
});
|
||||
addExtensionHostProviderRegistration(registry, {
|
||||
pluginId: "provider-demo",
|
||||
source: "test",
|
||||
provider: {
|
||||
id: "provider-demo",
|
||||
label: "Provider Demo",
|
||||
auth: [],
|
||||
},
|
||||
});
|
||||
addExtensionHostServiceRegistration(registry, {
|
||||
pluginId: "svc-demo",
|
||||
source: "test",
|
||||
|
|
@ -125,7 +136,8 @@ describe("extension host runtime registry accessors", () => {
|
|||
handler,
|
||||
});
|
||||
|
||||
expect(listExtensionHostToolRegistrations(registry)).toBe(registry.tools);
|
||||
expect(listExtensionHostToolRegistrations(registry)).toEqual(registry.tools);
|
||||
expect(listExtensionHostProviderRegistrations(registry)).toEqual(registry.providers);
|
||||
expect(listExtensionHostServiceRegistrations(registry)).toEqual(registry.services);
|
||||
expect(listExtensionHostCliRegistrations(registry)).toEqual(registry.cliRegistrars);
|
||||
expect(listExtensionHostHttpRoutes(registry)).toEqual(registry.httpRoutes);
|
||||
|
|
@ -189,4 +201,32 @@ describe("extension host runtime registry accessors", () => {
|
|||
expect(registry.services[0]?.service).toBe(service);
|
||||
expect(registry.cliRegistrars[0]?.register).toBe(register);
|
||||
});
|
||||
|
||||
it("keeps legacy tool and provider mirrors synchronized with host-owned state", () => {
|
||||
const registry = createEmptyPluginRegistry();
|
||||
const factory = (() => ({}) as never) as never;
|
||||
const provider = {
|
||||
id: "provider-demo",
|
||||
label: "Provider Demo",
|
||||
auth: [],
|
||||
};
|
||||
|
||||
addExtensionHostToolRegistration(registry, {
|
||||
pluginId: "tool-demo",
|
||||
optional: false,
|
||||
source: "test",
|
||||
names: ["tool_demo"],
|
||||
factory,
|
||||
});
|
||||
addExtensionHostProviderRegistration(registry, {
|
||||
pluginId: "provider-demo",
|
||||
source: "test",
|
||||
provider,
|
||||
});
|
||||
|
||||
expect(listExtensionHostToolRegistrations(registry)).toEqual(registry.tools);
|
||||
expect(listExtensionHostProviderRegistrations(registry)).toEqual(registry.providers);
|
||||
expect(registry.tools[0]?.factory).toBe(factory);
|
||||
expect(registry.providers[0]?.provider).toBe(provider);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ const EMPTY_GATEWAY_HANDLERS: Readonly<GatewayRequestHandlers> = Object.freeze({
|
|||
const EXTENSION_HOST_RUNTIME_REGISTRY_STATE = Symbol.for("openclaw.extensionHostRuntimeRegistry");
|
||||
|
||||
type ExtensionHostRuntimeRegistryState = {
|
||||
tools: PluginToolRegistration[];
|
||||
legacyTools: PluginToolRegistration[];
|
||||
providers: PluginProviderRegistration[];
|
||||
legacyProviders: PluginProviderRegistration[];
|
||||
cliRegistrars: PluginCliRegistration[];
|
||||
legacyCliRegistrars: PluginCliRegistration[];
|
||||
services: PluginServiceRegistration[];
|
||||
|
|
@ -29,7 +33,7 @@ type ExtensionHostRuntimeRegistryState = {
|
|||
|
||||
type RuntimeRegistryBackedPluginRegistry = Pick<
|
||||
PluginRegistry,
|
||||
"cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
"tools" | "providers" | "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
> & {
|
||||
[EXTENSION_HOST_RUNTIME_REGISTRY_STATE]?: ExtensionHostRuntimeRegistryState;
|
||||
};
|
||||
|
|
@ -49,8 +53,16 @@ function ensureExtensionHostRuntimeRegistryState(
|
|||
registry.cliRegistrars = legacyCliRegistrars;
|
||||
const legacyServices = registry.services ?? [];
|
||||
registry.services = legacyServices;
|
||||
const legacyTools = registry.tools ?? [];
|
||||
registry.tools = legacyTools;
|
||||
const legacyProviders = registry.providers ?? [];
|
||||
registry.providers = legacyProviders;
|
||||
|
||||
const state: ExtensionHostRuntimeRegistryState = {
|
||||
tools: [...legacyTools],
|
||||
legacyTools,
|
||||
providers: [...legacyProviders],
|
||||
legacyProviders,
|
||||
cliRegistrars: [...legacyCliRegistrars],
|
||||
legacyCliRegistrars,
|
||||
services: [...legacyServices],
|
||||
|
|
@ -64,6 +76,14 @@ function ensureExtensionHostRuntimeRegistryState(
|
|||
return state;
|
||||
}
|
||||
|
||||
function syncLegacyTools(state: ExtensionHostRuntimeRegistryState): void {
|
||||
state.legacyTools.splice(0, state.legacyTools.length, ...state.tools);
|
||||
}
|
||||
|
||||
function syncLegacyProviders(state: ExtensionHostRuntimeRegistryState): void {
|
||||
state.legacyProviders.splice(0, state.legacyProviders.length, ...state.providers);
|
||||
}
|
||||
|
||||
function syncLegacyCliRegistrars(state: ExtensionHostRuntimeRegistryState): void {
|
||||
state.legacyCliRegistrars.splice(0, state.legacyCliRegistrars.length, ...state.cliRegistrars);
|
||||
}
|
||||
|
|
@ -110,8 +130,8 @@ export function hasExtensionHostRuntimeEntries(
|
|||
return (
|
||||
registry.plugins.length > 0 ||
|
||||
registry.channels.length > 0 ||
|
||||
registry.tools.length > 0 ||
|
||||
registry.providers.length > 0 ||
|
||||
listExtensionHostToolRegistrations(registry).length > 0 ||
|
||||
listExtensionHostProviderRegistrations(registry).length > 0 ||
|
||||
Object.keys(getExtensionHostGatewayHandlers(registry)).length > 0 ||
|
||||
listExtensionHostHttpRoutes(registry).length > 0 ||
|
||||
listExtensionHostCliRegistrations(registry).length > 0 ||
|
||||
|
|
@ -123,15 +143,35 @@ export function hasExtensionHostRuntimeEntries(
|
|||
}
|
||||
|
||||
export function listExtensionHostProviderRegistrations(
|
||||
registry: Pick<PluginRegistry, "providers"> | null | undefined,
|
||||
registry:
|
||||
| Pick<
|
||||
PluginRegistry,
|
||||
"tools" | "providers" | "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
>
|
||||
| null
|
||||
| undefined,
|
||||
): readonly PluginProviderRegistration[] {
|
||||
return registry?.providers ?? EMPTY_PROVIDERS;
|
||||
if (!registry) {
|
||||
return EMPTY_PROVIDERS;
|
||||
}
|
||||
return ensureExtensionHostRuntimeRegistryState(registry as RuntimeRegistryBackedPluginRegistry)
|
||||
.providers;
|
||||
}
|
||||
|
||||
export function listExtensionHostToolRegistrations(
|
||||
registry: Pick<PluginRegistry, "tools"> | null | undefined,
|
||||
registry:
|
||||
| Pick<
|
||||
PluginRegistry,
|
||||
"tools" | "providers" | "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
>
|
||||
| null
|
||||
| undefined,
|
||||
): readonly PluginToolRegistration[] {
|
||||
return registry?.tools ?? EMPTY_TOOLS;
|
||||
if (!registry) {
|
||||
return EMPTY_TOOLS;
|
||||
}
|
||||
return ensureExtensionHostRuntimeRegistryState(registry as RuntimeRegistryBackedPluginRegistry)
|
||||
.tools;
|
||||
}
|
||||
|
||||
export function listExtensionHostServiceRegistrations(
|
||||
|
|
@ -161,7 +201,13 @@ export function listExtensionHostCliRegistrations(
|
|||
}
|
||||
|
||||
export function listExtensionHostHttpRoutes(
|
||||
registry: Pick<PluginRegistry, "httpRoutes" | "gatewayHandlers"> | null | undefined,
|
||||
registry:
|
||||
| Pick<
|
||||
PluginRegistry,
|
||||
"tools" | "providers" | "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
>
|
||||
| null
|
||||
| undefined,
|
||||
): readonly PluginHttpRouteRegistration[] {
|
||||
if (!registry) {
|
||||
return EMPTY_HTTP_ROUTES;
|
||||
|
|
@ -171,7 +217,13 @@ export function listExtensionHostHttpRoutes(
|
|||
}
|
||||
|
||||
export function getExtensionHostGatewayHandlers(
|
||||
registry: Pick<PluginRegistry, "httpRoutes" | "gatewayHandlers"> | null | undefined,
|
||||
registry:
|
||||
| Pick<
|
||||
PluginRegistry,
|
||||
"tools" | "providers" | "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
>
|
||||
| null
|
||||
| undefined,
|
||||
): Readonly<GatewayRequestHandlers> {
|
||||
if (!registry) {
|
||||
return EMPTY_GATEWAY_HANDLERS;
|
||||
|
|
@ -181,7 +233,10 @@ export function getExtensionHostGatewayHandlers(
|
|||
}
|
||||
|
||||
export function addExtensionHostHttpRoute(
|
||||
registry: Pick<PluginRegistry, "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers">,
|
||||
registry: Pick<
|
||||
PluginRegistry,
|
||||
"tools" | "providers" | "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
>,
|
||||
entry: PluginHttpRouteRegistration,
|
||||
): void {
|
||||
const state = ensureExtensionHostRuntimeRegistryState(
|
||||
|
|
@ -192,7 +247,10 @@ export function addExtensionHostHttpRoute(
|
|||
}
|
||||
|
||||
export function replaceExtensionHostHttpRoute(params: {
|
||||
registry: Pick<PluginRegistry, "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers">;
|
||||
registry: Pick<
|
||||
PluginRegistry,
|
||||
"tools" | "providers" | "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
>;
|
||||
index: number;
|
||||
entry: PluginHttpRouteRegistration;
|
||||
}): void {
|
||||
|
|
@ -204,7 +262,10 @@ export function replaceExtensionHostHttpRoute(params: {
|
|||
}
|
||||
|
||||
export function removeExtensionHostHttpRoute(
|
||||
registry: Pick<PluginRegistry, "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers">,
|
||||
registry: Pick<
|
||||
PluginRegistry,
|
||||
"tools" | "providers" | "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
>,
|
||||
entry: PluginHttpRouteRegistration,
|
||||
): void {
|
||||
const state = ensureExtensionHostRuntimeRegistryState(
|
||||
|
|
@ -219,7 +280,10 @@ export function removeExtensionHostHttpRoute(
|
|||
}
|
||||
|
||||
export function setExtensionHostGatewayHandler(params: {
|
||||
registry: Pick<PluginRegistry, "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers">;
|
||||
registry: Pick<
|
||||
PluginRegistry,
|
||||
"tools" | "providers" | "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
>;
|
||||
method: string;
|
||||
handler: GatewayRequestHandlers[string];
|
||||
}): void {
|
||||
|
|
@ -231,7 +295,10 @@ export function setExtensionHostGatewayHandler(params: {
|
|||
}
|
||||
|
||||
export function addExtensionHostCliRegistration(
|
||||
registry: Pick<PluginRegistry, "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers">,
|
||||
registry: Pick<
|
||||
PluginRegistry,
|
||||
"tools" | "providers" | "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
>,
|
||||
entry: PluginCliRegistration,
|
||||
): void {
|
||||
const state = ensureExtensionHostRuntimeRegistryState(
|
||||
|
|
@ -242,7 +309,10 @@ export function addExtensionHostCliRegistration(
|
|||
}
|
||||
|
||||
export function addExtensionHostServiceRegistration(
|
||||
registry: Pick<PluginRegistry, "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers">,
|
||||
registry: Pick<
|
||||
PluginRegistry,
|
||||
"tools" | "providers" | "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
>,
|
||||
entry: PluginServiceRegistration,
|
||||
): void {
|
||||
const state = ensureExtensionHostRuntimeRegistryState(
|
||||
|
|
@ -251,3 +321,31 @@ export function addExtensionHostServiceRegistration(
|
|||
state.services.push(entry);
|
||||
syncLegacyServices(state);
|
||||
}
|
||||
|
||||
export function addExtensionHostToolRegistration(
|
||||
registry: Pick<
|
||||
PluginRegistry,
|
||||
"tools" | "providers" | "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
>,
|
||||
entry: PluginToolRegistration,
|
||||
): void {
|
||||
const state = ensureExtensionHostRuntimeRegistryState(
|
||||
registry as RuntimeRegistryBackedPluginRegistry,
|
||||
);
|
||||
state.tools.push(entry);
|
||||
syncLegacyTools(state);
|
||||
}
|
||||
|
||||
export function addExtensionHostProviderRegistration(
|
||||
registry: Pick<
|
||||
PluginRegistry,
|
||||
"tools" | "providers" | "cliRegistrars" | "services" | "httpRoutes" | "gatewayHandlers"
|
||||
>,
|
||||
entry: PluginProviderRegistration,
|
||||
): void {
|
||||
const state = ensureExtensionHostRuntimeRegistryState(
|
||||
registry as RuntimeRegistryBackedPluginRegistry,
|
||||
);
|
||||
state.providers.push(entry);
|
||||
syncLegacyProviders(state);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { AnyAgentTool } from "../agents/tools/common.js";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
||||
import { addExtensionHostToolRegistration } from "./runtime-registry.js";
|
||||
import { getExtensionHostPluginToolMeta, resolveExtensionHostPluginTools } from "./tool-runtime.js";
|
||||
|
||||
function makeTool(name: string): AnyAgentTool {
|
||||
|
|
@ -28,7 +29,7 @@ function createContext() {
|
|||
describe("resolveExtensionHostPluginTools", () => {
|
||||
it("allows optional tools through tool, plugin, and plugin-group allowlists", () => {
|
||||
const registry = createEmptyPluginRegistry();
|
||||
registry.tools.push({
|
||||
addExtensionHostToolRegistration(registry, {
|
||||
pluginId: "optional-demo",
|
||||
optional: true,
|
||||
source: "/tmp/optional-demo.js",
|
||||
|
|
@ -68,14 +69,14 @@ describe("resolveExtensionHostPluginTools", () => {
|
|||
it("records conflict diagnostics and preserves tool metadata", () => {
|
||||
const registry = createEmptyPluginRegistry();
|
||||
const extraTool = makeTool("other_tool");
|
||||
registry.tools.push({
|
||||
addExtensionHostToolRegistration(registry, {
|
||||
pluginId: "message",
|
||||
optional: false,
|
||||
source: "/tmp/message.js",
|
||||
factory: () => makeTool("optional_tool"),
|
||||
names: ["optional_tool"],
|
||||
});
|
||||
registry.tools.push({
|
||||
addExtensionHostToolRegistration(registry, {
|
||||
pluginId: "multi",
|
||||
optional: false,
|
||||
source: "/tmp/multi.js",
|
||||
|
|
@ -104,7 +105,7 @@ describe("resolveExtensionHostPluginTools", () => {
|
|||
const factory = vi.fn(() => {
|
||||
throw new Error("boom");
|
||||
});
|
||||
registry.tools.push({
|
||||
addExtensionHostToolRegistration(registry, {
|
||||
pluginId: "broken",
|
||||
optional: false,
|
||||
source: "/tmp/broken.js",
|
||||
|
|
|
|||
Loading…
Reference in New Issue