refactor: share onboarding secret prompt flows

This commit is contained in:
Peter Steinberger
2026-03-10 20:28:25 +00:00
parent 00170f8e1a
commit 725958c66f
9 changed files with 239 additions and 215 deletions

View File

@@ -1,16 +1,14 @@
import {
buildSingleChannelSecretPromptState,
formatDocsLink,
hasConfiguredSecretInput,
mapAllowFromEntries,
mergeAllowFromEntries,
patchScopedAccountConfig,
promptSingleChannelSecretInput,
runSingleChannelSecretStep,
resolveAccountIdForConfigure,
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
setTopLevelChannelDmPolicyWithAllowFrom,
type SecretInput,
type ChannelOnboardingAdapter,
type ChannelOnboardingDmPolicy,
type OpenClawConfig,
@@ -190,12 +188,6 @@ export const nextcloudTalkOnboardingAdapter: ChannelOnboardingAdapter = {
hasConfiguredSecretInput(resolvedAccount.config.botSecret) ||
resolvedAccount.config.botSecretFile,
);
const secretPromptState = buildSingleChannelSecretPromptState({
accountConfigured,
hasConfigToken: hasConfigSecret,
allowEnv,
envValue: process.env.NEXTCLOUD_TALK_BOT_SECRET,
});
let baseUrl = resolvedAccount.baseUrl;
if (!baseUrl) {
@@ -216,32 +208,35 @@ export const nextcloudTalkOnboardingAdapter: ChannelOnboardingAdapter = {
).trim();
}
let secret: SecretInput | null = null;
if (!accountConfigured) {
await noteNextcloudTalkSecretHelp(prompter);
}
const secretResult = await promptSingleChannelSecretInput({
const secretStep = await runSingleChannelSecretStep({
cfg: next,
prompter,
providerHint: "nextcloud-talk",
credentialLabel: "bot secret",
accountConfigured: secretPromptState.accountConfigured,
canUseEnv: secretPromptState.canUseEnv,
hasConfigToken: secretPromptState.hasConfigToken,
accountConfigured,
hasConfigToken: hasConfigSecret,
allowEnv,
envValue: process.env.NEXTCLOUD_TALK_BOT_SECRET,
envPrompt: "NEXTCLOUD_TALK_BOT_SECRET detected. Use env var?",
keepPrompt: "Nextcloud Talk bot secret already configured. Keep it?",
inputPrompt: "Enter Nextcloud Talk bot secret",
preferredEnvVar: "NEXTCLOUD_TALK_BOT_SECRET",
onMissingConfigured: async () => await noteNextcloudTalkSecretHelp(prompter),
applyUseEnv: async (cfg) =>
setNextcloudTalkAccountConfig(cfg as CoreConfig, accountId, {
baseUrl,
}),
applySet: async (cfg, value) =>
setNextcloudTalkAccountConfig(cfg as CoreConfig, accountId, {
baseUrl,
botSecret: value,
}),
});
if (secretResult.action === "set") {
secret = secretResult.value;
}
next = secretStep.cfg;
if (secretResult.action === "use-env" || secret || baseUrl !== resolvedAccount.baseUrl) {
if (secretStep.action === "keep" && baseUrl !== resolvedAccount.baseUrl) {
next = setNextcloudTalkAccountConfig(next, accountId, {
baseUrl,
...(secret ? { botSecret: secret } : {}),
});
}
@@ -262,26 +257,28 @@ export const nextcloudTalkOnboardingAdapter: ChannelOnboardingAdapter = {
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
}),
).trim();
const apiPasswordResult = await promptSingleChannelSecretInput({
const apiPasswordStep = await runSingleChannelSecretStep({
cfg: next,
prompter,
providerHint: "nextcloud-talk-api",
credentialLabel: "API password",
...buildSingleChannelSecretPromptState({
accountConfigured: Boolean(existingApiUser && existingApiPasswordConfigured),
hasConfigToken: existingApiPasswordConfigured,
allowEnv: false,
}),
accountConfigured: Boolean(existingApiUser && existingApiPasswordConfigured),
hasConfigToken: existingApiPasswordConfigured,
allowEnv: false,
envPrompt: "",
keepPrompt: "Nextcloud Talk API password already configured. Keep it?",
inputPrompt: "Enter Nextcloud Talk API password",
preferredEnvVar: "NEXTCLOUD_TALK_API_PASSWORD",
applySet: async (cfg, value) =>
setNextcloudTalkAccountConfig(cfg as CoreConfig, accountId, {
apiUser,
apiPassword: value,
}),
});
const apiPassword = apiPasswordResult.action === "set" ? apiPasswordResult.value : undefined;
next = setNextcloudTalkAccountConfig(next, accountId, {
apiUser,
...(apiPassword ? { apiPassword } : {}),
});
next =
apiPasswordStep.action === "keep"
? setNextcloudTalkAccountConfig(next, accountId, { apiUser })
: apiPasswordStep.cfg;
}
if (forceAllowFrom) {

View File

@@ -12,6 +12,7 @@ import {
mergeAllowFromEntries,
normalizeAccountId,
promptSingleChannelSecretInput,
runSingleChannelSecretStep,
resolveAccountIdForConfigure,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "openclaw/plugin-sdk/zalo";
@@ -255,80 +256,66 @@ export const zaloOnboardingAdapter: ChannelOnboardingAdapter = {
const hasConfigToken = Boolean(
hasConfiguredSecretInput(resolvedAccount.config.botToken) || resolvedAccount.config.tokenFile,
);
const tokenPromptState = buildSingleChannelSecretPromptState({
accountConfigured,
hasConfigToken,
allowEnv,
envValue: process.env.ZALO_BOT_TOKEN,
});
let token: SecretInput | null = null;
if (!accountConfigured) {
await noteZaloTokenHelp(prompter);
}
const tokenResult = await promptSingleChannelSecretInput({
const tokenStep = await runSingleChannelSecretStep({
cfg: next,
prompter,
providerHint: "zalo",
credentialLabel: "bot token",
accountConfigured: tokenPromptState.accountConfigured,
canUseEnv: tokenPromptState.canUseEnv,
hasConfigToken: tokenPromptState.hasConfigToken,
accountConfigured,
hasConfigToken,
allowEnv,
envValue: process.env.ZALO_BOT_TOKEN,
envPrompt: "ZALO_BOT_TOKEN detected. Use env var?",
keepPrompt: "Zalo token already configured. Keep it?",
inputPrompt: "Enter Zalo bot token",
preferredEnvVar: "ZALO_BOT_TOKEN",
});
if (tokenResult.action === "set") {
token = tokenResult.value;
}
if (tokenResult.action === "use-env" && zaloAccountId === DEFAULT_ACCOUNT_ID) {
next = {
...next,
channels: {
...next.channels,
zalo: {
...next.channels?.zalo,
enabled: true,
},
},
} as OpenClawConfig;
}
if (token) {
if (zaloAccountId === DEFAULT_ACCOUNT_ID) {
next = {
...next,
channels: {
...next.channels,
zalo: {
...next.channels?.zalo,
enabled: true,
botToken: token,
},
},
} as OpenClawConfig;
} else {
next = {
...next,
channels: {
...next.channels,
zalo: {
...next.channels?.zalo,
enabled: true,
accounts: {
...next.channels?.zalo?.accounts,
[zaloAccountId]: {
...next.channels?.zalo?.accounts?.[zaloAccountId],
onMissingConfigured: async () => await noteZaloTokenHelp(prompter),
applyUseEnv: async (cfg) =>
zaloAccountId === DEFAULT_ACCOUNT_ID
? ({
...cfg,
channels: {
...cfg.channels,
zalo: {
...cfg.channels?.zalo,
enabled: true,
botToken: token,
},
},
},
},
} as OpenClawConfig;
}
}
} as OpenClawConfig)
: cfg,
applySet: async (cfg, value) =>
zaloAccountId === DEFAULT_ACCOUNT_ID
? ({
...cfg,
channels: {
...cfg.channels,
zalo: {
...cfg.channels?.zalo,
enabled: true,
botToken: value,
},
},
} as OpenClawConfig)
: ({
...cfg,
channels: {
...cfg.channels,
zalo: {
...cfg.channels?.zalo,
enabled: true,
accounts: {
...cfg.channels?.zalo?.accounts,
[zaloAccountId]: {
...cfg.channels?.zalo?.accounts?.[zaloAccountId],
enabled: true,
botToken: value,
},
},
},
},
} as OpenClawConfig),
});
next = tokenStep.cfg;
const wantsWebhook = await prompter.confirm({
message: "Use webhook mode for Zalo?",

View File

@@ -20,15 +20,14 @@ import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onb
import { configureChannelAccessWithAllowlist } from "./channel-access-configure.js";
import {
applySingleTokenPromptResult,
buildSingleChannelSecretPromptState,
parseMentionOrPrefixedId,
noteChannelLookupFailure,
noteChannelLookupSummary,
patchChannelConfigForAccount,
promptLegacyChannelAllowFrom,
promptSingleChannelSecretInput,
resolveAccountIdForConfigure,
resolveOnboardingAccountId,
runSingleChannelSecretStep,
setAccountGroupPolicyForChannel,
setLegacyChannelDmPolicyWithAllowFrom,
setOnboardingChannelEnabled,
@@ -179,52 +178,39 @@ export const discordOnboardingAdapter: ChannelOnboardingAdapter = {
accountId: discordAccountId,
});
const allowEnv = discordAccountId === DEFAULT_ACCOUNT_ID;
const tokenPromptState = buildSingleChannelSecretPromptState({
accountConfigured: Boolean(resolvedAccount.token),
hasConfigToken: hasConfiguredSecretInput(resolvedAccount.config.token),
allowEnv,
envValue: process.env.DISCORD_BOT_TOKEN,
});
if (!tokenPromptState.accountConfigured) {
await noteDiscordTokenHelp(prompter);
}
const tokenResult = await promptSingleChannelSecretInput({
const tokenStep = await runSingleChannelSecretStep({
cfg: next,
prompter,
providerHint: "discord",
credentialLabel: "Discord bot token",
secretInputMode: options?.secretInputMode,
accountConfigured: tokenPromptState.accountConfigured,
canUseEnv: tokenPromptState.canUseEnv,
hasConfigToken: tokenPromptState.hasConfigToken,
accountConfigured: Boolean(resolvedAccount.token),
hasConfigToken: hasConfiguredSecretInput(resolvedAccount.config.token),
allowEnv,
envValue: process.env.DISCORD_BOT_TOKEN,
envPrompt: "DISCORD_BOT_TOKEN detected. Use env var?",
keepPrompt: "Discord token already configured. Keep it?",
inputPrompt: "Enter Discord bot token",
preferredEnvVar: allowEnv ? "DISCORD_BOT_TOKEN" : undefined,
onMissingConfigured: async () => await noteDiscordTokenHelp(prompter),
applyUseEnv: async (cfg) =>
applySingleTokenPromptResult({
cfg,
channel: "discord",
accountId: discordAccountId,
tokenPatchKey: "token",
tokenResult: { useEnv: true, token: null },
}),
applySet: async (cfg, value) =>
applySingleTokenPromptResult({
cfg,
channel: "discord",
accountId: discordAccountId,
tokenPatchKey: "token",
tokenResult: { useEnv: false, token: value },
}),
});
let resolvedTokenForAllowlist: string | undefined;
if (tokenResult.action === "use-env") {
next = applySingleTokenPromptResult({
cfg: next,
channel: "discord",
accountId: discordAccountId,
tokenPatchKey: "token",
tokenResult: { useEnv: true, token: null },
});
resolvedTokenForAllowlist = process.env.DISCORD_BOT_TOKEN?.trim() || undefined;
} else if (tokenResult.action === "set") {
next = applySingleTokenPromptResult({
cfg: next,
channel: "discord",
accountId: discordAccountId,
tokenPatchKey: "token",
tokenResult: { useEnv: false, token: tokenResult.value },
});
resolvedTokenForAllowlist = tokenResult.resolvedValue;
}
next = tokenStep.cfg;
const currentEntries = Object.entries(resolvedAccount.config.guilds ?? {}).flatMap(
([guildKey, value]) => {
@@ -261,7 +247,7 @@ export const discordOnboardingAdapter: ChannelOnboardingAdapter = {
input,
resolved: false,
}));
const activeToken = accountWithTokens.token || resolvedTokenForAllowlist || "";
const activeToken = accountWithTokens.token || tokenStep.resolvedValue || "";
if (activeToken && entries.length > 0) {
try {
resolved = await resolveDiscordChannelAllowlist({

View File

@@ -482,6 +482,82 @@ export type SingleChannelSecretInputPromptResult =
| { action: "use-env" }
| { action: "set"; value: SecretInput; resolvedValue: string };
export async function runSingleChannelSecretStep(params: {
cfg: OpenClawConfig;
prompter: Pick<WizardPrompter, "confirm" | "text" | "select" | "note">;
providerHint: string;
credentialLabel: string;
secretInputMode?: "plaintext" | "ref";
accountConfigured: boolean;
hasConfigToken: boolean;
allowEnv: boolean;
envValue?: string;
envPrompt: string;
keepPrompt: string;
inputPrompt: string;
preferredEnvVar?: string;
onMissingConfigured?: () => Promise<void>;
applyUseEnv?: (cfg: OpenClawConfig) => OpenClawConfig | Promise<OpenClawConfig>;
applySet?: (
cfg: OpenClawConfig,
value: SecretInput,
resolvedValue: string,
) => OpenClawConfig | Promise<OpenClawConfig>;
}): Promise<{
cfg: OpenClawConfig;
action: SingleChannelSecretInputPromptResult["action"];
resolvedValue?: string;
}> {
const promptState = buildSingleChannelSecretPromptState({
accountConfigured: params.accountConfigured,
hasConfigToken: params.hasConfigToken,
allowEnv: params.allowEnv,
envValue: params.envValue,
});
if (!promptState.accountConfigured && params.onMissingConfigured) {
await params.onMissingConfigured();
}
const result = await promptSingleChannelSecretInput({
cfg: params.cfg,
prompter: params.prompter,
providerHint: params.providerHint,
credentialLabel: params.credentialLabel,
secretInputMode: params.secretInputMode,
accountConfigured: promptState.accountConfigured,
canUseEnv: promptState.canUseEnv,
hasConfigToken: promptState.hasConfigToken,
envPrompt: params.envPrompt,
keepPrompt: params.keepPrompt,
inputPrompt: params.inputPrompt,
preferredEnvVar: params.preferredEnvVar,
});
if (result.action === "use-env") {
return {
cfg: params.applyUseEnv ? await params.applyUseEnv(params.cfg) : params.cfg,
action: result.action,
resolvedValue: params.envValue?.trim() || undefined,
};
}
if (result.action === "set") {
return {
cfg: params.applySet
? await params.applySet(params.cfg, result.value, result.resolvedValue)
: params.cfg,
action: result.action,
resolvedValue: result.resolvedValue,
};
}
return {
cfg: params.cfg,
action: result.action,
};
}
export async function promptSingleChannelSecretInput(params: {
cfg: OpenClawConfig;
prompter: Pick<WizardPrompter, "confirm" | "text" | "select" | "note">;

View File

@@ -14,15 +14,14 @@ import type { WizardPrompter } from "../../../wizard/prompts.js";
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
import { configureChannelAccessWithAllowlist } from "./channel-access-configure.js";
import {
buildSingleChannelSecretPromptState,
parseMentionOrPrefixedId,
noteChannelLookupFailure,
noteChannelLookupSummary,
patchChannelConfigForAccount,
promptLegacyChannelAllowFrom,
promptSingleChannelSecretInput,
resolveAccountIdForConfigure,
resolveOnboardingAccountId,
runSingleChannelSecretStep,
setAccountGroupPolicyForChannel,
setLegacyChannelDmPolicyWithAllowFrom,
setOnboardingChannelEnabled,
@@ -235,18 +234,6 @@ export const slackOnboardingAdapter: ChannelOnboardingAdapter = {
const accountConfigured =
Boolean(resolvedAccount.botToken && resolvedAccount.appToken) || hasConfigTokens;
const allowEnv = slackAccountId === DEFAULT_ACCOUNT_ID;
const botPromptState = buildSingleChannelSecretPromptState({
accountConfigured: Boolean(resolvedAccount.botToken) || hasConfiguredBotToken,
hasConfigToken: hasConfiguredBotToken,
allowEnv,
envValue: process.env.SLACK_BOT_TOKEN,
});
const appPromptState = buildSingleChannelSecretPromptState({
accountConfigured: Boolean(resolvedAccount.appToken) || hasConfiguredAppToken,
hasConfigToken: hasConfiguredAppToken,
allowEnv,
envValue: process.env.SLACK_APP_TOKEN,
});
let resolvedBotTokenForAllowlist = resolvedAccount.botToken;
const slackBotName = String(
await prompter.text({
@@ -257,54 +244,56 @@ export const slackOnboardingAdapter: ChannelOnboardingAdapter = {
if (!accountConfigured) {
await noteSlackTokenHelp(prompter, slackBotName);
}
const botTokenResult = await promptSingleChannelSecretInput({
const botTokenStep = await runSingleChannelSecretStep({
cfg: next,
prompter,
providerHint: "slack-bot",
credentialLabel: "Slack bot token",
secretInputMode: options?.secretInputMode,
accountConfigured: botPromptState.accountConfigured,
canUseEnv: botPromptState.canUseEnv,
hasConfigToken: botPromptState.hasConfigToken,
accountConfigured: Boolean(resolvedAccount.botToken) || hasConfiguredBotToken,
hasConfigToken: hasConfiguredBotToken,
allowEnv,
envValue: process.env.SLACK_BOT_TOKEN,
envPrompt: "SLACK_BOT_TOKEN detected. Use env var?",
keepPrompt: "Slack bot token already configured. Keep it?",
inputPrompt: "Enter Slack bot token (xoxb-...)",
preferredEnvVar: allowEnv ? "SLACK_BOT_TOKEN" : undefined,
applySet: async (cfg, value) =>
patchChannelConfigForAccount({
cfg,
channel: "slack",
accountId: slackAccountId,
patch: { botToken: value },
}),
});
if (botTokenResult.action === "use-env") {
resolvedBotTokenForAllowlist = process.env.SLACK_BOT_TOKEN?.trim() || undefined;
} else if (botTokenResult.action === "set") {
next = patchChannelConfigForAccount({
cfg: next,
channel: "slack",
accountId: slackAccountId,
patch: { botToken: botTokenResult.value },
});
resolvedBotTokenForAllowlist = botTokenResult.resolvedValue;
next = botTokenStep.cfg;
if (botTokenStep.resolvedValue) {
resolvedBotTokenForAllowlist = botTokenStep.resolvedValue;
}
const appTokenResult = await promptSingleChannelSecretInput({
const appTokenStep = await runSingleChannelSecretStep({
cfg: next,
prompter,
providerHint: "slack-app",
credentialLabel: "Slack app token",
secretInputMode: options?.secretInputMode,
accountConfigured: appPromptState.accountConfigured,
canUseEnv: appPromptState.canUseEnv,
hasConfigToken: appPromptState.hasConfigToken,
accountConfigured: Boolean(resolvedAccount.appToken) || hasConfiguredAppToken,
hasConfigToken: hasConfiguredAppToken,
allowEnv,
envValue: process.env.SLACK_APP_TOKEN,
envPrompt: "SLACK_APP_TOKEN detected. Use env var?",
keepPrompt: "Slack app token already configured. Keep it?",
inputPrompt: "Enter Slack app token (xapp-...)",
preferredEnvVar: allowEnv ? "SLACK_APP_TOKEN" : undefined,
applySet: async (cfg, value) =>
patchChannelConfigForAccount({
cfg,
channel: "slack",
accountId: slackAccountId,
patch: { appToken: value },
}),
});
if (appTokenResult.action === "set") {
next = patchChannelConfigForAccount({
cfg: next,
channel: "slack",
accountId: slackAccountId,
patch: { appToken: appTokenResult.value },
});
}
next = appTokenStep.cfg;
next = await configureChannelAccessWithAllowlist({
cfg: next,

View File

@@ -14,12 +14,11 @@ import { fetchTelegramChatId } from "../../telegram/api.js";
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
import {
applySingleTokenPromptResult,
buildSingleChannelSecretPromptState,
patchChannelConfigForAccount,
promptSingleChannelSecretInput,
promptResolvedAllowFrom,
resolveAccountIdForConfigure,
resolveOnboardingAccountId,
runSingleChannelSecretStep,
setChannelDmPolicyWithAllowFrom,
setOnboardingChannelEnabled,
splitOnboardingEntries,
@@ -194,59 +193,46 @@ export const telegramOnboardingAdapter: ChannelOnboardingAdapter = {
const hasConfigToken =
hasConfiguredBotToken || Boolean(resolvedAccount.config.tokenFile?.trim());
const allowEnv = telegramAccountId === DEFAULT_ACCOUNT_ID;
const tokenPromptState = buildSingleChannelSecretPromptState({
accountConfigured: Boolean(resolvedAccount.token) || hasConfigToken,
hasConfigToken,
allowEnv,
envValue: process.env.TELEGRAM_BOT_TOKEN,
});
if (!tokenPromptState.accountConfigured) {
await noteTelegramTokenHelp(prompter);
}
const tokenResult = await promptSingleChannelSecretInput({
const tokenStep = await runSingleChannelSecretStep({
cfg: next,
prompter,
providerHint: "telegram",
credentialLabel: "Telegram bot token",
secretInputMode: options?.secretInputMode,
accountConfigured: tokenPromptState.accountConfigured,
canUseEnv: tokenPromptState.canUseEnv,
hasConfigToken: tokenPromptState.hasConfigToken,
accountConfigured: Boolean(resolvedAccount.token) || hasConfigToken,
hasConfigToken,
allowEnv,
envValue: process.env.TELEGRAM_BOT_TOKEN,
envPrompt: "TELEGRAM_BOT_TOKEN detected. Use env var?",
keepPrompt: "Telegram token already configured. Keep it?",
inputPrompt: "Enter Telegram bot token",
preferredEnvVar: allowEnv ? "TELEGRAM_BOT_TOKEN" : undefined,
onMissingConfigured: async () => await noteTelegramTokenHelp(prompter),
applyUseEnv: async (cfg) =>
applySingleTokenPromptResult({
cfg,
channel: "telegram",
accountId: telegramAccountId,
tokenPatchKey: "botToken",
tokenResult: { useEnv: true, token: null },
}),
applySet: async (cfg, value) =>
applySingleTokenPromptResult({
cfg,
channel: "telegram",
accountId: telegramAccountId,
tokenPatchKey: "botToken",
tokenResult: { useEnv: false, token: value },
}),
});
let resolvedTokenForAllowFrom: string | undefined;
if (tokenResult.action === "use-env") {
next = applySingleTokenPromptResult({
cfg: next,
channel: "telegram",
accountId: telegramAccountId,
tokenPatchKey: "botToken",
tokenResult: { useEnv: true, token: null },
});
resolvedTokenForAllowFrom = process.env.TELEGRAM_BOT_TOKEN?.trim() || undefined;
} else if (tokenResult.action === "set") {
next = applySingleTokenPromptResult({
cfg: next,
channel: "telegram",
accountId: telegramAccountId,
tokenPatchKey: "botToken",
tokenResult: { useEnv: false, token: tokenResult.value },
});
resolvedTokenForAllowFrom = tokenResult.resolvedValue;
}
next = tokenStep.cfg;
if (forceAllowFrom) {
next = await promptTelegramAllowFrom({
cfg: next,
prompter,
accountId: telegramAccountId,
tokenOverride: resolvedTokenForAllowFrom,
tokenOverride: tokenStep.resolvedValue,
});
}

View File

@@ -33,6 +33,7 @@ export {
buildSingleChannelSecretPromptState,
promptAccountId,
promptSingleChannelSecretInput,
runSingleChannelSecretStep,
resolveAccountIdForConfigure,
} from "../channels/plugins/onboarding/helpers.js";
export {

View File

@@ -27,6 +27,7 @@ export {
mergeAllowFromEntries,
promptAccountId,
promptSingleChannelSecretInput,
runSingleChannelSecretStep,
resolveAccountIdForConfigure,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../channels/plugins/onboarding/helpers.js";

View File

@@ -21,6 +21,7 @@ export {
mergeAllowFromEntries,
promptAccountId,
promptSingleChannelSecretInput,
runSingleChannelSecretStep,
resolveAccountIdForConfigure,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../channels/plugins/onboarding/helpers.js";