← Files Codex Browser RecorderARCHIVED FILE
skills/record-browser/scripts/recording-policy.mjs
2.57 KB · Sep 30, 2026 · 23:13 UTC
export const DEFAULT_RECORDING_DURATION_MS = 15_000;
export const MIN_RECORDING_DURATION_MS = 5_000;
export const MAX_RECORDING_DURATION_MS = 60_000;
export const RECORDING_HARD_LIMIT_MS = 65_000;
export const RECORDING_FPS = 10;
export const RECORDING_JPEG_QUALITY = 70;
export const RECORDING_MAX_DECODED_BYTES = 5 * 1024 * 1024;
export const RECORDING_MAX_HEIGHT = 720;
export const RECORDING_MAX_OUTPUT_BYTES = 500 * 1024 * 1024;
export const RECORDING_MAX_WIDTH = 1280;
export const DEFAULT_RECORDING_MODE = "interactive";
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
const RECORDING_MODES = new Set([
DEFAULT_RECORDING_MODE,
"unattended",
]);
class RecordingPolicyError extends Error {
constructor(code, message) {
super(message);
this.name = "RecordingPolicyError";
this.code = code;
}
}
export function originOf(value) {
try {
return new URL(value).origin;
} catch {
return null;
}
}
export function validateRecordingRequest({
durationMs = DEFAULT_RECORDING_DURATION_MS,
recordingMode = DEFAULT_RECORDING_MODE,
requirePointerEvents = false,
targetUrl,
}) {
let target;
try {
target = new URL(targetUrl);
} catch {
throw new RecordingPolicyError("invalid_target", "The recording target is not a valid URL");
}
if (target.username.length > 0 || target.password.length > 0) {
throw new RecordingPolicyError(
"target_credentials_present",
"The recording target must not contain URL credentials",
);
}
const secureTarget = target.protocol === "https:";
const loopbackTarget =
target.protocol === "http:" && LOOPBACK_HOSTS.has(target.hostname);
if (!secureTarget && !loopbackTarget) {
throw new RecordingPolicyError(
"target_scheme_not_allowed",
"The recording target must use HTTPS or an approved loopback origin",
);
}
if (
!Number.isInteger(durationMs) ||
durationMs < MIN_RECORDING_DURATION_MS ||
durationMs > MAX_RECORDING_DURATION_MS
) {
throw new RecordingPolicyError(
"invalid_duration",
"Recording duration must be between 5 and 60 seconds",
);
}
if (typeof requirePointerEvents !== "boolean") {
throw new RecordingPolicyError(
"invalid_configuration",
"Pointer-event requirements must be explicit",
);
}
if (!RECORDING_MODES.has(recordingMode)) {
throw new RecordingPolicyError(
"invalid_configuration",
"Recording mode must be interactive or unattended",
);
}
return {
approvedOrigin: target.origin,
durationMs,
recordingMode,
requirePointerEvents,
targetUrl,
};
}
SHA-256: 55335c9c329b274229c75700b8f09378de7b8679e9536867e9fe563d268ffd28