import { r as asNullableRecord } from "./record-coerce-DItp3I4t.js";
import { F as resolveTimerTimeoutMs } from "./number-coercion-CLj0HTDM.js";
import { a as writeRuntimeJson, r as defaultRuntime } from "./runtime-LRpY2Icg.js";
import { Et as array, Qn as url, Rn as string, Tn as object, wn as number } from "./schemas-CZ9Toj_c.js";
import { t as VERSION } from "./version-CyS-R7WB.js";
import { t as formatDocsLink } from "./links-ClIwBcy4.js";
import { r as theme } from "./theme-vjDs9tao.js";
import { r as formatInvalidPortOption } from "./error-format-HTpcnFye.js";
import { n as loadDeviceIdentityIfPresent, o as publicKeyRawBase64UrlFromPem } from "./device-identity-BP5V9Wxb.js";
import "./client-BBWFfmhX.js";
import { t as GatewayClientRequestError } from "./request-error-DS6rHm1Y.js";
import { t as createPendingRequestRegistry } from "./pending-request-registry-B-sdjY_C.js";
import { t as formatHelpExamples } from "./help-format-CAcwboTs.js";
import { a as loadNodeHostConfig } from "./config-BJ3FtXnH.js";
import { a as runNodeDaemonStop, c as resolveNodePairGatewayOptions, d as runStartupMigrations, f as prepareNodeHostRuntime, i as runNodeDaemonStatus, n as runNodeDaemonRestart, o as runNodeDaemonUninstall, p as startNodeHostConnection, r as runNodeDaemonStart, s as resolveNodeGatewayOptions, t as runNodeDaemonInstall, u as runNodeHost } from "./daemon-Bn5b08-h.js";
import { AsyncLocalStorage } from "node:async_hooks";
import { Option } from "commander";
import { createInterface } from "node:readline";
//#region src/node-host/worker-support.ts
const connectionSchema = object({
	url: url(),
	protocol: number().int().positive(),
	capabilities: array(string()),
	tlsFingerprint: string().optional(),
	cloudflareAccess: object({
		clientId: string(),
		clientSecret: string()
	}).optional()
});
function parseNodeHostWorkerInput(line) {
	try {
		const parsed = asNullableRecord(JSON.parse(line));
		const type = typeof parsed?.type === "string" ? parsed.type : "";
		if (type === "stop") return { type };
		const generation = parsed?.generation;
		if (typeof generation !== "number" || !Number.isSafeInteger(generation) || generation < 0) return null;
		if (type === "gateway-connection") return {
			type,
			generation,
			connection: parsed?.connection === null ? null : connectionSchema.parse(parsed?.connection)
		};
		if (type === "invoke") {
			const request = asNullableRecord(parsed?.request);
			if (request && typeof request.id === "string" && typeof request.nodeId === "string" && typeof request.command === "string") return {
				type,
				generation,
				request
			};
			return null;
		}
		if (type === "gateway-response") {
			const id = typeof parsed?.id === "string" ? parsed.id : "";
			if (!id) return null;
			return parsed?.ok === true ? {
				type,
				generation,
				id,
				ok: true,
				result: parsed.result
			} : {
				type,
				generation,
				id,
				ok: false,
				error: object({
					code: string(),
					message: string()
				}).parse(parsed?.error)
			};
		}
		if (type === "invoke-input") {
			const invokeId = typeof parsed?.invokeId === "string" ? parsed.invokeId : "";
			const seq = typeof parsed?.seq === "number" ? parsed.seq : -1;
			const payloadJSON = typeof parsed?.payloadJSON === "string" ? parsed.payloadJSON : null;
			return invokeId && Number.isInteger(seq) && seq >= 0 && payloadJSON !== null ? {
				type,
				generation,
				invokeId,
				seq,
				payloadJSON
			} : null;
		}
		if (type === "invoke-cancel") {
			const invokeId = typeof parsed?.invokeId === "string" ? parsed.invokeId : "";
			return invokeId ? {
				type,
				generation,
				invokeId
			} : null;
		}
		return null;
	} catch {
		return null;
	}
}
var NodeHostWorkerBridgeClient = class {
	setConnection(generation, connected) {
		this.pending.rejectAll(/* @__PURE__ */ new Error("node-host Gateway route changed"));
		this.generation = generation;
		this.connected = connected;
	}
	withConnection(generation, run) {
		return this.invocationGeneration.run(generation, run);
	}
	constructor(writeMessage) {
		this.writeMessage = writeMessage;
		this.nextRequestId = 1;
		this.generation = 0;
		this.connected = false;
		this.invocationGeneration = new AsyncLocalStorage();
		this.pending = createPendingRequestRegistry();
	}
	async request(method, params, opts) {
		const generation = this.invocationGeneration.getStore() ?? this.generation;
		if (!this.connected || generation !== this.generation) throw new Error("node-host Gateway route is closed");
		if (method === "node.invoke.result") {
			this.writeMessage({
				type: "invoke-result",
				generation,
				result: params ?? {}
			});
			return {};
		}
		if (method === "node.event") {
			this.writeMessage({
				type: "node-event",
				generation,
				event: params ?? {}
			});
			return {};
		}
		const id = `gateway-${this.nextRequestId++}`;
		const timeoutMs = resolveTimerTimeoutMs(opts?.timeoutMs, 15e3);
		const pending = this.pending.add(id, {
			value: void 0,
			timeoutMs,
			timeoutError: () => /* @__PURE__ */ new Error(`Gateway request timed out: ${method}`)
		});
		if (!pending) throw new Error(`Gateway request id collision: ${id}`);
		this.writeMessage({
			type: "gateway-request",
			generation,
			id,
			method,
			params: params ?? {},
			timeoutMs
		});
		return await pending.promise;
	}
	handleResponse(message) {
		if (message.generation !== this.generation) return false;
		const pending = this.pending.take(message.id);
		if (!pending) return false;
		if (message.ok) pending.resolve(message.result);
		else pending.reject(new GatewayClientRequestError({
			code: message.error.code,
			message: message.error.message
		}));
		return true;
	}
	close() {
		this.connected = false;
		this.pending.rejectAll(/* @__PURE__ */ new Error("node-host worker stopped"));
	}
};
async function stopNodeHostWorkerFromSignal(input, stop, exitCode) {
	const stopped = stop(exitCode);
	input.close();
	await stopped;
}
//#endregion
//#region src/node-host/worker.ts
/** Private JSONL worker exposing the CLI node-host runtime to the macOS app. */
function writeMessage(message) {
	process.stdout.write(`${JSON.stringify(message)}\n`);
}
function writeStderrLine(message) {
	process.stderr.write(`${message}\n`);
}
async function runNodeHostWorker() {
	await runStartupMigrations({ log: {
		info: writeStderrLine,
		warn: writeStderrLine
	} });
	const prepared = await prepareNodeHostRuntime({
		enableDuplexPluginCommands: true,
		enableWorkerRuns: true,
		installedAppsSharingEnabled: (await loadNodeHostConfig())?.installedAppsSharing === true
	});
	const client = new NodeHostWorkerBridgeClient(writeMessage);
	let stopping = false;
	let resolveStopped;
	const stopped = new Promise((resolve) => {
		resolveStopped = resolve;
	});
	const stop = async (exitCode) => {
		if (stopping) return;
		stopping = true;
		try {
			client.close();
			await runtime.close();
			process.exitCode = exitCode;
		} finally {
			resolveStopped?.();
		}
	};
	let generation = 0;
	let connected = false;
	let readySent = false;
	let currentManifest = prepared.manifest;
	const runtime = startNodeHostConnection({
		prepared,
		client,
		writeStderrLine,
		onManifestChanged: (manifest) => {
			currentManifest = manifest;
			if (readySent) {
				connected = false;
				client.setConnection(generation, false);
				runtime.disconnect();
				writeMessage({
					type: "manifest",
					version: VERSION,
					manifest
				});
			}
		}
	});
	writeMessage({
		type: "ready",
		version: VERSION,
		manifest: currentManifest
	});
	readySent = true;
	const input = createInterface({
		input: process.stdin,
		crlfDelay: Infinity
	});
	input.on("line", (line) => {
		const message = parseNodeHostWorkerInput(line);
		if (!message) {
			writeMessage({
				type: "protocol-error",
				error: "invalid worker request"
			});
			return;
		}
		if (message.type === "gateway-connection") {
			if (message.generation <= generation) return;
			generation = message.generation;
			connected = message.connection !== null;
			client.setConnection(generation, connected);
			runtime.disconnect();
			if (message.connection) {
				const connectionGeneration = generation;
				runtime.connect(message.connection, { request: (...args) => client.withConnection(connectionGeneration, () => client.request(...args)) });
			}
			return;
		}
		if (message.type === "gateway-response") {
			client.handleResponse(message);
			return;
		}
		if (message.type === "stop") {
			input.close();
			stop(0);
			return;
		}
		if (!connected || message.generation !== generation) return;
		if (message.type === "invoke-input") {
			runtime.handleInput(message.invokeId, message.seq, message.payloadJSON);
			return;
		}
		if (message.type === "invoke-cancel") {
			runtime.cancel(message.invokeId);
			return;
		}
		client.withConnection(generation, () => runtime.invoke(message.request));
	});
	input.on("close", () => void stop(0));
	const onInterrupt = () => void stopNodeHostWorkerFromSignal(input, stop, 130);
	const onTerminate = () => void stopNodeHostWorkerFromSignal(input, stop, 143);
	process.once("SIGINT", onInterrupt);
	process.once("SIGTERM", onTerminate);
	try {
		await stopped;
	} finally {
		process.off("SIGINT", onInterrupt);
		process.off("SIGTERM", onTerminate);
	}
}
//#endregion
//#region src/cli/node-cli/identity.ts
/**
* Read-only by design: the SSH-verified pairing probe calls this remotely and
* must never mint a fresh identity on a host that has not run the node host.
*/
function runNodeIdentityShow(opts) {
	const identity = loadDeviceIdentityIfPresent();
	if (!identity) {
		defaultRuntime.error("no node device identity found (start the node host once with `openclaw node run` or `openclaw node install`)");
		defaultRuntime.exit(1);
		return;
	}
	const payload = {
		deviceId: identity.deviceId,
		publicKey: publicKeyRawBase64UrlFromPem(identity.publicKeyPem)
	};
	if (opts.json) {
		writeRuntimeJson(defaultRuntime, payload, 0);
		return;
	}
	defaultRuntime.log(`deviceId:  ${payload.deviceId}`);
	defaultRuntime.log(`publicKey: ${payload.publicKey}`);
}
//#endregion
//#region src/cli/node-cli/register.ts
function registerNodeCli(program) {
	const node = program.command("node").description("Run and manage the headless node host service").addHelpText("after", () => `\n${theme.heading("Examples:")}\n${formatHelpExamples([
		["openclaw node run --host 127.0.0.1 --port 18789", "Run the node host in the foreground."],
		["openclaw node status", "Check node host service status."],
		["openclaw node install", "Install the node host service."],
		["openclaw node start", "Start the installed node host service."],
		["openclaw node restart", "Restart the installed node host service."]
	])}\n\n${theme.muted("Docs:")} ${formatDocsLink("/cli/node", "docs.openclaw.ai/cli/node")}\n`);
	node.command("worker", { hidden: true }).description("Run the private macOS app node-host worker").action(async () => {
		await runNodeHostWorker();
	});
	node.command("run").description("Run the headless node host (foreground)").option("--pair <code-or-url>", "Pair with a setup code or oc-pair URL; explicit gateway flags take precedence").option("--host <host>", "Gateway host").option("--port <port>", "Gateway port").option("--context-path <path>", "Gateway WebSocket context path (e.g. /openclaw-gw)").option("--tls", "Use TLS for the gateway connection").option("--no-tls", "Disable TLS for the gateway connection").option("--tls-fingerprint <sha256>", "Expected TLS certificate fingerprint (sha256)").option("--node-id <id>", "Override the generated node instance id").option("--display-name <name>", "Override node display name").addOption(new Option("--ephemeral").hideHelp()).option("--share-installed-apps", "Share installed macOS applications with the Gateway").option("--no-share-installed-apps", "Disable installed application sharing").action(async (opts) => {
		let pair;
		let gatewayOptions;
		try {
			pair = opts.pair ? resolveNodePairGatewayOptions(opts.pair) : void 0;
			gatewayOptions = resolveNodeGatewayOptions(opts, await loadNodeHostConfig(), pair);
		} catch (error) {
			defaultRuntime.error(error instanceof Error ? error.message : String(error));
			defaultRuntime.exit(1);
			return;
		}
		const { host, port, contextPath, tls, tlsFingerprint, cloudflareAccess, gatewayCandidates } = gatewayOptions;
		if (port === null) {
			defaultRuntime.error(formatInvalidPortOption("--port"));
			defaultRuntime.exit(1);
			return;
		}
		if (opts.tls === false && opts.tlsFingerprint !== void 0) {
			defaultRuntime.error("--no-tls cannot be combined with --tls-fingerprint");
			defaultRuntime.exit(1);
			return;
		}
		await runNodeHost({
			gatewayHost: host,
			gatewayPort: port,
			gatewayTls: tls,
			gatewayTlsFingerprint: tlsFingerprint,
			gatewayContextPath: contextPath,
			gatewayCloudflareAccess: cloudflareAccess,
			gatewayCandidates,
			gatewayBootstrapToken: pair?.bootstrapToken,
			preferGatewayBootstrapToken: pair !== void 0,
			...opts.ephemeral === true ? {
				forceWorkerRuns: true,
				ephemeral: true
			} : {},
			nodeId: opts.nodeId,
			displayName: opts.displayName,
			installedAppsSharing: opts.shareInstalledApps
		});
	});
	node.command("status").description("Show node host status").option("--json", "Output JSON", false).action(async (opts) => {
		await runNodeDaemonStatus(opts);
	});
	node.command("identity").description("Print the node host device identity (device id + public key)").option("--json", "Output JSON", false).action((opts) => {
		runNodeIdentityShow(opts);
	});
	node.command("install").description("Install the node host service (launchd/systemd/schtasks)").option("--host <host>", "Gateway host").option("--port <port>", "Gateway port").option("--context-path <path>", "Gateway WebSocket context path (e.g. /openclaw-gw)").option("--tls", "Use TLS for the gateway connection").option("--no-tls", "Disable TLS for the gateway connection").option("--tls-fingerprint <sha256>", "Expected TLS certificate fingerprint (sha256)").option("--node-id <id>", "Override the generated node instance id").option("--display-name <name>", "Override node display name").option("--share-installed-apps", "Share installed macOS applications with the Gateway").option("--no-share-installed-apps", "Disable installed application sharing").option("--runtime <runtime>", "Service runtime (node|bun). Default: node").option("--force", "Reinstall/overwrite if already installed", false).option("--json", "Output JSON", false).action(async (opts) => {
		await runNodeDaemonInstall(opts);
	});
	for (const [name, action] of [
		["uninstall", runNodeDaemonUninstall],
		["stop", runNodeDaemonStop],
		["start", runNodeDaemonStart],
		["restart", runNodeDaemonRestart]
	]) node.command(name).description(`${name.charAt(0).toUpperCase()}${name.slice(1)} the node host service (launchd/systemd/schtasks)`).option("--json", "Output JSON", false).action(async (opts) => {
		await action(opts);
	});
}
//#endregion
export { registerNodeCli };
