openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
161 lines (160 loc) • 8.84 kB
JavaScript
import { c as isRecord } from "./record-coerce-DItp3I4t.js";
import { F as hasExactOwnKeys } from "./worker-process-protocol-COVu2ppZ.js";
import path from "node:path";
//#region src/worker/node-workspace-protocol.ts
const IDENTIFIER_MAX_CHARS = 256;
const GATEWAY_NAMESPACE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
const REQUEST_MAX_BYTES = 262144;
const NODE_WORKER_WORKSPACE_STDIN_MAX_BYTES = 131072;
const OUTPUT_MAX_BYTES = 65536;
const STDERR_MAX_BYTES = 16384;
const ARGV_MAX_ITEMS = 128;
const ARG_MAX_BYTES = 131072;
const TIMEOUT_MAX_MS = 6e5;
function parseJson(raw) {
if (!raw || Buffer.byteLength(raw, "utf8") > REQUEST_MAX_BYTES) throw new Error("INVALID_REQUEST: invalid node worker workspace request");
try {
return JSON.parse(raw);
} catch {
throw new Error("INVALID_REQUEST: malformed node worker workspace request");
}
}
function requireIdentifier(value, label) {
if (typeof value !== "string" || value.length === 0 || value.length > IDENTIFIER_MAX_CHARS || value.trim() !== value || value.includes("\0")) throw new Error(`INVALID_REQUEST: ${label} must be a bounded non-empty identifier`);
return value;
}
function parseNodeWorkerWorkspaceExecInput(raw) {
const value = parseJson(raw);
if (!isRecord(value) || !hasExactOwnKeys(value, [
"gatewayNamespace",
"environmentId",
"sessionId",
"generation",
"argv"
], [
"input",
"timeoutMs",
"resetWorkspace",
"transfer",
"seed"
])) throw new Error("INVALID_REQUEST: invalid node worker workspace request");
const gatewayNamespace = requireIdentifier(value.gatewayNamespace, "gatewayNamespace");
if (!GATEWAY_NAMESPACE_PATTERN.test(gatewayNamespace)) throw new Error("INVALID_REQUEST: gatewayNamespace must be a safe bounded path component");
if (!Number.isSafeInteger(value.generation) || typeof value.generation !== "number" || value.generation < 0) throw new Error("INVALID_REQUEST: generation must be a non-negative safe integer");
if (!Array.isArray(value.argv) || value.argv.length === 0 || value.argv.length > ARGV_MAX_ITEMS || !value.argv.every((arg) => typeof arg === "string" && arg.length > 0 && !arg.includes("\0") && Buffer.byteLength(arg, "utf8") <= ARG_MAX_BYTES)) throw new Error("INVALID_REQUEST: argv must be a bounded non-empty string array");
if (value.input !== void 0 && (typeof value.input !== "string" || Buffer.byteLength(value.input, "utf8") > 131072)) throw new Error("INVALID_REQUEST: workspace command input exceeds its bound");
if (value.timeoutMs !== void 0 && (typeof value.timeoutMs !== "number" || !Number.isSafeInteger(value.timeoutMs) || value.timeoutMs < 1 || value.timeoutMs > TIMEOUT_MAX_MS)) throw new Error("INVALID_REQUEST: workspace command timeout is invalid");
if (value.resetWorkspace !== void 0 && typeof value.resetWorkspace !== "boolean") throw new Error("INVALID_REQUEST: resetWorkspace must be a boolean");
let seed;
if (value.seed !== void 0) {
if (value.transfer !== void 0 || value.resetWorkspace !== void 0) throw new Error("INVALID_REQUEST: workspace seed cannot combine with transfer or resetWorkspace");
if (!isRecord(value.seed) || typeof value.seed.key !== "string" || !/^[a-f0-9]{64}$/u.test(value.seed.key)) throw new Error("INVALID_REQUEST: workspace seed key must be a SHA-256 hex digest");
const { action, key, maxAgeMs } = value.seed;
if (action === "apply" && hasExactOwnKeys(value.seed, ["action", "key"])) seed = {
action,
key
};
else if (action === "store" && hasExactOwnKeys(value.seed, [
"action",
"key",
"maxAgeMs"
]) && typeof maxAgeMs === "number" && Number.isSafeInteger(maxAgeMs) && maxAgeMs >= 0) seed = {
action,
key,
maxAgeMs
};
else throw new Error("INVALID_REQUEST: workspace seed action or maxAgeMs is invalid");
}
let transfer;
if (value.transfer !== void 0) {
if (!isRecord(value.transfer)) throw new Error("INVALID_REQUEST: workspace transfer is invalid");
const direction = value.transfer.direction;
const token = value.transfer.token;
const manifestRef = value.transfer.manifestRef;
const baseManifestRef = value.transfer.baseManifestRef;
const validRef = (candidate) => typeof candidate === "string" && /^sha256:[a-f0-9]{64}$/u.test(candidate);
if (typeof token !== "string" || token.length === 0 || token.length > 1024 || token.includes("\0") || (direction === "download" ? !hasExactOwnKeys(value.transfer, [
"direction",
"token",
"manifestRef"
], ["attachments", "seedKey"]) || !validRef(manifestRef) || value.transfer.attachments !== void 0 && value.transfer.attachments !== true || value.transfer.seedKey !== void 0 && (typeof value.transfer.seedKey !== "string" || !/^[a-f0-9]{64}$/u.test(value.transfer.seedKey) || value.transfer.attachments !== void 0) : direction === "upload" ? !hasExactOwnKeys(value.transfer, [
"direction",
"token",
"baseManifestRef"
]) || !validRef(baseManifestRef) : true)) throw new Error("INVALID_REQUEST: workspace transfer is invalid");
transfer = direction === "download" ? {
direction,
token,
manifestRef,
...typeof value.transfer.seedKey === "string" ? { seedKey: value.transfer.seedKey } : {},
...value.transfer.attachments === true ? { attachments: true } : {}
} : {
direction: "upload",
token,
baseManifestRef
};
}
return {
gatewayNamespace,
environmentId: requireIdentifier(value.environmentId, "environmentId"),
sessionId: requireIdentifier(value.sessionId, "sessionId"),
generation: value.generation,
argv: [...value.argv],
...value.input === void 0 ? {} : { input: value.input },
...value.timeoutMs === void 0 ? {} : { timeoutMs: value.timeoutMs },
...value.resetWorkspace === void 0 ? {} : { resetWorkspace: value.resetWorkspace },
...transfer ? { transfer } : {},
...seed ? { seed } : {}
};
}
function isBoundedText(value, maxBytes) {
return typeof value === "string" && Buffer.byteLength(value, "utf8") <= maxBytes;
}
function isAbsoluteHostPath(value) {
return path.posix.isAbsolute(value) || path.win32.isAbsolute(value);
}
function parseNodeWorkerWorkspaceExecResult(value) {
if (!isRecord(value) || !hasExactOwnKeys(value, [
"workspaceDir",
"stdout",
"stderr",
"code",
"signal",
"killed",
"termination"
], [
"stdoutTruncatedBytes",
"stderrTruncatedBytes",
"noOutputTimedOut",
"outputLimitExceeded",
"outputErrorStream"
]) || typeof value.workspaceDir !== "string" || !isAbsoluteHostPath(value.workspaceDir) || value.workspaceDir.length > 4096 || !isBoundedText(value.stdout, OUTPUT_MAX_BYTES) || !isBoundedText(value.stderr, STDERR_MAX_BYTES) || value.code !== null && (!Number.isSafeInteger(value.code) || typeof value.code !== "number") || value.signal !== null && (typeof value.signal !== "string" || value.signal.length === 0 || value.signal.length > 32) || typeof value.killed !== "boolean" || value.termination !== "exit" && value.termination !== "timeout" && value.termination !== "no-output-timeout" && value.termination !== "signal") return null;
for (const key of ["stdoutTruncatedBytes", "stderrTruncatedBytes"]) {
const count = value[key];
if (count !== void 0 && (typeof count !== "number" || !Number.isSafeInteger(count) || count < 0)) return null;
}
if (value.noOutputTimedOut !== void 0 && typeof value.noOutputTimedOut !== "boolean" || value.outputLimitExceeded !== void 0 && typeof value.outputLimitExceeded !== "boolean" || value.outputErrorStream !== void 0 && value.outputErrorStream !== "stdout" && value.outputErrorStream !== "stderr") return null;
return value;
}
function projectNodeWorkerWorkspaceExecResult(workspaceDir, result) {
const parsed = parseNodeWorkerWorkspaceExecResult({
workspaceDir,
stdout: result.stdout,
stderr: result.stderr,
code: result.code,
signal: result.signal,
killed: result.killed,
termination: result.termination,
...result.stdoutTruncatedBytes === void 0 ? {} : { stdoutTruncatedBytes: result.stdoutTruncatedBytes },
...result.stderrTruncatedBytes === void 0 ? {} : { stderrTruncatedBytes: result.stderrTruncatedBytes },
...result.noOutputTimedOut === void 0 ? {} : { noOutputTimedOut: result.noOutputTimedOut },
...result.outputLimitExceeded === void 0 ? {} : { outputLimitExceeded: result.outputLimitExceeded },
...result.outputErrorStream === void 0 ? {} : { outputErrorStream: result.outputErrorStream }
});
if (!parsed) throw new Error("node worker workspace result violated its bounded contract");
return parsed;
}
const NODE_WORKER_WORKSPACE_STDOUT_MAX_BYTES = OUTPUT_MAX_BYTES;
const NODE_WORKER_WORKSPACE_STDERR_MAX_BYTES = STDERR_MAX_BYTES;
//#endregion
export { parseNodeWorkerWorkspaceExecResult as a, parseNodeWorkerWorkspaceExecInput as i, NODE_WORKER_WORKSPACE_STDIN_MAX_BYTES as n, projectNodeWorkerWorkspaceExecResult as o, NODE_WORKER_WORKSPACE_STDOUT_MAX_BYTES as r, NODE_WORKER_WORKSPACE_STDERR_MAX_BYTES as t };