Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const require_chunk = require("./chunk-Ble4zEEl.js");
const require_manifest_python_deps = require("./manifest-python-deps-DgWowaW0.js");
const require_worker_device_restore = require("./worker-device-restore-B_Di-zuW.js");
let _camstack_types = require("@camstack/types");
let node_fs = require("node:fs");
node_fs = require_chunk.__toESM(node_fs);
let node_path = require("node:path");
node_path = require_chunk.__toESM(node_path);
//#region src/moleculer/process-runner.ts
/**
* Process Runner — generic entry point for spawned addon processes.
*
* Each spawned process has its own Moleculer ServiceBroker with a
* hierarchical nodeID ({parentNodeId}/{processName}).
* Communication with the parent is exclusively via Moleculer TCP.
*
* Environment variables:
* - CAMSTACK_PROCESS_NAME: name of this process (e.g., "detection-pipeline")
* - CAMSTACK_ADDON_ID: addon ID to load
* - CAMSTACK_ADDON_DIR: path to addon package directory
* - CAMSTACK_PARENT_NODE_ID: parent broker nodeID (e.g., "hub")
* - CAMSTACK_DATA_DIR: path to data directory
*/
function isRecord(value) {
	return typeof value === "object" && value !== null && !Array.isArray(value);
}
function isAddonConstructor(fn) {
	if (typeof fn !== "function") return false;
	const proto = fn.prototype;
	if (!isRecord(proto)) return false;
	return typeof proto["initialize"] === "function";
}
function resolveAddonClass(mod) {
	let candidate = mod["default"];
	if (isRecord(candidate) && "default" in candidate) candidate = candidate["default"];
	if (isAddonConstructor(candidate)) return candidate;
	if (isRecord(candidate)) {
		const fn = Object.values(candidate).find(isAddonConstructor);
		if (fn) return fn;
	}
	return Object.values(mod).find(isAddonConstructor);
}
async function main() {
	const processName = process.env["CAMSTACK_PROCESS_NAME"];
	const addonId = process.env["CAMSTACK_ADDON_ID"];
	const addonDir = process.env["CAMSTACK_ADDON_DIR"];
	const parentNodeId = process.env["CAMSTACK_PARENT_NODE_ID"];
	const dataDir = process.env["CAMSTACK_DATA_DIR"] ?? "./camstack-data";
	if (!processName || !addonId || !addonDir || !parentNodeId) {
		console.error("[process-runner] Missing required env vars: CAMSTACK_PROCESS_NAME, CAMSTACK_ADDON_ID, CAMSTACK_ADDON_DIR, CAMSTACK_PARENT_NODE_ID");
		process.exit(1);
	}
	const nodeId = `${parentNodeId}/${processName}`;
	console.log(`[process-runner] Starting "${nodeId}" for addon "${addonId}"`);
	const childLogLevel = process.env["CAMSTACK_LOG_LEVEL"] ?? "debug";
	const parentPort = Number(process.env["CAMSTACK_PARENT_TCP_PORT"] ?? "6000");
	const parentUrl = `${process.env["CAMSTACK_PARENT_HOST"] ?? "127.0.0.1"}:${parentPort}/${parentNodeId}`;
	const broker = new require_manifest_python_deps.ServiceBroker({
		nodeID: nodeId,
		namespace: process.env["CAMSTACK_CLUSTER_SECRET"] || void 0,
		serializer: "MsgPack",
		transporter: {
			type: "TCP",
			options: {
				udpDiscovery: false,
				urls: parentUrl,
				port: 0,
				maxPacketSize: 10 * 1024 * 1024
			}
		},
		metrics: {
			enabled: true,
			reporter: [{
				type: "Event",
				options: {
					eventName: "$metrics.snapshot",
					interval: 5
				}
			}]
		},
		logger: {
			type: "Console",
			options: { level: {
				BROKER: "error",
				TRANSPORTER: "error",
				REGISTRY: "error",
				DISCOVERER: "error",
				"**": childLogLevel
			} }
		}
	});
	const pkgJsonPath = node_path.join(addonDir, "package.json");
	if (!node_fs.existsSync(pkgJsonPath)) {
		console.error(`[process-runner] No package.json at ${addonDir}`);
		process.exit(1);
	}
	const declaration = JSON.parse(node_fs.readFileSync(pkgJsonPath, "utf-8"))["camstack"]?.addons?.find((a) => a.id === addonId);
	if (!declaration?.entry) {
		console.error(`[process-runner] Addon "${addonId}" not found in package.json manifest`);
		process.exit(1);
	}
	const entryPath = node_path.resolve(addonDir, declaration.entry);
	if (!node_fs.existsSync(entryPath)) {
		console.error(`[process-runner] Entry point not found: ${entryPath}`);
		process.exit(1);
	}
	const modUnknown = await import(entryPath);
	const AddonClass = resolveAddonClass(isRecord(modUnknown) ? modUnknown : {});
	if (!AddonClass) {
		console.error(`[process-runner] No addon class found in ${entryPath}`);
		process.exit(1);
	}
	require_manifest_python_deps.registerClusterEventsService(broker);
	broker.createService({
		name: "$node-mgmt",
		actions: { setLogLevel: { handler(ctx) {
			const level = ctx.params.level;
			const factory = broker.logger;
			const appenders = factory?.appenders;
			if (appenders) for (const appender of appenders) appender.opts.level = level;
			const cache = factory?.cache;
			if (cache) cache.clear();
			console.log(`[process-runner] Log level changed to "${level}"`);
			return true;
		} } }
	});
	require_manifest_python_deps.mountNativeCapService(broker, addonId, _camstack_types.deviceOpsCapability);
	await broker.start();
	await broker.waitForServices(["$hub"]);
	require_manifest_python_deps.setHubConnected(broker);
	const addon = new AddonClass();
	const context = require_manifest_python_deps.createAddonContext(broker, declaration, dataDir);
	await require_manifest_python_deps.installManifestPythonDeps(declaration, addonDir, context.deps, context.logger);
	const registeredProviders = /* @__PURE__ */ new Map();
	const initResult = (0, _camstack_types.normalizeAddonInitResult)(await addon.initialize(context));
	for (const reg of initResult?.providers ?? []) {
		const capName = reg.capability.name;
		registeredProviders.set(capName, reg.provider);
		context.registerProvider(capName, reg.provider);
	}
	const customActionRegistry = new require_manifest_python_deps.CustomActionRegistry();
	if (initResult?.customActions && initResult.actionHandlers) {
		const handlers = initResult.actionHandlers;
		customActionRegistry.registerAddon(addonId, initResult.customActions, (action, input) => {
			const fn = handlers[action];
			if (!fn) throw new Error(`addon '${addonId}' has no handler for custom action '${action}'`);
			return fn(input);
		});
	}
	require_manifest_python_deps.validateProviderRegistrations(addonId, declaration, registeredProviders, console);
	if (typeof addon.restoreDevices === "function") require_worker_device_restore.runWorkerDeviceRestoreWithRetry(addon, context, addonId, broker.nodeID);
	const methodResolver = (capName) => {
		const reg = initResult?.providers?.find((r) => r.capability.name === capName);
		if (!reg) return [];
		return Object.keys((0, _camstack_types.expandCapMethods)(reg.capability));
	};
	const timeoutResolver = (capName, methodName) => {
		const reg = initResult?.providers?.find((r) => r.capability.name === capName);
		if (!reg) return void 0;
		return (0, _camstack_types.expandCapMethods)(reg.capability)[methodName]?.timeoutMs;
	};
	const schema = require_manifest_python_deps.createAddonService(addon, declaration, {
		providers: registeredProviders,
		methodResolver,
		timeoutResolver,
		customActions: initResult?.customActions,
		actionHandlers: initResult?.actionHandlers
	});
	const actionNames = Object.keys(schema.actions ?? {});
	console.log(`[process-runner] "${addonId}" actions: ${actionNames.length > 0 ? actionNames.join(", ") : "(none)"}`);
	broker.createService(schema);
	addon.postBrokerStart();
	const parentBase = nodeId.split("/")[0] ?? nodeId;
	let hubReachableFired = false;
	broker.localBus.on("$node.connected", ({ node }) => {
		if (node.id !== parentBase && node.id !== "hub") return;
		require_manifest_python_deps.setHubConnected(broker);
		if (hubReachableFired) return;
		hubReachableFired = true;
		if (typeof addon.onHubReachable === "function") Promise.resolve(addon.onHubReachable()).catch((err) => {
			console.error(`[process-runner] "${nodeId}" onHubReachable() threw:`, err);
		});
	});
	console.log(`[process-runner] "${nodeId}" started — waiting for parent node discovery`);
	process.stdout.write(`READY:${nodeId}\n`);
	const shutdown = async () => {
		console.log(`[process-runner] "${nodeId}" shutting down...`);
		try {
			await addon.shutdown();
		} catch {}
		await broker.stop();
		process.exit(0);
	};
	process.on("SIGTERM", shutdown);
	process.on("SIGINT", shutdown);
	const initialPpid = process.ppid;
	setInterval(() => {
		if (process.ppid === 1 && initialPpid !== 1) {
			console.error(`[process-runner] "${nodeId}" parent died (reparented to init) — exiting`);
			process.exit(0);
		}
	}, 2e3).unref();
}
main().catch((err) => {
	console.error("[process-runner] Fatal:", err);
	process.exit(1);
});
//#endregion
exports.main = main;

//# sourceMappingURL=process-runner.js.map