mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
refactor: share onboarding secret prompt flows
This commit is contained in:
@@ -1,16 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
buildSingleChannelSecretPromptState,
|
|
||||||
formatDocsLink,
|
formatDocsLink,
|
||||||
hasConfiguredSecretInput,
|
hasConfiguredSecretInput,
|
||||||
mapAllowFromEntries,
|
mapAllowFromEntries,
|
||||||
mergeAllowFromEntries,
|
mergeAllowFromEntries,
|
||||||
patchScopedAccountConfig,
|
patchScopedAccountConfig,
|
||||||
promptSingleChannelSecretInput,
|
runSingleChannelSecretStep,
|
||||||
resolveAccountIdForConfigure,
|
resolveAccountIdForConfigure,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
normalizeAccountId,
|
normalizeAccountId,
|
||||||
setTopLevelChannelDmPolicyWithAllowFrom,
|
setTopLevelChannelDmPolicyWithAllowFrom,
|
||||||
type SecretInput,
|
|
||||||
type ChannelOnboardingAdapter,
|
type ChannelOnboardingAdapter,
|
||||||
type ChannelOnboardingDmPolicy,
|
type ChannelOnboardingDmPolicy,
|
||||||
type OpenClawConfig,
|
type OpenClawConfig,
|
||||||
@@ -190,12 +188,6 @@ export const nextcloudTalkOnboardingAdapter: ChannelOnboardingAdapter = {
|
|||||||
hasConfiguredSecretInput(resolvedAccount.config.botSecret) ||
|
hasConfiguredSecretInput(resolvedAccount.config.botSecret) ||
|
||||||
resolvedAccount.config.botSecretFile,
|
resolvedAccount.config.botSecretFile,
|
||||||
);
|
);
|
||||||
const secretPromptState = buildSingleChannelSecretPromptState({
|
|
||||||
accountConfigured,
|
|
||||||
hasConfigToken: hasConfigSecret,
|
|
||||||
allowEnv,
|
|
||||||
envValue: process.env.NEXTCLOUD_TALK_BOT_SECRET,
|
|
||||||
});
|
|
||||||
|
|
||||||
let baseUrl = resolvedAccount.baseUrl;
|
let baseUrl = resolvedAccount.baseUrl;
|
||||||
if (!baseUrl) {
|
if (!baseUrl) {
|
||||||
@@ -216,32 +208,35 @@ export const nextcloudTalkOnboardingAdapter: ChannelOnboardingAdapter = {
|
|||||||
).trim();
|
).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
let secret: SecretInput | null = null;
|
const secretStep = await runSingleChannelSecretStep({
|
||||||
if (!accountConfigured) {
|
|
||||||
await noteNextcloudTalkSecretHelp(prompter);
|
|
||||||
}
|
|
||||||
|
|
||||||
const secretResult = await promptSingleChannelSecretInput({
|
|
||||||
cfg: next,
|
cfg: next,
|
||||||
prompter,
|
prompter,
|
||||||
providerHint: "nextcloud-talk",
|
providerHint: "nextcloud-talk",
|
||||||
credentialLabel: "bot secret",
|
credentialLabel: "bot secret",
|
||||||
accountConfigured: secretPromptState.accountConfigured,
|
accountConfigured,
|
||||||
canUseEnv: secretPromptState.canUseEnv,
|
hasConfigToken: hasConfigSecret,
|
||||||
hasConfigToken: secretPromptState.hasConfigToken,
|
allowEnv,
|
||||||
|
envValue: process.env.NEXTCLOUD_TALK_BOT_SECRET,
|
||||||
envPrompt: "NEXTCLOUD_TALK_BOT_SECRET detected. Use env var?",
|
envPrompt: "NEXTCLOUD_TALK_BOT_SECRET detected. Use env var?",
|
||||||
keepPrompt: "Nextcloud Talk bot secret already configured. Keep it?",
|
keepPrompt: "Nextcloud Talk bot secret already configured. Keep it?",
|
||||||
inputPrompt: "Enter Nextcloud Talk bot secret",
|
inputPrompt: "Enter Nextcloud Talk bot secret",
|
||||||
preferredEnvVar: "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") {
|
next = secretStep.cfg;
|
||||||
secret = secretResult.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (secretResult.action === "use-env" || secret || baseUrl !== resolvedAccount.baseUrl) {
|
if (secretStep.action === "keep" && baseUrl !== resolvedAccount.baseUrl) {
|
||||||
next = setNextcloudTalkAccountConfig(next, accountId, {
|
next = setNextcloudTalkAccountConfig(next, accountId, {
|
||||||
baseUrl,
|
baseUrl,
|
||||||
...(secret ? { botSecret: secret } : {}),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,26 +257,28 @@ export const nextcloudTalkOnboardingAdapter: ChannelOnboardingAdapter = {
|
|||||||
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
|
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
|
||||||
}),
|
}),
|
||||||
).trim();
|
).trim();
|
||||||
const apiPasswordResult = await promptSingleChannelSecretInput({
|
const apiPasswordStep = await runSingleChannelSecretStep({
|
||||||
cfg: next,
|
cfg: next,
|
||||||
prompter,
|
prompter,
|
||||||
providerHint: "nextcloud-talk-api",
|
providerHint: "nextcloud-talk-api",
|
||||||
credentialLabel: "API password",
|
credentialLabel: "API password",
|
||||||
...buildSingleChannelSecretPromptState({
|
accountConfigured: Boolean(existingApiUser && existingApiPasswordConfigured),
|
||||||
accountConfigured: Boolean(existingApiUser && existingApiPasswordConfigured),
|
hasConfigToken: existingApiPasswordConfigured,
|
||||||
hasConfigToken: existingApiPasswordConfigured,
|
allowEnv: false,
|
||||||
allowEnv: false,
|
|
||||||
}),
|
|
||||||
envPrompt: "",
|
envPrompt: "",
|
||||||
keepPrompt: "Nextcloud Talk API password already configured. Keep it?",
|
keepPrompt: "Nextcloud Talk API password already configured. Keep it?",
|
||||||
inputPrompt: "Enter Nextcloud Talk API password",
|
inputPrompt: "Enter Nextcloud Talk API password",
|
||||||
preferredEnvVar: "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 =
|
||||||
next = setNextcloudTalkAccountConfig(next, accountId, {
|
apiPasswordStep.action === "keep"
|
||||||
apiUser,
|
? setNextcloudTalkAccountConfig(next, accountId, { apiUser })
|
||||||
...(apiPassword ? { apiPassword } : {}),
|
: apiPasswordStep.cfg;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forceAllowFrom) {
|
if (forceAllowFrom) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
mergeAllowFromEntries,
|
mergeAllowFromEntries,
|
||||||
normalizeAccountId,
|
normalizeAccountId,
|
||||||
promptSingleChannelSecretInput,
|
promptSingleChannelSecretInput,
|
||||||
|
runSingleChannelSecretStep,
|
||||||
resolveAccountIdForConfigure,
|
resolveAccountIdForConfigure,
|
||||||
setTopLevelChannelDmPolicyWithAllowFrom,
|
setTopLevelChannelDmPolicyWithAllowFrom,
|
||||||
} from "openclaw/plugin-sdk/zalo";
|
} from "openclaw/plugin-sdk/zalo";
|
||||||
@@ -255,80 +256,66 @@ export const zaloOnboardingAdapter: ChannelOnboardingAdapter = {
|
|||||||
const hasConfigToken = Boolean(
|
const hasConfigToken = Boolean(
|
||||||
hasConfiguredSecretInput(resolvedAccount.config.botToken) || resolvedAccount.config.tokenFile,
|
hasConfiguredSecretInput(resolvedAccount.config.botToken) || resolvedAccount.config.tokenFile,
|
||||||
);
|
);
|
||||||
const tokenPromptState = buildSingleChannelSecretPromptState({
|
const tokenStep = await runSingleChannelSecretStep({
|
||||||
accountConfigured,
|
|
||||||
hasConfigToken,
|
|
||||||
allowEnv,
|
|
||||||
envValue: process.env.ZALO_BOT_TOKEN,
|
|
||||||
});
|
|
||||||
|
|
||||||
let token: SecretInput | null = null;
|
|
||||||
if (!accountConfigured) {
|
|
||||||
await noteZaloTokenHelp(prompter);
|
|
||||||
}
|
|
||||||
const tokenResult = await promptSingleChannelSecretInput({
|
|
||||||
cfg: next,
|
cfg: next,
|
||||||
prompter,
|
prompter,
|
||||||
providerHint: "zalo",
|
providerHint: "zalo",
|
||||||
credentialLabel: "bot token",
|
credentialLabel: "bot token",
|
||||||
accountConfigured: tokenPromptState.accountConfigured,
|
accountConfigured,
|
||||||
canUseEnv: tokenPromptState.canUseEnv,
|
hasConfigToken,
|
||||||
hasConfigToken: tokenPromptState.hasConfigToken,
|
allowEnv,
|
||||||
|
envValue: process.env.ZALO_BOT_TOKEN,
|
||||||
envPrompt: "ZALO_BOT_TOKEN detected. Use env var?",
|
envPrompt: "ZALO_BOT_TOKEN detected. Use env var?",
|
||||||
keepPrompt: "Zalo token already configured. Keep it?",
|
keepPrompt: "Zalo token already configured. Keep it?",
|
||||||
inputPrompt: "Enter Zalo bot token",
|
inputPrompt: "Enter Zalo bot token",
|
||||||
preferredEnvVar: "ZALO_BOT_TOKEN",
|
preferredEnvVar: "ZALO_BOT_TOKEN",
|
||||||
});
|
onMissingConfigured: async () => await noteZaloTokenHelp(prompter),
|
||||||
if (tokenResult.action === "set") {
|
applyUseEnv: async (cfg) =>
|
||||||
token = tokenResult.value;
|
zaloAccountId === DEFAULT_ACCOUNT_ID
|
||||||
}
|
? ({
|
||||||
if (tokenResult.action === "use-env" && zaloAccountId === DEFAULT_ACCOUNT_ID) {
|
...cfg,
|
||||||
next = {
|
channels: {
|
||||||
...next,
|
...cfg.channels,
|
||||||
channels: {
|
zalo: {
|
||||||
...next.channels,
|
...cfg.channels?.zalo,
|
||||||
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],
|
|
||||||
enabled: true,
|
enabled: true,
|
||||||
botToken: token,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
} as OpenClawConfig)
|
||||||
},
|
: cfg,
|
||||||
} as OpenClawConfig;
|
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({
|
const wantsWebhook = await prompter.confirm({
|
||||||
message: "Use webhook mode for Zalo?",
|
message: "Use webhook mode for Zalo?",
|
||||||
|
|||||||
@@ -20,15 +20,14 @@ import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onb
|
|||||||
import { configureChannelAccessWithAllowlist } from "./channel-access-configure.js";
|
import { configureChannelAccessWithAllowlist } from "./channel-access-configure.js";
|
||||||
import {
|
import {
|
||||||
applySingleTokenPromptResult,
|
applySingleTokenPromptResult,
|
||||||
buildSingleChannelSecretPromptState,
|
|
||||||
parseMentionOrPrefixedId,
|
parseMentionOrPrefixedId,
|
||||||
noteChannelLookupFailure,
|
noteChannelLookupFailure,
|
||||||
noteChannelLookupSummary,
|
noteChannelLookupSummary,
|
||||||
patchChannelConfigForAccount,
|
patchChannelConfigForAccount,
|
||||||
promptLegacyChannelAllowFrom,
|
promptLegacyChannelAllowFrom,
|
||||||
promptSingleChannelSecretInput,
|
|
||||||
resolveAccountIdForConfigure,
|
resolveAccountIdForConfigure,
|
||||||
resolveOnboardingAccountId,
|
resolveOnboardingAccountId,
|
||||||
|
runSingleChannelSecretStep,
|
||||||
setAccountGroupPolicyForChannel,
|
setAccountGroupPolicyForChannel,
|
||||||
setLegacyChannelDmPolicyWithAllowFrom,
|
setLegacyChannelDmPolicyWithAllowFrom,
|
||||||
setOnboardingChannelEnabled,
|
setOnboardingChannelEnabled,
|
||||||
@@ -179,52 +178,39 @@ export const discordOnboardingAdapter: ChannelOnboardingAdapter = {
|
|||||||
accountId: discordAccountId,
|
accountId: discordAccountId,
|
||||||
});
|
});
|
||||||
const allowEnv = discordAccountId === DEFAULT_ACCOUNT_ID;
|
const allowEnv = discordAccountId === DEFAULT_ACCOUNT_ID;
|
||||||
const tokenPromptState = buildSingleChannelSecretPromptState({
|
const tokenStep = await runSingleChannelSecretStep({
|
||||||
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({
|
|
||||||
cfg: next,
|
cfg: next,
|
||||||
prompter,
|
prompter,
|
||||||
providerHint: "discord",
|
providerHint: "discord",
|
||||||
credentialLabel: "Discord bot token",
|
credentialLabel: "Discord bot token",
|
||||||
secretInputMode: options?.secretInputMode,
|
secretInputMode: options?.secretInputMode,
|
||||||
accountConfigured: tokenPromptState.accountConfigured,
|
accountConfigured: Boolean(resolvedAccount.token),
|
||||||
canUseEnv: tokenPromptState.canUseEnv,
|
hasConfigToken: hasConfiguredSecretInput(resolvedAccount.config.token),
|
||||||
hasConfigToken: tokenPromptState.hasConfigToken,
|
allowEnv,
|
||||||
|
envValue: process.env.DISCORD_BOT_TOKEN,
|
||||||
envPrompt: "DISCORD_BOT_TOKEN detected. Use env var?",
|
envPrompt: "DISCORD_BOT_TOKEN detected. Use env var?",
|
||||||
keepPrompt: "Discord token already configured. Keep it?",
|
keepPrompt: "Discord token already configured. Keep it?",
|
||||||
inputPrompt: "Enter Discord bot token",
|
inputPrompt: "Enter Discord bot token",
|
||||||
preferredEnvVar: allowEnv ? "DISCORD_BOT_TOKEN" : undefined,
|
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 },
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
next = tokenStep.cfg;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentEntries = Object.entries(resolvedAccount.config.guilds ?? {}).flatMap(
|
const currentEntries = Object.entries(resolvedAccount.config.guilds ?? {}).flatMap(
|
||||||
([guildKey, value]) => {
|
([guildKey, value]) => {
|
||||||
@@ -261,7 +247,7 @@ export const discordOnboardingAdapter: ChannelOnboardingAdapter = {
|
|||||||
input,
|
input,
|
||||||
resolved: false,
|
resolved: false,
|
||||||
}));
|
}));
|
||||||
const activeToken = accountWithTokens.token || resolvedTokenForAllowlist || "";
|
const activeToken = accountWithTokens.token || tokenStep.resolvedValue || "";
|
||||||
if (activeToken && entries.length > 0) {
|
if (activeToken && entries.length > 0) {
|
||||||
try {
|
try {
|
||||||
resolved = await resolveDiscordChannelAllowlist({
|
resolved = await resolveDiscordChannelAllowlist({
|
||||||
|
|||||||
@@ -482,6 +482,82 @@ export type SingleChannelSecretInputPromptResult =
|
|||||||
| { action: "use-env" }
|
| { action: "use-env" }
|
||||||
| { action: "set"; value: SecretInput; resolvedValue: string };
|
| { 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: {
|
export async function promptSingleChannelSecretInput(params: {
|
||||||
cfg: OpenClawConfig;
|
cfg: OpenClawConfig;
|
||||||
prompter: Pick<WizardPrompter, "confirm" | "text" | "select" | "note">;
|
prompter: Pick<WizardPrompter, "confirm" | "text" | "select" | "note">;
|
||||||
|
|||||||
@@ -14,15 +14,14 @@ import type { WizardPrompter } from "../../../wizard/prompts.js";
|
|||||||
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
|
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
|
||||||
import { configureChannelAccessWithAllowlist } from "./channel-access-configure.js";
|
import { configureChannelAccessWithAllowlist } from "./channel-access-configure.js";
|
||||||
import {
|
import {
|
||||||
buildSingleChannelSecretPromptState,
|
|
||||||
parseMentionOrPrefixedId,
|
parseMentionOrPrefixedId,
|
||||||
noteChannelLookupFailure,
|
noteChannelLookupFailure,
|
||||||
noteChannelLookupSummary,
|
noteChannelLookupSummary,
|
||||||
patchChannelConfigForAccount,
|
patchChannelConfigForAccount,
|
||||||
promptLegacyChannelAllowFrom,
|
promptLegacyChannelAllowFrom,
|
||||||
promptSingleChannelSecretInput,
|
|
||||||
resolveAccountIdForConfigure,
|
resolveAccountIdForConfigure,
|
||||||
resolveOnboardingAccountId,
|
resolveOnboardingAccountId,
|
||||||
|
runSingleChannelSecretStep,
|
||||||
setAccountGroupPolicyForChannel,
|
setAccountGroupPolicyForChannel,
|
||||||
setLegacyChannelDmPolicyWithAllowFrom,
|
setLegacyChannelDmPolicyWithAllowFrom,
|
||||||
setOnboardingChannelEnabled,
|
setOnboardingChannelEnabled,
|
||||||
@@ -235,18 +234,6 @@ export const slackOnboardingAdapter: ChannelOnboardingAdapter = {
|
|||||||
const accountConfigured =
|
const accountConfigured =
|
||||||
Boolean(resolvedAccount.botToken && resolvedAccount.appToken) || hasConfigTokens;
|
Boolean(resolvedAccount.botToken && resolvedAccount.appToken) || hasConfigTokens;
|
||||||
const allowEnv = slackAccountId === DEFAULT_ACCOUNT_ID;
|
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;
|
let resolvedBotTokenForAllowlist = resolvedAccount.botToken;
|
||||||
const slackBotName = String(
|
const slackBotName = String(
|
||||||
await prompter.text({
|
await prompter.text({
|
||||||
@@ -257,54 +244,56 @@ export const slackOnboardingAdapter: ChannelOnboardingAdapter = {
|
|||||||
if (!accountConfigured) {
|
if (!accountConfigured) {
|
||||||
await noteSlackTokenHelp(prompter, slackBotName);
|
await noteSlackTokenHelp(prompter, slackBotName);
|
||||||
}
|
}
|
||||||
const botTokenResult = await promptSingleChannelSecretInput({
|
const botTokenStep = await runSingleChannelSecretStep({
|
||||||
cfg: next,
|
cfg: next,
|
||||||
prompter,
|
prompter,
|
||||||
providerHint: "slack-bot",
|
providerHint: "slack-bot",
|
||||||
credentialLabel: "Slack bot token",
|
credentialLabel: "Slack bot token",
|
||||||
secretInputMode: options?.secretInputMode,
|
secretInputMode: options?.secretInputMode,
|
||||||
accountConfigured: botPromptState.accountConfigured,
|
accountConfigured: Boolean(resolvedAccount.botToken) || hasConfiguredBotToken,
|
||||||
canUseEnv: botPromptState.canUseEnv,
|
hasConfigToken: hasConfiguredBotToken,
|
||||||
hasConfigToken: botPromptState.hasConfigToken,
|
allowEnv,
|
||||||
|
envValue: process.env.SLACK_BOT_TOKEN,
|
||||||
envPrompt: "SLACK_BOT_TOKEN detected. Use env var?",
|
envPrompt: "SLACK_BOT_TOKEN detected. Use env var?",
|
||||||
keepPrompt: "Slack bot token already configured. Keep it?",
|
keepPrompt: "Slack bot token already configured. Keep it?",
|
||||||
inputPrompt: "Enter Slack bot token (xoxb-...)",
|
inputPrompt: "Enter Slack bot token (xoxb-...)",
|
||||||
preferredEnvVar: allowEnv ? "SLACK_BOT_TOKEN" : undefined,
|
preferredEnvVar: allowEnv ? "SLACK_BOT_TOKEN" : undefined,
|
||||||
|
applySet: async (cfg, value) =>
|
||||||
|
patchChannelConfigForAccount({
|
||||||
|
cfg,
|
||||||
|
channel: "slack",
|
||||||
|
accountId: slackAccountId,
|
||||||
|
patch: { botToken: value },
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
if (botTokenResult.action === "use-env") {
|
next = botTokenStep.cfg;
|
||||||
resolvedBotTokenForAllowlist = process.env.SLACK_BOT_TOKEN?.trim() || undefined;
|
if (botTokenStep.resolvedValue) {
|
||||||
} else if (botTokenResult.action === "set") {
|
resolvedBotTokenForAllowlist = botTokenStep.resolvedValue;
|
||||||
next = patchChannelConfigForAccount({
|
|
||||||
cfg: next,
|
|
||||||
channel: "slack",
|
|
||||||
accountId: slackAccountId,
|
|
||||||
patch: { botToken: botTokenResult.value },
|
|
||||||
});
|
|
||||||
resolvedBotTokenForAllowlist = botTokenResult.resolvedValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const appTokenResult = await promptSingleChannelSecretInput({
|
const appTokenStep = await runSingleChannelSecretStep({
|
||||||
cfg: next,
|
cfg: next,
|
||||||
prompter,
|
prompter,
|
||||||
providerHint: "slack-app",
|
providerHint: "slack-app",
|
||||||
credentialLabel: "Slack app token",
|
credentialLabel: "Slack app token",
|
||||||
secretInputMode: options?.secretInputMode,
|
secretInputMode: options?.secretInputMode,
|
||||||
accountConfigured: appPromptState.accountConfigured,
|
accountConfigured: Boolean(resolvedAccount.appToken) || hasConfiguredAppToken,
|
||||||
canUseEnv: appPromptState.canUseEnv,
|
hasConfigToken: hasConfiguredAppToken,
|
||||||
hasConfigToken: appPromptState.hasConfigToken,
|
allowEnv,
|
||||||
|
envValue: process.env.SLACK_APP_TOKEN,
|
||||||
envPrompt: "SLACK_APP_TOKEN detected. Use env var?",
|
envPrompt: "SLACK_APP_TOKEN detected. Use env var?",
|
||||||
keepPrompt: "Slack app token already configured. Keep it?",
|
keepPrompt: "Slack app token already configured. Keep it?",
|
||||||
inputPrompt: "Enter Slack app token (xapp-...)",
|
inputPrompt: "Enter Slack app token (xapp-...)",
|
||||||
preferredEnvVar: allowEnv ? "SLACK_APP_TOKEN" : undefined,
|
preferredEnvVar: allowEnv ? "SLACK_APP_TOKEN" : undefined,
|
||||||
|
applySet: async (cfg, value) =>
|
||||||
|
patchChannelConfigForAccount({
|
||||||
|
cfg,
|
||||||
|
channel: "slack",
|
||||||
|
accountId: slackAccountId,
|
||||||
|
patch: { appToken: value },
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
if (appTokenResult.action === "set") {
|
next = appTokenStep.cfg;
|
||||||
next = patchChannelConfigForAccount({
|
|
||||||
cfg: next,
|
|
||||||
channel: "slack",
|
|
||||||
accountId: slackAccountId,
|
|
||||||
patch: { appToken: appTokenResult.value },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
next = await configureChannelAccessWithAllowlist({
|
next = await configureChannelAccessWithAllowlist({
|
||||||
cfg: next,
|
cfg: next,
|
||||||
|
|||||||
@@ -14,12 +14,11 @@ import { fetchTelegramChatId } from "../../telegram/api.js";
|
|||||||
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
|
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
|
||||||
import {
|
import {
|
||||||
applySingleTokenPromptResult,
|
applySingleTokenPromptResult,
|
||||||
buildSingleChannelSecretPromptState,
|
|
||||||
patchChannelConfigForAccount,
|
patchChannelConfigForAccount,
|
||||||
promptSingleChannelSecretInput,
|
|
||||||
promptResolvedAllowFrom,
|
promptResolvedAllowFrom,
|
||||||
resolveAccountIdForConfigure,
|
resolveAccountIdForConfigure,
|
||||||
resolveOnboardingAccountId,
|
resolveOnboardingAccountId,
|
||||||
|
runSingleChannelSecretStep,
|
||||||
setChannelDmPolicyWithAllowFrom,
|
setChannelDmPolicyWithAllowFrom,
|
||||||
setOnboardingChannelEnabled,
|
setOnboardingChannelEnabled,
|
||||||
splitOnboardingEntries,
|
splitOnboardingEntries,
|
||||||
@@ -194,59 +193,46 @@ export const telegramOnboardingAdapter: ChannelOnboardingAdapter = {
|
|||||||
const hasConfigToken =
|
const hasConfigToken =
|
||||||
hasConfiguredBotToken || Boolean(resolvedAccount.config.tokenFile?.trim());
|
hasConfiguredBotToken || Boolean(resolvedAccount.config.tokenFile?.trim());
|
||||||
const allowEnv = telegramAccountId === DEFAULT_ACCOUNT_ID;
|
const allowEnv = telegramAccountId === DEFAULT_ACCOUNT_ID;
|
||||||
const tokenPromptState = buildSingleChannelSecretPromptState({
|
const tokenStep = await runSingleChannelSecretStep({
|
||||||
accountConfigured: Boolean(resolvedAccount.token) || hasConfigToken,
|
|
||||||
hasConfigToken,
|
|
||||||
allowEnv,
|
|
||||||
envValue: process.env.TELEGRAM_BOT_TOKEN,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!tokenPromptState.accountConfigured) {
|
|
||||||
await noteTelegramTokenHelp(prompter);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokenResult = await promptSingleChannelSecretInput({
|
|
||||||
cfg: next,
|
cfg: next,
|
||||||
prompter,
|
prompter,
|
||||||
providerHint: "telegram",
|
providerHint: "telegram",
|
||||||
credentialLabel: "Telegram bot token",
|
credentialLabel: "Telegram bot token",
|
||||||
secretInputMode: options?.secretInputMode,
|
secretInputMode: options?.secretInputMode,
|
||||||
accountConfigured: tokenPromptState.accountConfigured,
|
accountConfigured: Boolean(resolvedAccount.token) || hasConfigToken,
|
||||||
canUseEnv: tokenPromptState.canUseEnv,
|
hasConfigToken,
|
||||||
hasConfigToken: tokenPromptState.hasConfigToken,
|
allowEnv,
|
||||||
|
envValue: process.env.TELEGRAM_BOT_TOKEN,
|
||||||
envPrompt: "TELEGRAM_BOT_TOKEN detected. Use env var?",
|
envPrompt: "TELEGRAM_BOT_TOKEN detected. Use env var?",
|
||||||
keepPrompt: "Telegram token already configured. Keep it?",
|
keepPrompt: "Telegram token already configured. Keep it?",
|
||||||
inputPrompt: "Enter Telegram bot token",
|
inputPrompt: "Enter Telegram bot token",
|
||||||
preferredEnvVar: allowEnv ? "TELEGRAM_BOT_TOKEN" : undefined,
|
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 },
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
next = tokenStep.cfg;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forceAllowFrom) {
|
if (forceAllowFrom) {
|
||||||
next = await promptTelegramAllowFrom({
|
next = await promptTelegramAllowFrom({
|
||||||
cfg: next,
|
cfg: next,
|
||||||
prompter,
|
prompter,
|
||||||
accountId: telegramAccountId,
|
accountId: telegramAccountId,
|
||||||
tokenOverride: resolvedTokenForAllowFrom,
|
tokenOverride: tokenStep.resolvedValue,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export {
|
|||||||
buildSingleChannelSecretPromptState,
|
buildSingleChannelSecretPromptState,
|
||||||
promptAccountId,
|
promptAccountId,
|
||||||
promptSingleChannelSecretInput,
|
promptSingleChannelSecretInput,
|
||||||
|
runSingleChannelSecretStep,
|
||||||
resolveAccountIdForConfigure,
|
resolveAccountIdForConfigure,
|
||||||
} from "../channels/plugins/onboarding/helpers.js";
|
} from "../channels/plugins/onboarding/helpers.js";
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export {
|
|||||||
mergeAllowFromEntries,
|
mergeAllowFromEntries,
|
||||||
promptAccountId,
|
promptAccountId,
|
||||||
promptSingleChannelSecretInput,
|
promptSingleChannelSecretInput,
|
||||||
|
runSingleChannelSecretStep,
|
||||||
resolveAccountIdForConfigure,
|
resolveAccountIdForConfigure,
|
||||||
setTopLevelChannelDmPolicyWithAllowFrom,
|
setTopLevelChannelDmPolicyWithAllowFrom,
|
||||||
} from "../channels/plugins/onboarding/helpers.js";
|
} from "../channels/plugins/onboarding/helpers.js";
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export {
|
|||||||
mergeAllowFromEntries,
|
mergeAllowFromEntries,
|
||||||
promptAccountId,
|
promptAccountId,
|
||||||
promptSingleChannelSecretInput,
|
promptSingleChannelSecretInput,
|
||||||
|
runSingleChannelSecretStep,
|
||||||
resolveAccountIdForConfigure,
|
resolveAccountIdForConfigure,
|
||||||
setTopLevelChannelDmPolicyWithAllowFrom,
|
setTopLevelChannelDmPolicyWithAllowFrom,
|
||||||
} from "../channels/plugins/onboarding/helpers.js";
|
} from "../channels/plugins/onboarding/helpers.js";
|
||||||
|
|||||||
Reference in New Issue
Block a user