mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-03 19:31:34 +00:00
111 lines
3.8 KiB
TypeScript
111 lines
3.8 KiB
TypeScript
/** Verifies non-duplex plugin commands inherit the node invocation lifetime. */
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import type { GatewayClient } from "../gateway/client.js";
|
|
import { createEmptyPluginRegistry } from "../plugins/registry-empty.js";
|
|
import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../plugins/runtime.js";
|
|
import type { OpenClawPluginNodeHostCommandContext } from "../plugins/types.node-host.js";
|
|
import { handleInvoke } from "./invoke.js";
|
|
|
|
afterEach(() => {
|
|
resetPluginRuntimeStateForTest();
|
|
});
|
|
|
|
describe("non-duplex node-host plugin cancellation", () => {
|
|
it("passes the actual invocation signal into the node-owned plugin context", async () => {
|
|
const controller = new AbortController();
|
|
const sendNodeEvent = vi.fn(async () => undefined);
|
|
const handle = vi.fn(
|
|
async (
|
|
_paramsJSON?: string | null,
|
|
_io?: unknown,
|
|
context?: OpenClawPluginNodeHostCommandContext,
|
|
) => {
|
|
await new Promise<void>((_resolve, reject) => {
|
|
context?.signal?.addEventListener(
|
|
"abort",
|
|
() =>
|
|
reject(
|
|
context.signal?.reason instanceof Error
|
|
? context.signal.reason
|
|
: new Error("node plugin invocation aborted", { cause: context.signal?.reason }),
|
|
),
|
|
{ once: true },
|
|
);
|
|
});
|
|
return '{"stale":true}';
|
|
},
|
|
);
|
|
const registry = createEmptyPluginRegistry();
|
|
registry.nodeHostCommands = [
|
|
{
|
|
pluginId: "ollama",
|
|
pluginName: "Ollama",
|
|
command: { command: "ollama.chat", cap: "local-inference", handle },
|
|
source: "test",
|
|
},
|
|
];
|
|
setActivePluginRegistry(registry);
|
|
const request = vi.fn<GatewayClient["request"]>().mockResolvedValue(null);
|
|
|
|
const invocation = handleInvoke(
|
|
{
|
|
id: "cancelable-model-inference",
|
|
nodeId: "paired-node",
|
|
command: "ollama.chat",
|
|
paramsJSON: '{"model":"local-only:small"}',
|
|
sessionKey: "agent:main:local-model",
|
|
},
|
|
{ request } as unknown as GatewayClient,
|
|
{ current: async () => [] },
|
|
undefined,
|
|
{ signal: controller.signal, pluginCommandContext: { sendNodeEvent } },
|
|
);
|
|
await vi.waitFor(() => expect(handle).toHaveBeenCalledOnce());
|
|
|
|
controller.abort(new Error("paired inference canceled"));
|
|
await invocation;
|
|
|
|
expect(handle).toHaveBeenCalledWith('{"model":"local-only:small"}', undefined, {
|
|
sendNodeEvent,
|
|
sessionKey: "agent:main:local-model",
|
|
signal: controller.signal,
|
|
});
|
|
expect(request).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("preserves legacy plugin context when the caller supplies no signal", async () => {
|
|
const sendNodeEvent = vi.fn(async () => undefined);
|
|
const handle = vi.fn(async () => '{"ok":true}');
|
|
const registry = createEmptyPluginRegistry();
|
|
registry.nodeHostCommands = [
|
|
{
|
|
pluginId: "ollama",
|
|
pluginName: "Ollama",
|
|
command: { command: "ollama.chat", cap: "local-inference", handle },
|
|
source: "test",
|
|
},
|
|
];
|
|
setActivePluginRegistry(registry);
|
|
const request = vi.fn<GatewayClient["request"]>().mockResolvedValue(null);
|
|
|
|
await handleInvoke(
|
|
{
|
|
id: "legacy-model-inference",
|
|
nodeId: "paired-node",
|
|
command: "ollama.chat",
|
|
paramsJSON: "{}",
|
|
},
|
|
{ request } as unknown as GatewayClient,
|
|
{ current: async () => [] },
|
|
undefined,
|
|
{ pluginCommandContext: { sendNodeEvent } },
|
|
);
|
|
|
|
expect(handle).toHaveBeenCalledWith("{}", undefined, { sendNodeEvent });
|
|
expect(request).toHaveBeenCalledWith(
|
|
"node.invoke.result",
|
|
expect.objectContaining({ ok: true, payloadJSON: '{"ok":true}' }),
|
|
);
|
|
});
|
|
});
|