refactor: build channel setup input generically

This commit is contained in:
Peter Steinberger
2026-04-22 18:57:10 +01:00
parent 1e61279b35
commit 5ad06d0b20
2 changed files with 41 additions and 88 deletions

View File

@@ -11,44 +11,6 @@ import { formatHelpExamples } from "./help-format.js";
type ChannelsCommandsModule = typeof import("../commands/channels.js");
const optionNamesAdd = [
"channel",
"account",
"name",
"token",
"privateKey",
"tokenFile",
"botToken",
"appToken",
"signalNumber",
"cliPath",
"dbPath",
"service",
"region",
"authDir",
"httpUrl",
"httpHost",
"httpPort",
"webhookPath",
"webhookUrl",
"audienceType",
"audience",
"useEnv",
"homeserver",
"userId",
"accessToken",
"password",
"deviceName",
"initialSyncLimit",
"ship",
"url",
"relayUrls",
"code",
"groupChannels",
"dmAllowlist",
"autoDiscoverChannels",
] as const;
const optionNamesRemove = ["channel", "account", "delete"] as const;
let channelsCommandsPromise: Promise<ChannelsCommandsModule> | undefined;
@@ -69,6 +31,10 @@ function runChannelsCommandWithDanger(action: () => Promise<void>, label: string
});
}
function getOptionNames(command: Command): string[] {
return command.options.map((option) => option.attributeName());
}
export function registerChannelsCli(program: Command) {
const channelNames = formatCliChannelOptions();
const channels = program
@@ -210,7 +176,7 @@ export function registerChannelsCli(program: Command) {
.action(async (opts, command) => {
await runChannelsCommand(async () => {
const { channelsAddCommand } = await loadChannelsCommands();
const hasFlags = hasExplicitOptions(command, optionNamesAdd);
const hasFlags = hasExplicitOptions(command, getOptionNames(command));
await channelsAddCommand(opts, defaultRuntime, { hasFlags });
});
});

View File

@@ -34,10 +34,9 @@ function loadOnboardChannels(): Promise<OnboardChannelsModule> {
export type ChannelsAddOptions = {
channel?: string;
account?: string;
initialSyncLimit?: number | string;
groupChannels?: string;
dmAllowlist?: string;
} & Omit<ChannelSetupInput, "groupChannels" | "dmAllowlist" | "initialSyncLimit">;
} & Record<string, unknown>;
const CHANNEL_ADD_CONTROL_OPTION_KEYS = new Set(["channel", "account"]);
async function resolveCatalogChannelEntry(raw: string, cfg: OpenClawConfig | null) {
const trimmed = normalizeOptionalLowercaseString(raw);
@@ -56,6 +55,38 @@ async function resolveCatalogChannelEntry(raw: string, cfg: OpenClawConfig | nul
});
}
function parseOptionalInt(value: unknown): number | undefined {
if (typeof value === "number") {
return value;
}
if (typeof value === "string" && value.trim()) {
return Number.parseInt(value, 10);
}
return undefined;
}
function parseOptionalDelimitedInput(value: unknown): string[] | undefined {
if (Array.isArray(value)) {
return value.filter((entry): entry is string => typeof entry === "string");
}
return parseOptionalDelimitedEntries(typeof value === "string" ? value : undefined);
}
function buildChannelSetupInput(opts: ChannelsAddOptions): ChannelSetupInput {
const input: Record<string, unknown> = {};
for (const [key, value] of Object.entries(opts)) {
if (CHANNEL_ADD_CONTROL_OPTION_KEYS.has(key) || value === undefined) {
continue;
}
input[key] = value;
}
input.initialSyncLimit = parseOptionalInt(opts.initialSyncLimit);
input.groupChannels = parseOptionalDelimitedInput(opts.groupChannels);
input.dmAllowlist = parseOptionalDelimitedInput(opts.dmAllowlist);
return input as ChannelSetupInput;
}
export async function channelsAddCommand(
opts: ChannelsAddOptions,
runtime: RuntimeEnv = defaultRuntime,
@@ -282,51 +313,7 @@ export async function channelsAddCommand(
runtime.exit(1);
return;
}
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 groupChannels = parseOptionalDelimitedEntries(opts.groupChannels);
const dmAllowlist = parseOptionalDelimitedEntries(opts.dmAllowlist);
const input: ChannelSetupInput = {
name: opts.name,
token: opts.token,
privateKey: opts.privateKey,
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,
webhookPath: opts.webhookPath,
webhookUrl: opts.webhookUrl,
audienceType: opts.audienceType,
audience: opts.audience,
homeserver: opts.homeserver,
userId: opts.userId,
accessToken: opts.accessToken,
password: opts.password,
deviceName: opts.deviceName,
initialSyncLimit,
useEnv,
ship: opts.ship,
url: opts.url,
relayUrls: opts.relayUrls,
code: opts.code,
groupChannels,
dmAllowlist,
autoDiscoverChannels: opts.autoDiscoverChannels,
};
const input = buildChannelSetupInput(opts);
const accountId =
plugin.setup.resolveAccountId?.({
cfg: nextConfig,