openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,041 lines (1,021 loc) • 40.2 kB
JavaScript
import { n as resolveOpenClawPackageRootSync } from "./openclaw-root-CfYY-fyD.js";
import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js";
import { p as redactSensitiveText } from "./redact-BtvPPfTi.js";
import { w as resolveStateDir } from "./paths-D2sRr1a_.js";
import { t as VERSION } from "./version-v1kuAkGj.js";
import { r as isExactSemverVersion, s as resolveNpmJsonEntries } from "./npm-registry-spec-CM_p1_uq.js";
import "./worker-admission-C1l9t3yY.js";
import { t as PROCESS_NODE_VERSION_CHECK } from "./node-version-WVgs0-1c.js";
import { r as runCommandWithTimeout } from "./exec-BIE-3oLG.js";
import { fs as validateWorkerAdmissionHandshake } from "./src-BiL5aQto.js";
import { i as normalizeScpRemotePath } from "./scp-host-3FoHUz1i.js";
import { a as compareWorkerBundlePaths, i as WORKER_BUNDLE_RSYNC_RECEIVER_PATH, n as WORKER_BUNDLE_GITHUB_EXEC_LAUNCHER_PATH, o as hashWorkerBundleManifest, r as WORKER_BUNDLE_MANIFEST_VERSION, t as WORKER_BUNDLE_ENTRY_PATH } from "./worker-bundle-hash-CxAJivBJ.js";
import { t as DEFAULT_WORKER_BUNDLE_ARCHIVE_LIMITS } from "./worker-bundle-limits-CF239rpc.js";
import { n as readWorkerBundleArchiveManifest } from "./worker-bundle-archive-BeoTON-C.js";
import { a as workerSshOptions, i as workerSshCommandOptions, o as workerSshRemoteCommand, r as runWorkerSshCandidates, t as prepareWorkerSsh } from "./ssh-Dn3RJoa6.js";
import { constants, createReadStream } from "node:fs";
import path from "node:path";
import fs$1 from "node:fs/promises";
import os from "node:os";
import { createHash, randomUUID } from "node:crypto";
import * as tar from "tar";
//#region src/gateway/worker-environments/bundle-staging.ts
const WORKER_DEPLOY_ARTIFACT_PATHS = [
WORKER_BUNDLE_GITHUB_EXEC_LAUNCHER_PATH,
WORKER_BUNDLE_ENTRY_PATH,
WORKER_BUNDLE_RSYNC_RECEIVER_PATH
];
async function stageWorkerDeployArtifact(params) {
const relativeSourcePath = `dist/worker/${params.artifactPath}`;
const sourcePath = path.join(params.sourceRoot, relativeSourcePath);
let expectedRealPath;
try {
expectedRealPath = await fs$1.realpath(sourcePath);
} catch (error) {
throw new Error(`OpenClaw worker deploy artifact is missing; build the running package at ${params.sourceRoot}`, { cause: error });
}
const expectedPath = path.resolve(params.sourceRoot, relativeSourcePath);
if (expectedRealPath !== expectedPath) throw new Error(`Unsafe worker deploy artifact: ${relativeSourcePath}`);
const initialStats = await fs$1.lstat(sourcePath);
if (initialStats.isSymbolicLink() || !initialStats.isFile()) throw new Error(`Unsafe worker deploy artifact: ${relativeSourcePath}`);
const handle = await fs$1.open(sourcePath, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
let contents;
try {
const openedStats = await handle.stat();
const currentStats = await fs$1.lstat(sourcePath);
const currentRealPath = await fs$1.realpath(sourcePath);
if (!openedStats.isFile() || currentStats.isSymbolicLink() || !currentStats.isFile() || currentRealPath !== expectedRealPath || currentStats.dev !== openedStats.dev || currentStats.ino !== openedStats.ino) throw new Error(`Worker deploy artifact changed while packaging: ${relativeSourcePath}`);
contents = await handle.readFile();
} finally {
await handle.close();
}
const stagedPath = path.join(params.stagingRoot, params.artifactPath);
await fs$1.writeFile(stagedPath, contents, { mode: 448 });
await fs$1.chmod(stagedPath, 448);
return {
path: params.artifactPath,
mode: 448,
size: contents.byteLength,
sha256: createHash("sha256").update(contents).digest("hex")
};
}
async function collectWorkerBundleManifest(sourceRoot, stagingRoot) {
const manifest = [];
for (const artifactPath of WORKER_DEPLOY_ARTIFACT_PATHS) manifest.push(await stageWorkerDeployArtifact({
sourceRoot,
stagingRoot,
artifactPath
}));
return manifest;
}
//#endregion
//#region src/gateway/worker-environments/bundle.ts
const OPENCLAW_NPM_REGISTRY = "https://registry.npmjs.org/";
const NPM_RELEASE_PROOF_TIMEOUT_MS = 6e4;
const NPM_SHA512_INTEGRITY_PATTERN = /^sha512-[A-Za-z0-9+/]{86}==$/u;
const BUNDLE_TARBALL_NAME_PATTERN = /^([a-f0-9]{64})\.tgz$/u;
const BUNDLE_STAGING_NAME_PATTERN = /^\.staging-[A-Za-z0-9_-]+$/u;
const BUNDLE_TEMP_NAME_PATTERN = /^[a-f0-9]{64}\.tgz\.[0-9]+\.[0-9a-f-]{36}\.tmp$/u;
function normalizeProtocolFeatures(features) {
const normalized = features.map((feature) => feature.trim());
if (normalized.some((feature) => feature.length === 0)) throw new Error("Worker protocol features must be non-empty strings");
return [...new Set(normalized)].toSorted(compareWorkerBundlePaths);
}
function resolveBundleCacheDir(cacheDir) {
return cacheDir ? path.resolve(cacheDir) : path.join(resolveStateDir(), "cache", "worker-bundles");
}
function resolvePackageRoot(packageRoot) {
if (packageRoot) return path.resolve(packageRoot);
const resolved = resolveOpenClawPackageRootSync({
moduleUrl: import.meta.url,
argv1: process.argv[1],
cwd: process.cwd()
});
if (!resolved) throw new Error("Unable to locate the running OpenClaw package root for worker bundling");
return resolved;
}
async function isReleasedPackageInstall(packageRoot) {
const entries = new Set(await fs$1.readdir(packageRoot));
return !entries.has(".git") && !entries.has("pnpm-lock.yaml") && !entries.has("bun.lock") && !entries.has("bun.lockb");
}
function readNonEmptyString(record, key) {
const value = record[key];
return typeof value === "string" && value.trim() ? value.trim() : void 0;
}
function parseNpmPackageIdentity(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) return;
const record = value;
const name = readNonEmptyString(record, "name");
const version = readNonEmptyString(record, "version");
const integrity = readNonEmptyString(record, "integrity") ?? readNonEmptyString(record, "dist.integrity");
const filename = readNonEmptyString(record, "filename");
return name && version && integrity ? {
name,
version,
integrity,
filename
} : void 0;
}
function unwrapNpmJsonEntry(value) {
return resolveNpmJsonEntries(value)[0];
}
async function runNpmProofCommand(params) {
let result;
try {
result = await params.runCommand(params.argv, {
cwd: params.cwd,
timeoutMs: NPM_RELEASE_PROOF_TIMEOUT_MS,
env: {
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0",
NPM_CONFIG_IGNORE_SCRIPTS: "true"
}
});
} catch {
throw new Error(params.failureMessage);
}
if (result.code !== 0 || result.stdoutTruncatedBytes) throw new Error(params.failureMessage);
try {
return JSON.parse(result.stdout.trim());
} catch {
throw new Error(params.failureMessage);
}
}
async function updateHashFromFile(hash, filePath) {
for await (const chunk of createReadStream(filePath)) hash.update(chunk);
}
async function hashNpmTarballIntegrity(tarballPath) {
const hash = createHash("sha512");
await updateHashFromFile(hash, tarballPath);
return `sha512-${hash.digest("base64")}`;
}
async function hashWorkerBundleTarball(tarballPath) {
const hash = createHash("sha256");
await updateHashFromFile(hash, tarballPath);
return hash.digest("hex");
}
async function verifyPublishedNpmRelease(params) {
const runCommand = params.runCommand ?? runCommandWithTimeout;
const temporaryRoot = await fs$1.mkdtemp(path.join(os.tmpdir(), "openclaw-worker-npm-proof-"));
try {
const published = parseNpmPackageIdentity(unwrapNpmJsonEntry(await runNpmProofCommand({
argv: [
"npm",
"view",
`openclaw@${params.version}`,
"name",
"version",
"dist.integrity",
"--json",
`--registry=${OPENCLAW_NPM_REGISTRY}`
],
cwd: temporaryRoot,
failureMessage: `OpenClaw ${params.version} is not published; use the worker bundle install`,
runCommand
})));
if (published?.name !== "openclaw" || published.version !== params.version || !NPM_SHA512_INTEGRITY_PATTERN.test(published.integrity)) throw new Error(`Cannot verify exact public npm release openclaw@${params.version}; use the worker bundle install`);
const packed = parseNpmPackageIdentity(unwrapNpmJsonEntry(await runNpmProofCommand({
argv: [
"npm",
"pack",
`openclaw@${params.version}`,
"--pack-destination",
temporaryRoot,
"--ignore-scripts",
"--json",
`--registry=${OPENCLAW_NPM_REGISTRY}`
],
cwd: temporaryRoot,
failureMessage: "Unable to verify the installed OpenClaw package; use the worker bundle install",
runCommand
})));
if (!packed?.filename || path.basename(packed.filename) !== packed.filename) throw new Error("npm pack returned incomplete worker package metadata");
const packedTarballPath = path.join(temporaryRoot, packed.filename);
let packedTarballIntegrity;
try {
packedTarballIntegrity = await hashNpmTarballIntegrity(packedTarballPath);
} catch {
throw new Error("Unable to verify the installed OpenClaw package; use the worker bundle install");
}
if (packed.name !== published.name || packed.version !== published.version || packed.integrity !== published.integrity || packedTarballIntegrity !== published.integrity) throw new Error(`Installed OpenClaw ${params.version} does not match the published package; use the worker bundle install`);
const extractedRoot = path.join(temporaryRoot, "package");
await fs$1.mkdir(extractedRoot);
await tar.extract({
cwd: extractedRoot,
file: packedTarballPath,
preservePaths: false,
strict: true,
strip: 1
});
if ((await prepareWorkerBundle({
packageRoot: extractedRoot,
cacheDir: path.join(temporaryRoot, "bundle-cache"),
openclawVersion: params.version
})).bundleHash !== params.bundleHash) throw new Error(`Published OpenClaw ${params.version} does not match the prepared worker bundle; use the worker bundle install`);
return published.integrity;
} finally {
await fs$1.rm(temporaryRoot, {
recursive: true,
force: true
});
}
}
function manifestsMatch(left, right) {
return left.length === right.length && left.every((entry, index) => {
const other = right[index];
return other !== void 0 && entry.path === other.path && entry.mode === other.mode && entry.size === other.size && entry.sha256 === other.sha256;
});
}
async function isCachedTarball(filePath) {
try {
const stats = await fs$1.lstat(filePath);
if (stats.isSymbolicLink() || !stats.isFile()) throw new Error(`Unsafe worker bundle cache path: ${filePath}`);
return true;
} catch (error) {
if (error.code === "ENOENT") return false;
throw error;
}
}
async function cachedTarballMatches(tarballPath, manifest) {
if (!await isCachedTarball(tarballPath)) return false;
try {
return manifestsMatch(await readWorkerBundleArchiveManifest(tarballPath, DEFAULT_WORKER_BUNDLE_ARCHIVE_LIMITS), manifest);
} catch {
return false;
}
}
async function writeTarball(params) {
const temporaryPath = `${params.tarballPath}.${process.pid}.${randomUUID()}.tmp`;
try {
await tar.create({
cwd: params.stagingRoot,
file: temporaryPath,
gzip: true,
noDirRecurse: true,
noMtime: true,
portable: true,
strict: true,
onWriteEntry: ({ stat }) => {
if (stat) stat.mode = stat.mode & -512 | 448;
}
}, params.entries.map((entry) => entry.path));
try {
await fs$1.rename(temporaryPath, params.tarballPath);
} catch (error) {
if (error.code !== "EEXIST") throw error;
if (!await cachedTarballMatches(params.tarballPath, params.entries)) {
await fs$1.rm(params.tarballPath, { force: true });
try {
await fs$1.rename(temporaryPath, params.tarballPath);
} catch (publishError) {
if (publishError.code !== "EEXIST" || !await cachedTarballMatches(params.tarballPath, params.entries)) throw publishError;
}
}
}
} finally {
await fs$1.rm(temporaryPath, { force: true });
}
}
async function pruneWorkerBundleCache(params) {
const retained = new Set([params.currentBundleHash, ...params.retainedBundleHashes].filter((hash) => /^[a-f0-9]{64}$/u.test(hash)));
let entries;
try {
entries = await fs$1.readdir(params.cacheDir, { withFileTypes: true });
} catch (error) {
if (error.code !== "ENOENT") params.onError?.(error);
return;
}
for (const entry of entries.toSorted((left, right) => compareWorkerBundlePaths(left.name, right.name))) {
const tarball = BUNDLE_TARBALL_NAME_PATTERN.exec(entry.name);
const removableTarball = tarball && !retained.has(tarball[1]);
const removableStaging = BUNDLE_STAGING_NAME_PATTERN.test(entry.name);
const removableTemp = BUNDLE_TEMP_NAME_PATTERN.test(entry.name);
if (!removableTarball && !removableStaging && !removableTemp) continue;
const target = path.join(params.cacheDir, entry.name);
try {
const stats = await fs$1.lstat(target);
if (stats.isSymbolicLink()) continue;
if (removableStaging ? !stats.isDirectory() : !stats.isFile()) continue;
await fs$1.rm(target, {
recursive: removableStaging,
force: true
});
} catch (error) {
if (error.code !== "ENOENT") params.onError?.(error);
}
}
}
async function prepareWorkerBundle(options) {
const packageRoot = resolvePackageRoot(options.packageRoot);
const cacheDir = resolveBundleCacheDir(options.cacheDir);
const openclawVersion = (options.openclawVersion ?? VERSION).trim();
if (!openclawVersion) throw new Error("Worker bundle requires a non-empty OpenClaw version");
const protocolFeatures = normalizeProtocolFeatures(options.protocolFeatures ?? []);
await fs$1.mkdir(cacheDir, { recursive: true });
const stagingRoot = await fs$1.mkdtemp(path.join(cacheDir, ".staging-"));
try {
const manifest = await collectWorkerBundleManifest(packageRoot, stagingRoot);
const bundleHash = hashWorkerBundleManifest(manifest);
const tarballPath = path.join(cacheDir, `${bundleHash}.tgz`);
if (!await cachedTarballMatches(tarballPath, manifest)) await writeTarball({
stagingRoot,
entries: manifest,
tarballPath
});
return {
install: "bundle",
bundleHash,
openclawVersion,
protocolFeatures,
tarballBytes: (await fs$1.stat(tarballPath)).size,
tarballSha256: await hashWorkerBundleTarball(tarballPath),
tarballPath
};
} finally {
await fs$1.rm(stagingRoot, {
recursive: true,
force: true
});
}
}
/** Creates a process-lifecycle bundle producer that scans the running build at most once. */
function createWorkerBundleProducer(options = {}) {
let prepared;
let currentArtifact;
let pruning = Promise.resolve();
return {
prepare() {
if (!prepared) {
const pending = prepareWorkerBundle(options).then((artifact) => {
currentArtifact = artifact;
return artifact;
}).catch((error) => {
if (prepared === pending) prepared = void 0;
throw error;
});
prepared = pending;
}
return prepared;
},
async prune(retainedBundleHashes) {
const artifact = currentArtifact;
if (options.cacheOwnership !== "exclusive" || !artifact) return;
const operation = pruning.then(async () => {
await pruneWorkerBundleCache({
cacheDir: resolveBundleCacheDir(options.cacheDir),
currentBundleHash: artifact.bundleHash,
retainedBundleHashes,
onError: options.onCacheCleanupError
});
});
pruning = operation.catch(() => void 0);
await operation;
}
};
}
/**
* Selects the exact npm package only after the public tarball's canonical worker manifest proves
* parity with the running gateway bundle.
*/
async function resolveWorkerNpmInstallationArtifact(params) {
const version = params.bundle.openclawVersion.trim();
if (!isExactSemverVersion(version)) throw new Error(`Worker npm install requires the exact published gateway version; expected ${version}`);
const packageRoot = resolvePackageRoot(params.packageRoot);
if (!(params.isPackageInstall ? await params.isPackageInstall(packageRoot) : await isReleasedPackageInstall(packageRoot))) throw new Error("Worker npm install requires the gateway to run from a packaged release install");
const packageIntegrity = await (params.verifyRelease ?? verifyPublishedNpmRelease)({
bundleHash: params.bundle.bundleHash,
version
});
return {
install: "npm",
bundleHash: params.bundle.bundleHash,
openclawVersion: version,
packageIntegrity,
protocolFeatures: params.bundle.protocolFeatures,
packageSpec: `openclaw@${version}`
};
}
//#endregion
//#region src/gateway/worker-environments/bootstrap.ts
const BOOTSTRAP_ROOT = ".openclaw-worker";
const BOOTSTRAP_RECEIPT = "bootstrap-receipt.json";
const DEFAULT_BOOTSTRAP_TIMEOUT_MS = 6e5;
const BUNDLE_TRANSFER_MIN_THROUGHPUT_BYTES_PER_SECOND = 125e3;
const BUNDLE_TRANSFER_TIMEOUT_MAX_MS = 36e5;
const BOOTSTRAP_OPERATION_HEADROOM_MS = 3e5;
const NODE_MISSING_EXIT_CODE = 42;
const NPM_MISSING_EXIT_CODE = 43;
const LOCK_TIMEOUT_EXIT_CODE = 44;
const NODE_UNSUPPORTED_EXIT_CODE = 45;
const LOCK_MAX_AGE_SECONDS = 3600;
const NODE_MISSING_MARKER = "OPENCLAW_WORKER_NODE_MISSING";
const NODE_UNSUPPORTED_MARKER = "OPENCLAW_WORKER_NODE_UNSUPPORTED";
const NPM_MISSING_MARKER = "OPENCLAW_WORKER_NPM_MISSING";
const BOOTSTRAP_OUTPUT_TAG = "OPENCLAW_WORKER_BOOTSTRAP_V1";
const BUNDLE_HASH_PATTERN = /^[a-f0-9]{64}$/u;
const NPM_INTEGRITY_PATTERN = /^sha512-[A-Za-z0-9+/]{86}==$/u;
const WORKER_BUNDLE_ARTIFACT_PATHS = [
WORKER_BUNDLE_GITHUB_EXEC_LAUNCHER_PATH,
WORKER_BUNDLE_ENTRY_PATH,
WORKER_BUNDLE_RSYNC_RECEIVER_PATH
];
function bundleTransferTimeoutMs(tarballBytes, floorMs) {
if (!Number.isSafeInteger(tarballBytes) || tarballBytes < 0) throw new Error("Worker bundle artifact has an invalid tarball size");
return Math.min(BUNDLE_TRANSFER_TIMEOUT_MAX_MS, Math.max(floorMs, Math.ceil(tarballBytes / BUNDLE_TRANSFER_MIN_THROUGHPUT_BYTES_PER_SECOND) * 1e3));
}
/** Bounds the complete bootstrap lifecycle without preempting any permitted phase. */
function workerBootstrapOperationTimeoutMs(artifact) {
return DEFAULT_BOOTSTRAP_TIMEOUT_MS * 3 + (artifact.install === "bundle" ? bundleTransferTimeoutMs(artifact.tarballBytes, DEFAULT_BOOTSTRAP_TIMEOUT_MS) : 0) + BOOTSTRAP_OPERATION_HEADROOM_MS;
}
const NODE_RUNTIME_CHECK_JS = String.raw`const parse = (value) => /^(\d+)\.(\d+)\.(\d+)$/.exec(value)?.slice(1).map(Number); const atLeast = (version, floor) => version[0] > floor[0] || (version[0] === floor[0] && (version[1] > floor[1] || (version[1] === floor[1] && version[2] >= floor[2])));
const nodeSafe = ${PROCESS_NODE_VERSION_CHECK};
if (!nodeSafe) process.exit(1);
try { const { DatabaseSync } = require("node:sqlite"); const db = new DatabaseSync(":memory:");
const sqlite = parse(String(db.prepare("SELECT sqlite_version() AS version").get()?.version ?? ""));
db.close(); if (!sqlite) process.exit(1);
const sqliteSafe = atLeast(sqlite, [3, 51, 3]) || (sqlite[0] === 3 && ((sqlite[1] === 50 && sqlite[2] >= 7) || (sqlite[1] === 44 && sqlite[2] >= 6)));
process.exit(sqliteSafe ? 0 : 1); } catch { process.exit(1); }`;
const RECEIPT_MATCH_JS = String.raw`const fs = require("node:fs");
try {
const actual = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
const expected = JSON.parse(process.argv[2]);
const shapeMatches =
Object.keys(actual).sort().join(",") === "bundleHash,openclawVersion,protocolFeatures";
const featuresMatch =
Array.isArray(actual.protocolFeatures) &&
Array.isArray(expected.protocolFeatures) &&
actual.protocolFeatures.length === expected.protocolFeatures.length &&
actual.protocolFeatures.every((feature, index) => feature === expected.protocolFeatures[index]);
process.exit(
shapeMatches &&
actual.bundleHash === expected.bundleHash &&
actual.openclawVersion === expected.openclawVersion &&
featuresMatch
? 0
: 1,
);
} catch {
process.exit(1);
}`;
const VERIFY_ARCHIVE_JS = String.raw`const crypto = require("node:crypto");
const fs = require("node:fs");
try {
const actual = crypto.createHash("sha256").update(fs.readFileSync(process.argv[1])).digest("hex");
process.exit(actual === process.argv[2] ? 0 : 1);
} catch {
process.exit(1);
}`;
const VERIFY_NPM_PACKAGE_JS = String.raw`const crypto = require("node:crypto");
const fs = require("node:fs");
try {
const actual = "sha512-" + crypto.createHash("sha512").update(fs.readFileSync(process.argv[1])).digest("base64");
process.exit(actual === process.argv[2] ? 0 : 1);
} catch {
process.exit(1);
}`;
const READ_NPM_PACK_FILENAME_JS = String.raw`const fs = require("node:fs");
const path = require("node:path");
try {
const value = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
const filename = Array.isArray(value) && value.length === 1 ? value[0]?.filename : undefined;
if (typeof filename !== "string" || !filename || path.basename(filename) !== filename) {
process.exit(1);
}
process.stdout.write(filename);
} catch {
process.exit(1);
}`;
const VERIFY_INSTALL_JS = String.raw`const crypto = require("node:crypto");
const fs = require("node:fs");
const path = require("node:path");
const root = process.argv[1];
const expected = process.argv[2];
const install = process.argv[3];
const artifactPaths = ${JSON.stringify(WORKER_BUNDLE_ARTIFACT_PATHS)};
const entries = [];
function fail(message) {
throw new Error(message);
}
function assertRoot() {
const stats = fs.lstatSync(root);
if (stats.isSymbolicLink() || !stats.isDirectory()) {
fail("unsafe worker install root");
}
fs.chmodSync(root, 0o700);
}
function assertDirectory(relative) {
const absolute = path.join(root, ...relative.split("/"));
const stats = fs.lstatSync(absolute);
if (stats.isSymbolicLink() || !stats.isDirectory()) {
fail("unsafe worker directory: " + relative);
}
fs.chmodSync(absolute, 0o700);
}
function addFile(relative) {
const parts = relative.split("/");
for (let index = 1; index < parts.length; index += 1) {
assertDirectory(parts.slice(0, index).join("/"));
}
const absolute = path.join(root, ...relative.split("/"));
const stats = fs.lstatSync(absolute);
if (stats.isSymbolicLink() || !stats.isFile()) {
fail("unsafe worker file: " + relative);
}
const contents = fs.readFileSync(absolute);
const mode = artifactPaths.includes(relative) || (stats.mode & 0o111) !== 0 ? 0o700 : 0o600;
fs.chmodSync(absolute, mode);
entries.push({
path: relative,
mode,
size: contents.byteLength,
sha256: crypto.createHash("sha256").update(contents).digest("hex"),
});
}
try {
assertRoot();
if (install === "npm" || install === "bundle") {
const allowedPaths = new Set([...artifactPaths, "bootstrap-receipt.json"]);
for (const name of fs.readdirSync(root)) {
if (!allowedPaths.has(name)) {
fail("unexpected worker bundle path: " + name);
}
}
for (const artifactPath of artifactPaths) addFile(artifactPath);
} else {
fail("invalid worker install channel");
}
entries.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
const separator = String.fromCharCode(0);
const hash = crypto.createHash("sha256");
hash.update("${WORKER_BUNDLE_MANIFEST_VERSION}" + separator);
for (const entry of entries) {
hash.update(entry.path + separator + entry.mode.toString(8) + separator + entry.size + separator + entry.sha256 + separator);
}
process.exit(hash.digest("hex") === expected ? 0 : 1);
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
}`;
const PREFLIGHT_SCRIPT = String.raw`set -eu
umask 077
hash=$1
expected_receipt=$2
install=$3
operation_token=$4
root=$HOME/${BOOTSTRAP_ROOT}
install_dir=$root/$hash
receipt=$install_dir/${BOOTSTRAP_RECEIPT}
case "$operation_token" in
*[!a-f0-9]*|'') printf '%s\n' 'invalid worker bootstrap operation token' >&2; exit 2 ;;
esac
if [ "${"${"}#operation_token}" -ne 64 ]; then
printf '%s\n' 'invalid worker bootstrap operation token' >&2
exit 2
fi
ensure_private_directory() {
directory=$1
if [ -e "$directory" ] || [ -L "$directory" ]; then
if [ ! -d "$directory" ] || [ -L "$directory" ]; then
printf '%s\n' 'unsafe worker bootstrap directory' >&2
exit 2
fi
else
mkdir "$directory"
fi
chmod 700 "$directory"
}
ensure_private_directory "$root"
if ! command -v node >/dev/null 2>&1; then
printf '%s\n' '${NODE_MISSING_MARKER}' >&2
exit ${NODE_MISSING_EXIT_CODE}
fi
if ! node -e '${NODE_RUNTIME_CHECK_JS}'; then
printf '%s: ' '${NODE_UNSUPPORTED_MARKER}' >&2
node --version >&2 || true
exit ${NODE_UNSUPPORTED_EXIT_CODE}
fi
incoming=$root/.incoming
ensure_private_directory "$incoming"
incoming=$(cd "$incoming" && pwd -P)
find "$incoming" -type f -name 'openclaw-upload-*.tgz.*' -mmin +60 -exec rm -f -- {} + 2>/dev/null || true
upload=$incoming/openclaw-upload-$hash.tgz.$operation_token
if [ -d "$install_dir" ] && [ ! -L "$install_dir" ] && [ -f "$receipt" ] &&
node -e '${RECEIPT_MATCH_JS}' "$receipt" "$expected_receipt" &&
node -e '${VERIFY_INSTALL_JS}' "$install_dir" "$hash" "$install"; then
rm -f -- "$upload"
printf '%s\t%s\t' '${BOOTSTRAP_OUTPUT_TAG}' current
cat "$receipt"
printf '\n'
exit 0
fi
if [ ! -e "$upload" ] && [ ! -L "$upload" ]; then
(set -C; : > "$upload") 2>/dev/null || true
fi
if [ ! -f "$upload" ] || [ -L "$upload" ]; then
printf '%s\n' 'unsafe worker bootstrap upload' >&2
exit 2
fi
chmod 600 "$upload"
printf '%s\t%s\t%s\n' '${BOOTSTRAP_OUTPUT_TAG}' install "$upload"
`;
const INSTALL_SCRIPT = String.raw`set -eu
umask 077
install=$1
hash=$2
package_spec=$3
package_integrity=$4
receipt_json=$5
upload=$6
archive_sha256=$7
root=$HOME/${BOOTSTRAP_ROOT}
install_dir=$root/$hash
receipt=$install_dir/${BOOTSTRAP_RECEIPT}
staging=$root/.staging-$hash-$$
lock_root=$root/.locks
lock=$lock_root/$hash
locked=0
lock_identity="$$:$(date +%s)"
ensure_private_directory() {
directory=$1
if [ -e "$directory" ] || [ -L "$directory" ]; then
if [ ! -d "$directory" ] || [ -L "$directory" ]; then
printf '%s\n' 'unsafe worker bootstrap directory' >&2
exit 2
fi
else
mkdir "$directory"
fi
chmod 700 "$directory"
}
ensure_private_directory "$root"
ensure_private_directory "$lock_root"
cleanup() {
rm -rf "$staging"
if [ "$locked" -eq 1 ]; then
owner=$(readlink "$lock" 2>/dev/null || true)
if [ "$owner" = "$lock_identity" ]; then
rm -f "$lock"
fi
fi
}
trap cleanup 0
trap 'exit 1' 1 2 15
receipt_matches() {
[ -d "$install_dir" ] && [ ! -L "$install_dir" ] && [ -f "$receipt" ] &&
node -e '${RECEIPT_MATCH_JS}' "$receipt" "$receipt_json" &&
node -e '${VERIFY_INSTALL_JS}' "$install_dir" "$hash" "$install"
}
finish_with_receipt() {
# A durable receipt makes retries independent of this operation's upload.
rm -f -- "$upload"
printf '%s\t%s\t' '${BOOTSTRAP_OUTPUT_TAG}' receipt
cat "$receipt"
printf '\n'
}
read_lock_owner() {
if [ -L "$lock" ]; then
readlink "$lock" 2>/dev/null || true
elif [ -r "$lock/pid" ]; then
cat "$lock/pid" 2>/dev/null || true
fi
}
attempt=0
while ! ln -s "$lock_identity" "$lock" 2>/dev/null; do
if receipt_matches; then
finish_with_receipt
exit 0
fi
owner=$(read_lock_owner)
stale_owner=0
case "$owner" in
*:*:*) valid_owner=0 ;;
*:*)
owner_pid=${"${"}owner%%:*}
owner_started=${"${"}owner#*:}
case "$owner_pid" in
*[!0-9]*|'') valid_owner=0 ;;
*)
case "$owner_started" in
*[!0-9]*|'') valid_owner=0 ;;
*)
now=$(date +%s)
if [ "$owner_started" -le "$now" ] && [ $((now - owner_started)) -le ${LOCK_MAX_AGE_SECONDS} ]; then
valid_owner=1
else
valid_owner=0
stale_owner=1
fi
;;
esac
;;
esac
;;
*) valid_owner=0 ;;
esac
if [ "$stale_owner" -eq 1 ]; then
current_owner=$(read_lock_owner)
if [ "$current_owner" = "$owner" ]; then
if [ -L "$lock" ]; then rm -f "$lock"; else rm -rf "$lock"; fi
fi
continue
fi
if [ "$valid_owner" -eq 1 ] && kill -0 "$owner_pid" 2>/dev/null; then
attempt=$((attempt + 1))
if [ "$attempt" -ge 60 ]; then
printf '%s\n' 'worker bootstrap lock timed out' >&2
exit ${LOCK_TIMEOUT_EXIT_CODE}
fi
sleep 1
continue
fi
if [ "$valid_owner" -eq 1 ]; then
current_owner=$(read_lock_owner)
if [ "$current_owner" = "$owner" ]; then
if [ -L "$lock" ]; then rm -f "$lock"; else rm -rf "$lock"; fi
fi
continue
fi
attempt=$((attempt + 1))
if [ "$valid_owner" -eq 0 ] && [ "$attempt" -ge 5 ]; then
current_owner=$(read_lock_owner)
if [ "$current_owner" = "$owner" ]; then
if [ -L "$lock" ]; then rm -f "$lock"; else rm -rf "$lock"; fi
fi
continue
fi
sleep 1
done
locked=1
# The per-hash lock makes cleanup safe: no live installer for this build can own an older staging dir.
for stale_staging in "$root"/.staging-"$hash"-*; do
if [ -L "$stale_staging" ]; then
rm -f "$stale_staging"
elif [ -d "$stale_staging" ]; then
rm -rf "$stale_staging"
fi
done
if receipt_matches; then
finish_with_receipt
exit 0
fi
rm -rf "$staging"
mkdir -p "$staging"
case "$install" in
bundle)
if ! node -e '${VERIFY_ARCHIVE_JS}' "$upload" "$archive_sha256"; then
printf '%s\n' 'worker bundle archive digest mismatch' >&2
exit 2
fi
tar -xzf "$upload" -C "$staging"
;;
npm)
if ! command -v npm >/dev/null 2>&1; then
printf '%s\n' '${NPM_MISSING_MARKER}' >&2
exit ${NPM_MISSING_EXIT_CODE}
fi
npm_pack_json=$staging/npm-pack.json
npm pack "$package_spec" --pack-destination "$staging" --ignore-scripts --json --registry=https://registry.npmjs.org/ > "$npm_pack_json"
package_archive=$(node -e '${READ_NPM_PACK_FILENAME_JS}' "$npm_pack_json")
package_archive=$staging/$package_archive
if ! node -e '${VERIFY_NPM_PACKAGE_JS}' "$package_archive" "$package_integrity"; then
printf '%s\n' 'worker npm package integrity mismatch' >&2
exit 2
fi
tar -xzf "$package_archive" -C "$staging" --strip-components=3 \
package/dist/worker/${WORKER_BUNDLE_GITHUB_EXEC_LAUNCHER_PATH} \
package/dist/worker/${WORKER_BUNDLE_ENTRY_PATH} \
package/dist/worker/${WORKER_BUNDLE_RSYNC_RECEIVER_PATH}
rm -f "$npm_pack_json" "$package_archive"
;;
*)
printf '%s\n' 'invalid worker install channel' >&2
exit 2
;;
esac
if ! node -e '${VERIFY_INSTALL_JS}' "$staging" "$hash" "$install"; then
printf '%s\n' 'worker install content does not match the expected bundle hash' >&2
exit 2
fi
printf '%s\n' "$receipt_json" > "$staging/${BOOTSTRAP_RECEIPT}"
chmod 600 "$staging/${BOOTSTRAP_RECEIPT}"
rm -rf "$install_dir"
mv "$staging" "$install_dir"
finish_with_receipt
`;
function normalizeHandshake(artifact) {
const bundleHash = artifact.bundleHash.trim();
const openclawVersion = artifact.openclawVersion.trim();
const protocolFeatures = artifact.protocolFeatures.map((feature) => feature.trim());
if (!BUNDLE_HASH_PATTERN.test(bundleHash)) throw new Error("Worker bundle hash must be a lowercase SHA-256 digest");
if (!openclawVersion) throw new Error("Worker OpenClaw version must be non-empty");
if (protocolFeatures.length > 64 || protocolFeatures.some((feature) => !feature) || protocolFeatures.some((feature) => feature.length > 128) || new Set(protocolFeatures).size !== protocolFeatures.length) throw new Error("Worker protocol features must be unique non-empty strings");
if (artifact.install === "npm") {
if (!isExactSemverVersion(openclawVersion) || artifact.packageSpec !== `openclaw@${openclawVersion}`) throw new Error(`Worker npm install must use exact package openclaw@${openclawVersion}`);
if (!NPM_INTEGRITY_PATTERN.test(artifact.packageIntegrity)) throw new Error("Worker npm install requires a pinned SHA-512 package integrity");
} else if (!BUNDLE_HASH_PATTERN.test(artifact.tarballSha256)) throw new Error("Worker bundle archive digest must be a lowercase SHA-256 digest");
return {
bundleHash,
openclawVersion,
protocolFeatures
};
}
function parseReceiptJson(value, expected) {
let parsed;
try {
parsed = JSON.parse(value ?? "");
} catch {
throw new Error("Worker bootstrap returned an invalid receipt");
}
if (!validateWorkerAdmissionHandshake(parsed)) throw new Error("Worker bootstrap returned an invalid receipt");
if (parsed.bundleHash !== expected.bundleHash || parsed.openclawVersion !== expected.openclawVersion || parsed.protocolFeatures.length !== expected.protocolFeatures.length || parsed.protocolFeatures.some((feature, index) => feature !== expected.protocolFeatures[index])) throw new Error("Worker bootstrap receipt does not match the requested artifact");
return parsed;
}
function commandFailure(phase, result) {
const output = truncateUtf16Safe(redactSensitiveText(result.stderr.trim() || result.stdout.trim(), { mode: "tools" }).replace(/\s+/gu, " "), 512);
const status = result.termination === "exit" ? `exit ${result.code ?? "unknown"}` : result.termination;
return /* @__PURE__ */ new Error(`Worker bootstrap ${phase} failed (${status})${output ? `: ${output}` : ""}`);
}
function isSuccess(result) {
return result.termination === "exit" && result.code === 0;
}
async function runSshScript(params) {
return await params.runCommand([
"ssh",
...workerSshOptions(params.prepared, { forwarding: "disabled" }),
"-a",
"-x",
"-T",
"-p",
String(params.port ?? params.prepared.port),
"--",
params.prepared.sshTarget,
workerSshRemoteCommand([
"sh",
"-s",
"--",
...params.scriptArgs
])
], workerSshCommandOptions({
input: params.script,
timeoutMs: params.timeoutMs,
signal: params.signal
}));
}
function workerUploadFilename(bundleHash, operationToken) {
return `openclaw-upload-${bundleHash}.tgz.${operationToken}`;
}
const CLEANUP_UPLOAD_SCRIPT = String.raw`set -eu
hash=$1
operation_token=$2
case "$hash" in
*[!a-f0-9]*|'') exit 2 ;;
esac
case "$operation_token" in
*[!a-f0-9]*|'') exit 2 ;;
esac
if [ "${"${"}#hash}" -ne 64 ] || [ "${"${"}#operation_token}" -ne 64 ]; then
exit 2
fi
root=$HOME/${BOOTSTRAP_ROOT}
if [ ! -e "$root" ] && [ ! -L "$root" ]; then
exit 0
fi
if [ ! -d "$root" ] || [ -L "$root" ]; then
exit 2
fi
incoming=$root/.incoming
if [ ! -d "$incoming" ] || [ -L "$incoming" ]; then
exit 0
fi
incoming=$(cd "$incoming" && pwd -P)
rm -f -- "$incoming/openclaw-upload-$hash.tgz.$operation_token"
`;
async function cleanupRemoteUpload(params) {
const cleanupTimeoutMs = Math.min(params.timeoutMs, 1e4);
await runWorkerSshCandidates(params.prepared, cleanupTimeoutMs, (port, remainingTimeoutMs) => {
return runSshScript({
prepared: params.prepared,
runCommand: params.runCommand,
script: CLEANUP_UPLOAD_SCRIPT,
scriptArgs: [params.bundleHash, params.operationToken],
timeoutMs: remainingTimeoutMs,
port
});
}).catch(() => void 0);
}
function parseTaggedOutput(stdout) {
const prefix = `${BOOTSTRAP_OUTPUT_TAG}\t`;
const record = stdout.split(/\r?\n/u).findLast((line) => line.startsWith(prefix));
if (!record) return;
const actionEnd = record.indexOf(" ", prefix.length);
if (actionEnd === -1) return;
const action = record.slice(prefix.length, actionEnd);
const payload = record.slice(actionEnd + 1).trim();
return action && payload ? {
action,
payload
} : void 0;
}
function parsePreflight(result, expected, expectedUploadFilename) {
if (result.code === NODE_MISSING_EXIT_CODE || result.stderr.includes(NODE_MISSING_MARKER) || result.stdout.includes(NODE_MISSING_MARKER)) throw new Error("Worker bootstrap requires Node.js on the leased host; install Node in the provider setup phase and retry");
if (result.code === NODE_UNSUPPORTED_EXIT_CODE || result.stderr.includes(NODE_UNSUPPORTED_MARKER) || result.stdout.includes(NODE_UNSUPPORTED_MARKER)) throw new Error("Worker bootstrap requires Node 22.22.3+, 24.15.0+, or 25.9.0+ with WAL-reset-safe SQLite on the leased host; install a supported Node runtime in the provider setup phase and retry");
if (!isSuccess(result)) throw commandFailure("preflight", result);
const output = parseTaggedOutput(result.stdout);
if (output?.action === "current") return {
action: "current",
receipt: parseReceiptJson(output.payload, expected)
};
const remotePath = output?.action === "install" ? output.payload : void 0;
const normalizedPath = normalizeScpRemotePath(remotePath);
const expectedSuffix = `/${BOOTSTRAP_ROOT}/.incoming/${expectedUploadFilename}`;
const hasCanonicalSegments = normalizedPath?.split("/").slice(1).every((segment) => segment !== "" && segment !== "." && segment !== "..");
if (!normalizedPath || !hasCanonicalSegments || !normalizedPath.endsWith(expectedSuffix)) throw new Error("Worker bootstrap preflight returned an invalid upload path");
return {
action: "install",
path: normalizedPath
};
}
/** Installs one exact worker artifact over SSH and returns its admission receipt. */
async function bootstrapWorker(request, dependencies) {
const artifact = request.artifact;
const timeoutMs = dependencies.timeoutMs ?? DEFAULT_BOOTSTRAP_TIMEOUT_MS;
const transferTimeoutMs = artifact.install === "bundle" ? bundleTransferTimeoutMs(artifact.tarballBytes, timeoutMs) : timeoutMs;
const receipt = normalizeHandshake(artifact);
const operationToken = createHash("sha256").update(request.operationId).digest("hex");
const uploadFilename = workerUploadFilename(receipt.bundleHash, operationToken);
const runCommand = dependencies.runCommand ?? runCommandWithTimeout;
const prepared = await prepareWorkerSsh({
ssh: request.ssh,
pinnedHostKey: request.pinnedHostKey,
resolveIdentity: dependencies.resolveIdentity,
temporaryDirectoryPrefix: "openclaw-worker-bootstrap-"
});
let needsUploadCleanup = true;
try {
const preflight = parsePreflight(await runWorkerSshCandidates(prepared, timeoutMs, (port, remainingTimeoutMs) => runSshScript({
prepared,
runCommand,
script: PREFLIGHT_SCRIPT,
scriptArgs: [
receipt.bundleHash,
JSON.stringify(receipt),
artifact.install,
operationToken
],
timeoutMs: remainingTimeoutMs,
port,
signal: dependencies.signal
})), receipt, uploadFilename);
if (preflight.action === "current") {
needsUploadCleanup = false;
return preflight.receipt;
}
if (artifact.install === "bundle") {
const transfer = await runWorkerSshCandidates(prepared, transferTimeoutMs, (port, remainingTimeoutMs) => runCommand([
"scp",
...workerSshOptions(prepared, { forwarding: "disabled" }),
"-P",
String(port),
"--",
artifact.tarballPath,
`${prepared.scpTarget}:${preflight.path}`
], workerSshCommandOptions({
timeoutMs: remainingTimeoutMs,
signal: dependencies.signal
})));
if (!isSuccess(transfer)) throw commandFailure("bundle transfer", transfer);
}
const install = await runWorkerSshCandidates(prepared, timeoutMs, (port, remainingTimeoutMs) => runSshScript({
prepared,
runCommand,
script: INSTALL_SCRIPT,
scriptArgs: [
artifact.install,
receipt.bundleHash,
artifact.install === "npm" ? artifact.packageSpec : "",
artifact.install === "npm" ? artifact.packageIntegrity : "",
JSON.stringify(receipt),
preflight.path,
artifact.install === "bundle" ? artifact.tarballSha256 : ""
],
timeoutMs: remainingTimeoutMs,
port,
signal: dependencies.signal
}));
if (install.code === NPM_MISSING_EXIT_CODE || install.stderr.includes(NPM_MISSING_MARKER) || install.stdout.includes(NPM_MISSING_MARKER)) throw new Error("Worker npm bootstrap requires npm on the leased host; use bundle install or provide npm in the provider setup phase");
if (!isSuccess(install)) throw commandFailure("install", install);
const output = parseTaggedOutput(install.stdout);
if (output?.action !== "receipt") throw new Error("Worker bootstrap install returned an invalid receipt");
return parseReceiptJson(output.payload, receipt);
} finally {
if (needsUploadCleanup) await cleanupRemoteUpload({
prepared,
bundleHash: receipt.bundleHash,
operationToken,
runCommand,
timeoutMs
});
await prepared.dispose();
}
}
//#endregion
export { resolveWorkerNpmInstallationArtifact as i, workerBootstrapOperationTimeoutMs as n, createWorkerBundleProducer as r, bootstrapWorker as t };