mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-29 02:41:07 +00:00
refactor: share zod setup validators across channels
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
createZodSetupInputValidator,
|
||||
createTopLevelChannelDmPolicySetter,
|
||||
normalizeAccountId,
|
||||
patchScopedAccountConfig,
|
||||
@@ -7,6 +8,7 @@ import {
|
||||
type DmPolicy,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/setup";
|
||||
import { z } from "zod";
|
||||
import { applyBlueBubblesConnectionConfig } from "./config-apply.js";
|
||||
|
||||
const channel = "bluebubbles" as const;
|
||||
@@ -14,6 +16,13 @@ const setBlueBubblesTopLevelDmPolicy = createTopLevelChannelDmPolicySetter({
|
||||
channel,
|
||||
});
|
||||
|
||||
const BlueBubblesSetupInputSchema = z
|
||||
.object({
|
||||
httpUrl: z.string().optional(),
|
||||
password: z.string().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export function setBlueBubblesDmPolicy(cfg: OpenClawConfig, dmPolicy: DmPolicy): OpenClawConfig {
|
||||
return setBlueBubblesTopLevelDmPolicy(cfg, dmPolicy);
|
||||
}
|
||||
@@ -42,18 +51,21 @@ export const blueBubblesSetupAdapter: ChannelSetupAdapter = {
|
||||
accountId,
|
||||
name,
|
||||
}),
|
||||
validateInput: ({ input }) => {
|
||||
if (!input.httpUrl && !input.password) {
|
||||
return "BlueBubbles requires --http-url and --password.";
|
||||
}
|
||||
if (!input.httpUrl) {
|
||||
return "BlueBubbles requires --http-url.";
|
||||
}
|
||||
if (!input.password) {
|
||||
return "BlueBubbles requires --password.";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
validateInput: createZodSetupInputValidator({
|
||||
schema: BlueBubblesSetupInputSchema,
|
||||
validate: ({ input }) => {
|
||||
if (!input.httpUrl && !input.password) {
|
||||
return "BlueBubbles requires --http-url and --password.";
|
||||
}
|
||||
if (!input.httpUrl) {
|
||||
return "BlueBubbles requires --http-url.";
|
||||
}
|
||||
if (!input.password) {
|
||||
return "BlueBubbles requires --password.";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
applyAccountConfig: ({ cfg, accountId, input }) => {
|
||||
const next = prepareScopedSetupConfig({
|
||||
cfg,
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
import { createPatchedAccountSetupAdapter, DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
|
||||
import {
|
||||
createPatchedAccountSetupAdapter,
|
||||
createZodSetupInputValidator,
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
} from "openclaw/plugin-sdk/setup";
|
||||
import { z } from "zod";
|
||||
|
||||
const channel = "googlechat" as const;
|
||||
|
||||
const GoogleChatSetupInputSchema = z
|
||||
.object({
|
||||
useEnv: z.boolean().optional(),
|
||||
token: z.string().optional(),
|
||||
tokenFile: z.string().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const googlechatSetupAdapter = createPatchedAccountSetupAdapter({
|
||||
channelKey: channel,
|
||||
validateInput: ({ accountId, input }) => {
|
||||
if (input.useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
||||
return "GOOGLE_CHAT_SERVICE_ACCOUNT env vars can only be used for the default account.";
|
||||
}
|
||||
if (!input.useEnv && !input.token && !input.tokenFile) {
|
||||
return "Google Chat requires --token (service account JSON) or --token-file.";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
validateInput: createZodSetupInputValidator({
|
||||
schema: GoogleChatSetupInputSchema,
|
||||
validate: ({ accountId, input }) => {
|
||||
if (input.useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
||||
return "GOOGLE_CHAT_SERVICE_ACCOUNT env vars can only be used for the default account.";
|
||||
}
|
||||
if (!input.useEnv && !input.token && !input.tokenFile) {
|
||||
return "Google Chat requires --token (service account JSON) or --token-file.";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
buildPatch: (input) => {
|
||||
const patch = input.useEnv
|
||||
? {}
|
||||
|
||||
@@ -3,10 +3,12 @@ import type { DmPolicy } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
|
||||
import {
|
||||
applyAccountNameToChannelSection,
|
||||
createZodSetupInputValidator,
|
||||
createTopLevelChannelAllowFromSetter,
|
||||
createTopLevelChannelDmPolicySetter,
|
||||
patchScopedAccountConfig,
|
||||
} from "openclaw/plugin-sdk/setup";
|
||||
import { z } from "zod";
|
||||
import type { CoreConfig, IrcAccountConfig, IrcNickServConfig } from "./types.js";
|
||||
|
||||
const channel = "irc" as const;
|
||||
@@ -28,6 +30,13 @@ type IrcSetupInput = ChannelSetupInput & {
|
||||
password?: string;
|
||||
};
|
||||
|
||||
const IrcSetupInputSchema = z
|
||||
.object({
|
||||
host: z.string().trim().min(1, "IRC requires host."),
|
||||
nick: z.string().trim().min(1, "IRC requires nick."),
|
||||
})
|
||||
.passthrough() as z.ZodType<IrcSetupInput>;
|
||||
|
||||
export function parsePort(raw: string, fallback: number): number {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) {
|
||||
@@ -101,16 +110,7 @@ export const ircSetupAdapter: ChannelSetupAdapter = {
|
||||
accountId,
|
||||
name,
|
||||
}),
|
||||
validateInput: ({ input }) => {
|
||||
const setupInput = input as IrcSetupInput;
|
||||
if (!setupInput.host?.trim()) {
|
||||
return "IRC requires host.";
|
||||
}
|
||||
if (!setupInput.nick?.trim()) {
|
||||
return "IRC requires nick.";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
validateInput: createZodSetupInputValidator({ schema: IrcSetupInputSchema }),
|
||||
applyAccountConfig: ({ cfg, accountId, input }) => {
|
||||
const setupInput = input as IrcSetupInput;
|
||||
const namedConfig = applyAccountNameToChannelSection({
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { ChannelSetupAdapter, OpenClawConfig } from "openclaw/plugin-sdk/setup";
|
||||
import { createZodSetupInputValidator } from "openclaw/plugin-sdk/setup";
|
||||
import { z } from "zod";
|
||||
import { hasLineCredentials, parseLineAllowFromId } from "./account-helpers.js";
|
||||
import {
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
@@ -10,6 +12,16 @@ import {
|
||||
|
||||
const channel = "line" as const;
|
||||
|
||||
const LineSetupInputSchema = z
|
||||
.object({
|
||||
useEnv: z.boolean().optional(),
|
||||
channelAccessToken: z.string().optional(),
|
||||
channelSecret: z.string().optional(),
|
||||
tokenFile: z.string().optional(),
|
||||
secretFile: z.string().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export function patchLineAccountConfig(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId: string;
|
||||
@@ -80,25 +92,21 @@ export const lineSetupAdapter: ChannelSetupAdapter = {
|
||||
accountId,
|
||||
patch: name?.trim() ? { name: name.trim() } : {},
|
||||
}),
|
||||
validateInput: ({ accountId, input }) => {
|
||||
const typedInput = input as {
|
||||
useEnv?: boolean;
|
||||
channelAccessToken?: string;
|
||||
channelSecret?: string;
|
||||
tokenFile?: string;
|
||||
secretFile?: string;
|
||||
};
|
||||
if (typedInput.useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
||||
return "LINE_CHANNEL_ACCESS_TOKEN can only be used for the default account.";
|
||||
}
|
||||
if (!typedInput.useEnv && !typedInput.channelAccessToken && !typedInput.tokenFile) {
|
||||
return "LINE requires channelAccessToken or --token-file (or --use-env).";
|
||||
}
|
||||
if (!typedInput.useEnv && !typedInput.channelSecret && !typedInput.secretFile) {
|
||||
return "LINE requires channelSecret or --secret-file (or --use-env).";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
validateInput: createZodSetupInputValidator({
|
||||
schema: LineSetupInputSchema,
|
||||
validate: ({ accountId, input }) => {
|
||||
if (input.useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
||||
return "LINE_CHANNEL_ACCESS_TOKEN can only be used for the default account.";
|
||||
}
|
||||
if (!input.useEnv && !input.channelAccessToken && !input.tokenFile) {
|
||||
return "LINE requires channelAccessToken or --token-file (or --use-env).";
|
||||
}
|
||||
if (!input.useEnv && !input.channelSecret && !input.secretFile) {
|
||||
return "LINE requires channelSecret or --secret-file (or --use-env).";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
applyAccountConfig: ({ cfg, accountId, input }) => {
|
||||
const typedInput = input as {
|
||||
useEnv?: boolean;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { ChannelSetupAdapter } from "openclaw/plugin-sdk/channel-setup";
|
||||
import { createZodSetupInputValidator } from "openclaw/plugin-sdk/setup";
|
||||
import { z } from "zod";
|
||||
import { resolveMattermostAccount, type ResolvedMattermostAccount } from "./mattermost/accounts.js";
|
||||
import { normalizeMattermostBaseUrl } from "./mattermost/client.js";
|
||||
import {
|
||||
@@ -13,6 +15,15 @@ import { hasConfiguredSecretInput } from "./secret-input.js";
|
||||
|
||||
const channel = "mattermost" as const;
|
||||
|
||||
const MattermostSetupInputSchema = z
|
||||
.object({
|
||||
useEnv: z.boolean().optional(),
|
||||
botToken: z.string().optional(),
|
||||
token: z.string().optional(),
|
||||
httpUrl: z.string().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export function isMattermostConfigured(account: ResolvedMattermostAccount): boolean {
|
||||
const tokenConfigured =
|
||||
Boolean(account.botToken?.trim()) || hasConfiguredSecretInput(account.config.botToken);
|
||||
@@ -36,20 +47,23 @@ export const mattermostSetupAdapter: ChannelSetupAdapter = {
|
||||
accountId,
|
||||
name,
|
||||
}),
|
||||
validateInput: ({ accountId, input }) => {
|
||||
const token = input.botToken ?? input.token;
|
||||
const baseUrl = normalizeMattermostBaseUrl(input.httpUrl);
|
||||
if (input.useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
||||
return "Mattermost env vars can only be used for the default account.";
|
||||
}
|
||||
if (!input.useEnv && (!token || !baseUrl)) {
|
||||
return "Mattermost requires --bot-token and --http-url (or --use-env).";
|
||||
}
|
||||
if (input.httpUrl && !baseUrl) {
|
||||
return "Mattermost --http-url must include a valid base URL.";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
validateInput: createZodSetupInputValidator({
|
||||
schema: MattermostSetupInputSchema,
|
||||
validate: ({ accountId, input }) => {
|
||||
const token = input.botToken ?? input.token;
|
||||
const baseUrl = normalizeMattermostBaseUrl(input.httpUrl);
|
||||
if (input.useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
||||
return "Mattermost env vars can only be used for the default account.";
|
||||
}
|
||||
if (!input.useEnv && (!token || !baseUrl)) {
|
||||
return "Mattermost requires --bot-token and --http-url (or --use-env).";
|
||||
}
|
||||
if (input.httpUrl && !baseUrl) {
|
||||
return "Mattermost --http-url must include a valid base URL.";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
applyAccountConfig: ({ cfg, accountId, input }) => {
|
||||
const token = input.botToken ?? input.token;
|
||||
const baseUrl = normalizeMattermostBaseUrl(input.httpUrl);
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/routing";
|
||||
import {
|
||||
applyAccountNameToChannelSection,
|
||||
createZodSetupInputValidator,
|
||||
patchScopedAccountConfig,
|
||||
} from "openclaw/plugin-sdk/setup";
|
||||
import {
|
||||
@@ -16,6 +17,7 @@ import type { ChannelSetupDmPolicy } from "openclaw/plugin-sdk/setup";
|
||||
import { type ChannelSetupWizard } from "openclaw/plugin-sdk/setup";
|
||||
import { formatDocsLink } from "openclaw/plugin-sdk/setup";
|
||||
import type { WizardPrompter } from "openclaw/plugin-sdk/setup";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
listNextcloudTalkAccountIds,
|
||||
resolveDefaultNextcloudTalkAccountId,
|
||||
@@ -32,6 +34,15 @@ type NextcloudSetupInput = ChannelSetupInput & {
|
||||
};
|
||||
type NextcloudTalkSection = NonNullable<CoreConfig["channels"]>["nextcloud-talk"];
|
||||
|
||||
const NextcloudSetupInputSchema = z
|
||||
.object({
|
||||
useEnv: z.boolean().optional(),
|
||||
baseUrl: z.string().optional(),
|
||||
secret: z.string().optional(),
|
||||
secretFile: z.string().optional(),
|
||||
})
|
||||
.passthrough() as z.ZodType<NextcloudSetupInput>;
|
||||
|
||||
export function normalizeNextcloudTalkBaseUrl(value: string | undefined): string {
|
||||
return value?.trim().replace(/\/+$/, "") ?? "";
|
||||
}
|
||||
@@ -181,19 +192,21 @@ export const nextcloudTalkSetupAdapter: ChannelSetupAdapter = {
|
||||
accountId,
|
||||
name,
|
||||
}),
|
||||
validateInput: ({ accountId, input }) => {
|
||||
const setupInput = input as NextcloudSetupInput;
|
||||
if (setupInput.useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
||||
return "NEXTCLOUD_TALK_BOT_SECRET can only be used for the default account.";
|
||||
}
|
||||
if (!setupInput.useEnv && !setupInput.secret && !setupInput.secretFile) {
|
||||
return "Nextcloud Talk requires bot secret or --secret-file (or --use-env).";
|
||||
}
|
||||
if (!setupInput.baseUrl) {
|
||||
return "Nextcloud Talk requires --base-url.";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
validateInput: createZodSetupInputValidator({
|
||||
schema: NextcloudSetupInputSchema,
|
||||
validate: ({ accountId, input }) => {
|
||||
if (input.useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
||||
return "NEXTCLOUD_TALK_BOT_SECRET can only be used for the default account.";
|
||||
}
|
||||
if (!input.useEnv && !input.secret && !input.secretFile) {
|
||||
return "Nextcloud Talk requires bot secret or --secret-file (or --use-env).";
|
||||
}
|
||||
if (!input.baseUrl) {
|
||||
return "Nextcloud Talk requires --base-url.";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
applyAccountConfig: ({ cfg, accountId, input }) => {
|
||||
const setupInput = input as NextcloudSetupInput;
|
||||
const namedConfig = applyAccountNameToChannelSection({
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
createDelegatedSetupWizardProxy,
|
||||
createDelegatedTextInputShouldPrompt,
|
||||
createPatchedAccountSetupAdapter,
|
||||
createZodSetupInputValidator,
|
||||
createTopLevelChannelDmPolicy,
|
||||
normalizeE164,
|
||||
parseSetupEntriesAllowingWildcard,
|
||||
@@ -18,6 +19,7 @@ import type {
|
||||
ChannelSetupWizardTextInput,
|
||||
} from "openclaw/plugin-sdk/setup";
|
||||
import { formatCliCommand, formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
listSignalAccountIds,
|
||||
resolveDefaultSignalAccountId,
|
||||
@@ -31,6 +33,16 @@ const DIGITS_ONLY = /^\d+$/;
|
||||
const INVALID_SIGNAL_ACCOUNT_ERROR =
|
||||
"Invalid E.164 phone number (must start with + and country code, e.g. +15555550123)";
|
||||
|
||||
const SignalSetupInputSchema = z
|
||||
.object({
|
||||
signalNumber: z.string().optional(),
|
||||
cliPath: z.string().optional(),
|
||||
httpUrl: z.string().optional(),
|
||||
httpHost: z.string().optional(),
|
||||
httpPort: z.string().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export function normalizeSignalAccountInput(value: string | null | undefined): string | null {
|
||||
const trimmed = value?.trim();
|
||||
if (!trimmed) {
|
||||
@@ -184,18 +196,21 @@ export const signalCompletionNote = {
|
||||
|
||||
export const signalSetupAdapter: ChannelSetupAdapter = createPatchedAccountSetupAdapter({
|
||||
channelKey: channel,
|
||||
validateInput: ({ input }) => {
|
||||
if (
|
||||
!input.signalNumber &&
|
||||
!input.httpUrl &&
|
||||
!input.httpHost &&
|
||||
!input.httpPort &&
|
||||
!input.cliPath
|
||||
) {
|
||||
return "Signal requires --signal-number or --http-url/--http-host/--http-port/--cli-path.";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
validateInput: createZodSetupInputValidator({
|
||||
schema: SignalSetupInputSchema,
|
||||
validate: ({ input }) => {
|
||||
if (
|
||||
!input.signalNumber &&
|
||||
!input.httpUrl &&
|
||||
!input.httpHost &&
|
||||
!input.httpPort &&
|
||||
!input.cliPath
|
||||
) {
|
||||
return "Signal requires --signal-number or --http-url/--http-host/--http-port/--cli-path.";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
buildPatch: (input) => buildSignalSetupPatch(input),
|
||||
});
|
||||
|
||||
|
||||
@@ -4,11 +4,13 @@ import {
|
||||
normalizeAccountId,
|
||||
patchScopedAccountConfig,
|
||||
prepareScopedSetupConfig,
|
||||
createZodSetupInputValidator,
|
||||
type ChannelSetupAdapter,
|
||||
type ChannelSetupInput,
|
||||
type ChannelSetupWizard,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/setup";
|
||||
import { z } from "zod";
|
||||
import { buildTlonAccountFields } from "./account-fields.js";
|
||||
import { normalizeShip } from "./targets.js";
|
||||
import { listTlonAccountIds, resolveTlonAccount, type TlonResolvedAccount } from "./types.js";
|
||||
@@ -29,6 +31,14 @@ export type TlonSetupInput = ChannelSetupInput & {
|
||||
ownerShip?: string;
|
||||
};
|
||||
|
||||
const TlonSetupInputSchema = z
|
||||
.object({
|
||||
ship: z.string().optional(),
|
||||
url: z.string().optional(),
|
||||
code: z.string().optional(),
|
||||
})
|
||||
.passthrough() as z.ZodType<TlonSetupInput>;
|
||||
|
||||
function isConfigured(account: TlonResolvedAccount): boolean {
|
||||
return Boolean(account.ship && account.url && account.code);
|
||||
}
|
||||
@@ -186,23 +196,25 @@ export const tlonSetupAdapter: ChannelSetupAdapter = {
|
||||
accountId,
|
||||
name,
|
||||
}),
|
||||
validateInput: ({ cfg, accountId, input }) => {
|
||||
const setupInput = input as TlonSetupInput;
|
||||
const resolved = resolveTlonAccount(cfg, accountId ?? undefined);
|
||||
const ship = setupInput.ship?.trim() || resolved.ship;
|
||||
const url = setupInput.url?.trim() || resolved.url;
|
||||
const code = setupInput.code?.trim() || resolved.code;
|
||||
if (!ship) {
|
||||
return "Tlon requires --ship.";
|
||||
}
|
||||
if (!url) {
|
||||
return "Tlon requires --url.";
|
||||
}
|
||||
if (!code) {
|
||||
return "Tlon requires --code.";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
validateInput: createZodSetupInputValidator({
|
||||
schema: TlonSetupInputSchema,
|
||||
validate: ({ cfg, accountId, input }) => {
|
||||
const resolved = resolveTlonAccount(cfg, accountId ?? undefined);
|
||||
const ship = input.ship?.trim() || resolved.ship;
|
||||
const url = input.url?.trim() || resolved.url;
|
||||
const code = input.code?.trim() || resolved.code;
|
||||
if (!ship) {
|
||||
return "Tlon requires --ship.";
|
||||
}
|
||||
if (!url) {
|
||||
return "Tlon requires --url.";
|
||||
}
|
||||
if (!code) {
|
||||
return "Tlon requires --code.";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
applyAccountConfig: ({ cfg, accountId, input }) =>
|
||||
applyTlonSetupConfig({
|
||||
cfg,
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
import { createPatchedAccountSetupAdapter, DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
|
||||
import {
|
||||
createPatchedAccountSetupAdapter,
|
||||
createZodSetupInputValidator,
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
} from "openclaw/plugin-sdk/setup";
|
||||
import { z } from "zod";
|
||||
|
||||
const channel = "zalo" as const;
|
||||
|
||||
const ZaloSetupInputSchema = z
|
||||
.object({
|
||||
useEnv: z.boolean().optional(),
|
||||
token: z.string().optional(),
|
||||
tokenFile: z.string().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const zaloSetupAdapter = createPatchedAccountSetupAdapter({
|
||||
channelKey: channel,
|
||||
validateInput: ({ accountId, input }) => {
|
||||
if (input.useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
||||
return "ZALO_BOT_TOKEN can only be used for the default account.";
|
||||
}
|
||||
if (!input.useEnv && !input.token && !input.tokenFile) {
|
||||
return "Zalo requires token or --token-file (or --use-env).";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
validateInput: createZodSetupInputValidator({
|
||||
schema: ZaloSetupInputSchema,
|
||||
validate: ({ accountId, input }) => {
|
||||
if (input.useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
||||
return "ZALO_BOT_TOKEN can only be used for the default account.";
|
||||
}
|
||||
if (!input.useEnv && !input.token && !input.tokenFile) {
|
||||
return "Zalo requires token or --token-file (or --use-env).";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
buildPatch: (input) =>
|
||||
input.useEnv
|
||||
? {}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ZodType } from "zod";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
|
||||
import type { ChannelSetupAdapter } from "./types.adapters.js";
|
||||
@@ -205,6 +206,24 @@ export function createPatchedAccountSetupAdapter(params: {
|
||||
};
|
||||
}
|
||||
|
||||
export function createZodSetupInputValidator<T extends ChannelSetupInput>(params: {
|
||||
schema: ZodType<T>;
|
||||
validate?: (params: { cfg: OpenClawConfig; accountId: string; input: T }) => string | null;
|
||||
}): NonNullable<ChannelSetupAdapter["validateInput"]> {
|
||||
return (inputParams) => {
|
||||
const parsed = params.schema.safeParse(inputParams.input);
|
||||
if (!parsed.success) {
|
||||
return parsed.error.issues[0]?.message ?? "invalid input";
|
||||
}
|
||||
return (
|
||||
params.validate?.({
|
||||
...inputParams,
|
||||
input: parsed.data,
|
||||
}) ?? null
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function createEnvPatchedAccountSetupAdapter(params: {
|
||||
channelKey: string;
|
||||
alwaysUseAccounts?: boolean;
|
||||
|
||||
@@ -29,6 +29,7 @@ export {
|
||||
applySetupAccountConfigPatch,
|
||||
createEnvPatchedAccountSetupAdapter,
|
||||
createPatchedAccountSetupAdapter,
|
||||
createZodSetupInputValidator,
|
||||
migrateBaseNameToDefaultAccount,
|
||||
patchScopedAccountConfig,
|
||||
prepareScopedSetupConfig,
|
||||
|
||||
Reference in New Issue
Block a user