mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 05:07:38 +00:00
213 lines
6.0 KiB
TypeScript
213 lines
6.0 KiB
TypeScript
import { writeConfigFile } from "../../config/config.js";
|
|
import { normalizeChatProviderId } from "../../providers/registry.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 { setupProviders } from "../onboard-providers.js";
|
|
import type { ProviderChoice } from "../onboard-types.js";
|
|
import {
|
|
applyAccountName,
|
|
applyProviderAccountConfig,
|
|
} from "./add-mutators.js";
|
|
import {
|
|
providerLabel,
|
|
requireValidConfig,
|
|
shouldUseWizard,
|
|
} from "./shared.js";
|
|
|
|
export type ProvidersAddOptions = {
|
|
provider?: 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;
|
|
};
|
|
|
|
export async function providersAddCommand(
|
|
opts: ProvidersAddOptions,
|
|
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: ProviderChoice[] = [];
|
|
const accountIds: Partial<Record<ProviderChoice, string>> = {};
|
|
await prompter.intro("Provider setup");
|
|
let nextConfig = await setupProviders(cfg, runtime, prompter, {
|
|
allowDisable: false,
|
|
allowSignalInstall: true,
|
|
promptAccountIds: true,
|
|
onSelection: (value) => {
|
|
selection = value;
|
|
},
|
|
onAccountId: (provider, accountId) => {
|
|
accountIds[provider] = accountId;
|
|
},
|
|
});
|
|
if (selection.length === 0) {
|
|
await prompter.outro("No providers selected.");
|
|
return;
|
|
}
|
|
|
|
const wantsNames = await prompter.confirm({
|
|
message: "Add display names for these accounts? (optional)",
|
|
initialValue: false,
|
|
});
|
|
if (wantsNames) {
|
|
for (const provider of selection) {
|
|
const accountId = accountIds[provider] ?? DEFAULT_ACCOUNT_ID;
|
|
const existingName =
|
|
provider === "whatsapp"
|
|
? nextConfig.whatsapp?.accounts?.[accountId]?.name
|
|
: provider === "telegram"
|
|
? (nextConfig.telegram?.accounts?.[accountId]?.name ??
|
|
nextConfig.telegram?.name)
|
|
: provider === "discord"
|
|
? (nextConfig.discord?.accounts?.[accountId]?.name ??
|
|
nextConfig.discord?.name)
|
|
: provider === "slack"
|
|
? (nextConfig.slack?.accounts?.[accountId]?.name ??
|
|
nextConfig.slack?.name)
|
|
: provider === "signal"
|
|
? (nextConfig.signal?.accounts?.[accountId]?.name ??
|
|
nextConfig.signal?.name)
|
|
: provider === "imessage"
|
|
? (nextConfig.imessage?.accounts?.[accountId]?.name ??
|
|
nextConfig.imessage?.name)
|
|
: undefined;
|
|
const name = await prompter.text({
|
|
message: `${provider} account name (${accountId})`,
|
|
initialValue: existingName,
|
|
});
|
|
if (name?.trim()) {
|
|
nextConfig = applyAccountName({
|
|
cfg: nextConfig,
|
|
provider,
|
|
accountId,
|
|
name,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
await writeConfigFile(nextConfig);
|
|
await prompter.outro("Providers updated.");
|
|
return;
|
|
}
|
|
|
|
const provider = normalizeChatProviderId(opts.provider);
|
|
if (!provider) {
|
|
runtime.error(`Unknown provider: ${String(opts.provider ?? "")}`);
|
|
runtime.exit(1);
|
|
return;
|
|
}
|
|
|
|
const accountId = normalizeAccountId(opts.account);
|
|
const useEnv = opts.useEnv === true;
|
|
|
|
if (provider === "telegram") {
|
|
if (useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
|
runtime.error(
|
|
"TELEGRAM_BOT_TOKEN can only be used for the default account.",
|
|
);
|
|
runtime.exit(1);
|
|
return;
|
|
}
|
|
if (!useEnv && !opts.token && !opts.tokenFile) {
|
|
runtime.error(
|
|
"Telegram requires --token or --token-file (or --use-env).",
|
|
);
|
|
runtime.exit(1);
|
|
return;
|
|
}
|
|
}
|
|
if (provider === "discord") {
|
|
if (useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
|
runtime.error(
|
|
"DISCORD_BOT_TOKEN can only be used for the default account.",
|
|
);
|
|
runtime.exit(1);
|
|
return;
|
|
}
|
|
if (!useEnv && !opts.token) {
|
|
runtime.error("Discord requires --token (or --use-env).");
|
|
runtime.exit(1);
|
|
return;
|
|
}
|
|
}
|
|
if (provider === "slack") {
|
|
if (useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
|
runtime.error(
|
|
"Slack env tokens can only be used for the default account.",
|
|
);
|
|
runtime.exit(1);
|
|
return;
|
|
}
|
|
if (!useEnv && (!opts.botToken || !opts.appToken)) {
|
|
runtime.error(
|
|
"Slack requires --bot-token and --app-token (or --use-env).",
|
|
);
|
|
runtime.exit(1);
|
|
return;
|
|
}
|
|
}
|
|
if (provider === "signal") {
|
|
if (
|
|
!opts.signalNumber &&
|
|
!opts.httpUrl &&
|
|
!opts.httpHost &&
|
|
!opts.httpPort &&
|
|
!opts.cliPath
|
|
) {
|
|
runtime.error(
|
|
"Signal requires --signal-number or --http-url/--http-host/--http-port/--cli-path.",
|
|
);
|
|
runtime.exit(1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const nextConfig = applyProviderAccountConfig({
|
|
cfg,
|
|
provider,
|
|
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,
|
|
useEnv,
|
|
});
|
|
|
|
await writeConfigFile(nextConfig);
|
|
runtime.log(`Added ${providerLabel(provider)} account "${accountId}".`);
|
|
}
|