Files
openclaw/src/config/zod-schema.hooks.ts
Henry Loenwind 96318641d8 fix: Finish credential redaction that was merged unfinished (#13073)
* Squash

* Removed unused files

Not mine, someone merged that stuff in earlier.

* fix: patch redaction regressions and schema breakages

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-13 16:19:21 +01:00

137 lines
3.9 KiB
TypeScript

import { z } from "zod";
import { sensitive } from "./zod-schema.sensitive.js";
export const HookMappingSchema = z
.object({
id: z.string().optional(),
match: z
.object({
path: z.string().optional(),
source: z.string().optional(),
})
.optional(),
action: z.union([z.literal("wake"), z.literal("agent")]).optional(),
wakeMode: z.union([z.literal("now"), z.literal("next-heartbeat")]).optional(),
name: z.string().optional(),
agentId: z.string().optional(),
sessionKey: z.string().optional().register(sensitive),
messageTemplate: z.string().optional(),
textTemplate: z.string().optional(),
deliver: z.boolean().optional(),
allowUnsafeExternalContent: z.boolean().optional(),
channel: z
.union([
z.literal("last"),
z.literal("whatsapp"),
z.literal("telegram"),
z.literal("discord"),
z.literal("irc"),
z.literal("slack"),
z.literal("signal"),
z.literal("imessage"),
z.literal("msteams"),
])
.optional(),
to: z.string().optional(),
model: z.string().optional(),
thinking: z.string().optional(),
timeoutSeconds: z.number().int().positive().optional(),
transform: z
.object({
module: z.string(),
export: z.string().optional(),
})
.strict()
.optional(),
})
.strict()
.optional();
export const InternalHookHandlerSchema = z
.object({
event: z.string(),
module: z.string(),
export: z.string().optional(),
})
.strict();
const HookConfigSchema = z
.object({
enabled: z.boolean().optional(),
env: z.record(z.string(), z.string()).optional(),
})
// Hook configs are intentionally open-ended (handlers can define their own keys).
// Keep enabled/env typed, but allow additional per-hook keys without marking the
// whole config invalid (which triggers doctor/best-effort loads).
.passthrough();
const HookInstallRecordSchema = z
.object({
source: z.union([z.literal("npm"), z.literal("archive"), z.literal("path")]),
spec: z.string().optional(),
sourcePath: z.string().optional(),
installPath: z.string().optional(),
version: z.string().optional(),
installedAt: z.string().optional(),
hooks: z.array(z.string()).optional(),
})
.strict();
export const InternalHooksSchema = z
.object({
enabled: z.boolean().optional(),
handlers: z.array(InternalHookHandlerSchema).optional(),
entries: z.record(z.string(), HookConfigSchema).optional(),
load: z
.object({
extraDirs: z.array(z.string()).optional(),
})
.strict()
.optional(),
installs: z.record(z.string(), HookInstallRecordSchema).optional(),
})
.strict()
.optional();
export const HooksGmailSchema = z
.object({
account: z.string().optional(),
label: z.string().optional(),
topic: z.string().optional(),
subscription: z.string().optional(),
pushToken: z.string().optional().register(sensitive),
hookUrl: z.string().optional(),
includeBody: z.boolean().optional(),
maxBytes: z.number().int().positive().optional(),
renewEveryMinutes: z.number().int().positive().optional(),
allowUnsafeExternalContent: z.boolean().optional(),
serve: z
.object({
bind: z.string().optional(),
port: z.number().int().positive().optional(),
path: z.string().optional(),
})
.strict()
.optional(),
tailscale: z
.object({
mode: z.union([z.literal("off"), z.literal("serve"), z.literal("funnel")]).optional(),
path: z.string().optional(),
target: z.string().optional(),
})
.strict()
.optional(),
model: z.string().optional(),
thinking: z
.union([
z.literal("off"),
z.literal("minimal"),
z.literal("low"),
z.literal("medium"),
z.literal("high"),
])
.optional(),
})
.strict()
.optional();