mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 08:00:42 +00:00
305 lines
8.6 KiB
TypeScript
305 lines
8.6 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import type { OpenClawConfig } from "../config/config.js";
|
|
import { resetConfigRuntimeState, setRuntimeConfigSnapshot } from "../config/config.js";
|
|
import { activateSecretsRuntimeSnapshot, clearSecretsRuntimeSnapshot } from "../secrets/runtime.js";
|
|
import { resolveOpenClawPluginToolsForOptions } from "./openclaw-plugin-tools.js";
|
|
|
|
const hoisted = vi.hoisted(() => ({
|
|
resolvePluginTools: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("../plugins/tools.js", () => ({
|
|
resolvePluginTools: (...args: unknown[]) => hoisted.resolvePluginTools(...args),
|
|
}));
|
|
|
|
describe("createOpenClawTools browser plugin integration", () => {
|
|
afterEach(() => {
|
|
hoisted.resolvePluginTools.mockReset();
|
|
clearSecretsRuntimeSnapshot();
|
|
resetConfigRuntimeState();
|
|
});
|
|
|
|
it("keeps the browser tool returned by plugin resolution", () => {
|
|
hoisted.resolvePluginTools.mockReturnValue([
|
|
{
|
|
name: "browser",
|
|
description: "browser fixture tool",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {},
|
|
},
|
|
async execute() {
|
|
return {
|
|
content: [{ type: "text", text: "ok" }],
|
|
};
|
|
},
|
|
},
|
|
]);
|
|
|
|
const config = {
|
|
plugins: {
|
|
allow: ["browser"],
|
|
},
|
|
} as OpenClawConfig;
|
|
|
|
const tools = resolveOpenClawPluginToolsForOptions({
|
|
options: { config },
|
|
resolvedConfig: config,
|
|
});
|
|
|
|
expect(tools.map((tool) => tool.name)).toContain("browser");
|
|
});
|
|
|
|
it("omits the browser tool when plugin resolution returns no browser tool", () => {
|
|
hoisted.resolvePluginTools.mockReturnValue([]);
|
|
|
|
const config = {
|
|
plugins: {
|
|
allow: ["browser"],
|
|
entries: {
|
|
browser: {
|
|
enabled: false,
|
|
},
|
|
},
|
|
},
|
|
} as OpenClawConfig;
|
|
|
|
const tools = resolveOpenClawPluginToolsForOptions({
|
|
options: { config },
|
|
resolvedConfig: config,
|
|
});
|
|
|
|
expect(tools.map((tool) => tool.name)).not.toContain("browser");
|
|
});
|
|
|
|
it("forwards fsPolicy into plugin tool context", async () => {
|
|
let capturedContext: { fsPolicy?: { workspaceOnly: boolean } } | undefined;
|
|
hoisted.resolvePluginTools.mockImplementation((params: unknown) => {
|
|
const resolvedParams = params as { context?: { fsPolicy?: { workspaceOnly: boolean } } };
|
|
capturedContext = resolvedParams.context;
|
|
return [
|
|
{
|
|
name: "browser",
|
|
description: "browser fixture tool",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {},
|
|
},
|
|
async execute() {
|
|
return {
|
|
content: [{ type: "text", text: "ok" }],
|
|
details: { workspaceOnly: capturedContext?.fsPolicy?.workspaceOnly ?? null },
|
|
};
|
|
},
|
|
},
|
|
];
|
|
});
|
|
|
|
const tools = resolveOpenClawPluginToolsForOptions({
|
|
options: {
|
|
config: {
|
|
plugins: {
|
|
allow: ["browser"],
|
|
},
|
|
} as OpenClawConfig,
|
|
fsPolicy: { workspaceOnly: true },
|
|
},
|
|
resolvedConfig: {
|
|
plugins: {
|
|
allow: ["browser"],
|
|
},
|
|
} as OpenClawConfig,
|
|
});
|
|
|
|
const browserTool = tools.find((tool) => tool.name === "browser");
|
|
expect(browserTool).toBeDefined();
|
|
if (!browserTool) {
|
|
throw new Error("expected browser tool");
|
|
}
|
|
|
|
const result = await browserTool.execute("tool-call", {});
|
|
const details = (result.details ?? {}) as { workspaceOnly?: boolean | null };
|
|
expect(details.workspaceOnly).toBe(true);
|
|
});
|
|
|
|
it("forwards gateway subagent binding to plugin resolution", () => {
|
|
hoisted.resolvePluginTools.mockReturnValue([]);
|
|
const config = {
|
|
plugins: {
|
|
allow: ["browser"],
|
|
},
|
|
} as OpenClawConfig;
|
|
|
|
resolveOpenClawPluginToolsForOptions({
|
|
options: { config, allowGatewaySubagentBinding: true },
|
|
resolvedConfig: config,
|
|
});
|
|
|
|
expect(hoisted.resolvePluginTools).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
allowGatewaySubagentBinding: true,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("forwards plugin tool deny policy to plugin resolution", () => {
|
|
hoisted.resolvePluginTools.mockReturnValue([]);
|
|
const config = {
|
|
plugins: {
|
|
allow: ["browser"],
|
|
},
|
|
} as OpenClawConfig;
|
|
|
|
resolveOpenClawPluginToolsForOptions({
|
|
options: {
|
|
config,
|
|
pluginToolAllowlist: ["*"],
|
|
pluginToolDenylist: ["browser"],
|
|
},
|
|
resolvedConfig: config,
|
|
});
|
|
|
|
expect(hoisted.resolvePluginTools).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
toolAllowlist: ["*"],
|
|
toolDenylist: ["browser"],
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("does not pass a stale active snapshot as plugin runtime config for a resolved run config", () => {
|
|
const staleSourceConfig = {
|
|
plugins: {
|
|
allow: ["old-plugin"],
|
|
},
|
|
} as OpenClawConfig;
|
|
const staleRuntimeConfig = {
|
|
plugins: {
|
|
allow: ["old-plugin"],
|
|
},
|
|
} as OpenClawConfig;
|
|
const resolvedRunConfig = {
|
|
plugins: {
|
|
allow: ["browser"],
|
|
},
|
|
tools: {
|
|
experimental: {
|
|
planTool: true,
|
|
},
|
|
},
|
|
} as OpenClawConfig;
|
|
let capturedRuntimeConfig: OpenClawConfig | undefined;
|
|
hoisted.resolvePluginTools.mockImplementation((params: unknown) => {
|
|
capturedRuntimeConfig = (params as { context?: { runtimeConfig?: OpenClawConfig } }).context
|
|
?.runtimeConfig;
|
|
return [];
|
|
});
|
|
activateSecretsRuntimeSnapshot({
|
|
sourceConfig: staleSourceConfig,
|
|
config: staleRuntimeConfig,
|
|
authStores: [],
|
|
warnings: [],
|
|
webTools: {
|
|
search: {
|
|
providerSource: "none",
|
|
diagnostics: [],
|
|
},
|
|
fetch: {
|
|
providerSource: "none",
|
|
diagnostics: [],
|
|
},
|
|
diagnostics: [],
|
|
},
|
|
});
|
|
|
|
resolveOpenClawPluginToolsForOptions({
|
|
options: { config: resolvedRunConfig },
|
|
resolvedConfig: resolvedRunConfig,
|
|
});
|
|
|
|
expect(capturedRuntimeConfig).toBe(resolvedRunConfig);
|
|
});
|
|
|
|
it("does not let a source-less pinned config snapshot override explicit plugin tool config", () => {
|
|
const pinnedRuntimeConfig = {
|
|
plugins: {
|
|
allow: ["old-plugin"],
|
|
},
|
|
} as OpenClawConfig;
|
|
const explicitConfig = {
|
|
plugins: {
|
|
allow: ["browser"],
|
|
},
|
|
tools: {
|
|
experimental: {
|
|
planTool: true,
|
|
},
|
|
},
|
|
} as OpenClawConfig;
|
|
let capturedRuntimeConfig: OpenClawConfig | undefined;
|
|
let getRuntimeConfig: (() => OpenClawConfig | undefined) | undefined;
|
|
hoisted.resolvePluginTools.mockImplementation((params: unknown) => {
|
|
const context = (
|
|
params as {
|
|
context?: {
|
|
runtimeConfig?: OpenClawConfig;
|
|
getRuntimeConfig?: () => OpenClawConfig | undefined;
|
|
};
|
|
}
|
|
).context;
|
|
capturedRuntimeConfig = context?.runtimeConfig;
|
|
getRuntimeConfig = context?.getRuntimeConfig;
|
|
return [];
|
|
});
|
|
setRuntimeConfigSnapshot(pinnedRuntimeConfig);
|
|
|
|
resolveOpenClawPluginToolsForOptions({
|
|
options: { config: explicitConfig },
|
|
resolvedConfig: explicitConfig,
|
|
});
|
|
|
|
expect(capturedRuntimeConfig).toBe(explicitConfig);
|
|
expect(getRuntimeConfig?.()).toBe(explicitConfig);
|
|
});
|
|
|
|
it("exposes a live runtime config getter to plugin tool factories", () => {
|
|
const sourceConfig = {
|
|
plugins: {
|
|
allow: ["memory-core"],
|
|
},
|
|
} as OpenClawConfig;
|
|
const firstRuntimeConfig = {
|
|
plugins: {
|
|
allow: ["memory-core"],
|
|
entries: { "memory-core": { enabled: true } },
|
|
},
|
|
} as OpenClawConfig;
|
|
const nextRuntimeConfig = {
|
|
plugins: {
|
|
allow: ["memory-core"],
|
|
entries: { "memory-core": { enabled: false } },
|
|
},
|
|
} as OpenClawConfig;
|
|
let getRuntimeConfig: (() => OpenClawConfig | undefined) | undefined;
|
|
hoisted.resolvePluginTools.mockImplementation((params: unknown) => {
|
|
getRuntimeConfig = (
|
|
params as { context?: { getRuntimeConfig?: () => OpenClawConfig | undefined } }
|
|
).context?.getRuntimeConfig;
|
|
return [];
|
|
});
|
|
setRuntimeConfigSnapshot(firstRuntimeConfig, sourceConfig);
|
|
|
|
resolveOpenClawPluginToolsForOptions({
|
|
options: { config: sourceConfig },
|
|
resolvedConfig: sourceConfig,
|
|
});
|
|
|
|
expect(getRuntimeConfig?.()).toStrictEqual(firstRuntimeConfig);
|
|
|
|
setRuntimeConfigSnapshot(nextRuntimeConfig, sourceConfig);
|
|
|
|
expect(getRuntimeConfig?.()).toStrictEqual(nextRuntimeConfig);
|
|
expect(getRuntimeConfig?.()?.plugins?.entries?.["memory-core"]?.enabled).toBe(false);
|
|
});
|
|
});
|