chore: migrate to oxlint and oxfmt

Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
This commit is contained in:
Peter Steinberger
2026-01-14 14:31:43 +00:00
parent 912ebffc63
commit c379191f80
1480 changed files with 28608 additions and 43547 deletions

View File

@@ -5,25 +5,17 @@ import {
resolveDefaultDiscordAccountId,
resolveDiscordAccount,
} from "../../../discord/accounts.js";
import {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
} from "../../../routing/session-key.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../routing/session-key.js";
import { formatDocsLink } from "../../../terminal/links.js";
import type { WizardPrompter } from "../../../wizard/prompts.js";
import type {
ChannelOnboardingAdapter,
ChannelOnboardingDmPolicy,
} from "../onboarding-types.js";
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
import { addWildcardAllowFrom, promptAccountId } from "./helpers.js";
const channel = "discord" as const;
function setDiscordDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy) {
const allowFrom =
dmPolicy === "open"
? addWildcardAllowFrom(cfg.channels?.discord?.dm?.allowFrom)
: undefined;
dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.discord?.dm?.allowFrom) : undefined;
return {
...cfg,
channels: {
@@ -77,12 +69,7 @@ export const discordOnboardingAdapter: ChannelOnboardingAdapter = {
quickstartScore: configured ? 2 : 1,
};
},
configure: async ({
cfg,
prompter,
accountOverrides,
shouldPromptAccountIds,
}) => {
configure: async ({ cfg, prompter, accountOverrides, shouldPromptAccountIds }) => {
const discordOverride = accountOverrides.discord?.trim();
const defaultDiscordAccountId = resolveDefaultDiscordAccountId(cfg);
let discordAccountId = discordOverride
@@ -106,8 +93,7 @@ export const discordOnboardingAdapter: ChannelOnboardingAdapter = {
});
const accountConfigured = Boolean(resolvedAccount.token);
const allowEnv = discordAccountId === DEFAULT_ACCOUNT_ID;
const canUseEnv =
allowEnv && Boolean(process.env.DISCORD_BOT_TOKEN?.trim());
const canUseEnv = allowEnv && Boolean(process.env.DISCORD_BOT_TOKEN?.trim());
const hasConfigToken = Boolean(resolvedAccount.config.token);
let token: string | null = null;
@@ -178,9 +164,7 @@ export const discordOnboardingAdapter: ChannelOnboardingAdapter = {
...next.channels?.discord?.accounts,
[discordAccountId]: {
...next.channels?.discord?.accounts?.[discordAccountId],
enabled:
next.channels?.discord?.accounts?.[discordAccountId]
?.enabled ?? true,
enabled: next.channels?.discord?.accounts?.[discordAccountId]?.enabled ?? true,
token,
},
},

View File

@@ -1,18 +1,9 @@
import {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
} from "../../../routing/session-key.js";
import type {
PromptAccountId,
PromptAccountIdParams,
} from "../onboarding-types.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../routing/session-key.js";
import type { PromptAccountId, PromptAccountIdParams } from "../onboarding-types.js";
export const promptAccountId: PromptAccountId = async (
params: PromptAccountIdParams,
) => {
export const promptAccountId: PromptAccountId = async (params: PromptAccountIdParams) => {
const existingIds = params.listAccountIds(params.cfg);
const initial =
params.currentId?.trim() || params.defaultAccountId || DEFAULT_ACCOUNT_ID;
const initial = params.currentId?.trim() || params.defaultAccountId || DEFAULT_ACCOUNT_ID;
const choice = (await params.prompter.select({
message: `${params.label} account`,
options: [

View File

@@ -6,24 +6,16 @@ import {
resolveDefaultIMessageAccountId,
resolveIMessageAccount,
} from "../../../imessage/accounts.js";
import {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
} from "../../../routing/session-key.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../routing/session-key.js";
import { formatDocsLink } from "../../../terminal/links.js";
import type {
ChannelOnboardingAdapter,
ChannelOnboardingDmPolicy,
} from "../onboarding-types.js";
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
import { addWildcardAllowFrom, promptAccountId } from "./helpers.js";
const channel = "imessage" as const;
function setIMessageDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy) {
const allowFrom =
dmPolicy === "open"
? addWildcardAllowFrom(cfg.channels?.imessage?.allowFrom)
: undefined;
dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.imessage?.allowFrom) : undefined;
return {
...cfg,
channels: {
@@ -53,10 +45,10 @@ export const imessageOnboardingAdapter: ChannelOnboardingAdapter = {
const account = resolveIMessageAccount({ cfg, accountId });
return Boolean(
account.config.cliPath ||
account.config.dbPath ||
account.config.allowFrom ||
account.config.service ||
account.config.region,
account.config.dbPath ||
account.config.allowFrom ||
account.config.service ||
account.config.region,
);
});
const imessageCliPath = cfg.channels?.imessage?.cliPath ?? "imsg";
@@ -72,12 +64,7 @@ export const imessageOnboardingAdapter: ChannelOnboardingAdapter = {
quickstartScore: imessageCliDetected ? 1 : 0,
};
},
configure: async ({
cfg,
prompter,
accountOverrides,
shouldPromptAccountIds,
}) => {
configure: async ({ cfg, prompter, accountOverrides, shouldPromptAccountIds }) => {
const imessageOverride = accountOverrides.imessage?.trim();
const defaultIMessageAccountId = resolveDefaultIMessageAccountId(cfg);
let imessageAccountId = imessageOverride
@@ -109,10 +96,7 @@ export const imessageOnboardingAdapter: ChannelOnboardingAdapter = {
});
resolvedCliPath = String(entered).trim();
if (!resolvedCliPath) {
await prompter.note(
"imsg CLI path required to enable iMessage.",
"iMessage",
);
await prompter.note("imsg CLI path required to enable iMessage.", "iMessage");
}
}
@@ -141,9 +125,7 @@ export const imessageOnboardingAdapter: ChannelOnboardingAdapter = {
...next.channels?.imessage?.accounts,
[imessageAccountId]: {
...next.channels?.imessage?.accounts?.[imessageAccountId],
enabled:
next.channels?.imessage?.accounts?.[imessageAccountId]
?.enabled ?? true,
enabled: next.channels?.imessage?.accounts?.[imessageAccountId]?.enabled ?? true,
cliPath: resolvedCliPath,
},
},

View File

@@ -4,10 +4,7 @@ import { resolveMSTeamsCredentials } from "../../../msteams/token.js";
import { DEFAULT_ACCOUNT_ID } from "../../../routing/session-key.js";
import { formatDocsLink } from "../../../terminal/links.js";
import type { WizardPrompter } from "../../../wizard/prompts.js";
import type {
ChannelOnboardingAdapter,
ChannelOnboardingDmPolicy,
} from "../onboarding-types.js";
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
import { addWildcardAllowFrom } from "./helpers.js";
const channel = "msteams" as const;
@@ -15,9 +12,7 @@ const channel = "msteams" as const;
function setMSTeamsDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy) {
const allowFrom =
dmPolicy === "open"
? addWildcardAllowFrom(cfg.channels?.msteams?.allowFrom)?.map((entry) =>
String(entry),
)
? addWildcardAllowFrom(cfg.channels?.msteams?.allowFrom)?.map((entry) => String(entry))
: undefined;
return {
...cfg,
@@ -32,9 +27,7 @@ function setMSTeamsDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy) {
};
}
async function noteMSTeamsCredentialHelp(
prompter: WizardPrompter,
): Promise<void> {
async function noteMSTeamsCredentialHelp(prompter: WizardPrompter): Promise<void> {
await prompter.note(
[
"1) Azure Bot registration → get App ID + Tenant ID",
@@ -59,15 +52,11 @@ const dmPolicy: ChannelOnboardingDmPolicy = {
export const msteamsOnboardingAdapter: ChannelOnboardingAdapter = {
channel,
getStatus: async ({ cfg }) => {
const configured = Boolean(
resolveMSTeamsCredentials(cfg.channels?.msteams),
);
const configured = Boolean(resolveMSTeamsCredentials(cfg.channels?.msteams));
return {
channel,
configured,
statusLines: [
`MS Teams: ${configured ? "configured" : "needs app credentials"}`,
],
statusLines: [`MS Teams: ${configured ? "configured" : "needs app credentials"}`],
selectionHint: configured ? "configured" : "needs app creds",
quickstartScore: configured ? 2 : 0,
};
@@ -76,14 +65,14 @@ export const msteamsOnboardingAdapter: ChannelOnboardingAdapter = {
const resolved = resolveMSTeamsCredentials(cfg.channels?.msteams);
const hasConfigCreds = Boolean(
cfg.channels?.msteams?.appId?.trim() &&
cfg.channels?.msteams?.appPassword?.trim() &&
cfg.channels?.msteams?.tenantId?.trim(),
cfg.channels?.msteams?.appPassword?.trim() &&
cfg.channels?.msteams?.tenantId?.trim(),
);
const canUseEnv = Boolean(
!hasConfigCreds &&
process.env.MSTEAMS_APP_ID?.trim() &&
process.env.MSTEAMS_APP_PASSWORD?.trim() &&
process.env.MSTEAMS_TENANT_ID?.trim(),
process.env.MSTEAMS_APP_ID?.trim() &&
process.env.MSTEAMS_APP_PASSWORD?.trim() &&
process.env.MSTEAMS_TENANT_ID?.trim(),
);
let next = cfg;

View File

@@ -2,29 +2,21 @@ import { detectBinary } from "../../../commands/onboard-helpers.js";
import { installSignalCli } from "../../../commands/signal-install.js";
import type { ClawdbotConfig } from "../../../config/config.js";
import type { DmPolicy } from "../../../config/types.js";
import {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
} from "../../../routing/session-key.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../routing/session-key.js";
import {
listSignalAccountIds,
resolveDefaultSignalAccountId,
resolveSignalAccount,
} from "../../../signal/accounts.js";
import { formatDocsLink } from "../../../terminal/links.js";
import type {
ChannelOnboardingAdapter,
ChannelOnboardingDmPolicy,
} from "../onboarding-types.js";
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
import { addWildcardAllowFrom, promptAccountId } from "./helpers.js";
const channel = "signal" as const;
function setSignalDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy) {
const allowFrom =
dmPolicy === "open"
? addWildcardAllowFrom(cfg.channels?.signal?.allowFrom)
: undefined;
dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.signal?.allowFrom) : undefined;
return {
...cfg,
channels: {
@@ -62,9 +54,7 @@ export const signalOnboardingAdapter: ChannelOnboardingAdapter = {
`Signal: ${configured ? "configured" : "needs setup"}`,
`signal-cli: ${signalCliDetected ? "found" : "missing"} (${signalCliPath})`,
],
selectionHint: signalCliDetected
? "signal-cli found"
: "signal-cli missing",
selectionHint: signalCliDetected ? "signal-cli found" : "signal-cli missing",
quickstartScore: signalCliDetected ? 1 : 0,
};
},
@@ -113,21 +103,12 @@ export const signalOnboardingAdapter: ChannelOnboardingAdapter = {
if (result.ok && result.cliPath) {
cliDetected = true;
resolvedCliPath = result.cliPath;
await prompter.note(
`Installed signal-cli at ${result.cliPath}`,
"Signal",
);
await prompter.note(`Installed signal-cli at ${result.cliPath}`, "Signal");
} else if (!result.ok) {
await prompter.note(
result.error ?? "signal-cli install failed.",
"Signal",
);
await prompter.note(result.error ?? "signal-cli install failed.", "Signal");
}
} catch (err) {
await prompter.note(
`signal-cli install failed: ${String(err)}`,
"Signal",
);
await prompter.note(`signal-cli install failed: ${String(err)}`, "Signal");
}
}
}
@@ -183,9 +164,7 @@ export const signalOnboardingAdapter: ChannelOnboardingAdapter = {
...next.channels?.signal?.accounts,
[signalAccountId]: {
...next.channels?.signal?.accounts?.[signalAccountId],
enabled:
next.channels?.signal?.accounts?.[signalAccountId]
?.enabled ?? true,
enabled: next.channels?.signal?.accounts?.[signalAccountId]?.enabled ?? true,
account,
cliPath: resolvedCliPath ?? "signal-cli",
},

View File

@@ -1,9 +1,6 @@
import type { ClawdbotConfig } from "../../../config/config.js";
import type { DmPolicy } from "../../../config/types.js";
import {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
} from "../../../routing/session-key.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../routing/session-key.js";
import {
listSlackAccountIds,
resolveDefaultSlackAccountId,
@@ -11,19 +8,14 @@ import {
} from "../../../slack/accounts.js";
import { formatDocsLink } from "../../../terminal/links.js";
import type { WizardPrompter } from "../../../wizard/prompts.js";
import type {
ChannelOnboardingAdapter,
ChannelOnboardingDmPolicy,
} from "../onboarding-types.js";
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
import { addWildcardAllowFrom, promptAccountId } from "./helpers.js";
const channel = "slack" as const;
function setSlackDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy) {
const allowFrom =
dmPolicy === "open"
? addWildcardAllowFrom(cfg.channels?.slack?.dm?.allowFrom)
: undefined;
dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.slack?.dm?.allowFrom) : undefined;
return {
...cfg,
channels: {
@@ -110,10 +102,7 @@ function buildSlackManifest(botName: string) {
return JSON.stringify(manifest, null, 2);
}
async function noteSlackTokenHelp(
prompter: WizardPrompter,
botName: string,
): Promise<void> {
async function noteSlackTokenHelp(prompter: WizardPrompter, botName: string): Promise<void> {
const manifest = buildSlackManifest(botName);
await prompter.note(
[
@@ -156,17 +145,10 @@ export const slackOnboardingAdapter: ChannelOnboardingAdapter = {
quickstartScore: configured ? 2 : 1,
};
},
configure: async ({
cfg,
prompter,
accountOverrides,
shouldPromptAccountIds,
}) => {
configure: async ({ cfg, prompter, accountOverrides, shouldPromptAccountIds }) => {
const slackOverride = accountOverrides.slack?.trim();
const defaultSlackAccountId = resolveDefaultSlackAccountId(cfg);
let slackAccountId = slackOverride
? normalizeAccountId(slackOverride)
: defaultSlackAccountId;
let slackAccountId = slackOverride ? normalizeAccountId(slackOverride) : defaultSlackAccountId;
if (shouldPromptAccountIds && !slackOverride) {
slackAccountId = await promptAccountId({
cfg,
@@ -183,9 +165,7 @@ export const slackOnboardingAdapter: ChannelOnboardingAdapter = {
cfg: next,
accountId: slackAccountId,
});
const accountConfigured = Boolean(
resolvedAccount.botToken && resolvedAccount.appToken,
);
const accountConfigured = Boolean(resolvedAccount.botToken && resolvedAccount.appToken);
const allowEnv = slackAccountId === DEFAULT_ACCOUNT_ID;
const canUseEnv =
allowEnv &&
@@ -206,10 +186,7 @@ export const slackOnboardingAdapter: ChannelOnboardingAdapter = {
if (!accountConfigured) {
await noteSlackTokenHelp(prompter, slackBotName);
}
if (
canUseEnv &&
(!resolvedAccount.config.botToken || !resolvedAccount.config.appToken)
) {
if (canUseEnv && (!resolvedAccount.config.botToken || !resolvedAccount.config.appToken)) {
const keepEnv = await prompter.confirm({
message: "SLACK_BOT_TOKEN + SLACK_APP_TOKEN detected. Use env vars?",
initialValue: true,
@@ -296,9 +273,7 @@ export const slackOnboardingAdapter: ChannelOnboardingAdapter = {
...next.channels?.slack?.accounts,
[slackAccountId]: {
...next.channels?.slack?.accounts?.[slackAccountId],
enabled:
next.channels?.slack?.accounts?.[slackAccountId]?.enabled ??
true,
enabled: next.channels?.slack?.accounts?.[slackAccountId]?.enabled ?? true,
botToken,
appToken,
},

View File

@@ -1,9 +1,6 @@
import type { ClawdbotConfig } from "../../../config/config.js";
import type { DmPolicy } from "../../../config/types.js";
import {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
} from "../../../routing/session-key.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../routing/session-key.js";
import {
listTelegramAccountIds,
resolveDefaultTelegramAccountId,
@@ -11,19 +8,14 @@ import {
} from "../../../telegram/accounts.js";
import { formatDocsLink } from "../../../terminal/links.js";
import type { WizardPrompter } from "../../../wizard/prompts.js";
import type {
ChannelOnboardingAdapter,
ChannelOnboardingDmPolicy,
} from "../onboarding-types.js";
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
import { addWildcardAllowFrom, promptAccountId } from "./helpers.js";
const channel = "telegram" as const;
function setTelegramDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy) {
const allowFrom =
dmPolicy === "open"
? addWildcardAllowFrom(cfg.channels?.telegram?.allowFrom)
: undefined;
dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.telegram?.allowFrom) : undefined;
return {
...cfg,
channels: {
@@ -62,9 +54,7 @@ async function promptTelegramAllowFrom(params: {
const entry = await prompter.text({
message: "Telegram allowFrom (user id)",
placeholder: "123456789",
initialValue: existingAllowFrom[0]
? String(existingAllowFrom[0])
: undefined,
initialValue: existingAllowFrom[0] ? String(existingAllowFrom[0]) : undefined,
validate: (value) => {
const raw = String(value ?? "").trim();
if (!raw) return "Required";
@@ -105,8 +95,7 @@ async function promptTelegramAllowFrom(params: {
...cfg.channels?.telegram?.accounts,
[accountId]: {
...cfg.channels?.telegram?.accounts?.[accountId],
enabled:
cfg.channels?.telegram?.accounts?.[accountId]?.enabled ?? true,
enabled: cfg.channels?.telegram?.accounts?.[accountId]?.enabled ?? true,
dmPolicy: "allowlist",
allowFrom: unique,
},
@@ -135,9 +124,7 @@ export const telegramOnboardingAdapter: ChannelOnboardingAdapter = {
channel,
configured,
statusLines: [`Telegram: ${configured ? "configured" : "needs token"}`],
selectionHint: configured
? "recommended · configured"
: "recommended · newcomer-friendly",
selectionHint: configured ? "recommended · configured" : "recommended · newcomer-friendly",
quickstartScore: configured ? 1 : 10,
};
},
@@ -171,8 +158,7 @@ export const telegramOnboardingAdapter: ChannelOnboardingAdapter = {
});
const accountConfigured = Boolean(resolvedAccount.token);
const allowEnv = telegramAccountId === DEFAULT_ACCOUNT_ID;
const canUseEnv =
allowEnv && Boolean(process.env.TELEGRAM_BOT_TOKEN?.trim());
const canUseEnv = allowEnv && Boolean(process.env.TELEGRAM_BOT_TOKEN?.trim());
const hasConfigToken = Boolean(
resolvedAccount.config.botToken || resolvedAccount.config.tokenFile,
);
@@ -252,9 +238,7 @@ export const telegramOnboardingAdapter: ChannelOnboardingAdapter = {
...next.channels?.telegram?.accounts,
[telegramAccountId]: {
...next.channels?.telegram?.accounts?.[telegramAccountId],
enabled:
next.channels?.telegram?.accounts?.[telegramAccountId]
?.enabled ?? true,
enabled: next.channels?.telegram?.accounts?.[telegramAccountId]?.enabled ?? true,
botToken: token,
},
},

View File

@@ -4,10 +4,7 @@ import { loginWeb } from "../../../channel-web.js";
import type { ClawdbotConfig } from "../../../config/config.js";
import { mergeWhatsAppConfig } from "../../../config/merge-config.js";
import type { DmPolicy } from "../../../config/types.js";
import {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
} from "../../../routing/session-key.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../routing/session-key.js";
import type { RuntimeEnv } from "../../../runtime.js";
import { formatDocsLink } from "../../../terminal/links.js";
import { normalizeE164 } from "../../../utils.js";
@@ -22,28 +19,15 @@ import { promptAccountId } from "./helpers.js";
const channel = "whatsapp" as const;
function setWhatsAppDmPolicy(
cfg: ClawdbotConfig,
dmPolicy: DmPolicy,
): ClawdbotConfig {
function setWhatsAppDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy): ClawdbotConfig {
return mergeWhatsAppConfig(cfg, { dmPolicy });
}
function setWhatsAppAllowFrom(
cfg: ClawdbotConfig,
allowFrom?: string[],
): ClawdbotConfig {
return mergeWhatsAppConfig(
cfg,
{ allowFrom },
{ unsetOnUndefined: ["allowFrom"] },
);
function setWhatsAppAllowFrom(cfg: ClawdbotConfig, allowFrom?: string[]): ClawdbotConfig {
return mergeWhatsAppConfig(cfg, { allowFrom }, { unsetOnUndefined: ["allowFrom"] });
}
function setMessagesResponsePrefix(
cfg: ClawdbotConfig,
responsePrefix?: string,
): ClawdbotConfig {
function setMessagesResponsePrefix(cfg: ClawdbotConfig, responsePrefix?: string): ClawdbotConfig {
return {
...cfg,
messages: {
@@ -53,10 +37,7 @@ function setMessagesResponsePrefix(
};
}
function setWhatsAppSelfChatMode(
cfg: ClawdbotConfig,
selfChatMode: boolean,
): ClawdbotConfig {
function setWhatsAppSelfChatMode(cfg: ClawdbotConfig, selfChatMode: boolean): ClawdbotConfig {
return mergeWhatsAppConfig(cfg, { selfChatMode });
}
@@ -69,10 +50,7 @@ async function pathExists(filePath: string): Promise<boolean> {
}
}
async function detectWhatsAppLinked(
cfg: ClawdbotConfig,
accountId: string,
): Promise<boolean> {
async function detectWhatsAppLinked(cfg: ClawdbotConfig, accountId: string): Promise<boolean> {
const { authDir } = resolveWhatsAppAuthDir({ cfg, accountId });
const credsPath = path.join(authDir, "creds.json");
return await pathExists(credsPath);
@@ -86,8 +64,7 @@ async function promptWhatsAppAllowFrom(
): Promise<ClawdbotConfig> {
const existingPolicy = cfg.channels?.whatsapp?.dmPolicy ?? "pairing";
const existingAllowFrom = cfg.channels?.whatsapp?.allowFrom ?? [];
const existingLabel =
existingAllowFrom.length > 0 ? existingAllowFrom.join(", ") : "unset";
const existingLabel = existingAllowFrom.length > 0 ? existingAllowFrom.join(", ") : "unset";
const existingResponsePrefix = cfg.messages?.responsePrefix;
if (options?.forceAllowlist) {
@@ -96,8 +73,7 @@ async function promptWhatsAppAllowFrom(
"WhatsApp number",
);
const entry = await prompter.text({
message:
"Your personal WhatsApp number (the phone you will message from)",
message: "Your personal WhatsApp number (the phone you will message from)",
placeholder: "+15555550123",
initialValue: existingAllowFrom[0],
validate: (value) => {
@@ -164,8 +140,7 @@ async function promptWhatsAppAllowFrom(
"WhatsApp number",
);
const entry = await prompter.text({
message:
"Your personal WhatsApp number (the phone you will message from)",
message: "Your personal WhatsApp number (the phone you will message from)",
placeholder: "+15555550123",
initialValue: existingAllowFrom[0],
validate: (value) => {
@@ -274,9 +249,7 @@ async function promptWhatsAppAllowFrom(
.split(/[\n,;]+/g)
.map((p) => p.trim())
.filter(Boolean);
const normalized = parts.map((part) =>
part === "*" ? "*" : normalizeE164(part),
);
const normalized = parts.map((part) => (part === "*" ? "*" : normalizeE164(part)));
const unique = [...new Set(normalized.filter(Boolean))];
next = setWhatsAppAllowFrom(next, unique);
}
@@ -289,18 +262,13 @@ export const whatsappOnboardingAdapter: ChannelOnboardingAdapter = {
getStatus: async ({ cfg, accountOverrides }) => {
const overrideId = accountOverrides.whatsapp?.trim();
const defaultAccountId = resolveDefaultWhatsAppAccountId(cfg);
const accountId = overrideId
? normalizeAccountId(overrideId)
: defaultAccountId;
const accountId = overrideId ? normalizeAccountId(overrideId) : defaultAccountId;
const linked = await detectWhatsAppLinked(cfg, accountId);
const accountLabel =
accountId === DEFAULT_ACCOUNT_ID ? "default" : accountId;
const accountLabel = accountId === DEFAULT_ACCOUNT_ID ? "default" : accountId;
return {
channel,
configured: linked,
statusLines: [
`WhatsApp (${accountLabel}): ${linked ? "linked" : "not linked"}`,
],
statusLines: [`WhatsApp (${accountLabel}): ${linked ? "linked" : "not linked"}`],
selectionHint: linked ? "linked" : "not linked",
quickstartScore: linked ? 5 : 4,
};
@@ -343,9 +311,7 @@ export const whatsappOnboardingAdapter: ChannelOnboardingAdapter = {
...next.channels?.whatsapp?.accounts,
[accountId]: {
...next.channels?.whatsapp?.accounts?.[accountId],
enabled:
next.channels?.whatsapp?.accounts?.[accountId]?.enabled ??
true,
enabled: next.channels?.whatsapp?.accounts?.[accountId]?.enabled ?? true,
},
},
},
@@ -370,9 +336,7 @@ export const whatsappOnboardingAdapter: ChannelOnboardingAdapter = {
);
}
const wantsLink = await prompter.confirm({
message: linked
? "WhatsApp already linked. Re-link now?"
: "Link WhatsApp now (QR)?",
message: linked ? "WhatsApp already linked. Re-link now?" : "Link WhatsApp now (QR)?",
initialValue: !linked,
});
if (wantsLink) {
@@ -380,16 +344,10 @@ export const whatsappOnboardingAdapter: ChannelOnboardingAdapter = {
await loginWeb(false, undefined, runtime, accountId);
} catch (err) {
runtime.error(`WhatsApp login failed: ${String(err)}`);
await prompter.note(
`Docs: ${formatDocsLink("/whatsapp", "whatsapp")}`,
"WhatsApp help",
);
await prompter.note(`Docs: ${formatDocsLink("/whatsapp", "whatsapp")}`, "WhatsApp help");
}
} else if (!linked) {
await prompter.note(
"Run `clawdbot channels login` later to link WhatsApp.",
"WhatsApp",
);
await prompter.note("Run `clawdbot channels login` later to link WhatsApp.", "WhatsApp");
}
next = await promptWhatsAppAllowFrom(next, runtime, prompter, {