@camstack/agent 1.0.8 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-U6SD2QE6.mjs → chunk-EMAD7O4T.mjs} +132 -16
- package/dist/chunk-EMAD7O4T.mjs.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +153 -16
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +26 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +126 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +3 -2
- package/dist/chunk-U6SD2QE6.mjs.map +0 -1
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "CamStack remote agent — standalone Moleculer node for distributed addon execution",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|
|
@@ -41,7 +41,8 @@
|
|
|
41
41
|
"fastify": "^5",
|
|
42
42
|
"@fastify/static": "^9.1.3",
|
|
43
43
|
"@fastify/cors": "^11.2.0",
|
|
44
|
-
"moleculer": "^0.15.0"
|
|
44
|
+
"moleculer": "^0.15.0",
|
|
45
|
+
"undici": "^7.28.0"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
48
|
"tsup": "^8.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/agent-config.ts","../src/agent-service.ts","../src/agent-deploy-swap.ts","../src/agent-http.ts","../src/agent-bootstrap.ts","../src/agent-group-runner.ts","../src/agent-cap-dispatch-service.ts","../src/register-agent-cap-dispatch.ts"],"sourcesContent":["import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as crypto from 'node:crypto'\nimport * as os from 'node:os'\n\ninterface AgentConfig {\n readonly nodeId: string\n readonly name: string\n readonly hubAddress?: string\n readonly dataDir: string\n readonly addonsDir: string\n readonly logLevel: string\n readonly secret?: string\n /** Resolved path to the config file (for persisting renames). */\n readonly configPath: string\n /** HTTP port for the agent status API + optional UI (default: 4444). */\n readonly statusPort: number\n}\n\n/**\n * Generate a stable nodeId and persist it to the config file.\n *\n * Resolution order (first match wins, persisted on first boot):\n * 1. Existing `nodeId` in the config file — survives env var changes\n * and host renames once seeded.\n * 2. `CAMSTACK_NODE_ID` env var on first boot — lets dev workflows\n * pin a stable, human-readable nodeId (e.g. `dev-agent-0`)\n * without clobbering already-persisted random ids.\n * 3. A fresh random hex (`agent-a1b2c3`) — production fallback when\n * no env var is set.\n *\n * Seeded values get written to the config file so subsequent boots\n * round-trip exactly the same identity (Moleculer nodeID, capability\n * routing, persisted agentSettings — all keyed by this string).\n */\nfunction ensurePersistedNodeId(configPath: string, dataDir: string): string {\n const resolvedPath = path.resolve(dataDir, configPath)\n let raw: Record<string, unknown> = {}\n if (fs.existsSync(resolvedPath)) {\n try {\n raw = JSON.parse(fs.readFileSync(resolvedPath, 'utf-8')) as Record<string, unknown>\n } catch {\n /* corrupt file */\n }\n }\n if (typeof raw.nodeId === 'string' && raw.nodeId.length > 0) {\n return raw.nodeId\n }\n const envSeed = process.env.CAMSTACK_NODE_ID\n const id =\n envSeed && envSeed.length > 0 ? envSeed : `agent-${crypto.randomBytes(4).toString('hex')}`\n raw.nodeId = id\n try {\n fs.mkdirSync(path.dirname(resolvedPath), { recursive: true })\n fs.writeFileSync(resolvedPath, JSON.stringify(raw, null, 2), 'utf-8')\n } catch {\n /* best-effort */\n }\n return id\n}\n\nfunction loadAgentConfig(configPath?: string, dataDirOverride?: string): AgentConfig {\n const filePath = configPath ?? process.env.CAMSTACK_AGENT_CONFIG ?? 'agent.json'\n const dataDir = dataDirOverride ?? process.env.CAMSTACK_DATA_DIR ?? './camstack-data'\n const resolvedDataDir = path.resolve(dataDir)\n\n // Name: config file takes priority over env (allows UI rename to stick).\n // Env is the initial seed, config file is the persisted override.\n const envName =\n process.env.CAMSTACK_NODE_ID ??\n process.env.CAMSTACK_AGENT_NAME ??\n `${os.hostname()}-${os.arch()}`\n\n // Environment variables for hub connection (optional — agent starts without)\n const envHubAddress = process.env.CAMSTACK_HUB_ADDRESS || undefined\n const envSecret = process.env.CAMSTACK_CLUSTER_SECRET || undefined\n const configFullPath = path.resolve(resolvedDataDir, filePath)\n const nodeId = ensurePersistedNodeId(filePath, resolvedDataDir)\n\n // Read persisted config — these override env vars\n let fileHubAddress: string | undefined\n let fileName: string | undefined\n let fileSecret: string | undefined\n if (fs.existsSync(configFullPath)) {\n try {\n const raw = JSON.parse(fs.readFileSync(configFullPath, 'utf-8')) as Record<string, unknown>\n fileHubAddress = typeof raw.hubAddress === 'string' ? raw.hubAddress : undefined\n fileName = typeof raw.name === 'string' ? raw.name : undefined\n fileSecret = typeof raw.secret === 'string' ? raw.secret : undefined\n } catch {\n /* corrupt config */\n }\n }\n\n // Config file wins over env for all user-editable fields\n const effectiveName = fileName ?? envName\n const effectiveHub = fileHubAddress ?? envHubAddress\n const effectiveSecret = fileSecret ?? envSecret\n\n return {\n nodeId,\n name: effectiveName,\n hubAddress: effectiveHub,\n dataDir: resolvedDataDir,\n addonsDir: path.resolve(resolvedDataDir, 'addons'),\n logLevel: process.env.CAMSTACK_LOG_LEVEL ?? 'info',\n secret: effectiveSecret,\n configPath: configFullPath,\n statusPort: Number(process.env.CAMSTACK_STATUS_PORT) || 4444,\n }\n}\n\nexport { loadAgentConfig }\nexport type { AgentConfig }\n","import * as os from 'node:os'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type { ServiceSchema, Context } from 'moleculer'\nimport { Errors } from 'moleculer'\nimport type { AgentHealth, IMetricsProvider, IScopedLogger } from '@camstack/types'\nimport type { RegisterNodeParams, RegisterNodeResult } from '@camstack/system'\nimport { clusterSecretMatches, CLUSTER_SECRET_MISMATCH_TYPE } from '@camstack/system'\nimport { applyDeployedBundle } from './agent-deploy-swap.js'\n\n/**\n * Console-backed scoped logger for the deploy swap (the only consumer in this\n * file). `applyDeployedBundle` logs only on the rare restore-after-failed-swap\n * path, so a thin console adapter is sufficient.\n */\nconst deploySwapLogger: IScopedLogger = {\n info: (msg) => console.log(`[Agent] ${msg}`),\n warn: (msg) => console.warn(`[Agent] ${msg}`),\n error: (msg) => console.error(`[Agent] ${msg}`),\n debug: (msg) => console.debug(`[Agent] ${msg}`),\n child: () => deploySwapLogger,\n withTags: () => deploySwapLogger,\n}\n\n/**\n * Narrow interface for the Moleculer broker surface used in this file.\n * moleculer's index.d.ts chains through eventemitter2 whose package.json\n * has no `types` field; typescript-eslint's no-unsafe-* rules flag every\n * broker/ctx access. Cast once at each handler boundary via CtxLike.\n */\ninterface BrokerLike {\n readonly nodeID: string\n logger: {\n info(msg: string, ...args: unknown[]): void\n warn(msg: string, ...args: unknown[]): void\n }\n destroyService(name: string): Promise<void>\n}\n\ninterface CtxLike<P> {\n params: P\n broker: BrokerLike\n}\n\nfunction getLocalIps(): string[] {\n const interfaces = os.networkInterfaces()\n const ips: string[] = []\n for (const ifaces of Object.values(interfaces)) {\n if (!ifaces) continue\n for (const iface of ifaces) {\n if (iface.internal) continue\n ips.push(iface.address)\n }\n }\n return ips\n}\n\n/**\n * Resolve a promise but never block longer than `ms` — on timeout, resolve to\n * `fallback`. `$agent.status`/`$agent.health` MUST always answer promptly: the\n * hub drops a node from `nodes.topology` whenever `$agent.status` times out, so\n * a status RPC that hangs on a transiently-unavailable metrics provider (e.g.\n * mid-reload) makes the agent silently vanish from the cluster. Bounding the\n * metrics fetch keeps the node visible with a degraded (zeroed) metrics block\n * instead of disappearing.\n */\nasync function withTimeout<T>(p: Promise<T>, ms: number, fallback: T): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined\n const timeout = new Promise<T>((resolve) => {\n timer = setTimeout(() => resolve(fallback), ms)\n })\n try {\n return await Promise.race([p, timeout])\n } finally {\n if (timer) clearTimeout(timer)\n }\n}\n\n/** Bound for the metrics snapshot inside status/health — see `withTimeout`. */\nconst METRICS_SNAPSHOT_TIMEOUT_MS = 2_000\n\ninterface LoadedAddonEntry {\n readonly id: string\n readonly status: string\n /** Addon DECLARATION version (from the addon manifest). */\n readonly version?: string\n readonly packageName?: string\n /**\n * npm PACKAGE version (from the package's package.json) — distinct from the\n * addon declaration `version`. The hub's per-node update check compares the\n * installed PACKAGE version against the registry, so it must be reported\n * here; reporting the declaration version (often `0.1.0` while the package\n * is `1.0.4`) makes `listUpdates` show a permanent phantom update.\n */\n readonly packageVersion?: string\n readonly addon?: {\n shutdown?(): Promise<void>\n onHubReachable?(): Promise<void> | void\n }\n}\n\ninterface AgentServiceDeps {\n readonly addonsDir: string\n readonly dataDir: string\n /** Human-readable agent name from env/config (e.g. \"dev-agent-0\"). */\n agentName: string\n /** Path to the agent config file (for persisting renames). */\n readonly configPath: string\n readonly loadedAddons: Map<string, LoadedAddonEntry>\n /**\n * Resolver for the current metrics-provider cap. Looked up lazily so the\n * service still answers `$agent.status` even before the metrics addon has\n * finished initializing.\n */\n readonly getMetricsProvider?: () => IMetricsProvider | null\n /** Package version of the agent runtime (informational, surfaced via `$agent.health`). */\n readonly agentVersion?: string\n /**\n * Re-runs the agent's `loadDeployedAddons` discovery pass — picks up new\n * tarballs written under `addonsDir/` by `$agent.deploy` and spawns/wires\n * them without an agent restart. Returns the addon IDs that were freshly\n * loaded this pass. Wired by `agent-bootstrap.ts`; left optional so test\n * harnesses can build a service without driving the full bootstrap.\n */\n readonly reloadDeployedAddons?: () => Promise<readonly string[]>\n /**\n * Called when a group-runner child calls `$agent.registerNode`. The\n * agent-bootstrap wires this to aggregate the child's manifest into the\n * agent's subtree registry and re-register the complete union upward with\n * the hub. Optional so test harnesses can build the service without the\n * full bootstrap subtree machinery.\n */\n readonly onChildRegistered?: (params: RegisterNodeParams) => void\n /**\n * SHA-256 hash of the agent's own cluster secret, or `undefined` when none\n * is configured. When set, `$agent.registerNode` rejects a child runner not\n * presenting a matching `clusterSecretHash`. (Cluster-secret gate.)\n */\n readonly expectedClusterSecretHash?: string\n}\n\ninterface AgentConfigFileShape {\n readonly hubAddress?: string\n}\n\nfunction readHubAddressFromConfig(configPath: string): string | null {\n if (!configPath) return null\n try {\n if (!fs.existsSync(configPath)) return null\n const raw = JSON.parse(fs.readFileSync(configPath, 'utf-8')) as AgentConfigFileShape\n return typeof raw.hubAddress === 'string' && raw.hubAddress.length > 0 ? raw.hubAddress : null\n } catch {\n return null\n }\n}\n\n/**\n * Read the addon declaration ids contributed by a deployed package.\n *\n * `loadedAddons` is keyed by `camstack.addons[].id` (the declaration\n * id), not the package name. A redeploy's `$agent.deploy` param is the\n * package name, so the deploy handler reads the freshly-extracted\n * `package.json` to recover the real ids it must evict before\n * `$agent.reload`. Best-effort — returns `[]` on a missing/corrupt\n * manifest (the reload then just falls back to its on-disk scan).\n */\nfunction readDeployedAddonIds(addonDir: string): readonly string[] {\n try {\n const manifestPath = path.join(addonDir, 'package.json')\n if (!fs.existsSync(manifestPath)) return []\n const raw = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) as {\n camstack?: { addons?: readonly { id?: unknown }[] }\n }\n const entries = raw.camstack?.addons ?? []\n const ids: string[] = []\n for (const entry of entries) {\n if (typeof entry.id === 'string' && entry.id.length > 0) ids.push(entry.id)\n }\n return ids\n } catch {\n return []\n }\n}\n\nfunction isHubConnected(broker: BrokerLike): boolean {\n try {\n const registry = (broker as unknown as Record<string, unknown>).registry as\n | { getNodeList?: (opts: { onlyAvailable: boolean }) => readonly { id: string }[] }\n | undefined\n const nodes = registry?.getNodeList?.({ onlyAvailable: true }) ?? []\n return nodes.some((n) => n.id === 'hub')\n } catch {\n return false\n }\n}\n\nfunction createAgentService(deps: AgentServiceDeps): ServiceSchema {\n return {\n name: '$agent',\n actions: {\n status: {\n handler: async (ctx: Context) => {\n const { broker } = ctx as unknown as CtxLike<Record<string, never>>\n const cpus = os.cpus()\n let cpuPercent = 0\n let memoryPercent = 0\n const metrics = deps.getMetricsProvider?.()\n if (metrics) {\n // Bounded so $agent.status never hangs (and drops the node from\n // topology) when the metrics provider is mid-reload/unavailable.\n const snapshot = await withTimeout(\n metrics.getCached(),\n METRICS_SNAPSHOT_TIMEOUT_MS,\n null,\n )\n if (snapshot) {\n cpuPercent = snapshot.cpu.total\n memoryPercent = snapshot.memory.percent\n }\n }\n return {\n nodeId: broker.nodeID,\n name: deps.agentName,\n platform: os.platform(),\n arch: os.arch(),\n hostname: os.hostname(),\n cpuCores: cpus.length,\n cpuModel: cpus[0]?.model,\n totalMemoryMB: Math.round(os.totalmem() / 1024 / 1024),\n freeMemoryMB: Math.round(os.freemem() / 1024 / 1024),\n cpuPercent,\n memoryPercent,\n uptime: os.uptime(),\n localIps: getLocalIps(),\n addons: [...deps.loadedAddons.values()].map((a) => ({\n id: a.id,\n status: a.status,\n // Report the PACKAGE version (falls back to the declaration\n // version) so the hub's per-node update check is accurate.\n version: a.packageVersion ?? a.version,\n packageName: a.packageName,\n })),\n }\n },\n },\n\n health: {\n handler: async (ctx: Context): Promise<AgentHealth> => {\n const { broker } = ctx as unknown as CtxLike<Record<string, never>>\n let cpuPercent = 0\n let memoryPercent = 0\n const metrics = deps.getMetricsProvider?.()\n if (metrics) {\n try {\n // Bounded — see $agent.status. Health must answer promptly too.\n const snapshot = await withTimeout(\n metrics.getCached(),\n METRICS_SNAPSHOT_TIMEOUT_MS,\n null,\n )\n if (snapshot) {\n cpuPercent = snapshot.cpu.total\n memoryPercent = snapshot.memory.percent\n }\n } catch {\n /* metrics may be transiently unavailable */\n }\n }\n let total = 0\n let running = 0\n let errored = 0\n for (const a of deps.loadedAddons.values()) {\n total++\n if (a.status === 'running') running++\n else if (a.status === 'error') errored++\n }\n const hubAddress = readHubAddressFromConfig(deps.configPath)\n return {\n ok: errored === 0,\n nodeId: broker.nodeID,\n name: deps.agentName,\n version: deps.agentVersion ?? 'unknown',\n uptimeSeconds: Math.round(process.uptime()),\n pid: process.pid,\n hubConnected: isHubConnected(broker),\n hubAddress,\n addons: { total, running, error: errored },\n cpuPercent,\n memoryPercent,\n checkedAt: new Date().toISOString(),\n }\n },\n },\n\n shutdown: {\n handler() {\n // Graceful shutdown — schedule so the Moleculer response goes out first\n setTimeout(() => process.exit(0), 500)\n return { success: true }\n },\n },\n\n rename: {\n handler(ctx: Context<{ name: string }>) {\n const { params, broker } = ctx as unknown as CtxLike<{ name: string }>\n const newName = params.name\n if (!newName || typeof newName !== 'string') {\n throw new Error('$agent.rename: name is required')\n }\n const oldName = deps.agentName\n // Update in-memory name (affects subsequent $agent.status responses)\n deps.agentName = newName.trim()\n broker.logger.info(`Agent renamed: \"${oldName}\" → \"${deps.agentName}\"`)\n // Persist to config file\n try {\n const configFile = path.resolve(deps.configPath)\n let raw: Record<string, unknown> = {}\n if (fs.existsSync(configFile)) {\n try {\n raw = JSON.parse(fs.readFileSync(configFile, 'utf-8')) as Record<string, unknown>\n } catch {\n /* corrupt */\n }\n }\n raw.name = deps.agentName\n fs.mkdirSync(path.dirname(configFile), { recursive: true })\n fs.writeFileSync(configFile, JSON.stringify(raw, null, 2), 'utf-8')\n broker.logger.info(`Agent name persisted to ${configFile}`)\n } catch (err) {\n broker.logger.warn(\n 'Agent rename: config file write failed (in-memory rename still active)',\n { error: String(err) },\n )\n }\n return { success: true, name: deps.agentName }\n },\n },\n\n listAddons: {\n handler() {\n return [...deps.loadedAddons.keys()]\n },\n },\n\n deploy: {\n handler: async (\n ctx: Context<{\n addonId: string\n bundle: Buffer | string\n config?: Record<string, unknown>\n }>,\n ) => {\n const { params } = ctx as unknown as CtxLike<{\n addonId: string\n bundle: Buffer | string\n config?: Record<string, unknown>\n }>\n const { addonId, bundle } = params\n const bufferData = typeof bundle === 'string' ? Buffer.from(bundle, 'base64') : bundle\n\n // execFile (no shell) instead of execSync — addonId comes from RPC\n // params and was previously interpolated into a shell command, which\n // is a command-injection vector. argv form doesn't go through a shell\n // so any payload in addonId stays a literal path arg. Use the ASYNC\n // form (not execFileSync): a synchronous extract blocks the agent\n // event loop for the whole untar, which on a large package starves\n // concurrent hub RPCs ($agent.status heartbeat) long enough for the\n // hub to time them out and drop the node from topology mid-deploy.\n const { execFile } = await import('node:child_process')\n const { promisify } = await import('node:util')\n const execFileAsync = promisify(execFile)\n\n // Atomic install: extract into a temp dir then swap into place. The\n // old body `rm`'d the live dir THEN untarred — a killed/timed-out\n // untar (e.g. a redeploy whose RPC was cut) left the addon dir\n // MISSING. `applyDeployedBundle` never leaves the live dir absent\n // (the non-atomic-deploy fix).\n const extract = async (tgz: Buffer, destDir: string): Promise<void> => {\n const tgzPath = path.join(destDir, '..', `.${path.basename(destDir)}.tgz`)\n fs.writeFileSync(tgzPath, tgz)\n try {\n await execFileAsync('tar', ['-xzf', tgzPath, '-C', destDir, '--strip-components=1'], {\n timeout: 60000,\n })\n } finally {\n try {\n fs.unlinkSync(tgzPath)\n } catch {\n /* best-effort temp cleanup */\n }\n }\n }\n\n const { addonDir } = await applyDeployedBundle({\n addonsDir: deps.addonsDir,\n addonId,\n bundle: bufferData,\n extract,\n logger: deploySwapLogger,\n })\n\n // Evict every addon DECLARATION id this package contributes\n // from `loadedAddons` so the follow-up `$agent.reload` actually\n // re-instantiates it. `loadDeployedAddons` skips any addon\n // still present in `loadedAddons`; without this eviction a\n // redeploy would leave the agent pinned to the pre-update\n // version. The `deploy` param `addonId` is the PACKAGE name\n // (used only as the on-disk dir), whereas `loadedAddons` is\n // keyed by the addon DECLARATION id — they differ for scoped\n // packages, so we read the extracted manifest to bridge them.\n for (const declId of readDeployedAddonIds(addonDir)) {\n deps.loadedAddons.delete(declId)\n }\n\n return { success: true, addonId, path: addonDir }\n },\n },\n\n /**\n * Genuinely drop a deployed addon from this agent.\n *\n * A plain `loadedAddons.delete()` only forgets the bookkeeping entry —\n * the addon instance keeps running and, crucially, its Moleculer\n * service keeps advertising the addon's capabilities into the cluster\n * (the hub still sees the provider as a live `<cap>@<agent>` entry).\n * To truly undeploy we must, in order:\n * 1. `shutdown()` the running instance (releases timers, sockets,\n * native handles) — same hook the agent's SIGTERM path uses.\n * 2. `broker.destroyService(addonId)` — the deployed-addon Moleculer\n * service is named after the addon DECLARATION id (see\n * `createAddonService` → `name: declaration.id`). Destroying it\n * unregisters every capability action so the cluster stops\n * routing to / advertising this agent's provider.\n * 3. Drop the `loadedAddons` entry and delete the on-disk folder.\n *\n * `addonId` here is the addon DECLARATION id — the same key\n * `loadedAddons` uses and the same value `$agent.status` reports, so\n * the hub reconciler can match it directly.\n */\n undeploy: {\n handler: async (ctx: Context<{ addonId: string }>) => {\n const { params, broker } = ctx as unknown as CtxLike<{ addonId: string }>\n const { addonId } = params\n const entry = deps.loadedAddons.get(addonId)\n\n // 1. Shut the running instance down (best-effort — a throwing\n // disposer must not block the rest of the teardown).\n if (entry?.addon?.shutdown) {\n try {\n await entry.addon.shutdown()\n } catch (err) {\n broker.logger.warn(`$agent.undeploy: ${addonId} shutdown() threw`, {\n error: String(err),\n })\n }\n }\n\n // 2. Destroy the Moleculer service so the cluster stops seeing\n // this agent's capability providers for the addon.\n try {\n await broker.destroyService(addonId)\n } catch {\n // Service may not exist (group-spawned addons have no per-addon\n // service on the agent broker, or it was never created).\n }\n\n // 3. Forget the entry and remove the deployed folder. The deploy\n // handler keys the on-disk dir by the PACKAGE name, but $agent\n // redeploys for a single addon use addonId === packageName for\n // unscoped packages; remove whichever folder matches.\n deps.loadedAddons.delete(addonId)\n const addonDir = path.join(deps.addonsDir, addonId)\n if (fs.existsSync(addonDir)) {\n fs.rmSync(addonDir, { recursive: true, force: true })\n }\n const pkgName = entry?.packageName\n if (pkgName && pkgName !== addonId) {\n const pkgDir = path.join(deps.addonsDir, pkgName)\n if (fs.existsSync(pkgDir)) {\n fs.rmSync(pkgDir, { recursive: true, force: true })\n }\n }\n\n broker.logger.info(`$agent.undeploy: ${addonId} disposed (instance + service + folder)`)\n return { success: true, addonId }\n },\n },\n\n /**\n * Re-run `loadDeployedAddons` so addons just dropped onto disk via\n * `$agent.deploy` (or via the hub broadcasting an upload) start\n * immediately. Without this the agent only discovers them on boot.\n * Returns the ids that were newly loaded — the hub uses this to\n * surface a per-agent diff in the upload response.\n */\n reload: {\n handler: async (): Promise<{ success: boolean; loaded: readonly string[] }> => {\n if (!deps.reloadDeployedAddons) {\n return { success: false, loaded: [] }\n }\n const loaded = await deps.reloadDeployedAddons()\n return { success: true, loaded }\n },\n },\n\n restart: {\n handler: async (ctx: Context<{ addonId: string }>) => {\n const { params } = ctx as unknown as CtxLike<{ addonId: string }>\n const { addonId } = params\n return { success: true, addonId, action: 'restart-queued' }\n },\n },\n\n /**\n * D3 subtree aggregation: a forked group-runner child calls this action\n * to deliver its complete capability manifest to the agent. The agent\n * then re-registers the UNION of its own in-process addons + all children\n * with the hub via `$hub.registerNode`.\n */\n registerNode: {\n handler(ctx: Context<RegisterNodeParams>): RegisterNodeResult {\n const { params } = ctx as unknown as CtxLike<RegisterNodeParams>\n // Cluster-secret gate: when the agent has a secret configured, a child\n // runner must present a matching hash or the registration is rejected.\n if (!clusterSecretMatches(deps.expectedClusterSecretHash, params.clusterSecretHash)) {\n throw new Errors.MoleculerError(\n `cluster secret mismatch — node \"${params.nodeId}\" rejected`,\n 403,\n CLUSTER_SECRET_MISMATCH_TYPE,\n )\n }\n deps.onChildRegistered?.(params)\n return { ok: true }\n },\n },\n },\n }\n}\n\nexport { createAgentService }\nexport type { AgentServiceDeps, LoadedAddonEntry }\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { randomBytes } from 'node:crypto'\nimport type { IScopedLogger } from '@camstack/types'\n\n/**\n * Inputs for {@link applyDeployedBundle}.\n *\n * `extract` is injected (rather than hard-wiring `tar`) so the swap logic is\n * unit-testable without a real tarball and so the caller controls the extractor\n * (the `$agent.deploy` handler wires the async `execFile tar --strip-components=1`\n * it already uses).\n */\nexport interface ApplyDeployedBundleInput {\n /** Directory holding installed addon package dirs (`<addonsDir>/<addonId>`). */\n readonly addonsDir: string\n /** On-disk dir name for this package (the `$agent.deploy` `addonId` param). */\n readonly addonId: string\n /** The `.tgz` bytes to extract. */\n readonly bundle: Buffer\n /** Extract `tgz` into `destDir` (stripping the npm `package/` prefix). */\n readonly extract: (tgz: Buffer, destDir: string) => Promise<void>\n readonly logger: IScopedLogger\n}\n\n/** Recursively remove a path, ignoring a missing target. */\nfunction rmrf(target: string): void {\n fs.rmSync(target, { recursive: true, force: true })\n}\n\n/**\n * Move `from` → `to` atomically when on the same filesystem; fall back to a\n * recursive copy + remove on a cross-device boundary (`EXDEV`). The agent's\n * `/data` addonsDir is frequently a different filesystem from the OS temp dir,\n * so a bare `renameSync` would throw `EXDEV` — the same fallback\n * `AddonInstaller.applyUpdateFromStaged` uses.\n */\nfunction moveDir(from: string, to: string): void {\n try {\n fs.renameSync(from, to)\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code\n if (code === 'EXDEV') {\n fs.cpSync(from, to, { recursive: true })\n rmrf(from)\n } else {\n throw err\n }\n }\n}\n\n/**\n * Atomically install a deployed addon bundle into `<addonsDir>/<addonId>`.\n *\n * Unlike the old `$agent.deploy` body (`rm` the live dir, THEN untar into it),\n * this never leaves the live dir missing: extraction happens in a sibling temp\n * dir first, the live dir is swapped in by rename, and any failure restores the\n * previous copy. A killed/timed-out extract (the non-atomic-deploy hazard that\n * a contaminated propagation test hit) leaves the old version fully intact.\n *\n * No `AddonInstaller` manifest dependency — works for any deployed addon,\n * including ones never tracked in the agent's install manifest.\n *\n * @returns the live addon directory path (`<addonsDir>/<addonId>`).\n */\nexport async function applyDeployedBundle(\n input: ApplyDeployedBundleInput,\n): Promise<{ addonDir: string }> {\n const { addonsDir, addonId, bundle, extract, logger } = input\n\n fs.mkdirSync(addonsDir, { recursive: true })\n const liveDir = path.join(addonsDir, addonId)\n // Place the temp + backup dirs as SIBLINGS of the live dir (same parent →\n // same filesystem → atomic rename). Deriving them from the live dir's parent\n // and basename keeps a scoped `addonId` like \"@camstack/addon-pipeline\" — whose\n // slash would otherwise turn `.<addonId>.next` into a nested path — correct.\n const liveParent = path.dirname(liveDir)\n const liveBase = path.basename(liveDir)\n fs.mkdirSync(liveParent, { recursive: true })\n const token = randomBytes(6).toString('hex')\n const nextDir = path.join(liveParent, `.${liveBase}.next.${token}`)\n const backupDir = path.join(liveParent, `.${liveBase}.backup.${token}`)\n\n // 1. Extract into a temp dir. If extraction throws (e.g. a killed untar),\n // drop the temp dir and rethrow — the live dir is never touched.\n fs.mkdirSync(nextDir, { recursive: true })\n try {\n await extract(bundle, nextDir)\n } catch (err) {\n rmrf(nextDir)\n throw err\n }\n\n // 2. Swap. Back up the live dir (if present), then move next → live. On any\n // failure, restore the backup so the addon stays installed at the old\n // version.\n const hadLive = fs.existsSync(liveDir)\n if (hadLive) {\n fs.renameSync(liveDir, backupDir)\n }\n try {\n moveDir(nextDir, liveDir)\n } catch (swapErr) {\n if (hadLive) {\n try {\n if (fs.existsSync(liveDir)) rmrf(liveDir)\n fs.renameSync(backupDir, liveDir)\n } catch (restoreErr) {\n logger.error(\n `agent deploy swap: restore of \"${addonId}\" failed after a failed swap — manual recovery may be needed`,\n {\n meta: {\n backupDir,\n error: restoreErr instanceof Error ? restoreErr.message : String(restoreErr),\n },\n },\n )\n }\n }\n rmrf(nextDir)\n throw swapErr\n }\n\n // 3. Success — drop the backup.\n if (hadLive) rmrf(backupDir)\n return { addonDir: liveDir }\n}\n","/**\n * Agent HTTP server -- lightweight Fastify instance for agent status,\n * process management, config editing, and static UI serving.\n */\nimport Fastify from 'fastify'\nimport type { FastifyInstance } from 'fastify'\nimport type { ServiceBroker } from 'moleculer'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\n// ---------------------------------------------------------------------------\n// Connection state\n// ---------------------------------------------------------------------------\n\n/**\n * Discriminated hub connection state surfaced by the agent status API.\n *\n * - `connected` — hub node is in the Moleculer registry (available).\n * - `searching` — discovery mode active, no hub found yet.\n * - `disconnected` — direct address configured but hub not in registry.\n * - `secret-mismatch`— `$hub.registerNode` was rejected with a cluster-secret error.\n *\n * `registering` (transport up but registerNode not yet acked) is not\n * currently deterministic from the broker registry alone — it would require\n * a tighter integration with the retry loop. Omitted until a clean signal\n * exists; the brief gap is invisible at 3 s poll rate.\n */\nexport type HubConnectionState = 'connected' | 'searching' | 'disconnected' | 'secret-mismatch'\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nexport interface AgentHttpConfig {\n readonly port: number\n readonly nodeId: string\n readonly dataDir: string\n readonly configPath: string\n /** Called when the UI requests a reconnect (hub/secret changed). */\n readonly onReconnect?: () => Promise<void>\n /**\n * Optional getter for the current hub connection state. When provided,\n * the status endpoint uses this to surface a richer discriminated state\n * instead of the legacy boolean `hubConnected`. The bootstrap wires this\n * to expose `secret-mismatch` which is not derivable from the broker\n * registry alone.\n */\n readonly getHubConnectionState?: () => HubConnectionState\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction resolveUiDistDir(dataDir: string): string | null {\n const candidates = [\n path.resolve(__dirname, '../../addon-agent-ui/dist'),\n path.join(dataDir, 'addons', '@camstack', 'addon-agent-ui', 'dist'),\n path.join(dataDir, 'agent-ui'),\n ]\n for (const dir of candidates) {\n if (fs.existsSync(path.join(dir, 'index.html'))) return dir\n }\n return null\n}\n\nfunction readConfigFile(configPath: string): Record<string, unknown> {\n if (!fs.existsSync(configPath)) return {}\n try {\n return JSON.parse(fs.readFileSync(configPath, 'utf-8')) as Record<string, unknown>\n } catch {\n return {}\n }\n}\n\nfunction writeConfigFile(configPath: string, data: Record<string, unknown>): void {\n fs.mkdirSync(path.dirname(configPath), { recursive: true })\n fs.writeFileSync(configPath, JSON.stringify(data, null, 2), 'utf-8')\n}\n\nfunction getRegistryNodes(broker: ServiceBroker): readonly { id: string }[] {\n try {\n const registry = (broker as unknown as Record<string, unknown>).registry as\n | { getNodeList?: (opts: { onlyAvailable: boolean }) => readonly { id: string }[] }\n | undefined\n return registry?.getNodeList?.({ onlyAvailable: true }) ?? []\n } catch {\n return []\n }\n}\n\n/**\n * Derive the hub connection state from broker registry + config when no\n * external getter is wired. Does NOT detect `secret-mismatch` — that\n * requires the bootstrap to inject `getHubConnectionState`.\n */\nfunction deriveHubConnectionState(\n nodes: readonly { id: string }[],\n discoveryMode: boolean,\n): HubConnectionState {\n const hubInRegistry = nodes.some((n) => n.id === 'hub')\n if (hubInRegistry) return 'connected'\n if (discoveryMode) return 'searching'\n return 'disconnected'\n}\n\n/**\n * Read the current effective config from the persisted file.\n * This is the single source of truth -- always reflects what the UI wrote.\n */\nfunction getEffectiveConfig(\n configPath: string,\n nodeId: string,\n): {\n name: string\n hubAddress: string | null\n hasSecret: boolean\n} {\n const raw = readConfigFile(configPath)\n return {\n name: typeof raw.name === 'string' ? raw.name : nodeId,\n hubAddress: typeof raw.hubAddress === 'string' ? raw.hubAddress : null,\n hasSecret: typeof raw.secret === 'string' && raw.secret.length > 0,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport async function createAgentHttpServer(\n getBroker: () => ServiceBroker,\n config: AgentHttpConfig,\n): Promise<FastifyInstance> {\n const app = Fastify({ logger: false })\n\n const cors = await import('@fastify/cors')\n await app.register(cors.default)\n\n // -- Health ---------------------------------------------------------\n // Detailed shape lives on `$agent.health` (Moleculer action) so the\n // hub can call the same surface via `broker.call('$agent.health', {},\n // { nodeID })`. The HTTP route delegates so external monitors see the\n // identical payload.\n app.get('/health', async (_req, reply) => {\n try {\n const result = await getBroker().call('$agent.health')\n return result\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n return reply.status(503).send({\n ok: false,\n nodeId: config.nodeId,\n error: message,\n })\n }\n })\n\n // -- Agent status (enriched with connection state) ------------------\n\n app.get('/api/agent/status', async () => {\n const broker = getBroker()\n const eff = getEffectiveConfig(config.configPath, config.nodeId)\n const nodes = getRegistryNodes(broker)\n const hubConnected = nodes.some((n) => n.id === 'hub')\n const discoveryMode = !eff.hubAddress\n const hubConnectionState: HubConnectionState = config.getHubConnectionState\n ? config.getHubConnectionState()\n : deriveHubConnectionState(nodes, discoveryMode)\n\n try {\n const status = (await broker.call('$agent.status')) as Record<string, unknown>\n return {\n ...status,\n name: eff.name,\n hubAddress: eff.hubAddress,\n hubConnected,\n hubConnectionState,\n discoveryMode,\n hasSecret: eff.hasSecret,\n discoveredNodes: nodes.filter((n) => n.id !== broker.nodeID).map((n) => n.id),\n }\n } catch {\n return {\n nodeId: config.nodeId,\n name: eff.name,\n hubAddress: eff.hubAddress,\n hubConnected,\n hubConnectionState,\n discoveryMode,\n hasSecret: eff.hasSecret,\n discoveredNodes: [],\n addons: [],\n localIps: [],\n }\n }\n })\n\n // -- Processes ------------------------------------------------------\n\n app.get('/api/agent/processes', async () => {\n try {\n return await getBroker().call('$process.list')\n } catch {\n return []\n }\n })\n\n // -- Addon restart --------------------------------------------------\n\n app.post<{ Body: { addonId: string } }>('/api/agent/addon/restart', async (req, reply) => {\n const addonId = req.body?.addonId\n if (!addonId) return reply.status(400).send({ error: 'addonId required' })\n return getBroker().call('$agent.restart', { addonId })\n })\n\n // -- Process restart ------------------------------------------------\n\n app.post<{ Body: { name: string } }>('/api/agent/process/restart', async (req, reply) => {\n const name = req.body?.name\n if (!name) return reply.status(400).send({ error: 'name required' })\n return getBroker().call('$process.restart', { name })\n })\n\n // -- Config read (always from file -- single source of truth) -------\n\n app.get('/api/agent/config', async () => {\n const persisted = readConfigFile(config.configPath)\n const eff = getEffectiveConfig(config.configPath, config.nodeId)\n return {\n nodeId: config.nodeId,\n name: eff.name,\n hubAddress: eff.hubAddress,\n hasSecret: eff.hasSecret,\n configPath: config.configPath,\n dataDir: config.dataDir,\n // Include all persisted fields except raw secret\n ...Object.fromEntries(Object.entries(persisted).filter(([k]) => k !== 'secret')),\n }\n })\n\n // -- Config write (merge-patch + persist) ---------------------------\n\n // Fastify (not Express) natively awaits async route handlers and serializes\n // the returned value / catches rejections, so an async handler is correct here.\n // eslint-disable-next-line oxc/no-async-endpoint-handlers\n app.post<{ Body: Record<string, unknown> }>('/api/agent/config', async (req) => {\n const patch = req.body ?? {}\n const existing = readConfigFile(config.configPath)\n const merged = { ...existing, ...patch }\n writeConfigFile(config.configPath, merged)\n\n // Apply name change immediately (no reconnect needed)\n if (typeof patch.name === 'string' && patch.name.trim()) {\n try {\n await getBroker().call('$agent.rename', { name: patch.name.trim() })\n console.log(`[Agent] Name changed to \"${patch.name.trim()}\"`)\n } catch {\n /* best-effort */\n }\n }\n\n // Only flag reconnect when a connection-affecting field actually\n // CHANGED. The form posts every visible field on every Save, so\n // `patch.hubAddress !== undefined` was always true and produced\n // false-positive \"Restart required\" prompts on no-op saves.\n const reconnectRelevant: ReadonlyArray<keyof typeof patch> = ['hubAddress', 'secret']\n const needsReconnect = reconnectRelevant.some(\n (k) => Object.prototype.hasOwnProperty.call(patch, k) && patch[k] !== existing[k],\n )\n\n return {\n success: true,\n restartRequired: needsReconnect,\n }\n })\n\n // -- Agent reconnect (applies new hub/secret config) ----------------\n\n app.post('/api/agent/restart', async () => {\n if (!config.onReconnect) {\n return { success: false, message: 'Reconnect not available' }\n }\n console.log('[Agent] Reconnect requested from UI')\n void config.onReconnect().catch((err: unknown) => {\n console.error('[Agent] Reconnect failed:', err)\n })\n return { success: true, message: 'Agent reconnecting...' }\n })\n\n // -- Discovered nodes -----------------------------------------------\n\n app.get('/api/agent/discovered-nodes', async () => {\n const b = getBroker()\n const nodes = getRegistryNodes(b)\n return nodes.filter((n) => n.id !== b.nodeID).map((n) => ({ id: n.id, isHub: n.id === 'hub' }))\n })\n\n // -- Static file serving (agent-ui) ---------------------------------\n\n const uiDir = resolveUiDistDir(config.dataDir)\n if (uiDir) {\n const fastifyStatic = await import('@fastify/static')\n await app.register(fastifyStatic.default, {\n root: uiDir,\n prefix: '/',\n wildcard: false,\n decorateReply: false,\n })\n\n app.setNotFoundHandler(async (req, reply) => {\n if (req.url.startsWith('/api/') || req.url.startsWith('/health')) {\n return reply.status(404).send({ error: 'Not found' })\n }\n return reply.type('text/html').sendFile('index.html')\n })\n\n console.log(`[Agent] UI served from: ${uiDir}`)\n }\n\n return app\n}\n\n// ---------------------------------------------------------------------------\n// Start helper\n// ---------------------------------------------------------------------------\n\nexport async function startAgentHttpServer(\n getBroker: () => ServiceBroker,\n config: AgentHttpConfig,\n): Promise<void> {\n try {\n const app = await createAgentHttpServer(getBroker, config)\n await app.listen({ port: config.port, host: '0.0.0.0' })\n console.log(`[Agent] HTTP server: http://localhost:${config.port}`)\n } catch (err) {\n console.warn(`[Agent] HTTP server failed to start on port ${config.port}:`, err)\n }\n}\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { startAgentHttpServer } from './agent-http.js'\nimport type { HubConnectionState } from './agent-http.js'\nimport {\n createBroker,\n createAddonService,\n createAddonContext,\n createProcessService,\n deriveAgentListenPort,\n AddonLoader,\n AddonInstaller,\n detectWorkspacePackagesDir,\n CapabilityRegistry,\n INFRA_CAPABILITIES,\n registerEventBusService,\n createHwAccelService,\n createKernelHwAccel,\n HubNodeRegistry,\n callRegisterNodeWithRetry,\n buildNodeManifest,\n hashClusterSecret,\n isClusterSecretMismatchError,\n LocalChildRegistry,\n createLocalTransport,\n localEndpointPath,\n udsChildLogToWorkerEntry,\n createUdsEventBridge,\n getBrokerEventBus,\n getOrInitReadinessRegistry,\n createParentUnownedCallHandler,\n} from '@camstack/system'\nimport type {\n AddonContextOptions,\n RegisterNodeParams,\n RegisteredAddonManifest,\n ChildCapDescriptor,\n InProcessProviderLookup,\n InProcessProviderRef,\n} from '@camstack/system'\nimport type {\n IStorageProvider,\n IScopedLogger,\n ILogDestination,\n IMetricsProvider,\n} from '@camstack/types'\nimport {\n normalizeAddonInitResult,\n isDeployableToAgent,\n resolveRunnerId,\n resolveAddonPlacement,\n} from '@camstack/types'\n// Capability definitions — must be declared before addon registerProvider() calls\nimport {\n storageCapability,\n storageProviderCapability,\n settingsStoreCapability,\n logDestinationCapability,\n metricsProviderCapability,\n decoderCapability,\n motionDetectionCapability,\n pipelineExecutorCapability,\n pipelineRunnerCapability,\n audioAnalyzerCapability,\n platformProbeCapability,\n errMsg,\n} from '@camstack/types'\nimport { loadAgentConfig } from './agent-config.js'\nimport { createAgentService } from './agent-service.js'\nimport type { LoadedAddonEntry } from './agent-service.js'\nimport {\n isGroupRunnerAddon,\n ensureGroupRunner,\n registerGroupRunnerProviders,\n readPackageManifestAddons,\n type GroupCandidate,\n} from './agent-group-runner.js'\nimport { registerAgentCapDispatch } from './register-agent-cap-dispatch.js'\nimport type { ServiceBroker, ServiceSchema, Service } from 'moleculer'\n\n/**\n * Narrow interface for the Moleculer broker surface used in this file.\n * moleculer's index.d.ts chains through eventemitter2 whose package.json\n * has no `types` field; typescript-eslint's no-unsafe-* rules flag every\n * broker access. Cast once: `createBroker(...) as unknown as BrokerLike`.\n *\n * `createService` matches the real `ServiceBroker.createService` signature\n * so that `BrokerLike` is structurally compatible with `AgentServiceRegistrar`.\n */\ninterface BrokerLike {\n readonly nodeID: string\n start(): Promise<void>\n stop(): Promise<void>\n call<T = unknown>(action: string, params?: unknown, opts?: unknown): Promise<T>\n createService(schema: ServiceSchema, schemaMods?: Partial<ServiceSchema>): Service\n localBus: { on(event: string, handler: (data: unknown) => void): void }\n}\n\n// ---------------------------------------------------------------------------\n// Agent LogManager — shared log pipeline for all addon loggers.\n// The hub-forwarder addon registers as a destination, so all log\n// entries flow through it (console output + hub forwarding).\n// ---------------------------------------------------------------------------\nimport { LogManager } from '@camstack/system'\n\nconst agentLogManager = new LogManager(5000)\n\n// ---------------------------------------------------------------------------\n// Main\n// ---------------------------------------------------------------------------\n\nasync function startAgent(configPath?: string): Promise<void> {\n const config = loadAgentConfig(configPath)\n\n if (config.hubAddress) {\n console.log(\n `[Agent] Starting node \"${config.nodeId}\" (name=\"${config.name}\") connecting to ${config.hubAddress}`,\n )\n } else {\n console.log(\n `[Agent] Starting node \"${config.nodeId}\" (name=\"${config.name}\") in discovery mode`,\n )\n }\n console.log(`[Agent] Config: ${config.configPath}`)\n console.log(`[Agent] Data dir: ${config.dataDir}`)\n\n // Ensure required addon packages are installed in the agent's addonsDir.\n // Resolution order:\n // 1. `CAMSTACK_BUNDLED_ADDONS_DIR` — Electron-packaged builds copy\n // from `<resourcesPath>/addons` (`'local'` mode).\n // 2. Otherwise npm from registry. Local dev pushes addons via\n // `camstack deploy` (CLI tarball upload), bypassing this path.\n const explicitSource = process.env['CAMSTACK_INSTALL_SOURCE'] as 'npm' | 'local' | undefined\n const bundledDir = process.env['CAMSTACK_BUNDLED_ADDONS_DIR']\n let workspaceDir: string | null = null\n let resolvedSource: typeof explicitSource = explicitSource\n if (bundledDir && fs.existsSync(bundledDir)) {\n workspaceDir = bundledDir\n resolvedSource = 'local'\n console.log(`[Agent] Using bundled addons from ${bundledDir}`)\n } else if (explicitSource === 'local') {\n workspaceDir = detectWorkspacePackagesDir(config.dataDir)\n }\n const installer = new AddonInstaller({\n addonsDir: config.addonsDir,\n workspacePackagesDir: workspaceDir ?? undefined,\n installSource: resolvedSource,\n })\n await installer.ensureRequiredPackages(AddonInstaller.AGENT_PACKAGES)\n console.log(`[Agent] Addon packages ready in ${config.addonsDir}`)\n\n let broker: BrokerLike = createBroker({\n nodeID: config.nodeId,\n mode: 'agent',\n hubAddress: config.hubAddress,\n logLevel: config.logLevel,\n secret: config.secret,\n }) as unknown as BrokerLike\n\n /**\n * Reconnect: stop the current broker, reload config from file,\n * create a new broker with the updated hub/secret, and restart.\n * The HTTP server stays alive throughout.\n */\n const reconnect = async (): Promise<void> => {\n console.log('[Agent] Reconnecting with updated config...')\n try {\n await broker.stop()\n } catch {\n /* already stopped */\n }\n\n // Reload config from file (UI may have written new hubAddress/secret)\n const fresh = loadAgentConfig(undefined, config.dataDir)\n console.log(\n `[Agent] New config: hub=${fresh.hubAddress ?? 'discovery'}, secret=${fresh.secret ? 'yes' : 'none'}`,\n )\n\n broker = createBroker({\n nodeID: config.nodeId,\n mode: 'agent',\n hubAddress: fresh.hubAddress,\n logLevel: fresh.logLevel,\n secret: fresh.secret,\n }) as unknown as BrokerLike\n await broker.start()\n console.log('[Agent] Reconnected successfully')\n }\n\n const loadedAddons = new Map<string, LoadedAddonEntry>()\n\n // D3 subtree registry: accumulates manifests from every group-runner child\n // that calls `$agent.registerNode`. The agent re-registers the UNION of its\n // own in-process addons + all children with the hub on every child update.\n const subtree = new HubNodeRegistry()\n\n // ---------------------------------------------------------------------------\n // Hub connection state tracking (for richer UI status)\n // ---------------------------------------------------------------------------\n // Tracks the last known discriminated state so the HTTP status endpoint\n // can surface `secret-mismatch` which is not observable from the broker\n // registry alone. All mutations are synchronous/single-threaded (Node.js\n // event loop) so no locking is needed.\n let hubConnectionStateOverride: HubConnectionState | null = null\n\n function getHubConnectionState(): HubConnectionState {\n // If a definitive override is set (e.g. secret-mismatch), return it\n // regardless of registry state.\n if (hubConnectionStateOverride !== null) return hubConnectionStateOverride\n // Fall through to live registry-derived state (connected/searching/disconnected).\n // getRegistryNodes-equivalent inline: broker.registry.getNodeList\n try {\n const registry = (broker as unknown as Record<string, unknown>).registry as\n | { getNodeList?: (opts: { onlyAvailable: boolean }) => readonly { id: string }[] }\n | undefined\n const nodes = registry?.getNodeList?.({ onlyAvailable: true }) ?? []\n if (nodes.some((n) => n.id === 'hub')) return 'connected'\n } catch {\n /* fall through */\n }\n const configNow = readConfigFile(config.configPath)\n const discoveryMode =\n typeof configNow.hubAddress !== 'string' || configNow.hubAddress.length === 0\n return discoveryMode ? 'searching' : 'disconnected'\n }\n\n function readConfigFile(p: string): Record<string, unknown> {\n if (!fs.existsSync(p)) return {}\n try {\n return JSON.parse(fs.readFileSync(p, 'utf-8')) as Record<string, unknown>\n } catch {\n return {}\n }\n }\n\n // AbortController for the upward hub registration retry loop — cancelled on\n // agent shutdown so we don't leak the retry loop after stop().\n const registerAbortController = new AbortController()\n\n /**\n * Build the manifest for the agent's OWN in-process addons (those loaded by\n * bootCoreAddons and loadDeployedAddons that ended up in `loadedAddons` WITH\n * an `addon` instance). Group-spawned addons (loadClusterCapableAddons) have\n * no `addon` instance and register via `$agent.registerNode` from the child.\n *\n * We reconstruct per-addon capability lists by reverse-querying the\n * capabilityRegistry: for every declared capability, check which addonId\n * currently holds the provider and group the result by addonId.\n */\n function buildAgentOwnManifest(): readonly RegisteredAddonManifest[] {\n // Build addonId → capNames map from the registry.\n const addonCapMap = new Map<string, string[]>()\n\n for (const cap of agentCapabilities) {\n if (cap.mode === 'collection') {\n // Collection caps: multiple providers keyed by addonId.\n for (const [addonId] of capabilityRegistry.getCollectionEntries(cap.name)) {\n const list = addonCapMap.get(addonId) ?? []\n list.push(cap.name)\n addonCapMap.set(addonId, list)\n }\n } else {\n // Singleton caps: one active provider.\n const addonId = capabilityRegistry.getSingletonAddonId(cap.name)\n if (addonId) {\n const list = addonCapMap.get(addonId) ?? []\n list.push(cap.name)\n addonCapMap.set(addonId, list)\n }\n }\n }\n\n // Only emit in-process addons (those with an `addon` instance).\n const result: RegisteredAddonManifest[] = []\n for (const [addonId, entry] of loadedAddons) {\n if (!entry.addon) continue\n const caps = addonCapMap.get(addonId) ?? []\n result.push({ addonId, capabilities: caps })\n }\n return result\n }\n\n /**\n * Aggregate the agent's own manifest + every child's manifest into a single\n * RegisterNodeParams for the agent's nodeId. This is sent upward to the hub.\n */\n function aggregateManifest(): RegisterNodeParams {\n const ownAddons = buildAgentOwnManifest()\n const allAddons: RegisteredAddonManifest[] = [...ownAddons]\n const allNativeCaps = []\n\n for (const childNodeId of subtree.listNodeIds()) {\n const childAddons = subtree.getNodeManifest(childNodeId)\n if (childAddons) {\n allAddons.push(...childAddons)\n }\n const childNativeCaps = subtree.getNodeNativeCaps(childNodeId)\n if (childNativeCaps) {\n allNativeCaps.push(...childNativeCaps)\n }\n }\n\n return buildNodeManifest(\n config.nodeId,\n allAddons,\n allNativeCaps.length > 0 ? allNativeCaps : undefined,\n config.secret ? hashClusterSecret(config.secret) : undefined,\n )\n }\n\n /**\n * Fire (or re-fire) the upward `$hub.registerNode` registration carrying\n * the agent's complete subtree union. Called:\n * - after initial addon loading completes\n * - on every `$agent.registerNode` from a child\n * - on hub reconnect (via `$node.connected`)\n */\n function triggerUpwardRegistration(): void {\n callRegisterNodeWithRetry(\n broker as unknown as Parameters<typeof callRegisterNodeWithRetry>[0],\n aggregateManifest(),\n {\n target: 'hub',\n signal: registerAbortController.signal,\n log: (msg) => console.log(`[Agent] ${msg}`),\n },\n ).catch((err: unknown) => {\n if (isClusterSecretMismatchError(err)) {\n consoleLogger.error(\n 'hub registration rejected: cluster secret mismatch — correct CAMSTACK_CLUSTER_SECRET and restart the agent',\n )\n // Surface the mismatch in the UI status.\n hubConnectionStateOverride = 'secret-mismatch'\n return\n }\n // abort (shutdown) — preserve prior void behaviour: ignore.\n })\n }\n\n const consoleLogger: IScopedLogger = {\n info: (msg) => console.log(`[Agent] ${msg}`),\n warn: (msg) => console.warn(`[Agent] ${msg}`),\n error: (msg) => console.error(`[Agent] ${msg}`),\n debug: (msg) => console.debug(`[Agent] ${msg}`),\n child: () => consoleLogger,\n withTags: () => consoleLogger,\n }\n const capabilityRegistry = new CapabilityRegistry(consoleLogger)\n\n // Declare all capabilities the agent uses — required before registerProvider()\n const agentCapabilities = [\n storageCapability,\n storageProviderCapability,\n settingsStoreCapability,\n logDestinationCapability,\n metricsProviderCapability,\n decoderCapability,\n motionDetectionCapability,\n pipelineExecutorCapability,\n pipelineRunnerCapability,\n audioAnalyzerCapability,\n platformProbeCapability,\n ]\n for (const cap of agentCapabilities) {\n capabilityRegistry.declareCapability(cap)\n }\n\n // Logger factory — creates scoped loggers from the shared LogManager.\n // All entries flow through registered ILogDestination providers (hub-forwarder).\n // No scope — the brand bracket `[agent/addonId]` already identifies the addon.\n // `addonId` tag is required for the brand bracket resolver.\n const loggerFactory = (addonId: string) => agentLogManager.createLogger().withTags({ addonId })\n\n const agentServiceSchema = createAgentService({\n addonsDir: config.addonsDir,\n dataDir: config.dataDir,\n agentName: config.name,\n configPath: config.configPath,\n loadedAddons,\n // Resolve the current metrics-provider cap lazily from the registry.\n // The cap is registered during bootCoreAddons but may not exist in test\n // scenarios — `null` is a documented fallback for both.\n getMetricsProvider: () => capabilityRegistry.getSingleton<IMetricsProvider>('metrics-provider'),\n agentVersion: readAgentVersion(),\n // Drives `$agent.reload` — re-runs the same discovery pass the bootstrap\n // does at startup, picking up tarballs just landed via `$agent.deploy`.\n // `storageProvider` is resolved lazily on each call because this closure\n // is captured here, BEFORE `bootCoreAddons` registers the storage cap.\n reloadDeployedAddons: async (): Promise<readonly string[]> => {\n const before = new Set(loadedAddons.keys())\n const storage = capabilityRegistry.getSingleton<IStorageProvider>('storage') ?? undefined\n await loadDeployedAddons(\n broker,\n config.addonsDir,\n config.dataDir,\n loadedAddons,\n storage,\n loggerFactory,\n capabilityRegistry,\n )\n const loaded: string[] = []\n for (const id of loadedAddons.keys()) {\n if (!before.has(id)) loaded.push(id)\n }\n return loaded\n },\n // D3 subtree aggregation: when a group-runner child delivers its manifest\n // via `$agent.registerNode`, merge it into the local subtree registry and\n // immediately re-register the complete union with the hub.\n onChildRegistered: (params: RegisterNodeParams): void => {\n subtree.registerNode(params)\n triggerUpwardRegistration()\n },\n expectedClusterSecretHash: config.secret ? hashClusterSecret(config.secret) : undefined,\n })\n broker.createService(agentServiceSchema)\n\n // $process service — manages forked child processes (same as hub).\n // Pass the agent's own TCP listen port so spawned addon-runners connect\n // back here instead of falling back to port 6000 (the hub). Bug-4:\n // without this, runners called `$agent.registerNode` through the hub\n // which has no `$agent` service → retry storm (attempt 80+).\n const agentTcpPort = deriveAgentListenPort(broker.nodeID)\n\n // UDS local transport — the agent hosts a LocalChildRegistry so its\n // forked addon-runners route cap calls over a Unix-domain socket. The\n // broker stays as the no-route fallback. On failure the children fall\n // back to broker-only (no parentUdsPath propagated). See moleculer.service.ts.\n let agentParentUdsPath: string | undefined\n let agentUdsRegistry: LocalChildRegistry | undefined\n try {\n const agentNodeId = broker.nodeID\n // F0 (slice-5 outbound): route a forked child's unowned `ctx.api.<cap>`\n // call from the agent (the parent) instead of throwing UDS_NO_ROUTE. The\n // agent has NO CapRouteResolver — it routes everything to the hub over its\n // own broker — so `getResolver` returns null and the handler uses the\n // broker fallback exclusively. The agent's `broker.call` reaches the hub\n // mesh (and any cluster node) via the same service-discovery + action-name\n // convention the child's brokerTransportLink used before F0. F1+F2 removes\n // the child broker, so the agent must own this outbound path.\n const onUnownedCall = createParentUnownedCallHandler({\n getResolver: () => null,\n broker: broker as unknown as ServiceBroker,\n // The agent's subtree registry — lets the broker fallback pin a\n // device-scoped child call to the owning node instead of load-balancing.\n nodeRegistry: subtree,\n // Hub-local UDS child dispatcher — routes a device-scoped native cap\n // owned by an agent-local child directly over UDS before any broker\n // fallback. Getter: `agentUdsRegistry` is assigned later in this scope,\n // after the handler is constructed.\n getLocalDispatcher: () => agentUdsRegistry ?? null,\n logger: { warn: (msg) => consoleLogger.warn(`[uds-fallback] ${msg}`) },\n })\n agentUdsRegistry = new LocalChildRegistry({\n server: createLocalTransport().createServer(agentNodeId),\n onUnownedCall,\n })\n await agentUdsRegistry.start()\n // E1: apply child manifest + cleanup from the UDS lifecycle (agent-local children).\n // When a runner connects over UDS, synthesise a RegisterNodeParams for the\n // agent's subtree and trigger upward hub registration — same effect as the\n // `$agent.registerNode` Moleculer RPC path. Idempotent: if the Moleculer path\n // fires first, the subtree's atomic-replace handles the re-registration safely.\n agentUdsRegistry.onChildRegistered((child) => {\n const childNodeId = `${agentNodeId}/${child.childId}`\n const childParams = buildAgentChildUdsManifest(childNodeId, child.childId, child.caps)\n subtree.registerNode(childParams)\n triggerUpwardRegistration()\n consoleLogger.info(`UDS child registered — subtree updated: ${childNodeId}`)\n })\n agentUdsRegistry.onChildGone((childId) => {\n const childNodeId = `${agentNodeId}/${childId}`\n subtree.removeNode(childNodeId)\n triggerUpwardRegistration()\n consoleLogger.info(`UDS child gone — subtree updated: ${childNodeId}`)\n })\n // B2: forward UDS child logs onward to the hub's log-receiver service so\n // they appear in the hub LogManager / admin-UI log stream.\n //\n // The agent has NO local LogManager readable by the admin-UI — all log\n // entries must reach the hub. We re-use the same `log-receiver.ingest`\n // Moleculer call that `HubForwarderDestination` uses for the agent's OWN\n // logs, but preserve the child's original `addonId`/`nodeId`/`tags` so\n // the admin-UI shows the originating addon (not the agent's identity).\n //\n // If the hub is not yet reachable, the call silently fails — the same\n // best-effort semantic as `HubForwarderDestination.forward`. Phase F will\n // retire the broker path once every log source emits over UDS end-to-end.\n agentUdsRegistry.onChildLog((childId, entry) => {\n const workerEntry = udsChildLogToWorkerEntry(childId, entry)\n broker.call('log-receiver.ingest', workerEntry).catch(() => {\n // Hub unreachable or not yet discovered — silently drop.\n // HubForwarderDestination handles the agent's own buffered logs;\n // child UDS logs emitted before hub connection are not buffered here.\n })\n })\n agentParentUdsPath = localEndpointPath(agentNodeId)\n consoleLogger.info(`UDS child registry listening on ${agentParentUdsPath}`)\n } catch (err) {\n consoleLogger.warn(\n `UDS child registry failed to start; children stay broker-only: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n\n const processServiceSchema = createProcessService(\n broker.nodeID,\n config.dataDir,\n undefined,\n agentTcpPort,\n agentParentUdsPath,\n )\n broker.createService(processServiceSchema)\n\n // Slice-5, Task 7 — register the agent-side cap-dispatch service so the hub\n // can route `agent-child-forward` cap calls to this agent's UDS children.\n // Registered only when the UDS child registry started successfully; if it\n // didn't start the service would have nothing to forward to.\n //\n // Caps hosted in the agent's OWN main process — core addons such as\n // `platform-probe` and `metrics-provider` register singleton providers in\n // `capabilityRegistry`, NOT as forked UDS children — are exposed via this\n // in-process lookup so hub-dispatched `agent-child-forward` calls resolve to\n // them instead of failing with \"no provider\". Mirrors the hub's\n // `createInProcessProviderLookup`.\n const agentInProcessLookup: InProcessProviderLookup = (\n capName: string,\n ): InProcessProviderRef | null => {\n const provider = capabilityRegistry.getSingleton<Record<string, unknown>>(capName)\n if (provider === null || provider === undefined) return null\n return {\n invoke: (method: string, args: unknown): Promise<unknown> => {\n const fn = provider[method]\n if (typeof fn !== 'function') {\n return Promise.reject(new Error(`method \"${method}\" not found on cap \"${capName}\"`))\n }\n const result: unknown = fn.call(provider, args)\n return Promise.resolve(result)\n },\n }\n }\n registerAgentCapDispatch(broker, agentUdsRegistry, agentInProcessLookup, consoleLogger)\n\n // $addonHost — REMOVED (Sprint 6). Three-level settings are now\n // exposed per-addon via `settings.*` actions in createAddonService.\n\n // Register $event-bus BEFORE start so the service subscription\n // is announced during discovery. See addon-context-factory.ts for\n // the rationale — post-start registration propagates via heartbeat\n // and is unreliable for the first ~10s.\n registerEventBusService(broker as unknown as ServiceBroker)\n\n // Per-node `$hwaccel` — exposes this agent's local hwaccel probe to\n // the rest of the cluster. Hub UI calls\n // `broker.call('$hwaccel.resolve', {}, { nodeID: '<agent>' })` to\n // render the pipeline-page \"hardware acceleration\" column.\n broker.createService(createHwAccelService(createKernelHwAccel()))\n\n // Start broker BEFORE bootCoreAddons. Core infra addons (storage,\n // sqlite-settings, ...) run their own BaseAddon.initialize() which\n // calls `ctx.settings.readAddonStore()` on every addon; that call\n // routes through `brokerTransportLink` and would deadline-free poll\n // for the hub's `settings-store.get` service if the broker weren't\n // connected to the mesh yet. Starting the broker first lets service\n // discovery resolve (or time out cleanly via the read-blob fallback)\n // so agent boot completes instead of hanging on the first infra addon.\n await broker.start()\n\n // C2: wire the UDS ↔ Moleculer event bridge so events emitted by UDS\n // children fan to siblings and reach the cluster, and cluster / parent-\n // local events propagate to every UDS child. Inert when agentUdsRegistry\n // was not started (no UDS children). Wired after broker.start() so\n // getBrokerEventBus returns the fully operational shared bus.\n let udsEventBridgeDispose: (() => void) | null = null\n if (agentUdsRegistry !== undefined) {\n const agentBrokerEventBus = getBrokerEventBus(broker as unknown as ServiceBroker)\n udsEventBridgeDispose = createUdsEventBridge({\n registry: agentUdsRegistry,\n parentBus: agentBrokerEventBus,\n parentNodeId: broker.nodeID,\n })\n // D1: answer readiness-snapshot requests from UDS children over the\n // agent's own readiness registry view. The agent hydrates its registry\n // from the hub's `$readiness.getSnapshot` Moleculer action (broker\n // path, intact until Phase F) — its snapshot reflects the hub's\n // authoritative view plus any agent-local readiness events. Children\n // request the snapshot over UDS without an additional Moleculer hop.\n // `getOrInitReadinessRegistry` is the same function addon-context-factory\n // uses, so the shared per-broker instance is returned on the first call\n // and reused on subsequent calls — no separate registry is created.\n // Wired after broker.start() so the broker event bus is operational.\n const agentReadinessRegistry = getOrInitReadinessRegistry(\n broker as unknown as ServiceBroker,\n agentBrokerEventBus,\n consoleLogger,\n )\n agentUdsRegistry.onReadinessSnapshotRequest(() =>\n agentReadinessRegistry.getSnapshotForTransport(),\n )\n }\n\n // ── HTTP server (Fastify) — status API + process management + UI ──\n // Started early (before addon boot) so the status page is reachable\n // even while addons are still loading.\n const getBrokerFn = (() => broker) as unknown as () => ServiceBroker\n void startAgentHttpServer(getBrokerFn, {\n port: config.statusPort ?? 4444,\n nodeId: config.nodeId,\n dataDir: config.dataDir,\n configPath: config.configPath,\n onReconnect: reconnect,\n getHubConnectionState,\n })\n\n // ── Phase 1: Load core infrastructure addons (in-process) ──\n // storage + settings + metrics + hub-forwarder (log destination)\n await bootCoreAddons(broker, config, capabilityRegistry, loadedAddons, loggerFactory)\n\n // Plug every registered `log-destination` provider into the shared\n // LogManager. The LogManager replays its ring buffer to each destination\n // on registration, so boot-time log entries still reach destinations that\n // came up mid-boot (e.g. hub-forwarder arriving after the first logs).\n for (const dest of capabilityRegistry.getCollection<ILogDestination>('log-destination')) {\n agentLogManager.addDestination(dest)\n }\n\n // Everything downstream reads the resolved infra providers from the\n // capability registry — no hand-curated side-channel.\n const storageProvider = capabilityRegistry.getSingleton<IStorageProvider>('storage') ?? undefined\n\n // `ctx.api` for every addon is built inside `createAddonContext` from\n // `[localProviderLink, brokerTransportLink(broker)]`. Unresolved calls\n // route via Moleculer to the hub (or any other node hosting the cap).\n // No separate hub WSS client, no `CAMSTACK_HUB_API_URL` — all cross-node\n // traffic rides the broker mesh.\n\n // ── Phase 1.5: Load cluster-capable addon packages (forkable → child process) ──\n await loadClusterCapableAddons(broker, config, capabilityRegistry, loadedAddons)\n\n // ── Phase 2: Load deployed addons (from hub $agent.deploy) ──\n await loadDeployedAddons(\n broker,\n config.addonsDir,\n config.dataDir,\n loadedAddons,\n storageProvider,\n loggerFactory,\n capabilityRegistry,\n )\n\n // ── D3: Fire the initial upward hub registration carrying the agent's\n // complete in-process manifest (+ any children that already registered).\n // Group-runner children call `$agent.registerNode` and trigger a re-fire\n // via `onChildRegistered`; hub reconnects re-fire via `$node.connected`.\n triggerUpwardRegistration()\n\n // Fire `onHubReachable()` on every in-process addon as soon as the hub\n // node connects to our broker — this is the safe point for ctx.api.* calls\n // into hub-provided capabilities. Forked children get the same hook via\n // `process-runner.ts`.\n let hubReachableFired = false\n broker.localBus.on('$node.connected', (data: unknown) => {\n const node = (data as { node?: { id?: string } }).node\n if (node?.id !== 'hub') return\n // Hub connected — clear any stale secret-mismatch override so the\n // status endpoint reflects the live state again.\n if (hubConnectionStateOverride === 'secret-mismatch') {\n hubConnectionStateOverride = null\n }\n // Re-register with the hub on reconnect — the hub may have restarted and\n // lost the agent's manifest. D3 idempotent-replace: safe to re-fire.\n triggerUpwardRegistration()\n if (hubReachableFired) return\n hubReachableFired = true\n for (const [, entry] of loadedAddons) {\n if (!entry.addon || typeof entry.addon.onHubReachable !== 'function') continue\n Promise.resolve(entry.addon.onHubReachable()).catch((err: unknown) => {\n console.error(`[Agent] ${entry.id} onHubReachable() threw:`, err)\n })\n }\n })\n\n console.log(\n `[Agent] Node \"${config.nodeId}\" (name=\"${config.name}\") ready — ${loadedAddons.size} addon(s) loaded`,\n )\n\n // Graceful shutdown\n const shutdown = async () => {\n console.log('[Agent] Shutting down...')\n // Abort any in-flight upward hub registration retry loop.\n registerAbortController.abort()\n // Dispose the UDS event bridge to remove the parent-bus subscriber and\n // clear the child-event handler, preventing subscriber leaks on shutdown.\n udsEventBridgeDispose?.()\n udsEventBridgeDispose = null\n for (const [, entry] of loadedAddons) {\n if (entry.addon?.shutdown) {\n try {\n await entry.addon.shutdown()\n } catch {\n /* */\n }\n }\n }\n await broker.stop()\n process.exit(0)\n }\n process.on('SIGTERM', shutdown)\n process.on('SIGINT', shutdown)\n}\n\n// ---------------------------------------------------------------------------\n// Phase 1: Boot core addons (storage, settings, metrics — NO winston)\n// ---------------------------------------------------------------------------\n\n// Core infra addons to load on agent — all infra including log-destination (hub-forwarder)\nconst AGENT_INFRA = INFRA_CAPABILITIES\n\nasync function bootCoreAddons(\n broker: BrokerLike,\n config: { dataDir: string; addonsDir: string },\n registry: CapabilityRegistry,\n loadedAddons: Map<string, LoadedAddonEntry>,\n loggerFactory: (addonId: string) => IScopedLogger,\n): Promise<void> {\n // Scan every installed addon package — infra providers may live outside\n // `@camstack/system` (e.g. `@camstack/addon-platform-probe-native`).\n const packageDirs = resolveAddonPackageDirs(config.addonsDir)\n if (packageDirs.length === 0) {\n console.warn('[Agent] No addon packages found — running without infrastructure addons')\n return\n }\n\n console.log(`[Agent] Scanning ${packageDirs.length} addon package(s) for infra providers`)\n const loader = new AddonLoader()\n for (const dir of packageDirs) {\n try {\n await loader.loadFromAddonDir(dir)\n } catch (err) {\n console.warn(`[Agent] Failed to scan ${dir}: ${errMsg(err)}`)\n }\n }\n\n for (const infra of AGENT_INFRA) {\n const candidates = loader.listAddons().filter((a) =>\n a.declaration.capabilities?.some((c) => {\n const capName = typeof c === 'string' ? c : c.name\n return capName === infra.name\n }),\n )\n // For log-destination, prefer hub-forwarder over winston-logging\n const addon =\n infra.name === 'log-destination'\n ? (candidates.find((a) => a.declaration.id === 'hub-forwarder') ?? candidates[0])\n : candidates[0]\n if (!addon) {\n if (infra.required) {\n console.error(`[Agent] Required infrastructure addon for \"${infra.name}\" not found`)\n }\n continue\n }\n\n const addonId = addon.declaration.id\n try {\n const instance = new addon.addonClass()\n // Seed ctx.kernel.storage from whatever storage provider is already\n // in the registry (the storage addon declares itself before the\n // addons that depend on it per AGENT_INFRA order).\n const storageProvider = registry.getSingleton<IStorageProvider>('storage') ?? undefined\n const context = await createAddonContext(\n broker as unknown as ServiceBroker,\n addon.declaration,\n config.dataDir,\n {\n storageProvider,\n addonConfig: { rootPath: config.dataDir },\n createLogger: loggerFactory,\n capabilityRegistry: registry,\n // Feed the storage-orchestrator its first-boot seed declarations\n // (addons-data:default, models:default, …). Without this the agent's\n // sqlite-settings aborts boot: \"No default storage location\n // configured for type addons-data\" → fatal crash-loop.\n listStorageLocationDeclarations: () => loader.listStorageLocationDeclarations(),\n },\n )\n\n const initResult = normalizeAddonInitResult(await instance.initialize(context))\n\n for (const reg of initResult?.providers ?? []) {\n const capName = reg.capability.name\n registry.registerProvider(capName, addonId, reg.provider)\n // Also register in the per-broker context registry\n context.registerProvider(capName, reg.provider)\n }\n\n loadedAddons.set(addonId, {\n id: addonId,\n status: 'running',\n version: addon.declaration.version,\n packageName: addon.packageName,\n packageVersion: addon.packageVersion,\n addon: instance,\n })\n\n console.log(`[Agent] Core addon \"${addonId}\" initialized`)\n } catch (err) {\n const msg = errMsg(err)\n console.error(`[Agent] Failed to initialize core addon \"${addonId}\": ${msg}`)\n if (infra.required) {\n throw new Error(`Required infrastructure addon \"${addonId}\" failed: ${msg}`, { cause: err })\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Phase 1.5: Load cluster-capable addon packages\n// ---------------------------------------------------------------------------\n\nasync function loadClusterCapableAddons(\n broker: BrokerLike,\n config: { dataDir: string; addonsDir: string },\n capabilityRegistry: CapabilityRegistry,\n loadedAddons: Map<string, LoadedAddonEntry>,\n): Promise<void> {\n const addonPackageDirs = resolveAddonPackageDirs(config.addonsDir)\n if (addonPackageDirs.length === 0) return\n\n // ── Phase 0 (cross-package): collect every group-eligible addon\n // across ALL package dirs FIRST so we issue exactly one\n // `$process.spawnGroup` per group. Doing this inside the per-dir\n // loop spawned the same group multiple times — Moleculer rejected\n // subsequent attempts with \"already running\" and the surviving\n // subprocess only had the first dir's subset of addons.\n const allGroupCandidates: GroupCandidate[] = []\n // Loaders are reused below for the per-addon legacy path; cache them\n // so each dir is parsed once.\n const dirToLoader = new Map<string, AddonLoader>()\n\n for (const dir of addonPackageDirs) {\n const loader = new AddonLoader()\n try {\n await loader.loadFromAddonDir(dir)\n } catch (err) {\n console.warn(`[Agent] Skipping ${dir}: ${errMsg(err)}`)\n continue\n }\n dirToLoader.set(dir, loader)\n\n for (const registered of loader.listAddons()) {\n if (loadedAddons.has(registered.declaration.id)) continue\n if (registered.declaration.execution === undefined) continue\n const placement = resolveAddonPlacement(registered.declaration)\n if (placement === 'hub-only') continue\n allGroupCandidates.push({\n groupId: resolveRunnerId(registered.declaration, registered.declaration.id),\n addonId: registered.declaration.id,\n addonDir: dir,\n version: registered.declaration.version ?? '0.0.0',\n packageName: registered.packageName,\n packageVersion: registered.packageVersion,\n capabilities: registered.declaration.capabilities ?? [],\n })\n }\n }\n\n if (allGroupCandidates.length > 0) {\n const grouped = new Map<string, GroupCandidate[]>()\n for (const c of allGroupCandidates) {\n const arr = grouped.get(c.groupId) ?? []\n arr.push(c)\n grouped.set(c.groupId, arr)\n }\n for (const [groupId, addons] of grouped) {\n try {\n await broker.call('$process.spawnRunner', {\n runnerId: groupId,\n addons: addons.map((a) => ({ addonId: a.addonId, addonDir: a.addonDir })),\n })\n // Shared with the reload path (`ensureGroupRunner`) so the boot and\n // reload registrations can never drift — the drift was the in-process\n // reload-wedge's root cause.\n registerGroupRunnerProviders({ broker, capabilityRegistry, loadedAddons }, addons)\n console.log(\n `[Agent] Group \"${groupId}\" spawned with ${addons.length} addon(s): ${addons.map((a) => a.addonId).join(', ')}`,\n )\n } catch (err) {\n const msg = err instanceof Error ? (err.stack ?? err.message) : String(err)\n console.error(`[Agent] Failed to spawn group \"${groupId}\": ${msg}`)\n for (const a of addons) {\n loadedAddons.set(a.addonId, { id: a.addonId, status: 'error' })\n }\n }\n }\n }\n\n // Diagnostic — list addons that exist in the agent's package dir but\n // were filtered out by Phase 0 (placement=hub-only or no execution).\n for (const dir of addonPackageDirs) {\n const loader = dirToLoader.get(dir)\n if (!loader) continue\n for (const registered of loader.listAddons()) {\n const addonId = registered.declaration.id\n if (loadedAddons.has(addonId)) continue\n if (!isDeployableToAgent(registered.declaration)) continue\n console.warn(\n `[Agent] Addon \"${addonId}\" is deployable but missing from any spawned group — verify package.json execution field`,\n )\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Phase 2: Load deployed addons (pushed from hub via $agent.deploy)\n// ---------------------------------------------------------------------------\n\nasync function loadDeployedAddons(\n broker: BrokerLike,\n addonsDir: string,\n dataDir: string,\n loadedAddons: Map<string, LoadedAddonEntry>,\n storageProvider: IStorageProvider | undefined,\n loggerFactory: (addonId: string) => IScopedLogger,\n capabilityRegistry?: CapabilityRegistry,\n): Promise<void> {\n if (!fs.existsSync(addonsDir)) return\n\n // Discover deployable addons by reading each package's MANIFEST\n // (package.json `camstack.addons[]`) — NOT by importing the entry module.\n // Importing heavy native addon entries (node-av / onnxruntime bridge / …) on\n // the agent MAIN thread is what wedged `$agent.reload` and dropped the node\n // from topology. A group-runner addon only needs its declaration to be\n // (re)dispatched to a subprocess, which loads the real module itself.\n const packageDirs = resolveAddonPackageDirs(addonsDir)\n const groupCandidates: GroupCandidate[] = []\n // Dirs holding a deployable NON-group addon — and ONLY these — need the\n // importing loader. None exist under the default `hub-only` placement\n // (deployable ⟹ cluster-capable), so the heavy import path is never taken.\n const inProcessDirs = new Set<string>()\n\n for (const dir of packageDirs) {\n const manifest = readPackageManifestAddons(dir)\n if (!manifest) continue\n for (const decl of manifest.declarations) {\n const addonId = decl.id\n if (loadedAddons.has(addonId)) continue\n if (!isDeployableToAgent(decl)) continue\n\n if (isGroupRunnerAddon(decl)) {\n groupCandidates.push({\n groupId: resolveRunnerId(decl, addonId),\n addonId,\n addonDir: dir,\n version: decl.version ?? '0.0.0',\n packageName: manifest.packageName,\n packageVersion: manifest.packageVersion,\n capabilities: decl.capabilities ?? [],\n })\n } else {\n inProcessDirs.add(dir)\n }\n }\n }\n\n // Rare in-process fallback (imports lazily, per-dir, only when such an addon\n // actually exists — never under the current placement model).\n if (inProcessDirs.size > 0) {\n await loadInProcessDeployedAddons(\n broker,\n [...inProcessDirs],\n dataDir,\n loadedAddons,\n storageProvider,\n loggerFactory,\n capabilityRegistry,\n )\n }\n\n // (Re)spawn each cluster-capable group runner with the current on-disk code —\n // no module import on the main thread.\n if (groupCandidates.length === 0) return\n if (!capabilityRegistry) {\n console.warn(\n `[Agent] ${groupCandidates.length} deployed group addon(s) skipped — no capabilityRegistry passed`,\n )\n return\n }\n const grouped = new Map<string, GroupCandidate[]>()\n for (const c of groupCandidates) {\n const arr = grouped.get(c.groupId) ?? []\n arr.push(c)\n grouped.set(c.groupId, arr)\n }\n const deployLogger = loggerFactory('agent-group-runner')\n for (const [groupId, addons] of grouped) {\n await ensureGroupRunner(\n { broker, capabilityRegistry, loadedAddons, logger: deployLogger },\n groupId,\n addons,\n )\n }\n}\n\n/**\n * In-process loader path for deployable NON-group addons (rare). Uses the\n * importing `AddonLoader` — invoked lazily, per-dir, ONLY when such an addon\n * exists. Group-runner addons never reach here, so heavy native modules are\n * never imported on the agent main thread during a reload.\n */\nasync function loadInProcessDeployedAddons(\n broker: BrokerLike,\n dirs: readonly string[],\n dataDir: string,\n loadedAddons: Map<string, LoadedAddonEntry>,\n storageProvider: IStorageProvider | undefined,\n loggerFactory: (addonId: string) => IScopedLogger,\n capabilityRegistry?: CapabilityRegistry,\n): Promise<void> {\n const modelsDir = storageProvider\n ? await storageProvider.resolve({ location: 'models', relativePath: '' }).catch(() => undefined)\n : undefined\n const contextOptions: AddonContextOptions = {\n storageProvider,\n addonConfig: modelsDir ? { modelsDir } : {},\n createLogger: loggerFactory,\n capabilityRegistry,\n }\n\n for (const dir of dirs) {\n const loader = new AddonLoader()\n try {\n await loader.loadFromAddonDir(dir)\n } catch (err) {\n console.warn(`[Agent] Skipping ${dir}: ${errMsg(err)}`)\n continue\n }\n for (const registered of loader.listAddons()) {\n const addonId = registered.declaration.id\n if (loadedAddons.has(addonId)) continue\n if (!isDeployableToAgent(registered.declaration)) continue\n // Group-runner addons are dispatched to subprocesses by the caller.\n if (isGroupRunnerAddon(registered.declaration)) continue\n\n try {\n const instance = new registered.addonClass()\n const context = await createAddonContext(\n broker as unknown as ServiceBroker,\n registered.declaration,\n dataDir,\n contextOptions,\n )\n await instance.initialize(context)\n\n const serviceSchema = createAddonService(instance, registered.declaration)\n broker.createService(serviceSchema)\n\n loadedAddons.set(addonId, {\n id: addonId,\n status: 'running',\n version: registered.declaration.version,\n packageName: registered.packageName,\n packageVersion: registered.packageVersion,\n addon: instance,\n })\n\n console.log(`[Agent] Deployed addon \"${addonId}\" loaded in-process`)\n } catch (err) {\n const msg = errMsg(err)\n console.error(`[Agent] Failed to load deployed addon \"${addonId}\": ${msg}`)\n loadedAddons.set(addonId, { id: addonId, status: 'error' })\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Read the agent's own package.json version (best-effort). */\nfunction readAgentVersion(): string {\n // package.json sits two dirs up from `dist/`. Walk up until we hit it.\n const candidates = [\n path.resolve(__dirname, '..', 'package.json'),\n path.resolve(__dirname, '..', '..', 'package.json'),\n ]\n for (const candidate of candidates) {\n try {\n if (!fs.existsSync(candidate)) continue\n const raw = JSON.parse(fs.readFileSync(candidate, 'utf-8')) as {\n name?: string\n version?: string\n }\n if (raw.name === '@camstack/agent' && typeof raw.version === 'string') return raw.version\n } catch {\n /* keep searching */\n }\n }\n return 'unknown'\n}\n\n/** Check if path is a directory (follows symlinks) */\nfunction isDir(p: string): boolean {\n try {\n return fs.statSync(p).isDirectory()\n } catch {\n return false\n }\n}\n\n/** Scan addonsDir for addon packages (scoped and unscoped, follows symlinks) */\nfunction resolveAddonPackageDirs(addonsDir: string): string[] {\n const dirs: string[] = []\n if (!fs.existsSync(addonsDir)) return dirs\n\n for (const name of fs.readdirSync(addonsDir)) {\n const full = path.join(addonsDir, name)\n if (name.startsWith('@') && isDir(full)) {\n // Scoped packages: @camstack/addon-xyz\n for (const sub of fs.readdirSync(full)) {\n const subFull = path.join(full, sub)\n if (isDir(subFull) && fs.existsSync(path.join(subFull, 'package.json'))) {\n dirs.push(subFull)\n }\n }\n } else if (isDir(full) && fs.existsSync(path.join(full, 'package.json'))) {\n dirs.push(full)\n }\n }\n\n return dirs\n}\n\n// ---------------------------------------------------------------------------\n// E1 helper — agent-local UDS child manifest adaptation\n// ---------------------------------------------------------------------------\n\n/**\n * Adapt a child's UDS `ChildCapDescriptor[]` (no addonId) into a\n * `RegisterNodeParams` for the agent's subtree HubNodeRegistry.\n * Uses `childId` as the synthetic `addonId` — matches the Moleculer path\n * where `runnerId = addonId` for single-addon runners (the current default).\n * Only system (non-device-scoped) caps go into `addons`; device-scoped native\n * caps arrive on a later re-handshake via the Moleculer path.\n *\n * TODO(co-location): This function synthesises ONE manifest entry with `addonId = childId`\n * (the runner id). This is correct under the current one-addon-one-process invariant\n * where `childId = runnerId = addonId`. If `execution.group` co-location is ever\n * activated (multiple addons sharing one runner), a single runner would host multiple\n * addonIds but this function would register all their caps under one synthetic addonId —\n * collapsing distinct provider registryKeys into one and breaking per-addon routing.\n * Multi-addon manifest support (splitting the `ChildCapDescriptor[]` by addonId once the\n * protocol carries addonId) would be needed here before enabling co-location post-Phase-F.\n */\nfunction buildAgentChildUdsManifest(\n nodeId: string,\n childId: string,\n caps: readonly ChildCapDescriptor[],\n): RegisterNodeParams {\n const systemCapNames = new Set<string>()\n for (const cap of caps) {\n if (cap.deviceId === undefined) {\n systemCapNames.add(cap.capName)\n }\n }\n const addons: readonly RegisteredAddonManifest[] = [\n { addonId: childId, capabilities: [...systemCapNames] },\n ]\n return { nodeId, addons }\n}\n\n// Run if executed directly\nconst scriptName = process.argv[1] ?? ''\nif (scriptName.endsWith('agent-bootstrap.js') || scriptName.endsWith('agent-bootstrap.ts')) {\n startAgent().catch((err: unknown) => {\n console.error('[Agent] Fatal error:', err)\n process.exit(1)\n })\n}\n\nexport { startAgent }\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { resolveAddonPlacement } from '@camstack/types'\nimport type { AddonDeclaration, IScopedLogger } from '@camstack/types'\nimport type { LoadedAddonEntry } from './agent-service.js'\n\n/** A package's addon declarations read straight from package.json (NO module import). */\nexport interface PackageManifestAddons {\n readonly packageName: string\n readonly packageVersion: string\n readonly declarations: readonly AddonDeclaration[]\n}\n\n/**\n * Read a package's `camstack.addons[]` declarations directly from its\n * package.json — WITHOUT importing the addon entry module.\n *\n * The reload path must NOT `import()` heavy native addon entries (node-av, the\n * onnxruntime bridge, …) on the agent's MAIN thread: doing so blocks the event\n * loop long enough that `$agent.status` stops answering and the hub drops the\n * node from topology — and a hung import wedges `$agent.reload` outright (the\n * reload-wedge). A group-runner addon only needs its declaration (id,\n * capabilities, execution) to be (re)dispatched to a runner SUBPROCESS, which\n * loads the real module itself. Returns null on a missing/corrupt manifest.\n */\nexport function readPackageManifestAddons(dir: string): PackageManifestAddons | null {\n try {\n const pkgPath = path.join(dir, 'package.json')\n if (!fs.existsSync(pkgPath)) return null\n // Documented JSON boundary: the package.json shape is validated field-by-field below.\n const raw = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as {\n name?: unknown\n version?: unknown\n camstack?: { addons?: unknown }\n }\n const packageName = typeof raw.name === 'string' ? raw.name : ''\n const packageVersion = typeof raw.version === 'string' ? raw.version : '0.0.0'\n const entries = Array.isArray(raw.camstack?.addons) ? raw.camstack.addons : []\n const declarations: AddonDeclaration[] = []\n for (const entry of entries) {\n if (\n entry !== null &&\n typeof entry === 'object' &&\n typeof (entry as { id?: unknown }).id === 'string'\n ) {\n // Boundary cast: validated to have a string `id`; the runtime helpers\n // (isDeployableToAgent / resolveRunnerId) read only `execution` / `id`.\n declarations.push(entry as AddonDeclaration)\n }\n }\n if (!packageName || declarations.length === 0) return null\n return { packageName, packageVersion, declarations }\n } catch {\n return null\n }\n}\n\n/**\n * A single addon to be hosted by a forked group runner on the agent.\n *\n * `groupId` is the runner id (`resolveRunnerId` — `execution.group ?? addonId`);\n * addons sharing a `groupId` collapse into one runner subprocess.\n */\nexport interface GroupCandidate {\n readonly groupId: string\n readonly addonId: string\n readonly addonDir: string\n readonly version: string\n readonly packageName: string\n readonly packageVersion: string\n readonly capabilities: ReadonlyArray<string | { readonly name: string }>\n}\n\n/** Minimal broker surface (`call`) used by the group-runner helpers. */\ninterface BrokerCall {\n call<T = unknown>(action: string, params?: unknown, opts?: unknown): Promise<T>\n}\n\n/** Minimal capability-registry surface used by the group-runner helpers. */\ninterface ProviderRegistrar {\n registerProvider(capabilityName: string, addonId: string, provider: unknown): void\n}\n\n/** Deps for the provider-registration step (no logging needed). */\nexport interface ProviderDeps {\n readonly broker: BrokerCall\n readonly capabilityRegistry: ProviderRegistrar\n readonly loadedAddons: Map<string, LoadedAddonEntry>\n}\n\n/** Deps for {@link ensureGroupRunner} — adds a logger for the restart/spawn path. */\nexport interface GroupRunnerDeps extends ProviderDeps {\n readonly logger: IScopedLogger\n}\n\n/** Shape of the `$process.restart` action result we branch on. */\ninterface RestartResult {\n readonly success: boolean\n readonly reason?: string\n}\n\n/**\n * Whether a deployed addon must run in a forked group runner (one addon = one\n * process) rather than in-process. True when the addon declares an `execution`\n * block with a placement reachable on an agent (`any-node`/`agent-only`).\n *\n * The default placement is `hub-only`, so a declaration without `execution`\n * is hub-only and not a group-runner addon — and, being hub-only, is never\n * deployable to an agent in the first place.\n */\nexport function isGroupRunnerAddon(decl: Pick<AddonDeclaration, 'execution'>): boolean {\n return decl.execution !== undefined && resolveAddonPlacement(decl) !== 'hub-only'\n}\n\n/**\n * Register the broker-call cap proxies + `loadedAddons` bookkeeping for a group\n * of addons whose runner is (now) live. Extracted so the boot path\n * (`loadClusterCapableAddons`) and the reload path (`ensureGroupRunner`) share\n * ONE implementation — preventing the two from drifting (the original cause of\n * the in-process-reload wedge).\n */\nexport function registerGroupRunnerProviders(\n deps: ProviderDeps,\n addons: readonly GroupCandidate[],\n): void {\n for (const a of addons) {\n for (const cap of a.capabilities) {\n const capName = typeof cap === 'string' ? cap : cap.name\n const proxy = new Proxy<Record<string, unknown>>(\n {},\n {\n get(_target, prop: string) {\n if (prop === 'then' || typeof prop === 'symbol') return undefined\n return (params: unknown) => deps.broker.call(`${a.addonId}.${capName}.${prop}`, params)\n },\n },\n )\n deps.capabilityRegistry.registerProvider(capName, a.addonId, proxy)\n }\n deps.loadedAddons.set(a.addonId, {\n id: a.addonId,\n status: 'running',\n version: a.version,\n packageName: a.packageName,\n packageVersion: a.packageVersion,\n })\n }\n}\n\n/**\n * Ensure a group runner is live with the CURRENT on-disk addon code, then wire\n * its providers.\n *\n * A redeploy's runner is already running (the deploy step swapped its on-disk\n * code but never killed the process), and `$process.spawnRunner` throws\n * \"already running\" for a live runnerId. So restart first — `$process.restart`\n * stops the old child, evicts it from the Moleculer registry, and re-spawns it\n * from the same dir (now holding the new code) in a subprocess, off the agent's\n * main event loop. Only when no runner exists yet (first deploy →\n * `{success:false, reason:'not found'}`) do we `$process.spawnRunner`.\n *\n * On a hard failure of both paths the addons are marked `error` (no providers\n * registered) so `$agent.status` reports the degraded state.\n */\nexport async function ensureGroupRunner(\n deps: GroupRunnerDeps,\n groupId: string,\n addons: readonly GroupCandidate[],\n): Promise<void> {\n try {\n const restart = await deps.broker.call<RestartResult>('$process.restart', { name: groupId })\n if (!restart.success) {\n if (restart.reason !== undefined && restart.reason !== 'not found') {\n deps.logger.warn(\n `group \"${groupId}\" restart returned \"${restart.reason}\" — spawning a fresh runner`,\n )\n }\n await deps.broker.call('$process.spawnRunner', {\n runnerId: groupId,\n addons: addons.map((a) => ({ addonId: a.addonId, addonDir: a.addonDir })),\n })\n }\n registerGroupRunnerProviders(deps, addons)\n deps.logger.info(\n `group \"${groupId}\" live with ${addons.length} addon(s): ${addons.map((a) => a.addonId).join(', ')}`,\n )\n } catch (err) {\n const msg = err instanceof Error ? (err.stack ?? err.message) : String(err)\n deps.logger.error(`failed to ensure group \"${groupId}\": ${msg}`)\n for (const a of addons) {\n deps.loadedAddons.set(a.addonId, { id: a.addonId, status: 'error' })\n }\n }\n}\n","/**\n * Slice-5, Task 6 — agent-side Moleculer service for forwarding hub-dispatched\n * cap calls to the agent's forked UDS children.\n *\n * The hub's `CapRouteResolver` dispatches an `agent-child-forward` route by\n * calling `callWithServiceDiscovery(broker, AGENT_CAP_FWD_SERVICE,\n * AGENT_CAP_FWD_ACTION, params, { nodeID: agentNodeId })`. This service\n * receives those calls, resolves the child locally via the agent's\n * `LocalChildRegistry`, and forwards the call over UDS.\n */\n\nimport type { ServiceSchema, Context } from 'moleculer'\nimport type {\n HubLocalChildDispatcher,\n AgentCapForwardParams,\n CapCallInput,\n InProcessProviderLookup,\n LocalChildRegistryLogger,\n} from '@camstack/system'\nimport { AGENT_CAP_FWD_SERVICE } from '@camstack/system'\n\n// ---------------------------------------------------------------------------\n// ctx.params narrowing helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Moleculer types `ctx.params` loosely; narrow it to `AgentCapForwardParams`\n * by checking the required string fields without unsafe casts.\n */\nfunction narrowParams(raw: unknown): AgentCapForwardParams {\n if (raw === null || typeof raw !== 'object') {\n throw new Error(\n '$agent-cap-fwd.forward: invalid params — capName and method are required strings',\n )\n }\n const capName: unknown = Reflect.get(raw, 'capName')\n const method: unknown = Reflect.get(raw, 'method')\n if (typeof capName !== 'string' || typeof method !== 'string') {\n throw new Error(\n '$agent-cap-fwd.forward: invalid params — capName and method are required strings',\n )\n }\n const childId: unknown = Reflect.get(raw, 'childId')\n const deviceId: unknown = Reflect.get(raw, 'deviceId')\n return {\n capName,\n method,\n args: Reflect.get(raw, 'args'),\n childId: typeof childId === 'string' ? childId : undefined,\n deviceId: typeof deviceId === 'number' ? deviceId : undefined,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates the Moleculer `ServiceSchema` for the agent-side cap-dispatch service.\n *\n * @param agentNodeId The Moleculer nodeId of this agent (used in error messages).\n * @param agentUdsRegistry The agent's `LocalChildRegistry` (or a compatible spy in tests).\n * `LocalChildRegistry` structurally satisfies `HubLocalChildDispatcher`.\n * @param inProcessLookup Optional resolver for caps hosted in the agent's OWN main process\n * (core addons such as `platform-probe`, `metrics-provider` register\n * their singleton providers in the agent's `CapabilityRegistry`, NOT\n * as forked UDS children). Consulted ONLY when no UDS child provides\n * the cap. Without it, agent in-process core caps are unroutable from\n * the hub.\n * @param logger Optional scoped logger; logs the no-provider path at INFO level.\n */\nfunction createAgentCapDispatchService(\n agentNodeId: string,\n agentUdsRegistry: HubLocalChildDispatcher,\n inProcessLookup?: InProcessProviderLookup,\n logger?: LocalChildRegistryLogger,\n): ServiceSchema {\n return {\n name: AGENT_CAP_FWD_SERVICE,\n actions: {\n forward: {\n handler: async (ctx: Context): Promise<unknown> => {\n const params = narrowParams(ctx.params)\n const { capName, method, args, deviceId } = params\n\n // Resolve the child: prefer the explicitly-provided childId (hub may\n // know it from its HubNodeRegistry), otherwise resolve locally.\n const childId: string | null | undefined =\n params.childId !== undefined\n ? params.childId\n : agentUdsRegistry.resolveChildId(capName, deviceId)\n\n if (childId == null) {\n // No forked UDS child owns this cap. It may instead be hosted by a\n // core addon running in the agent's OWN main process (platform-probe,\n // metrics-provider, …) — resolve it against the in-process registry.\n const ref = inProcessLookup?.(capName) ?? null\n if (ref !== null) {\n return ref.invoke(method, args)\n }\n logger?.info(\n `agent ${agentNodeId}: no provider for cap \"${capName}\"${deviceId !== undefined ? ` (deviceId ${deviceId})` : ''}`,\n )\n throw new Error(\n `agent ${agentNodeId} has no provider for cap \"${capName}\"${deviceId !== undefined ? ` (deviceId ${deviceId})` : ''}`,\n )\n }\n\n const input: CapCallInput = {\n capName,\n method,\n args,\n ...(deviceId !== undefined ? { deviceId } : {}),\n }\n\n return agentUdsRegistry.callCapOnChild(childId, input)\n },\n },\n },\n }\n}\n\nexport { createAgentCapDispatchService }\n","/**\n * Extracted, testable helper for registering the agent-side cap-dispatch\n * service with a Moleculer broker.\n *\n * The helper is separated from `agent-bootstrap.ts` so the wiring decision\n * (undefined → no-op; defined → create service) can be exercised in a unit\n * test independently of the full bootstrap lifecycle.\n */\n\nimport type { ServiceSchema, Service } from 'moleculer'\nimport type {\n HubLocalChildDispatcher,\n InProcessProviderLookup,\n LocalChildRegistryLogger,\n} from '@camstack/system'\nimport { createAgentCapDispatchService } from './agent-cap-dispatch-service.js'\n\n// ---------------------------------------------------------------------------\n// AgentServiceRegistrar — narrow interface satisfied by real ServiceBroker\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal surface of `ServiceBroker` required to register the cap-dispatch\n * service. Keeping a narrow interface here avoids coupling the helper to the\n * full broker type (which chains through eventemitter2 without exported types)\n * and prevents `as unknown as ServiceBroker` casts at every call-site.\n *\n * The real `ServiceBroker` structurally satisfies this interface:\n * `ServiceBroker.nodeID: string` ✓\n * `ServiceBroker.createService(schema: ServiceSchema, ...): Service` ✓\n */\nexport interface AgentServiceRegistrar {\n readonly nodeID: string\n createService(schema: ServiceSchema, schemaMods?: Partial<ServiceSchema>): Service\n}\n\n// ---------------------------------------------------------------------------\n// Exported helper\n// ---------------------------------------------------------------------------\n\n/**\n * Registers the `$agent-cap-fwd` Moleculer service with `registrar` when\n * `agentUdsRegistry` is defined; otherwise this is a no-op.\n *\n * Called from `agent-bootstrap.ts` after the UDS child registry starts\n * successfully so the hub can route `agent-child-forward` cap calls to this\n * agent's forked children.\n *\n * @param registrar Moleculer broker (or compatible double in tests).\n * @param agentUdsRegistry The agent's `LocalChildRegistry`; pass `undefined`\n * to skip registration (UDS failed to start).\n * @param inProcessLookup Optional resolver for caps hosted in the agent's own\n * main process (core addons); forwarded to the service\n * so hub-dispatched calls to those caps resolve instead\n * of failing with \"no provider\".\n * @param logger Optional scoped logger forwarded to the service.\n */\nexport function registerAgentCapDispatch(\n registrar: AgentServiceRegistrar,\n agentUdsRegistry: HubLocalChildDispatcher | undefined,\n inProcessLookup?: InProcessProviderLookup,\n logger?: LocalChildRegistryLogger,\n): void {\n if (agentUdsRegistry === undefined) return\n registrar.createService(\n createAgentCapDispatchService(registrar.nodeID, agentUdsRegistry, inProcessLookup, logger),\n )\n}\n"],"mappings":"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","names":["os","fs","path","fs","path","randomBytes","resolve","cpus","fs","path","fs","path","resolveAddonPlacement","fs","path","readConfigFile","resolveAddonPlacement"]}
|