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?",