import path from "node:path"; import { z } from "zod"; import { InstallRecordShape } from "./zod-schema.installs.js"; import { sensitive } from "./zod-schema.sensitive.js";
function isSafeRelativeModulePath(raw: string): boolean { const value = raw.trim(); if (!value) { returnfalse;
} // Hook modules are loaded via file-path resolution + dynamic import(). // Keep this strictly relative to a configured base dir to avoid path traversal and surprises. if (path.isAbsolute(value)) { returnfalse;
} if (value.startsWith("~")) { returnfalse;
} // Disallow URL-ish and drive-relative forms (e.g. "file:...", "C:foo"). if (value.includes(":")) { returnfalse;
} const parts = value.split(/[\\/]+/g); if (parts.some((part) => part === "..")) { returnfalse;
} returntrue;
}
const SafeRelativeModulePathSchema = z
.string()
.refine(isSafeRelativeModulePath, "module must be a safe relative path (no absolute paths)");
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(), // Keep this open-ended so runtime channel plugins (for example feishu) can be // referenced without hard-coding every channel id in the config schema. // Runtime still validates the resolved value against currently registered channels.
channel: z.string().trim().min(1).optional(),
to: z.string().optional(),
model: z.string().optional(),
thinking: z.string().optional(),
timeoutSeconds: z.number().int().positive().optional(),
transform: z
.object({
module: SafeRelativeModulePathSchema,
export: z.string().optional(),
})
.strict()
.optional(),
})
.strict()
.optional();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.