refactor: unify plugin sdk pairing flows

This commit is contained in:
Peter Steinberger
2026-03-19 00:30:55 +00:00
parent b736a92e19
commit 4cc0bb07c1
9 changed files with 192 additions and 79 deletions

View File

@@ -1,6 +1,9 @@
import { describe, expect, it, vi } from "vitest";
import type { PluginRuntime } from "../plugins/runtime/types.js";
import { createChannelPairingController } from "./channel-pairing.js";
import {
createChannelPairingChallengeIssuer,
createChannelPairingController,
} from "./channel-pairing.js";
describe("createChannelPairingController", () => {
it("scopes store access and issues pairing challenges through the scoped store", async () => {
@@ -46,3 +49,28 @@ describe("createChannelPairingController", () => {
expect(replies[0]).toContain("123456");
});
});
describe("createChannelPairingChallengeIssuer", () => {
it("binds a channel and scoped pairing store to challenge issuance", async () => {
const upsertPairingRequest = vi.fn(async () => ({ code: "654321", created: true }));
const replies: string[] = [];
const issueChallenge = createChannelPairingChallengeIssuer({
channel: "signal",
upsertPairingRequest,
});
await issueChallenge({
senderId: "user-2",
senderIdLine: "Your id: user-2",
sendPairingReply: async (text: string) => {
replies.push(text);
},
});
expect(upsertPairingRequest).toHaveBeenCalledWith({
id: "user-2",
meta: undefined,
});
expect(replies[0]).toContain("654321");
});
});

View File

@@ -13,6 +13,23 @@ export type ChannelPairingController = ScopedPairingAccess & {
) => ReturnType<typeof issuePairingChallenge>;
};
export function createChannelPairingChallengeIssuer(params: {
channel: ChannelId;
upsertPairingRequest: Parameters<typeof issuePairingChallenge>[0]["upsertPairingRequest"];
}) {
return (
challenge: Omit<
Parameters<typeof issuePairingChallenge>[0],
"channel" | "upsertPairingRequest"
>,
) =>
issuePairingChallenge({
channel: params.channel,
upsertPairingRequest: params.upsertPairingRequest,
...challenge,
});
}
export function createChannelPairingController(params: {
core: PluginRuntime;
channel: ChannelId;
@@ -21,11 +38,9 @@ export function createChannelPairingController(params: {
const access = createScopedPairingAccess(params);
return {
...access,
issueChallenge: (challenge) =>
issuePairingChallenge({
channel: params.channel,
upsertPairingRequest: access.upsertPairingRequest,
...challenge,
}),
issueChallenge: createChannelPairingChallengeIssuer({
channel: params.channel,
upsertPairingRequest: access.upsertPairingRequest,
}),
};
}

View File

@@ -57,8 +57,7 @@ export type {
ChannelToolSend,
} from "../channels/plugins/types.js";
export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
export { createChannelReplyPipeline, createReplyPrefixOptions } from "./channel-reply-pipeline.js";
export { createTypingCallbacks } from "./channel-reply-pipeline.js";
export { createChannelReplyPipeline } from "./channel-reply-pipeline.js";
export type { OpenClawConfig } from "../config/config.js";
export {
GROUP_POLICY_BLOCKED_LABEL,
@@ -82,7 +81,6 @@ export {
export { ToolPolicySchema } from "../config/zod-schema.agent-runtime.js";
export { MarkdownConfigSchema } from "../config/zod-schema.core.js";
export { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js";
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";
export type { PluginRuntime, RuntimeLogger } from "../plugins/runtime/types.js";
export type { OpenClawPluginApi } from "../plugins/types.js";
@@ -100,7 +98,7 @@ export {
evaluateGroupRouteAccessForPolicy,
resolveSenderScopedGroupPolicy,
} from "./group-access.js";
export { createChannelPairingController, createScopedPairingAccess } from "./channel-pairing.js";
export { createChannelPairingController } from "./channel-pairing.js";
export { formatResolvedUnresolvedNote } from "./resolution-notes.js";
export { runPluginCommandWithTimeout } from "./run-command.js";
export { dispatchReplyFromConfigWithSettledDispatcher } from "./inbound-reply-dispatch.js";