From de503dbcbbd82e21a2c2630ca421fbba78820aec Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 15 Mar 2026 20:39:29 -0700 Subject: [PATCH] refactor: move setup fallback into setup registry --- extensions/line/setup-entry.ts | 5 ++ extensions/line/src/channel.setup.ts | 69 +++++++++++++++++ src/channels/plugins/setup-registry.ts | 48 +++++++++--- src/commands/channel-setup/registry.ts | 94 ++++------------------- src/commands/channel-test-helpers.ts | 28 +++---- src/commands/channels/add.ts | 2 +- src/commands/onboard-channels.e2e.test.ts | 14 ++-- src/commands/onboard-channels.ts | 87 ++++++++++----------- 8 files changed, 187 insertions(+), 160 deletions(-) create mode 100644 extensions/line/setup-entry.ts create mode 100644 extensions/line/src/channel.setup.ts diff --git a/extensions/line/setup-entry.ts b/extensions/line/setup-entry.ts new file mode 100644 index 00000000000..ca25d243155 --- /dev/null +++ b/extensions/line/setup-entry.ts @@ -0,0 +1,5 @@ +import { lineSetupPlugin } from "./src/channel.setup.js"; + +export default { + plugin: lineSetupPlugin, +}; diff --git a/extensions/line/src/channel.setup.ts b/extensions/line/src/channel.setup.ts new file mode 100644 index 00000000000..71a1d87c45d --- /dev/null +++ b/extensions/line/src/channel.setup.ts @@ -0,0 +1,69 @@ +import { + buildChannelConfigSchema, + LineConfigSchema, + type ChannelPlugin, + type OpenClawConfig, + type ResolvedLineAccount, +} from "openclaw/plugin-sdk/line"; +import { + listLineAccountIds, + resolveDefaultLineAccountId, + resolveLineAccount, +} from "../../../src/line/accounts.js"; +import { lineSetupAdapter } from "./setup-core.js"; +import { lineSetupWizard } from "./setup-surface.js"; + +const meta = { + id: "line", + label: "LINE", + selectionLabel: "LINE (Messaging API)", + detailLabel: "LINE Bot", + docsPath: "/channels/line", + docsLabel: "line", + blurb: "LINE Messaging API bot for Japan/Taiwan/Thailand markets.", + systemImage: "message.fill", +} as const; + +const normalizeLineAllowFrom = (entry: string) => entry.replace(/^line:(?:user:)?/i, ""); + +export const lineSetupPlugin: ChannelPlugin = { + id: "line", + meta: { + ...meta, + quickstartAllowFrom: true, + }, + capabilities: { + chatTypes: ["direct", "group"], + reactions: false, + threads: false, + media: true, + nativeCommands: false, + blockStreaming: true, + }, + reload: { configPrefixes: ["channels.line"] }, + configSchema: buildChannelConfigSchema(LineConfigSchema), + config: { + listAccountIds: (cfg: OpenClawConfig) => listLineAccountIds(cfg), + resolveAccount: (cfg: OpenClawConfig, accountId?: string | null) => + resolveLineAccount({ cfg, accountId: accountId ?? undefined }), + defaultAccountId: (cfg: OpenClawConfig) => resolveDefaultLineAccountId(cfg), + isConfigured: (account) => + Boolean(account.channelAccessToken?.trim() && account.channelSecret?.trim()), + describeAccount: (account) => ({ + accountId: account.accountId, + name: account.name, + enabled: account.enabled, + configured: Boolean(account.channelAccessToken?.trim() && account.channelSecret?.trim()), + tokenSource: account.tokenSource ?? undefined, + }), + resolveAllowFrom: ({ cfg, accountId }) => + resolveLineAccount({ cfg, accountId: accountId ?? undefined }).config.allowFrom, + formatAllowFrom: ({ allowFrom }) => + allowFrom + .map((entry) => String(entry).trim()) + .filter(Boolean) + .map((entry) => normalizeLineAllowFrom(entry)), + }, + setupWizard: lineSetupWizard, + setup: lineSetupAdapter, +}; diff --git a/src/channels/plugins/setup-registry.ts b/src/channels/plugins/setup-registry.ts index 493b14351cc..a8c7212ca1f 100644 --- a/src/channels/plugins/setup-registry.ts +++ b/src/channels/plugins/setup-registry.ts @@ -1,3 +1,12 @@ +import { discordSetupPlugin } from "../../../extensions/discord/src/channel.setup.js"; +import { googlechatPlugin } from "../../../extensions/googlechat/src/channel.js"; +import { imessageSetupPlugin } from "../../../extensions/imessage/src/channel.setup.js"; +import { ircPlugin } from "../../../extensions/irc/src/channel.js"; +import { lineSetupPlugin } from "../../../extensions/line/src/channel.setup.js"; +import { signalSetupPlugin } from "../../../extensions/signal/src/channel.setup.js"; +import { slackSetupPlugin } from "../../../extensions/slack/src/channel.setup.js"; +import { telegramSetupPlugin } from "../../../extensions/telegram/src/channel.setup.js"; +import { whatsappSetupPlugin } from "../../../extensions/whatsapp/src/channel.setup.js"; import { getActivePluginRegistryVersion, requireActivePluginRegistry, @@ -19,6 +28,18 @@ const EMPTY_CHANNEL_SETUP_CACHE: CachedChannelSetupPlugins = { let cachedChannelSetupPlugins = EMPTY_CHANNEL_SETUP_CACHE; +const BUNDLED_CHANNEL_SETUP_PLUGINS = [ + telegramSetupPlugin, + whatsappSetupPlugin, + discordSetupPlugin, + ircPlugin, + googlechatPlugin, + slackSetupPlugin, + signalSetupPlugin, + imessageSetupPlugin, + lineSetupPlugin, +] as ChannelPlugin[]; + function dedupeSetupPlugins(plugins: ChannelPlugin[]): ChannelPlugin[] { const seen = new Set(); const resolved: ChannelPlugin[] = []; @@ -33,17 +54,8 @@ function dedupeSetupPlugins(plugins: ChannelPlugin[]): ChannelPlugin[] { return resolved; } -function resolveCachedChannelSetupPlugins(): CachedChannelSetupPlugins { - const registry = requireActivePluginRegistry(); - const registryVersion = getActivePluginRegistryVersion(); - const cached = cachedChannelSetupPlugins; - if (cached.registryVersion === registryVersion) { - return cached; - } - - const sorted = dedupeSetupPlugins( - (registry.channelSetups ?? []).map((entry) => entry.plugin), - ).toSorted((a, b) => { +function sortChannelSetupPlugins(plugins: ChannelPlugin[]): ChannelPlugin[] { + return dedupeSetupPlugins(plugins).toSorted((a, b) => { const indexA = CHAT_CHANNEL_ORDER.indexOf(a.id as ChatChannelId); const indexB = CHAT_CHANNEL_ORDER.indexOf(b.id as ChatChannelId); const orderA = a.meta.order ?? (indexA === -1 ? 999 : indexA); @@ -53,6 +65,20 @@ function resolveCachedChannelSetupPlugins(): CachedChannelSetupPlugins { } return a.id.localeCompare(b.id); }); +} + +function resolveCachedChannelSetupPlugins(): CachedChannelSetupPlugins { + const registry = requireActivePluginRegistry(); + const registryVersion = getActivePluginRegistryVersion(); + const cached = cachedChannelSetupPlugins; + if (cached.registryVersion === registryVersion) { + return cached; + } + + const registryPlugins = (registry.channelSetups ?? []).map((entry) => entry.plugin); + const sorted = sortChannelSetupPlugins( + registryPlugins.length > 0 ? registryPlugins : BUNDLED_CHANNEL_SETUP_PLUGINS, + ); const byId = new Map(); for (const plugin of sorted) { byId.set(plugin.id, plugin); diff --git a/src/commands/channel-setup/registry.ts b/src/commands/channel-setup/registry.ts index bedc2f9bf6d..9bfd1cf188b 100644 --- a/src/commands/channel-setup/registry.ts +++ b/src/commands/channel-setup/registry.ts @@ -1,46 +1,20 @@ -import { discordPlugin } from "../../../extensions/discord/src/channel.js"; -import { googlechatPlugin } from "../../../extensions/googlechat/src/channel.js"; -import { imessagePlugin } from "../../../extensions/imessage/src/channel.js"; -import { ircPlugin } from "../../../extensions/irc/src/channel.js"; -import { linePlugin } from "../../../extensions/line/src/channel.js"; -import { signalPlugin } from "../../../extensions/signal/src/channel.js"; -import { slackPlugin } from "../../../extensions/slack/src/channel.js"; -import { telegramPlugin } from "../../../extensions/telegram/src/channel.js"; -import { whatsappPlugin } from "../../../extensions/whatsapp/src/channel.js"; import { listChannelSetupPlugins } from "../../channels/plugins/setup-registry.js"; -import { buildChannelOnboardingAdapterFromSetupWizard } from "../../channels/plugins/setup-wizard.js"; +import { buildChannelSetupFlowAdapterFromSetupWizard } from "../../channels/plugins/setup-wizard.js"; import type { ChannelPlugin } from "../../channels/plugins/types.js"; import type { ChannelChoice } from "../onboard-types.js"; -import type { ChannelOnboardingAdapter } from "../onboarding/types.js"; +import type { ChannelSetupFlowAdapter } from "./types.js"; -const EMPTY_REGISTRY_FALLBACK_PLUGINS = [ - telegramPlugin, - whatsappPlugin, - discordPlugin, - ircPlugin, - googlechatPlugin, - slackPlugin, - signalPlugin, - imessagePlugin, - linePlugin, -]; +const setupWizardAdapters = new WeakMap(); -export type ChannelOnboardingSetupPlugin = Pick< - ChannelPlugin, - "id" | "meta" | "capabilities" | "config" | "setup" | "setupWizard" ->; - -const setupWizardAdapters = new WeakMap(); - -export function resolveChannelOnboardingAdapterForPlugin( - plugin?: ChannelOnboardingSetupPlugin, -): ChannelOnboardingAdapter | undefined { +export function resolveChannelSetupFlowAdapterForPlugin( + plugin?: ChannelPlugin, +): ChannelSetupFlowAdapter | undefined { if (plugin?.setupWizard) { const cached = setupWizardAdapters.get(plugin); if (cached) { return cached; } - const adapter = buildChannelOnboardingAdapterFromSetupWizard({ + const adapter = buildChannelSetupFlowAdapterFromSetupWizard({ plugin, wizard: plugin.setupWizard, }); @@ -50,15 +24,10 @@ export function resolveChannelOnboardingAdapterForPlugin( return undefined; } -const CHANNEL_ONBOARDING_ADAPTERS = () => { - const adapters = new Map(); - const setupPlugins = listChannelSetupPlugins(); - const plugins = - setupPlugins.length > 0 - ? setupPlugins - : (EMPTY_REGISTRY_FALLBACK_PLUGINS as unknown as ReturnType); - for (const plugin of plugins) { - const adapter = resolveChannelOnboardingAdapterForPlugin(plugin); +const CHANNEL_SETUP_FLOW_ADAPTERS = () => { + const adapters = new Map(); + for (const plugin of listChannelSetupPlugins()) { + const adapter = resolveChannelSetupFlowAdapterForPlugin(plugin); if (!adapter) { continue; } @@ -67,43 +36,12 @@ const CHANNEL_ONBOARDING_ADAPTERS = () => { return adapters; }; -export function getChannelOnboardingAdapter( +export function getChannelSetupFlowAdapter( channel: ChannelChoice, -): ChannelOnboardingAdapter | undefined { - return CHANNEL_ONBOARDING_ADAPTERS().get(channel); +): ChannelSetupFlowAdapter | undefined { + return CHANNEL_SETUP_FLOW_ADAPTERS().get(channel); } -export function listChannelOnboardingAdapters(): ChannelOnboardingAdapter[] { - return Array.from(CHANNEL_ONBOARDING_ADAPTERS().values()); +export function listChannelSetupFlowAdapters(): ChannelSetupFlowAdapter[] { + return Array.from(CHANNEL_SETUP_FLOW_ADAPTERS().values()); } - -export async function loadBundledChannelOnboardingPlugin( - channel: ChannelChoice, -): Promise { - switch (channel) { - case "discord": - return discordPlugin as ChannelPlugin; - case "googlechat": - return googlechatPlugin as ChannelPlugin; - case "imessage": - return imessagePlugin as ChannelPlugin; - case "irc": - return ircPlugin as ChannelPlugin; - case "line": - return linePlugin as ChannelPlugin; - case "signal": - return signalPlugin as ChannelPlugin; - case "slack": - return slackPlugin as ChannelPlugin; - case "telegram": - return telegramPlugin as ChannelPlugin; - case "whatsapp": - return whatsappPlugin as ChannelPlugin; - default: - return undefined; - } -} - -// Legacy aliases (pre-rename). -export const getProviderOnboardingAdapter = getChannelOnboardingAdapter; -export const listProviderOnboardingAdapters = listChannelOnboardingAdapters; diff --git a/src/commands/channel-test-helpers.ts b/src/commands/channel-test-helpers.ts index 97167228e7f..7a6d687a91c 100644 --- a/src/commands/channel-test-helpers.ts +++ b/src/commands/channel-test-helpers.ts @@ -6,22 +6,22 @@ import { telegramPlugin } from "../../extensions/telegram/src/channel.js"; import { whatsappPlugin } from "../../extensions/whatsapp/src/channel.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; import { createTestRegistry } from "../test-utils/channel-plugins.js"; -import { getChannelOnboardingAdapter } from "./channel-setup/registry.js"; +import { getChannelSetupFlowAdapter } from "./channel-setup/registry.js"; +import type { ChannelSetupFlowAdapter } from "./channel-setup/types.js"; import type { ChannelChoice } from "./onboard-types.js"; -import type { ChannelOnboardingAdapter } from "./onboarding/types.js"; -type ChannelOnboardingAdapterPatch = Partial< +type ChannelSetupFlowAdapterPatch = Partial< Pick< - ChannelOnboardingAdapter, + ChannelSetupFlowAdapter, "configure" | "configureInteractive" | "configureWhenConfigured" | "getStatus" > >; -type PatchedOnboardingAdapterFields = { - configure?: ChannelOnboardingAdapter["configure"]; - configureInteractive?: ChannelOnboardingAdapter["configureInteractive"]; - configureWhenConfigured?: ChannelOnboardingAdapter["configureWhenConfigured"]; - getStatus?: ChannelOnboardingAdapter["getStatus"]; +type PatchedSetupAdapterFields = { + configure?: ChannelSetupFlowAdapter["configure"]; + configureInteractive?: ChannelSetupFlowAdapter["configureInteractive"]; + configureWhenConfigured?: ChannelSetupFlowAdapter["configureWhenConfigured"]; + getStatus?: ChannelSetupFlowAdapter["getStatus"]; }; export function setDefaultChannelPluginRegistryForTests(): void { @@ -36,16 +36,16 @@ export function setDefaultChannelPluginRegistryForTests(): void { setActivePluginRegistry(createTestRegistry(channels)); } -export function patchChannelOnboardingAdapter( +export function patchChannelSetupFlowAdapter( channel: ChannelChoice, - patch: ChannelOnboardingAdapterPatch, + patch: ChannelSetupFlowAdapterPatch, ): () => void { - const adapter = getChannelOnboardingAdapter(channel); + const adapter = getChannelSetupFlowAdapter(channel); if (!adapter) { - throw new Error(`missing onboarding adapter for ${channel}`); + throw new Error(`missing setup adapter for ${channel}`); } - const previous: PatchedOnboardingAdapterFields = {}; + const previous: PatchedSetupAdapterFields = {}; if (Object.prototype.hasOwnProperty.call(patch, "getStatus")) { previous.getStatus = adapter.getStatus; diff --git a/src/commands/channels/add.ts b/src/commands/channels/add.ts index 30fe44f1b54..d4175cf100b 100644 --- a/src/commands/channels/add.ts +++ b/src/commands/channels/add.ts @@ -2,7 +2,7 @@ import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/ag import { listChannelPluginCatalogEntries } from "../../channels/plugins/catalog.js"; import { parseOptionalDelimitedEntries } from "../../channels/plugins/helpers.js"; import { getChannelPlugin, normalizeChannelId } from "../../channels/plugins/index.js"; -import type { ChannelOnboardingSetupPlugin } from "../../channels/plugins/onboarding-types.js"; +import type { ChannelOnboardingSetupPlugin } from "../../channels/plugins/setup-flow-types.js"; import { moveSingleAccountChannelSectionToDefaultAccount } from "../../channels/plugins/setup-helpers.js"; import type { ChannelId, ChannelPlugin, ChannelSetupInput } from "../../channels/plugins/types.js"; import { writeConfigFile, type OpenClawConfig } from "../../config/config.js"; diff --git a/src/commands/onboard-channels.e2e.test.ts b/src/commands/onboard-channels.e2e.test.ts index 0f2fb4c2e1e..faf1e7cfb7e 100644 --- a/src/commands/onboard-channels.e2e.test.ts +++ b/src/commands/onboard-channels.e2e.test.ts @@ -5,7 +5,7 @@ import { createEmptyPluginRegistry } from "../plugins/registry.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; import type { WizardPrompter } from "../wizard/prompts.js"; import { - patchChannelOnboardingAdapter, + patchChannelSetupFlowAdapter, setDefaultChannelPluginRegistryForTests, } from "./channel-test-helpers.js"; import { setupChannels } from "./onboard-channels.js"; @@ -96,8 +96,8 @@ function createTelegramCfg(botToken: string, enabled?: boolean): OpenClawConfig } as OpenClawConfig; } -function patchTelegramAdapter(overrides: Parameters[1]) { - return patchChannelOnboardingAdapter("telegram", { +function patchTelegramAdapter(overrides: Parameters[1]) { + return patchChannelSetupFlowAdapter("telegram", { ...overrides, getStatus: overrides.getStatus ?? @@ -277,7 +277,7 @@ describe("setupChannels", () => { expect(multiselect).not.toHaveBeenCalled(); }); - it("continues Telegram onboarding even when plugin registry is empty (avoids 'plugin not available' block)", async () => { + it("continues Telegram setup when the plugin registry is empty", async () => { // Simulate missing registry entries (the scenario reported in #25545). setActivePluginRegistry(createEmptyPluginRegistry()); // Avoid accidental env-token configuration changing the prompt path. @@ -311,11 +311,7 @@ describe("setupChannels", () => { ); }); expect(sawHardStop).toBe(false); - expect(loadOnboardingPluginRegistrySnapshotForChannel).toHaveBeenCalledWith( - expect.objectContaining({ - channel: "telegram", - }), - ); + expect(loadOnboardingPluginRegistrySnapshotForChannel).not.toHaveBeenCalled(); expect(reloadOnboardingPluginRegistry).not.toHaveBeenCalled(); }); diff --git a/src/commands/onboard-channels.ts b/src/commands/onboard-channels.ts index ffc4932f7b8..67c78e7a72c 100644 --- a/src/commands/onboard-channels.ts +++ b/src/commands/onboard-channels.ts @@ -1,7 +1,7 @@ import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js"; import { listChannelPluginCatalogEntries } from "../channels/plugins/catalog.js"; import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js"; -import type { ChannelOnboardingSetupPlugin } from "../channels/plugins/onboarding-types.js"; +import type { ChannelOnboardingSetupPlugin } from "../channels/plugins/setup-flow-types.js"; import { getChannelSetupPlugin, listChannelSetupPlugins, @@ -21,23 +21,20 @@ import type { RuntimeEnv } from "../runtime.js"; import { formatDocsLink } from "../terminal/links.js"; import type { WizardPrompter, WizardSelectOption } from "../wizard/prompts.js"; import { resolveChannelSetupEntries } from "./channel-setup/discovery.js"; -import { - loadBundledChannelOnboardingPlugin, - resolveChannelOnboardingAdapterForPlugin, -} from "./channel-setup/registry.js"; +import { resolveChannelSetupFlowAdapterForPlugin } from "./channel-setup/registry.js"; +import type { + ChannelSetupFlowAdapter, + ChannelSetupConfiguredResult, + ChannelSetupDmPolicy, + ChannelSetupResult, + ChannelSetupStatus, + SetupChannelsOptions, +} from "./channel-setup/types.js"; import type { ChannelChoice } from "./onboard-types.js"; import { ensureOnboardingPluginInstalled, loadOnboardingPluginRegistrySnapshotForChannel, } from "./onboarding/plugin-install.js"; -import type { - ChannelOnboardingAdapter, - ChannelOnboardingConfiguredResult, - ChannelOnboardingDmPolicy, - ChannelOnboardingResult, - ChannelOnboardingStatus, - SetupChannelsOptions, -} from "./onboarding/types.js"; type ConfiguredChannelAction = "update" | "disable" | "delete" | "skip"; @@ -45,7 +42,7 @@ type ChannelStatusSummary = { installedPlugins: ReturnType; catalogEntries: ReturnType; installedCatalogEntries: ReturnType; - statusByChannel: Map; + statusByChannel: Map; statusLines: string[]; }; @@ -122,7 +119,7 @@ async function collectChannelStatus(params: { options?: SetupChannelsOptions; accountOverrides: Partial>; installedPlugins?: ChannelOnboardingSetupPlugin[]; - resolveAdapter?: (channel: ChannelChoice) => ChannelOnboardingAdapter | undefined; + resolveAdapter?: (channel: ChannelChoice) => ChannelSetupFlowAdapter | undefined; }): Promise { const installedPlugins = params.installedPlugins ?? listChannelSetupPlugins(); const workspaceDir = resolveAgentWorkspaceDir(params.cfg, resolveDefaultAgentId(params.cfg)); @@ -134,7 +131,7 @@ async function collectChannelStatus(params: { const resolveAdapter = params.resolveAdapter ?? ((channel: ChannelChoice) => - resolveChannelOnboardingAdapterForPlugin( + resolveChannelSetupFlowAdapterForPlugin( installedPlugins.find((plugin) => plugin.id === channel), )); const statusEntries = await Promise.all( @@ -274,13 +271,13 @@ async function maybeConfigureDmPolicies(params: { selection: ChannelChoice[]; prompter: WizardPrompter; accountIdsByChannel?: Map; - resolveAdapter?: (channel: ChannelChoice) => ChannelOnboardingAdapter | undefined; + resolveAdapter?: (channel: ChannelChoice) => ChannelSetupFlowAdapter | undefined; }): Promise { const { selection, prompter, accountIdsByChannel } = params; const resolve = params.resolveAdapter ?? (() => undefined); const dmPolicies = selection - .map((channel) => resolve?.(channel)?.dmPolicy) - .filter(Boolean) as ChannelOnboardingDmPolicy[]; + .map((channel) => resolve(channel)?.dmPolicy) + .filter(Boolean) as ChannelSetupDmPolicy[]; if (dmPolicies.length === 0) { return params.cfg; } @@ -294,7 +291,7 @@ async function maybeConfigureDmPolicies(params: { } let cfg = params.cfg; - const selectPolicy = async (policy: ChannelOnboardingDmPolicy) => { + const selectPolicy = async (policy: ChannelSetupDmPolicy) => { await prompter.note( [ "Default: pairing (unknown DMs get a pairing code).", @@ -337,7 +334,7 @@ async function maybeConfigureDmPolicies(params: { return cfg; } -// Channel-specific prompts moved into onboarding adapters. +// Channel-specific prompts moved into setup flow adapters. export async function setupChannels( cfg: OpenClawConfig, @@ -393,21 +390,17 @@ export async function setupChannels( rememberScopedPlugin(plugin); return plugin; } - const bundledPlugin = await loadBundledChannelOnboardingPlugin(channel); - if (bundledPlugin) { - rememberScopedPlugin(bundledPlugin); - } - return bundledPlugin; + return undefined; }; - const getVisibleOnboardingAdapter = (channel: ChannelChoice) => { + const getVisibleSetupFlowAdapter = (channel: ChannelChoice) => { const scopedPlugin = scopedPluginsById.get(channel); if (scopedPlugin) { - return resolveChannelOnboardingAdapterForPlugin(scopedPlugin); + return resolveChannelSetupFlowAdapterForPlugin(scopedPlugin); } - return resolveChannelOnboardingAdapterForPlugin(getChannelSetupPlugin(channel)); + return resolveChannelSetupFlowAdapterForPlugin(getChannelSetupPlugin(channel)); }; const preloadConfiguredExternalPlugins = () => { - // Keep onboarding memory bounded by snapshot-loading only configured external plugins. + // Keep setup memory bounded by snapshot-loading only configured external plugins. const workspaceDir = resolveWorkspaceDir(); for (const entry of listChannelPluginCatalogEntries({ workspaceDir })) { const channel = entry.id as ChannelChoice; @@ -438,7 +431,7 @@ export async function setupChannels( options, accountOverrides, installedPlugins: listVisibleInstalledPlugins(), - resolveAdapter: getVisibleOnboardingAdapter, + resolveAdapter: getVisibleSetupFlowAdapter, }); if (!options?.skipStatusNote && statusLines.length > 0) { await prompter.note(statusLines.join("\n"), "Channel status"); @@ -493,7 +486,7 @@ export async function setupChannels( const accountIdsByChannel = new Map(); const recordAccount = (channel: ChannelChoice, accountId: string) => { options?.onAccountId?.(channel, accountId); - const adapter = getVisibleOnboardingAdapter(channel); + const adapter = getVisibleSetupFlowAdapter(channel); adapter?.onAccountRecorded?.(accountId, options); accountIdsByChannel.set(channel, accountId); }; @@ -566,7 +559,7 @@ export async function setupChannels( }; const refreshStatus = async (channel: ChannelChoice) => { - const adapter = getVisibleOnboardingAdapter(channel); + const adapter = getVisibleSetupFlowAdapter(channel); if (!adapter) { return; } @@ -589,11 +582,11 @@ export async function setupChannels( return false; } const plugin = await loadScopedChannelPlugin(channel); - const adapter = getVisibleOnboardingAdapter(channel); + const adapter = getVisibleSetupFlowAdapter(channel); if (!plugin) { if (adapter) { await prompter.note( - `${channel} plugin not available (continuing with onboarding). If the channel still doesn't work after setup, run \`${formatCliCommand( + `${channel} plugin not available (continuing with setup). If the channel still doesn't work after setup, run \`${formatCliCommand( "openclaw plugins list", )}\` and \`${formatCliCommand("openclaw plugins enable " + channel)}\`, then restart the gateway.`, "Channel setup", @@ -608,7 +601,7 @@ export async function setupChannels( return true; }; - const applyOnboardingResult = async (channel: ChannelChoice, result: ChannelOnboardingResult) => { + const applySetupResult = async (channel: ChannelChoice, result: ChannelSetupResult) => { next = result.cfg; if (result.accountId) { recordAccount(channel, result.accountId); @@ -617,21 +610,21 @@ export async function setupChannels( await refreshStatus(channel); }; - const applyCustomOnboardingResult = async ( + const applyCustomSetupResult = async ( channel: ChannelChoice, - result: ChannelOnboardingConfiguredResult, + result: ChannelSetupConfiguredResult, ) => { if (result === "skip") { return false; } - await applyOnboardingResult(channel, result); + await applySetupResult(channel, result); return true; }; const configureChannel = async (channel: ChannelChoice) => { - const adapter = getVisibleOnboardingAdapter(channel); + const adapter = getVisibleSetupFlowAdapter(channel); if (!adapter) { - await prompter.note(`${channel} does not support onboarding yet.`, "Channel setup"); + await prompter.note(`${channel} does not support guided setup yet.`, "Channel setup"); return; } const result = await adapter.configure({ @@ -643,12 +636,12 @@ export async function setupChannels( shouldPromptAccountIds, forceAllowFrom: forceAllowFromChannels.has(channel), }); - await applyOnboardingResult(channel, result); + await applySetupResult(channel, result); }; const handleConfiguredChannel = async (channel: ChannelChoice, label: string) => { const plugin = getVisibleChannelPlugin(channel); - const adapter = getVisibleOnboardingAdapter(channel); + const adapter = getVisibleSetupFlowAdapter(channel); if (adapter?.configureWhenConfigured) { const custom = await adapter.configureWhenConfigured({ cfg: next, @@ -661,7 +654,7 @@ export async function setupChannels( configured: true, label, }); - if (!(await applyCustomOnboardingResult(channel, custom))) { + if (!(await applyCustomSetupResult(channel, custom))) { return; } return; @@ -772,7 +765,7 @@ export async function setupChannels( } const plugin = getVisibleChannelPlugin(channel); - const adapter = getVisibleOnboardingAdapter(channel); + const adapter = getVisibleSetupFlowAdapter(channel); const label = plugin?.meta.label ?? catalogEntry?.meta.label ?? channel; const status = statusByChannel.get(channel); const configured = status?.configured ?? false; @@ -788,7 +781,7 @@ export async function setupChannels( configured, label, }); - if (!(await applyCustomOnboardingResult(channel, custom))) { + if (!(await applyCustomSetupResult(channel, custom))) { return; } return; @@ -861,7 +854,7 @@ export async function setupChannels( selection, prompter, accountIdsByChannel, - resolveAdapter: getVisibleOnboardingAdapter, + resolveAdapter: getVisibleSetupFlowAdapter, }); }