import { getChannelPlugin, normalizeChannelId } from "../../channels/plugins/index.js"; import type { ChannelId } from "../../channels/plugins/types.js"; import { writeConfigFile } from "../../config/config.js"; import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js"; import { defaultRuntime, type RuntimeEnv } from "../../runtime.js"; import { createClackPrompter } from "../../wizard/clack-prompter.js"; import { setupChannels } from "../onboard-channels.js"; import type { ChannelChoice } from "../onboard-types.js"; import { applyAccountName, applyChannelAccountConfig } from "./add-mutators.js"; import { channelLabel, requireValidConfig, shouldUseWizard } from "./shared.js"; export type ChannelsAddOptions = { channel?: string; account?: string; name?: string; token?: string; tokenFile?: string; botToken?: string; appToken?: string; signalNumber?: string; cliPath?: string; dbPath?: string; service?: "imessage" | "sms" | "auto"; region?: string; authDir?: string; httpUrl?: string; httpHost?: string; httpPort?: string; useEnv?: boolean; homeserver?: string; userId?: string; accessToken?: string; password?: string; deviceName?: string; initialSyncLimit?: number | string; }; export async function channelsAddCommand( opts: ChannelsAddOptions, runtime: RuntimeEnv = defaultRuntime, params?: { hasFlags?: boolean }, ) { const cfg = await requireValidConfig(runtime); if (!cfg) return; const useWizard = shouldUseWizard(params); if (useWizard) { const prompter = createClackPrompter(); let selection: ChannelChoice[] = []; const accountIds: Partial> = {}; await prompter.intro("Channel setup"); let nextConfig = await setupChannels(cfg, runtime, prompter, { allowDisable: false, allowSignalInstall: true, promptAccountIds: true, onSelection: (value) => { selection = value; }, onAccountId: (channel, accountId) => { accountIds[channel] = accountId; }, }); if (selection.length === 0) { await prompter.outro("No channels selected."); return; } const wantsNames = await prompter.confirm({ message: "Add display names for these accounts? (optional)", initialValue: false, }); if (wantsNames) { for (const channel of selection) { const accountId = accountIds[channel] ?? DEFAULT_ACCOUNT_ID; const plugin = getChannelPlugin(channel as ChannelId); const account = plugin?.config.resolveAccount(nextConfig, accountId) as | { name?: string } | undefined; const snapshot = plugin?.config.describeAccount?.(account, nextConfig); const existingName = snapshot?.name ?? account?.name; const name = await prompter.text({ message: `${channel} account name (${accountId})`, initialValue: existingName, }); if (name?.trim()) { nextConfig = applyAccountName({ cfg: nextConfig, channel, accountId, name, }); } } } await writeConfigFile(nextConfig); await prompter.outro("Channels updated."); return; } const channel = normalizeChannelId(opts.channel); if (!channel) { runtime.error(`Unknown channel: ${String(opts.channel ?? "")}`); runtime.exit(1); return; } const plugin = getChannelPlugin(channel); if (!plugin?.setup?.applyAccountConfig) { runtime.error(`Channel ${channel} does not support add.`); runtime.exit(1); return; } const accountId = plugin.setup.resolveAccountId?.({ cfg, accountId: opts.account }) ?? normalizeAccountId(opts.account); const useEnv = opts.useEnv === true; const initialSyncLimit = typeof opts.initialSyncLimit === "number" ? opts.initialSyncLimit : typeof opts.initialSyncLimit === "string" && opts.initialSyncLimit.trim() ? Number.parseInt(opts.initialSyncLimit, 10) : undefined; const validationError = plugin.setup.validateInput?.({ cfg, accountId, input: { name: opts.name, token: opts.token, tokenFile: opts.tokenFile, botToken: opts.botToken, appToken: opts.appToken, signalNumber: opts.signalNumber, cliPath: opts.cliPath, dbPath: opts.dbPath, service: opts.service, region: opts.region, authDir: opts.authDir, httpUrl: opts.httpUrl, httpHost: opts.httpHost, httpPort: opts.httpPort, homeserver: opts.homeserver, userId: opts.userId, accessToken: opts.accessToken, password: opts.password, deviceName: opts.deviceName, initialSyncLimit, useEnv, }, }); if (validationError) { runtime.error(validationError); runtime.exit(1); return; } const nextConfig = applyChannelAccountConfig({ cfg, channel, accountId, name: opts.name, token: opts.token, tokenFile: opts.tokenFile, botToken: opts.botToken, appToken: opts.appToken, signalNumber: opts.signalNumber, cliPath: opts.cliPath, dbPath: opts.dbPath, service: opts.service, region: opts.region, authDir: opts.authDir, httpUrl: opts.httpUrl, httpHost: opts.httpHost, httpPort: opts.httpPort, homeserver: opts.homeserver, userId: opts.userId, accessToken: opts.accessToken, password: opts.password, deviceName: opts.deviceName, initialSyncLimit, useEnv, }); await writeConfigFile(nextConfig); runtime.log(`Added ${channelLabel(channel)} account "${accountId}".`); }