From 26a8aee01cfbdf15e23c13eb2d62841aa119e6bd Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 15 Mar 2026 18:23:18 -0700 Subject: [PATCH] refactor: drop channel onboarding fallback --- docs/tools/plugin.md | 15 ------------- src/channels/plugins/types.plugin.ts | 3 --- src/commands/onboard-channels.e2e.test.ts | 20 +++++++++-------- src/commands/onboard-channels.ts | 10 ++++++++- src/commands/onboarding/registry.ts | 3 --- src/plugin-sdk/subpaths.test.ts | 26 +++++++++++++++++++++++ 6 files changed, 46 insertions(+), 31 deletions(-) diff --git a/docs/tools/plugin.md b/docs/tools/plugin.md index 1cfe6ae1cd0..4113c9fbd05 100644 --- a/docs/tools/plugin.md +++ b/docs/tools/plugin.md @@ -1437,16 +1437,6 @@ Preferred setup split: - `plugin.setup` owns account-id normalization, validation, and config writes. - `plugin.setupWizard` lets the host run the common wizard flow while the channel only supplies status, credential, DM allowlist, and channel-access descriptors. -Use `plugin.onboarding` only when the host-owned setup wizard cannot express the flow and the -channel needs to fully own prompting. - -Wizard precedence: - -1. `plugin.setupWizard` (preferred, host-owned prompts) -2. `plugin.onboarding.configureInteractive` -3. `plugin.onboarding.configureWhenConfigured` (already-configured channel only) -4. `plugin.onboarding.configure` - `plugin.setupWizard` is best for channels that fit the shared pattern: - one account picker driven by `plugin.config.listAccountIds` @@ -1458,11 +1448,6 @@ Wizard precedence: - optional DM allowlist resolution (for example `@username` -> numeric id) - optional completion note after setup finishes -`plugin.onboarding` hooks still return the same values as before: - -- `"skip"` leaves selection and account tracking unchanged. -- `{ cfg, accountId? }` applies config updates and records account selection. - ### Write a new messaging channel (step‑by‑step) Use this when you want a **new chat surface** (a "messaging channel"), not a model provider. diff --git a/src/channels/plugins/types.plugin.ts b/src/channels/plugins/types.plugin.ts index 3c821ab601b..cf09af29048 100644 --- a/src/channels/plugins/types.plugin.ts +++ b/src/channels/plugins/types.plugin.ts @@ -1,4 +1,3 @@ -import type { ChannelOnboardingAdapter } from "./onboarding-types.js"; import type { ChannelSetupWizard } from "./setup-wizard.js"; import type { ChannelAuthAdapter, @@ -57,8 +56,6 @@ export type ChannelPlugin; configSchema?: ChannelConfigSchema; diff --git a/src/commands/onboard-channels.e2e.test.ts b/src/commands/onboard-channels.e2e.test.ts index 6c505c6d4e2..c469f50a54e 100644 --- a/src/commands/onboard-channels.e2e.test.ts +++ b/src/commands/onboard-channels.e2e.test.ts @@ -472,15 +472,17 @@ describe("setupChannels", () => { )?.accounts?.[accountId] ?? { accountId }, setAccountEnabled, }, - onboarding: { - getStatus: vi.fn(async ({ cfg }: { cfg: OpenClawConfig }) => ({ - channel: "msteams", - configured: Boolean( - (cfg.channels?.msteams as { tenantId?: string } | undefined)?.tenantId, - ), - statusLines: [], - selectionHint: "configured", - })), + setupWizard: { + channel: "msteams", + status: { + configuredLabel: "configured", + unconfiguredLabel: "needs setup", + resolveConfigured: ({ cfg }: { cfg: OpenClawConfig }) => + Boolean((cfg.channels?.msteams as { tenantId?: string } | undefined)?.tenantId), + resolveStatusLines: async () => [], + resolveSelectionHint: async () => "configured", + }, + credentials: [], }, outbound: { deliveryMode: "direct" }, }, diff --git a/src/commands/onboard-channels.ts b/src/commands/onboard-channels.ts index 4a313ebf913..81deb95e901 100644 --- a/src/commands/onboard-channels.ts +++ b/src/commands/onboard-channels.ts @@ -5,6 +5,7 @@ import { getChannelSetupPlugin, listChannelSetupPlugins, } from "../channels/plugins/setup-registry.js"; +import { buildChannelOnboardingAdapterFromSetupWizard } from "../channels/plugins/setup-wizard.js"; import type { ChannelMeta, ChannelPlugin } from "../channels/plugins/types.js"; import { formatChannelPrimerLine, @@ -354,7 +355,14 @@ export async function setupChannels( if (adapter) { return adapter; } - return scopedPluginsById.get(channel)?.onboarding; + const scopedPlugin = scopedPluginsById.get(channel); + if (!scopedPlugin?.setupWizard) { + return undefined; + } + return buildChannelOnboardingAdapterFromSetupWizard({ + plugin: scopedPlugin, + wizard: scopedPlugin.setupWizard, + }); }; const preloadConfiguredExternalPlugins = () => { // Keep onboarding memory bounded by snapshot-loading only configured external plugins. diff --git a/src/commands/onboarding/registry.ts b/src/commands/onboarding/registry.ts index 14074daf193..99009ee8fac 100644 --- a/src/commands/onboarding/registry.ts +++ b/src/commands/onboarding/registry.ts @@ -60,9 +60,6 @@ function resolveChannelOnboardingAdapter( setupWizardAdapters.set(plugin, adapter); return adapter; } - if (plugin.onboarding) { - return plugin.onboarding; - } return undefined; } diff --git a/src/plugin-sdk/subpaths.test.ts b/src/plugin-sdk/subpaths.test.ts index 09341c4e82b..42d69512925 100644 --- a/src/plugin-sdk/subpaths.test.ts +++ b/src/plugin-sdk/subpaths.test.ts @@ -101,6 +101,12 @@ describe("plugin-sdk subpath exports", () => { expect("resolveWhatsAppMentionStripPatterns" in whatsappSdk).toBe(false); }); + it("exports Feishu helpers", async () => { + const feishuSdk = await import("openclaw/plugin-sdk/feishu"); + expect(typeof feishuSdk.feishuSetupWizard).toBe("object"); + expect(typeof feishuSdk.feishuSetupAdapter).toBe("object"); + }); + it("exports LINE helpers", () => { expect(typeof lineSdk.processLineMessage).toBe("function"); expect(typeof lineSdk.createInfoCard).toBe("function"); @@ -109,6 +115,8 @@ describe("plugin-sdk subpath exports", () => { it("exports Microsoft Teams helpers", () => { expect(typeof msteamsSdk.resolveControlCommandGate).toBe("function"); expect(typeof msteamsSdk.loadOutboundMediaFromUrl).toBe("function"); + expect(typeof msteamsSdk.msteamsSetupWizard).toBe("object"); + expect(typeof msteamsSdk.msteamsSetupAdapter).toBe("object"); }); it("exports Google Chat helpers", async () => { @@ -117,6 +125,18 @@ describe("plugin-sdk subpath exports", () => { expect(typeof googlechatSdk.googlechatSetupAdapter).toBe("object"); }); + it("exports Zalo helpers", async () => { + const zaloSdk = await import("openclaw/plugin-sdk/zalo"); + expect(typeof zaloSdk.zaloSetupWizard).toBe("object"); + expect(typeof zaloSdk.zaloSetupAdapter).toBe("object"); + }); + + it("exports Zalouser helpers", async () => { + const zalouserSdk = await import("openclaw/plugin-sdk/zalouser"); + expect(typeof zalouserSdk.zalouserSetupWizard).toBe("object"); + expect(typeof zalouserSdk.zalouserSetupAdapter).toBe("object"); + }); + it("exports Tlon helpers", async () => { const tlonSdk = await import("openclaw/plugin-sdk/tlon"); expect(typeof tlonSdk.fetchWithSsrFGuard).toBe("function"); @@ -142,6 +162,10 @@ describe("plugin-sdk subpath exports", () => { const bluebubbles = await import("openclaw/plugin-sdk/bluebubbles"); expect(typeof bluebubbles.parseFiniteNumber).toBe("function"); + const matrix = await import("openclaw/plugin-sdk/matrix"); + expect(typeof matrix.matrixSetupWizard).toBe("object"); + expect(typeof matrix.matrixSetupAdapter).toBe("object"); + const mattermost = await import("openclaw/plugin-sdk/mattermost"); expect(typeof mattermost.parseStrictPositiveInteger).toBe("function"); @@ -151,6 +175,8 @@ describe("plugin-sdk subpath exports", () => { const twitch = await import("openclaw/plugin-sdk/twitch"); expect(typeof twitch.DEFAULT_ACCOUNT_ID).toBe("string"); expect(typeof twitch.normalizeAccountId).toBe("function"); + expect(typeof twitch.twitchSetupWizard).toBe("object"); + expect(typeof twitch.twitchSetupAdapter).toBe("object"); const zalo = await import("openclaw/plugin-sdk/zalo"); expect(typeof zalo.resolveClientIp).toBe("function");