refactor: move setup fallback into setup registry

This commit is contained in:
Peter Steinberger 2026-03-15 20:39:29 -07:00
parent 77d0ff629c
commit de503dbcbb
No known key found for this signature in database
8 changed files with 187 additions and 160 deletions

View File

@ -0,0 +1,5 @@
import { lineSetupPlugin } from "./src/channel.setup.js";
export default {
plugin: lineSetupPlugin,
};

View File

@ -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<ResolvedLineAccount> = {
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,
};

View File

@ -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<string>();
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<string, ChannelPlugin>();
for (const plugin of sorted) {
byId.set(plugin.id, plugin);

View File

@ -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<object, ChannelSetupFlowAdapter>();
export type ChannelOnboardingSetupPlugin = Pick<
ChannelPlugin,
"id" | "meta" | "capabilities" | "config" | "setup" | "setupWizard"
>;
const setupWizardAdapters = new WeakMap<object, ChannelOnboardingAdapter>();
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<ChannelChoice, ChannelOnboardingAdapter>();
const setupPlugins = listChannelSetupPlugins();
const plugins =
setupPlugins.length > 0
? setupPlugins
: (EMPTY_REGISTRY_FALLBACK_PLUGINS as unknown as ReturnType<typeof listChannelSetupPlugins>);
for (const plugin of plugins) {
const adapter = resolveChannelOnboardingAdapterForPlugin(plugin);
const CHANNEL_SETUP_FLOW_ADAPTERS = () => {
const adapters = new Map<ChannelChoice, ChannelSetupFlowAdapter>();
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<ChannelOnboardingSetupPlugin | undefined> {
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;

View File

@ -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;

View File

@ -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";

View File

@ -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<typeof patchChannelOnboardingAdapter>[1]) {
return patchChannelOnboardingAdapter("telegram", {
function patchTelegramAdapter(overrides: Parameters<typeof patchChannelSetupFlowAdapter>[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();
});

View File

@ -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<typeof listChannelSetupPlugins>;
catalogEntries: ReturnType<typeof listChannelPluginCatalogEntries>;
installedCatalogEntries: ReturnType<typeof listChannelPluginCatalogEntries>;
statusByChannel: Map<ChannelChoice, ChannelOnboardingStatus>;
statusByChannel: Map<ChannelChoice, ChannelSetupStatus>;
statusLines: string[];
};
@ -122,7 +119,7 @@ async function collectChannelStatus(params: {
options?: SetupChannelsOptions;
accountOverrides: Partial<Record<ChannelChoice, string>>;
installedPlugins?: ChannelOnboardingSetupPlugin[];
resolveAdapter?: (channel: ChannelChoice) => ChannelOnboardingAdapter | undefined;
resolveAdapter?: (channel: ChannelChoice) => ChannelSetupFlowAdapter | undefined;
}): Promise<ChannelStatusSummary> {
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<ChannelChoice, string>;
resolveAdapter?: (channel: ChannelChoice) => ChannelOnboardingAdapter | undefined;
resolveAdapter?: (channel: ChannelChoice) => ChannelSetupFlowAdapter | undefined;
}): Promise<OpenClawConfig> {
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<ChannelChoice, string>();
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,
});
}