// Bootstrap-safe ownership of OpenClaw executable identity and CLI invocation shape. import { readFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { CLI_PARENT_BOUND_PRELOAD, CLI_RESPAWN_PARENT_ENV } from "./cli-parent-lifetime.mjs"; import { hasNodeStartupHooks } from "./node-runtime-env.mjs"; /** Packaging owns hashes; launch planning consumes only the artifact identity. */ export function readStandaloneRuntimeManifest(executable = process.execPath) { return readStandaloneManifest({ executable }); } /** Resolve a peer package through the same manifest contract, including renamed executables. */ export function readStandaloneRuntimeManifestForRoot(runtimeRoot) { return readStandaloneManifest({ runtimeRoot }); } /** The executing image owns its build facts, even if its sidecar is replaced. */ export function getStandaloneRuntimeBuild(executable = process.execPath) { if (executable === process.execPath) { // eslint-disable-next-line no-underscore-dangle -- Compiler-owned bootstrap contract. const embedded = globalThis.__openclawStandaloneBuildInfo; if (embedded !== undefined) { return embedded; } } return readStandaloneManifest({ executable, includeBuild: true }); } function projectStandaloneBuildInfo(value) { const nonBlank = (text) => typeof text === "string" && text.trim().length > 0; const stringList = (items) => Array.isArray(items) && items.every(nonBlank); const result = { mode: value.mode, openclaw: { version: value.openclaw.version, ...(nonBlank(value.openclaw.commit) ? { commit: value.openclaw.commit } : {}), }, bun: { version: value.bun.version }, }; for (const key of ["revision", "commit", "executableSha256"]) { if (nonBlank(value.bun[key])) { result.bun[key] = value.bun[key]; } } if (value.build !== undefined) { const { id, flavor, pluginSetSha256, moduleGraphSha256 } = value.build; if ( !nonBlank(id) || !["standard", "support"].includes(flavor) || !/^[a-f0-9]{64}$/.test(pluginSetSha256) || !/^[a-f0-9]{64}$/.test(moduleGraphSha256) ) { throw new Error("Invalid standalone build provenance"); } result.build = { id, flavor, pluginSetSha256, moduleGraphSha256 }; if (value.build.debugEntry !== undefined) { const entry = value.build.debugEntry; if ( typeof entry !== "string" || !entry.startsWith("debug/") || entry.includes("\\") || entry.split("/").some((part) => !part || part === "." || part === "..") ) { throw new Error("Invalid standalone debugger entry"); } result.build.debugEntry = entry; } } if (value.target !== undefined) { if (!nonBlank(value.target.platform) || !nonBlank(value.target.arch)) { throw new Error("Invalid standalone build target"); } result.target = { platform: value.target.platform, arch: value.target.arch }; } if (value.plugins !== undefined) { if (!stringList(value.plugins.embedded) || !stringList(value.plugins.omitted)) { throw new Error("Invalid standalone plugin inventory"); } result.plugins = { embedded: [...value.plugins.embedded], omitted: [...value.plugins.omitted] }; } if (value.nativeModules !== undefined) { if ( !Array.isArray(value.nativeModules) || value.nativeModules.some( (item) => !item || !nonBlank(item.name) || !nonBlank(item.version) || !stringList(item.artifacts) || item.qualification !== "unverified" || !nonBlank(item.backend), ) ) { throw new Error("Invalid standalone native inventory"); } result.nativeModules = value.nativeModules.map( ({ name, version, artifacts, qualification, backend }) => ({ name, version, artifacts: [...artifacts], qualification, backend, }), ); } return result; } function readStandaloneManifest({ executable, runtimeRoot, includeBuild = false }) { const location = executable ?? runtimeRoot; if (!path.isAbsolute(location)) { return undefined; } const root = path.dirname(location); const manifestPath = path.join(root, "manifest.json"); let raw; try { raw = readFileSync(manifestPath, "utf8"); } catch (error) { if (error?.code === "ENOENT" || error?.code === "ENOTDIR") { return undefined; } throw error; } let manifest; try { manifest = JSON.parse(raw); } catch (cause) { throw new Error(`Invalid standalone runtime manifest: ${manifestPath}`, { cause }); } const nonBlank = (value) => typeof value === "string" && value.trim().length > 0; if ( manifest?.schemaVersion !== 1 || !["jit", "bytecode", "aot"].includes(manifest.mode) || !nonBlank(manifest.executable?.path) || !nonBlank(manifest.runtime?.path) || !nonBlank(manifest.openclaw?.version) || !nonBlank(manifest.bun?.version) ) { throw new Error(`Invalid standalone runtime manifest: ${manifestPath}`); } const executableRelative = path.relative(root, path.resolve(root, manifest.executable.path)); const sidecarRelative = path.relative(root, path.resolve(root, manifest.runtime.path)); if ( path.isAbsolute(manifest.executable.path) || !executableRelative || executableRelative === ".." || executableRelative.startsWith(`..${path.sep}`) || (executable && path.resolve(root, manifest.executable.path) !== path.resolve(executable)) || (runtimeRoot && path.resolve(root, manifest.runtime.path) !== path.resolve(runtimeRoot)) || path.isAbsolute(manifest.runtime.path) || !sidecarRelative || sidecarRelative === ".." || sidecarRelative.startsWith(`..${path.sep}`) ) { throw new Error(`Invalid standalone runtime manifest: ${manifestPath}`); } return { schemaVersion: 1, mode: manifest.mode, executable: { path: manifest.executable.path, ...(nonBlank(manifest.executable.sha256) ? { sha256: manifest.executable.sha256 } : {}), }, runtime: { path: manifest.runtime.path, ...(nonBlank(manifest.runtime.sha256) ? { sha256: manifest.runtime.sha256 } : {}), }, openclaw: { version: manifest.openclaw.version, ...(nonBlank(manifest.openclaw.commit) ? { commit: manifest.openclaw.commit } : {}), }, bun: { version: manifest.bun.version }, ...(includeBuild ? projectStandaloneBuildInfo(manifest) : {}), }; } /** The compiler publishes this fact before any embedded module is loaded. */ export function getEmbeddedStandaloneRuntimeRoot() { // eslint-disable-next-line no-underscore-dangle -- Matches the existing compiler bootstrap contract. const rootUrl = globalThis.__openclawStandaloneRootUrl; return typeof rootUrl === "string" ? fileURLToPath(rootUrl) : undefined; } /** Image membership comes from the compiler's module inventory, never disk contents. */ export function isEmbeddedStandaloneModule(modulePath) { const filename = modulePath.startsWith("file:") ? fileURLToPath(modulePath) : modulePath; // eslint-disable-next-line no-underscore-dangle -- Compiler bootstrap contract. return globalThis.__openclawStandaloneIsEmbeddedModule?.(filename) === true; } export function resolveStandaloneSidecarRoot(executable = process.execPath) { if (executable === process.execPath) { const embeddedRoot = getEmbeddedStandaloneRuntimeRoot(); if (embeddedRoot !== undefined) { return embeddedRoot; } } const manifest = readStandaloneRuntimeManifest(executable); return manifest ? path.resolve(path.dirname(executable), manifest.runtime.path) : undefined; } export function isStandaloneRuntimeExecutable(executable = process.execPath) { return resolveStandaloneSidecarRoot(executable) !== undefined; } /** Startup hooks can own a separate compiler/reaper; source filenames cannot prove that. */ export function runtimeHasSourceLoader() { // BUN_BE_BUN executes disk programs, even when the executable has a manifest. if (process.env.BUN_BE_BUN !== "1" && isStandaloneRuntimeExecutable()) { return false; } return hasNodeStartupHooks(process.execArgv, process.env.NODE_OPTIONS); } /** Disk-executed package-manager programs belong to the standalone sidecar. */ export function resolveStandaloneNpmCliPath() { const root = resolveStandaloneSidecarRoot(process.execPath); return root === undefined ? undefined : path.join(root, "npm", "bin", "npm-cli.js"); } /** A compiled Bun executable needs this mode to execute a disk program as Bun. */ export function resolveRuntimeScriptEnv(env) { return isStandaloneRuntimeExecutable() ? { ...env, BUN_BE_BUN: "1" } : { ...env }; } /** A null entry explicitly selects a CLI command; undefined inherits the current script. */ export function resolveOpenClawCliInvocation({ execPath = process.execPath, entryPath, args = process.argv.slice(2), execArgv = process.execArgv, } = {}) { const standaloneRoot = resolveStandaloneSidecarRoot(execPath); if (standaloneRoot !== undefined || entryPath === null) { if (standaloneRoot !== undefined && entryPath !== undefined && entryPath !== null) { const relative = path.relative(standaloneRoot, path.resolve(entryPath)); if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) { throw new Error( "A standalone OpenClaw executable cannot run another installation's entry script.", ); } } return { command: execPath, args: [...args] }; } const entry = entryPath ?? process.argv[1]; if (!entry) { throw new Error("Cannot invoke the loose OpenClaw CLI without an entry script."); } return { command: execPath, args: [...execArgv, entry, ...args] }; } /** Bind only this CLI child to its launcher; runtime flags belong before a loose entry. */ export function resolveOpenClawCliRespawn({ env = process.env, ...params } = {}) { const execPath = params.execPath ?? process.execPath; const invocation = resolveOpenClawCliInvocation({ ...params, execArgv: ["--import", CLI_PARENT_BOUND_PRELOAD, ...(params.execArgv ?? process.execArgv)], }); return { ...invocation, env: isStandaloneRuntimeExecutable(execPath) ? { ...env, [CLI_RESPAWN_PARENT_ENV]: String(process.pid) } : env, }; }