@camstack/types 0.1.22 → 0.1.24
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/{auth-records-Q5ocndX5.js → auth-records-U1Xok3iP.js} +2 -4
- package/dist/{auth-records-Q5ocndX5.js.map → auth-records-U1Xok3iP.js.map} +1 -1
- package/dist/{auth-records-COv3plzv.mjs → auth-records-wKlyGWdW.mjs} +2 -4
- package/dist/{auth-records-COv3plzv.mjs.map → auth-records-wKlyGWdW.mjs.map} +1 -1
- package/dist/capabilities/mesh-orchestrator.cap.d.ts +0 -12
- package/dist/capabilities/mesh-orchestrator.cap.d.ts.map +1 -1
- package/dist/generated/addon-api.d.ts +0 -6
- package/dist/generated/addon-api.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/node.js +1 -1
- package/dist/node.mjs +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/disposer-chain.ts","../src/interfaces/addon.ts","../src/enums/event-category.ts","../src/interfaces/event-bus.ts","../src/addon/base-addon.ts","../src/readiness/readiness-registry.ts","../src/interfaces/storage.ts","../src/interfaces/device-capabilities/camera.ts","../src/interfaces/feature-flags.ts","../src/interfaces/server-analysis.ts","../src/interfaces/analysis-persistence.ts","../src/enums/event-source-type.ts","../src/constants.ts","../src/utils/json-safe.ts","../src/utils/hf-url.ts","../src/utils/cosine-similarity.ts","../src/utils/element-config-store.ts","../src/utils/runtime-mapping.ts","../src/utils/run-inference-step.ts","../src/catalogs/coco-classmap.ts","../src/types/device-type.ts","../src/device/device-config.ts","../src/device/device-runtime-state.ts","../src/device/runtime-state-helpers.ts","../src/generated/device-local-state.ts","../src/device/base-device.ts","../src/device/base-device-provider.ts","../src/device/zod-to-config-ui.ts","../src/device/device-state-handle.ts","../src/generated/device-proxy.ts","../src/utils/zone-rule-eval.ts","../src/generated/system-proxy.ts","../src/device/system-mirror.ts","../src/capabilities/custom-actions.ts","../src/generated/capability-router-map.ts","../src/generated/cap-status-types.ts","../src/utils/mask-url.ts","../src/utils/ring-buffer.ts","../src/helpers/bind-addon-actions.ts"],"sourcesContent":["// =============================================================================\n// DisposerChain — minimal LIFO cleanup registry used to back\n// `AddonContext.addDisposer`.\n//\n// Owned by whoever constructs an AddonContext (the addon host on the\n// hub, the worker host in forks). At shutdown / restart the host calls\n// `dispose()` once; this drains the chain in LIFO order so resources\n// registered later (which may depend on earlier ones) tear down first.\n//\n// Errors thrown by individual disposers are logged via the optional\n// onError callback but never abort the rest of the chain — partial\n// cleanup is always better than half-stopped state on the next restart.\n// =============================================================================\n\nexport type DisposerFn = () => void | Promise<void>\n\nexport interface DisposerChainOptions {\n /** Logged when an individual disposer throws. Defaults to console.error. */\n onError?: (err: unknown, index: number) => void\n}\n\nexport class DisposerChain {\n private disposers: DisposerFn[] = []\n private disposed = false\n private readonly onError: (err: unknown, index: number) => void\n\n constructor(opts: DisposerChainOptions = {}) {\n this.onError =\n opts.onError ??\n ((err, index) => {\n // eslint-disable-next-line no-console\n console.error(`[DisposerChain] disposer #${index} threw`, err)\n })\n }\n\n /**\n * Register a teardown callback. Returns an unregister function so\n * callers can drop a single disposer without disposing the whole\n * chain.\n *\n * If the chain has already been disposed, the callback runs immediately\n * (sync) — this matches the “register-after-shutdown” edge case where\n * an addon's late initialization races with kernel restart.\n */\n add(fn: DisposerFn): () => void {\n if (this.disposed) {\n try {\n const result = fn()\n if (result && typeof (result as Promise<void>).then === 'function') {\n ;(result as Promise<void>).catch((err) => this.onError(err, -1))\n }\n } catch (err) {\n this.onError(err, -1)\n }\n return () => undefined\n }\n\n this.disposers.push(fn)\n return () => {\n const idx = this.disposers.indexOf(fn)\n if (idx >= 0) this.disposers.splice(idx, 1)\n }\n }\n\n /** True after `dispose()` has been called at least once. */\n get isDisposed(): boolean {\n return this.disposed\n }\n\n /** Number of disposers currently registered. */\n get size(): number {\n return this.disposers.length\n }\n\n /**\n * Run every registered disposer in LIFO order. Idempotent: subsequent\n * calls do nothing. Awaits async disposers so callers can sequence\n * shutdown → restart correctly.\n */\n async dispose(): Promise<void> {\n if (this.disposed) return\n this.disposed = true\n // Snapshot + clear so concurrent `add()` from a disposer can't\n // re-enter the same array we're draining.\n const drain = this.disposers.slice().reverse()\n this.disposers = []\n\n for (let i = 0; i < drain.length; i++) {\n const fn = drain[i]!\n try {\n const result = fn()\n if (result && typeof (result as Promise<void>).then === 'function') {\n await result\n }\n } catch (err) {\n this.onError(err, drain.length - 1 - i)\n }\n }\n }\n}\n","import type { PipelineSlot } from '../types/pipeline.js'\nimport type { ConfigUISchemaWithValues, FieldProbeResult } from './config-ui.js'\nimport type { IScopedLogger } from './logging.js'\nimport type { IEventBus, ReadinessScope } from './event-bus.js'\nimport type { ICapabilityHandle } from './readiness.js'\nimport type { CapabilityDeclaration } from './capability.js'\nimport type { ICapabilityRegistry } from './kernel-abstractions.js'\nimport type { CapabilityDefinition, InferProvider } from '../capabilities/capability-definition.js'\nimport type { CustomActionsSpec } from '../capabilities/custom-actions.js'\nimport type { z } from 'zod'\n/** Sub-process info (inlined from deleted worker-protocol.ts) */\nexport interface SubProcessInfo {\n readonly pid: number\n readonly name: string\n readonly command: string\n readonly state: 'running' | 'stopped' | 'crashed'\n readonly cpuPercent: number\n readonly memoryRss: number\n readonly uptimeSeconds: number\n}\n\n/** Worker process stats (inlined from deleted worker-protocol.ts) */\nexport interface WorkerProcessStats {\n readonly pid: number\n readonly cpuPercent: number\n readonly memoryRss: number\n readonly heapUsed?: number\n readonly uptimeSeconds: number\n readonly restartCount: number\n readonly state: 'starting' | 'running' | 'stopping' | 'stopped' | 'crashed'\n}\n\n/** Storage interface for addon file operations */\nexport interface IAddonFileStorage {\n readFile(path: string): Promise<Buffer>\n writeFile(path: string, data: Buffer): Promise<void>\n deleteFile(path: string): Promise<void>\n listFiles(prefix?: string): Promise<readonly string[]>\n exists(path: string): Promise<boolean>\n}\n\nexport interface RequiredStep {\n readonly slot: PipelineSlot\n readonly outputClasses: readonly string[]\n readonly description: string\n}\n\n/**\n * Minimal model-management API exposed to addons via AddonContext.\n */\nexport interface IAddonModelManager {\n /** Ensure a model is downloaded. Returns the local file path. */\n ensure(modelId: string, format?: import('../types/models.js').ModelFormat): Promise<string>\n\n /** Ensure extra files for a model are downloaded. Returns paths. */\n ensureExtraFiles(modelId: string): Promise<readonly string[]>\n\n /** Absolute path to the shared models directory. */\n getModelsDir(): string\n\n /** Check if a model is already downloaded. */\n isDownloaded(modelId: string, format?: import('../types/models.js').ModelFormat): boolean\n\n}\n\n/** Process management for addon sub-processes (ffmpeg, python, etc.) */\nexport interface IAddonProcessManager {\n /** Spawn a child process managed by this addon's worker */\n spawn(config: SubProcessConfig): Promise<IManagedSubProcess>\n /** List all sub-processes spawned by this addon */\n listProcesses(): readonly SubProcessInfo[]\n /** Get worker stats (this addon's worker process) */\n getWorkerStats(): WorkerProcessStats\n}\n\nexport interface SubProcessConfig {\n readonly name: string\n readonly command: string\n readonly args?: readonly string[]\n readonly cwd?: string\n readonly env?: Readonly<Record<string, string>>\n readonly autoRestart?: boolean\n readonly maxRestarts?: number\n}\n\nexport interface IManagedSubProcess {\n readonly pid: number\n readonly name: string\n getStats(): WorkerProcessStats\n write(data: Buffer): void\n readonly stdout: AsyncIterable<Buffer>\n readonly stderr: AsyncIterable<Buffer>\n kill(signal?: NodeJS.Signals): void\n wait(): Promise<{ code: number | null; signal: string | null }>\n onExit(handler: (code: number | null) => void): void\n onError(handler: (error: Error) => void): void\n}\n\n/** Dependency manager — ensures binaries/tools are available for addons */\nexport interface IAddonDepsManager {\n /** Ensure a named binary is available (check local -> PATH -> download) */\n ensureBinary(opts: {\n readonly name: string\n readonly downloadUrl: string\n readonly isArchive?: boolean\n readonly archiveFormat?: 'zip' | 'tar.gz' | 'tar.xz'\n readonly archiveInnerPath?: string\n }): Promise<string>\n\n /** Ensure ffmpeg is available */\n ensureFfmpeg(): Promise<string>\n\n /** Ensure Python is available, returns path or null if unavailable */\n ensurePython(): Promise<string | null>\n\n /** Install Python packages in the managed Python environment */\n installPythonPackages(packages: readonly string[]): Promise<void>\n\n /**\n * Install a pip requirements file into the embedded Python.\n * Idempotent: keyed on (file basename + sha256 of contents). Marker\n * lives under the python install dir so re-downloading python wipes\n * everything together.\n */\n installPythonRequirements(requirementsFile: string): Promise<void>\n\n /** Get the deps directory path */\n getDepsDir(): string\n}\n\n/**\n * AddonContext -- injected into every addon at initialize().\n *\n * The context includes both the CamstackContext fields (id, logger, eventBus, storage, config)\n * and addon-specific extras (addonConfig, models, locationPaths, router, network).\n */\n/**\n * AddonContext -- injected into every addon at initialize().\n *\n * The `api` field is typed as `AddonApi` — a generated interface that\n * mirrors the server's tRPC router namespaces. This gives addons type-safe\n * access to server procedures without depending on the server package.\n *\n * To regenerate after changing routers: npx tsx scripts/generate-api-types.ts\n */\n// ── Provider registration (return from initialize) ──────────────────────\n/**\n * A capability provider binding returned from `ICamstackAddon.initialize()`.\n * The kernel registers each entry in the CapabilityRegistry after init completes.\n */\nexport interface ProviderRegistration<TCap extends CapabilityDefinition = CapabilityDefinition> {\n /** Capability definition this provider implements. Read `capability.name` to look up by name. */\n readonly capability: TCap\n /**\n * The provider object implementing the capability interface.\n *\n * - With a specific `TCap` (e.g. `ProviderRegistration<typeof metricsProviderCapability>`)\n * the provider is strictly checked against `InferProvider<TCap>`.\n * - With the default `TCap = CapabilityDefinition` (used by heterogeneous arrays like\n * `ProviderRegistration[]` returned from `onInitialize()`) the provider is typed as\n * `object`: a mapped type over the default `Record<string, CapabilityMethodSchema>`\n * collapses to an index signature that class instances cannot satisfy structurally.\n * The runtime capability router enforces the contract; the strict compile-time check\n * is applied at the specific-cap boundary (ProviderRegistration<typeof xxxCap>) instead.\n */\n readonly provider: CapabilityDefinition extends TCap ? object : InferProvider<TCap>\n /** 'native' (default) — absolute provider.\n * 'wrapper' — user-toggleable decorator that delegates to a native via `ctx.getNativeProvider`. */\n readonly kind?: 'native' | 'wrapper'\n /** Wrapper only — active by default for every compatible device. */\n readonly defaultActive?: boolean\n}\n\n/**\n * Return value of `ICamstackAddon.initialize()`. Both sections are optional —\n * addons that provide only capabilities omit `customActions`; addons that only\n * expose actions omit `providers`; pure consumers may return void.\n */\nexport interface AddonInitResult<TCatalog extends CustomActionsSpec = CustomActionsSpec> {\n readonly providers?: readonly ProviderRegistration[]\n readonly customActions?: TCatalog\n /**\n * Required iff `customActions` is set. One async handler per catalog entry —\n * a typed dispatch map rather than a single generic function so that each\n * handler's input/output types collapse to the concrete `z.infer<...>` of\n * its schema at the provider site (TypeScript cannot unify the output of a\n * generic `<K>(action: K, input) => Promise<...>` signature with a concrete\n * per-action return type, so the map form is strictly more typeable).\n */\n readonly actionHandlers?: {\n readonly [K in keyof TCatalog & string]: (\n input: z.infer<TCatalog[K]['input']>,\n ) => Promise<z.infer<TCatalog[K]['output']>>\n }\n}\n\n// ── Kernel services ─────────────────────────────────────────────────────\n/**\n * Infrastructure services injected by the kernel at boot time.\n * Grouped under `ctx.kernel` to separate kernel plumbing from\n * addon-level concerns (logger, settings, devices, api).\n */\nexport interface IKernelServices {\n /**\n * The Moleculer nodeID for this process (e.g. `'hub'` on the hub, or the\n * agent's own name on remote nodes). Addons use this to distinguish local\n * dispatch from remote dispatch without a direct Moleculer dependency.\n */\n readonly localNodeId?: string\n\n /**\n * Local storage provider — direct filesystem access with zero serialization.\n * Use for file I/O (write/read with Buffer data) that can't go through tRPC.\n * Always co-located: storage is a kernel-level service injected at boot.\n */\n readonly storage?: import('./storage.js').IStorageProvider\n\n /**\n * Per-process system-readiness tracker. One `ReadinessRegistry` is\n * created per broker by the kernel at boot and shared across every\n * addon on that broker — consumers that need `awaitReady` /\n * `onReadyState` should use this singleton rather than constructing\n * their own so subscriptions on the event bus stay de-duplicated and\n * the snapshot is consistent across the process.\n *\n * Tests that run without a real kernel (no `ctx.kernel`) should\n * either inject a mock or fall back to creating a local\n * `ReadinessRegistry`; the production code in kernel always\n * populates this field.\n */\n readonly readinessRegistry?: import('../capabilities/index.js').ITypedReadinessRegistry\n\n /**\n * Global device registry — read + mutate devices across all addons.\n * Hub-only: workers don't have a local registry (use `ctx.devices`\n * for per-addon scoped management, or `ctx.api.deviceManager.*` for\n * cross-process access).\n */\n readonly deviceRegistry?: import('../device/device-context.js').IDeviceRegistry\n\n /**\n * Per-addon scoped device management — create, register, remove, list devices.\n * Used by device provider addons (rtsp, onvif, frigate) to manage cameras.\n */\n readonly devices: import('../device/device-context.js').DeviceManagerApi\n\n /**\n * Cluster runtime — exposes the underlying message broker for addons that\n * need to participate in cluster-wide RPC outside the capability/tRPC\n * surface (e.g. log forwarding, hub readiness probes).\n *\n * Optional: only the agent and hub processes populate this. Forked workers\n * receive their broker through their own runner harness — they do not see\n * `cluster.broker` on ctx.kernel.\n */\n readonly cluster?: {\n readonly broker: IClusterBroker\n }\n\n /**\n * Kernel stream-probe bundle. Both entrypoints land on the same\n * hub-side `StreamProbeService` (ffprobe + HTTP reachability check\n * with a 1h cache). Hub resolves them directly; forked workers +\n * remote agents route through the `$stream-probe.*` Moleculer\n * broker service. Providers that call\n * `ctx.kernel.streamProbe.probe(url)` don't need to know where\n * they run.\n */\n readonly streamProbe?: IKernelStreamProbe\n\n /**\n * Kernel hardware-accel resolver. Platform-probe that picks an\n * ordered preference list of hw decode backends for the **local\n * host**. Every node (hub + forked worker + remote agent) runs its\n * own probe because hwaccel is inherently hardware-bound: decoder\n * addons on an NVIDIA agent get `['cuda', 'nvdec']`, the same addon\n * on a Mac hub gets `['videotoolbox']`. Cross-node queries for the\n * admin UI go via the `$hwaccel.resolve` Moleculer service\n * registered on every node.\n */\n readonly hwaccel?: IKernelHwAccel\n\n /**\n * Capability registry — provider lookups, native-cap resolution, wrapper listing.\n * Hub-only: forked workers don't see this (they use `ctx.api` for cross-process calls).\n */\n readonly capabilityRegistry?: ICapabilityRegistry\n}\n\n/**\n * Canonical hwaccel backend names. Accepted by both the `ffmpeg` CLI\n * (`-hwaccel <name>`) and node-av `HardwareDeviceContext.create()`\n * (`AV_HWDEVICE_TYPE_<upper>`), so a single string drives both layers.\n */\nexport type HwAccelBackend =\n | 'videotoolbox'\n | 'cuda'\n | 'nvdec'\n | 'vaapi'\n | 'qsv'\n | 'd3d11va'\n | 'dxva2'\n | 'amf'\n | 'vdpau'\n | 'drm'\n\n/** Resolution output — preference list + rationale for observability. */\nexport interface HwAccelResolution {\n readonly preferred: readonly HwAccelBackend[]\n readonly rationale: string\n}\n\nexport interface IKernelHwAccel {\n /**\n * Probe the local host and return the preferred backends.\n *\n * `prefer` accepts:\n * - `null` / `undefined` → auto, use platform probe\n * - `'none'` → hwaccel disabled; returns empty list\n * - an explicit backend name → single-entry list (skip probe)\n *\n * Implementations are pure functions over the host — safe to call\n * repeatedly; results are deterministic per host.\n */\n readonly resolve: (prefer?: HwAccelBackend | 'none' | null) => Promise<HwAccelResolution>\n}\n\nexport interface IKernelStreamProbe {\n /**\n * Run ffprobe on an RTSP / HTTP stream URL and return the raw\n * metadata. Use this when you need the width/height/codec/fps data\n * for profile classification. `classifyStream` / `classifyStreams`\n * in `@camstack/types` turn the metadata into a `StreamQuality`.\n */\n readonly probe: (url: string, options?: { force?: boolean }) => Promise<import('./device-capabilities/camera.js').StreamMetadata>\n /**\n * Generic field probe dispatcher — keys starting with `stream` run\n * ffprobe, others run an HTTP GET that aborts at response headers.\n * Returns a `FieldProbeResult` suitable for the admin UI's \"Test\"\n * button on any `probe`-typed form field.\n */\n readonly probeField: (key: string, value: unknown) => Promise<FieldProbeResult>\n}\n\n/**\n * Minimal structural shape of a cluster-message-broker (Moleculer in the\n * current implementation). Captured in @camstack/types so cluster-aware\n * addons can consume it without pulling a moleculer dependency.\n *\n * Add new methods sparingly — every addition becomes a contract the broker\n * implementation must satisfy.\n */\nexport interface IClusterBroker {\n readonly nodeID: string\n call<TParams = unknown, TResult = unknown>(\n action: string,\n params?: TParams,\n opts?: { nodeID?: string; timeout?: number },\n ): Promise<TResult>\n waitForServices(service: string | readonly string[], timeout?: number): Promise<void>\n readonly localBus: {\n on(event: string, handler: (payload: { node: { id: string } }) => void): void\n }\n}\n\nexport interface AddonContext<TConfig = Record<string, unknown>> {\n /** Immutable progressive ID */\n readonly id: string\n /** Pre-scoped logger */\n readonly logger: IScopedLogger\n /** System event bus */\n readonly eventBus: IEventBus\n /** Bootstrap configuration from server settings (read-only) */\n readonly addonConfig: Readonly<TConfig>\n /**\n * Private data directory for this addon.\n * Resolved via storageProvider.resolve('addons-data', '{addonId}/').\n * The addon owns this directory exclusively.\n */\n readonly dataDir: string\n\n // --- Kernel services ---\n\n /** Infrastructure services provided by the kernel. */\n readonly kernel: IKernelServices\n\n // --- Settings ---\n\n /**\n * Capability settings API — 3-level resolution (defaults → global → per-device).\n * Replaces context.config (IElementConfig) and context.addonConfig.\n */\n /**\n * Three-level settings store API.\n *\n * Raw, non-merging accessors for the addon's `addon_settings` row and\n * per-device overrides, plus a `(getSection, setSection)` pair for the\n * cluster-wide yml-backed sections consumed by the built-in\n * `system-config` addon.\n *\n * The addon combines these raw reads with its own schema via\n * `hydrateSchema()` inside `getAddonSettings / getGlobalSettings /\n * getDeviceSettings` on `ICamstackAddon`. There is no defaults\n * injection or cross-level merge at this layer.\n *\n * Cleanup note: the earlier `getGlobal / getDevice / updateGlobal /\n * updateDevice` trio (with a polymorphic `getGlobal({ section })`\n * overload) was removed after every caller migrated. The only\n * consumer of the yml-section path was `system-config`, which now\n * goes through the explicit `getSection / setSection` pair below.\n */\n readonly settings?: {\n /**\n * Read the raw addon store for this addon. Holds both `addon`-level\n * and `global`-level values; they're disjoint by schema invariant so\n * they can share one physical store. No defaults merged in. Returns\n * an empty object if the addon has never written anything.\n */\n readAddonStore(): Promise<Record<string, unknown>>\n\n /**\n * Write a patch into the addon store. Keys not in the patch are\n * preserved. Keys explicitly set to `null` are preserved as-is (they\n * do NOT fall back to schema default on subsequent reads — that\n * layering happens only at `hydrateSchema` time).\n */\n writeAddonStore(patch: Record<string, unknown>): Promise<void>\n\n /**\n * Read the raw per-device store for this addon + device. No defaults,\n * no merge with the addon store. Returns `{}` when no overrides exist.\n */\n readDeviceStore(deviceId: number): Promise<Record<string, unknown>>\n\n /**\n * Write a patch into the per-device store. Does NOT touch the\n * addon-level store. Keys preserved as in `writeAddonStore`.\n */\n writeDeviceStore(deviceId: number, patch: Record<string, unknown>): Promise<void>\n\n /**\n * Delete all keys for a device from the per-device store.\n * Use on device removal to guarantee no orphaned settings rows remain.\n */\n clearDeviceStore(deviceId: number): Promise<void>\n\n /**\n * Read a device's runtime-state blob — separate namespace from\n * `readDeviceStore` (which is the addon's config blob). Holds\n * mutable runtime signals discovered after boot (battery\n * snapshot, sleep flag, last-seen) that survive restart. Stored\n * under the kernel's reserved `__device-state` namespace so the\n * addon's config schema never sees it.\n */\n readDeviceRuntimeState(deviceId: number): Promise<Record<string, unknown>>\n\n /**\n * Replace the runtime-state blob for a device. The kernel's\n * `DeviceRuntimeState` always hands over the FULL persistent\n * slice — no shallow merge here, the latest blob wins.\n */\n writeDeviceRuntimeState(deviceId: number, data: Record<string, unknown>): Promise<void>\n\n /**\n * Delete the device's runtime-state blob. Use on device removal\n * so a recreated device with the same id starts clean instead\n * of inheriting stale runtime state from the previous lifetime.\n */\n clearDeviceRuntimeState(deviceId: number): Promise<void>\n\n /**\n * Read a cluster-wide yml-backed section (server, auth, ffmpeg,\n * logging, recording, retention — whatever\n * `ConfigManager.getSection` recognises). Returns the raw section\n * record with no schema merging. Used by the built-in\n * `system-config` addon to surface the legacy `config.yaml`\n * sections through its `getGlobalSettings()` method.\n */\n getSection(section: string): Promise<Record<string, unknown>>\n\n /**\n * Write a cluster-wide yml-backed section. Shallow-merges the patch\n * into the existing section so unrelated keys are preserved.\n * Hub-only — agents have no concept of yml sections and will\n * throw. Used by the `system-config` addon's\n * `updateGlobalSettings()` implementation.\n */\n setSection(section: string, patch: Record<string, unknown>): Promise<void>\n }\n\n /**\n * Full tRPC client — same API as the frontend.\n * Transport is transparent: direct caller (in-process), WSS (worker), or WSS (remote agent).\n * Use this for all server interactions: storage, events, devices, streaming, etc.\n */\n readonly api: import('../generated/addon-api.js').AddonApi\n\n /**\n * Binary / tool dependency manager. Addons call\n * `ctx.deps.ensureBinary(...)` / `ensureFfmpeg()` / `ensurePython()` to\n * materialise per-addon runtime deps under the shared `data/deps` tree.\n * Injected by the addon host so addons never import the concrete\n * implementation from `@camstack/core` directly.\n */\n readonly deps: IAddonDepsManager\n\n /**\n * Typed handle to the native provider of `cap` for `deviceId`. Bypasses the\n * currently-active wrapper — call this ONLY from the wrapper that owns\n * `cap`. Every other consumer should use `fetchDevice(id).<cap>.<method>()`\n * instead, which routes through the cap-router and respects the wrapper\n * chain.\n *\n * Wrapper-vs-consumer rule (memorise this):\n * - \"I am the wrapper for this cap and I need to delegate to the camera\n * driver\" → `getNativeProvider(cap, id)` (bypasses self, no recursion).\n * - \"I am consuming someone else's cap\" → `fetchDevice(id).<cap>...`\n * (uses the wrapper chain — cache, fallback, validation, etc.).\n *\n * Calling `fetchDevice` from inside the cap's own wrapper would re-enter\n * the wrapper and produce infinite recursion through the cap-router.\n *\n * Returns `null` when no addon has published a native provider for this\n * `(cap, deviceId)` — e.g. a camera driver that doesn't implement the cap\n * natively (RtspCamera without `snapshotUrl` does not register snapshot).\n * Wrappers use this to decide between native delegation and their own\n * fallback strategy; they MUST check for null.\n *\n * When the native is registered in this process, returns the local object\n * directly (zero serialization). When it lives in a forked worker, returns\n * a Moleculer-backed proxy constructed by the hub's native-cap fallback\n * (see `CapabilityRegistry.setNativeFallback`) — standard\n * `<addonId>.native-provider.<capName>.<method>` RPC path.\n */\n getNativeProvider<TCap extends CapabilityDefinition>(\n cap: TCap,\n deviceId: number,\n ): InferProvider<TCap> | null\n\n /**\n * Fetch a typed proxy for a device. THE canonical entry point for any\n * consumer that wants to read state, subscribe to changes, or dispatch\n * cap methods on a device — equivalent to `BackendClient.fetchDevice(id)`\n * on the client side.\n *\n * What you can do with the returned proxy:\n * - Read live state slices: `dev.state.battery.value` (typed\n * `BatteryStatus | undefined`, no provider call — reads the kernel's\n * mirrored snapshot).\n * - Subscribe to slice changes: `dev.state.battery.subscribe(cb)` —\n * refcounted, auto-seeds the callback with the current value.\n * - Invoke cap methods through the wrapper chain:\n * `await dev.snapshot.getSnapshot({...})`. Methods are typed against\n * each cap's definition; absent caps appear as `undefined`.\n *\n * Routing semantics: every method call on the returned proxy goes through\n * the tRPC cap-router → the active wrapper (if any) → the native provider.\n * That means a wrapper consuming its OWN cap via `fetchDevice` would\n * recurse — wrappers must use `getNativeProvider` instead. See the rule\n * documented on `getNativeProvider` above.\n *\n * Bindings are cached per-addon-context: the first call resolves bindings\n * via `deviceManager.getBindings.query`, subsequent calls reuse the cache.\n * The cache is invalidated implicitly when the device is removed.\n */\n fetchDevice(deviceId: number): Promise<import('../generated/device-proxy.js').DeviceProxy>\n\n /**\n * Runtime accessor for the capability registry — exposed to addons that\n * need to enumerate peers of a collection cap (e.g. `turn-provider`) or\n * grab the active singleton for a system cap. Injected by the kernel at\n * addon instantiation; absent when the addon is loaded in isolation\n * (tests, tooling), which is why the field is optional.\n */\n readonly capabilities?: CapabilitiesAccess\n\n /**\n * Returns a capability handle immediately (non-blocking). The handle tracks\n * ready/down state via the ReadinessRegistry and gates calls accordingly.\n * Repeated calls with the same `(capName, scope)` return the same cached instance.\n *\n * Use for runtime cap consumption where the cap may not be ready yet and\n * you want calls to block transparently via `handle.call(fn)`.\n */\n useCapability<T = unknown>(capName: string, scope?: ReadinessScope): ICapabilityHandle<T>\n\n /**\n * Blocks until the capability is ready, then returns the cached handle.\n * Throws `CapabilityUnavailableError` if the cap does not become ready\n * within `timeoutMs` (default 15 000ms).\n *\n * Use for boot sequencing where downstream work must not start until\n * the cap is confirmed available.\n */\n acquireCapability<T = unknown>(\n capName: string,\n scope?: ReadinessScope,\n opts?: { timeoutMs?: number },\n ): Promise<ICapabilityHandle<T>>\n\n /**\n * Subscribe to capability state changes (ready / down).\n * Returns an unsubscribe function. Thin wrapper over `ReadinessRegistry.onReadyState`.\n */\n onCapabilityStateChange(\n capName: string,\n scope: ReadinessScope,\n handler: (state: 'ready' | 'down') => void,\n ): () => void\n\n /**\n * Register a teardown callback that runs automatically when this\n * addon is shut down or restarted (e.g. by `restartAddon` after a\n * runtime update from the admin UI). Disposers run in LIFO order so\n * later registrations clean up before the resources they depend on.\n *\n * Use this for any side effect that survives across function calls\n * — `setInterval`, native socket listeners, watchers, file handles.\n * Capability/event subscriptions returned by `onCapabilityStateChange`\n * / `eventBus.subscribe` are unsubscribe functions you can pass\n * straight to `addDisposer`. Returns a manual unsubscribe so the\n * addon can drop the disposer early if needed.\n *\n * const stopTimer = setInterval(...);\n * ctx.addDisposer(() => clearInterval(stopTimer));\n *\n * const unsub = ctx.eventBus.subscribe('motion', cb);\n * ctx.addDisposer(unsub);\n *\n * The kernel calls each disposer once during `addon.shutdown()`. A\n * disposer that throws is logged and skipped — subsequent disposers\n * still run.\n */\n addDisposer(fn: () => void | Promise<void>): () => void\n}\n\n/**\n * Runtime-only capability registry surface — kept minimal on purpose.\n * `getCollection` returns every registered provider for a collection cap\n * (order-unstable); `get` returns the active provider for a singleton.\n * Both return `undefined` when the cap isn't declared or has no provider.\n */\nexport interface CapabilitiesAccess {\n /** All providers registered on a collection capability (by cap name). */\n getCollection<T = unknown>(name: string): readonly T[] | undefined\n /**\n * Like `getCollection` but returns `[addonId, provider]` tuples so\n * callers can attribute work back to the contributing addon. The\n * `addonId` for cap-routed remote providers is `<addonName>@<workerNodeId>`\n * (e.g. `decoder-nodeav@dev-agent-1/decoder-nodeav`); local providers\n * are just `<addonName>` (no `@`). Used by stream-broker to filter the\n * decoder collection by the orchestrator's per-camera placement\n * decision (`getDecoderAssignment.decoderNodeId`).\n */\n getCollectionEntries<T = unknown>(name: string): readonly (readonly [string, T])[] | undefined\n /** The active provider of a singleton capability (by cap name). */\n get<T = unknown>(name: string): T | undefined\n}\n\nexport interface ICamstackAddon {\n readonly id?: string\n /** Addon declaration — injected by the framework from package.json. Do not declare in addon class. */\n readonly manifest?: AddonDeclaration\n /**\n * Initialize the addon. Return capability provider bindings for the kernel\n * to register in the CapabilityRegistry. Returning `void` or an empty array\n * is valid for addons that don't provide capabilities (e.g. pure consumers).\n *\n * The kernel validates that every capability declared in `package.json`\n * has a corresponding entry in the returned array.\n *\n * @example\n * ```ts\n * async initialize(ctx: AddonContext): Promise<AddonInitResult> {\n * this.detector = new WasmMotionDetector()\n * await this.detector.load()\n * return { providers: [{ capability: motionDetectionCapability, provider: this }] }\n * }\n * ```\n */\n initialize(context: AddonContext): Promise<ProviderRegistration[] | AddonInitResult | void>\n /**\n * Called by the isolated-process runner after `broker.start()`.\n * In-process addons never need this — the hub broker is already running\n * when `initialize()` fires. Forked children emit readiness inside\n * `initialize()` before the broker is live; this re-emits it once the\n * transport is ready so consumers actually receive the event.\n */\n postBrokerStart(): void\n /**\n * Optional hook. Fires on forked workers / remote agents once the\n * broker has started AND the hub node has been discovered via the\n * Moleculer `$node.connected` event — the moment at which\n * `ctx.api.*` calls to hub-provided capabilities become safe.\n *\n * Use this instead of `initialize()` for any bootstrap work that\n * needs to query the hub (e.g. restoring persisted state, reading\n * hub-owned settings). Calling `ctx.api.*` from `initialize()` on a\n * worker is a documented foot-gun: the broker is not connected yet\n * and requests either time out or throw \"Service not found\".\n *\n * On the hub itself this never fires (the hub is its own node; there\n * is no separate hub to wait for).\n */\n onHubReachable?(): Promise<void> | void\n shutdown(): Promise<void>\n\n // Cleanup: the legacy `getConfigSchema / getConfig / onConfigChange`\n // trio was removed. Every addon implements the three-level settings\n // API below and nothing on the server reads the legacy shape any\n // more — the adapter shim in `addon-registry.service.ts` and the\n // `addons.getConfigSchema / getConfig / updateConfig` tRPC endpoints\n // are gone.\n\n // -- Three-level settings API --\n //\n // An addon implements only the levels it needs. Each level has a\n // disjoint schema — field keys must not collide across levels. Each\n // getter returns a hydrated `ConfigUISchemaWithValues` (schema + values\n // inline) so the admin UI can render in one pass.\n //\n // Level 1 — addon settings: pipeline/context-specific fields, rendered\n // by whatever page knows the addon belongs to it (e.g. the\n // Pipeline page hardcodes PIPELINE_CAPABILITIES and iterates\n // providers).\n // Level 2 — node-global settings: node-level fields shown in Cluster →\n // NodeDetailPanel → Settings. Dynamic discovery: an addon\n // that doesn't implement `getGlobalSettings` doesn't appear\n // there.\n // Level 3 — device settings: per-camera fields shown in Device\n // Overrides.\n //\n // All six methods are OPTIONAL. They will become the canonical way\n // addons expose settings once Phase 3 removes the deprecated trio\n // above. See docs/superpowers/specs/2026-04-11-settings-redesign-design.md.\n\n /** Level 1 — node-global settings (schema + values). Appears in\n * Cluster → Settings tab. `overlay` is an optional preview merge\n * consumed by override-mode UIs (benchmark) so cascade-aware\n * addons can re-derive dependent options without touching the\n * persisted store. */\n getGlobalSettings?(overlay?: Record<string, unknown>): Promise<ConfigUISchemaWithValues>\n updateGlobalSettings?(patch: Record<string, unknown>): Promise<void>\n\n /** Level 2 — per-device settings (schema + values). Appears in\n * Device Overrides. */\n getDeviceSettings?(deviceId: number): Promise<ConfigUISchemaWithValues>\n updateDeviceSettings?(deviceId: number, patch: Record<string, unknown>): Promise<void>\n\n /**\n * Called at boot after initialize() with all previously persisted devices.\n * The addon should re-instantiate device objects and register them via context.devices.register().\n */\n restoreDevices?(savedDevices: readonly import('../device/device-management.js').SavedDevice[]): Promise<void>\n\n // -- Model catalog (optional — only for inference addons) --\n getModelCatalog?(): unknown[]\n // `getModelRequirements` and `configure` were removed — inference addons\n // now resolve their own config inside `initialize()` by calling\n // `ctx.api.platformProbe.resolveInferenceConfig.query({ requirements })`.\n\n // -- Inference methods (optional — only for detection/classification addons) --\n\n /** Load/prepare a model engine for a given modelId. Called before process()/classifyAudio(). */\n ensureEngine?(modelId: string): Promise<void>\n\n /** Unified vision inference — handles detection, cropping, classification, refinement.\n * Input is FrameInput (root) or CropInput (child node). Output shape determines slot. */\n process?(input: unknown): Promise<unknown>\n\n /** Audio classification — separate from vision pipeline */\n classifyAudio?(input: unknown): Promise<unknown>\n}\n\n/** Declaration of a UI page provided by an addon */\nexport interface AddonPageDeclaration {\n /** Unique page ID (scoped to addon) */\n readonly id: string\n /** Display label for sidebar */\n readonly label: string\n /** Lucide icon name for sidebar */\n readonly icon: string\n /** Route path (e.g., '/benchmark') */\n readonly path: string\n /**\n * Module Federation remote name — must match the `name` field on the\n * page addon's `federation()` plugin config. Used by admin-ui's\n * `<AddonPageLoader>` to call `loadRemote('<remoteName>/page')`.\n * Conventionally `addon_<id>_page` (snake_case; MF names cannot\n * contain hyphens).\n */\n readonly remoteName: string\n /**\n * Bundle filename inside the addon's `dist/` dir served at\n * `/api/addon-pages/<addonId>/<bundle>`. With Module Federation this\n * is always `'remoteEntry.js'`.\n */\n readonly bundle: string\n}\n\n/** Provider interface for addons that expose UI pages */\nexport interface IAddonPageProvider {\n readonly id: string\n listPages(): readonly AddonPageDeclaration[]\n}\n\n/** Provider interface for the admin UI shell (singleton capability) */\nexport interface IAdminUI {\n getStaticDir(): string\n getVersion(): string\n}\n\n/**\n * Full addon declaration — lives in package.json `camstack.addons[]`.\n * This is the SINGLE SOURCE OF TRUTH for addon identity and metadata.\n * Addon classes no longer declare a `manifest` property.\n */\nexport interface AddonDeclaration {\n // ── Identity ────────────────────────────────────────────────────────\n readonly id: string\n /** JS entry point relative to package root (e.g., \"./dist/addon.js\") */\n readonly entry?: string\n /** Human-readable name */\n readonly name?: string\n /** Semver version */\n readonly version?: string\n /** Short description */\n readonly description?: string\n\n // ── Display ────────────────────────────────────────────────────────\n /** Relative path to SVG icon asset within the addon package */\n readonly icon?: string\n /** Brand hex color for UI display (e.g., \"#3b82f6\") */\n readonly color?: string\n\n // ── Runtime ────────────────────────────────────────────────────────\n readonly slot?: PipelineSlot | null\n /** Instance mode: 'unique' or 'multiple'. Default: 'multiple' */\n readonly instanceMode?: 'unique' | 'multiple'\n /** Whether this addon is protected (required by the system, cannot be uninstalled) */\n readonly protected?: boolean\n /**\n * Group-runner execution model.\n *\n * Every addon lives in a forked subprocess; the question is which one.\n * Addons sharing the same `group` value share one subprocess and one\n * Moleculer broker, so cap calls between them resolve in-process via\n * `localProviderLink` (zero IPC). Different groups speak via Moleculer\n * TCP.\n *\n * Defaults (when `execution` is omitted entirely):\n * - `placement: 'hub-only'` — addon does NOT deploy to agents.\n * - `group: 'main-group'` — joins the catch-all subprocess.\n *\n * Examples:\n * ```jsonc\n * // hub-only addon, joins main-group (most common)\n * \"execution\": {}\n *\n * // deployable to any node (cluster scenario)\n * \"execution\": { \"placement\": \"any-node\" }\n *\n * // bundle detection on a dedicated subprocess\n * \"execution\": { \"placement\": \"any-node\", \"group\": \"detection\" }\n *\n * // hardware-bound, only runs on agents (no hub instance)\n * \"execution\": { \"placement\": \"agent-only\" }\n *\n * // crash-isolated (private subprocess; unique group per addon)\n * \"execution\": { \"group\": \"untrusted-foo\" }\n * ```\n */\n readonly execution?: AddonExecution\n\n // ── Capabilities ───────────────────────────────────────────────────\n /** Capabilities this addon provides */\n readonly capabilities?: readonly CapabilityDeclaration[]\n /** UI pages provided by this addon */\n readonly pages?: readonly AddonPageDeclaration[]\n\n // ── Pipeline-specific ──────────────────────────────────────────────\n readonly requiredFeatures?: readonly string[]\n readonly inputClasses?: readonly string[]\n readonly outputClasses?: readonly string[]\n readonly requiredSteps?: readonly RequiredStep[]\n readonly supportsCustomModels?: boolean\n readonly mayRequirePython?: boolean\n /** Whether this addon performs active inference. Passive addons are excluded from inference pipeline. Default: true. */\n readonly passive?: boolean\n /**\n * Label output type for classifier/recognizer addons.\n *\n * `classification` — generic class name (e.g. `'bird'`, `'labrador'`)\n * `face` — recognised face identity\n * `plate` — recognised licence plate OCR\n * `recognition` — generic recognition result (embedding gallery)\n */\n readonly labelOutputType?: 'classification' | 'face' | 'plate' | 'recognition'\n\n // ── Python (auto-managed deps) ─────────────────────────────────────\n /**\n * Optional Python runtime dependencies. Declares the pip requirements\n * file the kernel must install into the embedded portable Python\n * before this addon's `onInitialize()` runs. Resolved relative to the\n * addon's package root.\n *\n * Idempotent: install is keyed on (file basename + sha256 of contents)\n * via the marker dir under the python install — back-to-back boots\n * skip the pip subprocess after the first install.\n *\n * Example:\n * ```jsonc\n * \"camstack\": {\n * \"addons\": [{\n * \"id\": \"detection-pipeline\",\n * \"python\": { \"requirements\": \"./python/requirements.txt\" }\n * }]\n * }\n * ```\n *\n * Backend / feature-specific deps that depend on user settings should\n * NOT be listed here — addons install those lazily via\n * `ctx.deps.installPythonRequirements(absolutePath)`.\n */\n readonly python?: AddonPythonRequirements\n\n // ── Config ─────────────────────────────────────────────────────────\n /** Default configuration values */\n readonly defaultConfig?: Readonly<Record<string, unknown>>\n}\n\n/**\n * Manifest declaration for an addon's Python runtime dependencies.\n * See `AddonDeclaration.python`.\n */\nexport interface AddonPythonRequirements {\n /**\n * Path to the pip requirements file the kernel installs into the\n * embedded portable Python before `onInitialize()` runs. Resolved\n * relative to the addon package root.\n */\n readonly requirements: string\n}\n\nexport interface AddonPackageManifest {\n /** Human-readable package name for UI display (e.g., \"CamStack Vision\") */\n readonly displayName?: string\n readonly addons: readonly AddonDeclaration[]\n /**\n * Optional bundle metadata for npm packages that ship multiple addon\n * entries (e.g., `@camstack/addon-pipeline` with 7 entries,\n * `@camstack/addon-remote-storage` with 3 entries). When set, the\n * Addons admin UI renders the entries as collapsible children under a\n * single bundle card. The npm package boundary IS the install/update\n * unit — operator-facing actions (Install, Update, Uninstall) act on\n * the bundle, not on individual children. Per-child enable/disable +\n * settings are still surfaced individually.\n *\n * Single-entry packages omit this field; their UI stays a standalone\n * card. Multi-entry packages WITHOUT this field fall back to deriving\n * displayName from `package.json:name`.\n */\n readonly bundle?: AddonBundleManifest\n /**\n * Native node modules that the addon needs at runtime but cannot be\n * bundled (`.node` binary files require ABI-matched compilation).\n * Phase E of the bundles + builder modernization spec — mirror of\n * the Python `requirements.txt` pattern, but for native Node deps.\n *\n * Format: package name → semver range, exactly like\n * `package.json#dependencies`. NOT listed in the package's regular\n * `dependencies` field (so workspace `npm install` skips them), but\n * the addon installer reads this field at install time and runs:\n *\n * npm install --prefix <addonDir> <name>@<range>\n * electron-rebuild -m <addonDir> -w <name> (for Electron host)\n * npm rebuild --prefix <addonDir> <name> (for plain Node host)\n *\n * The host's runtime ABI (Electron 35 = Node ABI 127, plain Node 22 =\n * ABI 133) is detected at install time. Both targets are first-class.\n *\n * Example:\n * ```jsonc\n * \"camstack\": {\n * \"nativeDependencies\": {\n * \"better-sqlite3\": \"^12.8.0\",\n * \"ssh2\": \"^1.16.0\"\n * }\n * }\n * ```\n *\n * Phase E will also produce prebuilt `.node` binaries for both ABIs\n * during `npm publish`, shipped in the tarball under\n * `dist/native-prebuilds/<runtime>-<abi>-<arch>/`. The installer\n * tries the matching prebuild first; falls back to source rebuild\n * only when no prebuild exists for the host triple.\n */\n readonly nativeDependencies?: Readonly<Record<string, string>>\n}\n\nexport interface AddonBundleManifest {\n /** Display name shown on the bundle card header (e.g., \"Pipeline\"). */\n readonly displayName: string\n /** Optional 1-2 sentence description for the operator. */\n readonly description?: string\n /** Optional icon path relative to the package root. */\n readonly icon?: string\n}\n\n/**\n * Where an addon may be deployed. The kernel honours this when picking\n * which node hosts the group-runner that will load the addon.\n *\n * - `'hub-only'` — runs only on the hub process. Default.\n * - `'agent-only'` — runs only on remote agent processes.\n * - `'any-node'` — eligible to run on either hub or agents.\n */\nexport type AddonPlacement = 'hub-only' | 'agent-only' | 'any-node'\n\n/**\n * Effective execution model for an addon. See `AddonDeclaration.execution`\n * for the full semantics.\n */\nexport interface AddonExecution {\n readonly placement?: AddonPlacement\n /**\n * Subprocess group label. Addons sharing the same value share one\n * forked subprocess + one Moleculer broker. Defaults to `'main-group'`\n * — the catch-all bucket for every addon that doesn't opt out.\n */\n readonly group?: string\n}\n\nexport const DEFAULT_ADDON_GROUP = 'main-group'\nexport const DEFAULT_ADDON_PLACEMENT: AddonPlacement = 'hub-only'\n\n/**\n * Resolve the effective `AddonExecution` for an addon. Returns a fully\n * populated value (placement + group always set) so callers can read\n * either field without a separate fallback.\n */\nexport function resolveAddonExecution(\n decl: Pick<AddonDeclaration, 'execution'>,\n): Required<AddonExecution> {\n return {\n placement: decl.execution?.placement ?? DEFAULT_ADDON_PLACEMENT,\n group: decl.execution?.group ?? DEFAULT_ADDON_GROUP,\n }\n}\n\n/** True when the addon may run on a remote agent (deployable from hub). */\nexport function isDeployableToAgent(decl: Pick<AddonDeclaration, 'execution'>): boolean {\n const placement = resolveAddonExecution(decl).placement\n return placement === 'any-node' || placement === 'agent-only'\n}\n\n/** True when the addon is skipped on the hub (agent-only deployment). */\nexport function isAgentOnlyPlacement(decl: Pick<AddonDeclaration, 'execution'>): boolean {\n return resolveAddonExecution(decl).placement === 'agent-only'\n}\n\n/** Convenience accessor — the resolved subprocess group label. */\nexport function resolveAddonGroup(decl: Pick<AddonDeclaration, 'execution'>): string {\n return resolveAddonExecution(decl).group\n}\n\n/** Convenience accessor — the resolved placement (defaults to `hub-only`). */\nexport function resolveAddonPlacement(decl: Pick<AddonDeclaration, 'execution'>): AddonPlacement {\n return resolveAddonExecution(decl).placement\n}\n\n// ── Kernel-internal context ─────────────────────────────────────────────\n/**\n * Extended AddonContext used exclusively by the kernel (hub + worker boot\n * sequences) to wire providers into the CapabilityRegistry after\n * `initialize()` returns. Addon code never sees this — it only receives\n * the public `AddonContext`.\n */\nexport interface InternalAddonContext extends AddonContext {\n /** Register a provider in the local capability registry. Kernel-only. */\n registerProvider(capabilityName: string, provider: unknown): void\n\n /** Resolve the preferred provider for a capability. Kernel-only. */\n resolveProvider<T = unknown>(capabilityName: string): T | null\n}\n","export enum EventCategory {\n // System\n SystemBoot = 'system.boot',\n SystemAddonsReady = 'system.addons-ready',\n SystemRestarting = 'system.restarting',\n /**\n * Readiness transition for a capability provider. Every producer emits\n * this event on `onInitialize` completion, `onDestroy`, and\n * `$node.reconnect`; every consumer that needs to gate on a cross-process\n * cap subscribes via the kernel's readiness module (`awaitReady` /\n * `onReadyState`) instead of polling. Payload is\n * `SystemReadyStatePayload` — see event-bus.ts.\n */\n SystemReadyState = 'system.ready-state',\n\n // Addon lifecycle\n AddonStarted = 'addon.started',\n AddonStopped = 'addon.stopped',\n AddonRestarted = 'addon.restarted',\n AddonUpdated = 'addon.updated',\n AddonInstalled = 'addon.installed',\n AddonUninstalled = 'addon.uninstalled',\n AddonCrashed = 'addon.crashed',\n AddonError = 'addon.error',\n AddonPageReady = 'addon.page-ready',\n AddonWidgetReady = 'addon.widget-ready',\n /**\n * Addon failed to load (import or initialize). Emitted by the kernel's\n * AddonHealthMonitor only AFTER the boot grace period ends — failures\n * during the first 5 minutes are silently retried without alerting\n * (slow-starting addons must have time to come up). Post-grace, this\n * event is emitted exactly once per failure-streak; AlertCenter\n * consumes it to create a persistent operator-visible alert.\n *\n * Payload: `{ packageName, addonId?, error: { message, stack }, retryCount, nextRetryAt }`.\n */\n AddonLoadFailed = 'addon.load-failed',\n /**\n * Addon recovered from a previous failure. Emitted when an addon\n * transitions from `failed` back to `healthy` (typically via the\n * monitor's auto-retry loop, or after manual `addons.retryLoad`).\n * AlertCenter dismisses the corresponding `AddonLoadFailed` alert\n * on this event.\n */\n AddonLoadRecovered = 'addon.load-recovered',\n /**\n * Monitor scheduled the next retry for a failed addon. Transient —\n * surfaced to the UI for live-updating the \"next retry in Ns\"\n * countdown on the Addons page row, NOT persisted as an alert.\n */\n AddonRetryScheduled = 'addon.retry-scheduled',\n /**\n * Monitor is attempting to reload a failed addon NOW. UI uses this\n * to show a spinner during the retry attempt. Same transient nature\n * as AddonRetryScheduled.\n */\n AddonRetryAttempting = 'addon.retry-attempting',\n\n // Device\n DeviceRegistered = 'device.registered',\n DeviceUnregistered = 'device.unregistered',\n DeviceEnabled = 'device.enabled',\n DeviceDisabled = 'device.disabled',\n DeviceSettingsUpdated = 'device.settings-updated',\n /**\n * Emitted when the set of native capability providers bound to a device\n * changes — e.g. an addon registers a new native cap via\n * `DeviceContext.registerNativeCap`, or all native bindings for a device\n * are cleared on removal. Hub consumers re-resolve device-proxy routes\n * when this fires.\n */\n DeviceBindingsChanged = 'device.bindings-changed',\n /**\n * Emitted when the operator-organisational meta surface changes\n * (`name` / `location` / `disabled`). Payload: `{deviceId, field,\n * value}`. Live consumers (UI device list, alert center) react\n * without polling. Distinct from `DeviceSettingsUpdated` which\n * fires on hardware-config changes (host/port/credentials/etc).\n */\n DeviceMetaChanged = 'device.meta-changed',\n /**\n * Emitted by DeviceStreamWiringService after a successful\n * `stream-broker.registerDeviceStreams` call. Payload includes\n * deviceId — consumers look up the full registered device info via\n * `brokerManager.getRegisteredDevice(deviceId)`.\n *\n * Replaces the legacy `StreamRouterService.onDeviceRegistered` callback.\n */\n DeviceStreamsRegistered = 'device.streams-registered',\n\n // Integration\n IntegrationEnabled = 'integration.enabled',\n IntegrationDisabled = 'integration.disabled',\n IntegrationDeleted = 'integration.deleted',\n\n // Provider\n ProviderStarted = 'provider.started',\n ProviderStopped = 'provider.stopped',\n\n // Process\n ProcessCrashed = 'process.crashed',\n ProcessRestartScheduled = 'process.restart_scheduled',\n ProcessRestarted = 'process.restarted',\n\n // Recording\n RecordingStarted = 'recording.started',\n RecordingStopped = 'recording.stopped',\n RecordingError = 'recording.error',\n RecordingHealthDegraded = 'recording.health.degraded',\n RecordingStorageCritical = 'recording.storage.critical',\n RecordingSegmentWritten = 'recording.segment.written',\n RecordingPolicyFallback = 'recording.policy.fallback',\n RecordingRetentionCompleted = 'recording.retention.completed',\n\n // Detection & analysis\n DetectionEvent = 'detection.event',\n\n // Session tracking\n SessionTrackNew = 'session.track.new',\n SessionTrackExpired = 'session.track.expired',\n\n // Benchmark\n BenchmarkProgress = 'benchmark.progress',\n\n // Platform probe\n PlatformProbePhase = 'platform-probe.phase',\n\n // Pipeline & inference\n PipelineProgress = 'pipeline.progress',\n /** Per-frame execution trace emitted by the pipeline executor for live observability. */\n PipelineTrace = 'pipeline.trace',\n /**\n * Raw inference output emitted by `addon-pipeline-runner` after running the\n * detection pipeline on a frame. Carries the `FrameResult` (with\n * `detections[]`, `width`/`height`, timing debug) — never the frame\n * buffer itself. Hub-side consumers (analysis pipeline, class filters,\n * notifications) subscribe to this and re-emit `detection.result` after\n * post-processing.\n */\n PipelineInferenceResult = 'pipeline.inference-result',\n /**\n * Camera lifecycle event emitted by `addon-pipeline-orchestrator` when it\n * assigns or unassigns a camera to/from an agent. Carries no frame data;\n * pure observability for UI dashboards and metrics consumers.\n */\n PipelineCameraAssigned = 'pipeline.camera-assigned',\n PipelineCameraUnassigned = 'pipeline.camera-unassigned',\n /**\n * Per-camera pipeline config was mutated by the orchestrator\n * (3-level settings change via `setAgentAddonDefaults` /\n * `setCameraStepToggle` / `setCameraPipelineForAgent` or a\n * pipeline-scoped `applyDeviceSettingsPatch`). Orchestrator\n * subscribes to its own emission to hot-reload the assigned runner\n * via `attachCamera` so the next frame executes against the new\n * engine/steps without waiting for a rebalance or the next\n * `DeviceStreamsRegistered` cycle.\n */\n PipelineCameraUpdated = 'pipeline.camera-updated',\n /**\n * Periodic snapshot of per-node pipeline-runner load\n * (`RunnerLocalLoad`). Emitted ~1Hz by every runner so UI dashboards\n * subscribe instead of polling `pipelineRunner.getLocalLoad`.\n * `nodeId` carried in the payload + on `event.source.nodeId`.\n */\n PipelineRunnerLoadSnapshot = 'pipeline.runner-load-snapshot',\n /**\n * Periodic snapshot of per-camera pipeline metrics (`CameraMetrics`\n * + `deviceId` + `nodeId`). Emitted ~1Hz by every runner for each\n * attached camera. UI subscribes to drive overlay phase / fps /\n * inference time without polling `getCameraMetrics`.\n */\n PipelineCameraMetricsSnapshot = 'pipeline.camera-metrics-snapshot',\n /**\n * Periodic snapshot of stream-broker per-broker statistics (input\n * fps, decoded fps, bitrate, codec). Emitted ~1Hz by every\n * stream-broker process for each active broker so the UI can drive\n * the Stream / Cluster dashboards without polling\n * `streamBroker.listAllProfileSlots` and friends.\n */\n StreamBrokerMetricsSnapshot = 'stream-broker.metrics-snapshot',\n /**\n * Cap event fired by `stream-broker` when a profile slot enters\n * \"demanded\" state — a cam stream has been assigned and at least one\n * consumer (RTSP restream, decoded subscriber, WebRTC session, …) is\n * present. Camera-provider addons (Reolink Baichuan push, …)\n * subscribe to this category to start their underlying transport\n * lazily. Payload: `{ deviceId, camStreamId, profile }`.\n */\n StreamBrokerOnCamStreamDemand = 'stream-broker.onCamStreamDemand',\n /**\n * Cap event fired by `stream-broker` when the last consumer leaves a\n * previously-demanded cam stream. Providers tear down their\n * underlying transport on receipt. Payload: `{ deviceId, camStreamId }`.\n */\n StreamBrokerOnCamStreamIdle = 'stream-broker.onCamStreamIdle',\n /**\n * Cap event fired by `stream-broker` when a broker fails to dial a\n * managed-loopback source (today: `pull-rfc4571`) and the publisher\n * needs to refresh the cached URL. Mirrors Scrypted's\n * `ensureRfcServer` self-heal pattern: the lib's TCP server\n * idle-tears-down on its own schedule, so a re-publish with a fresh\n * `host:port` is the only way to keep the broker dialable. Camera\n * providers (Reolink Baichuan native, …) subscribe and respond by\n * re-running their publish pipeline.\n * Payload: `{ deviceId, camStreamId, brokerId }`.\n */\n StreamBrokerOnRequestStreamSourceRefresh = 'stream-broker.onRequestStreamSourceRefresh',\n /**\n * Generic per-device runtime-state change. Fired by `device-manager`\n * whenever a persisted slice in any cap's `runtimeState` shape\n * mutates. Payload: `{deviceId, capName, slice}`. Subscribers are\n * the `deviceState` cap router (cross-process listeners) and the\n * deviceProxy reactive bindings (`device.state.<capName>.value`).\n * Cap-specific events (`battery.onStatusChanged`, …) still fire\n * — they're authoritative for callers that want a typed payload\n * without filtering on `capName`.\n */\n DeviceStateChanged = 'device.state-changed',\n /**\n * Cap event fired by every device that registers the `battery`\n * capability. Mirrors the cap definition's `onStatusChanged`. Carries\n * `{ deviceId, status: BatteryStatus }`. Subscribers (alert center,\n * snapshot wrapper, UI) react to charge/sleep transitions without\n * polling `batteryCapability.getStatus`.\n */\n BatteryOnStatusChanged = 'battery.onStatusChanged',\n /**\n * Cap event fired by every device that registers the `doorbell`\n * capability. Mirrors `doorbellCapability.events.onPressed`. Carries\n * `{ deviceId, timestamp }`. Operators consuming the UI subscribe\n * here to render transient ring toasts and a \"Recent presses\" row\n * on the device detail page.\n */\n DoorbellOnPressed = 'doorbell.onPressed',\n /**\n * Periodic snapshot of the per-node detection-pipeline engine\n * registry (loaded engines, models resident, in-use cameras, idle\n * TTL). Emitted ~0.2Hz (every 5 s) by every detection-pipeline\n * process. The Engines tab subscribes to drive its inventory view\n * without polling `pipelineExecutor.listLoadedEngines`.\n */\n PipelineEngineMetricsSnapshot = 'pipeline.engine-metrics-snapshot',\n /**\n * Cluster topology snapshot. Carries the same payload returned by\n * `nodes.topology` (every reachable node + addons + processes).\n * Emitted by the hub on any agent / addon lifecycle change\n * (debounced) plus a periodic safety net. Replaces UI polling on\n * `nodes.topology` — admin-ui dashboards subscribe to drive the\n * cluster view directly from the event payload.\n */\n ClusterTopologySnapshot = 'cluster.topology-snapshot',\n /**\n * Periodic per-node system metrics snapshot (CPU / memory / GPU /\n * disk / network). Emitted ~0.2 Hz by the metrics-provider addon\n * for each node. Drives the dashboard SystemStatus / ProcessResources\n * widgets without polling `metricsProvider.getCurrent`.\n */\n MetricsNodeResourcesSnapshot = 'metrics.node-resources-snapshot',\n /**\n * Periodic per-node process-tree snapshot (camstack-related pids\n * with ghost / managed / root classification). Emitted ~0.2 Hz by\n * the metrics-provider addon. Drives the Cluster → Processes tab\n * without polling `metricsProvider.listNodeProcesses`.\n */\n MetricsNodeProcessesSnapshot = 'metrics.node-processes-snapshot',\n /**\n * Capability binding change event emitted by `addon-pipeline-orchestrator`\n * when a user changes which addon implements a cap on a node. Subscribed\n * by every kernel process to update its local `preferredProviderRegistry`\n * so future capability lookups respect the new binding.\n */\n /**\n * A capability binding was changed for a node — addon X now provides\n * capability `cap` on node `nodeId`. Lives under the generic\n * `capability.*` namespace because capability bindings are a kernel-\n * level concept used by many addons, not strictly pipeline-scoped.\n */\n CapabilityBindingChanged = 'capability.binding-changed',\n ModelDownloadProgress = 'model.download.progress',\n\n // Agent\n AgentRegistered = 'agent.registered',\n AgentUnregistered = 'agent.unregistered',\n AgentOnline = 'agent.online',\n AgentOffline = 'agent.offline',\n /** Forked worker process (e.g. hub/pipeline) connected to the broker. */\n WorkerOnline = 'worker.online',\n /** Forked worker process disconnected from the broker. */\n WorkerOffline = 'worker.offline',\n AgentTaskDispatched = 'agent.task.dispatched',\n AgentTaskAssigned = 'agent.task.assigned',\n AgentTrpcConnected = 'agent.trpc.connected',\n AgentWsConnected = 'agent.ws.connected',\n AgentWsDisconnected = 'agent.ws.disconnected',\n AgentBackupActivated = 'agent.backup.activated',\n\n // Detection settings\n OrchestrationSettingsUpdated = 'orchestration.settings-updated',\n\n /**\n * Per-agent hwaccel preference changed (user override set, cleared, or\n * re-probed). Observability event — decoders pull the current pref at\n * `createSession`, so running sessions keep their current backend until\n * they rotate naturally (camera add/remove, stream restart). Future\n * work can wire a listener in stream-broker that force-rotates live\n * sessions; for now this event powers logs + admin-UI toast feedback.\n */\n PipelineAgentHwaccelChanged = 'pipeline.agent-hwaccel-changed',\n\n // Motion analysis\n MotionAnalysis = 'detection.motion-analysis',\n /** All raw motion zones from CCL before minArea filter — for UI debug overlay. */\n MotionZonesRaw = 'detection.motion-zones-raw',\n /**\n * Per-camera motion phase transition (`watching ↔ active`) emitted\n * by the runner. Mirrors the `motion.onMotionChanged` cap event\n * surface — payload `MotionOnMotionChangedPayload` carries\n * `{deviceId, detected, timestamp, source, regions?}`. Subscribers\n * include addons that need to react to motion state without\n * polling the runtime-state mirror.\n */\n MotionOnMotionChanged = 'motion.on-motion-changed',\n\n // Detection extended\n DetectionResult = 'detection.result',\n DetectionRaw = 'detection.raw',\n DetectionCameraNative = 'detection.camera-native',\n /**\n * Canonical per-chunk live audio pipeline output. Payload is\n * `PipelineAudioInferenceResultPayload` carrying a full `AudioResult`\n * (level + detections + debug). Lives on the pipeline.* namespace\n * alongside `pipeline.inference-result` (video) for symmetry.\n */\n PipelineAudioInferenceResult = 'pipeline.audio-inference-result',\n DetectionPhaseTransition = 'detection.phase-transition',\n ProviderMotion = 'provider.motion',\n ProviderDetection = 'provider.detection',\n\n // Enrichment\n EnrichmentEmbeddingStored = 'enrichment.embedding.stored',\n EnrichmentSceneStateChanged = 'enrichment.scene.state-changed',\n EnrichmentActivitySummary = 'enrichment.activity.summary',\n\n // Pipeline analytics\n PipelineAnalyticsTrackStarted = 'pipeline-analytics.track-started',\n PipelineAnalyticsTrackEnded = 'pipeline-analytics.track-ended',\n PipelineAnalyticsDetectionEvent = 'pipeline-analytics.detection-event',\n PipelineAnalyticsFrameTracked = 'pipeline-analytics.frame-tracked',\n\n // Provider-specific\n FrigateLiveEvent = 'frigate.live-event',\n\n // Camera streams\n CameraStreamsProfileSlotsChanged = 'camera-streams.onProfileSlotsChanged',\n /**\n * Stream-broker health watchdog. Per-broker (deviceId/profile) emission.\n * `stream.offline` fires after STREAM_STALE_TIMEOUT_MS without an encoded\n * packet on an active broker. `stream.online` fires on first packet\n * after a stale gap (or on initial first packet). Payload includes\n * the assigned camStreamId as `profileKey`.\n */\n StreamOnline = 'stream.online',\n StreamOffline = 'stream.offline',\n\n // Network\n NetworkTunnelStarted = 'network.tunnel.started',\n NetworkTunnelStopped = 'network.tunnel.stopped',\n /** Fired by the `local-network` cap when the host's interface set\n * changes (new IP from DHCP, VPN connect, docker bridge added). */\n LocalNetworkChanged = 'network.local.changed',\n\n // Backup\n BackupCompleted = 'backup.completed',\n BackupRestored = 'backup.restored',\n\n // Notification\n NotificationDispatched = 'notification.dispatched',\n NotificationFailed = 'notification.failed',\n\n // Device extended\n DeviceUpdated = 'device.updated',\n /**\n * Transport-level connectivity. Emitted by the device driver when\n * the underlying control socket actually connects / disconnects\n * (Baichuan TCP, ONVIF probe response, RTSP DESCRIBE, …) — NOT for\n * power-state transitions on a battery camera. For battery wake /\n * doze cycles see `DeviceAwake` / `DeviceSleeping`.\n */\n DeviceOnline = 'device.online',\n /**\n * Stream-broker watchdog — emitted when no encoded packet has been\n * received for STREAM_STALE_TIMEOUT_MS on an active broker (rtsp or\n * push). Paired with DeviceOnline which fires on first packet after\n * a stale gap. Payload: DeviceStreamHealthPayload.\n */\n DeviceOffline = 'device.offline',\n /**\n * Battery cam woke up — physical power-state transition reported\n * by the device firmware. Distinct from `DeviceOnline` so a UI panel\n * watching power state doesn't flap on every UDP socket reconnect.\n */\n DeviceAwake = 'device.awake',\n /**\n * Battery cam went to sleep. See `DeviceAwake`.\n */\n DeviceSleeping = 'device.sleeping',\n\n // Retention\n RetentionCleanup = 'retention.cleanup',\n}\n","import type { FrameResult, AudioResult } from '../types/detection.js'\nimport type { PipelineExecutionTrace } from '../types/pipeline-step.js'\nimport type { MotionRegion } from '../capabilities/motion-detection.cap.js'\nimport type { CameraPipelineConfig } from '../types/camera-pipeline.js'\nimport type { CameraMetrics } from './api-shared.js'\nimport type { RunnerLocalLoad } from './pipeline-runner-capability.js'\nimport type { BrokerStats } from './stream-broker.js'\nimport type { PipelineEngineChoice } from '../types/pipeline.js'\nimport type { NodeProcess, SystemResourceSnapshot } from './metrics-provider.js'\n\n// ---------------------------------------------------------------------------\n// Event source\n// ---------------------------------------------------------------------------\n\nexport interface EventSource {\n /** Primary source type: 'core', 'addon', 'device', 'pipeline', etc. */\n type: string\n /** Primary identifier — addonId when type='addon', deviceId when type='device', etc. */\n id: string | number\n /** Agent/node that originated the event (e.g. 'hub', 'agent-a1b2c3'). */\n nodeId?: string\n /** Addon that originated the event (populated even when type='device'). */\n addonId?: string\n /** Device the event relates to (populated even when type='addon'). */\n deviceId?: number\n}\n\n// ---------------------------------------------------------------------------\n// Detection event payloads\n// ---------------------------------------------------------------------------\n\n// MotionAnalysisRegion removed (2026-04-14) — consumers now use\n// `MotionRegion` from the motion-detection capability directly. The two\n// interfaces had identical shapes; keeping both was a duplication leak.\n\n/**\n * One detection entry emitted by a camera's firmware (not by the local\n * AI pipeline). Carried inside `detection.camera-native` events —\n * native providers fan out to this category for each ring/motion/AI\n * alarm push they receive from the camera.\n */\nexport interface CameraNativeDetection {\n /**\n * Firmware-reported class. Loose string to accommodate heterogeneous\n * firmwares (Reolink uses 'people'/'vehicle'/'animal'/…; ONVIF uses\n * 'PeopleDetect'; etc.). Common values: 'motion', 'person', 'vehicle',\n * 'animal', 'face', 'package', 'other', 'doorbell'.\n */\n readonly class: string\n /** Ms epoch when the server observed the push from the camera. */\n readonly timestamp: number\n /** Optional firmware-provided confidence [0..1]. Most firmwares don't expose it. */\n readonly confidence?: number\n /** Optional bbox [x, y, w, h] normalised [0..1]. Rare on native events. */\n readonly bbox?: readonly [number, number, number, number]\n}\n\nexport interface MotionAnalysisPayload {\n detected: boolean\n regionCount: number\n regions: readonly MotionRegion[]\n frameWidth: number\n frameHeight: number\n analysisMs: number\n [key: string]: unknown\n}\n\n/**\n * Motion phase transition payload. Single source of truth lives in\n * `motion.cap.ts` as `MotionOnMotionChangedDataSchema` — this alias\n * just re-exports the inferred type with the `[key: string]: unknown`\n * loose-index pattern the bus uses for forward-compat. Don't add\n * fields here; extend the schema in motion.cap.ts.\n */\nexport type MotionOnMotionChangedPayload =\n import('../capabilities/motion.cap.js').MotionOnMotionChangedData\n & { [key: string]: unknown }\n\n/** Raw motion zone from CCL (before minArea filter). */\nexport interface MotionRawZone {\n readonly bbox: readonly [number, number, number, number] // [x1, y1, x2, y2]\n readonly pixelCount: number\n readonly changeScore: number // 0-1 intensity\n}\n\n/** All raw motion zones — emitted for UI debug overlay. */\nexport interface MotionZonesRawPayload {\n readonly deviceId: number\n readonly zones: readonly MotionRawZone[]\n readonly frameSize: { readonly width: number; readonly height: number }\n readonly timestamp: number\n [key: string]: unknown\n}\n\nexport interface DetectionResultPayload {\n /**\n * The post-processed frame result the frontend consumes. Already\n * contains `width`/`height` + `detections[]` + optional debug. No\n * pipeline-raw wrapper — the new `FrameResult` shape IS the payload.\n */\n frame: FrameResult\n /** Events emitted by the analysis pipeline (tracking, scene state, face rec). */\n analysisResults: readonly unknown[]\n [key: string]: unknown\n}\n\n/**\n * Raw inference output emitted by `addon-pipeline-runner` per detection\n * frame. Carries the FrameResult produced by the runner — the hub-side\n * wiring service subscribes, applies the analysis pipeline +\n * notifications, and re-emits as `detection.result`.\n */\nexport interface PipelineInferenceResultPayload {\n readonly deviceId: number\n readonly frame: FrameResult\n /** The Moleculer node id of the runner that produced this result. */\n readonly nodeId: string\n readonly [key: string]: unknown\n}\n\n/**\n * Live per-audio-chunk output emitted by the pipeline-orchestrator for\n * each camera with audio analysis enabled. Mirrors `PipelineInferenceResultPayload`\n * but for audio: carries the canonical `AudioResult` produced by running\n * the audio-analyzer + audio-classifier inline on the hub.\n *\n * Replaces the legacy split `detection.audio.level` /\n * `detection.audio.classification` events — consumers should read\n * `frame.level` for dBFS/RMS and `frame.detections` for classifier\n * matches (both on the same single event).\n */\nexport interface PipelineAudioInferenceResultPayload {\n readonly deviceId: number\n readonly frame: AudioResult\n /** Node id of the host that ran the analyzer — always `'hub'` today\n * because audio runs inline on the hub, but the field is kept so the\n * event shape matches `PipelineInferenceResultPayload`. */\n readonly nodeId: string\n readonly [key: string]: unknown\n}\n\n/**\n * Camera assignment lifecycle payload emitted by `addon-pipeline-orchestrator`\n * when a camera is assigned to or unassigned from an agent runner. Used by\n * the UI / metrics dashboard. No frame data.\n */\nexport interface PipelineCameraAssignmentPayload {\n readonly deviceId: number\n readonly agentNodeId: string\n /** True when the assignment was set manually (preferredAgent setting), false when chosen by the load balancer. */\n readonly pinned: boolean\n readonly reason: 'manual' | 'capacity' | 'hardware-affinity' | 'failover' | 'rebalance' | 'unassigned'\n readonly [key: string]: unknown\n}\n\n/**\n * Capability binding change payload emitted whenever an operator picks\n * a different addon to implement a capability on a node. Each kernel\n * process subscribes to this event and updates its local\n * `preferredProviderRegistry` so subsequent capability lookups return\n * the newly chosen provider.\n *\n * The event lives under `capability.binding-changed` because binding\n * is a kernel-level concept used by any cap provider (not just the\n * pipeline flow). It was renamed from `pipeline.binding-changed` to\n * reflect that broader scope.\n */\nexport interface CapabilityBindingChangedPayload {\n readonly nodeId: string\n readonly capName: string\n /** New preferred addon id, or null when the binding was cleared. */\n readonly addonId: string | null\n readonly [key: string]: unknown\n}\n\nexport interface PhaseTransitionPayload {\n deviceId: number\n from: 'watching' | 'active'\n to: 'watching' | 'active'\n reason: 'motion_detected' | 'cooldown_expired'\n /** Motion source that armed the cooldown — present on both ON and OFF. */\n source?: 'onboard' | 'analyzer'\n /** Cooldown window in ms that was/is active for this transition. */\n cooldownMs?: number\n /** Ms epoch of the transition. */\n timestamp?: number\n [key: string]: unknown\n}\n\n// Legacy `AudioLevelPayload` / `AudioClassificationPayload` were removed\n// when the live audio path was migrated to the canonical `AudioResult`\n// shape — consumers now subscribe to `pipeline.audio-inference-result`\n// and read level + detections from the single payload.\n\n// ---------------------------------------------------------------------------\n// System readiness protocol — see docs/superpowers/specs/2026-04-19-system-readiness-design.md\n// ---------------------------------------------------------------------------\n\n/**\n * Scope of a readiness transition — the \"who does this ready/down apply to\".\n *\n * Two shapes today:\n * - `global` for singletons that are cluster-wide (there is only one hub).\n * - `node` for per-process providers (one readiness per broker nodeID).\n *\n * `device` scope covers per-device native caps registered via\n * `DeviceContext.registerNativeCap(cap, provider)`. Consumers of cross-\n * process per-device caps (stream-broker, orchestrator, snapshot, webrtc)\n * `awaitReady(capName, {type:'device', deviceId})` before calling the\n * native-cap bridge so they don't race the service-advertise window.\n * On worker disconnect the hub-side registry's `agent.offline` demux\n * synthesises `down` for every device-scoped cap whose `sourceNodeId`\n * matches the offline node — same pattern as node-scoped synthesis.\n */\nexport type ReadinessScope =\n | { readonly type: 'global' }\n | { readonly type: 'node'; readonly nodeId: string }\n | { readonly type: 'device'; readonly deviceId: number }\n\n/** States a capability provider transitions through. */\nexport type ReadinessState = 'starting' | 'ready' | 'down'\n\n/**\n * Payload for `system.ready-state` events.\n *\n * `generation` is a per-process random identifier that stays constant\n * for the producer's lifetime and changes whenever the producer process\n * is restarted. The kernel's local `ReadinessRegistry` derives a\n * monotonic `epoch` per `(capName, scope)` by counting generation\n * transitions — emitters never stamp epoch themselves. Consumers that\n * hold long-lived state compare `transition.epoch > lastSeenEpoch` to\n * decide whether to rebuild.\n *\n * Same-generation repeated `ready` events are idempotent keepalives\n * (e.g. re-emitted on every `$node.connected`) — the registry keeps\n * epoch stable.\n */\nexport interface SystemReadyStatePayload {\n readonly capName: string\n readonly scope: ReadinessScope\n readonly state: ReadinessState\n /** Per-process random id; changes on producer restart. */\n readonly generation: string\n readonly sourceNodeId: string\n readonly ts: number\n readonly [key: string]: unknown\n}\n\n// ---------------------------------------------------------------------------\n// Cluster topology snapshot shape — matches `nodes.topology` cap output\n// ---------------------------------------------------------------------------\n\n/** One service entry inside a topology process — addon + capabilities. */\nexport interface TopologyService {\n readonly addonId: string\n readonly capabilities: readonly string[]\n readonly status: string\n}\n\n/** One process under a topology node (main process + isolated workers). */\nexport interface TopologyProcess {\n readonly pid: number\n readonly name: string\n readonly state: string\n readonly cpuPercent: number\n readonly memoryRss: number\n readonly uptimeSeconds: number\n readonly services: readonly TopologyService[]\n readonly groupId?: string\n}\n\n/** Top-level cluster node — hub or remote agent. */\nexport interface TopologyNode {\n readonly id: string\n readonly name: string\n readonly hostname: string\n readonly platform: string\n readonly arch: string\n readonly cpuModel: string | null\n readonly cpuCores: number\n readonly memoryMB: number\n readonly engines: readonly string[]\n readonly isHub: boolean\n readonly isOnline: boolean\n readonly cpuPercent: number\n readonly memoryPercent: number\n readonly uptime: number\n readonly lastSeen: string\n /** Local IP addresses visible on the network (non-internal, IPv4/IPv6). */\n readonly localIps: readonly string[]\n readonly addons: ReadonlyArray<{ readonly id: string; readonly capabilities: readonly string[]; readonly status: string }>\n readonly processes: readonly TopologyProcess[]\n}\n\n// ---------------------------------------------------------------------------\n// Known event catalog — typed category → payload mapping\n// ---------------------------------------------------------------------------\n\n/** All known event categories with their typed payloads */\nexport interface EventCatalog {\n // ── System ──────────────────────────────────────────────────────────────\n 'system.boot': { mode: string }\n 'system.addons-ready': { activeAddons: readonly string[] }\n 'system.restarting': Record<string, never>\n 'system.ready-state': SystemReadyStatePayload\n\n // ── Addon lifecycle ─────────────────────────────────────────────────────\n 'addon.started': { addonId: string; packageName?: string; packageVersion?: string; agent?: string }\n 'addon.stopped': { addonId: string; packageName?: string; packageVersion?: string; agent?: string }\n 'addon.restarted': { addonId: string; packageName?: string; packageVersion?: string; agent?: string }\n 'addon.updated': { addonId: string; packageName?: string; packageVersion?: string; agent?: string; fromVersion?: string; toVersion?: string }\n 'addon.installed': { addonId: string; packageName?: string; packageVersion?: string; agent?: string }\n 'addon.uninstalled': { addonId: string; packageName?: string; packageVersion?: string; agent?: string }\n 'addon.crashed': { addonId: string; packageName?: string; packageVersion?: string; agent?: string; error?: string }\n 'addon.error': { addonId: string; packageName?: string; packageVersion?: string; agent?: string; error?: string; action?: string; phase?: string }\n\n // ── Device ──────────────────────────────────────────────────────────────\n 'device.registered': { deviceId: number; providerId: string; name?: string }\n 'device.unregistered': { deviceId: number; providerId: string }\n 'device.streams-registered': { deviceId: number; providerId: string }\n 'device.streams-unregistered': { deviceId: number }\n 'device.enabled': { deviceId: number; integrationId: string }\n 'device.disabled': { deviceId: number; integrationId: string }\n 'device.settings-updated': { deviceId: number; integrationId: string; keys: string[] }\n /**\n * Device online/offline aggregate. Emitted by the device's owning\n * provider (rtsp / reolink / onvif / frigate) when the union of its\n * stream-broker profile health flips. `online` fires when at least\n * one profile becomes healthy after all were offline; `offline` fires\n * when the last healthy profile goes stale (no packets for\n * STREAM_STALE_TIMEOUT_MS).\n */\n 'device.online': { deviceId: number; providerId: string; profileCount?: number; reason?: string }\n 'device.offline': { deviceId: number; providerId: string; reason?: string }\n 'device.awake': { deviceId: number; providerId: string; reason?: string }\n 'device.sleeping': { deviceId: number; providerId: string; reason?: string }\n /**\n * Emitted whenever the set of (device, capability) native providers or\n * wrapper activations changes. Hub subscribers use this to keep a\n * cross-process view of which addon/node currently serves each cap for\n * each device. `nodeId` is the broker nodeID where the provider lives;\n * 'hub' for hub-local. `addonId` is the provider (native or wrapper).\n */\n 'device.bindings-changed': {\n deviceId: number\n capName: string\n reason: 'native-registered' | 'native-unregistered' | 'wrapper-activated' | 'wrapper-deactivated'\n addonId: string\n nodeId: string\n }\n 'device.meta-changed': {\n deviceId: number\n field: 'name' | 'location' | 'disabled'\n value: string | null | boolean\n }\n\n // ── Integration ────────────────────────────────────────────────────────\n 'integration.enabled': { integrationId: string; addonId: string }\n 'integration.disabled': { integrationId: string; addonId: string }\n\n // ── Provider ────────────────────────────────────────────────────────────\n 'provider.started': { providerId: string; type?: string }\n 'provider.stopped': { providerId: string; reason?: string }\n\n // ── Process ─────────────────────────────────────────────────────────────\n 'process.crashed': { processId: string; exitCode?: number; signal?: string }\n 'process.restart_scheduled': { processId: string; delayMs: number }\n 'process.restarted': { processId: string }\n\n // ── Recording ───────────────────────────────────────────────────────────\n 'recording.started': { deviceId: number; streamId?: string }\n 'recording.stopped': { deviceId: number; reason?: string }\n 'recording.error': { deviceId: number; error: string }\n 'recording.health.degraded': { deviceId: number; message: string }\n 'recording.storage.critical': { deviceId: number; usagePercent?: number }\n 'recording.segment.written': { deviceId: number; path?: string; durationSec?: number; sizeMB?: number }\n 'recording.policy.fallback': { deviceId: number; reason: string }\n 'recording.retention.completed': { deletedCount?: number; freedMB?: number }\n\n // ── Detection & analysis ────────────────────────────────────────────────\n 'detection.event': { deviceId: number; detections?: unknown[]; [key: string]: unknown }\n 'detection.result': DetectionResultPayload\n 'detection.motion-analysis': MotionAnalysisPayload\n 'detection.motion-zones-raw': MotionZonesRawPayload\n 'motion.on-motion-changed': MotionOnMotionChangedPayload\n /**\n * On-board detection coming straight from the camera firmware (Reolink\n * AI alarms, ONVIF analytics, etc.) — as opposed to `detection.result`\n * which is produced by the pipeline running locally.\n *\n * `source` discriminates the origin when multiple pipelines coexist for\n * the same camera: `'onboard'` for native firmware events, `'camera-native'`\n * kept as an alias for backwards compatibility with pre-Reolink emitters.\n *\n * `detections` is tightened from `unknown[]` to a structured shape. Each\n * entry describes one firmware-reported class ('motion', 'person',\n * 'vehicle', 'animal', 'face', 'package', 'other', …) at a point in time.\n */\n 'detection.camera-native': {\n cameraId: number\n detections: readonly CameraNativeDetection[]\n source: 'onboard' | 'camera-native'\n }\n 'pipeline.audio-inference-result': PipelineAudioInferenceResultPayload\n 'detection.phase-transition': PhaseTransitionPayload\n /**\n * Emitted whenever a per-device override is written or cleared for any\n * pipeline-participating addon (pipeline-orchestrator, motion-wasm,\n * detection-pipeline, audio-analyzer, audio-classifier). The wiring\n * service subscribes to this event and restarts detection for the\n * affected camera so the new values take effect without a full reboot.\n * `addonId` is informational — most consumers only care about\n * `deviceId` and will rebuild the full RunnerCameraConfig anyway.\n */\n 'orchestration.settings-updated': { deviceId: number; addonId?: string }\n 'provider.motion': { active: boolean }\n 'provider.detection': { detections: unknown }\n\n // ── Session tracking ────────────────────────────────────────────────────\n 'session.track.new': { deviceId: number; trackId: string; label?: string }\n 'session.track.expired': { deviceId: number; trackId: string; durationMs?: number }\n\n // ── Benchmark ───────────────────────────────────────────────────────────\n /**\n * Progress from a running benchmark (pipeline image test, decoder perf test,\n * …). The payload is intentionally open (`kind` discriminator + free-form\n * additional fields) so different benchmark shapes share the same bus\n * category and a single UI subscriber can filter by `kind` + `sessionId`.\n */\n 'benchmark.progress': {\n kind: string\n sessionId: string\n phase?: string\n tSec?: number\n sample?: Record<string, unknown>\n message?: string\n }\n\n // ── Pipeline & inference ────────────────────────────────────────────────\n 'pipeline.progress': {\n /** Node that executed the pipeline step. Same value is also set on `event.source.nodeId`\n * but carried in the payload so admin-ui filters don't need to peek at the source shape. */\n nodeId: string\n /** Stable identifier per `runPipeline()` invocation — every progress event from the same\n * run shares this id so the UI can correlate a burst of messages into a single run card. */\n sessionId: string\n step: string\n addonId?: string\n modelId?: string\n ms?: number\n message?: string\n }\n /** Per-frame execution trace emitted by the pipeline executor for live observability. */\n 'pipeline.trace': PipelineExecutionTrace\n /** Raw inference output emitted by addon-pipeline-runner (no frame buffer). */\n 'pipeline.inference-result': PipelineInferenceResultPayload\n /** Camera assigned to an agent runner by addon-pipeline-orchestrator. */\n 'pipeline.camera-assigned': PipelineCameraAssignmentPayload\n /** Camera released from an agent runner. */\n 'pipeline.camera-unassigned': PipelineCameraAssignmentPayload\n /** Per-camera pipeline config changed — orchestrator hot-reloads the assigned runner. */\n 'pipeline.camera-updated': {\n readonly deviceId: number\n readonly config: CameraPipelineConfig\n }\n /**\n * Periodic per-runner load snapshot (~1 Hz). Replaces UI polling on\n * `pipelineRunner.getLocalLoad`. UI subscribes per agent node.\n */\n 'pipeline.runner-load-snapshot': {\n readonly nodeId: string\n readonly load: RunnerLocalLoad\n /** Wall-clock ms when the snapshot was sampled. */\n readonly timestamp: number\n }\n /**\n * Periodic per-camera metrics snapshot (~1 Hz). Replaces UI polling\n * on `pipelineOrchestrator.getCameraMetrics` /\n * `pipelineRunner.getCameraMetrics`. One event per attached camera\n * per tick. UI consumers filter by `deviceId`.\n */\n 'pipeline.camera-metrics-snapshot': {\n readonly deviceId: number\n readonly nodeId: string\n readonly metrics: CameraMetrics\n readonly timestamp: number\n }\n /**\n * Periodic per-broker stats snapshot (~1 Hz). Replaces polling on\n * `streamBroker.getBrokerStats` and `streamBroker.listAllProfileSlots`.\n * Carries the full `BrokerStats` payload so UI consumers can render\n * the panel directly from the event without round-tripping the cap.\n */\n 'stream-broker.metrics-snapshot': {\n readonly brokerId: string\n readonly deviceId: number\n readonly profile: string\n readonly nodeId: string\n readonly stats: BrokerStats\n readonly timestamp: number\n }\n /**\n * Cap event: stream-broker signals that a profile slot now has at\n * least one consumer. Camera-provider addons (Reolink Baichuan push,\n * etc.) start their underlying transport on receipt — see\n * `EventCategory.StreamBrokerOnCamStreamDemand`.\n */\n 'stream-broker.onCamStreamDemand': {\n readonly deviceId: number\n readonly camStreamId: string\n readonly profile: 'high' | 'mid' | 'low'\n }\n /**\n * Cap event: stream-broker signals the last consumer left. Providers\n * tear down their transport — see `EventCategory.StreamBrokerOnCamStreamIdle`.\n */\n 'stream-broker.onCamStreamIdle': {\n readonly deviceId: number\n readonly camStreamId: string\n }\n /**\n * Cap event: device's battery status changed (firmware push or\n * provider-side observation). Mirrors `batteryCapability.onStatusChanged`.\n * Payload shape duplicated inline (rather than importing from\n * `capabilities/battery.cap`) to avoid a cycle between the cap\n * definitions and the event bus typing — they're kept in lock-step\n * by hand (see also `BatteryStatus` in `capabilities/battery.cap.ts`).\n */\n 'battery.onStatusChanged': {\n readonly deviceId: number\n readonly status: {\n readonly percentage: number\n readonly charging: 'dc' | 'solar' | 'none'\n readonly sleeping: boolean\n readonly lastUpdated: number\n }\n }\n /**\n * Doorbell button press — emitted by every device that registers the\n * `doorbell` capability. Mirrors `doorbellCapability.events.onPressed`.\n * Subscribers (UI toast, advanced-notifier) react to physical rings\n * without holding a cap reference.\n */\n 'doorbell.onPressed': {\n readonly deviceId: number\n readonly timestamp: number\n }\n /**\n * Periodic per-node engine inventory snapshot (~0.2 Hz). Replaces\n * polling on `pipelineExecutor.listLoadedEngines`. One event per\n * detection-pipeline process per tick, carrying every loaded engine\n * with the same shape the cap returns.\n */\n 'pipeline.engine-metrics-snapshot': {\n readonly nodeId: string\n readonly engines: ReadonlyArray<{\n readonly engineKey: string\n readonly engine: PipelineEngineChoice\n readonly modelsLoaded: readonly string[]\n readonly inUseByCameras: readonly number[]\n readonly kind: 'runtime' | 'warm-override'\n readonly poolPid: number | null\n readonly idleMs: number | null\n readonly idleTtlMs: number | null\n }>\n readonly timestamp: number\n }\n /**\n * Cluster topology snapshot — same payload shape that\n * `nodes.topology` returns. Emitted by the hub on any agent /\n * addon lifecycle change (debounced ~200ms) plus a periodic safety\n * net. UI dashboards subscribe to drive their cluster view\n * directly from the event payload — zero round-trip.\n */\n 'cluster.topology-snapshot': {\n readonly nodes: ReadonlyArray<TopologyNode>\n readonly timestamp: number\n }\n /**\n * Periodic per-node system metrics snapshot. Carries the full\n * `SystemResourceSnapshot` returned by\n * `metricsProvider.getCached()` so dashboards render CPU /\n * memory / GPU / disk / network without polling.\n */\n 'metrics.node-resources-snapshot': {\n readonly nodeId: string\n readonly snapshot: SystemResourceSnapshot\n readonly timestamp: number\n }\n /**\n * Periodic per-node process-tree snapshot (`NodeProcess[]` —\n * camstack-related pids with ghost / managed / root classification).\n * One event per node per tick. Replaces polling on\n * `metricsProvider.listNodeProcesses`.\n */\n 'metrics.node-processes-snapshot': {\n readonly nodeId: string\n readonly processes: ReadonlyArray<NodeProcess>\n readonly timestamp: number\n }\n /**\n * Per-agent hwaccel override changed (set / cleared / re-probed). Payload\n * carries the new effective preference so UI consumers can toast or\n * refresh their pipeline view without re-fetching. Decoders still pull\n * from `pipeline-orchestrator.getAgentSettings` on session creation;\n * this event is additive observability.\n */\n 'pipeline.agent-hwaccel-changed': {\n readonly agentNodeId: string\n readonly userChoice: 'auto' | 'none' | string | null\n readonly probedBest: string\n readonly reason: 'user-set' | 'user-cleared' | 'reprobed' | 'seeded'\n }\n /** Capability binding changed for a node — kernels update their preferred-provider registry. */\n 'capability.binding-changed': CapabilityBindingChangedPayload\n 'model.download.progress': { modelId: string; progress: number; totalMB?: number }\n\n // ── Agent ───────────────────────────────────────────────────────────────\n 'agent.online': { agentId: string }\n 'agent.offline': { agentId: string }\n 'worker.online': { workerId: string }\n 'worker.offline': { workerId: string }\n 'agent.task.assigned': { agentId: string; taskId: string; taskType: string; payload?: unknown }\n\n // ── Enrichment ──────────────────────────────────────────────────────────\n 'enrichment.embedding.stored': {\n readonly deviceId: number\n readonly trackId: string\n readonly class: string\n readonly embeddingId: string\n readonly modelId: string\n readonly embeddingDim: number\n readonly inferenceMs: number\n readonly timestamp: number\n }\n 'enrichment.scene.state-changed': {\n readonly deviceId: number\n readonly monitorId: string\n readonly monitorLabel: string\n readonly previousState: string\n readonly currentState: string\n readonly confidence: number\n readonly timestamp: number\n }\n 'enrichment.activity.summary': {\n readonly deviceId: number\n readonly periodStart: number\n readonly periodEnd: number\n readonly objectCounts: Readonly<Record<string, number>>\n readonly zoneActivity: readonly { readonly zoneId: string; readonly entries: number; readonly exits: number; readonly avgDwellMs: number }[]\n readonly stateChanges: readonly { readonly monitorId: string; readonly from: string; readonly to: string; readonly timestamp: number }[]\n readonly activityLevel: 'none' | 'low' | 'medium' | 'high'\n }\n\n // ── Alerts ──────────────────────────────────────────────────────────────\n 'alert.created': { id: string; category: string; severity: string; title: string; status: string }\n 'alert.updated': { alertId: string; patch: Record<string, unknown> }\n\n // ── Stream broker health ────────────────────────────────────────────────\n /**\n * Per-broker stream health transition. Fired by the stream-broker\n * watchdog. `camStreamId` is the canonical identity of the source\n * (every broker is keyed by its cam stream now); `profile` is the\n * profile slot currently bound to it (or `null` if the broker is\n * only kept alive by a manual activation).\n */\n 'stream.online': {\n readonly deviceId: number\n readonly camStreamId: string\n readonly profile: 'high' | 'mid' | 'low' | null\n readonly brokerId: string\n readonly sourceType: string\n readonly lastPacketAt: number\n readonly reason?: string\n }\n 'stream.offline': {\n readonly deviceId: number\n readonly camStreamId: string\n readonly profile: 'high' | 'mid' | 'low' | null\n readonly brokerId: string\n readonly sourceType: string\n readonly lastPacketAt: number\n readonly reason?: string\n }\n\n // ── Retention ───────────────────────────────────────────────────────────\n 'retention.cleanup': {\n readonly deletedEvents?: number\n readonly deletedAudioRecords?: number\n readonly deletedSnapshots?: number\n readonly deletedCount?: number\n readonly freedMB?: number\n }\n}\n\n/** All known event category strings (derived from EventCatalog keys) */\nexport type KnownEventCategory = keyof EventCatalog\n\n// ---------------------------------------------------------------------------\n// SystemEvent — backward-compatible base type\n// ---------------------------------------------------------------------------\n\n/**\n * System event — `data` is `Record<string, unknown>` for backward compatibility.\n * Use `TypedSystemEvent<C>` or `narrowEvent()` for typed access to data.\n */\nexport interface SystemEvent {\n id: string\n timestamp: Date\n source: EventSource\n category: string\n data: Record<string, unknown>\n /**\n * Propagation chain when the event was re-emitted by the\n * `DeviceEventPropagator`. `via[0]` is the originating source (the\n * child that produced the event); subsequent entries walk up the\n * parent chain. Absent on the original emission and on events that\n * don't belong to a device-rooted source.\n *\n * Consumers that want ONLY direct events filter `ev.via === undefined`.\n * Consumers that want everything reaching a device (incl. children)\n * filter by `source.id === parentId` and accept any `via`.\n */\n via?: readonly EventSource[]\n}\n\n/**\n * A system event with a known category — `data` is typed.\n * Use this when you know the exact category at compile time.\n */\nexport type TypedSystemEvent<C extends KnownEventCategory> = {\n id: string\n timestamp: Date\n source: EventSource\n category: C\n data: EventCatalog[C]\n via?: readonly EventSource[]\n}\n\n// ---------------------------------------------------------------------------\n// Event filter\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Event filter — accepts known categories (strict) or strings (globs/custom)\n// ---------------------------------------------------------------------------\n\n/** A category filter value: a known category, a glob string, or an array of either */\nexport type EventCategoryFilter = KnownEventCategory | (string & {}) | readonly (KnownEventCategory | (string & {}))[]\n\nexport interface EventFilter {\n source?: EventSource\n /** Filter by top-level agent (e.g. 'hub', 'agent-a1b2c3'). Matches source.nodeId with prefix match so 'hub' includes 'hub/pipeline'. Same semantics as logs tags.agentId. */\n agentId?: string\n /** Filter by addon that originated the event. Matches source.addonId or source.id when type='addon'. */\n addonId?: string\n /** Filter by device the event relates to. Matches source.deviceId or source.id when type='device'. */\n deviceId?: number\n /** Category — known category (typed), glob (e.g. 'device.*'), or array */\n category?: EventCategoryFilter\n since?: Date\n}\n\n// ---------------------------------------------------------------------------\n// Typed event filter — for subscribe overloads\n// ---------------------------------------------------------------------------\n\nexport interface TypedEventFilter<C extends KnownEventCategory> {\n source?: EventSource\n category: C | readonly C[]\n since?: Date\n}\n\n// ---------------------------------------------------------------------------\n// IEventBus — emit and subscribe are typed when using known categories\n// ---------------------------------------------------------------------------\n\nexport interface IEventBus {\n /** Emit a known-category event with typed payload */\n emit<C extends KnownEventCategory>(event: TypedSystemEvent<C>): void\n /** Emit an arbitrary event (custom/unknown category) */\n emit(event: SystemEvent): void\n\n /** Subscribe to a known category — handler receives typed event */\n subscribe<C extends KnownEventCategory>(filter: TypedEventFilter<C>, handler: (event: TypedSystemEvent<C>) => void): () => void\n /** Subscribe with a generic filter — handler receives base SystemEvent */\n subscribe(filter: EventFilter, handler: (event: SystemEvent) => void): () => void\n\n /** Get recent events */\n getRecent(filter?: EventFilter, limit?: number): readonly SystemEvent[]\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Narrow a SystemEvent to a typed event by checking its category.\n * Returns `true` (and narrows the type) if the category matches.\n *\n * @example\n * ```typescript\n * eventBus.subscribe({ category: 'addon.started' }, (event) => {\n * if (isEvent(event, 'addon.started')) {\n * event.data.addonId // ✓ typed as string\n * }\n * })\n * ```\n */\nexport function isEvent<C extends KnownEventCategory>(\n event: SystemEvent,\n category: C,\n): event is TypedSystemEvent<C> {\n return event.category === category\n}\n\n/**\n * Create a typed event with less boilerplate.\n *\n * @example\n * ```typescript\n * eventBus.emit(createEvent('addon.started', { type: 'addon', id: 'pipeline' }, {\n * addonId: 'pipeline',\n * packageVersion: '0.1.8',\n * }))\n * ```\n */\nexport function createEvent<C extends KnownEventCategory>(\n category: C,\n source: EventSource,\n data: EventCatalog[C],\n): TypedSystemEvent<C> {\n return {\n id: typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2),\n timestamp: new Date(),\n source,\n category,\n data,\n }\n}\n\n// ---------------------------------------------------------------------------\n// System readiness protocol — low-level producer emission helper.\n// Consumers (kernel `ReadinessRegistry`) and their `awaitReady` /\n// `onReadyState` APIs live in `@camstack/kernel/src/readiness/`.\n// ---------------------------------------------------------------------------\n\n/**\n * Emit a `system.ready-state` event for a capability. Caller supplies a\n * per-process `generation` string — stable across a single process\n * lifetime, changes on restart — which consumer-side registries use to\n * derive a monotonic `epoch` without requiring the emitter to\n * coordinate.\n */\nexport function emitReadiness(\n bus: IEventBus,\n params: {\n readonly capName: string\n readonly scope: ReadinessScope\n readonly state: ReadinessState\n readonly generation: string\n readonly sourceNodeId: string\n readonly ts?: number\n },\n): void {\n const ts = params.ts ?? Date.now()\n bus.emit(createEvent(\n 'system.ready-state',\n { type: 'capability', id: params.capName, nodeId: params.sourceNodeId },\n {\n capName: params.capName,\n scope: params.scope,\n state: params.state,\n generation: params.generation,\n sourceNodeId: params.sourceNodeId,\n ts,\n },\n ))\n}\n","import type { AddonContext, AddonInitResult, CapabilitiesAccess, ProviderRegistration } from '../interfaces/addon.js'\nimport { EventCategory } from '../enums/event-category.js'\nimport type {\n ConfigUISchema, ConfigUISchemaWithValues,\n ConfigField, ConfigSection,\n} from '../interfaces/config-ui.js'\nimport { hydrateSchema } from '../interfaces/config-ui.js'\nimport { emitReadiness } from '../interfaces/event-bus.js'\n\nexport type { CapabilitiesAccess }\n\n// ── Typed field helper ──────────────────────────────────────────────────\n/**\n * Utility type that constrains a ConfigField's `key` to only accept\n * keys of TConfig. Usage inside schema builders:\n *\n * ```ts\n * protected globalSettingsSchema() {\n * return this.schema({\n * sections: [{\n * id: 'main',\n * title: 'Settings',\n * fields: [\n * this.field({ type: 'number', key: 'maxRetries', label: 'Max Retries' }),\n * // ^^^^^^^^^^^ — autocomplete + compile error if wrong\n * ],\n * }],\n * })\n * }\n * ```\n */\ntype TypedConfigField<TConfig extends object> =\n ConfigField extends infer F\n ? F extends { readonly key: string }\n ? Omit<F, 'key'> & { readonly key: keyof TConfig & string }\n : F // valueless fields (separator, info) pass through\n : never\n\n/**\n * A ConfigSection whose fields are key-constrained to TConfig.\n */\ninterface TypedConfigSection<TConfig extends object> extends Omit<ConfigSection, 'fields'> {\n readonly fields: ReadonlyArray<TypedConfigField<TConfig> | ConfigField>\n}\n\n/**\n * A ConfigUISchema whose sections use typed fields.\n */\ninterface TypedConfigUISchema<TConfig extends object> extends Omit<ConfigUISchema, 'sections'> {\n readonly sections: ReadonlyArray<TypedConfigSection<TConfig>>\n}\n\n// ── BaseAddon ───────────────────────────────────────────────────────────\n\n/**\n * Base class for CamStack addons. Eliminates settings boilerplate:\n *\n * - Typed `config` property with automatic resolution from store + defaults\n * - `getGlobalSettings()` / `updateGlobalSettings()` auto-implemented\n * - `getAddonSettings()` / `updateAddonSettings()` auto-implemented\n * - `getDeviceSettings()` / `updateDeviceSettings()` auto-implemented\n * - `ctx` accessor for the AddonContext (no manual `this.ctxRef` storage)\n * - `field()` helper that constrains field keys to TConfig keys\n * - `schema()` helper that validates the full schema structure\n *\n * Subclasses override:\n * - `onInitialize()` — addon-specific init logic, return ProviderRegistration[]\n * - `onShutdown()` — cleanup\n * - `globalSettingsSchema()` / `deviceSettingsSchema()` — UI schemas\n *\n * @example\n * ```ts\n * interface MyConfig {\n * maxRetries: number\n * endpoint: string\n * }\n *\n * export default class MyAddon extends BaseAddon<MyConfig> {\n * constructor() {\n * super({ maxRetries: 3, endpoint: 'http://localhost' })\n * }\n *\n * protected globalSettingsSchema() {\n * return this.schema({\n * sections: [{\n * id: 'main', title: 'Settings',\n * fields: [\n * this.field({ type: 'number', key: 'maxRetries', label: 'Max Retries', default: 3 }),\n * this.field({ type: 'text', key: 'endpoint', label: 'Endpoint' }),\n * ],\n * }],\n * })\n * }\n *\n * protected async onInitialize(): Promise<ProviderRegistration[]> {\n * const client = new Client(this.config.endpoint, this.config.maxRetries)\n * return [{ capability: 'my-feature', provider: client }]\n * }\n * }\n * ```\n */\nexport abstract class BaseAddon<TConfig extends object = Record<string, unknown>> {\n private _ctx: AddonContext | null = null\n private _config: TConfig\n /**\n * Per-process random id used as the `generation` stamp on every\n * `system.ready-state` event this addon emits. Constant for the\n * lifetime of this addon instance (== one process boot); consumer-\n * side registries derive a monotonic `epoch` by watching for\n * generation transitions.\n */\n private readonly _readinessGeneration: string =\n typeof crypto !== 'undefined' && crypto.randomUUID\n ? crypto.randomUUID()\n : Math.random().toString(36).slice(2, 14)\n /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */\n private _registeredCapNames: readonly string[] = []\n\n /** Default config values. Provided via constructor. */\n protected readonly defaults: TConfig\n\n constructor(defaults: TConfig) {\n this.defaults = defaults\n this._config = { ...defaults }\n }\n\n /**\n * Override to opt out of the automatic `system.ready-state` emission.\n * Returns `true` by default — addons that don't map cleanly to the\n * readiness protocol (e.g. pure collection providers whose readiness\n * is already reported per-device by upstream) can override to `false`\n * and emit manually.\n */\n protected get autoEmitReadiness(): boolean { return true }\n\n // ── Public accessors ──────────────────────────────────────────────────\n\n /** The AddonContext, available after initialize(). */\n get ctx(): AddonContext {\n if (!this._ctx) throw new Error(`${this.constructor.name}: ctx accessed before initialize()`)\n return this._ctx\n }\n\n /**\n * Non-throwing ctx accessor for code paths that can legitimately run\n * before `initialize()` has resolved — most commonly cap handlers the\n * hub invokes eagerly at page load (device-details aggregator pings\n * every addon's `getDeviceSettingsContribution` as soon as the page\n * mounts). Prefer `ctx` for the normal case; reach for this only in\n * capability methods that may be queried before the addon is wired up.\n */\n protected get ctxIfReady(): AddonContext | null {\n return this._ctx\n }\n\n /** Current resolved config (defaults merged with persisted store values). */\n get config(): Readonly<TConfig> {\n return this._config\n }\n\n // ── ICamstackAddon lifecycle ──────────────────────────────────────────\n\n async initialize(context: AddonContext): Promise<AddonInitResult | void> {\n this._ctx = context\n await this.resolveConfig()\n const result = await this.onInitialize()\n this.emitLifecycle(EventCategory.AddonStarted)\n const normalized = normalizeAddonInitResult(result)\n const providers: readonly ProviderRegistration[] =\n normalized && 'providers' in normalized && normalized.providers ? normalized.providers : []\n this._registeredCapNames = providers.map(p => p.capability.name)\n return normalized\n }\n\n /**\n * Called by the isolated-process runner AFTER `broker.start()`.\n * In-process addons never need this because the hub broker is already\n * running when `initialize()` fires. For forked children the broker\n * starts AFTER `initialize()`, so the readiness emit inside initialize()\n * fires before the broker can broadcast it. This method re-emits the\n * ready state once the transport is live.\n */\n postBrokerStart(): void {\n if (this.autoEmitReadiness && this._registeredCapNames.length > 0) {\n this.emitReadinessForProviders('ready')\n }\n }\n\n /**\n * Called by the isolated-process runner / agent bootstrap once the broker\n * has started AND the hub node is connected — the moment `ctx.api.*` calls\n * to hub-provided capabilities become safe. Wrap any bootstrap work that\n * must query the hub (device restore, settings fetch, initial sync) in\n * `onHubConnected()` rather than `onInitialize()` — during `initialize()`\n * on a worker the broker is not yet connected and remote cap calls either\n * time out or throw \"Service not found\".\n *\n * Default implementation is a no-op. Override in subclasses that need it.\n * Never fires on hub-local in-process addons (the hub is its own node).\n */\n async onHubReachable(): Promise<void> {\n // no-op — override in subclasses\n }\n\n async shutdown(): Promise<void> {\n this.emitLifecycle(EventCategory.AddonStopped)\n if (this.autoEmitReadiness) this.emitReadinessForProviders('down')\n await this.onShutdown()\n for (const unsub of this._subscriptions) unsub()\n this._subscriptions = []\n this._ctx = null\n }\n\n // ── Subclass hooks ────────────────────────────────────────────────────\n\n /**\n * Addon-specific initialization. Called after config is resolved.\n *\n * Subclasses may return:\n * - an array of `ProviderRegistration` (back-compat, most common),\n * - an `AddonInitResult` envelope to also declare `customActions`,\n * - `void` for pure consumers.\n */\n protected abstract onInitialize(): Promise<ProviderRegistration[] | AddonInitResult | void>\n\n /** Addon-specific cleanup. Override if needed. */\n protected async onShutdown(): Promise<void> { /* no-op by default */ }\n\n /**\n * Called after config is resolved during updateGlobalSettings/updateAddonSettings.\n * Override to react to config changes (e.g. restart a sampler, reconnect a service).\n * Not called during initialize() — use onInitialize() for initial setup.\n */\n protected async onConfigChanged(): Promise<void> { /* no-op by default */ }\n\n // ── Schema helpers (typed) ────────────────────────────────────────────\n\n /**\n * Create a ConfigField with `key` constrained to keys of TConfig.\n * Provides autocomplete and compile-time validation.\n */\n protected field<F extends ConfigField>(\n field: F extends { readonly key: string }\n ? Omit<F, 'key'> & { readonly key: keyof TConfig & string }\n : F\n ): ConfigField {\n return field as ConfigField\n }\n\n /**\n * Create a full ConfigUISchema with typed sections.\n * Fields created via `this.field()` get key validation automatically.\n */\n protected schema(schema: TypedConfigUISchema<TConfig>): ConfigUISchema {\n return schema as unknown as ConfigUISchema\n }\n\n // ── Settings schemas (override to provide UI) ─────────────────────────\n\n /** Override to provide global-level settings UI schema. */\n protected globalSettingsSchema(): ConfigUISchema | null { return null }\n\n /** Override to provide device-level settings UI schema. */\n protected deviceSettingsSchema(): ConfigUISchema | null { return null }\n\n // ── Two-level settings API (auto-implemented) ────────────────────────\n //\n // Note: the former three-level split (addon / global / device) collapsed\n // to two. `addon` and `global` both wrote to the same `writeAddonStore`\n // blob and every addon used exactly one of them; the distinction was\n // never semantically load-bearing. `global` won because it was the\n // widely-used one and the name reads naturally (per-node addon config).\n\n async getGlobalSettings(overlay?: Record<string, unknown>): Promise<ConfigUISchemaWithValues> {\n const schema = this.globalSettingsSchema()\n if (!schema) return { sections: [] }\n const raw = (await this._ctx?.settings?.readAddonStore()) ?? {}\n return hydrateSchema(schema, overlay ? { ...raw, ...overlay } : raw)\n }\n\n async updateGlobalSettings(patch: Partial<TConfig>): Promise<void> {\n await this._ctx?.settings?.writeAddonStore(patch as Record<string, unknown>)\n await this.resolveConfig()\n await this.onConfigChanged()\n this.emitLifecycle(EventCategory.AddonUpdated, { level: 'global' })\n this.maybeAutoRestart(patch, this.globalSettingsSchema())\n }\n\n /**\n * If any field in `patch` is marked `requiresRestart` in `schema`,\n * schedule an addon restart for the next tick. Deferred via\n * `setImmediate` so the tRPC mutation that triggered the write has\n * time to return its response before the addon is torn down and\n * re-initialised by `AddonRegistryService.restartAddon`.\n */\n private maybeAutoRestart(\n patch: Partial<TConfig>,\n schema: ConfigUISchema | null,\n ): void {\n if (!schema) return\n const restartKeys = new Set<string>()\n for (const section of schema.sections) {\n for (const field of section.fields) {\n if (field.type === 'separator' || field.type === 'info') continue\n if ((field as { readonly requiresRestart?: boolean }).requiresRestart) {\n restartKeys.add((field as { readonly key: string }).key)\n }\n }\n }\n if (restartKeys.size === 0) return\n const changed = Object.keys(patch).some((k) => restartKeys.has(k))\n if (!changed) return\n const ctx = this._ctx\n if (!ctx) return\n const addonId = ctx.id\n setImmediate(() => {\n const api = ctx.api as unknown as {\n addons?: { restartAddon?: { mutate: (input: { addonId: string }) => Promise<unknown> } }\n }\n api.addons?.restartAddon?.mutate({ addonId })\n .then(() => {\n ctx.logger.info('addon auto-restart triggered by restart-required setting change', {\n meta: { changedFields: Object.keys(patch).filter((k) => restartKeys.has(k)) },\n })\n })\n .catch((err: unknown) => {\n ctx.logger.error('addon auto-restart failed', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n })\n }\n\n async getDeviceSettings(deviceId: number): Promise<ConfigUISchemaWithValues> {\n const schema = this.deviceSettingsSchema()\n if (!schema) return { sections: [] }\n const raw = (await this._ctx?.settings?.readDeviceStore(deviceId)) ?? {}\n return hydrateSchema(schema, raw)\n }\n\n async updateDeviceSettings(deviceId: number, patch: Record<string, unknown>): Promise<void> {\n await this._ctx?.settings?.writeDeviceStore(deviceId, patch)\n }\n\n // ── Event subscriptions (auto-cleanup on shutdown) ────────────────────\n\n private _subscriptions: Array<() => void> = []\n\n /**\n * Subscribe to an event bus category. The subscription is automatically\n * unsubscribed on shutdown — no manual cleanup needed.\n *\n * @example\n * ```ts\n * this.subscribe({ category: EventCategory.DeviceRegistered }, (event) => {\n * void this.handleDevice(event)\n * })\n * ```\n */\n /**\n * Subscribe to `system.ready-state` events for one or more capabilities.\n * Abstracts the boilerplate type-narrowing + nodeId extraction that every\n * resilience subscription duplicates. Cleanup is automatic (registered on\n * `_subscriptions` like a normal `subscribe` call).\n */\n protected watchCapability(\n capNames: string | readonly string[],\n handlers: {\n readonly onDown?: (nodeId: string, capName: string) => void\n readonly onReady?: (nodeId: string, capName: string) => void\n },\n ): void {\n const names = Array.isArray(capNames) ? capNames : [capNames]\n const nameSet = new Set<string>(names as string[])\n this.subscribe(\n { category: 'system.ready-state' },\n (event) => {\n const data = event.data as {\n readonly capName?: unknown\n readonly state?: unknown\n readonly scope?: { readonly type?: unknown; readonly nodeId?: unknown }\n }\n if (typeof data.capName !== 'string') return\n if (!nameSet.has(data.capName)) return\n if (data.scope?.type !== 'node') return\n const nodeId = data.scope.nodeId\n if (typeof nodeId !== 'string' || nodeId.length === 0) return\n const capName = data.capName\n if (data.state === 'down') {\n handlers.onDown?.(nodeId, capName)\n } else if (data.state === 'ready') {\n handlers.onReady?.(nodeId, capName)\n }\n },\n )\n }\n\n protected subscribe<C extends import('../interfaces/event-bus.js').KnownEventCategory>(\n filter: import('../interfaces/event-bus.js').TypedEventFilter<C>,\n handler: (event: import('../interfaces/event-bus.js').TypedSystemEvent<C>) => void,\n ): void\n protected subscribe(\n filter: import('../interfaces/event-bus.js').EventFilter,\n handler: (event: import('../interfaces/event-bus.js').SystemEvent) => void,\n ): void\n protected subscribe(\n filter: import('../interfaces/event-bus.js').EventFilter,\n handler: (event: import('../interfaces/event-bus.js').SystemEvent) => void,\n ): void {\n const unsub = this.ctx.eventBus.subscribe(filter, handler)\n this._subscriptions.push(unsub)\n }\n\n // ── Lifecycle event emission ────────────────────────────────────────────\n\n private emitLifecycle(category: EventCategory, data?: Record<string, unknown>): void {\n try {\n this._ctx?.eventBus.emit({\n id: `${this._ctx.id}-${Date.now()}`,\n timestamp: new Date(),\n source: { type: 'addon', id: this._ctx.id, nodeId: this._ctx.kernel.localNodeId ?? 'hub' },\n category,\n data: data ?? {},\n })\n } catch { /* best-effort — don't crash addon if event bus is unavailable */ }\n }\n\n /**\n * Emit a `system.ready-state` event for every capability this addon\n * registered at init. Scope is `{type:'node', nodeId}` — readiness is\n * tied to the node that hosts the provider. Consumers that care about\n * per-device readiness can subscribe with `{type:'device', ...}` if\n * the provider emits at finer granularity (collection addons may\n * opt-out via `autoEmitReadiness` and emit manually).\n */\n private emitReadinessForProviders(state: 'ready' | 'down'): void {\n const ctx = this._ctx\n if (!ctx) return\n if (this._registeredCapNames.length === 0) return\n // Forked child processes have a hierarchical nodeId (e.g. 'hub/detection-pipeline').\n // Readiness must be scoped to the logical parent node so orchestrators that\n // subscribe to 'hub' or 'dev-agent-0' receive the event from their children too.\n const rawNodeId = ctx.kernel?.localNodeId ?? 'hub'\n const nodeId = rawNodeId.includes('/') ? rawNodeId.split('/')[0]! : rawNodeId\n for (const capName of this._registeredCapNames) {\n try {\n emitReadiness(ctx.eventBus, {\n capName,\n scope: { type: 'node', nodeId },\n state,\n generation: this._readinessGeneration,\n sourceNodeId: nodeId,\n })\n } catch { /* best-effort — a broken bus must not take the addon down */ }\n }\n }\n\n // ── Common helpers ─────────────────────────────────────────────────────\n\n /**\n * Resolve the shared models directory path via the storage capability.\n * Falls back to `camstack-data/models` if storage is unavailable.\n * Used by inference addons (detection-pipeline, audio-classifier, embedding-encoder).\n */\n protected async resolveModelsDir(): Promise<string> {\n return this.ctx.api.storage.resolve.query({ location: 'models', relativePath: '' })\n .catch(() => 'camstack-data/models')\n }\n\n /**\n * Access the runtime capability registry for in-process provider lookups.\n * Returns null if the registry is not available (e.g. on agents).\n * Used by addons that consume other capabilities directly (snapshot, stream-broker, enrichment).\n */\n protected get capabilities(): CapabilitiesAccess | null {\n const capCtx = this.ctx as { capabilities?: CapabilitiesAccess }\n return capCtx.capabilities ?? null\n }\n\n // ── Config resolution ─────────────────────────────────────────────────\n\n /**\n * Resolve config by merging defaults with persisted store values.\n * Called automatically during initialize() and after every updateSettings().\n *\n * The merge is shallow: each key in `defaults` is checked against the store.\n * Only keys present in defaults are read — the store can contain extra keys\n * (e.g. from older versions) without polluting the typed config.\n */\n protected async resolveConfig(): Promise<void> {\n const stored = await this.readAddonStoreWithRetry()\n const resolved = { ...this.defaults }\n\n for (const key of Object.keys(this.defaults) as Array<keyof TConfig & string>) {\n const storedValue = stored[key]\n if (storedValue !== undefined && storedValue !== null) {\n const defaultType = typeof this.defaults[key]\n if (typeof storedValue === defaultType) {\n ;(resolved as Record<string, unknown>)[key] = storedValue\n }\n }\n }\n\n this._config = resolved\n }\n\n /**\n * Wrap `ctx.settings.readAddonStore()` with a short retry budget so a\n * transient settings-store outage (mid-restart of sqlite-settings, tsx-watch\n * swap, or any race where the SqliteSettingsBackend is between shutdown and\n * re-initialize) doesn't propagate up into `initialize()` and leave the\n * addon permanently broken.\n *\n * Retry only the two known infra fingerprints. Anything else propagates so\n * real bugs surface immediately. After the budget expires we fall back to\n * `{}` (defaults) — the addon's first successful patch will rehydrate.\n */\n private async readAddonStoreWithRetry(): Promise<Record<string, unknown>> {\n const settings = this._ctx?.settings\n if (!settings) return {}\n const delaysMs = [150, 350, 600, 900]\n let lastErr: unknown\n for (let attempt = 0; attempt <= delaysMs.length; attempt++) {\n try {\n return (await settings.readAddonStore()) ?? {}\n } catch (err) {\n lastErr = err\n const msg = err instanceof Error ? err.message : String(err)\n const transient =\n msg.includes('SqliteSettingsBackend not initialized') ||\n msg.includes('provider not available')\n if (!transient) throw err\n if (attempt === delaysMs.length) break\n await new Promise<void>((r) => setTimeout(r, delaysMs[attempt]))\n }\n }\n this._ctx?.logger?.warn?.('readAddonStore: settings-store unavailable after retries — using defaults', {\n meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) },\n })\n return {}\n }\n}\n\n/**\n * Normalize an `ICamstackAddon.initialize()` return value into the\n * `AddonInitResult` envelope. Arrays are wrapped into `{ providers }`;\n * envelopes pass through; void stays void.\n *\n * Exported so the kernel boot path and the backend addon registry can\n * share the same normalizer instead of duplicating the shim. Addons that\n * extend `BaseAddon` already emit the envelope, so this helper is only a\n * safety net for direct `ICamstackAddon` implementations (mostly tests).\n */\nexport function normalizeAddonInitResult(\n result: ProviderRegistration[] | AddonInitResult | void,\n): AddonInitResult | void {\n if (result == null) return\n if (Array.isArray(result)) return { providers: result }\n return result\n}\n\n","/**\n * ReadinessRegistry — kernel-side tracker for capability readiness\n * transitions. Spec: `docs/superpowers/specs/2026-04-19-system-readiness-design.md`.\n *\n * Each process owns one registry. The registry:\n * - Listens to `system.ready-state` events on the event bus.\n * - Maintains a `Map<key, ReadinessRecord>` snapshot of the most\n * recently observed state + generation + derived epoch.\n * - Exposes `emitReady` / `emitStarting` / `emitDown` helpers for\n * local producers (which stamp the process-wide `generation`).\n * - Exposes `awaitReady(capName, scope, {timeoutMs, signal})` and\n * `onReadyState(capName, scope, handler)` for consumers.\n *\n * Epoch derivation:\n * - First `ready` observed for a `(capName, scope)` → epoch = 1.\n * - `ready` received with a generation different from the last\n * `currentGeneration` → epoch++.\n * - Same-generation repeated `ready` events → epoch unchanged\n * (idempotent keepalive; e.g. re-emit on `$node.connected`).\n *\n * Not persisted. Rebuilt from the event stream + optional boot-time\n * snapshot rehydration by the caller.\n */\nimport type {\n IEventBus,\n SystemReadyStatePayload,\n ReadinessScope,\n ReadinessState,\n} from '../interfaces/event-bus.js'\nimport type { IScopedLogger } from '../interfaces/logging.js'\nimport type {\n IReadinessRegistry,\n IReadinessHandler,\n IAwaitReadyOptions,\n IReadinessTransition,\n} from '../interfaces/readiness.js'\nimport { createEvent } from '../interfaces/event-bus.js'\n\n/**\n * Concrete types live here; `interfaces/readiness.ts` declares the\n * matching narrow interfaces (`IReadinessTransition`,\n * `IReadinessHandler`, `IAwaitReadyOptions`) used by `AddonContext`\n * and other consumer-facing types.\n */\nexport type ReadinessTransition = IReadinessTransition\nexport type ReadinessHandler = IReadinessHandler\nexport type AwaitReadyOptions = IAwaitReadyOptions\n\nexport class ReadinessTimeoutError extends Error {\n readonly capName: string\n readonly scope: ReadinessScope\n readonly waitedMs: number\n constructor(capName: string, scope: ReadinessScope, waitedMs: number) {\n super(`Timed out waiting for ${capName} (${scopeKey(scope)}) to become ready after ${waitedMs}ms`)\n this.name = 'ReadinessTimeoutError'\n this.capName = capName\n this.scope = scope\n this.waitedMs = waitedMs\n }\n}\n\ninterface ReadinessRecord {\n capName: string\n scope: ReadinessScope\n state: ReadinessState\n generation: string\n epoch: number\n lastChange: number\n /**\n * Node that emitted the latest transition for this record. Needed for\n * `agent.offline` demux of `device`-scoped caps (scope itself carries\n * no node info). For `node`-scoped caps this matches `scope.nodeId`.\n */\n sourceNodeId: string\n}\n\ninterface Subscription {\n readonly capName: string\n readonly scope: ReadinessScope\n readonly handler: ReadinessHandler\n}\n\n/**\n * Build a canonical string key for a `(capName, scope)` pair. Used as\n * the snapshot map key and for log/debug output.\n */\nexport function readinessKey(capName: string, scope: ReadinessScope): string {\n return `${capName}|${scopeKey(scope)}`\n}\n\nfunction scopeKey(scope: ReadinessScope): string {\n switch (scope.type) {\n case 'global': return 'global'\n case 'node': return `node:${scope.nodeId}`\n case 'device': return `device:${scope.deviceId}`\n }\n}\n\nfunction scopesEqual(a: ReadinessScope, b: ReadinessScope): boolean {\n if (a.type !== b.type) return false\n if (a.type === 'global' || b.type === 'global') return true\n if (a.type === 'node' && b.type === 'node') return a.nodeId === b.nodeId\n if (a.type === 'device' && b.type === 'device') return a.deviceId === b.deviceId\n return false\n}\n\nexport interface ReadinessRegistryOptions {\n readonly eventBus: IEventBus\n readonly sourceNodeId: string\n /**\n * Per-process generation id — stamped on every emit made via this\n * registry's `emitReady`/`emitStarting`/`emitDown` helpers. Caller\n * owns the value so it can survive across subsystem re-bindings\n * within the same process. Default: a random 12-char id.\n */\n readonly generation?: string\n readonly logger?: IScopedLogger\n /**\n * Clock source. Injectable for tests.\n */\n readonly now?: () => number\n}\n\nexport class ReadinessRegistry implements IReadinessRegistry {\n private readonly bus: IEventBus\n private readonly sourceNodeId: string\n private readonly logger: IScopedLogger | undefined\n private readonly now: () => number\n private readonly generation: string\n private readonly snapshot = new Map<string, ReadinessRecord>()\n private readonly subscriptions = new Set<Subscription>()\n private readonly unsubscribeBus: () => void\n private readonly unsubscribeAgentOffline: () => void\n\n constructor(options: ReadinessRegistryOptions) {\n this.bus = options.eventBus\n this.sourceNodeId = options.sourceNodeId\n this.logger = options.logger\n this.now = options.now ?? (() => Date.now())\n this.generation = options.generation ?? randomGeneration()\n\n this.unsubscribeBus = this.bus.subscribe(\n { category: 'system.ready-state' },\n (event) => this.ingest(event.data as SystemReadyStatePayload),\n )\n // Auto-heal on agent disconnect: when `agent.offline` fires, walk\n // our own snapshot and synthesize `down` transitions for every\n // node-scoped cap bound to the disconnected nodeId. This covers\n // ungraceful producer deaths (process crash, network blip,\n // SIGKILL) where the producer's own shutdown hook never fires —\n // subscribers would otherwise keep waiting on a stale `ready`.\n // Synthesized transitions are local-only (we don't re-emit on the\n // bus) — every registry reacts independently to the same agent.offline.\n this.unsubscribeAgentOffline = this.bus.subscribe(\n { category: 'agent.offline' },\n (event) => this.synthesizeDownForNode((event.data as { agentId: string }).agentId),\n )\n // Hydrate from recent bus events so a late-starting registry\n // (e.g. an addon subscribed mid-session, or a hub reboot where\n // producers haven't re-emitted yet) picks up already-fired\n // readiness transitions. Safe to run after subscribe: ingest()\n // is idempotent for same-generation replays.\n if (typeof this.bus.getRecent === 'function') {\n try {\n const recent = this.bus.getRecent({ category: 'system.ready-state' })\n for (const event of recent) {\n this.ingest(event.data as SystemReadyStatePayload)\n }\n } catch {\n // getRecent is optional — a minimal test bus may not support it.\n }\n }\n }\n\n /** Release the event-bus subscription. Idempotent. */\n close(): void {\n this.unsubscribeBus()\n this.unsubscribeAgentOffline()\n this.subscriptions.clear()\n }\n\n /** Current snapshot for a `(capName, scope)` pair, or `null` if never seen. */\n get(capName: string, scope: ReadinessScope): ReadinessRecord | null {\n return this.snapshot.get(readinessKey(capName, scope)) ?? null\n }\n\n /**\n * Serializable snapshot for cross-process transport. Returns an array\n * of `ReadinessRecord` plain objects — safe for MsgPack / JSON\n * transport. Used by the hub's `$readiness.getSnapshot` Moleculer\n * action; consumers hydrate their local registry from the result.\n */\n getSnapshotForTransport(): readonly ReadinessRecord[] {\n return Array.from(this.snapshot.values())\n }\n\n /**\n * Hydrate the snapshot from an authoritative source. Entries already\n * present locally are skipped — live deltas (received via the event\n * bus subscription) always take precedence over the snapshot. For\n * each newly hydrated entry, a one-shot transition is dispatched to\n * matching subscriptions so pending `awaitReady` callers unblock\n * without having to wait for a fresh event.\n *\n * Local `epoch` is reset to 1 per entry — consumer-side epoch is\n * derived from observed generation transitions, so this mirrors the\n * value that would have been assigned had the consumer observed the\n * first `ready` event directly.\n */\n hydrate(records: readonly ReadinessRecord[]): void {\n const now = this.now()\n for (const record of records) {\n const key = readinessKey(record.capName, record.scope)\n if (this.snapshot.has(key)) continue\n const hydrated: ReadinessRecord = {\n capName: record.capName,\n scope: record.scope,\n state: record.state,\n generation: record.generation,\n epoch: 1,\n lastChange: now,\n sourceNodeId: record.sourceNodeId,\n }\n this.snapshot.set(key, hydrated)\n if (this.logger) {\n this.logger.debug(\n `readiness: ${record.capName} (${scopeKey(record.scope)}) → ${record.state} (hydrated, gen=${record.generation.slice(0, 6)})`,\n )\n }\n const transition: ReadinessTransition = {\n capName: record.capName,\n scope: record.scope,\n state: record.state,\n epoch: 1,\n generation: record.generation,\n sourceNodeId: 'hydrated',\n ts: now,\n durationInPrevState: 0,\n }\n for (const sub of this.subscriptions) {\n if (sub.capName !== record.capName) continue\n if (!scopesEqual(sub.scope, record.scope)) continue\n try {\n sub.handler(transition)\n } catch (err) {\n this.logger?.warn(\n `readiness hydrate handler threw for ${record.capName}: ${(err as Error).message ?? String(err)}`,\n )\n }\n }\n }\n }\n\n /** Shallow copy of the full snapshot — mainly for diagnostics/tests. */\n getAll(): ReadonlyMap<string, ReadinessRecord> {\n return new Map(this.snapshot)\n }\n\n /**\n * Emit a `ready` transition for a locally-owned capability. The\n * payload carries this registry's `generation` so remote registries\n * can derive their own `epoch`.\n */\n emitReady(capName: string, scope: ReadinessScope): void {\n this.emitTransition(capName, scope, 'ready')\n }\n\n emitStarting(capName: string, scope: ReadinessScope): void {\n this.emitTransition(capName, scope, 'starting')\n }\n\n emitDown(capName: string, scope: ReadinessScope): void {\n this.emitTransition(capName, scope, 'down')\n }\n\n /**\n * One-shot: resolve once the cap is `ready`. Reads the snapshot\n * first — if already ready, resolves synchronously on the next tick.\n *\n * Default behaviour is **wait indefinitely** (timeoutMs = `Infinity`):\n * the orchestrator and other callers never want to attach a camera\n * with empty steps just because a cap took longer than 30s to come\n * up. Pass an explicit finite `timeoutMs` to bound the wait, or an\n * `AbortSignal` to cancel externally. `Infinity` is honoured natively\n * — no `setTimeout` is registered (Node's setTimeout silently clamps\n * values above ~24.8d, so we must skip the timer entirely).\n */\n awaitReady(capName: string, scope: ReadinessScope, opts: AwaitReadyOptions = {}): Promise<void> {\n const timeoutMs = opts.timeoutMs ?? Number.POSITIVE_INFINITY\n const start = this.now()\n const current = this.get(capName, scope)\n if (current?.state === 'ready') {\n return Promise.resolve()\n }\n if (opts.signal?.aborted) {\n return Promise.reject(opts.signal.reason ?? new Error('aborted'))\n }\n return new Promise<void>((resolve, reject) => {\n let settled = false\n const unsubscribe = this.onReadyState(capName, scope, (t) => {\n if (t.state !== 'ready') return\n if (settled) return\n settled = true\n cleanup()\n resolve()\n })\n const isInfinite = !Number.isFinite(timeoutMs)\n const timer: ReturnType<typeof setTimeout> | null = isInfinite\n ? null\n : setTimeout(() => {\n if (settled) return\n settled = true\n cleanup()\n reject(new ReadinessTimeoutError(capName, scope, this.now() - start))\n }, timeoutMs)\n const onAbort = (): void => {\n if (settled) return\n settled = true\n cleanup()\n reject(opts.signal?.reason ?? new Error('aborted'))\n }\n opts.signal?.addEventListener('abort', onAbort, { once: true })\n function cleanup(): void {\n unsubscribe()\n if (timer !== null) clearTimeout(timer)\n opts.signal?.removeEventListener('abort', onAbort)\n }\n })\n }\n\n /**\n * Observable: every transition (starting/ready/down) dispatches to\n * `handler`. On subscription, if the snapshot has a current state,\n * the handler fires ONCE asynchronously with that state as the\n * initial transition (duration = 0) so late subscribers can pick up\n * current state without racing the next transition.\n *\n * Returns an unsubscribe function.\n */\n onReadyState(capName: string, scope: ReadinessScope, handler: ReadinessHandler): () => void {\n const sub: Subscription = { capName, scope, handler }\n this.subscriptions.add(sub)\n const current = this.get(capName, scope)\n if (current !== null) {\n queueMicrotask(() => {\n if (!this.subscriptions.has(sub)) return\n handler({\n capName,\n scope,\n state: current.state,\n epoch: current.epoch,\n generation: current.generation,\n sourceNodeId: this.sourceNodeId,\n ts: current.lastChange,\n durationInPrevState: 0,\n })\n })\n }\n return () => { this.subscriptions.delete(sub) }\n }\n\n // ── Internals ─────────────────────────────────────────────────────────\n\n private emitTransition(capName: string, scope: ReadinessScope, state: ReadinessState): void {\n const ts = this.now()\n this.bus.emit(createEvent(\n 'system.ready-state',\n { type: 'capability', id: capName, nodeId: this.sourceNodeId },\n {\n capName,\n scope,\n state,\n generation: this.generation,\n sourceNodeId: this.sourceNodeId,\n ts,\n },\n ))\n }\n\n /**\n * Update snapshot + dispatch to subscribers. Idempotent: same\n * `generation + state` replay is a no-op for both snapshot and\n * subscribers.\n *\n * Defensive against malformed payloads (legal reason: a mock bus's\n * `getRecent` may ignore the category filter and replay unrelated\n * events when we hydrate at construction time).\n */\n private ingest(payload: SystemReadyStatePayload): void {\n if (typeof payload?.capName !== 'string') return\n if (payload.state !== 'ready' && payload.state !== 'starting' && payload.state !== 'down') return\n if (typeof payload.generation !== 'string') return\n if (payload.scope?.type !== 'global' && payload.scope?.type !== 'node' && payload.scope?.type !== 'device') return\n\n const key = readinessKey(payload.capName, payload.scope)\n const prev = this.snapshot.get(key) ?? null\n const now = this.now()\n\n let epoch: number\n if (prev === null) {\n epoch = payload.state === 'ready' ? 1 : 0\n } else if (prev.generation !== payload.generation && payload.state === 'ready') {\n epoch = prev.epoch + 1\n } else {\n epoch = prev.epoch\n }\n\n // Idempotent replay: same generation + same state + we've already\n // recorded it — skip both snapshot write and dispatch.\n if (prev !== null && prev.generation === payload.generation && prev.state === payload.state) {\n return\n }\n\n const durationInPrevState = prev === null ? 0 : Math.max(0, now - prev.lastChange)\n const next: ReadinessRecord = {\n capName: payload.capName,\n scope: payload.scope,\n state: payload.state,\n generation: payload.generation,\n epoch,\n lastChange: now,\n sourceNodeId: payload.sourceNodeId,\n }\n this.snapshot.set(key, next)\n\n const transition: ReadinessTransition = {\n capName: payload.capName,\n scope: payload.scope,\n state: payload.state,\n epoch,\n generation: payload.generation,\n sourceNodeId: payload.sourceNodeId,\n ts: payload.ts,\n durationInPrevState,\n }\n\n if (this.logger) {\n this.logger.debug(\n `readiness: ${payload.capName} (${scopeKey(payload.scope)}) → ${payload.state} epoch=${epoch} gen=${payload.generation.slice(0, 6)} (prev ${durationInPrevState}ms)`,\n )\n }\n\n for (const sub of this.subscriptions) {\n if (sub.capName !== payload.capName) continue\n if (!scopesEqual(sub.scope, payload.scope)) continue\n try {\n sub.handler(transition)\n } catch (err) {\n this.logger?.warn(`readiness handler threw for ${payload.capName}: ${(err as Error).message ?? String(err)}`)\n }\n }\n }\n\n /**\n * `agent.offline` demux. When an agent disconnects ungracefully,\n * its BaseAddon's shutdown hook doesn't fire — consumers would\n * otherwise keep waiting on a stale `ready`. Walk the snapshot and\n * synthesize a `down` transition for every node-scoped record bound\n * to the disconnected `nodeId`, skipping records already `down`.\n *\n * Synthesis is LOCAL: we don't re-emit on the bus. Every registry\n * (one per process) receives the same `agent.offline` and reacts\n * independently, which means no duplicated downs and no central\n * supervisor required.\n */\n private synthesizeDownForNode(offlineNodeId: string): void {\n const now = this.now()\n for (const [key, record] of this.snapshot) {\n if (record.state === 'down') continue\n // Node-scoped records: match on scope.nodeId.\n // Device-scoped records: match on sourceNodeId (which node emitted\n // the latest ready) because the scope itself carries no node info.\n // Global-scoped records are skipped — they're not tied to a node.\n const matchesOfflineNode =\n (record.scope.type === 'node' && record.scope.nodeId === offlineNodeId) ||\n (record.scope.type === 'device' && record.sourceNodeId === offlineNodeId)\n if (!matchesOfflineNode) continue\n\n const durationInPrevState = Math.max(0, now - record.lastChange)\n const next: ReadinessRecord = {\n ...record,\n state: 'down',\n lastChange: now,\n }\n this.snapshot.set(key, next)\n\n const transition: ReadinessTransition = {\n capName: record.capName,\n scope: record.scope,\n state: 'down',\n epoch: record.epoch,\n generation: record.generation,\n sourceNodeId: this.sourceNodeId,\n ts: now,\n durationInPrevState,\n }\n if (this.logger) {\n this.logger.debug(\n `readiness: ${record.capName} (${scopeKey(record.scope)}) → down (synthesized from agent.offline ${offlineNodeId})`,\n )\n }\n for (const sub of this.subscriptions) {\n if (sub.capName !== record.capName) continue\n if (!scopesEqual(sub.scope, record.scope)) continue\n try {\n sub.handler(transition)\n } catch (err) {\n this.logger?.warn(`readiness handler threw during agent.offline demux for ${record.capName}: ${(err as Error).message ?? String(err)}`)\n }\n }\n }\n }\n}\n\nfunction randomGeneration(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID()\n }\n return Math.random().toString(36).slice(2, 14)\n}\n\n// ---------------------------------------------------------------------------\n// Helpers for `$node.disconnected` demultiplexing\n// ---------------------------------------------------------------------------\n\n/**\n * Given a list of `(capName, scope)` pairs that a just-disconnected\n * node owned, emit a `down` transition for each. Callers (kernel\n * supervisor, hub `$node.disconnected` handler) wire this up once they\n * know which caps the node held.\n */\nexport function emitDownForOwnedCaps(\n registry: ReadinessRegistry,\n owned: ReadonlyArray<{ readonly capName: string; readonly scope: ReadinessScope }>,\n): void {\n for (const { capName, scope } of owned) {\n registry.emitDown(capName, scope)\n }\n}\n\n// Re-export the scope key helper for consumers that want to\n// collate by key in their own maps (e.g. debug UIs).\nexport { scopeKey }\n","// =============================================================================\n// Storage Location Types\n// =============================================================================\n//\n// `StorageLocationType` is owned by `./storage-location.ts` (Zod enum is\n// the authoritative source). This file used to declare a parallel TS\n// alias which drifted from the Zod enum (the alias had 4 values the enum\n// didn't). Re-exporting the inferred type keeps the wire surface and the\n// legacy `IStorageProvider` interface in lockstep — adding/removing a\n// value in the Zod enum is now the single source of truth.\n\nexport type { StorageLocationType } from './storage-location.js'\nimport type { StorageLocationType } from './storage-location.js'\n\n/** All location types as an array (for iteration) */\nexport const STORAGE_LOCATION_TYPES: readonly StorageLocationType[] = [\n 'data',\n 'media',\n 'recordings',\n 'recordings-high',\n 'recordings-low',\n 'recordings-clips',\n 'event-images',\n 'models',\n 'addons-data',\n 'cache',\n 'logs',\n 'backups',\n] as const\n\n/** Default subdirectory names for filesystem storage */\nexport const DEFAULT_LOCATION_SUBDIRS: Readonly<Record<StorageLocationType, string>> = {\n 'data': 'db',\n 'media': 'media',\n 'recordings': 'recordings',\n 'recordings-high': 'recordings-high',\n 'recordings-low': 'recordings-low',\n 'recordings-clips': 'recordings-clips',\n 'event-images': 'event-images',\n 'models': 'models',\n 'addons-data': 'addons-data',\n 'cache': '/tmp/camstack-cache',\n 'logs': 'logs',\n 'backups': 'backups',\n}\n\n// =============================================================================\n// Storage Provider (capability: 'storage', mode: collection)\n// =============================================================================\n\n/**\n * A storage provider serves file-based storage for one or more location types.\n * Multiple providers can coexist (local filesystem, NAS, S3, etc.).\n * Each addon/system setting selects which provider to use per location type.\n */\n// ── Named input types for IStorageProvider methods ──────────────────────\n\nexport interface StorageResolveInput { readonly location: StorageLocationType; readonly relativePath: string }\nexport interface StorageWriteInput { readonly location: StorageLocationType; readonly relativePath: string; readonly data: Buffer | NodeJS.ReadableStream }\nexport interface StorageReadInput { readonly location: StorageLocationType; readonly relativePath: string }\nexport interface StorageExistsInput { readonly location: StorageLocationType; readonly relativePath: string }\nexport interface StorageListInput { readonly location: StorageLocationType; readonly prefix?: string }\nexport interface StorageDeleteInput { readonly location: StorageLocationType; readonly relativePath: string }\nexport interface StorageAvailableSpaceInput { readonly location: StorageLocationType }\n\nexport interface IStorageProvider {\n readonly id: string\n readonly name: string\n readonly supportedLocations: readonly StorageLocationType[]\n\n resolve(input: StorageResolveInput): string\n write(input: StorageWriteInput): Promise<void>\n read(input: StorageReadInput): Promise<Buffer>\n exists(input: StorageExistsInput): Promise<boolean>\n list(input: StorageListInput): Promise<readonly string[]>\n delete(input: StorageDeleteInput): Promise<void>\n getAvailableSpace(input: StorageAvailableSpaceInput): Promise<number | null>\n}\n\n// =============================================================================\n// Settings Backend (capability: 'settings-store', mode: singleton)\n// =============================================================================\n\n/**\n * All named collections that the settings backend manages.\n * Each collection maps to a logical table/bucket in the backend.\n */\nexport type SettingsCollection =\n | 'system-settings' // Global system config\n | 'addon-settings' // Per-addon config (keyed by addonId)\n | 'provider-settings' // Per-device-provider config (keyed by providerId)\n | 'device-settings' // Per-device config (keyed by deviceId)\n | 'detection-events' // Detection event records\n | 'audio-levels' // Audio level readings\n | 'track-trails' // Object tracking trails\n | 'recording-segments' // Recording segment metadata\n | 'recording-thumbnails' // Thumbnail metadata\n | 'recording-policies' // Per-device recording policies\n | 'recording-storage-config' // Per-device storage + retention\n | 'recording-cleanup-queue' // Cleanup task queue\n | 'known-faces' // Face recognition identities\n | (string & {}) // Allow arbitrary collection names (e.g. addon-defined)\n\n// ── Named input types for ISettingsBackend methods ──────────────────────\n\nexport interface SettingsGetInput { readonly namespace?: string; readonly collection: SettingsCollection; readonly key: string }\nexport interface SettingsSetInput { readonly namespace?: string; readonly collection: SettingsCollection; readonly key: string; readonly value: unknown }\nexport interface SettingsQueryInput { readonly namespace?: string; readonly collection: SettingsCollection; readonly filter?: QueryFilter }\nexport interface SettingsInsertInput<T extends object = Record<string, unknown>> { readonly namespace?: string; readonly collection: SettingsCollection; readonly record: SettingsRecord<T> }\nexport interface SettingsUpdateInput { readonly namespace?: string; readonly collection: SettingsCollection; readonly id: string; readonly data: Record<string, unknown> }\nexport interface SettingsDeleteInput { readonly namespace?: string; readonly collection: SettingsCollection; readonly key: string }\nexport interface SettingsCountInput { readonly namespace?: string; readonly collection: SettingsCollection; readonly filter?: QueryFilter }\nexport interface SettingsIsEmptyInput { readonly namespace?: string; readonly collection: SettingsCollection }\n\n/**\n * Settings backend — persistent key-value and document storage.\n * Default implementation: SQLite. Can be replaced by Postgres, etc.\n * All methods use object-arg signatures for extensibility.\n */\nexport interface ISettingsBackend {\n /** Get a single value by key from a collection */\n get(input: SettingsGetInput): Promise<unknown>\n\n /** Set a value by key in a collection (upsert) */\n set(input: SettingsSetInput): Promise<void>\n\n /** Get all entries matching an optional filter */\n query<T extends object = Record<string, unknown>>(input: SettingsQueryInput): Promise<readonly SettingsRecord<T>[]>\n\n /** Insert a new record */\n insert<T extends object = Record<string, unknown>>(input: SettingsInsertInput<T>): Promise<void>\n\n /** Update an existing record by ID */\n update(input: SettingsUpdateInput): Promise<void>\n\n /** Delete a record by key/ID */\n delete(input: SettingsDeleteInput): Promise<void>\n\n /** Count entries in a collection */\n count(input: SettingsCountInput): Promise<number>\n\n /** Check if a collection is empty (used for first-boot detection) */\n isEmpty(input: SettingsIsEmptyInput): Promise<boolean>\n\n /**\n * Declare a typed (SQL-backed) collection. After declaration, the\n * standard `insert` / `update` / `delete` / `query` methods route to\n * typed columns for this collection instead of the JSON-blob default.\n * Idempotent — re-declaring the same shape is a no-op. Backends\n * without typed-table support should implement this as a no-op so\n * the cap contract stays uniform.\n */\n declareCollection(input: {\n namespace?: string\n collection: string\n columns: readonly import('../capabilities/settings-store.cap.js').CollectionColumn[]\n indexes?: readonly import('../capabilities/settings-store.cap.js').CollectionIndex[]\n }): Promise<void>\n\n // ── Structured tables ────────────────────────────────────────────────\n // Generic SQL-like operations for custom tables with typed columns.\n // Implementations create the table on first use if it doesn't exist.\n\n /** Ensure a structured table exists (CREATE TABLE IF NOT EXISTS) */\n ensureTable?(table: string, schema: TableSchema): Promise<void>\n\n /** Insert a row into a structured table */\n tableInsert?(table: string, row: Record<string, unknown>): Promise<void>\n\n /** Update rows matching a filter */\n tableUpdate?(table: string, filter: Record<string, unknown>, updates: Record<string, unknown>): Promise<number>\n\n /** Delete rows matching a filter. Returns count of deleted rows. */\n tableDelete?(table: string, filter: Record<string, unknown>): Promise<number>\n\n /** Query rows from a structured table */\n tableQuery?(table: string, options?: TableQueryOptions): Promise<readonly Record<string, unknown>[]>\n\n /** Get a single row by primary key or filter */\n tableGet?(table: string, filter: Record<string, unknown>): Promise<Record<string, unknown> | null>\n\n /** Count rows matching a filter */\n tableCount?(table: string, filter?: Record<string, unknown>): Promise<number>\n}\n\nexport interface SettingsRecord<T extends object = Record<string, unknown>> {\n readonly id: string\n readonly data: T\n}\n\nexport interface QueryFilter {\n where?: Record<string, unknown>\n whereIn?: Record<string, unknown[]>\n whereBetween?: Record<string, [unknown, unknown]>\n orderBy?: { field: string; direction: 'asc' | 'desc' }\n limit?: number\n offset?: number\n}\n\n/** Column definition for structured tables */\nexport interface TableColumnSchema {\n readonly name: string\n readonly type: 'TEXT' | 'INTEGER' | 'REAL' | 'JSON'\n readonly primaryKey?: boolean\n readonly notNull?: boolean\n readonly unique?: boolean\n readonly defaultValue?: string | number | null\n}\n\n/** Table schema for structured tables */\nexport interface TableSchema {\n readonly columns: readonly TableColumnSchema[]\n readonly indexes?: readonly TableIndexSchema[]\n}\n\n/** Index definition for structured tables */\nexport interface TableIndexSchema {\n readonly name: string\n readonly columns: readonly string[]\n readonly unique?: boolean\n}\n\n/** Query options for structured tables */\nexport interface TableQueryOptions {\n readonly where?: Record<string, unknown>\n readonly orderBy?: { field: string; direction: 'asc' | 'desc' }\n readonly limit?: number\n readonly offset?: number\n}\n\n// =============================================================================\n// Embeddings Backend (capability: 'embeddings', mode: singleton)\n// =============================================================================\n\n/**\n * Embeddings backend — vector storage with approximate nearest neighbor search.\n * Used for CLIP frame search, face recognition, plate matching.\n * Default implementation: HNSW (usearch/hnswlib). Can be replaced by Qdrant, etc.\n */\nexport interface IEmbeddingsBackend {\n /** Create or open a named vector index */\n openIndex(name: string, dimensions: number): Promise<IVectorIndex>\n\n /** List all index names */\n listIndexes(): Promise<readonly string[]>\n\n /** Delete an index and its data */\n deleteIndex(name: string): Promise<void>\n\n /** Lifecycle */\n initialize(): Promise<void>\n shutdown(): Promise<void>\n}\n\nexport interface IVectorIndex {\n /** Index name */\n readonly name: string\n\n /** Vector dimensions */\n readonly dimensions: number\n\n /** Insert a vector with ID and optional metadata */\n insert(id: string, vector: Float32Array, metadata?: Record<string, unknown>): Promise<void>\n\n /** Batch insert for performance */\n insertBatch(items: readonly VectorEntry[]): Promise<void>\n\n /** Search for nearest neighbors */\n search(query: Float32Array, topK: number, filter?: EmbeddingFilter): Promise<readonly VectorSearchResult[]>\n\n /** Delete by ID */\n delete(id: string): Promise<void>\n\n /** Count entries in the index */\n count(): Promise<number>\n\n /** Flush pending writes to disk */\n flush(): Promise<void>\n}\n\nexport interface VectorEntry {\n readonly id: string\n readonly vector: Float32Array\n readonly metadata?: Record<string, unknown>\n}\n\nexport interface VectorSearchResult {\n readonly id: string\n /** Similarity score (0-1, higher is more similar) */\n readonly score: number\n readonly metadata?: Record<string, unknown>\n}\n\nexport interface EmbeddingFilter {\n /** Filter by metadata key-value before vector search */\n readonly where?: Record<string, unknown>\n /** Filter by timestamp range */\n readonly timestampAfter?: number\n readonly timestampBefore?: number\n /** Filter by device */\n readonly deviceId?: string\n}\n\n// =============================================================================\n// Legacy re-exports (deprecated — migrate to new interfaces)\n// =============================================================================\n\n\n/**\n * A document record in collection-based storage.\n * Used by IStructuredStorage and ElementConfigStore.\n * New code should prefer ISettingsBackend + SettingsRecord.\n */\nexport interface StorageRecord {\n collection: string\n id: string\n data: Record<string, unknown>\n}\n\n/**\n * Collection-based document storage (query, insert, update, delete).\n * Used by ElementConfigStore and StorageManager shim layer.\n * New code should prefer ISettingsBackend.\n */\nexport interface IStructuredStorage {\n query(collection: string, filter?: QueryFilter): Promise<readonly StorageRecord[]>\n insert(record: StorageRecord): Promise<StorageRecord>\n update(collection: string, id: string, data: Record<string, unknown>): Promise<StorageRecord>\n delete(collection: string, id: string): Promise<void>\n count(collection: string, filter?: QueryFilter): Promise<number>\n}\n\n/**\n * File-based storage operations.\n * Used by StorageManager and addons for media/model files.\n * New code should prefer IStorageProvider.\n */\nexport interface IFileStorage {\n readFile(path: string): Promise<Buffer>\n writeFile(path: string, data: Buffer): Promise<void>\n deleteFile(path: string): Promise<void>\n listFiles(prefix?: string): Promise<readonly string[]>\n getFileUrl(path: string): Promise<string>\n exists(path: string): Promise<boolean>\n}\n\n/**\n * Combined storage location with optional structured + file storage.\n * Used by StorageManager.getLocation() and ElementConfigStore.\n * New code should prefer IStorageProvider + ISettingsBackend.\n */\nexport interface IStorageLocation {\n structured?: IStructuredStorage\n files?: IFileStorage\n}\n","import type { StreamSourceEntry } from '../../device/camera-device.js'\n\n// ---------------------------------------------------------------------------\n// Stream metadata — probed (ffprobe) or declared by provider\n// ---------------------------------------------------------------------------\n\nexport interface StreamMetadata {\n width?: number // e.g. 1920\n height?: number // e.g. 1080\n codec?: string // e.g. 'h264', 'h265', 'mjpeg', 'vp8', 'vp9', 'av1'\n fps?: number // e.g. 25\n bitrateKbps?: number // e.g. 4000\n /** Probed audio track. Undefined when stream carries no audio. */\n audio?: StreamAudioMetadata\n}\n\nexport interface StreamAudioMetadata {\n /** Codec name as reported by the demuxer (e.g. 'aac', 'pcm_mulaw', 'opus'). */\n codec?: string\n /** Sample rate in Hz (e.g. 8000, 16000, 44100, 48000). */\n sampleRate?: number\n /** Channel count (1 = mono, 2 = stereo). */\n channels?: number\n /** Stated bitrate in kbps when the demuxer reports one. */\n bitrateKbps?: number\n /** Codec profile/aac-mode (e.g. 'LC', 'HE-AAC') when known. */\n profile?: string\n}\n\n// ---------------------------------------------------------------------------\n// Stream quality — computed from metadata\n// ---------------------------------------------------------------------------\n\nexport type StreamQuality = 'high' | 'mid' | 'low'\n\n/** Friendly display labels for stream quality IDs. */\nexport const STREAM_QUALITY_LABELS: Readonly<Record<string, string>> = {\n high: 'High',\n mid: 'Mid',\n low: 'Low',\n}\n\n/** Get a friendly label for a stream ID. Falls back to capitalised id. */\nexport function streamQualityLabel(id: string): string {\n return STREAM_QUALITY_LABELS[id] ?? (id.charAt(0).toUpperCase() + id.slice(1))\n}\n\n/**\n * Compute pixel count for sorting. Returns w*h, or 0 if unknown.\n */\nexport function streamPixels(meta: StreamMetadata): number {\n return (meta.width ?? 0) * (meta.height ?? 0)\n}\n\n/**\n * Classify streams relative to each other.\n * Highest resolution → high, lowest → low, everything else → mid.\n * With 1 stream: high. With 2: high + low.\n */\nexport function classifyStreams<T extends { metadata?: StreamMetadata }>(\n streams: readonly T[],\n): Array<T & { quality: StreamQuality }> {\n if (streams.length === 0) return []\n\n // Sort by pixel count descending\n const sorted = streams\n .map((s, i) => ({ s, i, px: streamPixels(s.metadata ?? {}) }))\n .sort((a, b) => b.px - a.px)\n\n const result: Array<T & { quality: StreamQuality }> = Array.from<T & { quality: StreamQuality }>({ length: streams.length })\n\n for (let rank = 0; rank < sorted.length; rank++) {\n const { s, i } = sorted[rank]!\n let quality: StreamQuality\n if (rank === 0) quality = 'high'\n else if (rank === sorted.length - 1) quality = 'low'\n else quality = 'mid'\n result[i] = { ...s, quality }\n }\n\n return result\n}\n\n/**\n * Classify a single stream in isolation (no relative context).\n * Use classifyStreams() when you have the full set.\n */\nexport function classifyStream(meta: StreamMetadata): StreamQuality {\n const h = meta.height ?? 0\n const w = meta.width ?? 0\n if (h >= 1080 || w >= 1920) return 'high'\n if (h >= 480 || w >= 640) return 'mid'\n return 'low'\n}\n\n// ---------------------------------------------------------------------------\n// Stream protocol — how to consume a stream\n// ---------------------------------------------------------------------------\n\nexport type StreamProtocolType = 'rtsp' | 'rtmp' | 'http' | 'flv' | 'rtp' | 'raw'\n\nexport interface StreamProtocol {\n type: StreamProtocolType\n /** Source URL. undefined for 'raw' (push-based, e.g. Reolink Baichuan). */\n url?: string\n}\n\n// ---------------------------------------------------------------------------\n// AvailableStream — a physical stream exposed by a camera\n// ---------------------------------------------------------------------------\n\nexport interface AvailableStream {\n /** Unique identifier within the device (e.g. 'main', 'sub', 'ch0_0') */\n id: string\n /** Ways to consume this stream */\n protocols: StreamProtocol[]\n /** Probed or provider-declared stream characteristics */\n metadata?: StreamMetadata\n /** Computed from metadata via classifyStream() */\n quality: StreamQuality\n /** Optional display hint */\n label?: string\n}\n\n/** Convert AvailableStream to StreamSourceEntry for stream-broker consumers. */\nexport function toStreamSourceEntry(stream: AvailableStream): StreamSourceEntry {\n // Pick first URL-bearing protocol, prefer rtsp\n const urlProto = stream.protocols.find((p) => p.type === 'rtsp' && p.url)\n ?? stream.protocols.find((p) => p.url)\n\n const protocolRaw = urlProto?.type\n const protocol: StreamSourceEntry['protocol'] =\n protocolRaw === 'rtsp' ? 'rtsp'\n : protocolRaw === 'http' || protocolRaw === 'flv' ? 'http-mjpeg'\n : 'custom'\n\n return {\n id: stream.id,\n label: stream.label ?? stream.id,\n protocol,\n url: urlProto?.url,\n profileHint: stream.quality,\n }\n}\n\n\n// ---------------------------------------------------------------------------\n// Camera stream settings — stored in device settings by providers\n// ---------------------------------------------------------------------------\n\n/** A single stream entry in device settings. */\nexport interface CameraStreamEntry {\n readonly url: string\n readonly label?: string\n}\n\nexport interface CameraStreamConfig {\n readonly streams: readonly CameraStreamEntry[]\n /**\n * Snapshot source URL (JPEG).\n * Used internally by the provider to implement ICamera.getSnapshot().\n * Clients should call snapshot.getSnapshot() instead of hitting this URL directly.\n */\n readonly snapshot_url?: string\n}\n\nfunction isString(v: unknown): v is string {\n return typeof v === 'string'\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n}\n\nfunction parseStreamEntry(v: unknown): CameraStreamEntry | null {\n // \"rtsp://...\" → { url }\n if (isString(v) && v.trim()) return { url: v }\n // { url: \"rtsp://...\", label?: \"Main\" } → { url, label }\n if (isRecord(v) && isString(v['url']) && v['url'].trim()) {\n return {\n url: v['url'],\n label: isString(v['label']) ? v['label'] : undefined,\n }\n }\n return null\n}\n\n/** Extract CameraStreamConfig from untyped settings record. */\nexport function parseCameraStreamConfig(raw: Record<string, unknown>): CameraStreamConfig {\n const snapshotUrl = isString(raw['snapshot_url']) ? raw['snapshot_url'] : undefined\n\n // Array format: [\"url\"] or [{url, label}]\n if (Array.isArray(raw['streams'])) {\n const streams: CameraStreamEntry[] = []\n for (const item of raw['streams']) {\n const entry = parseStreamEntry(item)\n if (entry) streams.push(entry)\n }\n return { streams, snapshot_url: snapshotUrl }\n }\n\n // Legacy key-based format: main_stream_url, sub_stream_url, third_stream_url\n const streams: CameraStreamEntry[] = []\n\n const mainUrl = isString(raw['main_stream_url']) ? raw['main_stream_url'] : undefined\n if (mainUrl) streams.push({ url: mainUrl, label: isString(raw['main_stream_name']) ? raw['main_stream_name'] : undefined })\n\n const subUrl = isString(raw['sub_stream_url']) ? raw['sub_stream_url'] : undefined\n if (subUrl) streams.push({ url: subUrl, label: isString(raw['sub_stream_name']) ? raw['sub_stream_name'] : undefined })\n\n const thirdUrl = isString(raw['third_stream_url']) ? raw['third_stream_url'] : undefined\n if (thirdUrl) streams.push({ url: thirdUrl, label: isString(raw['third_stream_name']) ? raw['third_stream_name'] : undefined })\n\n return {\n streams,\n snapshot_url: snapshotUrl,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stream Profile Assignment\n// ---------------------------------------------------------------------------\n\n/**\n * Per-device mapping of quality profile → streamId.\n * Cameras expose N physical streams; the user assigns which stream\n * serves each of the 3 standard CamStack profiles.\n */\nexport interface StreamProfileMap {\n high?: string\n mid?: string\n low?: string\n}\n\n/**\n * Auto-assign profiles based on quality classification.\n * With 1 stream: all profiles point to it.\n * With 2: high + low. With 3+: high + mid + low.\n */\nexport function autoAssignProfiles(streams: readonly AvailableStream[]): StreamProfileMap {\n if (streams.length === 0) return {}\n\n const classified = classifyStreams(streams)\n const map: StreamProfileMap = {}\n\n // Assign by quality label, not array position (classifyStreams preserves original order)\n const high = classified.find((s) => s.quality === 'high')\n const mid = classified.find((s) => s.quality === 'mid')\n const low = classified.find((s) => s.quality === 'low')\n\n if (high) map.high = high.id\n if (mid) map.mid = mid.id\n if (low) map.low = low.id\n\n // Fallback: fill missing profiles from what's available\n const fallback = high ?? mid ?? low ?? classified[0]\n if (fallback) {\n if (!map.high) map.high = fallback.id\n if (!map.mid) map.mid = fallback.id\n if (!map.low) map.low = fallback.id\n }\n\n return map\n}\n\n// ---------------------------------------------------------------------------\n","export interface FeatureManifest {\n streaming: boolean\n notifications: boolean\n objectDetection: boolean\n remoteAccess: boolean\n agentCluster: boolean\n smartHome: boolean\n recordings: boolean\n backup: boolean\n repl: boolean\n}\n\nexport type FeatureFlag = keyof FeatureManifest\n\nexport const DEFAULT_FEATURES: FeatureManifest = {\n streaming: true,\n notifications: true,\n objectDetection: false,\n remoteAccess: true,\n agentCluster: false,\n smartHome: true,\n recordings: true,\n backup: true,\n repl: true,\n}\n","/**\n * Detection Analysis Pipeline -- types for tracked detections, sub-detections,\n * recognition results, and audio classification.\n */\n\nimport type { VideoFrame } from './camera-pipeline.js'\n\n/**\n * A single detection entry consumed by the analysis pipeline (tracking,\n * zones, session aggregation).\n *\n * Intentionally DIFFERENT from `SpatialDetection` (the engine-level\n * detector output in `types/detection.ts`):\n * - `SpatialDetection` is raw model output: `{class, bbox:{x,y,w,h},\n * landmarks?, mask?}`.\n * - `AnalysisDetection` is the pipeline's view: tuple-bbox\n * `[x1,y1,x2,y2]`, optional enrichment (`id`, `label`, `zones`,\n * `previousZones`) added by upstream stages.\n *\n * Renamed from the ambiguous `Detection` on 2026-04-14 to disambiguate\n * from the other Detection types in the codebase (`ObjectDetection`\n * for UI payloads, `SpatialDetection` for engine output).\n */\nexport interface AnalysisDetection {\n className: string\n score: number\n boundingBox?: [number, number, number, number]\n id?: string\n label?: string\n labelScore?: number\n zones?: string[]\n previousZones?: string[]\n}\n\n// --- Detection type const unions ---\n\nexport const SUB_DETECTION_TYPES = ['face', 'plate'] as const\nexport type SubDetectionType = typeof SUB_DETECTION_TYPES[number]\n\nexport const RECOGNITION_TYPES = ['face', 'plate', 'clip', 'custom'] as const\nexport type RecognitionType = typeof RECOGNITION_TYPES[number]\n\n// --- Analysis Context ---\n\nexport interface AnalysisContext {\n readonly deviceId: string\n readonly frame: VideoFrame\n readonly timestamp: number\n readonly rawDetections: readonly AnalysisDetection[]\n readonly trackedDetections: readonly ServerTrackedDetection[]\n readonly events: readonly AnalysisEvent[]\n readonly metadata: Readonly<Record<string, unknown>>\n}\n\n// --- Tracked Detection ---\n\nexport interface ServerTrackedDetection {\n readonly trackId: string\n readonly detection: AnalysisDetection\n readonly crop?: Buffer\n readonly tracking: TrackingInfo\n readonly subDetections: readonly SubDetection[]\n readonly recognitions: readonly RecognitionResult[]\n readonly zones: readonly string[]\n readonly previousZones: readonly string[]\n}\n\nexport interface TrackingInfo {\n readonly age: number\n readonly state: 'moving' | 'stationary' | 'new' | 'lost'\n readonly stationaryDuration: number\n readonly velocity: { readonly dx: number; readonly dy: number }\n readonly positionHistory: ReadonlyArray<{ readonly x: number; readonly y: number; readonly t: number }>\n}\n\n// --- Sub-Detection ---\n\nexport interface SubDetection {\n readonly detectionType: SubDetectionType\n readonly boundingBox: readonly [number, number, number, number]\n readonly score: number\n readonly crop?: Buffer\n}\n\n// --- Recognition ---\n\nexport interface RecognitionResult {\n readonly recognitionType: RecognitionType\n readonly label: string\n readonly score: number\n readonly embedding?: readonly number[]\n readonly metadata?: Readonly<Record<string, unknown>>\n}\n\n// --- Analysis Events ---\n\nexport interface AnalysisEvent {\n readonly category: string\n readonly severity: 'info' | 'warning' | 'alert'\n readonly detection: ServerTrackedDetection\n readonly description: string\n readonly data: Readonly<Record<string, unknown>>\n}\n\n// --- Audio Classifier ---\n//\n// The canonical `IAudioClassifier` lives in `./audio-analyzer.ts` and is\n// derived from the capability definition. The legacy interface that lived\n// here had a different shape (readonly ready + release()) and was never\n// actually consumed outside of this file — removed to avoid confusion.\n","/**\n * Analysis Persistence Types — interfaces for event persistence, track trails,\n * retention, session tracking, and known faces.\n *\n * These interfaces define the contracts used by the server; concrete\n * implementations live in addon-analytics and are received via CapabilityRegistry.\n */\n\nimport type { ServerTrackedDetection } from './server-analysis.js'\n\n// ---------------------------------------------------------------------------\n// Event Persistence\n// ---------------------------------------------------------------------------\n\nexport interface ObjectSnapshotResult {\n readonly trackId: string\n readonly thumbnail?: Buffer\n readonly fullCrop?: Buffer\n}\n\nexport interface AnnotatedSnapshotResult {\n readonly thumbnail: Buffer\n readonly full: Buffer\n}\n\nexport interface PersistableEvent {\n readonly id: string\n readonly timestamp: number\n readonly deviceId: string\n readonly category: string\n readonly className: string\n readonly score: number\n readonly trackId: string\n readonly severity: string\n readonly description: string\n readonly data: Record<string, unknown>\n readonly mediaFiles: readonly string[]\n}\n\nexport interface EventBufferStatus {\n readonly eventCount: number\n readonly mediaCount: number\n readonly mediaSizeMB: number\n}\n\nexport type TrackMediaType =\n | 'crop-thumb'\n | 'crop-full'\n | 'debug-annotated-thumb'\n | 'debug-annotated-full'\n | 'original'\n | 'original-thumb'\n | 'inline-crop'\n | 'unknown'\n\nexport interface TrackMediaFile {\n readonly path: string\n readonly type: TrackMediaType\n readonly data: Buffer\n readonly source: 'buffer' | 'storage'\n}\n\n// ---------------------------------------------------------------------------\n// Track Trail\n// ---------------------------------------------------------------------------\n\n// F15c: `enabled` removed — trail capture on/off lives on the\n// `track-trail` wrapper binding. See capabilities/track-trail.cap.ts.\nexport interface TrackCaptureConfig {\n readonly snapshotIntervalMs: number\n readonly maxTrailLength: number\n readonly saveThumbnails: boolean\n readonly thumbnailSize: { readonly width: number; readonly height: number }\n}\n\nexport interface TrackPosition {\n readonly x: number\n readonly y: number\n readonly timestamp: number\n readonly bbox: [number, number, number, number]\n}\n\nexport interface TrackSnapshot {\n readonly timestamp: number\n readonly position: TrackPosition\n readonly thumbnailPath: string\n}\n\nexport interface TrackTrail {\n readonly trackId: string\n readonly deviceId: number\n readonly className: string\n readonly label?: string\n readonly firstSeen: number\n readonly lastSeen: number\n readonly positions: readonly TrackPosition[]\n readonly snapshots: readonly TrackSnapshot[]\n readonly totalDistance: number\n readonly zonesVisited: readonly string[]\n readonly active: boolean\n}\n\n// ---------------------------------------------------------------------------\n// Retention\n// ---------------------------------------------------------------------------\n\nexport interface RetentionConfig {\n readonly cleanupIntervalMs: number\n readonly detectionEventsDays: number\n readonly audioLevelsDays: number\n /** @deprecated — snapshots are tied to events, use detectionEventsDays */\n readonly snapshotsDays: number\n readonly deviceOverrides?: Readonly<Record<string, Partial<Pick<RetentionConfig, 'detectionEventsDays' | 'audioLevelsDays' | 'snapshotsDays'>>>>\n}\n\nexport const DEFAULT_RETENTION: RetentionConfig = {\n cleanupIntervalMs: 60 * 60 * 1000,\n detectionEventsDays: 30,\n audioLevelsDays: 7,\n snapshotsDays: 14,\n}\n\nexport interface RetentionReport {\n readonly deletedEvents: number\n readonly deletedAudioRecords: number\n readonly deletedSnapshots: number\n}\n\n// ---------------------------------------------------------------------------\n// Session Tracker\n// ---------------------------------------------------------------------------\n\nexport interface SessionTrack {\n readonly trackId: string\n readonly deviceId: string\n readonly className: string\n readonly label?: string\n readonly firstSeen: number\n readonly lastSeen: number\n readonly totalFrames: number\n readonly lastDetection: ServerTrackedDetection\n readonly state: string\n readonly positions: ReadonlyArray<{ readonly x: number; readonly y: number; readonly t: number }>\n readonly embedding?: Float32Array\n readonly globalId?: string\n readonly bestCrop?: Buffer\n}\n\nexport interface GlobalIdentity {\n readonly globalId: string\n readonly embedding: Float32Array\n readonly label?: string\n readonly firstSeen: number\n readonly lastSeen: number\n readonly deviceIds: ReadonlyArray<string>\n}\n\n// ---------------------------------------------------------------------------\n// Known Faces\n// ---------------------------------------------------------------------------\n\nexport interface ClipRecognizer {\n getEmbedding(imageBuffer: Buffer): Promise<Float32Array>\n}\n\nexport interface KnownFaceEntry {\n readonly id: string\n readonly label: string\n readonly group?: string\n readonly embedding: readonly number[]\n readonly cropBase64: string\n readonly createdAt: number\n readonly updatedAt: number\n readonly source?: string\n readonly metadata?: Readonly<Record<string, unknown>>\n}\n\n// ---------------------------------------------------------------------------\n// Recording Addon (generic interface for recording engine access)\n// ---------------------------------------------------------------------------\n\n/**\n * Interface for the recording addon — the server uses this instead of\n * importing RecordingAddon directly from addon-recording.\n */\nexport interface IRecordingAddon {\n getCoordinator(): IRecordingCoordinator\n getRecordingDb(): IRecordingDb\n}\n\n/**\n * Minimal coordinator interface for the server's recording router.\n */\nexport interface IRecordingCoordinator {\n enableRecording(deviceId: string, options: {\n policy: unknown\n ffmpegOverrides?: unknown\n }): Promise<void>\n disableRecording(deviceId: string): Promise<void>\n isRecording(deviceId: string): boolean\n readonly playlistGenerator: {\n generate(\n deviceId: string,\n streamId: string,\n startTime: number,\n endTime: number,\n options?: { live?: boolean },\n ): unknown\n }\n readonly storageEstimator: {\n estimateForDevice(\n deviceId: string,\n motionInput?: { avgEventsPerDay: number; avgDurationSec: number },\n ): { totalEstimatedGb: number; [key: string]: unknown }\n }\n}\n\n/**\n * Minimal recording database interface for the server's recording router.\n */\nexport interface IRecordingDb {\n upsertStorageConfig(config: {\n deviceId: string\n dataCategory: string\n storageName: string\n subDirectory: string\n retentionDays: number | null\n retentionGb: number | null\n }): void\n getPolicy(deviceId: string): { readonly enabled: boolean; readonly mode: string; [key: string]: unknown } | null\n getEnabledPolicies(): Array<{ deviceId: string; [key: string]: unknown }>\n querySegments(deviceId: string, streamId: string, startTime: number, endTime: number): unknown\n getAvailability(deviceId: string, startTime: number, endTime: number): unknown\n getStorageUsage(deviceId: string, streamId: string): unknown\n upsertPolicy(policy: unknown): void\n findNearestThumbnail(deviceId: string, timestamp: number, category: string): unknown\n getMotionStats(deviceId: string, startTime: number, endTime: number): {\n avgEventsPerDay: number\n avgDurationSec: number\n dutyCyclePercent: number\n totalEvents: number\n }\n resolveStorageConfig(deviceId: string, dataCategory: string): unknown\n}\n\n// ---------------------------------------------------------------------------\n// Sub-service interfaces — used by the server's thin NestJS wrappers\n// to receive delegates via CapabilityRegistry.\n// ---------------------------------------------------------------------------\n\nexport interface IEventPersistence {\n start(): void\n stop(): void\n saveDetectionCrops(eventId: string, crops: readonly ObjectSnapshotResult[]): void\n saveAnnotatedFrame(eventId: string, result: AnnotatedSnapshotResult): void\n saveOriginalFrame(eventId: string, frame: import('./camera-pipeline.js').VideoFrame): Promise<void>\n getEventMedia(eventId: string): Promise<readonly TrackMediaFile[]>\n getTrackMedia(trackId: string): Promise<readonly TrackMediaFile[]>\n getDeviceMedia(deviceId: string, since?: number, until?: number): Promise<readonly TrackMediaFile[]>\n getBufferStatus(): { pendingEvents: number; pendingMedia: number; lastFlush?: number }\n flush(): Promise<void>\n}\n\nexport interface ITrackTrail {\n setConfig(deviceId: string, config: Partial<TrackCaptureConfig>): void\n getConfig(deviceId: string): TrackCaptureConfig\n recordFrame(ctx: unknown): Promise<void>\n getActiveTrail(trackId: string): TrackTrail | null\n getActiveTrails(deviceId: string): readonly TrackTrail[]\n getPersistedTrail(trackId: string): Promise<TrackTrail | null>\n getTrail(trackId: string): Promise<TrackTrail | null>\n listTrails(deviceId: string, options?: {\n since?: number\n until?: number\n limit?: number\n className?: string\n }): Promise<readonly TrackTrail[]>\n}\n\nexport interface IRetention {\n start(): void\n stop(): void\n runCleanup(): Promise<unknown>\n setConfig(update: Partial<RetentionConfig>): void\n getConfig(): unknown\n forceCleanup(): Promise<unknown>\n}\n\nexport interface ISessionTracker {\n updateFromAnalysis(deviceId: string, ctx: unknown): void\n getActiveTracks(deviceId: string): readonly SessionTrack[]\n getAllActiveTracks(): readonly SessionTrack[]\n getTrack(deviceId: string, trackId: string): SessionTrack | undefined\n getTrackCounts(): Record<string, number>\n clearDevice(deviceId: string): void\n clearAll(): void\n getGlobalPool(): unknown\n}\n\nexport interface KnownFaceSummary {\n readonly id: string\n readonly label: string\n readonly group?: string\n readonly cropBase64: string\n readonly createdAt: number\n readonly updatedAt: number\n readonly source?: string\n readonly sourceType?: string\n readonly count?: number\n}\n\nexport interface IKnownFaces {\n register(entry: KnownFaceEntry): Promise<void>\n listAll(): Promise<readonly KnownFaceSummary[]>\n delete(id: string): Promise<void>\n update(id: string, updates: Partial<Pick<KnownFaceEntry, 'label' | 'group'>>): Promise<void>\n recalculateEmbedding(id: string, clipRecognizer: ClipRecognizer): Promise<void>\n findMatch(embedding: Float32Array, threshold: number): Promise<unknown>\n batchRegister(\n entries: ReadonlyArray<{ readonly label: string; readonly cropBase64: string; readonly group?: string }>,\n clipRecognizer: ClipRecognizer,\n ): Promise<unknown>\n}\n\n/**\n * Composite interface exposed via CapabilityRegistry as the\n * 'analysis-data-persistence' singleton. Each sub-service is wired\n * individually to the corresponding NestJS wrapper service.\n */\nexport interface IAnalysisDataPersistence {\n readonly eventPersistence: IEventPersistence\n readonly knownFaces: IKnownFaces\n readonly sessionTracker: ISessionTracker\n readonly retention: IRetention\n readonly trackTrail: ITrackTrail\n}\n\n// ---------------------------------------------------------------------------\n// Provider Connection Testing\n// ---------------------------------------------------------------------------\n\nexport interface TestConnectionResult {\n readonly success: boolean\n readonly version?: string\n readonly cameraCount?: number\n readonly error?: string\n}\n\n/**\n * Generic interface for testing provider connections.\n * Each provider addon can expose a connection tester.\n */\nexport interface IProviderConnectionTester {\n testConnection(): Promise<TestConnectionResult>\n}\n","export enum EventSourceType {\n Addon = 'addon',\n Core = 'core',\n Device = 'device',\n Provider = 'provider',\n System = 'system',\n}\n","export const HF_REPO = 'camstack/camstack-models'\nexport const HF_BASE_URL = `https://huggingface.co/${HF_REPO}/resolve/main`\n\n/**\n * Shape of {@link RUNTIME_DEFAULTS}. Every key/value is typed explicitly so\n * consumers (ConfigManager.raw, feature accessors, settings-store seeder)\n * can read fields without `as` casts.\n *\n * Iteration via `Object.entries` still yields `[string, RuntimeDefaultsShape[keyof RuntimeDefaultsShape]]`\n * which is assignable to `[string, unknown]` — no change for consumers that\n * only need the iteration behaviour.\n */\nexport interface RuntimeDefaultsProvider {\n id: string\n type: string\n name: string\n}\n\nexport interface RuntimeDefaultsShape {\n /** Index signature for dynamic string-path lookups (e.g. ConfigManager.get).\n * Explicit keys below override this with their precise types. */\n [key: string]: unknown\n 'features.streaming': boolean\n 'features.notifications': boolean\n 'features.objectDetection': boolean\n 'features.remoteAccess': boolean\n 'features.agentCluster': boolean\n 'features.smartHome': boolean\n 'features.recordings': boolean\n 'features.backup': boolean\n 'features.repl': boolean\n 'retention.detectionEventsDays': number\n 'retention.audioLevelsDays': number\n 'logging.level': string\n 'logging.retentionDays': number\n 'eventBus.ringBufferSize': number\n 'storage.provider': string\n 'storage.locations': Record<string, string>\n 'providers': RuntimeDefaultsProvider[]\n 'recording.segmentDurationSeconds': number\n 'recording.defaultRetentionDays': number\n 'ffmpeg.binaryPath': string\n 'ffmpeg.hwAccel': string\n 'ffmpeg.threadCount': number\n 'auth.tokenExpiry': string\n}\n\n/**\n * Runtime defaults -- used by ConfigManager.get() for backward compatibility\n * until Plan B wires all runtime settings to the system_settings SQL table.\n *\n * Moved from @camstack/kernel/config-schema to @camstack/types so that\n * @camstack/core can reference it without a cross-package kernel import.\n */\nexport const RUNTIME_DEFAULTS: RuntimeDefaultsShape = {\n 'features.streaming': true,\n 'features.notifications': true,\n 'features.objectDetection': false,\n 'features.remoteAccess': true,\n 'features.agentCluster': false,\n 'features.smartHome': true,\n 'features.recordings': true,\n 'features.backup': true,\n 'features.repl': true,\n 'retention.detectionEventsDays': 30,\n 'retention.audioLevelsDays': 7,\n 'logging.level': 'info',\n 'logging.retentionDays': 30,\n 'eventBus.ringBufferSize': 10000,\n 'storage.provider': 'sqlite-storage',\n 'storage.locations': {\n data: 'camstack-data/data',\n media: 'camstack-data/media',\n recordings: 'camstack-data/recordings',\n cache: '/tmp/camstack-cache',\n logs: 'camstack-data/logs',\n models: 'camstack-data/models',\n },\n 'providers': [],\n // Recording\n 'recording.segmentDurationSeconds': 4,\n 'recording.defaultRetentionDays': 30,\n // Streaming ports are addon-specific (go2rtc owns its defaults)\n // FFmpeg\n 'ffmpeg.binaryPath': 'ffmpeg',\n 'ffmpeg.hwAccel': 'auto',\n 'ffmpeg.threadCount': 0,\n // Detection / motion / audio settings are owned by their respective addons\n // (pipeline-orchestrator, motion-wasm, audio-analyzer/classifier, detection-pipeline\n // step schemas). No detection defaults at this level.\n // Backup retention is addon-specific (local-backup owns its default)\n // Auth (runtime)\n 'auth.tokenExpiry': '24h',\n}\n","/**\n * Type-safe JSON parsing helpers.\n *\n * `JSON.parse` is typed as `any` in lib.es5.d.ts, which triggers\n * `no-unsafe-*` ESLint rules and destroys downstream inference. These\n * wrappers return `unknown` — callers narrow structurally via type\n * guards, `typeof` checks, or helpers like `asRecord`/`asString`.\n */\n\n/**\n * Parse JSON and return it as `unknown` — the only entry point for untrusted JSON.\n *\n * The optional generic overload `parseJsonUnknown<T>(text)` returns `T` for\n * call sites that know the shape at parse time (e.g. MQTT payloads with a\n * known protocol schema). This is a **type-level bridge only** — no runtime\n * validation is performed. Callers that need runtime validation should parse\n * as `unknown` and narrow via Zod or structural guards.\n */\nexport function parseJsonUnknown<T = unknown>(text: string): T {\n // Isolate JSON.parse's `any` to this single statement.\n const parsed: unknown = JSON.parse(text)\n return parsed as T\n}\n\n/** Narrow an unknown value to a plain `Record<string, unknown>` or return null. */\nexport function asJsonObject(value: unknown): Record<string, unknown> | null {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n return null\n }\n return { ...value }\n}\n\n/** Narrow an unknown value to a `readonly unknown[]` or return an empty array. */\nexport function asJsonArray(value: unknown): readonly unknown[] {\n return Array.isArray(value) ? value : []\n}\n\n/** Safe string extraction from an unknown record field. */\nexport function asString(value: unknown, fallback = ''): string {\n return typeof value === 'string' ? value : fallback\n}\n\n/** Safe number extraction from an unknown record field. */\nexport function asNumber(value: unknown, fallback = 0): number {\n return typeof value === 'number' ? value : fallback\n}\n\n/** Safe boolean extraction from an unknown record field. */\nexport function asBoolean(value: unknown, fallback = false): boolean {\n return typeof value === 'boolean' ? value : fallback\n}\n\n/** Parse JSON + narrow to object in one step. */\nexport function parseJsonObject(text: string): Record<string, unknown> | null {\n try {\n return asJsonObject(parseJsonUnknown(text))\n } catch {\n return null\n }\n}\n\n/** Parse JSON + narrow to array in one step. */\nexport function parseJsonArray(text: string): readonly unknown[] | null {\n try {\n const parsed = parseJsonUnknown(text)\n return Array.isArray(parsed) ? parsed : null\n } catch {\n return null\n }\n}\n","export function hfModelUrl(repo: string, path: string): string {\n return `https://huggingface.co/${repo}/resolve/main/${path}`\n}\n","/** Cosine similarity between two embedding vectors */\nexport function cosineSimilarity(a: Float32Array, b: Float32Array): number {\n if (a.length !== b.length) return 0\n let dotProduct = 0\n let normA = 0\n let normB = 0\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i]! * b[i]!\n normA += a[i]! * a[i]!\n normB += b[i]! * b[i]!\n }\n const denom = Math.sqrt(normA) * Math.sqrt(normB)\n return denom === 0 ? 0 : dotProduct / denom\n}\n","import type { IElementConfig } from '../interfaces/context.js'\nimport type { ISettingsBackend } from '../interfaces/storage.js'\n\n/**\n * Persisted config store for a single element.\n * Reads/writes to the element's scoped storage under the 'config' collection.\n * Notifies listeners on every change.\n */\nexport class ElementConfigStore implements IElementConfig {\n private cache: Record<string, unknown> = {}\n private listeners: Set<(config: Record<string, unknown>) => void> = new Set()\n private loaded = false\n\n private readonly getBackend: () => ISettingsBackend | null\n\n constructor(\n private readonly elementId: string,\n settingsBackend: ISettingsBackend | (() => ISettingsBackend | null) | null,\n ) {\n // Accept either a direct backend or a lazy getter for late-wired backends\n this.getBackend = typeof settingsBackend === 'function'\n ? settingsBackend\n : () => settingsBackend\n }\n\n /** Load config from storage into cache. Called once on first access. */\n private async ensureLoaded(): Promise<void> {\n if (this.loaded) return\n const backend = this.getBackend()\n if (!backend) {\n throw new Error(`ElementConfigStore(${this.elementId}): settings backend not available`)\n }\n\n const records = await backend.query({ collection: 'config', filter: {\n where: { id: this.elementId },\n limit: 1,\n } })\n if (records.length > 0) {\n this.cache = records[0]!.data ?? {}\n }\n this.loaded = true\n }\n\n getAll(): Record<string, unknown> {\n return { ...this.cache }\n }\n\n get<T = unknown>(key: string): T | undefined {\n const parts = key.split('.')\n let current: unknown = this.cache\n for (const part of parts) {\n if (current === null || typeof current !== 'object' || Array.isArray(current)) return undefined\n current = Reflect.get(current, part)\n }\n // Type-level bridge: callers supply T based on their knowledge of the\n // stored schema — the runtime has no way to prove the shape matches.\n return current as T | undefined\n }\n\n async set(key: string, value: unknown): Promise<void> {\n await this.ensureLoaded()\n setNestedValue(this.cache, key, value)\n await this.persist()\n this.notifyListeners()\n }\n\n async setAll(config: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n this.cache = { ...config }\n await this.persist()\n this.notifyListeners()\n }\n\n onChange(callback: (config: Record<string, unknown>) => void): () => void {\n this.listeners.add(callback)\n return () => { this.listeners.delete(callback) }\n }\n\n /** Initialize from storage — called by ContextFactory after creation */\n async load(): Promise<void> {\n await this.ensureLoaded()\n }\n\n /** Initialize with default values (doesn't overwrite existing) */\n async loadDefaults(defaults: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n if (Object.keys(this.cache).length === 0) {\n this.cache = { ...defaults }\n await this.persist()\n }\n }\n\n private async persist(): Promise<void> {\n const backend = this.getBackend()\n if (!backend) {\n throw new Error(`ElementConfigStore(${this.elementId}): settings backend not available — cannot persist config`)\n }\n\n // Use set() for upsert semantics — avoids race condition where two\n // concurrent persist() calls both see \"no existing row\" and both insert.\n await backend.set({ collection: 'config', key: this.elementId, value: this.cache })\n }\n\n private notifyListeners(): void {\n const snapshot = this.getAll()\n for (const listener of this.listeners) {\n try {\n listener(snapshot)\n } catch {\n // Don't let one bad listener kill others\n }\n }\n }\n}\n\nfunction setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split('.')\n let current: Record<string, unknown> = obj\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!\n const existing = current[part]\n if (existing === null || typeof existing !== 'object' || Array.isArray(existing)) {\n const fresh: Record<string, unknown> = {}\n current[part] = fresh\n current = fresh\n } else {\n current = existing as Record<string, unknown>\n }\n }\n current[parts[parts.length - 1]!] = value\n}\n","/**\n * Canonical runtime/backend/format mapping utilities.\n *\n * This is the SINGLE SOURCE OF TRUTH for mapping between:\n * - Pipeline config (runtime: 'node'|'python', backend: 'cpu'|'coreml'|'cuda'|...)\n * - DetectionRuntime ('onnx'|'coreml'|'pytorch'|'openvino'|'tflite')\n * - ModelFormat ('onnx'|'coreml'|'openvino'|'tflite'|'pt')\n *\n * Import from '@camstack/types' — do NOT duplicate these mappings anywhere.\n */\nimport type { ModelFormat } from '../types/models.js'\nimport type { DetectionRuntime } from '../types/config.js'\n\n// ---------------------------------------------------------------------------\n// Pipeline runtime: the two-level abstraction used in pipeline config\n// ---------------------------------------------------------------------------\n\n/** Pipeline-level runtime: 'node' (ONNX Runtime in Node.js) or 'python' (Python inference) */\nexport type PipelineRuntime = 'node' | 'python'\n\n// ---------------------------------------------------------------------------\n// Backend → Format: what model file format does a backend require?\n// ---------------------------------------------------------------------------\n\n/**\n * Map a backend ID to the model format it requires.\n *\n * Node.js backends (cpu, coreml, cuda, tensorrt) all use ONNX format\n * because onnxruntime-node loads .onnx and uses execution providers internally.\n *\n * Python backends use their native format.\n */\nconst BACKEND_TO_FORMAT: Readonly<Record<string, ModelFormat>> = {\n // Node.js backends — all ONNX (onnxruntime-node execution providers)\n cpu: 'onnx',\n coreml: 'onnx', // onnxruntime CoreML EP loads .onnx, not .mlpackage\n cuda: 'onnx',\n tensorrt: 'onnx',\n 'onnx-py': 'onnx',\n // Python native backends\n pytorch: 'pt',\n openvino: 'openvino',\n}\n\n/**\n * Map DetectionRuntime to ModelFormat.\n * Used when the addon receives an explicit runtime (not 'auto').\n */\nconst RUNTIME_TO_FORMAT: Readonly<Record<DetectionRuntime, ModelFormat>> = {\n onnx: 'onnx',\n coreml: 'coreml',\n openvino: 'openvino',\n tflite: 'tflite',\n pytorch: 'pt',\n}\n\n/**\n * Map a Python backend to the Python inference script name.\n */\nconst PYTHON_SCRIPT: Readonly<Record<string, string>> = {\n coreml: 'coreml_inference.py',\n pytorch: 'pytorch_inference.py',\n openvino: 'openvino_inference.py',\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve pipeline config (runtime + backend) to a DetectionRuntime.\n *\n * Pipeline config uses two levels:\n * runtime: 'node' | 'python'\n * backend: 'cpu' | 'coreml' | 'cuda' | 'openvino' | 'pytorch' | ...\n *\n * Addons expect a DetectionRuntime:\n * 'onnx' | 'coreml' | 'pytorch' | 'openvino' | 'tflite'\n *\n * Mapping rules:\n * node + any backend → 'onnx' (onnxruntime-node handles backend via EP)\n * python + coreml → 'coreml' (native .mlpackage via coremltools)\n * python + openvino → 'openvino'\n * python + pytorch → 'pytorch'\n * python + onnx-py → 'onnx'\n * python + <other> → backend as-is\n */\nexport function resolveDetectionRuntime(\n pipelineRuntime: PipelineRuntime | string,\n backend: string,\n): DetectionRuntime {\n if (pipelineRuntime === 'node') return 'onnx'\n // Python: backend IS the runtime (coreml, openvino, pytorch, etc.)\n const map: Record<string, DetectionRuntime> = {\n coreml: 'coreml',\n openvino: 'openvino',\n pytorch: 'pytorch',\n 'onnx-py': 'onnx',\n cpu: 'onnx',\n }\n return map[backend] ?? (backend as DetectionRuntime)\n}\n\n/**\n * Resolve pipeline config (runtime + backend) to a ModelFormat.\n *\n * Determines which model file format is needed (.onnx, .mlpackage, etc.)\n */\nexport function resolveModelFormat(\n pipelineRuntime: PipelineRuntime | string,\n backend: string,\n): ModelFormat {\n if (pipelineRuntime === 'node') {\n return BACKEND_TO_FORMAT[backend] ?? 'onnx'\n }\n // Python: derive format from backend\n const map: Record<string, ModelFormat> = {\n coreml: 'coreml',\n openvino: 'openvino',\n pytorch: 'pt',\n 'onnx-py': 'onnx',\n cpu: 'onnx',\n }\n return map[backend] ?? 'onnx'\n}\n\n/**\n * Resolve pipeline runtime string to 'auto' | DetectionRuntime for addon consumption.\n * Used when building addon addonConfig from pipeline step config.\n *\n * 'python' + 'coreml' → 'coreml'\n * 'node' + 'coreml' → 'onnx'\n * 'node' + 'cpu' → 'onnx'\n * 'auto' → 'auto'\n */\nexport function resolveAddonRuntime(\n pipelineRuntime: string | undefined,\n backend: string | undefined,\n): DetectionRuntime | 'auto' {\n if (!pipelineRuntime || pipelineRuntime === 'auto') return 'auto'\n return resolveDetectionRuntime(pipelineRuntime, backend ?? 'cpu')\n}\n\n/**\n * Get the model format needed for a given backend.\n * Convenience wrapper for backend-only lookup (e.g., UI components).\n */\nexport function formatForBackend(backend: string): ModelFormat {\n return BACKEND_TO_FORMAT[backend] ?? 'onnx'\n}\n\n/**\n * Get the model format for a given DetectionRuntime.\n */\nexport function formatForRuntime(runtime: DetectionRuntime): ModelFormat {\n return RUNTIME_TO_FORMAT[runtime]\n}\n\n/**\n * Get the Python inference script name for a backend.\n * Returns undefined if no Python script is needed (e.g., 'cpu' uses ONNX).\n */\nexport function pythonScriptForBackend(backend: string): string | undefined {\n return PYTHON_SCRIPT[backend]\n}\n\n/**\n * Check if a DetectionRuntime requires a Python binary.\n * 'coreml', 'pytorch', 'openvino' need Python; 'onnx' and 'tflite' don't.\n */\nexport function requiresPython(runtime: DetectionRuntime | string): boolean {\n return runtime === 'coreml' || runtime === 'pytorch' || runtime === 'openvino'\n}\n\n// Re-export constants for consumers that need the raw maps (e.g., UI filtering)\nexport { BACKEND_TO_FORMAT, RUNTIME_TO_FORMAT, PYTHON_SCRIPT }\n","import { errMsg } from './err-msg.js'\n\n/**\n * runInferenceStep — shared utility for executing a single inference call\n * with timing, error capture, and optional timeout.\n *\n * Used by: pipeline-executor, benchmark-executor, benchmark.service,\n * inference-engine-adapter — eliminating duplicated timing/error patterns.\n */\n\nexport interface InferenceStepResult<T> {\n /** The inference output, or undefined if the call failed */\n readonly output: T | undefined\n /** Wall-clock duration in ms (rounded to 2 decimals) */\n readonly durationMs: number\n /** True if the call succeeded */\n readonly ok: boolean\n /** Error message if the call failed */\n readonly error?: string\n}\n\n/**\n * Execute an inference function with timing and error capture.\n *\n * @param fn - The inference call (e.g. `() => engine.infer(frame)`)\n * @param timeoutMs - Optional timeout in ms. If exceeded, rejects with timeout error.\n * @returns InferenceStepResult with output, timing, and error info\n */\nexport async function runInferenceStep<T>(\n fn: () => Promise<T>,\n timeoutMs?: number,\n): Promise<InferenceStepResult<T>> {\n const t0 = performance.now()\n\n try {\n const output = timeoutMs !== undefined\n ? await withTimeout(fn(), timeoutMs)\n : await fn()\n\n return {\n output,\n durationMs: roundMs(performance.now() - t0),\n ok: true,\n }\n } catch (err) {\n return {\n output: undefined,\n durationMs: roundMs(performance.now() - t0),\n ok: false,\n error: errMsg(err),\n }\n }\n}\n\n/** Round to 2 decimal places */\nfunction roundMs(ms: number): number {\n return Math.round(ms * 100) / 100\n}\n\n/** Reject a promise if it exceeds the given timeout */\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`Inference timeout after ${ms}ms`)), ms)\n promise\n .then((v) => { clearTimeout(timer); resolve(v) })\n .catch((e: unknown) => { clearTimeout(timer); reject(e) })\n })\n}\n","import type { LabelDefinition, ClassMapDefinition } from '../types/labels.js'\n\nexport const COCO_80_LABELS: readonly LabelDefinition[] = [\n { id: 'person', name: 'Person' },\n { id: 'bicycle', name: 'Bicycle' },\n { id: 'car', name: 'Car' },\n { id: 'motorcycle', name: 'Motorcycle' },\n { id: 'airplane', name: 'Airplane' },\n { id: 'bus', name: 'Bus' },\n { id: 'train', name: 'Train' },\n { id: 'truck', name: 'Truck' },\n { id: 'boat', name: 'Boat' },\n { id: 'traffic light', name: 'Traffic Light' },\n { id: 'fire hydrant', name: 'Fire Hydrant' },\n { id: 'stop sign', name: 'Stop Sign' },\n { id: 'parking meter', name: 'Parking Meter' },\n { id: 'bench', name: 'Bench' },\n { id: 'bird', name: 'Bird' },\n { id: 'cat', name: 'Cat' },\n { id: 'dog', name: 'Dog' },\n { id: 'horse', name: 'Horse' },\n { id: 'sheep', name: 'Sheep' },\n { id: 'cow', name: 'Cow' },\n { id: 'elephant', name: 'Elephant' },\n { id: 'bear', name: 'Bear' },\n { id: 'zebra', name: 'Zebra' },\n { id: 'giraffe', name: 'Giraffe' },\n { id: 'backpack', name: 'Backpack' },\n { id: 'umbrella', name: 'Umbrella' },\n { id: 'handbag', name: 'Handbag' },\n { id: 'tie', name: 'Tie' },\n { id: 'suitcase', name: 'Suitcase' },\n { id: 'frisbee', name: 'Frisbee' },\n { id: 'skis', name: 'Skis' },\n { id: 'snowboard', name: 'Snowboard' },\n { id: 'sports ball', name: 'Sports Ball' },\n { id: 'kite', name: 'Kite' },\n { id: 'baseball bat', name: 'Baseball Bat' },\n { id: 'baseball glove', name: 'Baseball Glove' },\n { id: 'skateboard', name: 'Skateboard' },\n { id: 'surfboard', name: 'Surfboard' },\n { id: 'tennis racket', name: 'Tennis Racket' },\n { id: 'bottle', name: 'Bottle' },\n { id: 'wine glass', name: 'Wine Glass' },\n { id: 'cup', name: 'Cup' },\n { id: 'fork', name: 'Fork' },\n { id: 'knife', name: 'Knife' },\n { id: 'spoon', name: 'Spoon' },\n { id: 'bowl', name: 'Bowl' },\n { id: 'banana', name: 'Banana' },\n { id: 'apple', name: 'Apple' },\n { id: 'sandwich', name: 'Sandwich' },\n { id: 'orange', name: 'Orange' },\n { id: 'broccoli', name: 'Broccoli' },\n { id: 'carrot', name: 'Carrot' },\n { id: 'hot dog', name: 'Hot Dog' },\n { id: 'pizza', name: 'Pizza' },\n { id: 'donut', name: 'Donut' },\n { id: 'cake', name: 'Cake' },\n { id: 'chair', name: 'Chair' },\n { id: 'couch', name: 'Couch' },\n { id: 'potted plant', name: 'Potted Plant' },\n { id: 'bed', name: 'Bed' },\n { id: 'dining table', name: 'Dining Table' },\n { id: 'toilet', name: 'Toilet' },\n { id: 'tv', name: 'TV' },\n { id: 'laptop', name: 'Laptop' },\n { id: 'mouse', name: 'Mouse' },\n { id: 'remote', name: 'Remote' },\n { id: 'keyboard', name: 'Keyboard' },\n { id: 'cell phone', name: 'Cell Phone' },\n { id: 'microwave', name: 'Microwave' },\n { id: 'oven', name: 'Oven' },\n { id: 'toaster', name: 'Toaster' },\n { id: 'sink', name: 'Sink' },\n { id: 'refrigerator', name: 'Refrigerator' },\n { id: 'book', name: 'Book' },\n { id: 'clock', name: 'Clock' },\n { id: 'vase', name: 'Vase' },\n { id: 'scissors', name: 'Scissors' },\n { id: 'teddy bear', name: 'Teddy Bear' },\n { id: 'hair drier', name: 'Hair Drier' },\n { id: 'toothbrush', name: 'Toothbrush' },\n] as const\n\nexport const MACRO_LABELS: readonly LabelDefinition[] = [\n { id: 'person', name: 'Person' },\n { id: 'vehicle', name: 'Vehicle' },\n { id: 'animal', name: 'Animal' },\n] as const\n\nexport const COCO_TO_MACRO: ClassMapDefinition = {\n mapping: {\n person: 'person',\n bicycle: 'vehicle',\n car: 'vehicle',\n motorcycle: 'vehicle',\n airplane: 'vehicle',\n bus: 'vehicle',\n train: 'vehicle',\n truck: 'vehicle',\n boat: 'vehicle',\n bird: 'animal',\n cat: 'animal',\n dog: 'animal',\n horse: 'animal',\n sheep: 'animal',\n cow: 'animal',\n elephant: 'animal',\n bear: 'animal',\n zebra: 'animal',\n giraffe: 'animal',\n },\n // Today the pipeline only supports the three macro classes. Any other\n // COCO class (carrot, umbrella, spoon, …) is dropped upstream in the\n // executor instead of leaking through as a `macroClass: \"carrot\"`\n // detection with no child routing. When support for additional COCO\n // classes lands, either add them to `mapping` (mapped to a new macro)\n // or flip this back to `true` and teach `matchesMacroFilter` to handle\n // the raw-COCO case.\n preserveOriginal: false,\n}\n","/**\n * @deprecated Use DeviceType from '@camstack/types/device' instead.\n * This file is kept for backwards compatibility during migration.\n */\n\n/* eslint-disable @typescript-eslint/no-deprecated */\n\nexport enum DeviceType {\n Camera = 'camera',\n}\n\nexport interface DeviceTypeInfo {\n readonly type: DeviceType\n readonly label: string\n readonly icon: string // lucide icon name\n}\n\nexport const DEVICE_TYPE_INFO: Record<DeviceType, DeviceTypeInfo> = {\n [DeviceType.Camera]: { type: DeviceType.Camera, label: 'Camera', icon: 'camera' },\n}\n","import { z } from 'zod'\n\n/**\n * Optional callback invoked when `fromSchema` had to drop one or\n * more persisted fields to recover from a Zod validation failure.\n * Used by `BaseDevice` to surface the action via the device logger.\n */\nexport type ConfigRecoveryListener = (info: {\n droppedKeys: readonly string[]\n issues: readonly z.core.$ZodIssue[]\n}) => void\n\nexport class DeviceConfig<T extends z.ZodObject<z.core.$ZodLooseShape>> {\n readonly schema: T\n private data: z.infer<T>\n private readonly persistFn: (data: z.infer<T>) => Promise<void>\n\n private constructor(\n schema: T,\n data: z.infer<T>,\n persist: (data: z.infer<T>) => Promise<void>,\n ) {\n this.schema = schema\n this.data = data\n this.persistFn = persist\n }\n\n /**\n * Build a `DeviceConfig` from a persisted blob, with automatic\n * recovery from schema-validation failures. Boot must never be\n * blocked by stale persisted values: if Zod rejects the blob,\n * we drop every offending top-level field, retry, and persist\n * the cleaned blob so the bad value is healed in the DB on next\n * write. The most common trigger is a tightened range constraint\n * (e.g. `max(100) → max(50)`) on a field that already has an\n * out-of-range value persisted from the previous schema. Without\n * this safety net, the device would fail to instantiate and end\n * up with no caps registered — exactly the failure mode that\n * stranded device 15 when `motionSensitivity: 90` no longer fit\n * the new `1..50` schema.\n *\n * Recovery rules:\n * 1. Try `safeParse(initialData)`. If it succeeds, done.\n * 2. On failure, walk `error.issues`, collect the top-level path\n * of each issue, and drop those keys from `initialData`.\n * 3. Re-run `safeParse`. If the cleaned blob now passes (Zod\n * fills the missing keys with schema defaults / undefined for\n * `.optional()`), persist it via `persist()` so the bad\n * values disappear from the DB, and return the device.\n * 4. If the cleaned blob STILL fails (very rare — would require\n * a non-recoverable required field), fall back to\n * `schema.parse({})` so the device still boots with pure\n * schema defaults. Persist nothing in that path so the next\n * successful `setAll` still writes a coherent blob.\n */\n static fromSchema<T extends z.ZodObject<z.core.$ZodLooseShape>>(\n schema: T,\n persist: (data: z.infer<T>) => Promise<void>,\n initialData: Record<string, unknown> = {},\n onRecover?: ConfigRecoveryListener,\n ): DeviceConfig<T> {\n const first = schema.safeParse(initialData)\n if (first.success) {\n return new DeviceConfig(schema, first.data as z.infer<T>, persist)\n }\n\n const droppedKeys = new Set<string>()\n for (const issue of first.error.issues) {\n const top = issue.path[0]\n if (typeof top === 'string') droppedKeys.add(top)\n }\n\n const cleaned: Record<string, unknown> = { ...initialData }\n for (const k of droppedKeys) delete cleaned[k]\n const second = schema.safeParse(cleaned)\n\n onRecover?.({\n droppedKeys: [...droppedKeys],\n issues: first.error.issues,\n })\n\n if (second.success) {\n // Heal the DB: persist the cleaned blob asynchronously so the\n // offending fields are gone on next read. Best-effort — a\n // persist failure here doesn't block boot, the device still\n // runs from the in-memory cleaned data.\n void persist(second.data as z.infer<T>).catch(() => { /* swallow */ })\n return new DeviceConfig(schema, second.data as z.infer<T>, persist)\n }\n\n // Last-resort fallback: pure schema defaults. This branch should\n // be unreachable for well-formed schemas (every required field\n // must already be in `initialData`, and `.optional()` fields\n // accept undefined), but is here so a malformed blob can't\n // strand the device.\n const defaults = schema.parse({}) as z.infer<T>\n return new DeviceConfig(schema, defaults, persist)\n }\n\n get values(): z.infer<T> {\n return this.data\n }\n\n get<K extends keyof z.infer<T>>(key: K): z.infer<T>[K] {\n return this.data[key]\n }\n\n async set<K extends keyof z.infer<T>>(key: K, value: z.infer<T>[K]): Promise<void> {\n const next = this.schema.parse({ ...this.data, [key]: value })\n this.data = next\n await this.persistFn(this.data)\n }\n\n /**\n * Merge an untyped patch onto the current config and persist. Accepts\n * `Record<string, unknown>` because the patch typically comes from the\n * UI form layer (a `ConfigField.key → value` map) where the caller\n * doesn't hold the Zod schema's static type. Runtime validation is\n * authoritative: `this.schema.parse` rejects unknown keys or invalid\n * shapes before touching storage.\n */\n async setAll(partial: Record<string, unknown>): Promise<void> {\n const next = this.schema.parse({ ...this.data, ...partial })\n this.data = next\n await this.persistFn(this.data)\n }\n\n async deleteKey<K extends keyof z.infer<T>>(key: K): Promise<void> {\n const { [key as string]: _, ...rest } = this.data as Record<string, unknown>\n const next = this.schema.parse(rest)\n this.data = next\n await this.persistFn(this.data)\n }\n\n entries(): Array<{\n key: string\n schema: z.ZodType\n value: unknown\n description?: string\n }> {\n const shape = this.schema.shape as Record<string, z.ZodType>\n return Object.entries(shape).map(([key, fieldSchema]) => ({\n key,\n schema: fieldSchema,\n value: this.data[key as keyof z.infer<T>],\n description: fieldSchema.description,\n }))\n }\n}\n","import { z } from 'zod'\n\n/**\n * Per-device runtime state — cap-keyed. Mutable signals discovered\n * after boot live here, separate from `DeviceConfig` (operator\n * intent) and from `deviceCache` (immutable autodetect / abilities).\n *\n * Shape: `Record<capName, capStateSlice>`. Each slice's schema is\n * declared by the capability itself (`CapabilityDefinition.runtimeState`)\n * so that any provider implementing the cap inherits the same shape —\n * the BatteryStatus shape a Reolink cam emits is the same a Frigate or\n * ONVIF cam emits, no driver-specific re-declaration.\n *\n * Schemas are registered DYNAMICALLY as the device's caps are\n * installed (see `BaseDevice.registerNativeCap`). A slice without a\n * registered schema is rejected on write — the cap must be wired\n * first. Reads return `undefined` for unknown slices.\n *\n * Persistence: only registered slices round-trip to disk. The kernel\n * layer debounces writes; explicit `flush()` awaits in-flight saves\n * (used by the shutdown hook).\n */\nexport interface IDeviceRuntimeState {\n /**\n * Read the slice for `capName`. Three overloads, picked by\n * argument shape:\n *\n * 1. Pass a known cap-name literal (`'battery'`, `'motion'`, …)\n * → return type is auto-inferred from\n * `CapNameToRuntimeStateMap` (codegen output). No generic\n * argument, no `as`-cast.\n * 2. Pass a cap-name with an explicit generic\n * (`getCapState<MyShape>('custom-cap')`) → return that\n * shape. Used for caps the codegen doesn't see (test-only,\n * addon-private).\n * 3. Pass a cap-name with no generic (default `Record<string,\n * unknown>`) → permissive untyped fallback.\n *\n * Returns `undefined` if no slice has been written yet.\n */\n getCapState<K extends keyof import('../generated/device-local-state.js').CapNameToRuntimeStateMap>(\n capName: K,\n ): Readonly<import('../generated/device-local-state.js').CapNameToRuntimeStateMap[K]> | undefined\n getCapState<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n ): Readonly<T> | undefined\n\n /** Read a single key inside a slice. Convenience for the common\n * case of reading a primitive (`battery.percentage`). */\n getCapField(capName: string, key: string): unknown\n\n /**\n * Replace the slice for `capName`. The schema bound to `capName`\n * (via `installCapSchema`) validates `value`; throws on mismatch.\n * Listeners fire synchronously after the write applies.\n */\n setCapState(capName: string, value: Record<string, unknown>): void\n\n /**\n * Shallow-merge `partial` into the current slice for `capName`.\n * Re-validates the merged result against the cap's schema.\n */\n patchCapState(capName: string, partial: Record<string, unknown>): void\n\n /** Subscribe to ALL state changes. Receives the cap names whose\n * slices changed and the full snapshot. */\n subscribe(cb: (changedCaps: ReadonlyArray<string>, snapshot: Snapshot) => void): () => void\n\n /** Subscribe to a single cap's slice changes. Fires only when that\n * cap's slice is touched; ignores writes against other caps. */\n subscribeCap<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n cb: (slice: Readonly<T> | undefined) => void,\n ): () => void\n\n /** Frozen snapshot of every cap's slice. Cap names without a\n * written slice are omitted (sparse). */\n snapshot(): Snapshot\n\n /** Flush any pending debounced disk writes. */\n flush(): Promise<void>\n\n /**\n * Register a cap's runtime-state schema. Called by the kernel\n * when `ctx.registerNativeCap(cap, …)` runs and `cap.runtimeState`\n * is defined. Idempotent — repeat installs with the same schema\n * are no-ops; mismatched schemas throw to surface a config bug.\n *\n * Lazily validates any existing in-memory slice for `capName` so\n * a slice loaded from disk before the cap was wired is checked\n * the moment the cap shows up.\n */\n installCapSchema(capName: string, schema: z.ZodObject<z.core.$ZodLooseShape>): void\n}\n\nexport type Snapshot = Readonly<Record<string, Readonly<Record<string, unknown>>>>\n\n/**\n * Concrete implementation. Routes every successful write through\n * `writer(capName, slice)` — the kernel hooks this up to\n * `device-state.setCapSlice`, the canonical cross-layer write\n * entrypoint, which handles disk persistence (debounced on the hub)\n * and mirror updates.\n *\n * Schema validation runs in-process before the writer is called —\n * the round-trip should never carry an invalid slice. `flush()`\n * awaits any in-flight writer promises so shutdown is lossless.\n *\n * `initial` is the persisted blob loaded at boot. Slices for caps\n * whose schema hasn't been installed yet are kept in-memory verbatim\n * and validated when the cap registers later.\n */\nexport class DeviceRuntimeState implements IDeviceRuntimeState {\n private readonly writer: (capName: string, slice: Record<string, unknown>) => Promise<void>\n /** In-flight writer promises tracked so `flush()` can await them. */\n private readonly pendingWrites = new Set<Promise<unknown>>()\n /** Per-cap committed slice — after schema validation when known. */\n private slices: Map<string, Record<string, unknown>>\n /** Per-cap registered schema (set by `installCapSchema`). */\n private readonly schemas = new Map<string, z.ZodObject<z.core.$ZodLooseShape>>()\n private readonly listeners = new Set<(changed: ReadonlyArray<string>, snap: Snapshot) => void>()\n private readonly capListeners = new Map<string, Set<(slice: Readonly<Record<string, unknown>> | undefined) => void>>()\n\n private constructor(\n initial: Record<string, unknown>,\n writer: (capName: string, slice: Record<string, unknown>) => Promise<void>,\n ) {\n this.writer = writer\n // Hydrate slices: every top-level key in `initial` is a cap name,\n // its value should be an object. Non-object values are dropped\n // with no error — the persisted blob is owned by us, so a\n // garbage shape is a kernel bug we'd rather surface elsewhere.\n this.slices = new Map()\n for (const [k, v] of Object.entries(initial)) {\n if (v && typeof v === 'object' && !Array.isArray(v)) {\n this.slices.set(k, { ...v as Record<string, unknown> })\n }\n }\n }\n\n static fromInitial(\n initial: Record<string, unknown>,\n writer: (capName: string, slice: Record<string, unknown>) => Promise<void>,\n ): DeviceRuntimeState {\n return new DeviceRuntimeState(initial, writer)\n }\n\n installCapSchema(capName: string, schema: z.ZodObject<z.core.$ZodLooseShape>): void {\n const existing = this.schemas.get(capName)\n if (existing) {\n // Idempotent: caps register multiple times during the device's\n // lifetime (constructor + post-login retroactive registration).\n // Ignore identical re-registrations; a different schema for the\n // same cap is a programming error worth throwing on.\n if (existing !== schema) {\n throw new Error(\n `[DeviceRuntimeState] capability \"${capName}\" registered a different runtime-state schema; `\n + `each cap must declare ONE shape across every provider`,\n )\n }\n return\n }\n this.schemas.set(capName, schema)\n // Validate any pre-loaded slice now that we have the schema.\n // Reject silently and clear: the persisted blob shouldn't crash\n // the device on boot, just lose stale data the schema rejects.\n const stored = this.slices.get(capName)\n if (stored) {\n const result = schema.safeParse(stored)\n if (result.success) {\n this.slices.set(capName, result.data as Record<string, unknown>)\n } else {\n this.slices.delete(capName)\n }\n }\n }\n\n getCapState<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n ): Readonly<T> | undefined {\n const slice = this.slices.get(capName)\n if (!slice) return undefined\n return Object.freeze({ ...slice }) as unknown as Readonly<T>\n }\n\n getCapField(capName: string, key: string): unknown {\n return this.slices.get(capName)?.[key]\n }\n\n setCapState(capName: string, value: Record<string, unknown>): void {\n this.applyCapWrite(capName, value, /* merge */ false)\n }\n\n patchCapState(capName: string, partial: Record<string, unknown>): void {\n this.applyCapWrite(capName, partial, /* merge */ true)\n }\n\n /**\n * Internal worker. `merge` controls whether `value` replaces or\n * shallow-merges into the existing slice. Schema validation runs\n * on the FINAL composed object regardless.\n */\n private applyCapWrite(capName: string, value: Record<string, unknown>, merge: boolean): void {\n const schema = this.schemas.get(capName)\n if (!schema) {\n // Refuse: a cap without a registered schema can't have its\n // slice written. Drivers that legitimately need extra\n // ad-hoc state should attach a `runtimeState` schema to the\n // cap definition. The strict refusal catches typos\n // (`'batery'` vs `'battery'`) loudly.\n throw new Error(\n `[DeviceRuntimeState] no schema registered for cap \"${capName}\" — `\n + `did the device register it via ctx.registerNativeCap before writing?`,\n )\n }\n const current = this.slices.get(capName) ?? {}\n const next = merge ? { ...current, ...value } : { ...value }\n const parsed = schema.parse(next) as Record<string, unknown>\n // Equality check before commit — back-to-back identical writes\n // (e.g. battery push every minute with the same percentage)\n // should NOT churn listeners or disk.\n if (shallowEqual(current, parsed)) return\n this.slices.set(capName, parsed)\n this.fireListeners([capName])\n // Kick off the write asynchronously — schema validation already\n // ran, so failure here means a transport / hub issue. We log\n // and drop the error rather than throwing, mirroring the\n // fire-and-forget contract `setCapState` had with the legacy\n // debounced persister.\n const writePromise = this.writer(capName, { ...parsed }).catch(() => {\n /* transport error — caller can re-issue if it cares */\n })\n this.pendingWrites.add(writePromise)\n void writePromise.finally(() => { this.pendingWrites.delete(writePromise) })\n }\n\n private fireListeners(changed: ReadonlyArray<string>): void {\n const snap = this.snapshot()\n for (const cb of this.listeners) {\n try { cb(changed, snap) } catch { /* listener errors don't break the writer */ }\n }\n for (const capName of changed) {\n const subs = this.capListeners.get(capName)\n if (!subs) continue\n const slice = this.getCapState(capName)\n for (const cb of subs) {\n try { cb(slice) } catch { /* idem */ }\n }\n }\n }\n\n subscribe(cb: (changed: ReadonlyArray<string>, snap: Snapshot) => void): () => void {\n this.listeners.add(cb)\n return () => { this.listeners.delete(cb) }\n }\n\n subscribeCap<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n cb: (slice: Readonly<T> | undefined) => void,\n ): () => void {\n let subs = this.capListeners.get(capName)\n if (!subs) {\n subs = new Set()\n this.capListeners.set(capName, subs)\n }\n const adapter = (slice: Readonly<Record<string, unknown>> | undefined): void => {\n cb(slice as Readonly<T> | undefined)\n }\n subs.add(adapter)\n return () => {\n const set = this.capListeners.get(capName)\n if (!set) return\n set.delete(adapter)\n if (set.size === 0) this.capListeners.delete(capName)\n }\n }\n\n snapshot(): Snapshot {\n const out: Record<string, Record<string, unknown>> = {}\n for (const [k, v] of this.slices) out[k] = Object.freeze({ ...v })\n return Object.freeze(out) as Snapshot\n }\n\n async flush(): Promise<void> {\n if (this.pendingWrites.size === 0) return\n // Snapshot the current set so writes added while we await\n // (e.g. listeners that re-trigger setCapState) settle in a\n // separate flush round if the caller wants strict drainage.\n const inflight = [...this.pendingWrites]\n await Promise.allSettled(inflight)\n }\n}\n\nfunction shallowEqual(a: Record<string, unknown>, b: Record<string, unknown>): boolean {\n const ak = Object.keys(a)\n const bk = Object.keys(b)\n if (ak.length !== bk.length) return false\n for (const k of ak) {\n if (a[k] !== b[k]) return false\n }\n return true\n}\n","import type { z } from 'zod'\nimport type { CapabilityDefinition } from '../capabilities/capability-definition.js'\nimport type { IDeviceRuntimeState } from './device-runtime-state.js'\n\nconst LAST_FETCHED_FIELD = 'lastFetchedAt'\n\n/**\n * Bridge a cap's `runtimeState` slice to its read-side methods.\n *\n * Pattern: provider keeps the camera fetch / write path, but delegates\n * stale-check + slice read + cold-start fallback to this helper. Cuts\n * the boilerplate that every provider would otherwise repeat across\n * `getStatus`, `getSettings`, and any other read-side cap method.\n *\n * Convention: the cap's `runtimeState` schema must include a numeric\n * `lastFetchedAt` field (ms epoch) so the helper can drive the\n * stale-check. The field is stripped from the value returned by\n * `getStatus` so the cap's status schema (which doesn't carry it)\n * still parses cleanly.\n *\n * Usage in a provider:\n * ```ts\n * const bridge = createRuntimeStateBridge({\n * runtimeState: this.runtimeState,\n * cap: ptzAutotrackCapability,\n * ownDeviceId: this.id,\n * refresh: () => this.refreshAutotrackFromCamera(),\n * staleMs: 10_000,\n * empty: () => emptyAutotrackStatus(),\n * })\n *\n * const provider: InferNativeProvider<typeof ptzAutotrackCapability> = {\n * getStatus: bridge.getStatus,\n * getSettings: async ({ deviceId }) => {\n * if (deviceId !== this.id) return null\n * await bridge.ensureFresh()\n * return this.runtimeState.getCapState<...>(...)?.currentSettings ?? null\n * },\n * setEnabled: async (...) => { ... ; await this.refreshAutotrackFromCamera() },\n * setSettings: async (...) => { ... ; await this.refreshAutotrackFromCamera() },\n * }\n * ```\n *\n * Single-flight (collapsing concurrent `refresh()` invocations) lives\n * INSIDE `refresh` — the camera client owns its own promise slot. This\n * helper stays thin so a single provider can host multiple cap-bridged\n * readers without fighting over a shared promise.\n */\nexport interface RuntimeStateBridge<TStatus> {\n /** Run a stale-check; if the slice is missing or older than\n * `staleMs`, await `refresh()`. Idempotent within the freshness\n * window — back-to-back calls touch zero camera I/O. */\n readonly ensureFresh: () => Promise<void>\n /** Drop-in `getStatus` provider method. Includes the cross-device\n * guard + freshness check + status projection from the slice. */\n readonly getStatus: (input: { readonly deviceId: number }) => Promise<TStatus>\n}\n\nexport function createRuntimeStateBridge<\n TCap extends CapabilityDefinition & { status: { schema: z.ZodType } },\n>(params: {\n readonly runtimeState: IDeviceRuntimeState\n readonly cap: TCap\n readonly ownDeviceId: number\n readonly refresh: () => Promise<void>\n readonly staleMs: number\n readonly empty: () => z.infer<TCap['status']['schema']>\n}): RuntimeStateBridge<z.infer<TCap['status']['schema']>> {\n const { runtimeState, cap, ownDeviceId, refresh, staleMs, empty } = params\n\n const ensureFresh = async (): Promise<void> => {\n const slice = runtimeState.getCapState<Record<string, unknown>>(cap.name)\n const fetchedAt = typeof slice?.[LAST_FETCHED_FIELD] === 'number'\n ? (slice[LAST_FETCHED_FIELD] as number)\n : 0\n if (!slice || Date.now() - fetchedAt > staleMs) await refresh()\n }\n\n const projectStatus = (): z.infer<TCap['status']['schema']> => {\n const slice = runtimeState.getCapState<Record<string, unknown>>(cap.name)\n if (!slice) return empty()\n // Strip lastFetchedAt (and any future helper-only fields) before\n // returning. Status schemas don't carry this field; runtime-state\n // schemas extend status with it for stale tracking.\n const { [LAST_FETCHED_FIELD]: _omit, ...rest } = slice\n return rest as z.infer<TCap['status']['schema']>\n }\n\n const getStatus = async ({ deviceId }: { readonly deviceId: number }): Promise<z.infer<TCap['status']['schema']>> => {\n if (deviceId !== ownDeviceId) {\n throw new Error(`${cap.name}: deviceId mismatch, expected ${ownDeviceId}, got ${deviceId}`)\n }\n await ensureFresh()\n return projectStatus()\n }\n\n return { ensureFresh, getStatus }\n}\n","// AUTO-GENERATED — do not edit manually.\n// Regenerate with: npx tsx scripts/generate-device-proxy.ts\n/* eslint-disable */\n\nimport type { CapabilityDefinition, InferRuntimeState } from '../capabilities/capability-definition.js'\nimport { audioMetricsCapability } from '../capabilities/audio-metrics.cap.js'\nimport { batteryCapability } from '../capabilities/battery.cap.js'\nimport { brightnessCapability } from '../capabilities/brightness.cap.js'\nimport { cameraStreamsCapability } from '../capabilities/camera-streams.cap.js'\nimport { deviceDiscoveryCapability } from '../capabilities/device-discovery.cap.js'\nimport { deviceStatusCapability } from '../capabilities/device-status.cap.js'\nimport { doorbellCapability } from '../capabilities/doorbell.cap.js'\nimport { featureProbeCapability } from '../capabilities/feature-probe.cap.js'\nimport { motionCapability } from '../capabilities/motion.cap.js'\nimport { motionTriggerCapability } from '../capabilities/motion-trigger.cap.js'\nimport { ptzAutotrackCapability } from '../capabilities/ptz-autotrack.cap.js'\nimport { switchCapability } from '../capabilities/switch.cap.js'\nimport { zoneAnalyticsCapability } from '../capabilities/zone-analytics.cap.js'\nimport { zoneRulesCapability } from '../capabilities/zone-rules.cap.js'\nimport { zonesCapability } from '../capabilities/zones.cap.js'\n\n/**\n * Server-side, write-capable per-cap state shape for any device that\n * extends `BaseDevice`. One entry per cap with `runtimeState:` declared.\n *\n * Drivers access via `this.state.<capName>.<field>` — both reads and\n * writes route through the device's runtime-state slice (validate,\n * persist, fire `<cap>.onChanged`). No string keys, no manual generic.\n *\n * Adding a new cap with `runtimeState:` auto-extends this interface —\n * regenerate with `npm run codegen`.\n */\nexport interface DeviceLocalState {\n audioMetrics: InferRuntimeState<typeof audioMetricsCapability>\n battery: InferRuntimeState<typeof batteryCapability>\n brightness: InferRuntimeState<typeof brightnessCapability>\n cameraStreams: InferRuntimeState<typeof cameraStreamsCapability>\n deviceDiscovery: InferRuntimeState<typeof deviceDiscoveryCapability>\n deviceStatus: InferRuntimeState<typeof deviceStatusCapability>\n doorbell: InferRuntimeState<typeof doorbellCapability>\n featureProbe: InferRuntimeState<typeof featureProbeCapability>\n motion: InferRuntimeState<typeof motionCapability>\n motionTrigger: InferRuntimeState<typeof motionTriggerCapability>\n ptzAutotrack: InferRuntimeState<typeof ptzAutotrackCapability>\n switch: InferRuntimeState<typeof switchCapability>\n zoneAnalytics: InferRuntimeState<typeof zoneAnalyticsCapability>\n zoneRules: InferRuntimeState<typeof zoneRulesCapability>\n zones: InferRuntimeState<typeof zonesCapability>\n}\n\n/**\n * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s\n * `state` getter looks up the cap definition here to construct a\n * `sliceProxy()` lazily on first access. Generated alongside the type\n * so type and runtime registry can never drift apart.\n */\nexport const DEVICE_LOCAL_STATE_CAPS: Record<keyof DeviceLocalState, CapabilityDefinition> = {\n audioMetrics: audioMetricsCapability,\n battery: batteryCapability,\n brightness: brightnessCapability,\n cameraStreams: cameraStreamsCapability,\n deviceDiscovery: deviceDiscoveryCapability,\n deviceStatus: deviceStatusCapability,\n doorbell: doorbellCapability,\n featureProbe: featureProbeCapability,\n motion: motionCapability,\n motionTrigger: motionTriggerCapability,\n ptzAutotrack: ptzAutotrackCapability,\n switch: switchCapability,\n zoneAnalytics: zoneAnalyticsCapability,\n zoneRules: zoneRulesCapability,\n zones: zonesCapability,\n}\n\n/**\n * Cap-name keyed runtime-state map (kebab-case keys, e.g. `'battery'`,\n * `'device-discovery'`). Companion to `DeviceLocalState` (which uses\n * camelCase property keys). Consumed by the\n * `IDeviceRuntimeState.getCapState` overload so callers passing a\n * cap-name literal get the typed slice back without an explicit\n * generic argument:\n *\n * const slice = this.runtimeState.getCapState('battery')\n * // ^^^^^ inferred as InferRuntimeState<typeof batteryCapability>\n *\n * Eliminates the `as`-cast at every `getCapState<X>('cap-name')` site.\n */\nexport interface CapNameToRuntimeStateMap {\n 'audio-metrics': InferRuntimeState<typeof audioMetricsCapability>\n 'battery': InferRuntimeState<typeof batteryCapability>\n 'brightness': InferRuntimeState<typeof brightnessCapability>\n 'camera-streams': InferRuntimeState<typeof cameraStreamsCapability>\n 'device-discovery': InferRuntimeState<typeof deviceDiscoveryCapability>\n 'device-status': InferRuntimeState<typeof deviceStatusCapability>\n 'doorbell': InferRuntimeState<typeof doorbellCapability>\n 'feature-probe': InferRuntimeState<typeof featureProbeCapability>\n 'motion': InferRuntimeState<typeof motionCapability>\n 'motion-trigger': InferRuntimeState<typeof motionTriggerCapability>\n 'ptz-autotrack': InferRuntimeState<typeof ptzAutotrackCapability>\n 'switch': InferRuntimeState<typeof switchCapability>\n 'zone-analytics': InferRuntimeState<typeof zoneAnalyticsCapability>\n 'zone-rules': InferRuntimeState<typeof zoneRulesCapability>\n 'zones': InferRuntimeState<typeof zonesCapability>\n}\n","import { z } from 'zod'\nimport { DeviceConfig } from './device-config.js'\nimport { DeviceRuntimeState } from './device-runtime-state.js'\nimport type { IDeviceRuntimeState } from './device-runtime-state.js'\nimport type { DeviceType, DeviceFeature, DeviceRole } from './device-type.js'\nimport type { DeviceContext } from './device-context.js'\nimport type { IDevice } from './device.js'\nimport type { ConfigUISchemaWithValues } from '../interfaces/config-ui.js'\nimport { deviceStatusCapability, type DeviceStatus } from '../capabilities/device-status.cap.js'\nimport { featureProbeCapability, type FeatureProbeStatus } from '../capabilities/feature-probe.cap.js'\nimport type { CapabilityDefinition, InferRuntimeState } from '../capabilities/capability-definition.js'\nimport { DEVICE_LOCAL_STATE_CAPS, type DeviceLocalState } from '../generated/device-local-state.js'\n\n// Zod 4's `ZodRawShape = $ZodShape = Record<string, $ZodType<unknown, unknown>>`\n// is too strict for concrete-shape schemas: a `z.object({ host: z.string(), … })`\n// produces `ZodObject<{ host: ZodString, … }>` whose fields' types\n// (`ZodString`, `ZodDefault<ZodNumber>`, …) don't widen to\n// `$ZodType<unknown, unknown>` once a subclass has 30+ fields, so\n// HikvisionCamera fails TS2344 on the constraint.\n//\n// Zod ships `$ZodLooseShape = Record<string, any>` for exactly this\n// pattern — generic constraints that should accept any concrete schema.\n// Using it on the type parameter keeps the typed inference path\n// (`z.infer<T>`, `T['shape']['host']`) intact while admitting concrete\n// schemas from subclasses.\nexport abstract class BaseDevice<T extends z.ZodObject<z.core.$ZodLooseShape> = z.ZodObject<z.ZodRawShape>> implements IDevice {\n readonly id: number\n readonly stableId: string\n readonly type: DeviceType\n readonly name: string\n readonly parentDeviceId: number | null\n readonly role?: DeviceRole\n /**\n * Cap-keyed runtime-state slice is the single source of truth for\n * `online`. Both getter and setter proxy to the slice — drivers can\n * write `this.online = true` ergonomically, and the cap event fires\n * automatically through the runtime-state writer. `markOnline()` is\n * kept as the explicit method form mandated by `IDevice`.\n */\n get online(): boolean {\n const slice = this.runtimeState.getCapState<DeviceStatus>('device-status')\n return slice?.online ?? false\n }\n set online(value: boolean) {\n this.markOnline(value)\n }\n\n /**\n * Generic per-cap runtime-state namespace. One entry per cap with\n * `runtimeState:` declared, auto-generated by codegen — see\n * `device-local-state.ts`. Drivers access via:\n *\n * `this.state.battery.sleeping = true` // patches the battery slice\n * `const pct = this.state.battery.percentage` // reads the battery slice\n * `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`\n *\n * Adding a new cap with `runtimeState:` automatically extends this\n * namespace — drivers don't have to declare proxies. Reads return\n * `undefined` when the slice hasn't been seeded; writes patch via\n * `runtimeState.patchCapState` and validate against the cap's schema\n * (so partial writes need the slice to be seeded with the required\n * fields first — drivers do this on cap registration).\n *\n * For caps not exposed in `DeviceLocalState`, drivers can build their\n * own typed proxy via `this.sliceProxy(cap)`.\n */\n get state(): DeviceLocalState {\n if (!this._stateProxyCache) {\n const cache: Record<string, unknown> = {}\n const handler: ProxyHandler<Record<string, unknown>> = {\n get: (_target, key) => {\n const k = key as string\n if (k in cache) return cache[k]\n const cap = (DEVICE_LOCAL_STATE_CAPS as Record<string, CapabilityDefinition | undefined>)[k]\n if (!cap) return undefined\n const proxy = this.sliceProxy(cap)\n cache[k] = proxy\n return proxy\n },\n }\n this._stateProxyCache = new Proxy(cache, handler) as unknown as DeviceLocalState\n }\n return this._stateProxyCache\n }\n private _stateProxyCache?: DeviceLocalState\n abstract readonly features: readonly DeviceFeature[]\n readonly config: DeviceConfig<T>\n /**\n * Per-device runtime state, cap-keyed. Always installed — slices\n * for individual caps materialise as those caps register their\n * native providers (`ctx.registerNativeCap`). The cap's own\n * `runtimeState` schema is the source of truth for the slice\n * shape; drivers don't redeclare it, they just write through.\n *\n * Read: `this.runtimeState.getCapState('battery')` →\n * `{percentage, charging, sleeping, lastUpdated}` for any\n * provider that registers `batteryCapability`.\n * Write: `this.runtimeState.setCapState('battery', { … })`.\n *\n * Cross-process consumers reach this state through the\n * `deviceState` cap router (or via cap-specific events the driver\n * emits — e.g. `battery.onStatusChanged`). The local handle is\n * accessed in-process by the driver to avoid roundtrips.\n */\n readonly runtimeState: IDeviceRuntimeState\n readonly ctx: DeviceContext\n\n /**\n * Operator-organisational location label (room / area / zone).\n * Read from `ctx.deviceMeta.location`; mutated via\n * `kernel.devices.setLocation(id, value)`. Free-text — providers\n * don't interpret it; the UI groups devices by this for filters\n * like \"show me all cameras in Kitchen\". `null` when unset.\n */\n readonly location: string | null\n /**\n * Soft-disabled flag. When `true`, the device class is still\n * instantiated and visible in the UI (so the operator can flip\n * back on without re-adding) but lifecycle hooks (publishToBroker,\n * alarm-stream subscribe, …) MUST be gated by the driver to skip\n * work. The `BaseDevice` enforces this by exposing the flag here;\n * it does NOT mutate cap behaviour automatically — drivers consult\n * `this.disabled` at the top of their lifecycle methods. Read from\n * `ctx.deviceMeta.disabled`; mutated via\n * `kernel.devices.setDisabled(id, value)`.\n */\n readonly disabled: boolean\n\n constructor(\n ctx: DeviceContext,\n schema: T,\n options: {\n type: DeviceType\n /** Optional semantic role within parent — see `DeviceRole`. */\n role?: DeviceRole\n },\n ) {\n this.ctx = ctx\n this.id = ctx.id\n this.stableId = ctx.stableId\n this.type = options.type\n // Operator-organisational fields (`name` / `location` / `disabled`)\n // come exclusively from the kernel-managed meta surface. Production\n // contexts always populate `ctx.deviceMeta`; test fixtures must\n // supply it explicitly. No fallback to `options.name` — the only\n // way to set the display name is through the meta API.\n if (!ctx.deviceMeta) {\n throw new Error(\n `BaseDevice constructor: ctx.deviceMeta is required (id=${ctx.id} stableId=${ctx.stableId})`,\n )\n }\n this.name = ctx.deviceMeta.name\n this.location = ctx.deviceMeta.location\n this.disabled = ctx.deviceMeta.disabled\n this.role = options.role\n this.parentDeviceId = ctx.parentDeviceId\n // DeviceContext.persistConfig is typed as (data: unknown) => Promise<void>\n // to break the circular dependency between DeviceContext and every schema type T.\n // DeviceConfig.fromSchema requires (data: z.infer<T>) => Promise<void>, which is a\n // subtype of (data: unknown) => Promise<void> at runtime (TypeScript variance rules\n // prevent direct assignment for function parameters). The wrapper below narrows the\n // type without losing safety — the actual data passed is always z.infer<T> because\n // DeviceConfig itself only calls persistFn with validated T values.\n //\n // Self-hydrate: the kernel pre-loads the per-device persisted\n // config blob from the DB and stuffs it into `ctx.persistedConfig`\n // before this constructor runs. We seed `DeviceConfig` from that\n // blob alone — operator-supplied initial config (Add-Device form\n // input) reaches us via the same DB read because providers\n // pre-persist via `kernel.devices.create(stableId, Class, config)`\n // (or `persistInitialConfig` in the manual flow), which writes\n // the blob to the DB BEFORE constructing the device. The device\n // class therefore never sees an `initialData` constructor arg —\n // every config read goes through `this.config.get(...)` which is\n // a thin in-memory cache over the DB row. Schema defaults fill\n // any missing keys via Zod's parse pass.\n const seedData = ctx.persistedConfig ?? {}\n this.config = DeviceConfig.fromSchema(\n schema,\n (data: z.infer<T>) => ctx.persistConfig(data),\n seedData,\n ({ droppedKeys, issues }) => {\n // Stale persisted values that no longer match the schema —\n // logged once at boot so operators can see what got reset.\n // The DB blob is healed asynchronously inside `fromSchema`\n // (it persists the cleaned data), so this log is the only\n // visible trace of the recovery path firing.\n ctx.logger.warn('Device config recovery: dropping invalid persisted fields', {\n tags: { deviceId: ctx.id, stableId: ctx.stableId },\n meta: {\n droppedKeys: [...droppedKeys],\n firstIssue: issues[0]?.message ?? null,\n },\n })\n },\n )\n\n // Always install runtime state — schemas attach lazily as the\n // device's caps register their native providers (see\n // `ctx.registerNativeCap` wiring in the kernel context factory).\n //\n // Writer routes through `fetchDevice(id).deviceState.setCapSlice`\n // so we share the same per-device facade admin-ui + addons use.\n // The DeviceProxy auto-injects deviceId; the dispatch falls\n // through to the local hub provider (or its Moleculer bridge\n // proxy when device-manager runs cross-process).\n //\n // The proxy is fetched lazily on the first write and cached for\n // the device's lifetime — `getBindings` is one cheap query, and\n // `setCapSlice` is a system-cap call that doesn't read binding\n // entries (so cache staleness is irrelevant for this writer).\n let cachedProxy: Promise<import('../generated/device-proxy.js').DeviceProxy> | null = null\n const writer = async (capName: string, slice: Record<string, unknown>): Promise<void> => {\n if (!cachedProxy) cachedProxy = ctx.fetchDevice(ctx.id)\n const dev = await cachedProxy\n await dev.deviceState.setCapSlice({ capName, slice })\n }\n const initial = ctx.initialRuntimeState ?? {}\n this.runtimeState = DeviceRuntimeState.fromInitial(initial, writer)\n // Hand the reference back to the context so subsequent\n // `ctx.registerNativeCap` calls (fired from this constructor's\n // own `registerNativeCapabilities` chain) can install each\n // cap's `runtimeState` schema. Optional hook — test contexts\n // skip it; caps with runtime state in those contexts go memory-\n // only without schema validation.\n ctx.bindRuntimeState?.(this.runtimeState)\n\n // Auto-register the generic `device-status` cap so every device\n // exposes a uniform cap-keyed slice for the base device-level\n // flags (`online`, `lastChangedAt`). Cross-process consumers read\n // via `device-state.getCapSlice({deviceId, capName: 'device-status'})`\n // instead of having to special-case `online` against the device\n // summary RPC. Driver-specific caps (`battery`, `doorbell`, …)\n // own their own slices.\n ctx.registerNativeCap?.(deviceStatusCapability, {})\n // Seed the slice synchronously with the canonical pre-firmware\n // default (`online: false`) so the `online` getter resolves and\n // the first cross-process read returns a populated record.\n const seed: DeviceStatus = { online: false, lastChangedAt: Date.now() }\n this.runtimeState.setCapState('device-status', seed)\n\n // Auto-register the generic `feature-probe` cap. Drivers populate it\n // from `onProbe()` (kernel calls it once after register, before\n // accessory reconciliation). The slice is the single source of truth\n // for `getAccessoryChildren()` decisions and the public `features`\n // array — it replaces the older driver-local `deviceCache.has*`\n // duplication.\n ctx.registerNativeCap?.(featureProbeCapability, {})\n const probeSeed: FeatureProbeStatus = {\n flags: {},\n deviceType: null,\n model: null,\n channelCount: null,\n lastProbedAt: 0,\n lastFetchedAt: 0,\n }\n this.runtimeState.setCapState('feature-probe', probeSeed)\n }\n\n async removeDevice(): Promise<void> {\n // Override in subclass for cleanup (disconnect, stop streams, etc.)\n }\n\n /**\n * Set the device's online flag. Called by `BaseDeviceProvider` after\n * aggregating per-profile stream-broker health, or directly by drivers\n * that have provider-side liveness signals (e.g. ONVIF heartbeats,\n * Reolink Baichuan firmware push events). Mirrors the new value into\n * the `device-status` runtime-state slice so cross-process consumers\n * pick it up via the standard cap-state channel. Subclasses can\n * override to gate side effects on the transition.\n */\n markOnline(online: boolean): void {\n if (this.online === online) return\n const next: DeviceStatus = { online, lastChangedAt: Date.now() }\n this.runtimeState.setCapState('device-status', next)\n }\n\n /**\n * Re-publish the device's current `features` array to the persisted\n * meta blob. Drivers call this after a probe finishes when the live\n * `features` getter has gained new flags (e.g. `hasIntercom` flips\n * to true → `DeviceFeature.TwoWayAudio` joins the list).\n *\n * Without this, only the construction-time snapshot is written —\n * `deviceManager.registerDevice` is invoked once per boot, so probe-\n * driven additions don't reach the persisted index until the next\n * server restart, and `getDevice` / `listAll` keep returning the\n * stale list for forked-worker devices (whose live IDevice instance\n * is invisible to the hub registry).\n *\n * Idempotent: re-calling with the same features just no-ops on the\n * persisted meta. Best-effort: lookup or write failures are logged\n * at debug and swallowed — the live `device.features` getter is\n * still authoritative within this process, so callers never block\n * device boot on a meta refresh.\n */\n protected async refreshFeatures(): Promise<void> {\n const api = this.ctx.api as unknown as {\n deviceManager?: {\n registerDevice?: { mutate: (input: {\n addonId: string\n stableId: string\n id: number\n type: string\n name: string\n parentDeviceId: number | null\n features?: readonly string[]\n config: Record<string, unknown>\n }) => Promise<void> }\n }\n }\n const action = api?.deviceManager?.registerDevice\n if (!action) return\n try {\n await action.mutate({\n addonId: this.ctx.deviceMeta.addonId,\n stableId: this.stableId,\n id: this.id,\n type: this.type,\n name: this.name,\n parentDeviceId: this.parentDeviceId,\n features: [...this.features],\n // Empty config — the kernel's registerDevice ignores the field\n // when there's no payload (the per-device store is the\n // canonical source after construction).\n config: {},\n })\n } catch (err) {\n // No logger on BaseDevice (would require ctx wiring) — drivers\n // that want visibility wrap the call themselves.\n void err\n }\n }\n\n /**\n * Typed read-through to a cap-keyed runtime-state slice. Drivers\n * call `this.getCapSlice(batteryCapability)` and the return type\n * is inferred from the cap's `runtimeState` Zod schema — no string\n * key, no manual generic. Returns `null` when the slice hasn't\n * been written yet (e.g. driver hasn't seeded battery yet).\n */\n protected getCapSlice<TCap extends CapabilityDefinition>(cap: TCap): InferRuntimeState<TCap> | null {\n const slice = this.runtimeState.getCapState(cap.name)\n return (slice as InferRuntimeState<TCap> | undefined) ?? null\n }\n\n /**\n * Typed writer to a cap-keyed runtime-state slice. Routes through\n * the runtime-state writer (validate → persist → emit cap event).\n * Equivalent to `this.runtimeState.setCapState(cap.name, value)`\n * but with the cap's `runtimeState` schema enforcing the value\n * shape at compile time. Mirrors the symmetry of\n * `getCapSlice` / `setCapSlice` for cross-cap consistency.\n */\n protected setCapSlice<TCap extends CapabilityDefinition>(\n cap: TCap,\n value: InferRuntimeState<TCap> & Record<string, unknown>,\n ): void {\n this.runtimeState.setCapState(cap.name, value)\n }\n\n /**\n * Field-level read/write proxy over a cap's runtime-state slice.\n * Drivers that want ergonomic per-field access declare:\n *\n * ```ts\n * protected battery = this.sliceProxy(batteryCapability)\n * // …\n * this.battery.sleeping = true // patches the slice\n * const charging = this.battery.charging // reads the slice\n * ```\n *\n * Reads return `undefined` when the slice hasn't been seeded yet\n * (cap not registered, or seeded but the field is absent). Writes\n * route through `runtimeState.patchCapState` so the cap's `runtimeState`\n * schema validates the merged result and the cap event fires.\n *\n * Pattern is generic — same shape works for `battery`, `device-status`,\n * `motion`, `doorbell`, anything with a `runtimeState:` schema. Drivers\n * declare one proxy per cap they read/write directly.\n */\n protected sliceProxy<TCap extends CapabilityDefinition>(cap: TCap): InferRuntimeState<TCap> {\n const target: Record<string, unknown> = {}\n const handler: ProxyHandler<Record<string, unknown>> = {\n get: (_, key) => {\n const slice = this.runtimeState.getCapState(cap.name)\n return slice?.[key as string]\n },\n set: (_, key, value: unknown) => {\n this.runtimeState.patchCapState(cap.name, { [key as string]: value })\n return true\n },\n has: (_, key) => {\n const slice = this.runtimeState.getCapState(cap.name)\n return slice ? key in slice : false\n },\n ownKeys: () => {\n const slice = this.runtimeState.getCapState(cap.name)\n return slice ? Object.keys(slice) : []\n },\n getOwnPropertyDescriptor: (_, key) => {\n const slice = this.runtimeState.getCapState(cap.name)\n if (!slice || !(key in slice)) return undefined\n return { configurable: true, enumerable: true, value: slice[key as string] }\n },\n }\n return new Proxy(target, handler) as InferRuntimeState<TCap>\n }\n\n /**\n * Default empty settings UI. Drivers override this to expose an\n * editable form in the device-details page. Returning an empty sections\n * array signals \"nothing to contribute\" — the aggregator drops the\n * contribution entirely rather than rendering a blank panel.\n */\n getSettingsUISchema(): ConfigUISchemaWithValues {\n return { sections: [] }\n }\n\n /**\n * Default write path: forward the flat patch directly to storage.\n * Drivers that project a UI shape different from storage (e.g. `RtspCamera`\n * exposing `mainStreamUrl`/`subStreamUrl` over `streams[]`) override this\n * to reshape before `config.setAll`.\n */\n async applySettingsPatch(patch: Record<string, unknown>): Promise<void> {\n await this.config.setAll(patch)\n }\n\n // ── Lifecycle hooks ────────────────────────────────────────────────\n //\n // The kernel orchestrates the device lifecycle in distinct phases.\n // Drivers override only the hooks they need:\n //\n // 1. Construct — `new DeviceClass(ctx)` (sync)\n // 2. RegisterIdentity — kernel writes meta + id to device-manager\n // 3. Probe — `onProbe()` populates `feature-probe` slice\n // 4. ReconcileAccessories — kernel calls `getAccessoryChildren()`\n // (sees post-probe flags), spawns missing,\n // removes orphans, refreshes meta features\n // 5. Activate — `onActivate()` does \"device is live\" work:\n // broker publish, snapshot tabs, watchdogs\n //\n // Failures in `onProbe` / `onActivate` are logged and swallowed — the\n // device IS already registered; partial init shouldn't unwind the\n // create the caller succeeded at.\n\n /**\n * Phase 3 — populate device-scoped state needed by downstream phases\n * (accessory reconciliation, public `features` array, optional cap\n * registration). Called ONCE per construction, after register but\n * before `getAccessoryChildren()`.\n *\n * Drivers write the `feature-probe` runtime-state slice via\n * `this.runtimeState.setCapState('feature-probe', {...})` — flag bag\n * is open (Reolink writes `hasPtz/hasIntercom`, Hikvision writes\n * `hasSupplementalLight/hasAlarmIo`, etc).\n *\n * Default: no-op (driver had no probe to run).\n */\n async onProbe(): Promise<void> {\n /* override in subclass */\n }\n\n /**\n * Phase 5 — fired after the device + its accessories are registered.\n * Drivers publish streams to the broker, kick off background tasks,\n * or subscribe to lib events that need a fully-registered device id.\n *\n * Default: no-op.\n *\n * RENAMED FROM `onCreated` (which still exists for back-compat in this\n * pass). The new name reflects the post-probe, post-accessory contract.\n */\n async onActivate(): Promise<void> {\n /* override in subclass */\n }\n\n /**\n * Re-run the probe + reconcile accessories + refresh features meta.\n * Drivers call this when device-side state changes (battery cam wakes,\n * firmware update, manual operator trigger).\n *\n * The kernel injects `_kernelReprobe` on registration so this method\n * delegates to the same orchestrator that runs the boot-time phase\n * 3 + 4 sequence. Drivers should NOT override this — they override\n * `onProbe()` instead.\n */\n async reprobe(): Promise<void> {\n if (this._kernelReprobe) await this._kernelReprobe()\n else await this.onProbe()\n }\n\n /**\n * Kernel-injected callback that runs the full post-probe orchestration\n * (onProbe → registerDevice meta refresh → accessory reconciliation).\n * Set by `device-cap-proxy.register()`. Drivers should not touch this\n * directly — call `reprobe()` instead.\n */\n _kernelReprobe?: () => Promise<void>\n\n /**\n * Declare accessory child devices the kernel should auto-spawn\n * after `onProbe()` resolves. Each spec fully describes one child\n * — stableId suffix (deterministic per kind for restore-safety),\n * meta (type / name / location), config (initial blob the child\n * self-hydrates), and a factory that constructs the concrete\n * class with whatever closure-captured refs it needs (typically\n * `this` for the parent reference).\n *\n * The kernel handles the rest: allocateDeviceId, persistInitialConfig\n * (skipped on restore when the row already exists),\n * persistInitialMeta, createContext, factory invocation, register,\n * and recursive lifecycle (probe + accessories + activate).\n *\n * Implementations should derive children from\n * `this.runtimeState.getCapState('feature-probe')` (post-probe truth).\n * Drivers can use the `getProbeFlags()` helper to read the flag bag\n * with a typed cast.\n *\n * Default: no children.\n */\n getAccessoryChildren(): readonly AccessoryChildSpec[] {\n return []\n }\n\n /**\n * Read the current feature-probe flag bag with a typed cast. Helper\n * for `getAccessoryChildren()` and `features` getters that derive\n * outputs from the probe results.\n */\n protected getProbeFlags<F extends Record<string, unknown> = Record<string, unknown>>(): F {\n const slice = this.runtimeState.getCapState<FeatureProbeStatus>('feature-probe')\n return (slice?.flags ?? {}) as F\n }\n\n /**\n * Returns true once `onProbe` has completed at least once\n * (`lastProbedAt > 0`). Drivers gate `getAccessoryChildren()` on this\n * to avoid spawning stale accessories on a fresh device whose probe\n * hasn't landed yet.\n */\n protected hasProbed(): boolean {\n const slice = this.runtimeState.getCapState<FeatureProbeStatus>('feature-probe')\n return (slice?.lastProbedAt ?? 0) > 0\n }\n}\n\n/**\n * Specification for one accessory child a parent device wants the\n * kernel to auto-spawn. Returned from `BaseDevice.getAccessoryChildren()`.\n */\nexport interface AccessoryChildSpec {\n /**\n * Stable suffix appended to the parent's stableId to derive the\n * child's stableId (e.g. `siren` → `${parent.stableId}-siren`).\n * Must be deterministic per (parent, accessory kind) so restore\n * resolves the same numeric id and re-instantiates without\n * orphaning the persisted config.\n */\n readonly stableIdSuffix: string\n /** Operator-organisational meta passed to `kernel.devices.create()`'s\n * `initialMeta` arg. The kernel pre-writes the meta row before\n * the child class constructor runs. */\n readonly meta: import('./device-management.js').InitialDeviceMeta\n /** Initial config blob the child self-hydrates from on first\n * create. Empty `{}` on restore (DB row carries the canonical\n * values) — the kernel detects the existing row and skips the\n * pre-persist step. */\n readonly config: Record<string, unknown>\n /**\n * Constructor closure — invoked with the kernel-built context\n * after meta + config are persisted. Closures over the parent\n * reference for child classes that need `parent` as a constructor\n * arg (e.g. accessories that share the parent's connection). The\n * returned IDevice is registered + `onCreated`-fired by the\n * kernel.\n */\n readonly factory: (ctx: DeviceContext) => IDevice\n}\n","import type {\n ProviderRegistration,\n ConfigUISchema,\n SavedDevice,\n IDevice,\n} from '../index.js'\nimport { BaseAddon } from '../addon/base-addon.js'\nimport { deviceProviderCapability } from '../capabilities/device-provider.cap.js'\nimport { DeviceType } from './device-type.js'\nimport type { CreateDeviceSpec } from './device-management.js'\nimport type { DeviceConstructor } from './device-context.js'\n\n// ── Shared interfaces (previously duplicated in each provider) ──────\n\nexport interface DiscoveryCandidate {\n readonly stableId: string\n readonly type: DeviceType\n readonly suggestedName: string\n readonly prefilledConfig: Record<string, unknown>\n}\n\nexport interface DeviceSummary {\n readonly id: number\n readonly stableId: string\n readonly addonId: string\n readonly type: string\n readonly name: string\n readonly parentDeviceId: number | null\n readonly online: boolean\n readonly features: readonly string[]\n readonly config: Record<string, unknown>\n}\n\nexport interface ProviderStatus {\n readonly connected: boolean\n readonly deviceCount: number\n readonly error?: string\n}\n\nexport interface FieldProbeResult {\n readonly status: 'ok' | 'error'\n readonly labels?: readonly string[]\n readonly error?: string\n /**\n * Optional values the provider suggests applying to other form fields\n * after a successful probe. The form-builder merges these into the\n * working snapshot ONLY for fields the operator hasn't filled yet,\n * never overwriting user input. Typical use: a host-probe that\n * autodetects the camera model and proposes it as the device `name`.\n */\n readonly suggestedValues?: Readonly<Record<string, unknown>>\n}\n\n// ── Utility ─────────────────────────────────────────────────────────\n\n/**\n * Convert an IDevice to the flat DeviceSummary shape expected by the\n * device-provider cap router. Shared across all providers.\n */\nexport function toDeviceSummary(device: IDevice, addonId: string): DeviceSummary {\n const config: Record<string, unknown> = {}\n for (const entry of device.config.entries()) {\n config[entry.key] = entry.value\n }\n return {\n id: device.id,\n stableId: device.stableId,\n addonId,\n type: String(device.type),\n name: device.name,\n parentDeviceId: device.parentDeviceId,\n online: device.online,\n features: [...device.features],\n config,\n }\n}\n\n// ── Base class ──────────────────────────────────────────────────────\n\n/**\n * Base class for device-provider addons (rtsp, onvif, frigate).\n *\n * Provides default implementations for the common device-provider cap\n * methods (`start`, `stop`, `getStatus`, `getDevices`, `supportsDiscovery`,\n * `supportsManualCreation`, `toDeviceSummary`). Subclasses override the\n * methods that differ per provider.\n *\n * @example\n * ```ts\n * class RtspProvider extends BaseDeviceProvider {\n * protected readonly addonId = 'provider-rtsp'\n * protected readonly providerName = 'RTSP'\n *\n * protected async onCreateDevice(input) { ... }\n * protected async onGetCreationSchema(type) { ... }\n * protected async onRestoreDevices(saved) { ... }\n * }\n * ```\n */\nexport abstract class BaseDeviceProvider<TConfig extends object = Record<string, unknown>> extends BaseAddon<TConfig> {\n /** Addon ID used in DeviceSummary. Must match package.json id. */\n protected abstract readonly addonId: string\n\n /** Human-readable provider name for log messages. */\n protected abstract readonly providerName: string\n\n // ── Lifecycle (BaseAddon hooks) ─────────────────────────────────────\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n this.ctx.logger.info(`${this.providerName} Provider initialized`)\n return [{ capability: deviceProviderCapability, provider: this }]\n }\n\n protected async onShutdown(): Promise<void> {\n // Decommission every device this provider owns BEFORE returning.\n // Mirrors what Moleculer SIGTERM + in-process restart paths\n // expect: each device's `removeDevice()` lifecycle hook fires\n // (closes alarm streams, ISAPI clients, polling timers, intercom\n // sessions, …), native caps unregister cleanly, registry entries\n // drop. The persisted device-meta / config rows stay intact so\n // the next boot's `restoreDevices` rehydrates the same set with\n // freshly-instantiated IDevice classes — that's the whole point\n // of \"decommission, don't delete\".\n //\n // Subclasses that need extra teardown (Moleculer service handle,\n // background workers) should override and call `super.onShutdown()`\n // first so devices are torn down before vendor-specific resources.\n const devices = await this.ctx.kernel.devices?.getAll() ?? []\n for (const device of devices) {\n try {\n await this.ctx.kernel.devices?.decommission(device.id)\n } catch (err) {\n this.ctx.logger.warn(`${this.providerName}: decommission failed`, {\n tags: { deviceId: device.id, stableId: device.stableId },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n this.ctx.logger.info(`${this.providerName} Provider shut down`, {\n meta: { decommissionedCount: devices.length },\n })\n }\n\n // ── device-provider cap — lifecycle + introspection ─────────────────\n // Default implementations. Override in subclass if needed.\n\n async start(): Promise<void> {\n /* no-op — providers are passive by default */\n }\n\n async stop(): Promise<void> {\n /* no-op */\n }\n\n async getStatus(): Promise<ProviderStatus> {\n const all = (await this.ctx.kernel.devices?.getAll()) ?? []\n return { connected: true, deviceCount: all.length }\n }\n\n async getDevices(): Promise<ReadonlyArray<{ id: string; name: string; type: string }>> {\n const all = (await this.ctx.kernel.devices?.getAll()) ?? []\n return all.map((d) => ({ id: d.stableId, name: d.name, type: String(d.type) }))\n }\n\n // ── device-provider cap — discovery ─────────────────────────────────\n // Default: no discovery. Override in providers that support it (ONVIF).\n\n async supportsDiscovery(): Promise<boolean> {\n return false\n }\n\n async discoverDevices(): Promise<readonly DiscoveryCandidate[]> {\n return []\n }\n\n async adoptDiscoveredDevice(_input: {\n candidate: DiscoveryCandidate\n }): Promise<DeviceSummary> {\n throw new Error(`${this.providerName} provider does not support discovery-based adoption`)\n }\n\n // ── device-provider cap — manual creation ───────────────────────────\n // Default: supports manual creation. Subclass must implement\n // onGetCreationSchema and onCreateDevice.\n\n async supportsManualCreation(): Promise<boolean> {\n return true\n }\n\n async getChildCreationSchema(input: { type: DeviceType }): Promise<ConfigUISchema | null> {\n return this.onGetCreationSchema(input.type)\n }\n\n /**\n * Default kernel-orchestrated `createDevice` implementation. The\n * subclass's `onCreateDevice` returns a declarative\n * `CreateDeviceSpec` (`{meta, config}`) — this method handles\n * stableId generation, class lookup, kernel.devices.create\n * dispatch, and DeviceSummary mapping. Subclasses should NOT\n * override this method; override `onCreateDevice` and\n * `deviceClasses` instead.\n */\n async createDevice(input: {\n type: DeviceType\n config: Record<string, unknown>\n }): Promise<DeviceSummary> {\n const spec = await this.onCreateDevice(input.type, input.config)\n const Class = this.deviceClasses[spec.meta.type] as DeviceConstructor<IDevice> | undefined\n if (!Class) {\n throw new Error(\n `${this.providerName} provider: no device class registered for type \"${spec.meta.type}\" — add it to the deviceClasses map`,\n )\n }\n const stableId = this.generateStableId(spec.meta.type, spec.config)\n const device = await this.ctx.kernel.devices!.create(\n stableId,\n Class,\n spec.config,\n null,\n spec.meta,\n )\n if (spec.onAfterCreate) {\n try {\n await spec.onAfterCreate(device)\n } catch (err) {\n this.ctx.logger.warn('createDevice: onAfterCreate hook threw — device is already registered', {\n tags: { deviceId: device.id, stableId },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n return this.toSummary(device)\n }\n\n /**\n * Generate a stableId for a newly-created device. Default uses the\n * `${addonId}-${Date.now()}` pattern as a unique-but-opaque\n * fallback; any provider that has access to durable hardware\n * identity (UID, MAC, serial) should override and derive from it\n * so re-adding the same physical device reuses its persisted row.\n *\n * `config` is the parsed CreateDeviceSpec.config the subclass\n * returned from `onCreateDevice` — the override has access to\n * every operator-supplied + autodetect-resolved field. Optional\n * for back-compat: existing overrides that take only `type`\n * keep working unchanged.\n */\n protected generateStableId(_type: DeviceType, _config?: Record<string, unknown>): string {\n return `${this.addonId}-${Date.now()}`\n }\n\n async testCreationField(_input: {\n type: DeviceType\n key: string\n value: unknown\n formValues?: Record<string, unknown>\n }): Promise<FieldProbeResult> {\n return { status: 'ok', labels: ['probe not implemented'] }\n }\n\n // ── Boot restore ────────────────────────────────────────────────────\n\n async restoreDevices(savedDevices: readonly SavedDevice[]): Promise<void> {\n await this.onRestoreDevices(savedDevices)\n if (savedDevices.length > 0) {\n this.ctx.logger.info(`Restored ${savedDevices.length} ${this.providerName} device(s)`)\n }\n }\n\n // ── Subclass hooks ──────────────────────────────────────────────────\n\n /**\n * Concrete device classes this provider can spawn, keyed by\n * `DeviceType`. Used by:\n * - `createDevice` to look up the class for the type the operator\n * selected in the Add-Device modal\n * - the default `onRestoreDevices` to instantiate persisted rows\n * based on their `type` field\n *\n * Required for the declarative create + restore flow. Providers\n * that need custom-arg constructors (accessory child devices) wire\n * those up via `BaseDevice.getAccessoryChildren()` instead — the\n * top-level type → class map only handles parent devices.\n */\n protected abstract readonly deviceClasses: Partial<Record<DeviceType, DeviceConstructor<IDevice>>>\n\n /** Return the creation form schema for a given device type, or null if unsupported. */\n protected abstract onGetCreationSchema(type: DeviceType): Promise<ConfigUISchema | null>\n\n /**\n * Build the create spec from operator-supplied form values. Returns\n * `{meta, config}` — kernel handles allocateId, persist, instantiate,\n * register, and accessory auto-spawn. Provider's only job is to:\n * - validate / probe the input\n * - emit the meta (`type`, `name`, optional `location`)\n * - emit the `config` blob the device class will self-hydrate\n */\n protected abstract onCreateDevice(type: DeviceType, config: Record<string, unknown>): Promise<CreateDeviceSpec>\n\n /**\n * Restore devices from persisted state. Two-pass:\n *\n * 1. **Top-level pass** — invokes `kernel.devices.create()` for every\n * `parentDeviceId === null` row using the `deviceClasses` map.\n * The kernel's register flow handles `getAccessoryChildren()` for\n * each parent (siren / floodlight / PIR / etc).\n *\n * 2. **Hub-adopted children pass** — for rows with\n * `parentDeviceId !== null` whose `type` IS in `deviceClasses`\n * (e.g. Reolink hub-adopted cameras under an NVR), spawn them\n * explicitly with the persisted `parentDeviceId`. These are\n * NOT accessory children — they're first-class adopted devices\n * that just happen to have a parent. Without this pass, every\n * server restart would lose hub-adopted cameras (their type is\n * in `deviceClasses` but parent's `getAccessoryChildren` doesn't\n * spawn them — that callback is only for purpose-built\n * accessory roles).\n *\n * Rows whose `type` is NOT in `deviceClasses` are skipped — those\n * are accessory children (siren/light/sensor) that the kernel's\n * accessory-spawn flow handles via the parent's\n * `getAccessoryChildren()`. Override only when the default doesn't\n * fit.\n */\n protected async onRestoreDevices(savedDevices: readonly SavedDevice[]): Promise<void> {\n // Pass 1 — top-level\n const restored = new Set<number>()\n for (const saved of savedDevices) {\n if (saved.parentDeviceId !== null) continue\n const Class = this.deviceClasses[saved.type] as DeviceConstructor<IDevice> | undefined\n if (!Class) {\n this.ctx.logger.warn('No device class registered for restored type — skipping', {\n tags: { stableId: saved.stableId },\n meta: { type: saved.type },\n })\n continue\n }\n try {\n await this.ctx.kernel.devices!.create(saved.stableId, Class, {})\n restored.add(saved.id)\n } catch (err) {\n this.ctx.logger.warn('Failed to restore device', {\n tags: { stableId: saved.stableId },\n meta: { type: saved.type, error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n // Pass 2 — hub-adopted children (first-class devices with a parent\n // that is itself a registered device class). Iterates in order of\n // declared `parentDeviceId` to handle nested hubs gracefully.\n const childRows = savedDevices.filter(s => s.parentDeviceId !== null)\n for (const saved of childRows) {\n const Class = this.deviceClasses[saved.type] as DeviceConstructor<IDevice> | undefined\n if (!Class) continue // not a first-class type → accessory, skip\n if (saved.parentDeviceId === null) continue\n if (!restored.has(saved.parentDeviceId)) {\n // Parent isn't a known top-level device this provider owns —\n // probably a stale row pointing at a removed parent. Leave it\n // alone; orphan reconciliation runs later.\n continue\n }\n try {\n await this.ctx.kernel.devices!.create(\n saved.stableId,\n Class,\n {},\n saved.parentDeviceId,\n )\n restored.add(saved.id)\n } catch (err) {\n this.ctx.logger.warn('Failed to restore hub-adopted child', {\n tags: { stableId: saved.stableId, parentDeviceId: saved.parentDeviceId },\n meta: { type: saved.type, error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n }\n\n // ── Utility ─────────────────────────────────────────────────────────\n\n /** Convert an IDevice to the flat DeviceSummary for the cap router. */\n protected toSummary(device: IDevice): DeviceSummary {\n return toDeviceSummary(device, this.addonId)\n }\n}\n","import { z } from 'zod'\nimport type {\n ConfigUISchema,\n ConfigField,\n ConfigTextField,\n ConfigNumberField,\n ConfigBooleanField,\n ConfigSelectField,\n ConfigPasswordField,\n ConfigTextAreaField,\n} from '../interfaces/config-ui.js'\n\n// --- Entry type matching DeviceConfig.entries() output ---\n\nexport interface DeviceConfigEntry {\n key: string\n schema: z.ZodType\n value: unknown\n description?: string\n}\n\n// --- Internal Zod v4 _def shapes (accessed at runtime, not exposed in typings) ---\n\ninterface ZodV4CheckDef {\n check: string\n value?: number\n inclusive?: boolean\n}\n\ninterface ZodV4Check {\n _zod: { def: ZodV4CheckDef }\n}\n\ninterface ZodV4NumberDef {\n type: string\n checks?: ZodV4Check[]\n}\n\ninterface ZodV4DefaultDef {\n type: string\n defaultValue: unknown\n innerType: z.ZodType\n}\n\n/** Access Zod v4 internal .def — not in public typings but stable at runtime */\nfunction zodDef<T>(schema: z.ZodType): T {\n return (schema as unknown as { def: T }).def\n}\n\n/** Access internal properties on a Zod schema instance */\nfunction zodInternals<T>(schema: z.ZodType): T {\n return schema as unknown as T\n}\n\n// --- Public API ---\n\n/**\n * Convert DeviceConfig.entries() output to ConfigUISchema for the admin UI FormBuilder.\n *\n * Each entry's Zod type is inspected to determine the correct ConfigField type:\n * - ZodString → 'text' (or 'password' when key contains \"password\"/\"secret\"/\"token\"/\"apikey\")\n * - ZodNumber → 'number' (extracts min/max/step from Zod v4 checks)\n * - ZodBoolean → 'boolean'\n * - ZodEnum → 'select' (options built from enum values)\n * - Anything else → 'text' fallback\n *\n * Wrapper types ZodDefault, ZodOptional, and ZodNullable are unwrapped transparently.\n * Default values are extracted from ZodDefault wrappers.\n */\nexport function zodEntriesToConfigUI(\n entries: readonly DeviceConfigEntry[],\n sectionTitle = 'Configuration',\n sectionId = 'main',\n): ConfigUISchema {\n const fields: ConfigField[] = entries.map(entry =>\n zodToConfigField(entry.key, entry.schema, entry.description),\n )\n\n return {\n sections: [\n {\n id: sectionId,\n title: sectionTitle,\n fields,\n },\n ],\n }\n}\n\n// --- Internal helpers ---\n\nfunction zodToConfigField(key: string, schema: z.ZodType, description?: string): ConfigField {\n const inner = unwrapZod(schema)\n const defaultValue = getZodDefault(schema)\n const label = description ?? humanizeKey(key)\n\n const base = {\n key,\n label,\n description,\n default: defaultValue,\n } as const\n\n if (inner instanceof z.ZodString) {\n return buildStringField(key, base)\n }\n\n if (inner instanceof z.ZodNumber) {\n return buildNumberField(inner, base)\n }\n\n if (inner instanceof z.ZodBoolean) {\n const field: ConfigBooleanField = { ...base, type: 'boolean' }\n return field\n }\n\n if (inner instanceof z.ZodEnum) {\n return buildEnumField(inner, base)\n }\n\n // Arrays + objects don't fit any primitive field — render as a JSON\n // textarea so the operator at least sees the shape and can copy/edit\n // it, instead of `[object Object],…` produced by the text fallback.\n // `isJson: true` tells the UI + hydrator to round-trip via JSON.\n if (inner instanceof z.ZodArray || inner instanceof z.ZodObject) {\n const field: ConfigTextAreaField = { ...base, type: 'textarea', rows: 6, isJson: true }\n return field\n }\n\n // Fallback — render as plain text\n const fallback: ConfigTextField = { ...base, type: 'text' }\n return fallback\n}\n\nfunction buildStringField(\n key: string,\n base: { key: string; label: string; description?: string; default?: unknown },\n): ConfigTextField | ConfigPasswordField {\n const lowerKey = key.toLowerCase()\n const isSecret =\n lowerKey.includes('password') ||\n lowerKey.includes('secret') ||\n lowerKey.includes('token') ||\n lowerKey.includes('apikey') ||\n lowerKey.includes('api_key')\n\n if (isSecret) {\n const field: ConfigPasswordField = { ...base, type: 'password', showToggle: true }\n return field\n }\n\n const field: ConfigTextField = { ...base, type: 'text' }\n return field\n}\n\nfunction buildNumberField(\n inner: z.ZodNumber,\n base: { key: string; label: string; description?: string; default?: unknown },\n): ConfigNumberField {\n // Zod v4 exposes min/max via public getters minValue/maxValue.\n // When unconstrained, these return -Infinity / Infinity — treat those as absent.\n const anyInner = zodInternals<{ minValue?: number | null; maxValue?: number | null }>(inner)\n const rawMin = anyInner.minValue\n const rawMax = anyInner.maxValue\n const min =\n rawMin != null && isFinite(rawMin) ? rawMin : undefined\n const max =\n rawMax != null && isFinite(rawMax) ? rawMax : undefined\n\n // Step is stored in checks with check name 'multiple_of'\n const step = getMultipleOfStep(inner)\n\n const field: ConfigNumberField = {\n ...base,\n type: 'number',\n ...(min !== undefined ? { min } : {}),\n ...(max !== undefined ? { max } : {}),\n ...(step !== undefined ? { step } : {}),\n }\n return field\n}\n\nfunction getMultipleOfStep(inner: z.ZodNumber): number | undefined {\n const def = zodDef<ZodV4NumberDef>(inner)\n const checks = def.checks ?? []\n for (const check of checks) {\n if (check._zod?.def?.check === 'multiple_of' && check._zod.def.value !== undefined) {\n return check._zod.def.value\n }\n }\n return undefined\n}\n\n// Zod v4: ZodEnum<T> where T extends Readonly<Record<string, string | number>>\n// options returns Array<T[keyof T]> which is (string | number)[]\nfunction buildEnumField(\n inner: z.ZodEnum,\n base: { key: string; label: string; description?: string; default?: unknown },\n): ConfigSelectField {\n const values = (inner.options as (string | number)[]).map(v => String(v))\n\n const field: ConfigSelectField = {\n ...base,\n type: 'select',\n options: values.map(v => ({ label: humanizeKey(v), value: v })),\n }\n return field\n}\n\nfunction unwrapZod(schema: z.ZodType): z.ZodType {\n if (schema instanceof z.ZodDefault) return unwrapZod(zodDef<ZodV4DefaultDef>(schema).innerType)\n if (schema instanceof z.ZodOptional) return unwrapZod(zodDef<{ innerType: z.ZodType }>(schema).innerType)\n if (schema instanceof z.ZodNullable) return unwrapZod(zodDef<{ innerType: z.ZodType }>(schema).innerType)\n return schema\n}\n\nfunction getZodDefault(schema: z.ZodType): unknown {\n if (schema instanceof z.ZodDefault) {\n // Zod v4: def.defaultValue is the literal value (not a function at runtime)\n return zodDef<ZodV4DefaultDef>(schema).defaultValue\n }\n return undefined\n}\n\nfunction humanizeKey(key: string): string {\n return key\n .replace(/([A-Z])/g, ' $1')\n .replace(/[_-]/g, ' ')\n .replace(/^\\w/, c => c.toUpperCase())\n .trim()\n}\n","/**\n * Reactive read handle for one cap-keyed slice of a device's\n * runtime state. Returned by `createDeviceProxy(...).state[capName]`.\n *\n * Three pieces:\n * - `value`: last-known slice. `undefined` until something has\n * populated it (an active subscription, an explicit `refresh()`,\n * or — in the case of the SystemManager mirror source — a\n * warm-boot `getAllSnapshots` payload). Read sync.\n * - `refresh()`: one-shot pull from the underlying source. For\n * the lazy tRPC source this is `deviceState.getCapSlice`; for\n * the SystemManager mirror it's a no-op (the mirror is push-only).\n * - `subscribe(cb)`: hooks into the source's notification channel,\n * filtered by `(deviceId, capName)`. Refcounted — the underlying\n * subscription closes when the last caller unsubscribes.\n *\n * Cross-environment: the same API works in browser (admin-ui via the\n * tRPC client) and on the server (in-process AddonApi). The transport\n * is whatever the supplied source exposes.\n */\nexport interface SliceHandle<T> {\n /** Latest cached slice. Reflects the most recent `refresh()` or\n * push event. `undefined` if neither has happened. */\n readonly value: T | undefined\n /** Force a re-fetch from the hub mirror. Updates `value` + notifies\n * every active subscriber. No-op for the SystemManager mirror\n * source (it's already push-driven). */\n refresh(): Promise<void>\n /** Subscribe to slice changes. Returns the unsubscribe fn.\n * Auto-fires the callback once with the current value (or\n * `undefined`) so the caller sees a snapshot immediately. */\n subscribe(cb: (slice: T | undefined) => void): () => void\n /**\n * Replace the slice on the hub. Routes through\n * `device-state.setCapSlice` — the canonical cross-layer write\n * entrypoint. The local mirror updates via the `onChanged` event\n * (round-trip), so callers should treat the write as eventually\n * consistent and read the new value via `subscribe` rather than\n * synchronously after `await`.\n */\n set(slice: T): Promise<void>\n /**\n * Shallow-merge `partial` into the current slice on the hub.\n * Implemented as read-modify-write client-side: pulls `value`\n * (or refreshes), merges, calls `set()`. Single-writer\n * conventions on each cap make atomic-merge semantics\n * unnecessary — concurrent patches on the same slice from\n * different processes are undefined and should be avoided.\n */\n patch(partial: Partial<T>): Promise<void>\n}\n\n/**\n * Minimal tRPC API surface used by the lazy source. Both `AddonApi`\n * (server) and the tRPC client proxy (browser) satisfy it structurally\n * — we don't import either to avoid pulling them in from a leaf type\n * module.\n */\nexport interface SliceHandleApi {\n readonly deviceState: {\n readonly getCapSlice: {\n query(input: { deviceId: number; capName: string }): Promise<Record<string, unknown> | null>\n }\n readonly setCapSlice: {\n mutate(input: { deviceId: number; capName: string; slice: Record<string, unknown> }): Promise<void>\n }\n }\n readonly live?: {\n readonly onEvent: {\n subscribe(\n input: { category: string },\n opts: { onData: (evt: { data: unknown }) => void; onError?: (err: unknown) => void },\n ): { unsubscribe: () => void }\n }\n }\n}\n\n/**\n * Pluggable state-source contract. `createSliceHandle` is now a thin\n * adapter over this interface — every implementation produces an\n * identical `SliceHandle<T>` shape. Two impls today:\n *\n * - `createLazyTrpcSource(api)`: per-handle local cache, refresh\n * via `deviceState.getCapSlice`, subscribe via `live.onEvent`.\n * Default behavior, used by `createDeviceProxy(api, binding)`\n * when no source is passed.\n * - `createMirrorSource(mirror, listeners)`: reads from a shared\n * `Map<deviceId, Map<capName, slice>>` populated by a\n * `SystemManager` warm-boot. Refresh is a no-op; subscribe\n * registers in a shared listener set that the SystemManager\n * fans out to on every push event.\n *\n * Both produce the same `SliceHandle<T>` API; consumers don't see\n * which source is behind the handle.\n */\nexport interface SliceHandleSource {\n /** Sync read of the last-known slice for `(deviceId, capName)`. */\n read(deviceId: number, capName: string): unknown | undefined\n /** Force a re-fetch (where applicable). No-op when the source is\n * push-only (e.g. the SystemManager mirror). */\n refresh(deviceId: number, capName: string): Promise<void>\n /** Register a listener for slice changes. Returns the unsubscribe\n * fn. Implementations should fan out the latest cached slice to\n * the callback synchronously when seeding is desired (the handle\n * always seeds via a separate `cb(read(...))` call). */\n watch(\n deviceId: number,\n capName: string,\n cb: (slice: unknown | undefined) => void,\n ): () => void\n /** Write the slice for `(deviceId, capName)` to the hub via the\n * canonical `device-state.setCapSlice` entrypoint. Throws if the\n * source has no write transport (e.g. a mirror source built\n * without an api reference). */\n write(deviceId: number, capName: string, slice: Record<string, unknown>): Promise<void>\n}\n\nconst DEVICE_STATE_EVENT_CATEGORY = 'device.state-changed'\n\n/**\n * Lazy tRPC source — the default behavior. Maintains a per-key local\n * cache (`{deviceId}:{capName}` → last slice), one shared\n * `live.onEvent` bridge that fans out into the listener map, and\n * `refresh` round-trips through `deviceState.getCapSlice`.\n *\n * The bridge is opened on first `watch()` and closed when the last\n * watcher unsubscribes — keeps idle handles cheap.\n */\nexport function createLazyTrpcSource(api: SliceHandleApi): SliceHandleSource {\n const cache = new Map<string, unknown>()\n const listeners = new Map<string, Set<(slice: unknown | undefined) => void>>()\n let bridge: { unsubscribe: () => void } | null = null\n\n const keyOf = (deviceId: number, capName: string): string => `${deviceId}:${capName}`\n\n const ensureBridge = (): void => {\n if (bridge) return\n if (!api.live?.onEvent) return\n bridge = api.live.onEvent.subscribe(\n { category: DEVICE_STATE_EVENT_CATEGORY },\n {\n onData: (evt) => {\n const data = evt.data as { deviceId?: number; capName?: string; slice?: unknown } | null\n if (!data || typeof data.deviceId !== 'number' || typeof data.capName !== 'string') return\n const k = keyOf(data.deviceId, data.capName)\n cache.set(k, data.slice)\n const set = listeners.get(k)\n if (!set) return\n for (const cb of set) {\n try { cb(data.slice) } catch { /* listener errors don't break the bridge */ }\n }\n },\n },\n )\n }\n\n const closeBridgeIfIdle = (): void => {\n if (!bridge) return\n if (listeners.size > 0) return\n bridge.unsubscribe()\n bridge = null\n }\n\n return {\n read(deviceId, capName) {\n return cache.get(keyOf(deviceId, capName))\n },\n async refresh(deviceId, capName) {\n const slice = await api.deviceState.getCapSlice.query({ deviceId, capName })\n const k = keyOf(deviceId, capName)\n cache.set(k, slice ?? undefined)\n const set = listeners.get(k)\n if (set) {\n for (const cb of set) {\n try { cb(slice ?? undefined) } catch { /* listener errors don't break refresh */ }\n }\n }\n },\n watch(deviceId, capName, cb) {\n const k = keyOf(deviceId, capName)\n let set = listeners.get(k)\n if (!set) { set = new Set(); listeners.set(k, set) }\n set.add(cb)\n ensureBridge()\n return () => {\n set!.delete(cb)\n if (set!.size === 0) listeners.delete(k)\n closeBridgeIfIdle()\n }\n },\n async write(deviceId, capName, slice) {\n await api.deviceState.setCapSlice.mutate({ deviceId, capName, slice })\n },\n }\n}\n\n/**\n * Mirror source — reads from a shared map populated by a\n * `SystemManager` warm-boot + push event handler. Refresh is a no-op\n * (the SystemManager owns the update loop). `watch()` registers in a\n * shared listener set — the SystemManager calls these from its single\n * `device.state-changed` subscription.\n *\n * The mirror map and listener map are passed in by reference so the\n * SystemManager can mutate both as events arrive.\n */\nexport function createMirrorSource(\n mirror: ReadonlyMap<number, ReadonlyMap<string, unknown>>,\n listeners: Map<string, Set<(slice: unknown | undefined) => void>>,\n api?: SliceHandleApi,\n): SliceHandleSource {\n const keyOf = (deviceId: number, capName: string): string => `${deviceId}:${capName}`\n return {\n read(deviceId, capName) {\n return mirror.get(deviceId)?.get(capName)\n },\n async refresh() {\n // No-op: the mirror is fed by `device.state-changed` events at\n // the SystemManager level. Forcing a re-fetch from a single\n // handle would race with the global update loop and is never\n // what the caller wants.\n },\n watch(deviceId, capName, cb) {\n const k = keyOf(deviceId, capName)\n let set = listeners.get(k)\n if (!set) { set = new Set(); listeners.set(k, set) }\n set.add(cb)\n return () => {\n set!.delete(cb)\n if (set!.size === 0) listeners.delete(k)\n }\n },\n async write(deviceId, capName, slice) {\n if (!api) {\n throw new Error('createMirrorSource: write requires an api reference — pass it as the third argument when constructing the source')\n }\n await api.deviceState.setCapSlice.mutate({ deviceId, capName, slice })\n },\n }\n}\n\n/**\n * Build a `SliceHandle<T>` bound to `(deviceId, capName)`. Caller\n * provides the type parameter — codegen passes\n * `InferRuntimeState<typeof <cap>>` so consumers see the cap's typed\n * shape:\n *\n * const dev = createDeviceProxy(api, binding)\n * dev.state.battery.value // BatteryStatus | undefined\n * dev.state.battery.value?.percentage\n *\n * Two-arg form (deprecated path, kept for spec compatibility):\n * passes the api directly, builds a per-handle lazy source. New\n * code should pre-build a single source and reuse it across handles\n * via the explicit `source` form.\n */\nexport function createSliceHandle<T>(\n source: SliceHandleSource | SliceHandleApi,\n deviceId: number,\n capName: string,\n): SliceHandle<T> {\n // Discriminate between source and api by structure: a source has\n // a `read` method, an api has a `deviceState` namespace. Spec-style\n // tests still pass `api`; production codegen passes a source.\n const src: SliceHandleSource = isSource(source)\n ? source\n : createLazyTrpcSource(source)\n\n return {\n get value() { return src.read(deviceId, capName) as T | undefined },\n refresh() { return src.refresh(deviceId, capName) },\n subscribe(cb) {\n const unwatch = src.watch(deviceId, capName, (slice) => {\n try { cb(slice as T | undefined) } catch { /* listener errors are isolated */ }\n })\n // Seed the listener with the current cached read — sync,\n // deterministic, no transport. Mirror sources serve the\n // canonical value here; lazy sources serve their per-key\n // cache (undefined on cold start).\n try { cb(src.read(deviceId, capName) as T | undefined) } catch { /* ignore */ }\n // Cold-start kick: when the read returned nothing AND the\n // source has a meaningful refresh path (lazy tRPC), pull once\n // in the background. Fan-out via `watch` delivers the result\n // to the same listener — no double-seeding. The mirror\n // source's refresh is a no-op, so this is a cheap miss for\n // SystemManager-backed handles.\n if (src.read(deviceId, capName) === undefined) {\n void src.refresh(deviceId, capName).catch(() => undefined)\n }\n return unwatch\n },\n async set(slice) {\n await src.write(deviceId, capName, slice as Record<string, unknown>)\n },\n async patch(partial) {\n const current = src.read(deviceId, capName) as Record<string, unknown> | undefined\n const next = { ...(current ?? {}), ...partial as Record<string, unknown> }\n await src.write(deviceId, capName, next)\n },\n }\n}\n\nfunction isSource(x: SliceHandleSource | SliceHandleApi): x is SliceHandleSource {\n return typeof (x as SliceHandleSource).read === 'function'\n && typeof (x as SliceHandleSource).watch === 'function'\n}\n","// AUTO-GENERATED — do not edit manually.\n// Regenerate with: npx tsx scripts/generate-device-proxy.ts\n/* eslint-disable */\n\nimport type { InferDeviceProxyCap, InferRuntimeState } from '../capabilities/capability-definition.js'\nimport type { DeviceBinding } from '../device/device-binding.js'\nimport { createSliceHandle, createLazyTrpcSource, type SliceHandle, type SliceHandleApi, type SliceHandleSource } from '../device/device-state-handle.js'\nimport type { AddonApi } from './addon-api.js'\nimport type { accessoriesCapability } from '../capabilities/accessories.cap.js'\nimport type { audioAnalysisCapability } from '../capabilities/audio-analysis.cap.js'\nimport type { audioMetricsCapability } from '../capabilities/audio-metrics.cap.js'\nimport type { batteryCapability } from '../capabilities/battery.cap.js'\nimport type { brightnessCapability } from '../capabilities/brightness.cap.js'\nimport type { cameraCredentialsCapability } from '../capabilities/camera-credentials.cap.js'\nimport type { cameraStreamsCapability } from '../capabilities/camera-streams.cap.js'\nimport type { detectionPipelineCapability } from '../capabilities/detection-pipeline.cap.js'\nimport type { deviceDiscoveryCapability } from '../capabilities/device-discovery.cap.js'\nimport type { deviceOpsCapability } from '../capabilities/device-ops.cap.js'\nimport type { deviceStatusCapability } from '../capabilities/device-status.cap.js'\nimport type { doorbellCapability } from '../capabilities/doorbell.cap.js'\nimport type { eventsCapability } from '../capabilities/events.cap.js'\nimport type { featureProbeCapability } from '../capabilities/feature-probe.cap.js'\nimport type { intercomCapability } from '../capabilities/intercom.cap.js'\nimport type { motionCapability } from '../capabilities/motion.cap.js'\nimport type { motionDetectionCapability } from '../capabilities/motion-detection.cap.js'\nimport type { motionTriggerCapability } from '../capabilities/motion-trigger.cap.js'\nimport type { nativeObjectDetectionCapability } from '../capabilities/native-object-detection.cap.js'\nimport type { osdCapability } from '../capabilities/osd.cap.js'\nimport type { pipelineAnalyticsCapability } from '../capabilities/pipeline-analytics.cap.js'\nimport type { ptzCapability } from '../capabilities/ptz.cap.js'\nimport type { ptzAutotrackCapability } from '../capabilities/ptz-autotrack.cap.js'\nimport type { rebootCapability } from '../capabilities/reboot.cap.js'\nimport type { recordingCapability } from '../capabilities/recording.cap.js'\nimport type { snapshotCapability } from '../capabilities/snapshot.cap.js'\nimport type { switchCapability } from '../capabilities/switch.cap.js'\nimport type { webrtcSessionCapability } from '../capabilities/webrtc-session.cap.js'\nimport type { zoneAnalyticsCapability } from '../capabilities/zone-analytics.cap.js'\nimport type { zoneRulesCapability } from '../capabilities/zone-rules.cap.js'\nimport type { zonesCapability } from '../capabilities/zones.cap.js'\nimport type { addonSettingsCapability } from '../capabilities/addon-settings.cap.js'\nimport type { deviceManagerCapability } from '../capabilities/device-manager.cap.js'\nimport type { deviceStateCapability } from '../capabilities/device-state.cap.js'\nimport type { networkQualityCapability } from '../capabilities/network-quality.cap.js'\nimport type { pipelineExecutorCapability } from '../capabilities/pipeline-executor.cap.js'\nimport type { pipelineOrchestratorCapability } from '../capabilities/pipeline-orchestrator.cap.js'\nimport type { pipelineRunnerCapability } from '../capabilities/pipeline-runner.cap.js'\nimport type { recordingEngineCapability } from '../capabilities/recording-engine.cap.js'\nimport type { snapshotProviderCapability } from '../capabilities/snapshot-provider.cap.js'\nimport type { streamBrokerCapability } from '../capabilities/stream-broker.cap.js'\n\n/**\n * Reactive read handle bag for device runtime state. Each entry mirrors the\n * cap's `runtimeState` schema and stays live via the `device-state` cap's\n * `onChanged` event. See `device-state-handle.ts` for the contract.\n */\nexport interface DeviceProxyState {\n readonly audioMetrics: SliceHandle<InferRuntimeState<typeof audioMetricsCapability>>\n readonly battery: SliceHandle<InferRuntimeState<typeof batteryCapability>>\n readonly brightness: SliceHandle<InferRuntimeState<typeof brightnessCapability>>\n readonly cameraStreams: SliceHandle<InferRuntimeState<typeof cameraStreamsCapability>>\n readonly deviceDiscovery: SliceHandle<InferRuntimeState<typeof deviceDiscoveryCapability>>\n readonly deviceStatus: SliceHandle<InferRuntimeState<typeof deviceStatusCapability>>\n readonly doorbell: SliceHandle<InferRuntimeState<typeof doorbellCapability>>\n readonly featureProbe: SliceHandle<InferRuntimeState<typeof featureProbeCapability>>\n readonly motion: SliceHandle<InferRuntimeState<typeof motionCapability>>\n readonly motionTrigger: SliceHandle<InferRuntimeState<typeof motionTriggerCapability>>\n readonly ptzAutotrack: SliceHandle<InferRuntimeState<typeof ptzAutotrackCapability>>\n readonly switch: SliceHandle<InferRuntimeState<typeof switchCapability>>\n readonly zoneAnalytics: SliceHandle<InferRuntimeState<typeof zoneAnalyticsCapability>>\n readonly zoneRules: SliceHandle<InferRuntimeState<typeof zoneRulesCapability>>\n readonly zones: SliceHandle<InferRuntimeState<typeof zonesCapability>>\n}\n\n/**\n * Unified per-device proxy interface. Each optional property is present at\n * runtime only when the device's binding includes that capability.\n *\n * The optional `binding` field is populated post-construction by callers\n * that own a binding cache (e.g. the SDK's `System` mirror). Consumers\n * that need the full per-device binding map can read it from there\n * without paying for a second `getBindings` round-trip — see Phase 5\n * dedup notes in `scripts/generate-device-proxy.ts`.\n */\nexport interface DeviceProxy {\n readonly deviceId: number\n /** Reactive runtime state, one slot per cap that declares `runtimeState`. */\n readonly state: DeviceProxyState\n /** Resolved binding entry list (or null if the proxy was constructed\n * without a cache-aware caller — defaults to null since\n * `createDeviceProxy` itself does not own the binding). */\n readonly binding: DeviceBinding | null\n readonly accessories?: InferDeviceProxyCap<typeof accessoriesCapability>\n readonly audioAnalysis?: InferDeviceProxyCap<typeof audioAnalysisCapability>\n readonly audioMetrics?: InferDeviceProxyCap<typeof audioMetricsCapability>\n readonly battery?: InferDeviceProxyCap<typeof batteryCapability>\n readonly brightness?: InferDeviceProxyCap<typeof brightnessCapability>\n readonly cameraCredentials?: InferDeviceProxyCap<typeof cameraCredentialsCapability>\n readonly cameraStreams?: InferDeviceProxyCap<typeof cameraStreamsCapability>\n readonly detectionPipeline?: InferDeviceProxyCap<typeof detectionPipelineCapability>\n readonly deviceDiscovery?: InferDeviceProxyCap<typeof deviceDiscoveryCapability>\n readonly deviceOps?: InferDeviceProxyCap<typeof deviceOpsCapability>\n readonly deviceStatus?: InferDeviceProxyCap<typeof deviceStatusCapability>\n readonly doorbell?: InferDeviceProxyCap<typeof doorbellCapability>\n readonly events?: InferDeviceProxyCap<typeof eventsCapability>\n readonly featureProbe?: InferDeviceProxyCap<typeof featureProbeCapability>\n readonly intercom?: InferDeviceProxyCap<typeof intercomCapability>\n readonly motion?: InferDeviceProxyCap<typeof motionCapability>\n readonly motionDetection?: InferDeviceProxyCap<typeof motionDetectionCapability>\n readonly motionTrigger?: InferDeviceProxyCap<typeof motionTriggerCapability>\n readonly nativeObjectDetection?: InferDeviceProxyCap<typeof nativeObjectDetectionCapability>\n readonly osd?: InferDeviceProxyCap<typeof osdCapability>\n readonly pipelineAnalytics?: InferDeviceProxyCap<typeof pipelineAnalyticsCapability>\n readonly ptz?: InferDeviceProxyCap<typeof ptzCapability>\n readonly ptzAutotrack?: InferDeviceProxyCap<typeof ptzAutotrackCapability>\n readonly reboot?: InferDeviceProxyCap<typeof rebootCapability>\n readonly recording?: InferDeviceProxyCap<typeof recordingCapability>\n readonly snapshot?: InferDeviceProxyCap<typeof snapshotCapability>\n readonly switch?: InferDeviceProxyCap<typeof switchCapability>\n readonly webrtcSession?: InferDeviceProxyCap<typeof webrtcSessionCapability>\n readonly zoneAnalytics?: InferDeviceProxyCap<typeof zoneAnalyticsCapability>\n readonly zoneRules?: InferDeviceProxyCap<typeof zoneRulesCapability>\n readonly zones?: InferDeviceProxyCap<typeof zonesCapability>\n readonly addonSettings: Pick<InferDeviceProxyCap<typeof addonSettingsCapability>, 'getDeviceSettings' | 'updateDeviceSettings'>\n readonly deviceManager: Pick<InferDeviceProxyCap<typeof deviceManagerCapability>, 'loadConfig' | 'loadRuntimeState' | 'loadMeta' | 'setName' | 'setLocation' | 'setMetadata' | 'setDisabled' | 'getDevice' | 'getStreamSources' | 'getConfigSchema' | 'getSettingsSchema' | 'updateConfig' | 'enable' | 'disable' | 'remove' | 'getStreamProfileMap' | 'setStreamProfileMap' | 'probeStreams' | 'getBindings' | 'getAllBindings' | 'setWrapperActive' | 'getDeviceSettingsAggregate' | 'getDeviceLiveInfoAggregate' | 'getDeviceAggregate' | 'updateDeviceField' | 'updateDeviceFieldsBatch' | 'testField' | 'getDeviceStatusAggregate'>\n readonly deviceState: Pick<InferDeviceProxyCap<typeof deviceStateCapability>, 'getSnapshot' | 'getCapSlice' | 'setCapSlice'>\n readonly networkQuality: Pick<InferDeviceProxyCap<typeof networkQualityCapability>, 'getDeviceStats' | 'reportClientStats'>\n readonly pipelineExecutor: Pick<InferDeviceProxyCap<typeof pipelineExecutorCapability>, 'runPipeline' | 'runPipelineBatch'>\n readonly pipelineOrchestrator: Pick<InferDeviceProxyCap<typeof pipelineOrchestratorCapability>, 'assignPipeline' | 'unassignPipeline' | 'getPipelineAssignment' | 'getCameraMetrics' | 'assignDecoder' | 'unassignDecoder' | 'assignAudio' | 'unassignAudio' | 'getAudioAssignment' | 'getAudioAssignments' | 'getDecoderAssignment' | 'getCameraSettings' | 'setCameraStepToggle' | 'getCameraStepOverrides' | 'setCameraStepOverride' | 'setCameraPipelineForAgent' | 'resolvePipeline' | 'getDeviceSettingsContribution' | 'getDeviceLiveContribution' | 'applyDeviceSettingsPatch'>\n readonly pipelineRunner: Pick<InferDeviceProxyCap<typeof pipelineRunnerCapability>, 'detachCamera' | 'getCameraMetrics'>\n readonly recordingEngine: Pick<InferDeviceProxyCap<typeof recordingEngineCapability>, 'getPolicyStatus'>\n readonly snapshotProvider: Pick<InferDeviceProxyCap<typeof snapshotProviderCapability>, 'supportsDevice' | 'getSnapshot'>\n readonly streamBroker: Pick<InferDeviceProxyCap<typeof streamBrokerCapability>, 'publishCameraStream' | 'retractCameraStream' | 'assignProfile' | 'unassignProfile' | 'restartProfile' | 'getDeviceSettingsContribution' | 'getDeviceLiveContribution' | 'applyDeviceSettingsPatch'>\n}\n\n/**\n * Build a DeviceProxy that pre-binds deviceId + nodeId on every method call\n * and dispatches through the existing cap-router tRPC procedures.\n *\n * Optional `opts.stateSource` lets a SystemManager pass a shared mirror\n * source so every device proxy reads from the same in-memory map and\n * shares the warm-boot/push-event update loop. When omitted, a per-proxy\n * lazy tRPC source is created — the path used by `ctx.fetchDevice` and\n * `BackendClient.fetchDevice` for one-off reads.\n *\n * The returned proxy's `binding` field is set to the input `binding`\n * by default — callers that want to expose a different value (e.g. the\n * SDK's `System` patches it from a shared cache) can overwrite the\n * field on the returned object.\n */\nexport function createDeviceProxy(\n api: AddonApi,\n binding: DeviceBinding,\n opts?: { stateSource?: SliceHandleSource },\n): DeviceProxy {\n const typedApi = api as unknown as Record<string, Record<string, Record<string, (input?: unknown) => unknown>>>\n // Default lazy source: builds its own cache + bridge on first watch().\n // The SystemManager passes a shared mirror source instead.\n const stateSource: SliceHandleSource = opts?.stateSource\n ?? createLazyTrpcSource(api as unknown as SliceHandleApi)\n\n // ── Dispatch helpers ────────────────────────────────────────────────\n //\n // Every per-cap method below collapses to a single `dispatch(...)` /\n // `dispatchSystem(...)` call. The helpers handle three concerns\n // uniformly so the per-method code reads as a one-line declaration of\n // intent (cap + method + kind) rather than 25 lines of plumbing.\n type Kind = 'query' | 'mutation' | 'subscription'\n\n /** Merge `{deviceId, nodeId?}` into the caller-supplied input. */\n function mergeInput(input: unknown, nodeId: string | undefined): Record<string, unknown> {\n const base = typeof input === 'object' && input !== null ? input as Record<string, unknown> : {}\n return nodeId !== undefined\n ? { ...base, deviceId: binding.deviceId, nodeId }\n : { ...base, deviceId: binding.deviceId }\n }\n\n /**\n * Invoke `api.<capProp>.<method>.{query|mutate|subscribe}(merged)`.\n * Returns `any` so the caller-side per-method declaration assigns\n * cleanly into the strongly-typed `InferDeviceProxyCap<...>` shape on\n * the `DeviceProxy` interface — the proxy's contract is enforced by\n * the interface, not the dispatch helper.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function callLeaf(capProp: string, method: string, kind: Kind, merged: unknown, push: unknown): any {\n const leaf = typedApi[capProp]?.[method]\n if (!leaf) throw new Error(`DeviceProxy: api has no '${capProp}.${method}'`)\n const fn = leaf as unknown as {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n query: (i: unknown) => any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mutate: (i: unknown) => any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n subscribe: (i: unknown, push: unknown) => any\n }\n if (kind === 'mutation') return fn.mutate(merged)\n if (kind === 'subscription') return fn.subscribe(merged, push)\n return fn.query(merged)\n }\n\n /**\n * Device-scoped cap dispatch. Looks up the binding entry for `capName`\n * to pin the call to the worker that owns the per-device provider; when\n * no entry exists (cluster-wide singletons like `zones` /\n * `zone-rules` / `audio-metrics` that don't register per-device\n * natives), nodeId is omitted and the cap-router's `resolveProvider`\n * falls through to the local provider — a Moleculer bridge proxy when\n * the actual singleton lives in a worker.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function dispatch(capName: string, capProp: string, method: string, kind: Kind, input?: unknown, push?: unknown): any {\n const entry = binding.entries.find((e) => e.capName === capName)\n return callLeaf(capProp, method, kind, mergeInput(input, entry?.providerNodeId), push)\n }\n\n /**\n * System-cap dispatch. No binding gate — system caps are cluster-wide\n * singletons; the nodeId is left absent so caps that load-balance (e.g.\n * `pipeline-runner`) can resolve their own target node.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function dispatchSystem(capProp: string, method: string, kind: Kind, input?: unknown, push?: unknown): any {\n return callLeaf(capProp, method, kind, mergeInput(input, undefined), push)\n }\n\n const accessoriesInterface = {\n getStatus: (input?: unknown) => dispatch('accessories', 'accessories', 'getStatus', 'query', input),\n }\n\n const audioAnalysisInterface = {\n resolveDeviceSettings: (input?: unknown) => dispatch('audio-analysis', 'audioAnalysis', 'resolveDeviceSettings', 'query', input),\n getDeviceSettingsContribution: (input?: unknown) => dispatch('audio-analysis', 'audioAnalysis', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatch('audio-analysis', 'audioAnalysis', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatch('audio-analysis', 'audioAnalysis', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n const audioMetricsInterface = {\n getCurrentSnapshot: (input?: unknown) => dispatch('audio-metrics', 'audioMetrics', 'getCurrentSnapshot', 'query', input),\n getHistory: (input?: unknown) => dispatch('audio-metrics', 'audioMetrics', 'getHistory', 'query', input),\n }\n\n const batteryInterface = {\n getStatus: (input?: unknown) => dispatch('battery', 'battery', 'getStatus', 'query', input),\n }\n\n const brightnessInterface = {\n setBrightness: (input?: unknown) => dispatch('brightness', 'brightness', 'setBrightness', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('brightness', 'brightness', 'getStatus', 'query', input),\n }\n\n const cameraCredentialsInterface = {\n getCredentials: (input?: unknown) => dispatch('camera-credentials', 'cameraCredentials', 'getCredentials', 'query', input),\n getStatus: (input?: unknown) => dispatch('camera-credentials', 'cameraCredentials', 'getStatus', 'query', input),\n }\n\n const cameraStreamsInterface = {\n getCameraStreams: (input?: unknown) => dispatch('camera-streams', 'cameraStreams', 'getCameraStreams', 'query', input),\n getBrokerStreams: (input?: unknown) => dispatch('camera-streams', 'cameraStreams', 'getBrokerStreams', 'query', input),\n getRtspEntries: (input?: unknown) => dispatch('camera-streams', 'cameraStreams', 'getRtspEntries', 'query', input),\n }\n\n const detectionPipelineInterface = {\n getDeviceSettingsContribution: (input?: unknown) => dispatch('detection-pipeline', 'detectionPipeline', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatch('detection-pipeline', 'detectionPipeline', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatch('detection-pipeline', 'detectionPipeline', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n const deviceDiscoveryInterface = {\n listDiscovered: (input?: unknown) => dispatch('device-discovery', 'deviceDiscovery', 'listDiscovered', 'query', input),\n refreshDiscovery: (input?: unknown) => dispatch('device-discovery', 'deviceDiscovery', 'refreshDiscovery', 'mutation', input),\n adoptDevice: (input?: unknown) => dispatch('device-discovery', 'deviceDiscovery', 'adoptDevice', 'mutation', input),\n releaseDevice: (input?: unknown) => dispatch('device-discovery', 'deviceDiscovery', 'releaseDevice', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('device-discovery', 'deviceDiscovery', 'getStatus', 'query', input),\n }\n\n const deviceOpsInterface = {\n getStreamSources: (input?: unknown) => dispatch('device-ops', 'deviceOps', 'getStreamSources', 'query', input),\n getConfigEntries: (input?: unknown) => dispatch('device-ops', 'deviceOps', 'getConfigEntries', 'query', input),\n setConfig: (input?: unknown) => dispatch('device-ops', 'deviceOps', 'setConfig', 'mutation', input),\n removeDevice: (input?: unknown) => dispatch('device-ops', 'deviceOps', 'removeDevice', 'mutation', input),\n getSettingsSchema: (input?: unknown) => dispatch('device-ops', 'deviceOps', 'getSettingsSchema', 'query', input),\n }\n\n const deviceStatusInterface = {\n getStatus: (input?: unknown) => dispatch('device-status', 'deviceStatus', 'getStatus', 'query', input),\n }\n\n const doorbellInterface = {\n getStatus: (input?: unknown) => dispatch('doorbell', 'doorbell', 'getStatus', 'query', input),\n }\n\n const eventsInterface = {\n getEvents: (input?: unknown) => dispatch('events', 'events', 'getEvents', 'query', input),\n getEventThumbnail: (input?: unknown) => dispatch('events', 'events', 'getEventThumbnail', 'query', input),\n getEventClipUrl: (input?: unknown) => dispatch('events', 'events', 'getEventClipUrl', 'query', input),\n }\n\n const featureProbeInterface = {\n getStatus: (input?: unknown) => dispatch('feature-probe', 'featureProbe', 'getStatus', 'query', input),\n }\n\n const intercomInterface = {\n startSession: (input?: unknown) => dispatch('intercom', 'intercom', 'startSession', 'mutation', input),\n handleAnswer: (input?: unknown) => dispatch('intercom', 'intercom', 'handleAnswer', 'mutation', input),\n stopSession: (input?: unknown) => dispatch('intercom', 'intercom', 'stopSession', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('intercom', 'intercom', 'getStatus', 'query', input),\n }\n\n const motionInterface = {\n isDetected: (input?: unknown) => dispatch('motion', 'motion', 'isDetected', 'query', input),\n getStatus: (input?: unknown) => dispatch('motion', 'motion', 'getStatus', 'query', input),\n }\n\n const motionDetectionInterface = {\n analyze: (input?: unknown) => dispatch('motion-detection', 'motionDetection', 'analyze', 'mutation', input),\n removeCamera: (input?: unknown) => dispatch('motion-detection', 'motionDetection', 'removeCamera', 'mutation', input),\n reset: (input?: unknown) => dispatch('motion-detection', 'motionDetection', 'reset', 'mutation', input),\n getDeviceSettingsContribution: (input?: unknown) => dispatch('motion-detection', 'motionDetection', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatch('motion-detection', 'motionDetection', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatch('motion-detection', 'motionDetection', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n const motionTriggerInterface = {\n setMotionTrigger: (input?: unknown) => dispatch('motion-trigger', 'motionTrigger', 'setMotionTrigger', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('motion-trigger', 'motionTrigger', 'getStatus', 'query', input),\n }\n\n const nativeObjectDetectionInterface = {\n getStatus: (input?: unknown) => dispatch('native-object-detection', 'nativeObjectDetection', 'getStatus', 'query', input),\n }\n\n const osdInterface = {\n setOverlay: (input?: unknown) => dispatch('osd', 'osd', 'setOverlay', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('osd', 'osd', 'getStatus', 'query', input),\n }\n\n const pipelineAnalyticsInterface = {\n getActiveTracks: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getActiveTracks', 'query', input),\n getTrack: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getTrack', 'query', input),\n listTracks: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'listTracks', 'query', input),\n clearTracks: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'clearTracks', 'mutation', input),\n getMotionEvents: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getMotionEvents', 'query', input),\n getObjectEvents: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getObjectEvents', 'query', input),\n getAudioEvents: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getAudioEvents', 'query', input),\n getEventMedia: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getEventMedia', 'query', input),\n getTrackMedia: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getTrackMedia', 'query', input),\n getDeviceSettingsContribution: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n const ptzInterface = {\n move: (input?: unknown) => dispatch('ptz', 'ptz', 'move', 'mutation', input),\n continuousMove: (input?: unknown) => dispatch('ptz', 'ptz', 'continuousMove', 'mutation', input),\n stop: (input?: unknown) => dispatch('ptz', 'ptz', 'stop', 'mutation', input),\n getPresets: (input?: unknown) => dispatch('ptz', 'ptz', 'getPresets', 'query', input),\n goToPreset: (input?: unknown) => dispatch('ptz', 'ptz', 'goToPreset', 'mutation', input),\n goHome: (input?: unknown) => dispatch('ptz', 'ptz', 'goHome', 'mutation', input),\n getPosition: (input?: unknown) => dispatch('ptz', 'ptz', 'getPosition', 'query', input),\n getStatus: (input?: unknown) => dispatch('ptz', 'ptz', 'getStatus', 'query', input),\n }\n\n const ptzAutotrackInterface = {\n getStatus: (input?: unknown) => dispatch('ptz-autotrack', 'ptzAutotrack', 'getStatus', 'query', input),\n setEnabled: (input?: unknown) => dispatch('ptz-autotrack', 'ptzAutotrack', 'setEnabled', 'mutation', input),\n getSettings: (input?: unknown) => dispatch('ptz-autotrack', 'ptzAutotrack', 'getSettings', 'query', input),\n setSettings: (input?: unknown) => dispatch('ptz-autotrack', 'ptzAutotrack', 'setSettings', 'mutation', input),\n }\n\n const rebootInterface = {\n reboot: (input?: unknown) => dispatch('reboot', 'reboot', 'reboot', 'mutation', input),\n }\n\n const recordingInterface = {\n getSegments: (input?: unknown) => dispatch('recording', 'recording', 'getSegments', 'query', input),\n getPlaybackUrl: (input?: unknown) => dispatch('recording', 'recording', 'getPlaybackUrl', 'query', input),\n getThumbnailAt: (input?: unknown) => dispatch('recording', 'recording', 'getThumbnailAt', 'query', input),\n }\n\n const snapshotInterface = {\n getSnapshot: (input?: unknown) => dispatch('snapshot', 'snapshot', 'getSnapshot', 'query', input),\n invalidateCache: (input?: unknown) => dispatch('snapshot', 'snapshot', 'invalidateCache', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('snapshot', 'snapshot', 'getStatus', 'query', input),\n getDeviceSettingsContribution: (input?: unknown) => dispatch('snapshot', 'snapshot', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatch('snapshot', 'snapshot', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatch('snapshot', 'snapshot', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n const switchInterface = {\n setState: (input?: unknown) => dispatch('switch', 'switch', 'setState', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('switch', 'switch', 'getStatus', 'query', input),\n }\n\n const webrtcSessionInterface = {\n listStreams: (input?: unknown) => dispatch('webrtc-session', 'webrtcSession', 'listStreams', 'query', input),\n createSession: (input?: unknown) => dispatch('webrtc-session', 'webrtcSession', 'createSession', 'mutation', input),\n handleAnswer: (input?: unknown) => dispatch('webrtc-session', 'webrtcSession', 'handleAnswer', 'mutation', input),\n closeSession: (input?: unknown) => dispatch('webrtc-session', 'webrtcSession', 'closeSession', 'mutation', input),\n hasAdaptiveBitrate: (input?: unknown) => dispatch('webrtc-session', 'webrtcSession', 'hasAdaptiveBitrate', 'query', input),\n }\n\n const zoneAnalyticsInterface = {\n getCurrentSnapshot: (input?: unknown) => dispatch('zone-analytics', 'zoneAnalytics', 'getCurrentSnapshot', 'query', input),\n getZoneHistory: (input?: unknown) => dispatch('zone-analytics', 'zoneAnalytics', 'getZoneHistory', 'query', input),\n getCameraHistory: (input?: unknown) => dispatch('zone-analytics', 'zoneAnalytics', 'getCameraHistory', 'query', input),\n getUnzonedHistory: (input?: unknown) => dispatch('zone-analytics', 'zoneAnalytics', 'getUnzonedHistory', 'query', input),\n }\n\n const zoneRulesInterface = {\n listRules: (input?: unknown) => dispatch('zone-rules', 'zoneRules', 'listRules', 'query', input),\n setRules: (input?: unknown) => dispatch('zone-rules', 'zoneRules', 'setRules', 'mutation', input),\n }\n\n const zonesInterface = {\n listZones: (input?: unknown) => dispatch('zones', 'zones', 'listZones', 'query', input),\n addZone: (input?: unknown) => dispatch('zones', 'zones', 'addZone', 'mutation', input),\n removeZone: (input?: unknown) => dispatch('zones', 'zones', 'removeZone', 'mutation', input),\n updateZone: (input?: unknown) => dispatch('zones', 'zones', 'updateZone', 'mutation', input),\n }\n\n const addonSettingsInterface = {\n getDeviceSettings: (input?: unknown) => dispatchSystem('addonSettings', 'getDeviceSettings', 'query', input),\n updateDeviceSettings: (input?: unknown) => dispatchSystem('addonSettings', 'updateDeviceSettings', 'mutation', input),\n }\n\n const deviceManagerInterface = {\n loadConfig: (input?: unknown) => dispatchSystem('deviceManager', 'loadConfig', 'query', input),\n loadRuntimeState: (input?: unknown) => dispatchSystem('deviceManager', 'loadRuntimeState', 'query', input),\n loadMeta: (input?: unknown) => dispatchSystem('deviceManager', 'loadMeta', 'query', input),\n setName: (input?: unknown) => dispatchSystem('deviceManager', 'setName', 'mutation', input),\n setLocation: (input?: unknown) => dispatchSystem('deviceManager', 'setLocation', 'mutation', input),\n setMetadata: (input?: unknown) => dispatchSystem('deviceManager', 'setMetadata', 'mutation', input),\n setDisabled: (input?: unknown) => dispatchSystem('deviceManager', 'setDisabled', 'mutation', input),\n getDevice: (input?: unknown) => dispatchSystem('deviceManager', 'getDevice', 'query', input),\n getStreamSources: (input?: unknown) => dispatchSystem('deviceManager', 'getStreamSources', 'query', input),\n getConfigSchema: (input?: unknown) => dispatchSystem('deviceManager', 'getConfigSchema', 'query', input),\n getSettingsSchema: (input?: unknown) => dispatchSystem('deviceManager', 'getSettingsSchema', 'query', input),\n updateConfig: (input?: unknown) => dispatchSystem('deviceManager', 'updateConfig', 'mutation', input),\n enable: (input?: unknown) => dispatchSystem('deviceManager', 'enable', 'mutation', input),\n disable: (input?: unknown) => dispatchSystem('deviceManager', 'disable', 'mutation', input),\n remove: (input?: unknown) => dispatchSystem('deviceManager', 'remove', 'mutation', input),\n getStreamProfileMap: (input?: unknown) => dispatchSystem('deviceManager', 'getStreamProfileMap', 'query', input),\n setStreamProfileMap: (input?: unknown) => dispatchSystem('deviceManager', 'setStreamProfileMap', 'mutation', input),\n probeStreams: (input?: unknown) => dispatchSystem('deviceManager', 'probeStreams', 'mutation', input),\n getBindings: (input?: unknown) => dispatchSystem('deviceManager', 'getBindings', 'query', input),\n getAllBindings: (input?: unknown) => dispatchSystem('deviceManager', 'getAllBindings', 'query', input),\n setWrapperActive: (input?: unknown) => dispatchSystem('deviceManager', 'setWrapperActive', 'mutation', input),\n getDeviceSettingsAggregate: (input?: unknown) => dispatchSystem('deviceManager', 'getDeviceSettingsAggregate', 'query', input),\n getDeviceLiveInfoAggregate: (input?: unknown) => dispatchSystem('deviceManager', 'getDeviceLiveInfoAggregate', 'query', input),\n getDeviceAggregate: (input?: unknown) => dispatchSystem('deviceManager', 'getDeviceAggregate', 'query', input),\n updateDeviceField: (input?: unknown) => dispatchSystem('deviceManager', 'updateDeviceField', 'mutation', input),\n updateDeviceFieldsBatch: (input?: unknown) => dispatchSystem('deviceManager', 'updateDeviceFieldsBatch', 'mutation', input),\n testField: (input?: unknown) => dispatchSystem('deviceManager', 'testField', 'mutation', input),\n getDeviceStatusAggregate: (input?: unknown) => dispatchSystem('deviceManager', 'getDeviceStatusAggregate', 'query', input),\n }\n\n const deviceStateInterface = {\n getSnapshot: (input?: unknown) => dispatchSystem('deviceState', 'getSnapshot', 'query', input),\n getCapSlice: (input?: unknown) => dispatchSystem('deviceState', 'getCapSlice', 'query', input),\n setCapSlice: (input?: unknown) => dispatchSystem('deviceState', 'setCapSlice', 'mutation', input),\n }\n\n const networkQualityInterface = {\n getDeviceStats: (input?: unknown) => dispatchSystem('networkQuality', 'getDeviceStats', 'query', input),\n reportClientStats: (input?: unknown) => dispatchSystem('networkQuality', 'reportClientStats', 'mutation', input),\n }\n\n const pipelineExecutorInterface = {\n runPipeline: (input?: unknown) => dispatchSystem('pipelineExecutor', 'runPipeline', 'mutation', input),\n runPipelineBatch: (input?: unknown) => dispatchSystem('pipelineExecutor', 'runPipelineBatch', 'mutation', input),\n }\n\n const pipelineOrchestratorInterface = {\n assignPipeline: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'assignPipeline', 'mutation', input),\n unassignPipeline: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'unassignPipeline', 'mutation', input),\n getPipelineAssignment: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getPipelineAssignment', 'query', input),\n getCameraMetrics: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getCameraMetrics', 'query', input),\n assignDecoder: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'assignDecoder', 'mutation', input),\n unassignDecoder: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'unassignDecoder', 'mutation', input),\n assignAudio: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'assignAudio', 'mutation', input),\n unassignAudio: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'unassignAudio', 'mutation', input),\n getAudioAssignment: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getAudioAssignment', 'query', input),\n getAudioAssignments: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getAudioAssignments', 'query', input),\n getDecoderAssignment: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getDecoderAssignment', 'query', input),\n getCameraSettings: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getCameraSettings', 'query', input),\n setCameraStepToggle: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'setCameraStepToggle', 'mutation', input),\n getCameraStepOverrides: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getCameraStepOverrides', 'query', input),\n setCameraStepOverride: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'setCameraStepOverride', 'mutation', input),\n setCameraPipelineForAgent: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'setCameraPipelineForAgent', 'mutation', input),\n resolvePipeline: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'resolvePipeline', 'query', input),\n getDeviceSettingsContribution: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n const pipelineRunnerInterface = {\n detachCamera: (input?: unknown) => dispatchSystem('pipelineRunner', 'detachCamera', 'mutation', input),\n getCameraMetrics: (input?: unknown) => dispatchSystem('pipelineRunner', 'getCameraMetrics', 'query', input),\n }\n\n const recordingEngineInterface = {\n getPolicyStatus: (input?: unknown) => dispatchSystem('recordingEngine', 'getPolicyStatus', 'query', input),\n }\n\n const snapshotProviderInterface = {\n supportsDevice: (input?: unknown) => dispatchSystem('snapshotProvider', 'supportsDevice', 'query', input),\n getSnapshot: (input?: unknown) => dispatchSystem('snapshotProvider', 'getSnapshot', 'query', input),\n }\n\n const streamBrokerInterface = {\n publishCameraStream: (input?: unknown) => dispatchSystem('streamBroker', 'publishCameraStream', 'mutation', input),\n retractCameraStream: (input?: unknown) => dispatchSystem('streamBroker', 'retractCameraStream', 'mutation', input),\n assignProfile: (input?: unknown) => dispatchSystem('streamBroker', 'assignProfile', 'mutation', input),\n unassignProfile: (input?: unknown) => dispatchSystem('streamBroker', 'unassignProfile', 'mutation', input),\n restartProfile: (input?: unknown) => dispatchSystem('streamBroker', 'restartProfile', 'mutation', input),\n getDeviceSettingsContribution: (input?: unknown) => dispatchSystem('streamBroker', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatchSystem('streamBroker', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatchSystem('streamBroker', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n return {\n deviceId: binding.deviceId,\n binding,\n state: {\n audioMetrics: createSliceHandle<InferRuntimeState<typeof audioMetricsCapability>>(stateSource, binding.deviceId, 'audio-metrics'),\n battery: createSliceHandle<InferRuntimeState<typeof batteryCapability>>(stateSource, binding.deviceId, 'battery'),\n brightness: createSliceHandle<InferRuntimeState<typeof brightnessCapability>>(stateSource, binding.deviceId, 'brightness'),\n cameraStreams: createSliceHandle<InferRuntimeState<typeof cameraStreamsCapability>>(stateSource, binding.deviceId, 'camera-streams'),\n deviceDiscovery: createSliceHandle<InferRuntimeState<typeof deviceDiscoveryCapability>>(stateSource, binding.deviceId, 'device-discovery'),\n deviceStatus: createSliceHandle<InferRuntimeState<typeof deviceStatusCapability>>(stateSource, binding.deviceId, 'device-status'),\n doorbell: createSliceHandle<InferRuntimeState<typeof doorbellCapability>>(stateSource, binding.deviceId, 'doorbell'),\n featureProbe: createSliceHandle<InferRuntimeState<typeof featureProbeCapability>>(stateSource, binding.deviceId, 'feature-probe'),\n motion: createSliceHandle<InferRuntimeState<typeof motionCapability>>(stateSource, binding.deviceId, 'motion'),\n motionTrigger: createSliceHandle<InferRuntimeState<typeof motionTriggerCapability>>(stateSource, binding.deviceId, 'motion-trigger'),\n ptzAutotrack: createSliceHandle<InferRuntimeState<typeof ptzAutotrackCapability>>(stateSource, binding.deviceId, 'ptz-autotrack'),\n switch: createSliceHandle<InferRuntimeState<typeof switchCapability>>(stateSource, binding.deviceId, 'switch'),\n zoneAnalytics: createSliceHandle<InferRuntimeState<typeof zoneAnalyticsCapability>>(stateSource, binding.deviceId, 'zone-analytics'),\n zoneRules: createSliceHandle<InferRuntimeState<typeof zoneRulesCapability>>(stateSource, binding.deviceId, 'zone-rules'),\n zones: createSliceHandle<InferRuntimeState<typeof zonesCapability>>(stateSource, binding.deviceId, 'zones'),\n },\n accessories: accessoriesInterface,\n audioAnalysis: audioAnalysisInterface,\n audioMetrics: audioMetricsInterface,\n battery: batteryInterface,\n brightness: brightnessInterface,\n cameraCredentials: cameraCredentialsInterface,\n cameraStreams: cameraStreamsInterface,\n detectionPipeline: detectionPipelineInterface,\n deviceDiscovery: deviceDiscoveryInterface,\n deviceOps: deviceOpsInterface,\n deviceStatus: deviceStatusInterface,\n doorbell: doorbellInterface,\n events: eventsInterface,\n featureProbe: featureProbeInterface,\n intercom: intercomInterface,\n motion: motionInterface,\n motionDetection: motionDetectionInterface,\n motionTrigger: motionTriggerInterface,\n nativeObjectDetection: nativeObjectDetectionInterface,\n osd: osdInterface,\n pipelineAnalytics: pipelineAnalyticsInterface,\n ptz: ptzInterface,\n ptzAutotrack: ptzAutotrackInterface,\n reboot: rebootInterface,\n recording: recordingInterface,\n snapshot: snapshotInterface,\n switch: switchInterface,\n webrtcSession: webrtcSessionInterface,\n zoneAnalytics: zoneAnalyticsInterface,\n zoneRules: zoneRulesInterface,\n zones: zonesInterface,\n addonSettings: addonSettingsInterface,\n deviceManager: deviceManagerInterface,\n deviceState: deviceStateInterface,\n networkQuality: networkQualityInterface,\n pipelineExecutor: pipelineExecutorInterface,\n pipelineOrchestrator: pipelineOrchestratorInterface,\n pipelineRunner: pipelineRunnerInterface,\n recordingEngine: recordingEngineInterface,\n snapshotProvider: snapshotProviderInterface,\n streamBroker: streamBrokerInterface,\n }\n}\n","/**\n * Pure rules-driven zone evaluator. Shared between every consumer\n * that gates items (motion regions, detections, future audio events,\n * …) by `(zones, rules)` pairs. Lives in `@camstack/types/utils` so\n * addon-motion-wasm + addon-detection-pipeline share one canonical\n * implementation.\n *\n * Semantics — match the analytics zone-engine introduced in Phase 2a:\n * - any active include rule fires ⇒ whitelist mode (only items\n * satisfying at least one include rule pass)\n * - otherwise ⇒ blacklist mode (items in any exclude rule are\n * dropped, the rest pass)\n * - inactive rules (`enabled: false`) are skipped before the gate\n * - a rule's `classFilter` narrows it to the listed classes; an\n * item whose class isn't in the filter is invisible to that rule\n *\n * Test point: each item provides a normalised (0–1) center via the\n * `getCenter` accessor; rules fire when the center lies inside any\n * of the rule's referenced zone polygons (ray-cast point-in-polygon).\n * Bbox-area overlap would be marginally more intuitive on tiny\n * items but doubles the per-frame cost; the center test keeps the\n * hot path cheap and matches the operator's intuition for\n * \"where is the activity centered\".\n */\nimport type { Zone } from '../capabilities/zones.cap.js'\nimport type { ZoneRule } from '../capabilities/schemas/zone-rule.js'\n\nexport interface ZoneRuleEvalResult<T> {\n readonly passed: readonly T[]\n readonly excluded: readonly T[]\n}\n\n/**\n * Evaluate `rules` against `items`. Returns the items partitioned\n * into `passed` (kept by the gate) and `excluded` (dropped). When\n * `rules` is empty or `zones` is empty everything passes — the gate\n * is \"off\" without explicit configuration.\n *\n * `getCenter` returns the item's normalised (0–1) center point.\n * `getClassName` is optional — when omitted, every rule's\n * `classFilter` is treated as matching all classes (motion-stage\n * semantics). Provide it for object detections so per-class rules\n * narrow correctly.\n */\nexport function evaluateZoneRules<T>(\n items: readonly T[],\n zones: readonly Zone[],\n rules: readonly ZoneRule[],\n getCenter: (item: T) => { readonly x: number; readonly y: number },\n getClassName?: (item: T) => string | undefined,\n): ZoneRuleEvalResult<T> {\n if (rules.length === 0 || zones.length === 0) {\n return { passed: items, excluded: [] }\n }\n const activeRules = rules.filter(r => r.enabled !== false)\n const includeRules = activeRules.filter(r => r.mode === 'include')\n const excludeRules = activeRules.filter(r => r.mode === 'exclude')\n const whitelistMode = includeRules.length > 0\n const zonesById = new Map(zones.map(z => [z.id, z]))\n\n const passed: T[] = []\n const excluded: T[] = []\n\n for (const item of items) {\n const center = getCenter(item)\n const className = getClassName?.(item)\n const inInclude = includeRules.some(r => ruleApplies(r, center, className, zonesById))\n const inExclude = excludeRules.some(r => ruleApplies(r, center, className, zonesById))\n if (whitelistMode) {\n if (inInclude && !inExclude) passed.push(item)\n else excluded.push(item)\n } else {\n if (inExclude) excluded.push(item)\n else passed.push(item)\n }\n }\n return { passed, excluded }\n}\n\nfunction ruleApplies(\n rule: ZoneRule,\n center: { readonly x: number; readonly y: number },\n className: string | undefined,\n zonesById: ReadonlyMap<string, Zone>,\n): boolean {\n // Class narrowing — when className is undefined (motion stage),\n // we treat the filter as matching everything. When the item has a\n // class, missing it from the filter excludes the rule.\n if (rule.classFilter && rule.classFilter.length > 0) {\n if (className !== undefined && !rule.classFilter.includes(className)) return false\n }\n for (const zoneId of rule.zoneIds) {\n const zone = zonesById.get(zoneId)\n if (!zone) continue\n if (pointInPolygon(center, zone.polygon)) return true\n }\n return false\n}\n\n/**\n * Standard ray-casting point-in-polygon. Polygon vertices are\n * normalised (0–1); the test point is too — frame-resolution-\n * agnostic. Boundary points count as inside (treats `<` as `<=` on\n * the y-edge crossing).\n */\nfunction pointInPolygon(\n point: { readonly x: number; readonly y: number },\n polygon: ReadonlyArray<{ readonly x: number; readonly y: number }>,\n): boolean {\n if (polygon.length < 3) return false\n let inside = false\n const { x, y } = point\n for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {\n const a = polygon[i]!\n const b = polygon[j]!\n const intersect =\n ((a.y > y) !== (b.y > y)) &&\n (x < ((b.x - a.x) * (y - a.y)) / (b.y - a.y || Number.EPSILON) + a.x)\n if (intersect) inside = !inside\n }\n return inside\n}\n","// AUTO-GENERATED — do not edit manually.\n// Regenerate with: npx tsx scripts/generate-system-proxy.ts\n/* eslint-disable */\n\nimport type { InferProvider } from '../capabilities/capability-definition.js'\nimport type { AddonApi } from './addon-api.js'\nimport type { addonPagesCapability } from '../capabilities/addon-pages.cap.js'\nimport type { addonsCapability } from '../capabilities/addons.cap.js'\nimport type { addonSettingsCapability } from '../capabilities/addon-settings.cap.js'\nimport type { addonWidgetsCapability } from '../capabilities/addon-widgets.cap.js'\nimport type { alertsCapability } from '../capabilities/alerts.cap.js'\nimport type { audioAnalyzerCapability } from '../capabilities/audio-analyzer.cap.js'\nimport type { audioCodecCapability } from '../capabilities/audio-codec.cap.js'\nimport type { authenticationCapability } from '../capabilities/authentication.cap.js'\nimport type { backupCapability } from '../capabilities/backup.cap.js'\nimport type { decoderCapability } from '../capabilities/decoder.cap.js'\nimport type { deviceManagerCapability } from '../capabilities/device-manager.cap.js'\nimport type { deviceProviderCapability } from '../capabilities/device-provider.cap.js'\nimport type { deviceStateCapability } from '../capabilities/device-state.cap.js'\nimport type { integrationsCapability } from '../capabilities/integrations.cap.js'\nimport type { localNetworkCapability } from '../capabilities/local-network.cap.js'\nimport type { meshNetworkCapability } from '../capabilities/mesh-network.cap.js'\nimport type { meshOrchestratorCapability } from '../capabilities/mesh-orchestrator.cap.js'\nimport type { metricsProviderCapability } from '../capabilities/metrics-provider.cap.js'\nimport type { networkQualityCapability } from '../capabilities/network-quality.cap.js'\nimport type { nodesCapability } from '../capabilities/nodes.cap.js'\nimport type { notificationOutputCapability } from '../capabilities/notification-output.cap.js'\nimport type { pipelineExecutorCapability } from '../capabilities/pipeline-executor.cap.js'\nimport type { pipelineOrchestratorCapability } from '../capabilities/pipeline-orchestrator.cap.js'\nimport type { pipelineRunnerCapability } from '../capabilities/pipeline-runner.cap.js'\nimport type { platformProbeCapability } from '../capabilities/platform-probe.cap.js'\nimport type { recordingEngineCapability } from '../capabilities/recording-engine.cap.js'\nimport type { remoteAccessCapability } from '../capabilities/remote-access.cap.js'\nimport type { settingsStoreCapability } from '../capabilities/settings-store.cap.js'\nimport type { storageCapability } from '../capabilities/storage.cap.js'\nimport type { streamBrokerCapability } from '../capabilities/stream-broker.cap.js'\nimport type { systemCapability } from '../capabilities/system.cap.js'\nimport type { toastCapability } from '../capabilities/toast.cap.js'\nimport type { turnOrchestratorCapability } from '../capabilities/turn-orchestrator.cap.js'\nimport type { turnProviderCapability } from '../capabilities/turn-provider.cap.js'\nimport type { userManagementCapability } from '../capabilities/user-management.cap.js'\n\n/**\n * Cluster-wide facade over every `scope: 'system'` capability. Each entry\n * is a typed namespace exposing only the cap's non-device-bound methods —\n * device-scoped slices already surface on `DeviceProxy`.\n *\n * Consumed by the SDK's `System` class as the source of truth for\n * `system.<cap>.<method>(input)` calls.\n */\nexport interface SystemProxy {\n readonly addonPages: Pick<InferProvider<typeof addonPagesCapability>, 'listPages'>\n readonly addons: Pick<InferProvider<typeof addonsCapability>, 'list' | 'getLogs' | 'listPackages' | 'installPackage' | 'installFromWorkspace' | 'isWorkspaceAvailable' | 'listWorkspacePackages' | 'uninstallPackage' | 'reloadPackages' | 'searchAvailable' | 'listUpdates' | 'updatePackage' | 'rollbackPackage' | 'forceRefresh' | 'restartServer' | 'getVersions' | 'restartAddon' | 'retryLoad' | 'getAutoUpdateSettings' | 'setAutoUpdateSettings' | 'getAddonAutoUpdate' | 'setAddonAutoUpdate' | 'applyAutoUpdateToAll' | 'custom' | 'onAddonLogs'>\n readonly addonSettings: Pick<InferProvider<typeof addonSettingsCapability>, 'getGlobalSettings' | 'updateGlobalSettings'>\n readonly addonWidgets: Pick<InferProvider<typeof addonWidgetsCapability>, 'listWidgets'>\n readonly alerts: Pick<InferProvider<typeof alertsCapability>, 'emit' | 'update' | 'list' | 'getUnreadCount' | 'markRead' | 'markAllRead' | 'dismiss'>\n readonly audioAnalyzer: Pick<InferProvider<typeof audioAnalyzerCapability>, 'analyseChunk' | 'classify' | 'isReady' | 'dispose' | 'reprobeAudioEngine'>\n readonly audioCodec: Pick<InferProvider<typeof audioCodecCapability>, 'listSupportedCodecs' | 'canHandle' | 'createDecodeSession' | 'createEncodeSession' | 'closeSession' | 'pushEncodedFrame' | 'pullPcm' | 'pushPcm' | 'pullEncoded' | 'flushEncode' | 'listActiveSessions'>\n readonly authentication: Pick<InferProvider<typeof authenticationCapability>, 'listProviders' | 'setProviderEnabled'>\n readonly backup: Pick<InferProvider<typeof backupCapability>, 'listDestinations' | 'trigger' | 'list' | 'listLocations' | 'getEntries' | 'restore' | 'delete' | 'listArchives' | 'upsertDestinationPolicy' | 'previewSchedule'>\n readonly decoder: Pick<InferProvider<typeof decoderCapability>, 'supportsCodec' | 'getInfo' | 'createSession' | 'destroySession' | 'pushPacket' | 'openStream' | 'pullFrames' | 'updateConfig' | 'getStats' | 'listActiveSessions' | 'reprobeHwaccel'>\n readonly deviceManager: Pick<InferProvider<typeof deviceManagerCapability>, 'allocateDeviceId' | 'registerDevice' | 'removeDevice' | 'persistConfig' | 'listLocations' | 'addLocation' | 'removeLocation' | 'listPersistedByAddon' | 'listAll' | 'getChildren' | 'listWrappersForCap' | 'listBindableCapsForDeviceType' | 'discoverDevices' | 'adoptDevice' | 'getCreationSchema' | 'createDevice' | 'testCreationField'>\n readonly deviceProvider: Pick<InferProvider<typeof deviceProviderCapability>, 'start' | 'stop' | 'getStatus' | 'getDevices' | 'supportsDiscovery' | 'discoverDevices' | 'adoptDiscoveredDevice' | 'supportsManualCreation' | 'getChildCreationSchema' | 'createDevice' | 'testCreationField'>\n readonly deviceState: Pick<InferProvider<typeof deviceStateCapability>, 'getAllSnapshots'>\n readonly integrations: Pick<InferProvider<typeof integrationsCapability>, 'list' | 'get' | 'getByAddonId' | 'create' | 'update' | 'delete' | 'getSettings' | 'setSettings' | 'getAvailableTypes' | 'testConnection'>\n readonly localNetwork: Pick<InferProvider<typeof localNetworkCapability>, 'list' | 'getPreferred' | 'getConnectionEndpoints' | 'getAllowedAddresses' | 'setAllowedAddresses' | 'resetAllowlistToBestMatch'>\n readonly meshNetwork: Pick<InferProvider<typeof meshNetworkCapability>, 'getStatus' | 'join' | 'leave' | 'listPeers' | 'setPublicIngress' | 'setMeshIngress'>\n readonly meshOrchestrator: Pick<InferProvider<typeof meshOrchestratorCapability>, 'listProviders' | 'joinProvider' | 'leaveProvider'>\n readonly metricsProvider: Pick<InferProvider<typeof metricsProviderCapability>, 'collectSnapshot' | 'getCached' | 'getCurrent' | 'getDiskSpace' | 'getGpuInfo' | 'getCpuTemperature' | 'getProcessStats' | 'listAddonInstances' | 'getAddonStats' | 'listNodeProcesses' | 'killProcess'>\n readonly networkQuality: Pick<InferProvider<typeof networkQualityCapability>, 'getAllStats'>\n readonly nodes: Pick<InferProvider<typeof nodesCapability>, 'topology' | 'deployAddon' | 'undeployAddon' | 'restartAddon' | 'restartProcess' | 'restartNode' | 'shutdownNode' | 'renameNode' | 'clusterAddonStatus' | 'setProcessLogLevel' | 'executeQuery'>\n readonly notificationOutput: Pick<InferProvider<typeof notificationOutputCapability>, 'send' | 'sendTest'>\n readonly pipelineExecutor: Pick<InferProvider<typeof pipelineExecutorCapability>, 'getAvailableEngines' | 'getSelectedEngine' | 'getDefaultSteps' | 'reprobeEngine' | 'getVideoPipelineSteps' | 'setVideoPipelineSteps' | 'getSchema' | 'getGlobalSteps' | 'getGlobalPipelineConfig' | 'getOrchestratorConfigSchema' | 'listTemplates' | 'saveTemplate' | 'updateTemplate' | 'deleteTemplate' | 'getCapabilities' | 'getAddonModels' | 'downloadModel' | 'deleteModel' | 'detect' | 'cacheFrameInPool' | 'inferCached' | 'uncacheFrame' | 'getEffectiveTuning' | 'listLoadedEngines' | 'spinEngine' | 'killEngine' | 'listReferenceImages' | 'getReferenceImage' | 'getReferenceAudioFiles' | 'getReferenceAudio' | 'getAudioCapabilities' | 'runAudioTest' | 'getDetectionConfigSchema'>\n readonly pipelineOrchestrator: Pick<InferProvider<typeof pipelineOrchestratorCapability>, 'rebalance' | 'getPipelineAssignments' | 'getAgentLoad' | 'getGlobalMetrics' | 'getCapabilityBindings' | 'setCapabilityBinding' | 'getDecoderAssignments' | 'getAudioNodeLoad' | 'getAgentSettings' | 'listAgentSettings' | 'setAgentAddonDefaults' | 'removeAgentSettings' | 'listTemplates' | 'saveTemplate' | 'updateTemplate' | 'deleteTemplate'>\n readonly pipelineRunner: Pick<InferProvider<typeof pipelineRunnerCapability>, 'attachCamera' | 'reportMotion' | 'getLocalLoad' | 'getLocalMetrics' | 'getAllCameraMetrics' | 'getLocalCameras'>\n readonly platformProbe: Pick<InferProvider<typeof platformProbeCapability>, 'getCapabilities' | 'getHardware' | 'resolveInferenceConfig' | 'resolveHwAccel'>\n readonly recordingEngine: Pick<InferProvider<typeof recordingEngineCapability>, 'getStatus' | 'enable' | 'disable' | 'getConfig' | 'updateConfig' | 'getPlaylist' | 'getThumbnail' | 'getSegments' | 'getAvailability' | 'estimateStorage' | 'estimateGlobalStorage' | 'getStorageUsage' | 'setPolicy' | 'getPolicy' | 'getRetentionConfig' | 'updateRetentionConfig' | 'getMotionStats'>\n readonly remoteAccess: Pick<InferProvider<typeof remoteAccessCapability>, 'listProviders' | 'startProvider' | 'stopProvider'>\n readonly settingsStore: Pick<InferProvider<typeof settingsStoreCapability>, 'get' | 'set' | 'query' | 'insert' | 'update' | 'delete' | 'count' | 'isEmpty' | 'declareCollection'>\n readonly storage: Pick<InferProvider<typeof storageCapability>, 'resolve' | 'write' | 'read' | 'exists' | 'list' | 'delete' | 'getAvailableSpace' | 'beginUpload' | 'writeChunk' | 'finalizeUpload' | 'abortUpload' | 'beginDownload' | 'readChunk' | 'endDownload' | 'listLocations' | 'getDefaultLocation' | 'upsertLocation' | 'deleteLocation' | 'testLocation' | 'listProviders' | 'testConfig'>\n readonly streamBroker: Pick<InferProvider<typeof streamBrokerCapability>, 'listAllCameraStreams' | 'listAllProfileSlots' | 'getBrokerStats' | 'listClients' | 'killClient' | 'getStreamUrl' | 'getBroker' | 'setPreBufferDuration' | 'getPreBufferInfo' | 'getRtspPort' | 'getAllRtspEntries' | 'getRtspEntry' | 'regenerateRtspToken' | 'setRtspEnabled' | 'isRtspEnabled'>\n readonly system: Pick<InferProvider<typeof systemCapability>, 'info' | 'health' | 'featureFlags' | 'networkAddresses' | 'getRetentionConfig' | 'setRetentionConfig' | 'forceRetentionCleanup'>\n readonly toast: Pick<InferProvider<typeof toastCapability>, 'onToast'>\n readonly turnOrchestrator: Pick<InferProvider<typeof turnOrchestratorCapability>, 'listProviders' | 'getAllServers' | 'setProviderEnabled'>\n readonly turnProvider: Pick<InferProvider<typeof turnProviderCapability>, 'getTurnServers'>\n readonly userManagement: Pick<InferProvider<typeof userManagementCapability>, 'listUsers' | 'createUser' | 'updateUser' | 'deleteUser' | 'resetPassword' | 'validateCredentials' | 'listApiKeys' | 'createApiKey' | 'revokeApiKey' | 'validateApiKey' | 'createScopedToken' | 'revokeScopedToken' | 'validateScopedToken' | 'listScopedTokens'>\n}\n\n/**\n * Build a `SystemProxy` that dispatches every namespace method through\n * the existing system-cap tRPC procedures (`api.<capProp>.<method>.{query,mutate,subscribe}`).\n *\n * Pure function — no caching, no live state. The caller (typically\n * `System`) owns the `AddonApi` instance and disposes the proxy by\n * dropping the reference.\n */\nexport function createSystemProxy(api: AddonApi): SystemProxy {\n const typedApi = api as unknown as Record<string, Record<string, Record<string, (input?: unknown) => unknown>>>\n\n type Kind = 'query' | 'mutation' | 'subscription'\n\n /**\n * Invoke `api.<capProp>.<method>.{query|mutate|subscribe}(input)`.\n * Returns `any` so the caller-side per-method declaration assigns\n * cleanly into the strongly-typed `Pick<InferProvider<...>, ...>`\n * shape on the `SystemProxy` interface — the proxy's contract is\n * enforced by the interface, not the dispatch helper.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function dispatch(capProp: string, method: string, kind: Kind, input?: unknown, push?: unknown): any {\n const leaf = typedApi[capProp]?.[method]\n if (!leaf) throw new Error(`SystemProxy: api has no '${capProp}.${method}'`)\n const fn = leaf as unknown as {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n query: (i: unknown) => any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mutate: (i: unknown) => any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n subscribe: (i: unknown, push: unknown) => any\n }\n if (kind === 'mutation') return fn.mutate(input)\n if (kind === 'subscription') return fn.subscribe(input, push)\n return fn.query(input)\n }\n\n const addonPagesInterface = {\n listPages: (input?: unknown) => dispatch('addonPages', 'listPages', 'query', input),\n }\n\n const addonsInterface = {\n list: (input?: unknown) => dispatch('addons', 'list', 'query', input),\n getLogs: (input?: unknown) => dispatch('addons', 'getLogs', 'query', input),\n listPackages: (input?: unknown) => dispatch('addons', 'listPackages', 'query', input),\n installPackage: (input?: unknown) => dispatch('addons', 'installPackage', 'mutation', input),\n installFromWorkspace: (input?: unknown) => dispatch('addons', 'installFromWorkspace', 'mutation', input),\n isWorkspaceAvailable: (input?: unknown) => dispatch('addons', 'isWorkspaceAvailable', 'query', input),\n listWorkspacePackages: (input?: unknown) => dispatch('addons', 'listWorkspacePackages', 'query', input),\n uninstallPackage: (input?: unknown) => dispatch('addons', 'uninstallPackage', 'mutation', input),\n reloadPackages: (input?: unknown) => dispatch('addons', 'reloadPackages', 'mutation', input),\n searchAvailable: (input?: unknown) => dispatch('addons', 'searchAvailable', 'query', input),\n listUpdates: (input?: unknown) => dispatch('addons', 'listUpdates', 'query', input),\n updatePackage: (input?: unknown) => dispatch('addons', 'updatePackage', 'mutation', input),\n rollbackPackage: (input?: unknown) => dispatch('addons', 'rollbackPackage', 'mutation', input),\n forceRefresh: (input?: unknown) => dispatch('addons', 'forceRefresh', 'mutation', input),\n restartServer: (input?: unknown) => dispatch('addons', 'restartServer', 'mutation', input),\n getVersions: (input?: unknown) => dispatch('addons', 'getVersions', 'query', input),\n restartAddon: (input?: unknown) => dispatch('addons', 'restartAddon', 'mutation', input),\n retryLoad: (input?: unknown) => dispatch('addons', 'retryLoad', 'mutation', input),\n getAutoUpdateSettings: (input?: unknown) => dispatch('addons', 'getAutoUpdateSettings', 'query', input),\n setAutoUpdateSettings: (input?: unknown) => dispatch('addons', 'setAutoUpdateSettings', 'mutation', input),\n getAddonAutoUpdate: (input?: unknown) => dispatch('addons', 'getAddonAutoUpdate', 'query', input),\n setAddonAutoUpdate: (input?: unknown) => dispatch('addons', 'setAddonAutoUpdate', 'mutation', input),\n applyAutoUpdateToAll: (input?: unknown) => dispatch('addons', 'applyAutoUpdateToAll', 'mutation', input),\n custom: (input?: unknown) => dispatch('addons', 'custom', 'mutation', input),\n onAddonLogs: (input?: unknown, push?: unknown) => dispatch('addons', 'onAddonLogs', 'subscription', input, push),\n }\n\n const addonSettingsInterface = {\n getGlobalSettings: (input?: unknown) => dispatch('addonSettings', 'getGlobalSettings', 'query', input),\n updateGlobalSettings: (input?: unknown) => dispatch('addonSettings', 'updateGlobalSettings', 'mutation', input),\n }\n\n const addonWidgetsInterface = {\n listWidgets: (input?: unknown) => dispatch('addonWidgets', 'listWidgets', 'query', input),\n }\n\n const alertsInterface = {\n emit: (input?: unknown) => dispatch('alerts', 'emit', 'mutation', input),\n update: (input?: unknown) => dispatch('alerts', 'update', 'mutation', input),\n list: (input?: unknown) => dispatch('alerts', 'list', 'query', input),\n getUnreadCount: (input?: unknown) => dispatch('alerts', 'getUnreadCount', 'query', input),\n markRead: (input?: unknown) => dispatch('alerts', 'markRead', 'mutation', input),\n markAllRead: (input?: unknown) => dispatch('alerts', 'markAllRead', 'mutation', input),\n dismiss: (input?: unknown) => dispatch('alerts', 'dismiss', 'mutation', input),\n }\n\n const audioAnalyzerInterface = {\n analyseChunk: (input?: unknown) => dispatch('audioAnalyzer', 'analyseChunk', 'mutation', input),\n classify: (input?: unknown) => dispatch('audioAnalyzer', 'classify', 'query', input),\n isReady: (input?: unknown) => dispatch('audioAnalyzer', 'isReady', 'query', input),\n dispose: (input?: unknown) => dispatch('audioAnalyzer', 'dispose', 'mutation', input),\n reprobeAudioEngine: (input?: unknown) => dispatch('audioAnalyzer', 'reprobeAudioEngine', 'mutation', input),\n }\n\n const audioCodecInterface = {\n listSupportedCodecs: (input?: unknown) => dispatch('audioCodec', 'listSupportedCodecs', 'query', input),\n canHandle: (input?: unknown) => dispatch('audioCodec', 'canHandle', 'query', input),\n createDecodeSession: (input?: unknown) => dispatch('audioCodec', 'createDecodeSession', 'mutation', input),\n createEncodeSession: (input?: unknown) => dispatch('audioCodec', 'createEncodeSession', 'mutation', input),\n closeSession: (input?: unknown) => dispatch('audioCodec', 'closeSession', 'mutation', input),\n pushEncodedFrame: (input?: unknown) => dispatch('audioCodec', 'pushEncodedFrame', 'mutation', input),\n pullPcm: (input?: unknown) => dispatch('audioCodec', 'pullPcm', 'query', input),\n pushPcm: (input?: unknown) => dispatch('audioCodec', 'pushPcm', 'mutation', input),\n pullEncoded: (input?: unknown) => dispatch('audioCodec', 'pullEncoded', 'query', input),\n flushEncode: (input?: unknown) => dispatch('audioCodec', 'flushEncode', 'mutation', input),\n listActiveSessions: (input?: unknown) => dispatch('audioCodec', 'listActiveSessions', 'query', input),\n }\n\n const authenticationInterface = {\n listProviders: (input?: unknown) => dispatch('authentication', 'listProviders', 'query', input),\n setProviderEnabled: (input?: unknown) => dispatch('authentication', 'setProviderEnabled', 'mutation', input),\n }\n\n const backupInterface = {\n listDestinations: (input?: unknown) => dispatch('backup', 'listDestinations', 'query', input),\n trigger: (input?: unknown) => dispatch('backup', 'trigger', 'mutation', input),\n list: (input?: unknown) => dispatch('backup', 'list', 'query', input),\n listLocations: (input?: unknown) => dispatch('backup', 'listLocations', 'query', input),\n getEntries: (input?: unknown) => dispatch('backup', 'getEntries', 'query', input),\n restore: (input?: unknown) => dispatch('backup', 'restore', 'mutation', input),\n delete: (input?: unknown) => dispatch('backup', 'delete', 'mutation', input),\n listArchives: (input?: unknown) => dispatch('backup', 'listArchives', 'query', input),\n upsertDestinationPolicy: (input?: unknown) => dispatch('backup', 'upsertDestinationPolicy', 'mutation', input),\n previewSchedule: (input?: unknown) => dispatch('backup', 'previewSchedule', 'query', input),\n }\n\n const decoderInterface = {\n supportsCodec: (input?: unknown) => dispatch('decoder', 'supportsCodec', 'query', input),\n getInfo: (input?: unknown) => dispatch('decoder', 'getInfo', 'query', input),\n createSession: (input?: unknown) => dispatch('decoder', 'createSession', 'query', input),\n destroySession: (input?: unknown) => dispatch('decoder', 'destroySession', 'query', input),\n pushPacket: (input?: unknown) => dispatch('decoder', 'pushPacket', 'query', input),\n openStream: (input?: unknown) => dispatch('decoder', 'openStream', 'query', input),\n pullFrames: (input?: unknown) => dispatch('decoder', 'pullFrames', 'query', input),\n updateConfig: (input?: unknown) => dispatch('decoder', 'updateConfig', 'query', input),\n getStats: (input?: unknown) => dispatch('decoder', 'getStats', 'query', input),\n listActiveSessions: (input?: unknown) => dispatch('decoder', 'listActiveSessions', 'query', input),\n reprobeHwaccel: (input?: unknown) => dispatch('decoder', 'reprobeHwaccel', 'mutation', input),\n }\n\n const deviceManagerInterface = {\n allocateDeviceId: (input?: unknown) => dispatch('deviceManager', 'allocateDeviceId', 'mutation', input),\n registerDevice: (input?: unknown) => dispatch('deviceManager', 'registerDevice', 'mutation', input),\n removeDevice: (input?: unknown) => dispatch('deviceManager', 'removeDevice', 'mutation', input),\n persistConfig: (input?: unknown) => dispatch('deviceManager', 'persistConfig', 'mutation', input),\n listLocations: (input?: unknown) => dispatch('deviceManager', 'listLocations', 'query', input),\n addLocation: (input?: unknown) => dispatch('deviceManager', 'addLocation', 'mutation', input),\n removeLocation: (input?: unknown) => dispatch('deviceManager', 'removeLocation', 'mutation', input),\n listPersistedByAddon: (input?: unknown) => dispatch('deviceManager', 'listPersistedByAddon', 'query', input),\n listAll: (input?: unknown) => dispatch('deviceManager', 'listAll', 'query', input),\n getChildren: (input?: unknown) => dispatch('deviceManager', 'getChildren', 'query', input),\n listWrappersForCap: (input?: unknown) => dispatch('deviceManager', 'listWrappersForCap', 'query', input),\n listBindableCapsForDeviceType: (input?: unknown) => dispatch('deviceManager', 'listBindableCapsForDeviceType', 'query', input),\n discoverDevices: (input?: unknown) => dispatch('deviceManager', 'discoverDevices', 'mutation', input),\n adoptDevice: (input?: unknown) => dispatch('deviceManager', 'adoptDevice', 'mutation', input),\n getCreationSchema: (input?: unknown) => dispatch('deviceManager', 'getCreationSchema', 'query', input),\n createDevice: (input?: unknown) => dispatch('deviceManager', 'createDevice', 'mutation', input),\n testCreationField: (input?: unknown) => dispatch('deviceManager', 'testCreationField', 'mutation', input),\n }\n\n const deviceProviderInterface = {\n start: (input?: unknown) => dispatch('deviceProvider', 'start', 'mutation', input),\n stop: (input?: unknown) => dispatch('deviceProvider', 'stop', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('deviceProvider', 'getStatus', 'query', input),\n getDevices: (input?: unknown) => dispatch('deviceProvider', 'getDevices', 'query', input),\n supportsDiscovery: (input?: unknown) => dispatch('deviceProvider', 'supportsDiscovery', 'query', input),\n discoverDevices: (input?: unknown) => dispatch('deviceProvider', 'discoverDevices', 'mutation', input),\n adoptDiscoveredDevice: (input?: unknown) => dispatch('deviceProvider', 'adoptDiscoveredDevice', 'mutation', input),\n supportsManualCreation: (input?: unknown) => dispatch('deviceProvider', 'supportsManualCreation', 'query', input),\n getChildCreationSchema: (input?: unknown) => dispatch('deviceProvider', 'getChildCreationSchema', 'query', input),\n createDevice: (input?: unknown) => dispatch('deviceProvider', 'createDevice', 'mutation', input),\n testCreationField: (input?: unknown) => dispatch('deviceProvider', 'testCreationField', 'mutation', input),\n }\n\n const deviceStateInterface = {\n getAllSnapshots: (input?: unknown) => dispatch('deviceState', 'getAllSnapshots', 'query', input),\n }\n\n const integrationsInterface = {\n list: (input?: unknown) => dispatch('integrations', 'list', 'query', input),\n get: (input?: unknown) => dispatch('integrations', 'get', 'query', input),\n getByAddonId: (input?: unknown) => dispatch('integrations', 'getByAddonId', 'query', input),\n create: (input?: unknown) => dispatch('integrations', 'create', 'mutation', input),\n update: (input?: unknown) => dispatch('integrations', 'update', 'mutation', input),\n delete: (input?: unknown) => dispatch('integrations', 'delete', 'mutation', input),\n getSettings: (input?: unknown) => dispatch('integrations', 'getSettings', 'query', input),\n setSettings: (input?: unknown) => dispatch('integrations', 'setSettings', 'mutation', input),\n getAvailableTypes: (input?: unknown) => dispatch('integrations', 'getAvailableTypes', 'query', input),\n testConnection: (input?: unknown) => dispatch('integrations', 'testConnection', 'mutation', input),\n }\n\n const localNetworkInterface = {\n list: (input?: unknown) => dispatch('localNetwork', 'list', 'query', input),\n getPreferred: (input?: unknown) => dispatch('localNetwork', 'getPreferred', 'query', input),\n getConnectionEndpoints: (input?: unknown) => dispatch('localNetwork', 'getConnectionEndpoints', 'query', input),\n getAllowedAddresses: (input?: unknown) => dispatch('localNetwork', 'getAllowedAddresses', 'query', input),\n setAllowedAddresses: (input?: unknown) => dispatch('localNetwork', 'setAllowedAddresses', 'mutation', input),\n resetAllowlistToBestMatch: (input?: unknown) => dispatch('localNetwork', 'resetAllowlistToBestMatch', 'mutation', input),\n }\n\n const meshNetworkInterface = {\n getStatus: (input?: unknown) => dispatch('meshNetwork', 'getStatus', 'query', input),\n join: (input?: unknown) => dispatch('meshNetwork', 'join', 'mutation', input),\n leave: (input?: unknown) => dispatch('meshNetwork', 'leave', 'mutation', input),\n listPeers: (input?: unknown) => dispatch('meshNetwork', 'listPeers', 'query', input),\n setPublicIngress: (input?: unknown) => dispatch('meshNetwork', 'setPublicIngress', 'mutation', input),\n setMeshIngress: (input?: unknown) => dispatch('meshNetwork', 'setMeshIngress', 'mutation', input),\n }\n\n const meshOrchestratorInterface = {\n listProviders: (input?: unknown) => dispatch('meshOrchestrator', 'listProviders', 'query', input),\n joinProvider: (input?: unknown) => dispatch('meshOrchestrator', 'joinProvider', 'mutation', input),\n leaveProvider: (input?: unknown) => dispatch('meshOrchestrator', 'leaveProvider', 'mutation', input),\n }\n\n const metricsProviderInterface = {\n collectSnapshot: (input?: unknown) => dispatch('metricsProvider', 'collectSnapshot', 'query', input),\n getCached: (input?: unknown) => dispatch('metricsProvider', 'getCached', 'query', input),\n getCurrent: (input?: unknown) => dispatch('metricsProvider', 'getCurrent', 'query', input),\n getDiskSpace: (input?: unknown) => dispatch('metricsProvider', 'getDiskSpace', 'query', input),\n getGpuInfo: (input?: unknown) => dispatch('metricsProvider', 'getGpuInfo', 'query', input),\n getCpuTemperature: (input?: unknown) => dispatch('metricsProvider', 'getCpuTemperature', 'query', input),\n getProcessStats: (input?: unknown) => dispatch('metricsProvider', 'getProcessStats', 'query', input),\n listAddonInstances: (input?: unknown) => dispatch('metricsProvider', 'listAddonInstances', 'query', input),\n getAddonStats: (input?: unknown) => dispatch('metricsProvider', 'getAddonStats', 'query', input),\n listNodeProcesses: (input?: unknown) => dispatch('metricsProvider', 'listNodeProcesses', 'query', input),\n killProcess: (input?: unknown) => dispatch('metricsProvider', 'killProcess', 'mutation', input),\n }\n\n const networkQualityInterface = {\n getAllStats: (input?: unknown) => dispatch('networkQuality', 'getAllStats', 'query', input),\n }\n\n const nodesInterface = {\n topology: (input?: unknown) => dispatch('nodes', 'topology', 'query', input),\n deployAddon: (input?: unknown) => dispatch('nodes', 'deployAddon', 'mutation', input),\n undeployAddon: (input?: unknown) => dispatch('nodes', 'undeployAddon', 'mutation', input),\n restartAddon: (input?: unknown) => dispatch('nodes', 'restartAddon', 'mutation', input),\n restartProcess: (input?: unknown) => dispatch('nodes', 'restartProcess', 'mutation', input),\n restartNode: (input?: unknown) => dispatch('nodes', 'restartNode', 'mutation', input),\n shutdownNode: (input?: unknown) => dispatch('nodes', 'shutdownNode', 'mutation', input),\n renameNode: (input?: unknown) => dispatch('nodes', 'renameNode', 'mutation', input),\n clusterAddonStatus: (input?: unknown) => dispatch('nodes', 'clusterAddonStatus', 'query', input),\n setProcessLogLevel: (input?: unknown) => dispatch('nodes', 'setProcessLogLevel', 'mutation', input),\n executeQuery: (input?: unknown) => dispatch('nodes', 'executeQuery', 'mutation', input),\n }\n\n const notificationOutputInterface = {\n send: (input?: unknown) => dispatch('notificationOutput', 'send', 'mutation', input),\n sendTest: (input?: unknown) => dispatch('notificationOutput', 'sendTest', 'mutation', input),\n }\n\n const pipelineExecutorInterface = {\n getAvailableEngines: (input?: unknown) => dispatch('pipelineExecutor', 'getAvailableEngines', 'query', input),\n getSelectedEngine: (input?: unknown) => dispatch('pipelineExecutor', 'getSelectedEngine', 'query', input),\n getDefaultSteps: (input?: unknown) => dispatch('pipelineExecutor', 'getDefaultSteps', 'query', input),\n reprobeEngine: (input?: unknown) => dispatch('pipelineExecutor', 'reprobeEngine', 'mutation', input),\n getVideoPipelineSteps: (input?: unknown) => dispatch('pipelineExecutor', 'getVideoPipelineSteps', 'query', input),\n setVideoPipelineSteps: (input?: unknown) => dispatch('pipelineExecutor', 'setVideoPipelineSteps', 'mutation', input),\n getSchema: (input?: unknown) => dispatch('pipelineExecutor', 'getSchema', 'query', input),\n getGlobalSteps: (input?: unknown) => dispatch('pipelineExecutor', 'getGlobalSteps', 'query', input),\n getGlobalPipelineConfig: (input?: unknown) => dispatch('pipelineExecutor', 'getGlobalPipelineConfig', 'query', input),\n getOrchestratorConfigSchema: (input?: unknown) => dispatch('pipelineExecutor', 'getOrchestratorConfigSchema', 'query', input),\n listTemplates: (input?: unknown) => dispatch('pipelineExecutor', 'listTemplates', 'query', input),\n saveTemplate: (input?: unknown) => dispatch('pipelineExecutor', 'saveTemplate', 'mutation', input),\n updateTemplate: (input?: unknown) => dispatch('pipelineExecutor', 'updateTemplate', 'mutation', input),\n deleteTemplate: (input?: unknown) => dispatch('pipelineExecutor', 'deleteTemplate', 'mutation', input),\n getCapabilities: (input?: unknown) => dispatch('pipelineExecutor', 'getCapabilities', 'query', input),\n getAddonModels: (input?: unknown) => dispatch('pipelineExecutor', 'getAddonModels', 'query', input),\n downloadModel: (input?: unknown) => dispatch('pipelineExecutor', 'downloadModel', 'mutation', input),\n deleteModel: (input?: unknown) => dispatch('pipelineExecutor', 'deleteModel', 'mutation', input),\n detect: (input?: unknown) => dispatch('pipelineExecutor', 'detect', 'query', input),\n cacheFrameInPool: (input?: unknown) => dispatch('pipelineExecutor', 'cacheFrameInPool', 'mutation', input),\n inferCached: (input?: unknown) => dispatch('pipelineExecutor', 'inferCached', 'mutation', input),\n uncacheFrame: (input?: unknown) => dispatch('pipelineExecutor', 'uncacheFrame', 'mutation', input),\n getEffectiveTuning: (input?: unknown) => dispatch('pipelineExecutor', 'getEffectiveTuning', 'query', input),\n listLoadedEngines: (input?: unknown) => dispatch('pipelineExecutor', 'listLoadedEngines', 'query', input),\n spinEngine: (input?: unknown) => dispatch('pipelineExecutor', 'spinEngine', 'mutation', input),\n killEngine: (input?: unknown) => dispatch('pipelineExecutor', 'killEngine', 'mutation', input),\n listReferenceImages: (input?: unknown) => dispatch('pipelineExecutor', 'listReferenceImages', 'query', input),\n getReferenceImage: (input?: unknown) => dispatch('pipelineExecutor', 'getReferenceImage', 'query', input),\n getReferenceAudioFiles: (input?: unknown) => dispatch('pipelineExecutor', 'getReferenceAudioFiles', 'query', input),\n getReferenceAudio: (input?: unknown) => dispatch('pipelineExecutor', 'getReferenceAudio', 'query', input),\n getAudioCapabilities: (input?: unknown) => dispatch('pipelineExecutor', 'getAudioCapabilities', 'query', input),\n runAudioTest: (input?: unknown) => dispatch('pipelineExecutor', 'runAudioTest', 'mutation', input),\n getDetectionConfigSchema: (input?: unknown) => dispatch('pipelineExecutor', 'getDetectionConfigSchema', 'query', input),\n }\n\n const pipelineOrchestratorInterface = {\n rebalance: (input?: unknown) => dispatch('pipelineOrchestrator', 'rebalance', 'mutation', input),\n getPipelineAssignments: (input?: unknown) => dispatch('pipelineOrchestrator', 'getPipelineAssignments', 'query', input),\n getAgentLoad: (input?: unknown) => dispatch('pipelineOrchestrator', 'getAgentLoad', 'query', input),\n getGlobalMetrics: (input?: unknown) => dispatch('pipelineOrchestrator', 'getGlobalMetrics', 'query', input),\n getCapabilityBindings: (input?: unknown) => dispatch('pipelineOrchestrator', 'getCapabilityBindings', 'query', input),\n setCapabilityBinding: (input?: unknown) => dispatch('pipelineOrchestrator', 'setCapabilityBinding', 'mutation', input),\n getDecoderAssignments: (input?: unknown) => dispatch('pipelineOrchestrator', 'getDecoderAssignments', 'query', input),\n getAudioNodeLoad: (input?: unknown) => dispatch('pipelineOrchestrator', 'getAudioNodeLoad', 'query', input),\n getAgentSettings: (input?: unknown) => dispatch('pipelineOrchestrator', 'getAgentSettings', 'query', input),\n listAgentSettings: (input?: unknown) => dispatch('pipelineOrchestrator', 'listAgentSettings', 'query', input),\n setAgentAddonDefaults: (input?: unknown) => dispatch('pipelineOrchestrator', 'setAgentAddonDefaults', 'mutation', input),\n removeAgentSettings: (input?: unknown) => dispatch('pipelineOrchestrator', 'removeAgentSettings', 'mutation', input),\n listTemplates: (input?: unknown) => dispatch('pipelineOrchestrator', 'listTemplates', 'query', input),\n saveTemplate: (input?: unknown) => dispatch('pipelineOrchestrator', 'saveTemplate', 'mutation', input),\n updateTemplate: (input?: unknown) => dispatch('pipelineOrchestrator', 'updateTemplate', 'mutation', input),\n deleteTemplate: (input?: unknown) => dispatch('pipelineOrchestrator', 'deleteTemplate', 'mutation', input),\n }\n\n const pipelineRunnerInterface = {\n attachCamera: (input?: unknown) => dispatch('pipelineRunner', 'attachCamera', 'mutation', input),\n reportMotion: (input?: unknown) => dispatch('pipelineRunner', 'reportMotion', 'mutation', input),\n getLocalLoad: (input?: unknown) => dispatch('pipelineRunner', 'getLocalLoad', 'query', input),\n getLocalMetrics: (input?: unknown) => dispatch('pipelineRunner', 'getLocalMetrics', 'query', input),\n getAllCameraMetrics: (input?: unknown) => dispatch('pipelineRunner', 'getAllCameraMetrics', 'query', input),\n getLocalCameras: (input?: unknown) => dispatch('pipelineRunner', 'getLocalCameras', 'query', input),\n }\n\n const platformProbeInterface = {\n getCapabilities: (input?: unknown) => dispatch('platformProbe', 'getCapabilities', 'query', input),\n getHardware: (input?: unknown) => dispatch('platformProbe', 'getHardware', 'query', input),\n resolveInferenceConfig: (input?: unknown) => dispatch('platformProbe', 'resolveInferenceConfig', 'query', input),\n resolveHwAccel: (input?: unknown) => dispatch('platformProbe', 'resolveHwAccel', 'query', input),\n }\n\n const recordingEngineInterface = {\n getStatus: (input?: unknown) => dispatch('recordingEngine', 'getStatus', 'query', input),\n enable: (input?: unknown) => dispatch('recordingEngine', 'enable', 'mutation', input),\n disable: (input?: unknown) => dispatch('recordingEngine', 'disable', 'mutation', input),\n getConfig: (input?: unknown) => dispatch('recordingEngine', 'getConfig', 'query', input),\n updateConfig: (input?: unknown) => dispatch('recordingEngine', 'updateConfig', 'mutation', input),\n getPlaylist: (input?: unknown) => dispatch('recordingEngine', 'getPlaylist', 'query', input),\n getThumbnail: (input?: unknown) => dispatch('recordingEngine', 'getThumbnail', 'query', input),\n getSegments: (input?: unknown) => dispatch('recordingEngine', 'getSegments', 'query', input),\n getAvailability: (input?: unknown) => dispatch('recordingEngine', 'getAvailability', 'query', input),\n estimateStorage: (input?: unknown) => dispatch('recordingEngine', 'estimateStorage', 'query', input),\n estimateGlobalStorage: (input?: unknown) => dispatch('recordingEngine', 'estimateGlobalStorage', 'query', input),\n getStorageUsage: (input?: unknown) => dispatch('recordingEngine', 'getStorageUsage', 'query', input),\n setPolicy: (input?: unknown) => dispatch('recordingEngine', 'setPolicy', 'mutation', input),\n getPolicy: (input?: unknown) => dispatch('recordingEngine', 'getPolicy', 'query', input),\n getRetentionConfig: (input?: unknown) => dispatch('recordingEngine', 'getRetentionConfig', 'query', input),\n updateRetentionConfig: (input?: unknown) => dispatch('recordingEngine', 'updateRetentionConfig', 'mutation', input),\n getMotionStats: (input?: unknown) => dispatch('recordingEngine', 'getMotionStats', 'query', input),\n }\n\n const remoteAccessInterface = {\n listProviders: (input?: unknown) => dispatch('remoteAccess', 'listProviders', 'query', input),\n startProvider: (input?: unknown) => dispatch('remoteAccess', 'startProvider', 'mutation', input),\n stopProvider: (input?: unknown) => dispatch('remoteAccess', 'stopProvider', 'mutation', input),\n }\n\n const settingsStoreInterface = {\n get: (input?: unknown) => dispatch('settingsStore', 'get', 'query', input),\n set: (input?: unknown) => dispatch('settingsStore', 'set', 'mutation', input),\n query: (input?: unknown) => dispatch('settingsStore', 'query', 'query', input),\n insert: (input?: unknown) => dispatch('settingsStore', 'insert', 'mutation', input),\n update: (input?: unknown) => dispatch('settingsStore', 'update', 'mutation', input),\n delete: (input?: unknown) => dispatch('settingsStore', 'delete', 'mutation', input),\n count: (input?: unknown) => dispatch('settingsStore', 'count', 'query', input),\n isEmpty: (input?: unknown) => dispatch('settingsStore', 'isEmpty', 'query', input),\n declareCollection: (input?: unknown) => dispatch('settingsStore', 'declareCollection', 'mutation', input),\n }\n\n const storageInterface = {\n resolve: (input?: unknown) => dispatch('storage', 'resolve', 'query', input),\n write: (input?: unknown) => dispatch('storage', 'write', 'mutation', input),\n read: (input?: unknown) => dispatch('storage', 'read', 'query', input),\n exists: (input?: unknown) => dispatch('storage', 'exists', 'query', input),\n list: (input?: unknown) => dispatch('storage', 'list', 'query', input),\n delete: (input?: unknown) => dispatch('storage', 'delete', 'mutation', input),\n getAvailableSpace: (input?: unknown) => dispatch('storage', 'getAvailableSpace', 'query', input),\n beginUpload: (input?: unknown) => dispatch('storage', 'beginUpload', 'mutation', input),\n writeChunk: (input?: unknown) => dispatch('storage', 'writeChunk', 'mutation', input),\n finalizeUpload: (input?: unknown) => dispatch('storage', 'finalizeUpload', 'mutation', input),\n abortUpload: (input?: unknown) => dispatch('storage', 'abortUpload', 'mutation', input),\n beginDownload: (input?: unknown) => dispatch('storage', 'beginDownload', 'mutation', input),\n readChunk: (input?: unknown) => dispatch('storage', 'readChunk', 'query', input),\n endDownload: (input?: unknown) => dispatch('storage', 'endDownload', 'mutation', input),\n listLocations: (input?: unknown) => dispatch('storage', 'listLocations', 'query', input),\n getDefaultLocation: (input?: unknown) => dispatch('storage', 'getDefaultLocation', 'query', input),\n upsertLocation: (input?: unknown) => dispatch('storage', 'upsertLocation', 'mutation', input),\n deleteLocation: (input?: unknown) => dispatch('storage', 'deleteLocation', 'mutation', input),\n testLocation: (input?: unknown) => dispatch('storage', 'testLocation', 'query', input),\n listProviders: (input?: unknown) => dispatch('storage', 'listProviders', 'query', input),\n testConfig: (input?: unknown) => dispatch('storage', 'testConfig', 'query', input),\n }\n\n const streamBrokerInterface = {\n listAllCameraStreams: (input?: unknown) => dispatch('streamBroker', 'listAllCameraStreams', 'query', input),\n listAllProfileSlots: (input?: unknown) => dispatch('streamBroker', 'listAllProfileSlots', 'query', input),\n getBrokerStats: (input?: unknown) => dispatch('streamBroker', 'getBrokerStats', 'query', input),\n listClients: (input?: unknown) => dispatch('streamBroker', 'listClients', 'query', input),\n killClient: (input?: unknown) => dispatch('streamBroker', 'killClient', 'mutation', input),\n getStreamUrl: (input?: unknown) => dispatch('streamBroker', 'getStreamUrl', 'query', input),\n getBroker: (input?: unknown) => dispatch('streamBroker', 'getBroker', 'query', input),\n setPreBufferDuration: (input?: unknown) => dispatch('streamBroker', 'setPreBufferDuration', 'mutation', input),\n getPreBufferInfo: (input?: unknown) => dispatch('streamBroker', 'getPreBufferInfo', 'query', input),\n getRtspPort: (input?: unknown) => dispatch('streamBroker', 'getRtspPort', 'query', input),\n getAllRtspEntries: (input?: unknown) => dispatch('streamBroker', 'getAllRtspEntries', 'query', input),\n getRtspEntry: (input?: unknown) => dispatch('streamBroker', 'getRtspEntry', 'query', input),\n regenerateRtspToken: (input?: unknown) => dispatch('streamBroker', 'regenerateRtspToken', 'mutation', input),\n setRtspEnabled: (input?: unknown) => dispatch('streamBroker', 'setRtspEnabled', 'mutation', input),\n isRtspEnabled: (input?: unknown) => dispatch('streamBroker', 'isRtspEnabled', 'query', input),\n }\n\n const systemInterface = {\n info: (input?: unknown) => dispatch('system', 'info', 'query', input),\n health: (input?: unknown) => dispatch('system', 'health', 'query', input),\n featureFlags: (input?: unknown) => dispatch('system', 'featureFlags', 'query', input),\n networkAddresses: (input?: unknown) => dispatch('system', 'networkAddresses', 'query', input),\n getRetentionConfig: (input?: unknown) => dispatch('system', 'getRetentionConfig', 'query', input),\n setRetentionConfig: (input?: unknown) => dispatch('system', 'setRetentionConfig', 'mutation', input),\n forceRetentionCleanup: (input?: unknown) => dispatch('system', 'forceRetentionCleanup', 'mutation', input),\n }\n\n const toastInterface = {\n onToast: (input?: unknown, push?: unknown) => dispatch('toast', 'onToast', 'subscription', input, push),\n }\n\n const turnOrchestratorInterface = {\n listProviders: (input?: unknown) => dispatch('turnOrchestrator', 'listProviders', 'query', input),\n getAllServers: (input?: unknown) => dispatch('turnOrchestrator', 'getAllServers', 'query', input),\n setProviderEnabled: (input?: unknown) => dispatch('turnOrchestrator', 'setProviderEnabled', 'mutation', input),\n }\n\n const turnProviderInterface = {\n getTurnServers: (input?: unknown) => dispatch('turnProvider', 'getTurnServers', 'query', input),\n }\n\n const userManagementInterface = {\n listUsers: (input?: unknown) => dispatch('userManagement', 'listUsers', 'query', input),\n createUser: (input?: unknown) => dispatch('userManagement', 'createUser', 'mutation', input),\n updateUser: (input?: unknown) => dispatch('userManagement', 'updateUser', 'mutation', input),\n deleteUser: (input?: unknown) => dispatch('userManagement', 'deleteUser', 'mutation', input),\n resetPassword: (input?: unknown) => dispatch('userManagement', 'resetPassword', 'mutation', input),\n validateCredentials: (input?: unknown) => dispatch('userManagement', 'validateCredentials', 'mutation', input),\n listApiKeys: (input?: unknown) => dispatch('userManagement', 'listApiKeys', 'query', input),\n createApiKey: (input?: unknown) => dispatch('userManagement', 'createApiKey', 'mutation', input),\n revokeApiKey: (input?: unknown) => dispatch('userManagement', 'revokeApiKey', 'mutation', input),\n validateApiKey: (input?: unknown) => dispatch('userManagement', 'validateApiKey', 'mutation', input),\n createScopedToken: (input?: unknown) => dispatch('userManagement', 'createScopedToken', 'mutation', input),\n revokeScopedToken: (input?: unknown) => dispatch('userManagement', 'revokeScopedToken', 'mutation', input),\n validateScopedToken: (input?: unknown) => dispatch('userManagement', 'validateScopedToken', 'query', input),\n listScopedTokens: (input?: unknown) => dispatch('userManagement', 'listScopedTokens', 'query', input),\n }\n\n return {\n addonPages: addonPagesInterface,\n addons: addonsInterface,\n addonSettings: addonSettingsInterface,\n addonWidgets: addonWidgetsInterface,\n alerts: alertsInterface,\n audioAnalyzer: audioAnalyzerInterface,\n audioCodec: audioCodecInterface,\n authentication: authenticationInterface,\n backup: backupInterface,\n decoder: decoderInterface,\n deviceManager: deviceManagerInterface,\n deviceProvider: deviceProviderInterface,\n deviceState: deviceStateInterface,\n integrations: integrationsInterface,\n localNetwork: localNetworkInterface,\n meshNetwork: meshNetworkInterface,\n meshOrchestrator: meshOrchestratorInterface,\n metricsProvider: metricsProviderInterface,\n networkQuality: networkQualityInterface,\n nodes: nodesInterface,\n notificationOutput: notificationOutputInterface,\n pipelineExecutor: pipelineExecutorInterface,\n pipelineOrchestrator: pipelineOrchestratorInterface,\n pipelineRunner: pipelineRunnerInterface,\n platformProbe: platformProbeInterface,\n recordingEngine: recordingEngineInterface,\n remoteAccess: remoteAccessInterface,\n settingsStore: settingsStoreInterface,\n storage: storageInterface,\n streamBroker: streamBrokerInterface,\n system: systemInterface,\n toast: toastInterface,\n turnOrchestrator: turnOrchestratorInterface,\n turnProvider: turnProviderInterface,\n userManagement: userManagementInterface,\n }\n}\n","/**\n * SystemMirror — system-wide reactive view of every device, like\n * Scrypted's `systemManager.getDeviceById`. One warm-boot fetches the\n * full bindings + runtime-state snapshot + device metadata; live\n * `device.state-changed`, `capability.binding-changed`,\n * `device.registered`, `device.unregistered`, and `device.updated`\n * events keep the in-memory mirrors fresh. Subsequent\n * `getDeviceById(id)` calls are sync.\n *\n * Renamed from `SystemManager` (and its file `system-manager.ts`) when\n * the SDK introduced a top-level `System` facade. The cluster-wide\n * mirror is one of `System`'s collaborators — calling it a \"mirror\"\n * disambiguates from the new public class. The old export name is\n * still available as a deprecated alias from this file.\n *\n * Cap-agnostic by construction: the state mirror is `Map<deviceId,\n * Map<capName, slice>>` and the proxies are produced by the\n * codegen'd `createDeviceProxy` we already use everywhere — so a\n * brand-new cap with `runtimeState:` lights up here without a single\n * line of additional code.\n *\n * Two transports today, same shape:\n * - tRPC client (browser, Electron, SDK consumers).\n * - In-process AddonApi (server REPL, addons that want a global view).\n *\n * Boot:\n *\n * const sm = new SystemMirror(api)\n * await sm.init()\n *\n * const dev = sm.getDeviceById(8) // sync\n * dev?.state.battery.value?.sleeping // sync read from mirror\n * await dev?.snapshot?.getSnapshot({...}) // method dispatch via cap-router\n *\n * sm.query({ addonId: 'reolink', online: true })\n * sm.whereState('battery', s => s.percentage < 20)\n * sm.listenCap('motion', (id, slice) => …)\n * await sm.waitForState(8, 'battery', s => !s.sleeping, 30_000)\n *\n * Lifecycle: caller is responsible for `dispose()` when done — closes\n * the live event subscriptions.\n */\n\nimport type { DeviceBinding } from './device-binding.js'\nimport { createDeviceProxy, type DeviceProxy } from '../generated/device-proxy.js'\nimport type { AddonApi } from '../generated/addon-api.js'\nimport type { DeviceInfo } from '../capabilities/device-manager.cap.js'\nimport { DeviceType } from './device-type.js'\nimport { createMirrorSource, type SliceHandleApi, type SliceHandleSource } from './device-state-handle.js'\n\nconst STATE_CHANGED_CATEGORY = 'device.state-changed'\nconst BINDING_CHANGED_CATEGORY = 'capability.binding-changed'\nconst DEVICE_REGISTERED_CATEGORY = 'device.registered'\nconst DEVICE_UNREGISTERED_CATEGORY = 'device.unregistered'\nconst DEVICE_UPDATED_CATEGORY = 'device.updated'\n\n/**\n * Minimal subset of `AddonApi` the SystemMirror needs.\n */\nexport interface SystemMirrorApi extends SliceHandleApi {\n readonly deviceManager: {\n readonly getAllBindings: {\n query(input: Record<string, never>): Promise<ReadonlyArray<DeviceBinding>>\n }\n readonly listAll: {\n query(input: { addonId?: string }): Promise<ReadonlyArray<DeviceInfo>>\n }\n }\n readonly deviceState: SliceHandleApi['deviceState'] & {\n readonly getAllSnapshots: {\n query(input: Record<string, never>): Promise<Record<string, Record<string, Record<string, unknown>>>>\n }\n }\n}\n\nexport type SystemMirrorListener = (\n deviceId: number,\n capName: string,\n slice: Record<string, unknown> | undefined,\n) => void\n\nexport type DeviceCapListener = (\n deviceId: number,\n slice: Record<string, unknown> | undefined,\n) => void\n\nexport type DeviceLifecycleListener = (\n deviceId: number,\n info: DeviceInfo | null,\n) => void\n\n/**\n * Match shape — `string` is exact match, `RegExp` is regex match,\n * `{ contains }` is case-insensitive substring, `{ exact }` is the\n * verbose form of plain string. Useful for `query({ name: ... })`\n * where the same field accepts multiple match modes.\n */\nexport type StringMatch =\n | string\n | RegExp\n | { readonly exact: string }\n | { readonly contains: string }\n\n/**\n * Filter set for `query()`. All fields are optional and ANDed\n * together. Array values are ORed within a single field\n * (`addonId: ['a', 'b']` matches devices owned by either). The\n * `where` escape hatch runs LAST so the structured filters do the\n * cheap rejection first.\n */\nexport interface DeviceQueryFilters {\n /** Match by exact deviceId. Single value or array (OR'd). */\n readonly id?: number | readonly number[]\n /** Match by exact stableId. Single value or array. */\n readonly stableId?: string | readonly string[]\n /** Match by owning addonId. Single value or array. */\n readonly addonId?: string | readonly string[]\n /** Match by `DeviceType` enum value. Single value or array. */\n readonly type?: DeviceType | readonly DeviceType[]\n /** Match devices that bind ALL listed caps. Single capName or array. */\n readonly caps?: string | readonly string[]\n /** Match devices that bind ANY of the listed caps. Mutually exclusive\n * with `caps`. */\n readonly anyCap?: string | readonly string[]\n /** Match by device name. */\n readonly name?: StringMatch\n /** Match the `online` flag. */\n readonly online?: boolean\n /** Match by parent device id. Pass `null` for top-level devices. */\n readonly parentDeviceId?: number | null\n /** Match devices that expose a feature (e.g. `'BatteryOperated'`). */\n readonly feature?: string | readonly string[]\n /** Match `isCamera` flag. */\n readonly isCamera?: boolean\n /** Custom predicate. Runs last; receives both metadata and proxy. */\n readonly where?: (info: DeviceInfo, proxy: DeviceProxy) => boolean\n}\n\nexport class SystemMirror {\n /** deviceId → capName → slice (push-driven mirror). */\n private readonly stateMirror: Map<number, Map<string, Record<string, unknown>>> = new Map()\n /** deviceId → binding (warm-boot + binding-changed events). */\n private readonly bindings: Map<number, DeviceBinding> = new Map()\n /** deviceId → metadata (warm-boot + lifecycle events). */\n private readonly devices: Map<number, DeviceInfo> = new Map()\n /** Listener map for individual `(deviceId, capName)` watches —\n * shared with every `MirrorSource` we hand out. */\n private readonly handleListeners = new Map<string, Set<(slice: unknown | undefined) => void>>()\n /** Global state-change listeners registered via `listen()`. */\n private readonly globalStateListeners = new Set<SystemMirrorListener>()\n /** Per-cap state listeners registered via `listenCap(capName, …)`. */\n private readonly capListeners = new Map<string, Set<DeviceCapListener>>()\n /** Per-device state listeners registered via `listenDevice(id, …)`. */\n private readonly deviceListeners = new Map<number, Set<SystemMirrorListener>>()\n /** Lifecycle listeners. */\n private readonly addedListeners = new Set<DeviceLifecycleListener>()\n private readonly removedListeners = new Set<DeviceLifecycleListener>()\n /** waitForState pending requests — keyed by an internal counter. */\n private readonly pendingWaits = new Set<{ check: () => boolean; resolve: () => void }>()\n /** Memoised state source — every proxy shares it. */\n private readonly stateSource: SliceHandleSource\n /** Live event bridges — closed in `dispose()`. */\n private bridges: Array<{ unsubscribe: () => void }> = []\n private initialized = false\n private initPromise: Promise<void> | null = null\n\n constructor(private readonly api: SystemMirrorApi) {\n this.stateSource = createMirrorSource(this.stateMirror, this.handleListeners, this.api)\n }\n\n // ── Lifecycle ──────────────────────────────────────────────────────\n\n /**\n * Warm-boot: three round-trips (bindings + snapshots + listAll) and\n * then the live event subscriptions. Idempotent — concurrent callers\n * await the same in-flight Promise.\n *\n * Each warm-boot query is wrapped in a 15s timeout so a missing\n * endpoint or stuck broker poll surfaces as a clear error instead\n * of silently hanging the caller forever. The default is generous\n * enough for cluster-wide setups but short enough for ops to\n * notice and fix the underlying transport issue.\n */\n async init(timeoutMs: number = 15_000): Promise<void> {\n if (this.initialized) return\n if (this.initPromise) return this.initPromise\n\n this.initPromise = (async () => {\n const withTimeout = <T>(p: Promise<T>, label: string): Promise<T> => {\n if (!Number.isFinite(timeoutMs)) return p\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(\n () => reject(new Error(`SystemMirror.init: '${label}' timed out after ${timeoutMs}ms`)),\n timeoutMs,\n )\n p.then(\n (v) => { clearTimeout(timer); resolve(v) },\n (err) => { clearTimeout(timer); reject(err as Error) },\n )\n })\n }\n\n const [allBindings, allSnapshots, allDevices] = await Promise.all([\n withTimeout(this.api.deviceManager.getAllBindings.query({}), 'deviceManager.getAllBindings'),\n withTimeout(this.api.deviceState.getAllSnapshots.query({}), 'deviceState.getAllSnapshots'),\n withTimeout(this.api.deviceManager.listAll.query({}), 'deviceManager.listAll'),\n ])\n\n for (const b of allBindings) this.bindings.set(b.deviceId, b)\n\n for (const [deviceIdStr, perCap] of Object.entries(allSnapshots)) {\n const deviceId = Number(deviceIdStr)\n if (!Number.isFinite(deviceId)) continue\n const capMap = new Map<string, Record<string, unknown>>()\n for (const [capName, slice] of Object.entries(perCap)) capMap.set(capName, slice)\n this.stateMirror.set(deviceId, capMap)\n }\n\n for (const info of allDevices) this.devices.set(info.id, info)\n\n // Subscriptions are best-effort — `live.onEvent` may not be\n // wired in some environments (e.g. when the api is a partial\n // adapter). Catch errors so init still completes; the mirror\n // just stops getting live updates instead of hanging the\n // entire REPL/SDK boot.\n try {\n this.subscribeBus()\n } catch (err) {\n // Swallow — `subscribeBus` itself is safe (it checks\n // `api.live?.onEvent`), but defend against transport\n // wrappers that throw on subscribe.\n void err\n }\n this.initialized = true\n })()\n\n try {\n await this.initPromise\n } finally {\n this.initPromise = null\n }\n }\n\n /** True after `init()` resolves. */\n isReady(): boolean {\n return this.initialized\n }\n\n /** Promise that resolves once `init()` has completed. */\n async awaitReady(): Promise<void> {\n if (this.initialized) return\n if (this.initPromise) return this.initPromise\n return this.init()\n }\n\n /**\n * Tear down the event bridges + clear listener registries. Existing\n * proxy references become inert (slice reads keep working off the\n * stale mirror, but no further updates arrive).\n */\n dispose(): void {\n for (const b of this.bridges) {\n try { b.unsubscribe() } catch { /* ignore */ }\n }\n this.bridges = []\n this.handleListeners.clear()\n this.globalStateListeners.clear()\n this.capListeners.clear()\n this.deviceListeners.clear()\n this.addedListeners.clear()\n this.removedListeners.clear()\n this.pendingWaits.clear()\n this.initialized = false\n }\n\n // ── Lookup ─────────────────────────────────────────────────────────\n\n /** Sync lookup by numeric id. `null` if unknown. */\n getDeviceById(deviceId: number): DeviceProxy | null {\n const binding = this.bindings.get(deviceId)\n if (!binding) return null\n return createDeviceProxy(this.api as AddonApi, binding, { stateSource: this.stateSource })\n }\n\n /** Sync lookup by display name (exact match). `null` if unknown. */\n getDeviceByName(name: string): DeviceProxy | null {\n for (const info of this.devices.values()) {\n if (info.name === name) return this.getDeviceById(info.id)\n }\n return null\n }\n\n /** Sync lookup by stableId (the addon-scoped external id). */\n getDeviceByStableId(stableId: string): DeviceProxy | null {\n for (const info of this.devices.values()) {\n if (info.stableId === stableId) return this.getDeviceById(info.id)\n }\n return null\n }\n\n /** All devices owned by an addon. */\n getDevicesByAddon(addonId: string): readonly DeviceProxy[] {\n return this.query({ addonId })\n }\n\n /** All devices of a given `DeviceType`. */\n getDevicesByType(type: DeviceType): readonly DeviceProxy[] {\n return this.query({ type })\n }\n\n /**\n * Live metadata snapshot for a device. Same shape as\n * `deviceManager.getDevice(id)`; sync from the mirror.\n */\n getDeviceInfo(deviceId: number): DeviceInfo | null {\n return this.devices.get(deviceId) ?? null\n }\n\n /** Snapshot of every known device as a typed proxy. */\n getAllDevices(): readonly DeviceProxy[] {\n const out: DeviceProxy[] = []\n for (const id of this.bindings.keys()) {\n const proxy = this.getDeviceById(id)\n if (proxy) out.push(proxy)\n }\n return out\n }\n\n /** Filter devices by cap presence — shorthand for `query({caps: capName})`. */\n filterByCap(capName: string): readonly DeviceProxy[] {\n return this.query({ caps: capName })\n }\n\n // ── Unified query ──────────────────────────────────────────────────\n\n /**\n * Filter the fleet against a structured filter set. All fields ANDed,\n * arrays ORed within a single field. Returns proxies — chain with\n * cap-method calls or state reads as needed.\n *\n * Examples:\n *\n * sm.query({ addonId: 'reolink', online: true })\n * sm.query({ type: DeviceType.Camera, caps: ['snapshot', 'motion'] })\n * sm.query({ id: [1, 2, 3] })\n * sm.query({ name: { contains: 'sala' } })\n * sm.query({ name: /^cam-\\d+$/ })\n * sm.query({ parentDeviceId: 8 }) // accessories of cam 8\n * sm.query({ where: (info, dev) => dev.state.battery.value?.sleeping })\n */\n query(filters: DeviceQueryFilters = {}): readonly DeviceProxy[] {\n const out: DeviceProxy[] = []\n for (const [deviceId, binding] of this.bindings) {\n const info = this.devices.get(deviceId)\n // Most filters need DeviceInfo. If we don't have it, only `id` /\n // `caps` / `anyCap` filters can resolve — for everything else\n // skip the device. (This window is brief: warm-boot races\n // bindings vs metadata for a few ms.)\n if (filters.id !== undefined && !inSet(deviceId, filters.id)) continue\n if (filters.stableId !== undefined) {\n if (!info) continue\n if (!inSet(info.stableId, filters.stableId)) continue\n }\n if (filters.addonId !== undefined) {\n if (!info) continue\n if (!inSet(info.addonId, filters.addonId)) continue\n }\n if (filters.type !== undefined) {\n if (!info) continue\n if (!inSet(info.type, filters.type)) continue\n }\n if (filters.caps !== undefined) {\n const required = toArray(filters.caps)\n const bound = new Set(binding.entries.map((e) => e.capName))\n if (!required.every((c) => bound.has(c))) continue\n }\n if (filters.anyCap !== undefined) {\n const wanted = toArray(filters.anyCap)\n const bound = new Set(binding.entries.map((e) => e.capName))\n if (!wanted.some((c) => bound.has(c))) continue\n }\n if (filters.name !== undefined) {\n if (!info) continue\n if (!matchesString(info.name, filters.name)) continue\n }\n if (filters.online !== undefined) {\n if (!info) continue\n if (info.online !== filters.online) continue\n }\n if (filters.parentDeviceId !== undefined) {\n if (!info) continue\n const want = filters.parentDeviceId\n const got = info.parentDeviceId ?? null\n if (got !== want) continue\n }\n if (filters.feature !== undefined) {\n if (!info) continue\n const want = toArray(filters.feature)\n if (!want.every((f) => info.features.includes(f))) continue\n }\n if (filters.isCamera !== undefined) {\n if (!info) continue\n if (info.isCamera !== filters.isCamera) continue\n }\n const proxy = this.getDeviceById(deviceId)\n if (!proxy) continue\n if (filters.where && info) {\n if (!filters.where(info, proxy)) continue\n } else if (filters.where && !info) {\n continue\n }\n out.push(proxy)\n }\n return out\n }\n\n // ── State-driven queries ───────────────────────────────────────────\n\n /**\n * Return every proxy whose runtime-state slice for `capName` matches\n * `predicate`. Devices without the cap or without a slice are\n * skipped. The predicate receives a typed slice reference — pass\n * the typed cap (e.g. `'battery'`) and TypeScript widens to\n * `Record<string, unknown>`; cast inside the predicate body if the\n * caller knows the cap shape.\n */\n whereState<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n predicate: (slice: T, deviceId: number) => boolean,\n ): readonly DeviceProxy[] {\n const out: DeviceProxy[] = []\n for (const [deviceId, perCap] of this.stateMirror) {\n const slice = perCap.get(capName) as T | undefined\n if (!slice) continue\n if (!predicate(slice, deviceId)) continue\n const proxy = this.getDeviceById(deviceId)\n if (proxy) out.push(proxy)\n }\n return out\n }\n\n /** Map every device's slice for `capName` to a derived value. Devices\n * without the cap or without a slice are skipped. */\n mapState<T extends Record<string, unknown> = Record<string, unknown>, R = unknown>(\n capName: string,\n mapper: (slice: T, deviceId: number) => R,\n ): readonly R[] {\n const out: R[] = []\n for (const [deviceId, perCap] of this.stateMirror) {\n const slice = perCap.get(capName) as T | undefined\n if (!slice) continue\n out.push(mapper(slice, deviceId))\n }\n return out\n }\n\n /** First device whose slice matches `predicate`, or null. */\n findState<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n predicate: (slice: T, deviceId: number) => boolean,\n ): DeviceProxy | null {\n for (const [deviceId, perCap] of this.stateMirror) {\n const slice = perCap.get(capName) as T | undefined\n if (!slice) continue\n if (!predicate(slice, deviceId)) continue\n return this.getDeviceById(deviceId)\n }\n return null\n }\n\n /** Count devices that bind a cap. Faster than `filterByCap(...).length`. */\n countByCap(capName: string): number {\n let n = 0\n for (const binding of this.bindings.values()) {\n if (binding.entries.some((e) => e.capName === capName)) n++\n }\n return n\n }\n\n /** Count devices whose slice for `capName` matches `predicate`. */\n countByState<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n predicate: (slice: T, deviceId: number) => boolean,\n ): number {\n let n = 0\n for (const [deviceId, perCap] of this.stateMirror) {\n const slice = perCap.get(capName) as T | undefined\n if (!slice) continue\n if (predicate(slice, deviceId)) n++\n }\n return n\n }\n\n // ── Listeners ──────────────────────────────────────────────────────\n\n /**\n * Global listener — fires for every `device.state-changed` event the\n * mirror absorbs.\n */\n listen(cb: SystemMirrorListener): () => void {\n this.globalStateListeners.add(cb)\n return () => { this.globalStateListeners.delete(cb) }\n }\n\n /**\n * Per-cap listener — fires only for state changes on `capName`,\n * across every device. The callback receives the deviceId so the\n * caller can route.\n */\n listenCap(capName: string, cb: DeviceCapListener): () => void {\n let set = this.capListeners.get(capName)\n if (!set) { set = new Set(); this.capListeners.set(capName, set) }\n set.add(cb)\n return () => {\n set!.delete(cb)\n if (set!.size === 0) this.capListeners.delete(capName)\n }\n }\n\n /**\n * Per-device listener — fires for every cap change on `deviceId`.\n */\n listenDevice(deviceId: number, cb: SystemMirrorListener): () => void {\n let set = this.deviceListeners.get(deviceId)\n if (!set) { set = new Set(); this.deviceListeners.set(deviceId, set) }\n set.add(cb)\n return () => {\n set!.delete(cb)\n if (set!.size === 0) this.deviceListeners.delete(deviceId)\n }\n }\n\n /** Fires when `device.registered` lands. Receives the new metadata. */\n onDeviceAdded(cb: DeviceLifecycleListener): () => void {\n this.addedListeners.add(cb)\n return () => { this.addedListeners.delete(cb) }\n }\n\n /** Fires when `device.unregistered` lands. `info` is the LAST-known\n * metadata (or null if the device was never seen). */\n onDeviceRemoved(cb: DeviceLifecycleListener): () => void {\n this.removedListeners.add(cb)\n return () => { this.removedListeners.delete(cb) }\n }\n\n // ── Wait primitives ────────────────────────────────────────────────\n\n /**\n * Resolve when `predicate` over the runtime-state slice for\n * `(deviceId, capName)` becomes true. Resolves immediately if the\n * current slice already matches. Rejects with `Error('timeout')`\n * after `timeoutMs` (default 30s; pass `Infinity` to wait forever).\n *\n * Returns the matching slice — caller can read it directly without\n * a second mirror lookup.\n */\n waitForState<T extends Record<string, unknown> = Record<string, unknown>>(\n deviceId: number,\n capName: string,\n predicate: (slice: T) => boolean,\n timeoutMs: number = 30_000,\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const check = (): T | null => {\n const slice = this.stateMirror.get(deviceId)?.get(capName) as T | undefined\n if (slice && predicate(slice)) return slice\n return null\n }\n const initial = check()\n if (initial) {\n resolve(initial)\n return\n }\n\n let timer: ReturnType<typeof setTimeout> | null = null\n const off = this.listenDevice(deviceId, (_id, cap, slice) => {\n if (cap !== capName) return\n if (!slice) return\n if (predicate(slice as T)) {\n if (timer) clearTimeout(timer)\n off()\n resolve(slice as T)\n }\n })\n if (Number.isFinite(timeoutMs)) {\n timer = setTimeout(() => {\n off()\n reject(new Error(`waitForState timed out after ${timeoutMs}ms (deviceId=${deviceId}, capName=${capName})`))\n }, timeoutMs)\n }\n })\n }\n\n /**\n * Resolve when a device with `deviceId` becomes available (a\n * binding exists). Resolves immediately if already known. Rejects\n * with timeout.\n */\n waitForDevice(deviceId: number, timeoutMs: number = 30_000): Promise<DeviceProxy> {\n return new Promise<DeviceProxy>((resolve, reject) => {\n const existing = this.getDeviceById(deviceId)\n if (existing) {\n resolve(existing)\n return\n }\n let timer: ReturnType<typeof setTimeout> | null = null\n const off = this.onDeviceAdded((id) => {\n if (id !== deviceId) return\n const proxy = this.getDeviceById(id)\n if (!proxy) return\n if (timer) clearTimeout(timer)\n off()\n resolve(proxy)\n })\n if (Number.isFinite(timeoutMs)) {\n timer = setTimeout(() => {\n off()\n reject(new Error(`waitForDevice timed out after ${timeoutMs}ms (deviceId=${deviceId})`))\n }, timeoutMs)\n }\n })\n }\n\n // ── Batch actions ──────────────────────────────────────────────────\n\n /**\n * Iterate every device that binds `capName`. Awaits each callback\n * sequentially — for parallel use `invokeCap` with explicit\n * parallelism.\n */\n async forEachCap(\n capName: string,\n cb: (proxy: DeviceProxy) => void | Promise<void>,\n ): Promise<void> {\n for (const proxy of this.filterByCap(capName)) {\n await cb(proxy)\n }\n }\n\n /**\n * Invoke a cap method on every device that binds the cap. Returns\n * one result per device, success or failure isolated. Optional\n * parallelism cap — useful for \"snapshot all cameras but only 4\n * at a time so battery cams don't all wake at once\".\n *\n * Example:\n *\n * const results = await sm.invokeCap('snapshot', 'getSnapshot', {}, { parallelism: 4 })\n * const failed = results.filter(r => !r.ok)\n */\n async invokeCap<R = unknown>(\n capName: string,\n methodName: string,\n args: Record<string, unknown>,\n opts: { parallelism?: number } = {},\n ): Promise<ReadonlyArray<{ deviceId: number; ok: boolean; result?: R; error?: unknown }>> {\n const targets = this.filterByCap(capName)\n const parallelism = Math.max(1, opts.parallelism ?? targets.length)\n const out: Array<{ deviceId: number; ok: boolean; result?: R; error?: unknown }> = []\n\n for (let i = 0; i < targets.length; i += parallelism) {\n const chunk = targets.slice(i, i + parallelism)\n const settled = await Promise.allSettled(\n chunk.map(async (proxy) => {\n const cap = (proxy as unknown as Record<string, Record<string, (input: unknown) => unknown>>)[capName]\n if (!cap || typeof cap[methodName] !== 'function') {\n throw new Error(`device '${proxy.deviceId}' does not expose '${capName}.${methodName}'`)\n }\n return await cap[methodName]!(args) as R\n }),\n )\n for (let j = 0; j < settled.length; j++) {\n const proxy = chunk[j]!\n const r = settled[j]!\n if (r.status === 'fulfilled') {\n out.push({ deviceId: proxy.deviceId, ok: true, result: r.value })\n } else {\n out.push({ deviceId: proxy.deviceId, ok: false, error: r.reason })\n }\n }\n }\n\n return out\n }\n\n // ── Diagnostics ────────────────────────────────────────────────────\n\n /**\n * One-shot summary — fleet size, breakdown by cap / addon / type.\n * Designed for REPL inspection (`sm.summary()`).\n */\n summary(): {\n totalDevices: number\n online: number\n offline: number\n byCap: Record<string, number>\n byAddon: Record<string, number>\n byType: Record<string, number>\n statedDevices: number\n } {\n const byCap: Record<string, number> = {}\n const byAddon: Record<string, number> = {}\n const byType: Record<string, number> = {}\n let online = 0\n let offline = 0\n\n for (const binding of this.bindings.values()) {\n for (const entry of binding.entries) {\n byCap[entry.capName] = (byCap[entry.capName] ?? 0) + 1\n }\n }\n\n for (const info of this.devices.values()) {\n byAddon[info.addonId] = (byAddon[info.addonId] ?? 0) + 1\n byType[info.type] = (byType[info.type] ?? 0) + 1\n if (info.online) online++; else offline++\n }\n\n return {\n totalDevices: this.bindings.size,\n online,\n offline,\n byCap,\n byAddon,\n byType,\n statedDevices: this.stateMirror.size,\n }\n }\n\n /**\n * Debug-friendly dump — full state + binding + metadata for one\n * device or all devices. Cheap deep clone so caller mutations don't\n * leak into the mirror.\n */\n dump(deviceId?: number): unknown {\n const dumpOne = (id: number): unknown => {\n const info = this.devices.get(id) ?? null\n const binding = this.bindings.get(id) ?? null\n const state: Record<string, Record<string, unknown>> = {}\n const perCap = this.stateMirror.get(id)\n if (perCap) {\n for (const [cap, slice] of perCap) state[cap] = { ...slice }\n }\n return {\n deviceId: id,\n info,\n binding: binding ? { ...binding, entries: binding.entries.map((e) => ({ ...e })) } : null,\n state,\n }\n }\n\n if (deviceId !== undefined) return dumpOne(deviceId)\n\n const out: unknown[] = []\n for (const id of this.bindings.keys()) out.push(dumpOne(id))\n return out\n }\n\n /**\n * Direct read-only access to the underlying state mirror. Use\n * sparingly — `getSystemState` returns a deep copy that's safer for\n * exploratory work; this avoids the clone cost when iterating\n * thousands of slices.\n */\n getRawMirror(): ReadonlyMap<number, ReadonlyMap<string, Record<string, unknown>>> {\n return this.stateMirror\n }\n\n /**\n * Snapshot of the full state mirror — same shape as the warm-boot\n * payload. Deep-cloned; safe to mutate.\n */\n getSystemState(): Map<number, Map<string, Record<string, unknown>>> {\n const copy = new Map<number, Map<string, Record<string, unknown>>>()\n for (const [id, perCap] of this.stateMirror) {\n const dup = new Map<string, Record<string, unknown>>()\n for (const [k, v] of perCap) dup.set(k, { ...v })\n copy.set(id, dup)\n }\n return copy\n }\n\n // ── Internals ──────────────────────────────────────────────────────\n\n private subscribeBus(): void {\n if (!this.api.live?.onEvent) return\n const sub = this.api.live.onEvent\n\n this.bridges.push(\n sub.subscribe(\n { category: STATE_CHANGED_CATEGORY },\n {\n onData: (evt) => {\n const data = evt.data as { deviceId?: number; capName?: string; slice?: Record<string, unknown> } | null\n if (!data || typeof data.deviceId !== 'number' || typeof data.capName !== 'string') return\n this.applyStateUpdate(data.deviceId, data.capName, data.slice)\n },\n },\n ),\n )\n\n this.bridges.push(\n sub.subscribe(\n { category: BINDING_CHANGED_CATEGORY },\n {\n onData: (evt) => {\n const data = evt.data as { deviceId?: number; source?: { type?: string; id?: number | string } } | null\n const deviceId =\n typeof data?.deviceId === 'number'\n ? data.deviceId\n : data?.source?.type === 'device' && typeof data.source.id === 'number'\n ? data.source.id\n : null\n if (deviceId === null) return\n void this.refreshBinding(deviceId)\n },\n },\n ),\n )\n\n this.bridges.push(\n sub.subscribe(\n { category: DEVICE_REGISTERED_CATEGORY },\n {\n onData: (evt) => {\n const data = evt.data as { deviceId?: number } | null\n if (typeof data?.deviceId !== 'number') return\n void this.refreshDeviceMetadata(data.deviceId, 'added')\n },\n },\n ),\n )\n\n this.bridges.push(\n sub.subscribe(\n { category: DEVICE_UNREGISTERED_CATEGORY },\n {\n onData: (evt) => {\n const data = evt.data as { deviceId?: number } | null\n if (typeof data?.deviceId !== 'number') return\n this.applyDeviceRemoval(data.deviceId)\n },\n },\n ),\n )\n\n this.bridges.push(\n sub.subscribe(\n { category: DEVICE_UPDATED_CATEGORY },\n {\n onData: (evt) => {\n const data = evt.data as { deviceId?: number } | null\n if (typeof data?.deviceId !== 'number') return\n void this.refreshDeviceMetadata(data.deviceId, 'updated')\n },\n },\n ),\n )\n }\n\n private applyStateUpdate(\n deviceId: number,\n capName: string,\n slice: Record<string, unknown> | undefined,\n ): void {\n let perCap = this.stateMirror.get(deviceId)\n if (!perCap) {\n perCap = new Map()\n this.stateMirror.set(deviceId, perCap)\n }\n if (slice === undefined) {\n perCap.delete(capName)\n } else {\n perCap.set(capName, slice)\n }\n\n const handleKey = `${deviceId}:${capName}`\n const handleSet = this.handleListeners.get(handleKey)\n if (handleSet) {\n for (const cb of handleSet) {\n try { cb(slice) } catch { /* isolated */ }\n }\n }\n\n for (const cb of this.globalStateListeners) {\n try { cb(deviceId, capName, slice) } catch { /* isolated */ }\n }\n\n const capSet = this.capListeners.get(capName)\n if (capSet) {\n for (const cb of capSet) {\n try { cb(deviceId, slice) } catch { /* isolated */ }\n }\n }\n\n const devSet = this.deviceListeners.get(deviceId)\n if (devSet) {\n for (const cb of devSet) {\n try { cb(deviceId, capName, slice) } catch { /* isolated */ }\n }\n }\n }\n\n private applyDeviceRemoval(deviceId: number): void {\n const lastInfo = this.devices.get(deviceId) ?? null\n this.bindings.delete(deviceId)\n this.devices.delete(deviceId)\n this.stateMirror.delete(deviceId)\n for (const cb of this.removedListeners) {\n try { cb(deviceId, lastInfo) } catch { /* isolated */ }\n }\n }\n\n private async refreshBinding(deviceId: number): Promise<void> {\n try {\n const all = await this.api.deviceManager.getAllBindings.query({})\n const fresh = all.find((b) => b.deviceId === deviceId)\n if (fresh) {\n this.bindings.set(deviceId, fresh)\n } else {\n // Device was removed — emit lifecycle hook so consumers can\n // clean up. Use the same path as `device.unregistered` events.\n this.applyDeviceRemoval(deviceId)\n }\n } catch {\n // Transient failure — leave stale binding; next event retries.\n }\n }\n\n private async refreshDeviceMetadata(deviceId: number, kind: 'added' | 'updated'): Promise<void> {\n try {\n // listAll is cheap and idempotent. Single-device fetch\n // (`getDevice(id)`) would be lighter but means another method\n // on the api surface for tests; sticking with listAll keeps the\n // contract small.\n const all = await this.api.deviceManager.listAll.query({})\n const info = all.find((d) => d.id === deviceId)\n if (!info) return\n\n const wasNew = !this.devices.has(deviceId)\n this.devices.set(deviceId, info)\n\n if (kind === 'added' && wasNew) {\n // `device.registered` event ran but the bindings/state for\n // the new device may still be in flight from the worker.\n // Refresh bindings so consumers can immediately use the\n // device proxy. Lifecycle listener fires after binding lands.\n await this.refreshBinding(deviceId)\n for (const cb of this.addedListeners) {\n try { cb(deviceId, info) } catch { /* isolated */ }\n }\n }\n } catch {\n /* transient — next event retries */\n }\n }\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────\n\nfunction inSet<T>(value: T, set: T | readonly T[]): boolean {\n if (Array.isArray(set)) return (set as readonly T[]).includes(value)\n return value === set\n}\n\nfunction toArray<T>(value: T | readonly T[]): readonly T[] {\n return Array.isArray(value) ? (value as readonly T[]) : [value as T]\n}\n\nfunction matchesString(haystack: string, match: StringMatch): boolean {\n if (typeof match === 'string') return haystack === match\n if (match instanceof RegExp) return match.test(haystack)\n if ('exact' in match) return haystack === match.exact\n if ('contains' in match) return haystack.toLowerCase().includes(match.contains.toLowerCase())\n return false\n}\n\n","/**\n * Custom-action contracts — a mechanism for addons to expose system-level\n * extensibility endpoints (e.g. benchmark addon API) through a single\n * generic `api.addons.custom` tRPC endpoint.\n *\n * The contract shape mirrors `method()` from capability-definition.ts —\n * input/output Zod schemas, kind, auth — plus a `scope` discriminated union\n * (today only 'system' is runtime-supported; 'device' is typed for forward\n * compat).\n */\n\nimport type { z } from 'zod'\nimport type { DeviceType } from '../device/device-type.js'\nimport type { CapabilityMethodAuth } from './capability-definition.js'\n\n/** Scope of a custom action. Today only `system` is supported by the runtime. */\nexport type CustomActionScope =\n | { readonly kind: 'system' }\n | { readonly kind: 'device'; readonly deviceTypes?: readonly DeviceType[] }\n\nexport interface CustomActionSpec<\n TIn extends z.ZodType = z.ZodType,\n TOut extends z.ZodType = z.ZodType,\n TKind extends 'query' | 'mutation' | 'subscription' = 'query' | 'mutation' | 'subscription',\n TAuth extends CapabilityMethodAuth = CapabilityMethodAuth,\n TScope extends CustomActionScope = CustomActionScope,\n> {\n readonly input: TIn\n readonly output: TOut\n readonly kind: TKind\n readonly auth: TAuth\n readonly scope: TScope\n}\n\nexport type CustomActionsSpec = Record<string, CustomActionSpec>\n\n/**\n * Identity — preserves literal types for downstream inference.\n *\n * The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so\n * TypeScript does not widen each entry's literal `kind`/`auth` fields to\n * the broader unions declared on `CustomActionSpec`'s default generics.\n * Shape validity is enforced separately by the `customAction(...)` helper\n * whose return type is already a `CustomActionSpec<...>`.\n */\nexport function defineCustomActions<T extends Record<string, unknown>>(spec: T): T {\n return spec\n}\n\nexport interface CustomActionOptions<\n TKind extends 'query' | 'mutation' | 'subscription' = 'query',\n TAuth extends CapabilityMethodAuth = 'protected',\n> {\n readonly kind?: TKind\n readonly auth?: TAuth\n readonly scope?: CustomActionScope\n}\n\nexport function customAction<\n TIn extends z.ZodType,\n TOut extends z.ZodType,\n TKind extends 'query' | 'mutation' | 'subscription' = 'query',\n TAuth extends CapabilityMethodAuth = 'protected',\n>(\n input: TIn,\n output: TOut,\n options?: CustomActionOptions<TKind, TAuth>,\n): CustomActionSpec<TIn, TOut, TKind, TAuth, { readonly kind: 'system' }> {\n return {\n input,\n output,\n kind: (options?.kind ?? 'query') as TKind,\n auth: (options?.auth ?? 'protected') as TAuth,\n scope: (options?.scope ?? { kind: 'system' as const }) as { readonly kind: 'system' },\n }\n}\n","// AUTO-GENERATED by scripts/generate-capability-router-types.ts — DO NOT EDIT\n// Re-run after adding/modifying capability definitions.\n//\n// Generated: 2026-05-12T06:43:42.200Z\n// Capabilities: 80\n\n// ── Capability Definition Imports ────────────────────────────────────\n\nexport { accessoriesCapability } from '../capabilities/accessories.cap.js'\nexport { addonPagesCapability } from '../capabilities/addon-pages.cap.js'\nexport { addonPagesSourceCapability } from '../capabilities/addon-pages-source.cap.js'\nexport { addonRoutesCapability } from '../capabilities/addon-routes.cap.js'\nexport { addonSettingsCapability } from '../capabilities/addon-settings.cap.js'\nexport { addonWidgetsCapability } from '../capabilities/addon-widgets.cap.js'\nexport { addonWidgetsSourceCapability } from '../capabilities/addon-widgets-source.cap.js'\nexport { addonsCapability } from '../capabilities/addons.cap.js'\nexport { adminUiCapability } from '../capabilities/admin-ui.cap.js'\nexport { advancedNotifierCapability } from '../capabilities/advanced-notifier.cap.js'\nexport { alertsCapability } from '../capabilities/alerts.cap.js'\nexport { audioAnalysisCapability } from '../capabilities/audio-analysis.cap.js'\nexport { audioAnalyzerCapability } from '../capabilities/audio-analyzer.cap.js'\nexport { audioCodecCapability } from '../capabilities/audio-codec.cap.js'\nexport { audioMetricsCapability } from '../capabilities/audio-metrics.cap.js'\nexport { authProviderCapability } from '../capabilities/auth-provider.cap.js'\nexport { authenticationCapability } from '../capabilities/authentication.cap.js'\nexport { backupCapability } from '../capabilities/backup.cap.js'\nexport { batteryCapability } from '../capabilities/battery.cap.js'\nexport { brightnessCapability } from '../capabilities/brightness.cap.js'\nexport { cameraCredentialsCapability } from '../capabilities/camera-credentials.cap.js'\nexport { cameraStreamsCapability } from '../capabilities/camera-streams.cap.js'\nexport { decoderCapability } from '../capabilities/decoder.cap.js'\nexport { detectionPipelineCapability } from '../capabilities/detection-pipeline.cap.js'\nexport { deviceDiscoveryCapability } from '../capabilities/device-discovery.cap.js'\nexport { deviceManagerCapability } from '../capabilities/device-manager.cap.js'\nexport { deviceOpsCapability } from '../capabilities/device-ops.cap.js'\nexport { deviceProviderCapability } from '../capabilities/device-provider.cap.js'\nexport { deviceStateCapability } from '../capabilities/device-state.cap.js'\nexport { deviceStatusCapability } from '../capabilities/device-status.cap.js'\nexport { doorbellCapability } from '../capabilities/doorbell.cap.js'\nexport { embeddingEncoderCapability } from '../capabilities/embedding-encoder.cap.js'\nexport { eventsCapability } from '../capabilities/events.cap.js'\nexport { featureProbeCapability } from '../capabilities/feature-probe.cap.js'\nexport { integrationsCapability } from '../capabilities/integrations.cap.js'\nexport { intercomCapability } from '../capabilities/intercom.cap.js'\nexport { localNetworkCapability } from '../capabilities/local-network.cap.js'\nexport { logDestinationCapability } from '../capabilities/log-destination.cap.js'\nexport { meshNetworkCapability } from '../capabilities/mesh-network.cap.js'\nexport { meshOrchestratorCapability } from '../capabilities/mesh-orchestrator.cap.js'\nexport { metricsProviderCapability } from '../capabilities/metrics-provider.cap.js'\nexport { motionCapability } from '../capabilities/motion.cap.js'\nexport { motionDetectionCapability } from '../capabilities/motion-detection.cap.js'\nexport { motionTriggerCapability } from '../capabilities/motion-trigger.cap.js'\nexport { nativeObjectDetectionCapability } from '../capabilities/native-object-detection.cap.js'\nexport { networkAccessCapability } from '../capabilities/network-access.cap.js'\nexport { networkQualityCapability } from '../capabilities/network-quality.cap.js'\nexport { nodesCapability } from '../capabilities/nodes.cap.js'\nexport { notificationOutputCapability } from '../capabilities/notification-output.cap.js'\nexport { osdCapability } from '../capabilities/osd.cap.js'\nexport { pipelineAnalyticsCapability } from '../capabilities/pipeline-analytics.cap.js'\nexport { pipelineExecutorCapability } from '../capabilities/pipeline-executor.cap.js'\nexport { pipelineOrchestratorCapability } from '../capabilities/pipeline-orchestrator.cap.js'\nexport { pipelineRunnerCapability } from '../capabilities/pipeline-runner.cap.js'\nexport { platformProbeCapability } from '../capabilities/platform-probe.cap.js'\nexport { ptzCapability } from '../capabilities/ptz.cap.js'\nexport { ptzAutotrackCapability } from '../capabilities/ptz-autotrack.cap.js'\nexport { rebootCapability } from '../capabilities/reboot.cap.js'\nexport { recordingCapability } from '../capabilities/recording.cap.js'\nexport { recordingEngineCapability } from '../capabilities/recording-engine.cap.js'\nexport { remoteAccessCapability } from '../capabilities/remote-access.cap.js'\nexport { restreamerCapability } from '../capabilities/restreamer.cap.js'\nexport { settingsStoreCapability } from '../capabilities/settings-store.cap.js'\nexport { snapshotCapability } from '../capabilities/snapshot.cap.js'\nexport { snapshotProviderCapability } from '../capabilities/snapshot-provider.cap.js'\nexport { storageCapability } from '../capabilities/storage.cap.js'\nexport { storageProviderCapability } from '../capabilities/storage-provider.cap.js'\nexport { streamBrokerCapability } from '../capabilities/stream-broker.cap.js'\nexport { streamingEngineCapability } from '../capabilities/streaming-engine.cap.js'\nexport { switchCapability } from '../capabilities/switch.cap.js'\nexport { systemCapability } from '../capabilities/system.cap.js'\nexport { toastCapability } from '../capabilities/toast.cap.js'\nexport { turnOrchestratorCapability } from '../capabilities/turn-orchestrator.cap.js'\nexport { turnProviderCapability } from '../capabilities/turn-provider.cap.js'\nexport { userManagementCapability } from '../capabilities/user-management.cap.js'\nexport { webrtcCapability } from '../capabilities/webrtc.cap.js'\nexport { webrtcSessionCapability } from '../capabilities/webrtc-session.cap.js'\nexport { zoneAnalyticsCapability } from '../capabilities/zone-analytics.cap.js'\nexport { zoneRulesCapability } from '../capabilities/zone-rules.cap.js'\nexport { zonesCapability } from '../capabilities/zones.cap.js'\n\n// ── Capability Names (for registry key constants) ────────────────────\n\nexport const CAPABILITY_NAMES = {\n accessories: 'accessories' as const,\n addonPages: 'addon-pages' as const,\n addonPagesSource: 'addon-pages-source' as const,\n addonRoutes: 'addon-routes' as const,\n addonSettings: 'addon-settings' as const,\n addonWidgets: 'addon-widgets' as const,\n addonWidgetsSource: 'addon-widgets-source' as const,\n addons: 'addons' as const,\n adminUi: 'admin-ui' as const,\n advancedNotifier: 'advanced-notifier' as const,\n alerts: 'alerts' as const,\n audioAnalysis: 'audio-analysis' as const,\n audioAnalyzer: 'audio-analyzer' as const,\n audioCodec: 'audio-codec' as const,\n audioMetrics: 'audio-metrics' as const,\n authProvider: 'auth-provider' as const,\n authentication: 'authentication' as const,\n backup: 'backup' as const,\n battery: 'battery' as const,\n brightness: 'brightness' as const,\n cameraCredentials: 'camera-credentials' as const,\n cameraStreams: 'camera-streams' as const,\n decoder: 'decoder' as const,\n detectionPipeline: 'detection-pipeline' as const,\n deviceDiscovery: 'device-discovery' as const,\n deviceManager: 'device-manager' as const,\n deviceOps: 'device-ops' as const,\n deviceProvider: 'device-provider' as const,\n deviceState: 'device-state' as const,\n deviceStatus: 'device-status' as const,\n doorbell: 'doorbell' as const,\n embeddingEncoder: 'embedding-encoder' as const,\n events: 'events' as const,\n featureProbe: 'feature-probe' as const,\n integrations: 'integrations' as const,\n intercom: 'intercom' as const,\n localNetwork: 'local-network' as const,\n logDestination: 'log-destination' as const,\n meshNetwork: 'mesh-network' as const,\n meshOrchestrator: 'mesh-orchestrator' as const,\n metricsProvider: 'metrics-provider' as const,\n motion: 'motion' as const,\n motionDetection: 'motion-detection' as const,\n motionTrigger: 'motion-trigger' as const,\n nativeObjectDetection: 'native-object-detection' as const,\n networkAccess: 'network-access' as const,\n networkQuality: 'network-quality' as const,\n nodes: 'nodes' as const,\n notificationOutput: 'notification-output' as const,\n osd: 'osd' as const,\n pipelineAnalytics: 'pipeline-analytics' as const,\n pipelineExecutor: 'pipeline-executor' as const,\n pipelineOrchestrator: 'pipeline-orchestrator' as const,\n pipelineRunner: 'pipeline-runner' as const,\n platformProbe: 'platform-probe' as const,\n ptz: 'ptz' as const,\n ptzAutotrack: 'ptz-autotrack' as const,\n reboot: 'reboot' as const,\n recording: 'recording' as const,\n recordingEngine: 'recording-engine' as const,\n remoteAccess: 'remote-access' as const,\n restreamer: 'restreamer' as const,\n settingsStore: 'settings-store' as const,\n snapshot: 'snapshot' as const,\n snapshotProvider: 'snapshot-provider' as const,\n storage: 'storage' as const,\n storageProvider: 'storage-provider' as const,\n streamBroker: 'stream-broker' as const,\n streamingEngine: 'streaming-engine' as const,\n switch: 'switch' as const,\n system: 'system' as const,\n toast: 'toast' as const,\n turnOrchestrator: 'turn-orchestrator' as const,\n turnProvider: 'turn-provider' as const,\n userManagement: 'user-management' as const,\n webrtc: 'webrtc' as const,\n webrtcSession: 'webrtc-session' as const,\n zoneAnalytics: 'zone-analytics' as const,\n zoneRules: 'zone-rules' as const,\n zones: 'zones' as const,\n} as const\n\n/** All known capability names. */\nexport type CapabilityName = typeof CAPABILITY_NAMES[keyof typeof CAPABILITY_NAMES]\n\n/** Router key → capability name mapping for auto-mount. */\nexport const CAPABILITY_ROUTER_KEYS: ReadonlyArray<{ readonly key: string; readonly name: string }> = [\n { key: 'accessories', name: 'accessories' },\n { key: 'addonPages', name: 'addon-pages' },\n { key: 'addonPagesSource', name: 'addon-pages-source' },\n { key: 'addonRoutes', name: 'addon-routes' },\n { key: 'addonSettings', name: 'addon-settings' },\n { key: 'addonWidgets', name: 'addon-widgets' },\n { key: 'addonWidgetsSource', name: 'addon-widgets-source' },\n { key: 'addons', name: 'addons' },\n { key: 'adminUi', name: 'admin-ui' },\n { key: 'advancedNotifier', name: 'advanced-notifier' },\n { key: 'alerts', name: 'alerts' },\n { key: 'audioAnalysis', name: 'audio-analysis' },\n { key: 'audioAnalyzer', name: 'audio-analyzer' },\n { key: 'audioCodec', name: 'audio-codec' },\n { key: 'audioMetrics', name: 'audio-metrics' },\n { key: 'authProvider', name: 'auth-provider' },\n { key: 'authentication', name: 'authentication' },\n { key: 'backup', name: 'backup' },\n { key: 'battery', name: 'battery' },\n { key: 'brightness', name: 'brightness' },\n { key: 'cameraCredentials', name: 'camera-credentials' },\n { key: 'cameraStreams', name: 'camera-streams' },\n { key: 'decoder', name: 'decoder' },\n { key: 'detectionPipeline', name: 'detection-pipeline' },\n { key: 'deviceDiscovery', name: 'device-discovery' },\n { key: 'deviceManager', name: 'device-manager' },\n { key: 'deviceOps', name: 'device-ops' },\n { key: 'deviceProvider', name: 'device-provider' },\n { key: 'deviceState', name: 'device-state' },\n { key: 'deviceStatus', name: 'device-status' },\n { key: 'doorbell', name: 'doorbell' },\n { key: 'embeddingEncoder', name: 'embedding-encoder' },\n { key: 'events', name: 'events' },\n { key: 'featureProbe', name: 'feature-probe' },\n { key: 'integrations', name: 'integrations' },\n { key: 'intercom', name: 'intercom' },\n { key: 'localNetwork', name: 'local-network' },\n { key: 'logDestination', name: 'log-destination' },\n { key: 'meshNetwork', name: 'mesh-network' },\n { key: 'meshOrchestrator', name: 'mesh-orchestrator' },\n { key: 'metricsProvider', name: 'metrics-provider' },\n { key: 'motion', name: 'motion' },\n { key: 'motionDetection', name: 'motion-detection' },\n { key: 'motionTrigger', name: 'motion-trigger' },\n { key: 'nativeObjectDetection', name: 'native-object-detection' },\n { key: 'networkAccess', name: 'network-access' },\n { key: 'networkQuality', name: 'network-quality' },\n { key: 'nodes', name: 'nodes' },\n { key: 'notificationOutput', name: 'notification-output' },\n { key: 'osd', name: 'osd' },\n { key: 'pipelineAnalytics', name: 'pipeline-analytics' },\n { key: 'pipelineExecutor', name: 'pipeline-executor' },\n { key: 'pipelineOrchestrator', name: 'pipeline-orchestrator' },\n { key: 'pipelineRunner', name: 'pipeline-runner' },\n { key: 'platformProbe', name: 'platform-probe' },\n { key: 'ptz', name: 'ptz' },\n { key: 'ptzAutotrack', name: 'ptz-autotrack' },\n { key: 'reboot', name: 'reboot' },\n { key: 'recording', name: 'recording' },\n { key: 'recordingEngine', name: 'recording-engine' },\n { key: 'remoteAccess', name: 'remote-access' },\n { key: 'restreamer', name: 'restreamer' },\n { key: 'settingsStore', name: 'settings-store' },\n { key: 'snapshot', name: 'snapshot' },\n { key: 'snapshotProvider', name: 'snapshot-provider' },\n { key: 'storage', name: 'storage' },\n { key: 'storageProvider', name: 'storage-provider' },\n { key: 'streamBroker', name: 'stream-broker' },\n { key: 'streamingEngine', name: 'streaming-engine' },\n { key: 'switch', name: 'switch' },\n { key: 'system', name: 'system' },\n { key: 'toast', name: 'toast' },\n { key: 'turnOrchestrator', name: 'turn-orchestrator' },\n { key: 'turnProvider', name: 'turn-provider' },\n { key: 'userManagement', name: 'user-management' },\n { key: 'webrtc', name: 'webrtc' },\n { key: 'webrtcSession', name: 'webrtc-session' },\n { key: 'zoneAnalytics', name: 'zone-analytics' },\n { key: 'zoneRules', name: 'zone-rules' },\n { key: 'zones', name: 'zones' },\n]\n// ── Typed Router Map (for AppRouter) ─────────────────────────────────\n//\n// Import CapabilityTRPCRouter from the server to type the map.\n// This type is consumed by trpc.router.ts for typed spread.\n\n/**\n * Map of {name} → TRouter for typed AppRouter spread.\n * Generic TRouter defaults to unknown — the server layer resolves it\n * to the generated capability router type.\n */\nexport interface CapabilityRouterMap<TRouter = unknown> {\n readonly accessories: TRouter\n readonly addonPages: TRouter\n readonly addonPagesSource: TRouter\n readonly addonRoutes: TRouter\n readonly addonSettings: TRouter\n readonly addonWidgets: TRouter\n readonly addonWidgetsSource: TRouter\n readonly addons: TRouter\n readonly adminUi: TRouter\n readonly advancedNotifier: TRouter\n readonly alerts: TRouter\n readonly audioAnalysis: TRouter\n readonly audioAnalyzer: TRouter\n readonly audioCodec: TRouter\n readonly audioMetrics: TRouter\n readonly authProvider: TRouter\n readonly authentication: TRouter\n readonly backup: TRouter\n readonly battery: TRouter\n readonly brightness: TRouter\n readonly cameraCredentials: TRouter\n readonly cameraStreams: TRouter\n readonly decoder: TRouter\n readonly detectionPipeline: TRouter\n readonly deviceDiscovery: TRouter\n readonly deviceManager: TRouter\n readonly deviceOps: TRouter\n readonly deviceProvider: TRouter\n readonly deviceState: TRouter\n readonly deviceStatus: TRouter\n readonly doorbell: TRouter\n readonly embeddingEncoder: TRouter\n readonly events: TRouter\n readonly featureProbe: TRouter\n readonly integrations: TRouter\n readonly intercom: TRouter\n readonly localNetwork: TRouter\n readonly logDestination: TRouter\n readonly meshNetwork: TRouter\n readonly meshOrchestrator: TRouter\n readonly metricsProvider: TRouter\n readonly motion: TRouter\n readonly motionDetection: TRouter\n readonly motionTrigger: TRouter\n readonly nativeObjectDetection: TRouter\n readonly networkAccess: TRouter\n readonly networkQuality: TRouter\n readonly nodes: TRouter\n readonly notificationOutput: TRouter\n readonly osd: TRouter\n readonly pipelineAnalytics: TRouter\n readonly pipelineExecutor: TRouter\n readonly pipelineOrchestrator: TRouter\n readonly pipelineRunner: TRouter\n readonly platformProbe: TRouter\n readonly ptz: TRouter\n readonly ptzAutotrack: TRouter\n readonly reboot: TRouter\n readonly recording: TRouter\n readonly recordingEngine: TRouter\n readonly remoteAccess: TRouter\n readonly restreamer: TRouter\n readonly settingsStore: TRouter\n readonly snapshot: TRouter\n readonly snapshotProvider: TRouter\n readonly storage: TRouter\n readonly storageProvider: TRouter\n readonly streamBroker: TRouter\n readonly streamingEngine: TRouter\n readonly switch: TRouter\n readonly system: TRouter\n readonly toast: TRouter\n readonly turnOrchestrator: TRouter\n readonly turnProvider: TRouter\n readonly userManagement: TRouter\n readonly webrtc: TRouter\n readonly webrtcSession: TRouter\n readonly zoneAnalytics: TRouter\n readonly zoneRules: TRouter\n readonly zones: TRouter\n}\n\n// ── Singleton / Collection split (derived from cap.mode) ─────────────\n\n/** Capability names whose mode is `singleton` (65 caps). */\nexport const SINGLETON_CAPABILITY_NAMES = [\n 'accessories',\n 'addon-pages',\n 'addon-settings',\n 'addon-widgets',\n 'addons',\n 'admin-ui',\n 'advanced-notifier',\n 'alerts',\n 'audio-analysis',\n 'audio-analyzer',\n 'audio-codec',\n 'audio-metrics',\n 'authentication',\n 'backup',\n 'battery',\n 'brightness',\n 'camera-credentials',\n 'camera-streams',\n 'decoder',\n 'detection-pipeline',\n 'device-discovery',\n 'device-manager',\n 'device-ops',\n 'device-state',\n 'device-status',\n 'doorbell',\n 'events',\n 'feature-probe',\n 'integrations',\n 'intercom',\n 'local-network',\n 'mesh-orchestrator',\n 'metrics-provider',\n 'motion',\n 'motion-detection',\n 'motion-trigger',\n 'native-object-detection',\n 'network-quality',\n 'nodes',\n 'osd',\n 'pipeline-analytics',\n 'pipeline-executor',\n 'pipeline-orchestrator',\n 'pipeline-runner',\n 'platform-probe',\n 'ptz',\n 'ptz-autotrack',\n 'reboot',\n 'recording',\n 'recording-engine',\n 'remote-access',\n 'settings-store',\n 'snapshot',\n 'storage',\n 'stream-broker',\n 'streaming-engine',\n 'switch',\n 'system',\n 'toast',\n 'turn-orchestrator',\n 'user-management',\n 'webrtc-session',\n 'zone-analytics',\n 'zone-rules',\n 'zones',\n] as const\n\n/** Union of singleton capability names (literal string union). */\nexport type SingletonCapabilityName = typeof SINGLETON_CAPABILITY_NAMES[number]\n\n/** Capability names whose mode is `collection` (15 caps). */\nexport const COLLECTION_CAPABILITY_NAMES = [\n 'addon-pages-source',\n 'addon-routes',\n 'addon-widgets-source',\n 'auth-provider',\n 'device-provider',\n 'embedding-encoder',\n 'log-destination',\n 'mesh-network',\n 'network-access',\n 'notification-output',\n 'restreamer',\n 'snapshot-provider',\n 'storage-provider',\n 'turn-provider',\n 'webrtc',\n] as const\n\n/** Union of collection capability names (literal string union). */\nexport type CollectionCapabilityName = typeof COLLECTION_CAPABILITY_NAMES[number]\n\n/** Capability mode lookup at runtime — mirrors the `.cap.ts` definitions. */\nexport const CAPABILITY_MODE: Readonly<Record<string, 'singleton' | 'collection'>> = {\n 'accessories': 'singleton',\n 'addon-pages': 'singleton',\n 'addon-pages-source': 'collection',\n 'addon-routes': 'collection',\n 'addon-settings': 'singleton',\n 'addon-widgets': 'singleton',\n 'addon-widgets-source': 'collection',\n 'addons': 'singleton',\n 'admin-ui': 'singleton',\n 'advanced-notifier': 'singleton',\n 'alerts': 'singleton',\n 'audio-analysis': 'singleton',\n 'audio-analyzer': 'singleton',\n 'audio-codec': 'singleton',\n 'audio-metrics': 'singleton',\n 'auth-provider': 'collection',\n 'authentication': 'singleton',\n 'backup': 'singleton',\n 'battery': 'singleton',\n 'brightness': 'singleton',\n 'camera-credentials': 'singleton',\n 'camera-streams': 'singleton',\n 'decoder': 'singleton',\n 'detection-pipeline': 'singleton',\n 'device-discovery': 'singleton',\n 'device-manager': 'singleton',\n 'device-ops': 'singleton',\n 'device-provider': 'collection',\n 'device-state': 'singleton',\n 'device-status': 'singleton',\n 'doorbell': 'singleton',\n 'embedding-encoder': 'collection',\n 'events': 'singleton',\n 'feature-probe': 'singleton',\n 'integrations': 'singleton',\n 'intercom': 'singleton',\n 'local-network': 'singleton',\n 'log-destination': 'collection',\n 'mesh-network': 'collection',\n 'mesh-orchestrator': 'singleton',\n 'metrics-provider': 'singleton',\n 'motion': 'singleton',\n 'motion-detection': 'singleton',\n 'motion-trigger': 'singleton',\n 'native-object-detection': 'singleton',\n 'network-access': 'collection',\n 'network-quality': 'singleton',\n 'nodes': 'singleton',\n 'notification-output': 'collection',\n 'osd': 'singleton',\n 'pipeline-analytics': 'singleton',\n 'pipeline-executor': 'singleton',\n 'pipeline-orchestrator': 'singleton',\n 'pipeline-runner': 'singleton',\n 'platform-probe': 'singleton',\n 'ptz': 'singleton',\n 'ptz-autotrack': 'singleton',\n 'reboot': 'singleton',\n 'recording': 'singleton',\n 'recording-engine': 'singleton',\n 'remote-access': 'singleton',\n 'restreamer': 'collection',\n 'settings-store': 'singleton',\n 'snapshot': 'singleton',\n 'snapshot-provider': 'collection',\n 'storage': 'singleton',\n 'storage-provider': 'collection',\n 'stream-broker': 'singleton',\n 'streaming-engine': 'singleton',\n 'switch': 'singleton',\n 'system': 'singleton',\n 'toast': 'singleton',\n 'turn-orchestrator': 'singleton',\n 'turn-provider': 'collection',\n 'user-management': 'singleton',\n 'webrtc': 'collection',\n 'webrtc-session': 'singleton',\n 'zone-analytics': 'singleton',\n 'zone-rules': 'singleton',\n 'zones': 'singleton',\n}\n\n// ── All Capability Definitions (for boot-time declaration) ───────────\n\nimport type { CapabilityDefinition } from '../capabilities/capability-definition.js'\nimport { accessoriesCapability as _accessoriesCapability } from '../capabilities/accessories.cap.js'\nimport { addonPagesCapability as _addonPagesCapability } from '../capabilities/addon-pages.cap.js'\nimport { addonPagesSourceCapability as _addonPagesSourceCapability } from '../capabilities/addon-pages-source.cap.js'\nimport { addonRoutesCapability as _addonRoutesCapability } from '../capabilities/addon-routes.cap.js'\nimport { addonSettingsCapability as _addonSettingsCapability } from '../capabilities/addon-settings.cap.js'\nimport { addonWidgetsCapability as _addonWidgetsCapability } from '../capabilities/addon-widgets.cap.js'\nimport { addonWidgetsSourceCapability as _addonWidgetsSourceCapability } from '../capabilities/addon-widgets-source.cap.js'\nimport { addonsCapability as _addonsCapability } from '../capabilities/addons.cap.js'\nimport { adminUiCapability as _adminUiCapability } from '../capabilities/admin-ui.cap.js'\nimport { advancedNotifierCapability as _advancedNotifierCapability } from '../capabilities/advanced-notifier.cap.js'\nimport { alertsCapability as _alertsCapability } from '../capabilities/alerts.cap.js'\nimport { audioAnalysisCapability as _audioAnalysisCapability } from '../capabilities/audio-analysis.cap.js'\nimport { audioAnalyzerCapability as _audioAnalyzerCapability } from '../capabilities/audio-analyzer.cap.js'\nimport { audioCodecCapability as _audioCodecCapability } from '../capabilities/audio-codec.cap.js'\nimport { audioMetricsCapability as _audioMetricsCapability } from '../capabilities/audio-metrics.cap.js'\nimport { authProviderCapability as _authProviderCapability } from '../capabilities/auth-provider.cap.js'\nimport { authenticationCapability as _authenticationCapability } from '../capabilities/authentication.cap.js'\nimport { backupCapability as _backupCapability } from '../capabilities/backup.cap.js'\nimport { batteryCapability as _batteryCapability } from '../capabilities/battery.cap.js'\nimport { brightnessCapability as _brightnessCapability } from '../capabilities/brightness.cap.js'\nimport { cameraCredentialsCapability as _cameraCredentialsCapability } from '../capabilities/camera-credentials.cap.js'\nimport { cameraStreamsCapability as _cameraStreamsCapability } from '../capabilities/camera-streams.cap.js'\nimport { decoderCapability as _decoderCapability } from '../capabilities/decoder.cap.js'\nimport { detectionPipelineCapability as _detectionPipelineCapability } from '../capabilities/detection-pipeline.cap.js'\nimport { deviceDiscoveryCapability as _deviceDiscoveryCapability } from '../capabilities/device-discovery.cap.js'\nimport { deviceManagerCapability as _deviceManagerCapability } from '../capabilities/device-manager.cap.js'\nimport { deviceOpsCapability as _deviceOpsCapability } from '../capabilities/device-ops.cap.js'\nimport { deviceProviderCapability as _deviceProviderCapability } from '../capabilities/device-provider.cap.js'\nimport { deviceStateCapability as _deviceStateCapability } from '../capabilities/device-state.cap.js'\nimport { deviceStatusCapability as _deviceStatusCapability } from '../capabilities/device-status.cap.js'\nimport { doorbellCapability as _doorbellCapability } from '../capabilities/doorbell.cap.js'\nimport { embeddingEncoderCapability as _embeddingEncoderCapability } from '../capabilities/embedding-encoder.cap.js'\nimport { eventsCapability as _eventsCapability } from '../capabilities/events.cap.js'\nimport { featureProbeCapability as _featureProbeCapability } from '../capabilities/feature-probe.cap.js'\nimport { integrationsCapability as _integrationsCapability } from '../capabilities/integrations.cap.js'\nimport { intercomCapability as _intercomCapability } from '../capabilities/intercom.cap.js'\nimport { localNetworkCapability as _localNetworkCapability } from '../capabilities/local-network.cap.js'\nimport { logDestinationCapability as _logDestinationCapability } from '../capabilities/log-destination.cap.js'\nimport { meshNetworkCapability as _meshNetworkCapability } from '../capabilities/mesh-network.cap.js'\nimport { meshOrchestratorCapability as _meshOrchestratorCapability } from '../capabilities/mesh-orchestrator.cap.js'\nimport { metricsProviderCapability as _metricsProviderCapability } from '../capabilities/metrics-provider.cap.js'\nimport { motionCapability as _motionCapability } from '../capabilities/motion.cap.js'\nimport { motionDetectionCapability as _motionDetectionCapability } from '../capabilities/motion-detection.cap.js'\nimport { motionTriggerCapability as _motionTriggerCapability } from '../capabilities/motion-trigger.cap.js'\nimport { nativeObjectDetectionCapability as _nativeObjectDetectionCapability } from '../capabilities/native-object-detection.cap.js'\nimport { networkAccessCapability as _networkAccessCapability } from '../capabilities/network-access.cap.js'\nimport { networkQualityCapability as _networkQualityCapability } from '../capabilities/network-quality.cap.js'\nimport { nodesCapability as _nodesCapability } from '../capabilities/nodes.cap.js'\nimport { notificationOutputCapability as _notificationOutputCapability } from '../capabilities/notification-output.cap.js'\nimport { osdCapability as _osdCapability } from '../capabilities/osd.cap.js'\nimport { pipelineAnalyticsCapability as _pipelineAnalyticsCapability } from '../capabilities/pipeline-analytics.cap.js'\nimport { pipelineExecutorCapability as _pipelineExecutorCapability } from '../capabilities/pipeline-executor.cap.js'\nimport { pipelineOrchestratorCapability as _pipelineOrchestratorCapability } from '../capabilities/pipeline-orchestrator.cap.js'\nimport { pipelineRunnerCapability as _pipelineRunnerCapability } from '../capabilities/pipeline-runner.cap.js'\nimport { platformProbeCapability as _platformProbeCapability } from '../capabilities/platform-probe.cap.js'\nimport { ptzCapability as _ptzCapability } from '../capabilities/ptz.cap.js'\nimport { ptzAutotrackCapability as _ptzAutotrackCapability } from '../capabilities/ptz-autotrack.cap.js'\nimport { rebootCapability as _rebootCapability } from '../capabilities/reboot.cap.js'\nimport { recordingCapability as _recordingCapability } from '../capabilities/recording.cap.js'\nimport { recordingEngineCapability as _recordingEngineCapability } from '../capabilities/recording-engine.cap.js'\nimport { remoteAccessCapability as _remoteAccessCapability } from '../capabilities/remote-access.cap.js'\nimport { restreamerCapability as _restreamerCapability } from '../capabilities/restreamer.cap.js'\nimport { settingsStoreCapability as _settingsStoreCapability } from '../capabilities/settings-store.cap.js'\nimport { snapshotCapability as _snapshotCapability } from '../capabilities/snapshot.cap.js'\nimport { snapshotProviderCapability as _snapshotProviderCapability } from '../capabilities/snapshot-provider.cap.js'\nimport { storageCapability as _storageCapability } from '../capabilities/storage.cap.js'\nimport { storageProviderCapability as _storageProviderCapability } from '../capabilities/storage-provider.cap.js'\nimport { streamBrokerCapability as _streamBrokerCapability } from '../capabilities/stream-broker.cap.js'\nimport { streamingEngineCapability as _streamingEngineCapability } from '../capabilities/streaming-engine.cap.js'\nimport { switchCapability as _switchCapability } from '../capabilities/switch.cap.js'\nimport { systemCapability as _systemCapability } from '../capabilities/system.cap.js'\nimport { toastCapability as _toastCapability } from '../capabilities/toast.cap.js'\nimport { turnOrchestratorCapability as _turnOrchestratorCapability } from '../capabilities/turn-orchestrator.cap.js'\nimport { turnProviderCapability as _turnProviderCapability } from '../capabilities/turn-provider.cap.js'\nimport { userManagementCapability as _userManagementCapability } from '../capabilities/user-management.cap.js'\nimport { webrtcCapability as _webrtcCapability } from '../capabilities/webrtc.cap.js'\nimport { webrtcSessionCapability as _webrtcSessionCapability } from '../capabilities/webrtc-session.cap.js'\nimport { zoneAnalyticsCapability as _zoneAnalyticsCapability } from '../capabilities/zone-analytics.cap.js'\nimport { zoneRulesCapability as _zoneRulesCapability } from '../capabilities/zone-rules.cap.js'\nimport { zonesCapability as _zonesCapability } from '../capabilities/zones.cap.js'\n\n/**\n * Every CapabilityDefinition shipped by `@camstack/types`. The hub\n * iterates this array at boot to declare each cap on the registry\n * before addons (in-process or via the Moleculer bridge) attempt\n * `registerProvider`. Adding a new cap means dropping a `*.cap.ts`\n * file in `packages/types/src/capabilities/` and re-running\n * `npx tsx scripts/generate-capability-router-types.ts` — no manual\n * edit to `main.ts` required.\n */\nexport const ALL_CAPABILITY_DEFINITIONS: readonly CapabilityDefinition[] = [\n _accessoriesCapability as CapabilityDefinition,\n _addonPagesCapability as CapabilityDefinition,\n _addonPagesSourceCapability as CapabilityDefinition,\n _addonRoutesCapability as CapabilityDefinition,\n _addonSettingsCapability as CapabilityDefinition,\n _addonWidgetsCapability as CapabilityDefinition,\n _addonWidgetsSourceCapability as CapabilityDefinition,\n _addonsCapability as CapabilityDefinition,\n _adminUiCapability as CapabilityDefinition,\n _advancedNotifierCapability as CapabilityDefinition,\n _alertsCapability as CapabilityDefinition,\n _audioAnalysisCapability as CapabilityDefinition,\n _audioAnalyzerCapability as CapabilityDefinition,\n _audioCodecCapability as CapabilityDefinition,\n _audioMetricsCapability as CapabilityDefinition,\n _authProviderCapability as CapabilityDefinition,\n _authenticationCapability as CapabilityDefinition,\n _backupCapability as CapabilityDefinition,\n _batteryCapability as CapabilityDefinition,\n _brightnessCapability as CapabilityDefinition,\n _cameraCredentialsCapability as CapabilityDefinition,\n _cameraStreamsCapability as CapabilityDefinition,\n _decoderCapability as CapabilityDefinition,\n _detectionPipelineCapability as CapabilityDefinition,\n _deviceDiscoveryCapability as CapabilityDefinition,\n _deviceManagerCapability as CapabilityDefinition,\n _deviceOpsCapability as CapabilityDefinition,\n _deviceProviderCapability as CapabilityDefinition,\n _deviceStateCapability as CapabilityDefinition,\n _deviceStatusCapability as CapabilityDefinition,\n _doorbellCapability as CapabilityDefinition,\n _embeddingEncoderCapability as CapabilityDefinition,\n _eventsCapability as CapabilityDefinition,\n _featureProbeCapability as CapabilityDefinition,\n _integrationsCapability as CapabilityDefinition,\n _intercomCapability as CapabilityDefinition,\n _localNetworkCapability as CapabilityDefinition,\n _logDestinationCapability as CapabilityDefinition,\n _meshNetworkCapability as CapabilityDefinition,\n _meshOrchestratorCapability as CapabilityDefinition,\n _metricsProviderCapability as CapabilityDefinition,\n _motionCapability as CapabilityDefinition,\n _motionDetectionCapability as CapabilityDefinition,\n _motionTriggerCapability as CapabilityDefinition,\n _nativeObjectDetectionCapability as CapabilityDefinition,\n _networkAccessCapability as CapabilityDefinition,\n _networkQualityCapability as CapabilityDefinition,\n _nodesCapability as CapabilityDefinition,\n _notificationOutputCapability as CapabilityDefinition,\n _osdCapability as CapabilityDefinition,\n _pipelineAnalyticsCapability as CapabilityDefinition,\n _pipelineExecutorCapability as CapabilityDefinition,\n _pipelineOrchestratorCapability as CapabilityDefinition,\n _pipelineRunnerCapability as CapabilityDefinition,\n _platformProbeCapability as CapabilityDefinition,\n _ptzCapability as CapabilityDefinition,\n _ptzAutotrackCapability as CapabilityDefinition,\n _rebootCapability as CapabilityDefinition,\n _recordingCapability as CapabilityDefinition,\n _recordingEngineCapability as CapabilityDefinition,\n _remoteAccessCapability as CapabilityDefinition,\n _restreamerCapability as CapabilityDefinition,\n _settingsStoreCapability as CapabilityDefinition,\n _snapshotCapability as CapabilityDefinition,\n _snapshotProviderCapability as CapabilityDefinition,\n _storageCapability as CapabilityDefinition,\n _storageProviderCapability as CapabilityDefinition,\n _streamBrokerCapability as CapabilityDefinition,\n _streamingEngineCapability as CapabilityDefinition,\n _switchCapability as CapabilityDefinition,\n _systemCapability as CapabilityDefinition,\n _toastCapability as CapabilityDefinition,\n _turnOrchestratorCapability as CapabilityDefinition,\n _turnProviderCapability as CapabilityDefinition,\n _userManagementCapability as CapabilityDefinition,\n _webrtcCapability as CapabilityDefinition,\n _webrtcSessionCapability as CapabilityDefinition,\n _zoneAnalyticsCapability as CapabilityDefinition,\n _zoneRulesCapability as CapabilityDefinition,\n _zonesCapability as CapabilityDefinition,\n]\n\n","/* AUTO-GENERATED by scripts/generate-cap-status-types.ts. DO NOT EDIT. */\n/* eslint-disable */\n\nimport type { z } from 'zod'\n\nimport { AccessoriesStatusSchema } from '../capabilities/accessories.cap.js'\nimport { BatteryStatusSchema } from '../capabilities/battery.cap.js'\nimport { BrightnessStatusSchema } from '../capabilities/brightness.cap.js'\nimport { CameraCredentialsStatusSchema } from '../capabilities/camera-credentials.cap.js'\nimport { DeviceDiscoveryStatusSchema } from '../capabilities/device-discovery.cap.js'\nimport { deviceStatusCapability } from '../capabilities/device-status.cap.js'\nimport { DoorbellStatusSchema } from '../capabilities/doorbell.cap.js'\nimport { FeatureProbeStatusSchema } from '../capabilities/feature-probe.cap.js'\nimport { IntercomStatusSchema } from '../capabilities/intercom.cap.js'\nimport { MotionStatusSchema } from '../capabilities/motion.cap.js'\nimport { MotionTriggerStatusSchema } from '../capabilities/motion-trigger.cap.js'\nimport { NativeObjectDetectionStatusSchema } from '../capabilities/native-object-detection.cap.js'\nimport { OsdStatusSchema } from '../capabilities/osd.cap.js'\nimport { ptzCapability } from '../capabilities/ptz.cap.js'\nimport { PtzAutotrackStatusSchema } from '../capabilities/ptz-autotrack.cap.js'\nimport { SnapshotStatusSchema } from '../capabilities/snapshot.cap.js'\nimport { SwitchStatusSchema } from '../capabilities/switch.cap.js'\n\n/**\n * Lookup from cap name (literal) → the TypeScript type of that\n * capability's `status.schema`. Populated at codegen time from every\n * `*.cap.ts` file that declares a `status` block.\n */\nexport type CapStatusTypeMap = {\n readonly 'accessories': z.infer<typeof AccessoriesStatusSchema>\n readonly 'battery': z.infer<typeof BatteryStatusSchema>\n readonly 'brightness': z.infer<typeof BrightnessStatusSchema>\n readonly 'camera-credentials': z.infer<typeof CameraCredentialsStatusSchema>\n readonly 'device-discovery': z.infer<typeof DeviceDiscoveryStatusSchema>\n readonly 'device-status': z.infer<(typeof deviceStatusCapability)['status']['schema']>\n readonly 'doorbell': z.infer<typeof DoorbellStatusSchema>\n readonly 'feature-probe': z.infer<typeof FeatureProbeStatusSchema>\n readonly 'intercom': z.infer<typeof IntercomStatusSchema>\n readonly 'motion': z.infer<typeof MotionStatusSchema>\n readonly 'motion-trigger': z.infer<typeof MotionTriggerStatusSchema>\n readonly 'native-object-detection': z.infer<typeof NativeObjectDetectionStatusSchema>\n readonly 'osd': z.infer<typeof OsdStatusSchema>\n readonly 'ptz': z.infer<(typeof ptzCapability)['status']['schema']>\n readonly 'ptz-autotrack': z.infer<typeof PtzAutotrackStatusSchema>\n readonly 'snapshot': z.infer<typeof SnapshotStatusSchema>\n readonly 'switch': z.infer<typeof SwitchStatusSchema>\n}\n\n/** Union of every cap name that has a typed status block. */\nexport type CapNameWithStatus = keyof CapStatusTypeMap\n\n/**\n * Runtime list of cap names with status. Used by the settings\n * aggregator to enumerate caps whose `status` should be polled +\n * streamed via `subscribeDeviceStatusAggregate`.\n */\nexport const CAP_NAMES_WITH_STATUS = [\n 'accessories',\n 'battery',\n 'brightness',\n 'camera-credentials',\n 'device-discovery',\n 'device-status',\n 'doorbell',\n 'feature-probe',\n 'intercom',\n 'motion',\n 'motion-trigger',\n 'native-object-detection',\n 'osd',\n 'ptz',\n 'ptz-autotrack',\n 'snapshot',\n 'switch',\n] as const satisfies readonly CapNameWithStatus[]\n","/**\n * Strip the `userinfo` (`user:password@`) component from any URL we\n * surface in logs or telemetry. Camera credentials live inside RTSP /\n * RTMP / HTTP URLs by convention; emitting them verbatim leaks\n * plaintext secrets into log files, support transcripts, and shared\n * diagnostics. Returns the original string when the input is not a\n * parseable URL — a log helper must never throw.\n */\nexport function maskUrlCredentials(rawUrl: string): string {\n try {\n const u = new URL(rawUrl)\n if (!u.username && !u.password) return rawUrl\n u.username = ''\n u.password = ''\n return u.toString()\n } catch {\n return rawUrl\n }\n}\n","/**\n * Fixed-capacity ring buffer. When full, push() overwrites the oldest entry.\n * drain() returns up to maxCount items in FIFO order and removes them.\n */\nexport class RingBuffer<T> {\n private readonly items: Array<T | undefined>\n private head = 0\n private tail = 0\n private count = 0\n\n constructor(private readonly capacity: number) {\n this.items = Array.from<T | undefined>({ length: capacity })\n }\n\n get size(): number { return this.count }\n\n push(item: T): void {\n this.items[this.tail] = item\n this.tail = (this.tail + 1) % this.capacity\n if (this.count < this.capacity) {\n this.count++\n } else {\n this.head = (this.head + 1) % this.capacity\n }\n }\n\n drain(maxCount: number): T[] {\n const result: T[] = []\n const n = Math.min(maxCount, this.count)\n for (let i = 0; i < n; i++) {\n result.push(this.items[this.head]!)\n this.items[this.head] = undefined\n this.head = (this.head + 1) % this.capacity\n }\n this.count -= n\n return result\n }\n}\n","import type { z } from 'zod'\nimport type { CustomActionsSpec } from '../capabilities/custom-actions.js'\nimport type { AddonApi } from '../generated/addon-api.js'\n\n/**\n * Bind an addon's custom-action catalog to its tRPC surface, returning a\n * typed object with one method per action. Each method:\n * - Dispatches via `api.addons.custom.mutate({ addonId, action, input })`\n * - Inputs are typed as `z.infer<spec.input>`\n * - Outputs are typed as `z.infer<spec.output>`\n *\n * Example:\n * import { benchmarkActions } from '@camstack/addon-benchmark'\n * const benchmark = bindAddonActions(ctx.api, 'benchmark', benchmarkActions)\n * const { runId } = await benchmark.runBenchmark({ iterations: 100, target: 'cam-01' })\n */\nexport function bindAddonActions<T extends CustomActionsSpec>(\n api: AddonApi,\n addonId: string,\n catalog: T,\n): { [K in keyof T]: (input: z.infer<T[K]['input']>) => Promise<z.infer<T[K]['output']>> } {\n const out: Record<string, (input: unknown) => Promise<unknown>> = {}\n for (const action of Object.keys(catalog)) {\n out[action] = (input: unknown) => {\n const dispatcher = (\n api as unknown as {\n addons: {\n custom: {\n mutate: (args: { addonId: string; action: string; input: unknown }) => Promise<unknown>\n }\n }\n }\n ).addons.custom\n return dispatcher.mutate({ addonId, action, input })\n }\n }\n return out as never\n}\n"],"names":["EventCategory","hydrateSchema","streams","EventSourceType","errMsg","audioMetricsCapability","batteryCapability","brightnessCapability","cameraStreamsCapability","deviceDiscoveryCapability","deviceStatusCapability","doorbellCapability","featureProbeCapability","motionCapability","motionTriggerCapability","ptzAutotrackCapability","switchCapability","zoneAnalyticsCapability","zoneRulesCapability","zonesCapability","deviceProviderCapability","z","field","withTimeout","_accessoriesCapability","_addonPagesCapability","_addonPagesSourceCapability","_addonRoutesCapability","_addonSettingsCapability","_addonWidgetsCapability","_addonWidgetsSourceCapability","_addonsCapability","_adminUiCapability","_advancedNotifierCapability","_alertsCapability","_audioAnalysisCapability","_audioAnalyzerCapability","_audioCodecCapability","_audioMetricsCapability","_authProviderCapability","_authenticationCapability","_backupCapability","_batteryCapability","_brightnessCapability","_cameraCredentialsCapability","_cameraStreamsCapability","_decoderCapability","_detectionPipelineCapability","_deviceDiscoveryCapability","_deviceManagerCapability","_deviceOpsCapability","_deviceProviderCapability","_deviceStateCapability","_deviceStatusCapability","_doorbellCapability","_embeddingEncoderCapability","_eventsCapability","_featureProbeCapability","_integrationsCapability","_intercomCapability","_localNetworkCapability","_logDestinationCapability","_meshNetworkCapability","_meshOrchestratorCapability","_metricsProviderCapability","_motionCapability","_motionDetectionCapability","_motionTriggerCapability","_nativeObjectDetectionCapability","_networkAccessCapability","_networkQualityCapability","_nodesCapability","_notificationOutputCapability","_osdCapability","_pipelineAnalyticsCapability","_pipelineExecutorCapability","_pipelineOrchestratorCapability","_pipelineRunnerCapability","_platformProbeCapability","_ptzCapability","_ptzAutotrackCapability","_rebootCapability","_recordingCapability","_recordingEngineCapability","_remoteAccessCapability","_restreamerCapability","_settingsStoreCapability","_snapshotCapability","_snapshotProviderCapability","_storageCapability","_storageProviderCapability","_streamBrokerCapability","_streamingEngineCapability","_switchCapability","_systemCapability","_toastCapability","_turnOrchestratorCapability","_turnProviderCapability","_userManagementCapability","_webrtcCapability","_webrtcSessionCapability","_zoneAnalyticsCapability","_zoneRulesCapability","_zonesCapability"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/disposer-chain.ts","../src/interfaces/addon.ts","../src/enums/event-category.ts","../src/interfaces/event-bus.ts","../src/addon/base-addon.ts","../src/readiness/readiness-registry.ts","../src/interfaces/storage.ts","../src/interfaces/device-capabilities/camera.ts","../src/interfaces/feature-flags.ts","../src/interfaces/server-analysis.ts","../src/interfaces/analysis-persistence.ts","../src/enums/event-source-type.ts","../src/constants.ts","../src/utils/json-safe.ts","../src/utils/hf-url.ts","../src/utils/cosine-similarity.ts","../src/utils/element-config-store.ts","../src/utils/runtime-mapping.ts","../src/utils/run-inference-step.ts","../src/catalogs/coco-classmap.ts","../src/types/device-type.ts","../src/device/device-config.ts","../src/device/device-runtime-state.ts","../src/device/runtime-state-helpers.ts","../src/generated/device-local-state.ts","../src/device/base-device.ts","../src/device/base-device-provider.ts","../src/device/zod-to-config-ui.ts","../src/device/device-state-handle.ts","../src/generated/device-proxy.ts","../src/utils/zone-rule-eval.ts","../src/generated/system-proxy.ts","../src/device/system-mirror.ts","../src/capabilities/custom-actions.ts","../src/generated/capability-router-map.ts","../src/generated/cap-status-types.ts","../src/utils/mask-url.ts","../src/utils/ring-buffer.ts","../src/helpers/bind-addon-actions.ts"],"sourcesContent":["// =============================================================================\n// DisposerChain — minimal LIFO cleanup registry used to back\n// `AddonContext.addDisposer`.\n//\n// Owned by whoever constructs an AddonContext (the addon host on the\n// hub, the worker host in forks). At shutdown / restart the host calls\n// `dispose()` once; this drains the chain in LIFO order so resources\n// registered later (which may depend on earlier ones) tear down first.\n//\n// Errors thrown by individual disposers are logged via the optional\n// onError callback but never abort the rest of the chain — partial\n// cleanup is always better than half-stopped state on the next restart.\n// =============================================================================\n\nexport type DisposerFn = () => void | Promise<void>\n\nexport interface DisposerChainOptions {\n /** Logged when an individual disposer throws. Defaults to console.error. */\n onError?: (err: unknown, index: number) => void\n}\n\nexport class DisposerChain {\n private disposers: DisposerFn[] = []\n private disposed = false\n private readonly onError: (err: unknown, index: number) => void\n\n constructor(opts: DisposerChainOptions = {}) {\n this.onError =\n opts.onError ??\n ((err, index) => {\n // eslint-disable-next-line no-console\n console.error(`[DisposerChain] disposer #${index} threw`, err)\n })\n }\n\n /**\n * Register a teardown callback. Returns an unregister function so\n * callers can drop a single disposer without disposing the whole\n * chain.\n *\n * If the chain has already been disposed, the callback runs immediately\n * (sync) — this matches the “register-after-shutdown” edge case where\n * an addon's late initialization races with kernel restart.\n */\n add(fn: DisposerFn): () => void {\n if (this.disposed) {\n try {\n const result = fn()\n if (result && typeof (result as Promise<void>).then === 'function') {\n ;(result as Promise<void>).catch((err) => this.onError(err, -1))\n }\n } catch (err) {\n this.onError(err, -1)\n }\n return () => undefined\n }\n\n this.disposers.push(fn)\n return () => {\n const idx = this.disposers.indexOf(fn)\n if (idx >= 0) this.disposers.splice(idx, 1)\n }\n }\n\n /** True after `dispose()` has been called at least once. */\n get isDisposed(): boolean {\n return this.disposed\n }\n\n /** Number of disposers currently registered. */\n get size(): number {\n return this.disposers.length\n }\n\n /**\n * Run every registered disposer in LIFO order. Idempotent: subsequent\n * calls do nothing. Awaits async disposers so callers can sequence\n * shutdown → restart correctly.\n */\n async dispose(): Promise<void> {\n if (this.disposed) return\n this.disposed = true\n // Snapshot + clear so concurrent `add()` from a disposer can't\n // re-enter the same array we're draining.\n const drain = this.disposers.slice().reverse()\n this.disposers = []\n\n for (let i = 0; i < drain.length; i++) {\n const fn = drain[i]!\n try {\n const result = fn()\n if (result && typeof (result as Promise<void>).then === 'function') {\n await result\n }\n } catch (err) {\n this.onError(err, drain.length - 1 - i)\n }\n }\n }\n}\n","import type { PipelineSlot } from '../types/pipeline.js'\nimport type { ConfigUISchemaWithValues, FieldProbeResult } from './config-ui.js'\nimport type { IScopedLogger } from './logging.js'\nimport type { IEventBus, ReadinessScope } from './event-bus.js'\nimport type { ICapabilityHandle } from './readiness.js'\nimport type { CapabilityDeclaration } from './capability.js'\nimport type { ICapabilityRegistry } from './kernel-abstractions.js'\nimport type { CapabilityDefinition, InferProvider } from '../capabilities/capability-definition.js'\nimport type { CustomActionsSpec } from '../capabilities/custom-actions.js'\nimport type { z } from 'zod'\n/** Sub-process info (inlined from deleted worker-protocol.ts) */\nexport interface SubProcessInfo {\n readonly pid: number\n readonly name: string\n readonly command: string\n readonly state: 'running' | 'stopped' | 'crashed'\n readonly cpuPercent: number\n readonly memoryRss: number\n readonly uptimeSeconds: number\n}\n\n/** Worker process stats (inlined from deleted worker-protocol.ts) */\nexport interface WorkerProcessStats {\n readonly pid: number\n readonly cpuPercent: number\n readonly memoryRss: number\n readonly heapUsed?: number\n readonly uptimeSeconds: number\n readonly restartCount: number\n readonly state: 'starting' | 'running' | 'stopping' | 'stopped' | 'crashed'\n}\n\n/** Storage interface for addon file operations */\nexport interface IAddonFileStorage {\n readFile(path: string): Promise<Buffer>\n writeFile(path: string, data: Buffer): Promise<void>\n deleteFile(path: string): Promise<void>\n listFiles(prefix?: string): Promise<readonly string[]>\n exists(path: string): Promise<boolean>\n}\n\nexport interface RequiredStep {\n readonly slot: PipelineSlot\n readonly outputClasses: readonly string[]\n readonly description: string\n}\n\n/**\n * Minimal model-management API exposed to addons via AddonContext.\n */\nexport interface IAddonModelManager {\n /** Ensure a model is downloaded. Returns the local file path. */\n ensure(modelId: string, format?: import('../types/models.js').ModelFormat): Promise<string>\n\n /** Ensure extra files for a model are downloaded. Returns paths. */\n ensureExtraFiles(modelId: string): Promise<readonly string[]>\n\n /** Absolute path to the shared models directory. */\n getModelsDir(): string\n\n /** Check if a model is already downloaded. */\n isDownloaded(modelId: string, format?: import('../types/models.js').ModelFormat): boolean\n\n}\n\n/** Process management for addon sub-processes (ffmpeg, python, etc.) */\nexport interface IAddonProcessManager {\n /** Spawn a child process managed by this addon's worker */\n spawn(config: SubProcessConfig): Promise<IManagedSubProcess>\n /** List all sub-processes spawned by this addon */\n listProcesses(): readonly SubProcessInfo[]\n /** Get worker stats (this addon's worker process) */\n getWorkerStats(): WorkerProcessStats\n}\n\nexport interface SubProcessConfig {\n readonly name: string\n readonly command: string\n readonly args?: readonly string[]\n readonly cwd?: string\n readonly env?: Readonly<Record<string, string>>\n readonly autoRestart?: boolean\n readonly maxRestarts?: number\n}\n\nexport interface IManagedSubProcess {\n readonly pid: number\n readonly name: string\n getStats(): WorkerProcessStats\n write(data: Buffer): void\n readonly stdout: AsyncIterable<Buffer>\n readonly stderr: AsyncIterable<Buffer>\n kill(signal?: NodeJS.Signals): void\n wait(): Promise<{ code: number | null; signal: string | null }>\n onExit(handler: (code: number | null) => void): void\n onError(handler: (error: Error) => void): void\n}\n\n/** Dependency manager — ensures binaries/tools are available for addons */\nexport interface IAddonDepsManager {\n /** Ensure a named binary is available (check local -> PATH -> download) */\n ensureBinary(opts: {\n readonly name: string\n readonly downloadUrl: string\n readonly isArchive?: boolean\n readonly archiveFormat?: 'zip' | 'tar.gz' | 'tar.xz'\n readonly archiveInnerPath?: string\n }): Promise<string>\n\n /** Ensure ffmpeg is available */\n ensureFfmpeg(): Promise<string>\n\n /** Ensure Python is available, returns path or null if unavailable */\n ensurePython(): Promise<string | null>\n\n /** Install Python packages in the managed Python environment */\n installPythonPackages(packages: readonly string[]): Promise<void>\n\n /**\n * Install a pip requirements file into the embedded Python.\n * Idempotent: keyed on (file basename + sha256 of contents). Marker\n * lives under the python install dir so re-downloading python wipes\n * everything together.\n */\n installPythonRequirements(requirementsFile: string): Promise<void>\n\n /** Get the deps directory path */\n getDepsDir(): string\n}\n\n/**\n * AddonContext -- injected into every addon at initialize().\n *\n * The context includes both the CamstackContext fields (id, logger, eventBus, storage, config)\n * and addon-specific extras (addonConfig, models, locationPaths, router, network).\n */\n/**\n * AddonContext -- injected into every addon at initialize().\n *\n * The `api` field is typed as `AddonApi` — a generated interface that\n * mirrors the server's tRPC router namespaces. This gives addons type-safe\n * access to server procedures without depending on the server package.\n *\n * To regenerate after changing routers: npx tsx scripts/generate-api-types.ts\n */\n// ── Provider registration (return from initialize) ──────────────────────\n/**\n * A capability provider binding returned from `ICamstackAddon.initialize()`.\n * The kernel registers each entry in the CapabilityRegistry after init completes.\n */\nexport interface ProviderRegistration<TCap extends CapabilityDefinition = CapabilityDefinition> {\n /** Capability definition this provider implements. Read `capability.name` to look up by name. */\n readonly capability: TCap\n /**\n * The provider object implementing the capability interface.\n *\n * - With a specific `TCap` (e.g. `ProviderRegistration<typeof metricsProviderCapability>`)\n * the provider is strictly checked against `InferProvider<TCap>`.\n * - With the default `TCap = CapabilityDefinition` (used by heterogeneous arrays like\n * `ProviderRegistration[]` returned from `onInitialize()`) the provider is typed as\n * `object`: a mapped type over the default `Record<string, CapabilityMethodSchema>`\n * collapses to an index signature that class instances cannot satisfy structurally.\n * The runtime capability router enforces the contract; the strict compile-time check\n * is applied at the specific-cap boundary (ProviderRegistration<typeof xxxCap>) instead.\n */\n readonly provider: CapabilityDefinition extends TCap ? object : InferProvider<TCap>\n /** 'native' (default) — absolute provider.\n * 'wrapper' — user-toggleable decorator that delegates to a native via `ctx.getNativeProvider`. */\n readonly kind?: 'native' | 'wrapper'\n /** Wrapper only — active by default for every compatible device. */\n readonly defaultActive?: boolean\n}\n\n/**\n * Return value of `ICamstackAddon.initialize()`. Both sections are optional —\n * addons that provide only capabilities omit `customActions`; addons that only\n * expose actions omit `providers`; pure consumers may return void.\n */\nexport interface AddonInitResult<TCatalog extends CustomActionsSpec = CustomActionsSpec> {\n readonly providers?: readonly ProviderRegistration[]\n readonly customActions?: TCatalog\n /**\n * Required iff `customActions` is set. One async handler per catalog entry —\n * a typed dispatch map rather than a single generic function so that each\n * handler's input/output types collapse to the concrete `z.infer<...>` of\n * its schema at the provider site (TypeScript cannot unify the output of a\n * generic `<K>(action: K, input) => Promise<...>` signature with a concrete\n * per-action return type, so the map form is strictly more typeable).\n */\n readonly actionHandlers?: {\n readonly [K in keyof TCatalog & string]: (\n input: z.infer<TCatalog[K]['input']>,\n ) => Promise<z.infer<TCatalog[K]['output']>>\n }\n}\n\n// ── Kernel services ─────────────────────────────────────────────────────\n/**\n * Infrastructure services injected by the kernel at boot time.\n * Grouped under `ctx.kernel` to separate kernel plumbing from\n * addon-level concerns (logger, settings, devices, api).\n */\nexport interface IKernelServices {\n /**\n * The Moleculer nodeID for this process (e.g. `'hub'` on the hub, or the\n * agent's own name on remote nodes). Addons use this to distinguish local\n * dispatch from remote dispatch without a direct Moleculer dependency.\n */\n readonly localNodeId?: string\n\n /**\n * Local storage provider — direct filesystem access with zero serialization.\n * Use for file I/O (write/read with Buffer data) that can't go through tRPC.\n * Always co-located: storage is a kernel-level service injected at boot.\n */\n readonly storage?: import('./storage.js').IStorageProvider\n\n /**\n * Per-process system-readiness tracker. One `ReadinessRegistry` is\n * created per broker by the kernel at boot and shared across every\n * addon on that broker — consumers that need `awaitReady` /\n * `onReadyState` should use this singleton rather than constructing\n * their own so subscriptions on the event bus stay de-duplicated and\n * the snapshot is consistent across the process.\n *\n * Tests that run without a real kernel (no `ctx.kernel`) should\n * either inject a mock or fall back to creating a local\n * `ReadinessRegistry`; the production code in kernel always\n * populates this field.\n */\n readonly readinessRegistry?: import('../capabilities/index.js').ITypedReadinessRegistry\n\n /**\n * Global device registry — read + mutate devices across all addons.\n * Hub-only: workers don't have a local registry (use `ctx.devices`\n * for per-addon scoped management, or `ctx.api.deviceManager.*` for\n * cross-process access).\n */\n readonly deviceRegistry?: import('../device/device-context.js').IDeviceRegistry\n\n /**\n * Per-addon scoped device management — create, register, remove, list devices.\n * Used by device provider addons (rtsp, onvif, frigate) to manage cameras.\n */\n readonly devices: import('../device/device-context.js').DeviceManagerApi\n\n /**\n * Cluster runtime — exposes the underlying message broker for addons that\n * need to participate in cluster-wide RPC outside the capability/tRPC\n * surface (e.g. log forwarding, hub readiness probes).\n *\n * Optional: only the agent and hub processes populate this. Forked workers\n * receive their broker through their own runner harness — they do not see\n * `cluster.broker` on ctx.kernel.\n */\n readonly cluster?: {\n readonly broker: IClusterBroker\n }\n\n /**\n * Kernel stream-probe bundle. Both entrypoints land on the same\n * hub-side `StreamProbeService` (ffprobe + HTTP reachability check\n * with a 1h cache). Hub resolves them directly; forked workers +\n * remote agents route through the `$stream-probe.*` Moleculer\n * broker service. Providers that call\n * `ctx.kernel.streamProbe.probe(url)` don't need to know where\n * they run.\n */\n readonly streamProbe?: IKernelStreamProbe\n\n /**\n * Kernel hardware-accel resolver. Platform-probe that picks an\n * ordered preference list of hw decode backends for the **local\n * host**. Every node (hub + forked worker + remote agent) runs its\n * own probe because hwaccel is inherently hardware-bound: decoder\n * addons on an NVIDIA agent get `['cuda', 'nvdec']`, the same addon\n * on a Mac hub gets `['videotoolbox']`. Cross-node queries for the\n * admin UI go via the `$hwaccel.resolve` Moleculer service\n * registered on every node.\n */\n readonly hwaccel?: IKernelHwAccel\n\n /**\n * Capability registry — provider lookups, native-cap resolution, wrapper listing.\n * Hub-only: forked workers don't see this (they use `ctx.api` for cross-process calls).\n */\n readonly capabilityRegistry?: ICapabilityRegistry\n}\n\n/**\n * Canonical hwaccel backend names. Accepted by both the `ffmpeg` CLI\n * (`-hwaccel <name>`) and node-av `HardwareDeviceContext.create()`\n * (`AV_HWDEVICE_TYPE_<upper>`), so a single string drives both layers.\n */\nexport type HwAccelBackend =\n | 'videotoolbox'\n | 'cuda'\n | 'nvdec'\n | 'vaapi'\n | 'qsv'\n | 'd3d11va'\n | 'dxva2'\n | 'amf'\n | 'vdpau'\n | 'drm'\n\n/** Resolution output — preference list + rationale for observability. */\nexport interface HwAccelResolution {\n readonly preferred: readonly HwAccelBackend[]\n readonly rationale: string\n}\n\nexport interface IKernelHwAccel {\n /**\n * Probe the local host and return the preferred backends.\n *\n * `prefer` accepts:\n * - `null` / `undefined` → auto, use platform probe\n * - `'none'` → hwaccel disabled; returns empty list\n * - an explicit backend name → single-entry list (skip probe)\n *\n * Implementations are pure functions over the host — safe to call\n * repeatedly; results are deterministic per host.\n */\n readonly resolve: (prefer?: HwAccelBackend | 'none' | null) => Promise<HwAccelResolution>\n}\n\nexport interface IKernelStreamProbe {\n /**\n * Run ffprobe on an RTSP / HTTP stream URL and return the raw\n * metadata. Use this when you need the width/height/codec/fps data\n * for profile classification. `classifyStream` / `classifyStreams`\n * in `@camstack/types` turn the metadata into a `StreamQuality`.\n */\n readonly probe: (url: string, options?: { force?: boolean }) => Promise<import('./device-capabilities/camera.js').StreamMetadata>\n /**\n * Generic field probe dispatcher — keys starting with `stream` run\n * ffprobe, others run an HTTP GET that aborts at response headers.\n * Returns a `FieldProbeResult` suitable for the admin UI's \"Test\"\n * button on any `probe`-typed form field.\n */\n readonly probeField: (key: string, value: unknown) => Promise<FieldProbeResult>\n}\n\n/**\n * Minimal structural shape of a cluster-message-broker (Moleculer in the\n * current implementation). Captured in @camstack/types so cluster-aware\n * addons can consume it without pulling a moleculer dependency.\n *\n * Add new methods sparingly — every addition becomes a contract the broker\n * implementation must satisfy.\n */\nexport interface IClusterBroker {\n readonly nodeID: string\n call<TParams = unknown, TResult = unknown>(\n action: string,\n params?: TParams,\n opts?: { nodeID?: string; timeout?: number },\n ): Promise<TResult>\n waitForServices(service: string | readonly string[], timeout?: number): Promise<void>\n readonly localBus: {\n on(event: string, handler: (payload: { node: { id: string } }) => void): void\n }\n}\n\nexport interface AddonContext<TConfig = Record<string, unknown>> {\n /** Immutable progressive ID */\n readonly id: string\n /** Pre-scoped logger */\n readonly logger: IScopedLogger\n /** System event bus */\n readonly eventBus: IEventBus\n /** Bootstrap configuration from server settings (read-only) */\n readonly addonConfig: Readonly<TConfig>\n /**\n * Private data directory for this addon.\n * Resolved via storageProvider.resolve('addons-data', '{addonId}/').\n * The addon owns this directory exclusively.\n */\n readonly dataDir: string\n\n // --- Kernel services ---\n\n /** Infrastructure services provided by the kernel. */\n readonly kernel: IKernelServices\n\n // --- Settings ---\n\n /**\n * Capability settings API — 3-level resolution (defaults → global → per-device).\n * Replaces context.config (IElementConfig) and context.addonConfig.\n */\n /**\n * Three-level settings store API.\n *\n * Raw, non-merging accessors for the addon's `addon_settings` row and\n * per-device overrides, plus a `(getSection, setSection)` pair for the\n * cluster-wide yml-backed sections consumed by the built-in\n * `system-config` addon.\n *\n * The addon combines these raw reads with its own schema via\n * `hydrateSchema()` inside `getAddonSettings / getGlobalSettings /\n * getDeviceSettings` on `ICamstackAddon`. There is no defaults\n * injection or cross-level merge at this layer.\n *\n * Cleanup note: the earlier `getGlobal / getDevice / updateGlobal /\n * updateDevice` trio (with a polymorphic `getGlobal({ section })`\n * overload) was removed after every caller migrated. The only\n * consumer of the yml-section path was `system-config`, which now\n * goes through the explicit `getSection / setSection` pair below.\n */\n readonly settings?: {\n /**\n * Read the raw addon store for this addon. Holds both `addon`-level\n * and `global`-level values; they're disjoint by schema invariant so\n * they can share one physical store. No defaults merged in. Returns\n * an empty object if the addon has never written anything.\n */\n readAddonStore(): Promise<Record<string, unknown>>\n\n /**\n * Write a patch into the addon store. Keys not in the patch are\n * preserved. Keys explicitly set to `null` are preserved as-is (they\n * do NOT fall back to schema default on subsequent reads — that\n * layering happens only at `hydrateSchema` time).\n */\n writeAddonStore(patch: Record<string, unknown>): Promise<void>\n\n /**\n * Read the raw per-device store for this addon + device. No defaults,\n * no merge with the addon store. Returns `{}` when no overrides exist.\n */\n readDeviceStore(deviceId: number): Promise<Record<string, unknown>>\n\n /**\n * Write a patch into the per-device store. Does NOT touch the\n * addon-level store. Keys preserved as in `writeAddonStore`.\n */\n writeDeviceStore(deviceId: number, patch: Record<string, unknown>): Promise<void>\n\n /**\n * Delete all keys for a device from the per-device store.\n * Use on device removal to guarantee no orphaned settings rows remain.\n */\n clearDeviceStore(deviceId: number): Promise<void>\n\n /**\n * Read a device's runtime-state blob — separate namespace from\n * `readDeviceStore` (which is the addon's config blob). Holds\n * mutable runtime signals discovered after boot (battery\n * snapshot, sleep flag, last-seen) that survive restart. Stored\n * under the kernel's reserved `__device-state` namespace so the\n * addon's config schema never sees it.\n */\n readDeviceRuntimeState(deviceId: number): Promise<Record<string, unknown>>\n\n /**\n * Replace the runtime-state blob for a device. The kernel's\n * `DeviceRuntimeState` always hands over the FULL persistent\n * slice — no shallow merge here, the latest blob wins.\n */\n writeDeviceRuntimeState(deviceId: number, data: Record<string, unknown>): Promise<void>\n\n /**\n * Delete the device's runtime-state blob. Use on device removal\n * so a recreated device with the same id starts clean instead\n * of inheriting stale runtime state from the previous lifetime.\n */\n clearDeviceRuntimeState(deviceId: number): Promise<void>\n\n /**\n * Read a cluster-wide yml-backed section (server, auth, ffmpeg,\n * logging, recording, retention — whatever\n * `ConfigManager.getSection` recognises). Returns the raw section\n * record with no schema merging. Used by the built-in\n * `system-config` addon to surface the legacy `config.yaml`\n * sections through its `getGlobalSettings()` method.\n */\n getSection(section: string): Promise<Record<string, unknown>>\n\n /**\n * Write a cluster-wide yml-backed section. Shallow-merges the patch\n * into the existing section so unrelated keys are preserved.\n * Hub-only — agents have no concept of yml sections and will\n * throw. Used by the `system-config` addon's\n * `updateGlobalSettings()` implementation.\n */\n setSection(section: string, patch: Record<string, unknown>): Promise<void>\n }\n\n /**\n * Full tRPC client — same API as the frontend.\n * Transport is transparent: direct caller (in-process), WSS (worker), or WSS (remote agent).\n * Use this for all server interactions: storage, events, devices, streaming, etc.\n */\n readonly api: import('../generated/addon-api.js').AddonApi\n\n /**\n * Binary / tool dependency manager. Addons call\n * `ctx.deps.ensureBinary(...)` / `ensureFfmpeg()` / `ensurePython()` to\n * materialise per-addon runtime deps under the shared `data/deps` tree.\n * Injected by the addon host so addons never import the concrete\n * implementation from `@camstack/core` directly.\n */\n readonly deps: IAddonDepsManager\n\n /**\n * Typed handle to the native provider of `cap` for `deviceId`. Bypasses the\n * currently-active wrapper — call this ONLY from the wrapper that owns\n * `cap`. Every other consumer should use `fetchDevice(id).<cap>.<method>()`\n * instead, which routes through the cap-router and respects the wrapper\n * chain.\n *\n * Wrapper-vs-consumer rule (memorise this):\n * - \"I am the wrapper for this cap and I need to delegate to the camera\n * driver\" → `getNativeProvider(cap, id)` (bypasses self, no recursion).\n * - \"I am consuming someone else's cap\" → `fetchDevice(id).<cap>...`\n * (uses the wrapper chain — cache, fallback, validation, etc.).\n *\n * Calling `fetchDevice` from inside the cap's own wrapper would re-enter\n * the wrapper and produce infinite recursion through the cap-router.\n *\n * Returns `null` when no addon has published a native provider for this\n * `(cap, deviceId)` — e.g. a camera driver that doesn't implement the cap\n * natively (RtspCamera without `snapshotUrl` does not register snapshot).\n * Wrappers use this to decide between native delegation and their own\n * fallback strategy; they MUST check for null.\n *\n * When the native is registered in this process, returns the local object\n * directly (zero serialization). When it lives in a forked worker, returns\n * a Moleculer-backed proxy constructed by the hub's native-cap fallback\n * (see `CapabilityRegistry.setNativeFallback`) — standard\n * `<addonId>.native-provider.<capName>.<method>` RPC path.\n */\n getNativeProvider<TCap extends CapabilityDefinition>(\n cap: TCap,\n deviceId: number,\n ): InferProvider<TCap> | null\n\n /**\n * Fetch a typed proxy for a device. THE canonical entry point for any\n * consumer that wants to read state, subscribe to changes, or dispatch\n * cap methods on a device — equivalent to `BackendClient.fetchDevice(id)`\n * on the client side.\n *\n * What you can do with the returned proxy:\n * - Read live state slices: `dev.state.battery.value` (typed\n * `BatteryStatus | undefined`, no provider call — reads the kernel's\n * mirrored snapshot).\n * - Subscribe to slice changes: `dev.state.battery.subscribe(cb)` —\n * refcounted, auto-seeds the callback with the current value.\n * - Invoke cap methods through the wrapper chain:\n * `await dev.snapshot.getSnapshot({...})`. Methods are typed against\n * each cap's definition; absent caps appear as `undefined`.\n *\n * Routing semantics: every method call on the returned proxy goes through\n * the tRPC cap-router → the active wrapper (if any) → the native provider.\n * That means a wrapper consuming its OWN cap via `fetchDevice` would\n * recurse — wrappers must use `getNativeProvider` instead. See the rule\n * documented on `getNativeProvider` above.\n *\n * Bindings are cached per-addon-context: the first call resolves bindings\n * via `deviceManager.getBindings.query`, subsequent calls reuse the cache.\n * The cache is invalidated implicitly when the device is removed.\n */\n fetchDevice(deviceId: number): Promise<import('../generated/device-proxy.js').DeviceProxy>\n\n /**\n * Runtime accessor for the capability registry — exposed to addons that\n * need to enumerate peers of a collection cap (e.g. `turn-provider`) or\n * grab the active singleton for a system cap. Injected by the kernel at\n * addon instantiation; absent when the addon is loaded in isolation\n * (tests, tooling), which is why the field is optional.\n */\n readonly capabilities?: CapabilitiesAccess\n\n /**\n * Returns a capability handle immediately (non-blocking). The handle tracks\n * ready/down state via the ReadinessRegistry and gates calls accordingly.\n * Repeated calls with the same `(capName, scope)` return the same cached instance.\n *\n * Use for runtime cap consumption where the cap may not be ready yet and\n * you want calls to block transparently via `handle.call(fn)`.\n */\n useCapability<T = unknown>(capName: string, scope?: ReadinessScope): ICapabilityHandle<T>\n\n /**\n * Blocks until the capability is ready, then returns the cached handle.\n * Throws `CapabilityUnavailableError` if the cap does not become ready\n * within `timeoutMs` (default 15 000ms).\n *\n * Use for boot sequencing where downstream work must not start until\n * the cap is confirmed available.\n */\n acquireCapability<T = unknown>(\n capName: string,\n scope?: ReadinessScope,\n opts?: { timeoutMs?: number },\n ): Promise<ICapabilityHandle<T>>\n\n /**\n * Subscribe to capability state changes (ready / down).\n * Returns an unsubscribe function. Thin wrapper over `ReadinessRegistry.onReadyState`.\n */\n onCapabilityStateChange(\n capName: string,\n scope: ReadinessScope,\n handler: (state: 'ready' | 'down') => void,\n ): () => void\n\n /**\n * Register a teardown callback that runs automatically when this\n * addon is shut down or restarted (e.g. by `restartAddon` after a\n * runtime update from the admin UI). Disposers run in LIFO order so\n * later registrations clean up before the resources they depend on.\n *\n * Use this for any side effect that survives across function calls\n * — `setInterval`, native socket listeners, watchers, file handles.\n * Capability/event subscriptions returned by `onCapabilityStateChange`\n * / `eventBus.subscribe` are unsubscribe functions you can pass\n * straight to `addDisposer`. Returns a manual unsubscribe so the\n * addon can drop the disposer early if needed.\n *\n * const stopTimer = setInterval(...);\n * ctx.addDisposer(() => clearInterval(stopTimer));\n *\n * const unsub = ctx.eventBus.subscribe('motion', cb);\n * ctx.addDisposer(unsub);\n *\n * The kernel calls each disposer once during `addon.shutdown()`. A\n * disposer that throws is logged and skipped — subsequent disposers\n * still run.\n */\n addDisposer(fn: () => void | Promise<void>): () => void\n}\n\n/**\n * Runtime-only capability registry surface — kept minimal on purpose.\n * `getCollection` returns every registered provider for a collection cap\n * (order-unstable); `get` returns the active provider for a singleton.\n * Both return `undefined` when the cap isn't declared or has no provider.\n */\nexport interface CapabilitiesAccess {\n /** All providers registered on a collection capability (by cap name). */\n getCollection<T = unknown>(name: string): readonly T[] | undefined\n /**\n * Like `getCollection` but returns `[addonId, provider]` tuples so\n * callers can attribute work back to the contributing addon. The\n * `addonId` for cap-routed remote providers is `<addonName>@<workerNodeId>`\n * (e.g. `decoder-nodeav@dev-agent-1/decoder-nodeav`); local providers\n * are just `<addonName>` (no `@`). Used by stream-broker to filter the\n * decoder collection by the orchestrator's per-camera placement\n * decision (`getDecoderAssignment.decoderNodeId`).\n */\n getCollectionEntries<T = unknown>(name: string): readonly (readonly [string, T])[] | undefined\n /** The active provider of a singleton capability (by cap name). */\n get<T = unknown>(name: string): T | undefined\n}\n\nexport interface ICamstackAddon {\n readonly id?: string\n /** Addon declaration — injected by the framework from package.json. Do not declare in addon class. */\n readonly manifest?: AddonDeclaration\n /**\n * Initialize the addon. Return capability provider bindings for the kernel\n * to register in the CapabilityRegistry. Returning `void` or an empty array\n * is valid for addons that don't provide capabilities (e.g. pure consumers).\n *\n * The kernel validates that every capability declared in `package.json`\n * has a corresponding entry in the returned array.\n *\n * @example\n * ```ts\n * async initialize(ctx: AddonContext): Promise<AddonInitResult> {\n * this.detector = new WasmMotionDetector()\n * await this.detector.load()\n * return { providers: [{ capability: motionDetectionCapability, provider: this }] }\n * }\n * ```\n */\n initialize(context: AddonContext): Promise<ProviderRegistration[] | AddonInitResult | void>\n /**\n * Called by the isolated-process runner after `broker.start()`.\n * In-process addons never need this — the hub broker is already running\n * when `initialize()` fires. Forked children emit readiness inside\n * `initialize()` before the broker is live; this re-emits it once the\n * transport is ready so consumers actually receive the event.\n */\n postBrokerStart(): void\n /**\n * Optional hook. Fires on forked workers / remote agents once the\n * broker has started AND the hub node has been discovered via the\n * Moleculer `$node.connected` event — the moment at which\n * `ctx.api.*` calls to hub-provided capabilities become safe.\n *\n * Use this instead of `initialize()` for any bootstrap work that\n * needs to query the hub (e.g. restoring persisted state, reading\n * hub-owned settings). Calling `ctx.api.*` from `initialize()` on a\n * worker is a documented foot-gun: the broker is not connected yet\n * and requests either time out or throw \"Service not found\".\n *\n * On the hub itself this never fires (the hub is its own node; there\n * is no separate hub to wait for).\n */\n onHubReachable?(): Promise<void> | void\n shutdown(): Promise<void>\n\n // Cleanup: the legacy `getConfigSchema / getConfig / onConfigChange`\n // trio was removed. Every addon implements the three-level settings\n // API below and nothing on the server reads the legacy shape any\n // more — the adapter shim in `addon-registry.service.ts` and the\n // `addons.getConfigSchema / getConfig / updateConfig` tRPC endpoints\n // are gone.\n\n // -- Three-level settings API --\n //\n // An addon implements only the levels it needs. Each level has a\n // disjoint schema — field keys must not collide across levels. Each\n // getter returns a hydrated `ConfigUISchemaWithValues` (schema + values\n // inline) so the admin UI can render in one pass.\n //\n // Level 1 — addon settings: pipeline/context-specific fields, rendered\n // by whatever page knows the addon belongs to it (e.g. the\n // Pipeline page hardcodes PIPELINE_CAPABILITIES and iterates\n // providers).\n // Level 2 — node-global settings: node-level fields shown in Cluster →\n // NodeDetailPanel → Settings. Dynamic discovery: an addon\n // that doesn't implement `getGlobalSettings` doesn't appear\n // there.\n // Level 3 — device settings: per-camera fields shown in Device\n // Overrides.\n //\n // All six methods are OPTIONAL. They will become the canonical way\n // addons expose settings once Phase 3 removes the deprecated trio\n // above. See docs/superpowers/specs/2026-04-11-settings-redesign-design.md.\n\n /** Level 1 — node-global settings (schema + values). Appears in\n * Cluster → Settings tab. `overlay` is an optional preview merge\n * consumed by override-mode UIs (benchmark) so cascade-aware\n * addons can re-derive dependent options without touching the\n * persisted store. */\n getGlobalSettings?(overlay?: Record<string, unknown>): Promise<ConfigUISchemaWithValues>\n updateGlobalSettings?(patch: Record<string, unknown>): Promise<void>\n\n /** Level 2 — per-device settings (schema + values). Appears in\n * Device Overrides. */\n getDeviceSettings?(deviceId: number): Promise<ConfigUISchemaWithValues>\n updateDeviceSettings?(deviceId: number, patch: Record<string, unknown>): Promise<void>\n\n /**\n * Called at boot after initialize() with all previously persisted devices.\n * The addon should re-instantiate device objects and register them via context.devices.register().\n */\n restoreDevices?(savedDevices: readonly import('../device/device-management.js').SavedDevice[]): Promise<void>\n\n // -- Model catalog (optional — only for inference addons) --\n getModelCatalog?(): unknown[]\n // `getModelRequirements` and `configure` were removed — inference addons\n // now resolve their own config inside `initialize()` by calling\n // `ctx.api.platformProbe.resolveInferenceConfig.query({ requirements })`.\n\n // -- Inference methods (optional — only for detection/classification addons) --\n\n /** Load/prepare a model engine for a given modelId. Called before process()/classifyAudio(). */\n ensureEngine?(modelId: string): Promise<void>\n\n /** Unified vision inference — handles detection, cropping, classification, refinement.\n * Input is FrameInput (root) or CropInput (child node). Output shape determines slot. */\n process?(input: unknown): Promise<unknown>\n\n /** Audio classification — separate from vision pipeline */\n classifyAudio?(input: unknown): Promise<unknown>\n}\n\n/** Declaration of a UI page provided by an addon */\nexport interface AddonPageDeclaration {\n /** Unique page ID (scoped to addon) */\n readonly id: string\n /** Display label for sidebar */\n readonly label: string\n /** Lucide icon name for sidebar */\n readonly icon: string\n /** Route path (e.g., '/benchmark') */\n readonly path: string\n /**\n * Module Federation remote name — must match the `name` field on the\n * page addon's `federation()` plugin config. Used by admin-ui's\n * `<AddonPageLoader>` to call `loadRemote('<remoteName>/page')`.\n * Conventionally `addon_<id>_page` (snake_case; MF names cannot\n * contain hyphens).\n */\n readonly remoteName: string\n /**\n * Bundle filename inside the addon's `dist/` dir served at\n * `/api/addon-pages/<addonId>/<bundle>`. With Module Federation this\n * is always `'remoteEntry.js'`.\n */\n readonly bundle: string\n}\n\n/** Provider interface for addons that expose UI pages */\nexport interface IAddonPageProvider {\n readonly id: string\n listPages(): readonly AddonPageDeclaration[]\n}\n\n/** Provider interface for the admin UI shell (singleton capability) */\nexport interface IAdminUI {\n getStaticDir(): string\n getVersion(): string\n}\n\n/**\n * Full addon declaration — lives in package.json `camstack.addons[]`.\n * This is the SINGLE SOURCE OF TRUTH for addon identity and metadata.\n * Addon classes no longer declare a `manifest` property.\n */\nexport interface AddonDeclaration {\n // ── Identity ────────────────────────────────────────────────────────\n readonly id: string\n /** JS entry point relative to package root (e.g., \"./dist/addon.js\") */\n readonly entry?: string\n /** Human-readable name */\n readonly name?: string\n /** Semver version */\n readonly version?: string\n /** Short description */\n readonly description?: string\n\n // ── Display ────────────────────────────────────────────────────────\n /** Relative path to SVG icon asset within the addon package */\n readonly icon?: string\n /** Brand hex color for UI display (e.g., \"#3b82f6\") */\n readonly color?: string\n\n // ── Runtime ────────────────────────────────────────────────────────\n readonly slot?: PipelineSlot | null\n /** Instance mode: 'unique' or 'multiple'. Default: 'multiple' */\n readonly instanceMode?: 'unique' | 'multiple'\n /** Whether this addon is protected (required by the system, cannot be uninstalled) */\n readonly protected?: boolean\n /**\n * Group-runner execution model.\n *\n * Every addon lives in a forked subprocess; the question is which one.\n * Addons sharing the same `group` value share one subprocess and one\n * Moleculer broker, so cap calls between them resolve in-process via\n * `localProviderLink` (zero IPC). Different groups speak via Moleculer\n * TCP.\n *\n * Defaults (when `execution` is omitted entirely):\n * - `placement: 'hub-only'` — addon does NOT deploy to agents.\n * - `group: 'main-group'` — joins the catch-all subprocess.\n *\n * Examples:\n * ```jsonc\n * // hub-only addon, joins main-group (most common)\n * \"execution\": {}\n *\n * // deployable to any node (cluster scenario)\n * \"execution\": { \"placement\": \"any-node\" }\n *\n * // bundle detection on a dedicated subprocess\n * \"execution\": { \"placement\": \"any-node\", \"group\": \"detection\" }\n *\n * // hardware-bound, only runs on agents (no hub instance)\n * \"execution\": { \"placement\": \"agent-only\" }\n *\n * // crash-isolated (private subprocess; unique group per addon)\n * \"execution\": { \"group\": \"untrusted-foo\" }\n * ```\n */\n readonly execution?: AddonExecution\n\n // ── Capabilities ───────────────────────────────────────────────────\n /** Capabilities this addon provides */\n readonly capabilities?: readonly CapabilityDeclaration[]\n /** UI pages provided by this addon */\n readonly pages?: readonly AddonPageDeclaration[]\n\n // ── Pipeline-specific ──────────────────────────────────────────────\n readonly requiredFeatures?: readonly string[]\n readonly inputClasses?: readonly string[]\n readonly outputClasses?: readonly string[]\n readonly requiredSteps?: readonly RequiredStep[]\n readonly supportsCustomModels?: boolean\n readonly mayRequirePython?: boolean\n /** Whether this addon performs active inference. Passive addons are excluded from inference pipeline. Default: true. */\n readonly passive?: boolean\n /**\n * Label output type for classifier/recognizer addons.\n *\n * `classification` — generic class name (e.g. `'bird'`, `'labrador'`)\n * `face` — recognised face identity\n * `plate` — recognised licence plate OCR\n * `recognition` — generic recognition result (embedding gallery)\n */\n readonly labelOutputType?: 'classification' | 'face' | 'plate' | 'recognition'\n\n // ── Python (auto-managed deps) ─────────────────────────────────────\n /**\n * Optional Python runtime dependencies. Declares the pip requirements\n * file the kernel must install into the embedded portable Python\n * before this addon's `onInitialize()` runs. Resolved relative to the\n * addon's package root.\n *\n * Idempotent: install is keyed on (file basename + sha256 of contents)\n * via the marker dir under the python install — back-to-back boots\n * skip the pip subprocess after the first install.\n *\n * Example:\n * ```jsonc\n * \"camstack\": {\n * \"addons\": [{\n * \"id\": \"detection-pipeline\",\n * \"python\": { \"requirements\": \"./python/requirements.txt\" }\n * }]\n * }\n * ```\n *\n * Backend / feature-specific deps that depend on user settings should\n * NOT be listed here — addons install those lazily via\n * `ctx.deps.installPythonRequirements(absolutePath)`.\n */\n readonly python?: AddonPythonRequirements\n\n // ── Config ─────────────────────────────────────────────────────────\n /** Default configuration values */\n readonly defaultConfig?: Readonly<Record<string, unknown>>\n}\n\n/**\n * Manifest declaration for an addon's Python runtime dependencies.\n * See `AddonDeclaration.python`.\n */\nexport interface AddonPythonRequirements {\n /**\n * Path to the pip requirements file the kernel installs into the\n * embedded portable Python before `onInitialize()` runs. Resolved\n * relative to the addon package root.\n */\n readonly requirements: string\n}\n\nexport interface AddonPackageManifest {\n /** Human-readable package name for UI display (e.g., \"CamStack Vision\") */\n readonly displayName?: string\n readonly addons: readonly AddonDeclaration[]\n /**\n * Optional bundle metadata for npm packages that ship multiple addon\n * entries (e.g., `@camstack/addon-pipeline` with 7 entries,\n * `@camstack/addon-remote-storage` with 3 entries). When set, the\n * Addons admin UI renders the entries as collapsible children under a\n * single bundle card. The npm package boundary IS the install/update\n * unit — operator-facing actions (Install, Update, Uninstall) act on\n * the bundle, not on individual children. Per-child enable/disable +\n * settings are still surfaced individually.\n *\n * Single-entry packages omit this field; their UI stays a standalone\n * card. Multi-entry packages WITHOUT this field fall back to deriving\n * displayName from `package.json:name`.\n */\n readonly bundle?: AddonBundleManifest\n /**\n * Native node modules that the addon needs at runtime but cannot be\n * bundled (`.node` binary files require ABI-matched compilation).\n * Phase E of the bundles + builder modernization spec — mirror of\n * the Python `requirements.txt` pattern, but for native Node deps.\n *\n * Format: package name → semver range, exactly like\n * `package.json#dependencies`. NOT listed in the package's regular\n * `dependencies` field (so workspace `npm install` skips them), but\n * the addon installer reads this field at install time and runs:\n *\n * npm install --prefix <addonDir> <name>@<range>\n * electron-rebuild -m <addonDir> -w <name> (for Electron host)\n * npm rebuild --prefix <addonDir> <name> (for plain Node host)\n *\n * The host's runtime ABI (Electron 35 = Node ABI 127, plain Node 22 =\n * ABI 133) is detected at install time. Both targets are first-class.\n *\n * Example:\n * ```jsonc\n * \"camstack\": {\n * \"nativeDependencies\": {\n * \"better-sqlite3\": \"^12.8.0\",\n * \"ssh2\": \"^1.16.0\"\n * }\n * }\n * ```\n *\n * Phase E will also produce prebuilt `.node` binaries for both ABIs\n * during `npm publish`, shipped in the tarball under\n * `dist/native-prebuilds/<runtime>-<abi>-<arch>/`. The installer\n * tries the matching prebuild first; falls back to source rebuild\n * only when no prebuild exists for the host triple.\n */\n readonly nativeDependencies?: Readonly<Record<string, string>>\n}\n\nexport interface AddonBundleManifest {\n /** Display name shown on the bundle card header (e.g., \"Pipeline\"). */\n readonly displayName: string\n /** Optional 1-2 sentence description for the operator. */\n readonly description?: string\n /** Optional icon path relative to the package root. */\n readonly icon?: string\n}\n\n/**\n * Where an addon may be deployed. The kernel honours this when picking\n * which node hosts the group-runner that will load the addon.\n *\n * - `'hub-only'` — runs only on the hub process. Default.\n * - `'agent-only'` — runs only on remote agent processes.\n * - `'any-node'` — eligible to run on either hub or agents.\n */\nexport type AddonPlacement = 'hub-only' | 'agent-only' | 'any-node'\n\n/**\n * Effective execution model for an addon. See `AddonDeclaration.execution`\n * for the full semantics.\n */\nexport interface AddonExecution {\n readonly placement?: AddonPlacement\n /**\n * Subprocess group label. Addons sharing the same value share one\n * forked subprocess + one Moleculer broker. Defaults to `'main-group'`\n * — the catch-all bucket for every addon that doesn't opt out.\n */\n readonly group?: string\n}\n\nexport const DEFAULT_ADDON_GROUP = 'main-group'\nexport const DEFAULT_ADDON_PLACEMENT: AddonPlacement = 'hub-only'\n\n/**\n * Resolve the effective `AddonExecution` for an addon. Returns a fully\n * populated value (placement + group always set) so callers can read\n * either field without a separate fallback.\n */\nexport function resolveAddonExecution(\n decl: Pick<AddonDeclaration, 'execution'>,\n): Required<AddonExecution> {\n return {\n placement: decl.execution?.placement ?? DEFAULT_ADDON_PLACEMENT,\n group: decl.execution?.group ?? DEFAULT_ADDON_GROUP,\n }\n}\n\n/** True when the addon may run on a remote agent (deployable from hub). */\nexport function isDeployableToAgent(decl: Pick<AddonDeclaration, 'execution'>): boolean {\n const placement = resolveAddonExecution(decl).placement\n return placement === 'any-node' || placement === 'agent-only'\n}\n\n/** True when the addon is skipped on the hub (agent-only deployment). */\nexport function isAgentOnlyPlacement(decl: Pick<AddonDeclaration, 'execution'>): boolean {\n return resolveAddonExecution(decl).placement === 'agent-only'\n}\n\n/** Convenience accessor — the resolved subprocess group label. */\nexport function resolveAddonGroup(decl: Pick<AddonDeclaration, 'execution'>): string {\n return resolveAddonExecution(decl).group\n}\n\n/** Convenience accessor — the resolved placement (defaults to `hub-only`). */\nexport function resolveAddonPlacement(decl: Pick<AddonDeclaration, 'execution'>): AddonPlacement {\n return resolveAddonExecution(decl).placement\n}\n\n// ── Kernel-internal context ─────────────────────────────────────────────\n/**\n * Extended AddonContext used exclusively by the kernel (hub + worker boot\n * sequences) to wire providers into the CapabilityRegistry after\n * `initialize()` returns. Addon code never sees this — it only receives\n * the public `AddonContext`.\n */\nexport interface InternalAddonContext extends AddonContext {\n /** Register a provider in the local capability registry. Kernel-only. */\n registerProvider(capabilityName: string, provider: unknown): void\n\n /** Resolve the preferred provider for a capability. Kernel-only. */\n resolveProvider<T = unknown>(capabilityName: string): T | null\n}\n","export enum EventCategory {\n // System\n SystemBoot = 'system.boot',\n SystemAddonsReady = 'system.addons-ready',\n SystemRestarting = 'system.restarting',\n /**\n * Readiness transition for a capability provider. Every producer emits\n * this event on `onInitialize` completion, `onDestroy`, and\n * `$node.reconnect`; every consumer that needs to gate on a cross-process\n * cap subscribes via the kernel's readiness module (`awaitReady` /\n * `onReadyState`) instead of polling. Payload is\n * `SystemReadyStatePayload` — see event-bus.ts.\n */\n SystemReadyState = 'system.ready-state',\n\n // Addon lifecycle\n AddonStarted = 'addon.started',\n AddonStopped = 'addon.stopped',\n AddonRestarted = 'addon.restarted',\n AddonUpdated = 'addon.updated',\n AddonInstalled = 'addon.installed',\n AddonUninstalled = 'addon.uninstalled',\n AddonCrashed = 'addon.crashed',\n AddonError = 'addon.error',\n AddonPageReady = 'addon.page-ready',\n AddonWidgetReady = 'addon.widget-ready',\n /**\n * Addon failed to load (import or initialize). Emitted by the kernel's\n * AddonHealthMonitor only AFTER the boot grace period ends — failures\n * during the first 5 minutes are silently retried without alerting\n * (slow-starting addons must have time to come up). Post-grace, this\n * event is emitted exactly once per failure-streak; AlertCenter\n * consumes it to create a persistent operator-visible alert.\n *\n * Payload: `{ packageName, addonId?, error: { message, stack }, retryCount, nextRetryAt }`.\n */\n AddonLoadFailed = 'addon.load-failed',\n /**\n * Addon recovered from a previous failure. Emitted when an addon\n * transitions from `failed` back to `healthy` (typically via the\n * monitor's auto-retry loop, or after manual `addons.retryLoad`).\n * AlertCenter dismisses the corresponding `AddonLoadFailed` alert\n * on this event.\n */\n AddonLoadRecovered = 'addon.load-recovered',\n /**\n * Monitor scheduled the next retry for a failed addon. Transient —\n * surfaced to the UI for live-updating the \"next retry in Ns\"\n * countdown on the Addons page row, NOT persisted as an alert.\n */\n AddonRetryScheduled = 'addon.retry-scheduled',\n /**\n * Monitor is attempting to reload a failed addon NOW. UI uses this\n * to show a spinner during the retry attempt. Same transient nature\n * as AddonRetryScheduled.\n */\n AddonRetryAttempting = 'addon.retry-attempting',\n\n // Device\n DeviceRegistered = 'device.registered',\n DeviceUnregistered = 'device.unregistered',\n DeviceEnabled = 'device.enabled',\n DeviceDisabled = 'device.disabled',\n DeviceSettingsUpdated = 'device.settings-updated',\n /**\n * Emitted when the set of native capability providers bound to a device\n * changes — e.g. an addon registers a new native cap via\n * `DeviceContext.registerNativeCap`, or all native bindings for a device\n * are cleared on removal. Hub consumers re-resolve device-proxy routes\n * when this fires.\n */\n DeviceBindingsChanged = 'device.bindings-changed',\n /**\n * Emitted when the operator-organisational meta surface changes\n * (`name` / `location` / `disabled`). Payload: `{deviceId, field,\n * value}`. Live consumers (UI device list, alert center) react\n * without polling. Distinct from `DeviceSettingsUpdated` which\n * fires on hardware-config changes (host/port/credentials/etc).\n */\n DeviceMetaChanged = 'device.meta-changed',\n /**\n * Emitted by DeviceStreamWiringService after a successful\n * `stream-broker.registerDeviceStreams` call. Payload includes\n * deviceId — consumers look up the full registered device info via\n * `brokerManager.getRegisteredDevice(deviceId)`.\n *\n * Replaces the legacy `StreamRouterService.onDeviceRegistered` callback.\n */\n DeviceStreamsRegistered = 'device.streams-registered',\n\n // Integration\n IntegrationEnabled = 'integration.enabled',\n IntegrationDisabled = 'integration.disabled',\n IntegrationDeleted = 'integration.deleted',\n\n // Provider\n ProviderStarted = 'provider.started',\n ProviderStopped = 'provider.stopped',\n\n // Process\n ProcessCrashed = 'process.crashed',\n ProcessRestartScheduled = 'process.restart_scheduled',\n ProcessRestarted = 'process.restarted',\n\n // Recording\n RecordingStarted = 'recording.started',\n RecordingStopped = 'recording.stopped',\n RecordingError = 'recording.error',\n RecordingHealthDegraded = 'recording.health.degraded',\n RecordingStorageCritical = 'recording.storage.critical',\n RecordingSegmentWritten = 'recording.segment.written',\n RecordingPolicyFallback = 'recording.policy.fallback',\n RecordingRetentionCompleted = 'recording.retention.completed',\n\n // Detection & analysis\n DetectionEvent = 'detection.event',\n\n // Session tracking\n SessionTrackNew = 'session.track.new',\n SessionTrackExpired = 'session.track.expired',\n\n // Benchmark\n BenchmarkProgress = 'benchmark.progress',\n\n // Platform probe\n PlatformProbePhase = 'platform-probe.phase',\n\n // Pipeline & inference\n PipelineProgress = 'pipeline.progress',\n /** Per-frame execution trace emitted by the pipeline executor for live observability. */\n PipelineTrace = 'pipeline.trace',\n /**\n * Raw inference output emitted by `addon-pipeline-runner` after running the\n * detection pipeline on a frame. Carries the `FrameResult` (with\n * `detections[]`, `width`/`height`, timing debug) — never the frame\n * buffer itself. Hub-side consumers (analysis pipeline, class filters,\n * notifications) subscribe to this and re-emit `detection.result` after\n * post-processing.\n */\n PipelineInferenceResult = 'pipeline.inference-result',\n /**\n * Camera lifecycle event emitted by `addon-pipeline-orchestrator` when it\n * assigns or unassigns a camera to/from an agent. Carries no frame data;\n * pure observability for UI dashboards and metrics consumers.\n */\n PipelineCameraAssigned = 'pipeline.camera-assigned',\n PipelineCameraUnassigned = 'pipeline.camera-unassigned',\n /**\n * Per-camera pipeline config was mutated by the orchestrator\n * (3-level settings change via `setAgentAddonDefaults` /\n * `setCameraStepToggle` / `setCameraPipelineForAgent` or a\n * pipeline-scoped `applyDeviceSettingsPatch`). Orchestrator\n * subscribes to its own emission to hot-reload the assigned runner\n * via `attachCamera` so the next frame executes against the new\n * engine/steps without waiting for a rebalance or the next\n * `DeviceStreamsRegistered` cycle.\n */\n PipelineCameraUpdated = 'pipeline.camera-updated',\n /**\n * Periodic snapshot of per-node pipeline-runner load\n * (`RunnerLocalLoad`). Emitted ~1Hz by every runner so UI dashboards\n * subscribe instead of polling `pipelineRunner.getLocalLoad`.\n * `nodeId` carried in the payload + on `event.source.nodeId`.\n */\n PipelineRunnerLoadSnapshot = 'pipeline.runner-load-snapshot',\n /**\n * Periodic snapshot of per-camera pipeline metrics (`CameraMetrics`\n * + `deviceId` + `nodeId`). Emitted ~1Hz by every runner for each\n * attached camera. UI subscribes to drive overlay phase / fps /\n * inference time without polling `getCameraMetrics`.\n */\n PipelineCameraMetricsSnapshot = 'pipeline.camera-metrics-snapshot',\n /**\n * Periodic snapshot of stream-broker per-broker statistics (input\n * fps, decoded fps, bitrate, codec). Emitted ~1Hz by every\n * stream-broker process for each active broker so the UI can drive\n * the Stream / Cluster dashboards without polling\n * `streamBroker.listAllProfileSlots` and friends.\n */\n StreamBrokerMetricsSnapshot = 'stream-broker.metrics-snapshot',\n /**\n * Cap event fired by `stream-broker` when a profile slot enters\n * \"demanded\" state — a cam stream has been assigned and at least one\n * consumer (RTSP restream, decoded subscriber, WebRTC session, …) is\n * present. Camera-provider addons (Reolink Baichuan push, …)\n * subscribe to this category to start their underlying transport\n * lazily. Payload: `{ deviceId, camStreamId, profile }`.\n */\n StreamBrokerOnCamStreamDemand = 'stream-broker.onCamStreamDemand',\n /**\n * Cap event fired by `stream-broker` when the last consumer leaves a\n * previously-demanded cam stream. Providers tear down their\n * underlying transport on receipt. Payload: `{ deviceId, camStreamId }`.\n */\n StreamBrokerOnCamStreamIdle = 'stream-broker.onCamStreamIdle',\n /**\n * Cap event fired by `stream-broker` when a broker fails to dial a\n * managed-loopback source (today: `pull-rfc4571`) and the publisher\n * needs to refresh the cached URL. Mirrors Scrypted's\n * `ensureRfcServer` self-heal pattern: the lib's TCP server\n * idle-tears-down on its own schedule, so a re-publish with a fresh\n * `host:port` is the only way to keep the broker dialable. Camera\n * providers (Reolink Baichuan native, …) subscribe and respond by\n * re-running their publish pipeline.\n * Payload: `{ deviceId, camStreamId, brokerId }`.\n */\n StreamBrokerOnRequestStreamSourceRefresh = 'stream-broker.onRequestStreamSourceRefresh',\n /**\n * Generic per-device runtime-state change. Fired by `device-manager`\n * whenever a persisted slice in any cap's `runtimeState` shape\n * mutates. Payload: `{deviceId, capName, slice}`. Subscribers are\n * the `deviceState` cap router (cross-process listeners) and the\n * deviceProxy reactive bindings (`device.state.<capName>.value`).\n * Cap-specific events (`battery.onStatusChanged`, …) still fire\n * — they're authoritative for callers that want a typed payload\n * without filtering on `capName`.\n */\n DeviceStateChanged = 'device.state-changed',\n /**\n * Cap event fired by every device that registers the `battery`\n * capability. Mirrors the cap definition's `onStatusChanged`. Carries\n * `{ deviceId, status: BatteryStatus }`. Subscribers (alert center,\n * snapshot wrapper, UI) react to charge/sleep transitions without\n * polling `batteryCapability.getStatus`.\n */\n BatteryOnStatusChanged = 'battery.onStatusChanged',\n /**\n * Cap event fired by every device that registers the `doorbell`\n * capability. Mirrors `doorbellCapability.events.onPressed`. Carries\n * `{ deviceId, timestamp }`. Operators consuming the UI subscribe\n * here to render transient ring toasts and a \"Recent presses\" row\n * on the device detail page.\n */\n DoorbellOnPressed = 'doorbell.onPressed',\n /**\n * Periodic snapshot of the per-node detection-pipeline engine\n * registry (loaded engines, models resident, in-use cameras, idle\n * TTL). Emitted ~0.2Hz (every 5 s) by every detection-pipeline\n * process. The Engines tab subscribes to drive its inventory view\n * without polling `pipelineExecutor.listLoadedEngines`.\n */\n PipelineEngineMetricsSnapshot = 'pipeline.engine-metrics-snapshot',\n /**\n * Cluster topology snapshot. Carries the same payload returned by\n * `nodes.topology` (every reachable node + addons + processes).\n * Emitted by the hub on any agent / addon lifecycle change\n * (debounced) plus a periodic safety net. Replaces UI polling on\n * `nodes.topology` — admin-ui dashboards subscribe to drive the\n * cluster view directly from the event payload.\n */\n ClusterTopologySnapshot = 'cluster.topology-snapshot',\n /**\n * Periodic per-node system metrics snapshot (CPU / memory / GPU /\n * disk / network). Emitted ~0.2 Hz by the metrics-provider addon\n * for each node. Drives the dashboard SystemStatus / ProcessResources\n * widgets without polling `metricsProvider.getCurrent`.\n */\n MetricsNodeResourcesSnapshot = 'metrics.node-resources-snapshot',\n /**\n * Periodic per-node process-tree snapshot (camstack-related pids\n * with ghost / managed / root classification). Emitted ~0.2 Hz by\n * the metrics-provider addon. Drives the Cluster → Processes tab\n * without polling `metricsProvider.listNodeProcesses`.\n */\n MetricsNodeProcessesSnapshot = 'metrics.node-processes-snapshot',\n /**\n * Capability binding change event emitted by `addon-pipeline-orchestrator`\n * when a user changes which addon implements a cap on a node. Subscribed\n * by every kernel process to update its local `preferredProviderRegistry`\n * so future capability lookups respect the new binding.\n */\n /**\n * A capability binding was changed for a node — addon X now provides\n * capability `cap` on node `nodeId`. Lives under the generic\n * `capability.*` namespace because capability bindings are a kernel-\n * level concept used by many addons, not strictly pipeline-scoped.\n */\n CapabilityBindingChanged = 'capability.binding-changed',\n ModelDownloadProgress = 'model.download.progress',\n\n // Agent\n AgentRegistered = 'agent.registered',\n AgentUnregistered = 'agent.unregistered',\n AgentOnline = 'agent.online',\n AgentOffline = 'agent.offline',\n /** Forked worker process (e.g. hub/pipeline) connected to the broker. */\n WorkerOnline = 'worker.online',\n /** Forked worker process disconnected from the broker. */\n WorkerOffline = 'worker.offline',\n AgentTaskDispatched = 'agent.task.dispatched',\n AgentTaskAssigned = 'agent.task.assigned',\n AgentTrpcConnected = 'agent.trpc.connected',\n AgentWsConnected = 'agent.ws.connected',\n AgentWsDisconnected = 'agent.ws.disconnected',\n AgentBackupActivated = 'agent.backup.activated',\n\n // Detection settings\n OrchestrationSettingsUpdated = 'orchestration.settings-updated',\n\n /**\n * Per-agent hwaccel preference changed (user override set, cleared, or\n * re-probed). Observability event — decoders pull the current pref at\n * `createSession`, so running sessions keep their current backend until\n * they rotate naturally (camera add/remove, stream restart). Future\n * work can wire a listener in stream-broker that force-rotates live\n * sessions; for now this event powers logs + admin-UI toast feedback.\n */\n PipelineAgentHwaccelChanged = 'pipeline.agent-hwaccel-changed',\n\n // Motion analysis\n MotionAnalysis = 'detection.motion-analysis',\n /** All raw motion zones from CCL before minArea filter — for UI debug overlay. */\n MotionZonesRaw = 'detection.motion-zones-raw',\n /**\n * Per-camera motion phase transition (`watching ↔ active`) emitted\n * by the runner. Mirrors the `motion.onMotionChanged` cap event\n * surface — payload `MotionOnMotionChangedPayload` carries\n * `{deviceId, detected, timestamp, source, regions?}`. Subscribers\n * include addons that need to react to motion state without\n * polling the runtime-state mirror.\n */\n MotionOnMotionChanged = 'motion.on-motion-changed',\n\n // Detection extended\n DetectionResult = 'detection.result',\n DetectionRaw = 'detection.raw',\n DetectionCameraNative = 'detection.camera-native',\n /**\n * Canonical per-chunk live audio pipeline output. Payload is\n * `PipelineAudioInferenceResultPayload` carrying a full `AudioResult`\n * (level + detections + debug). Lives on the pipeline.* namespace\n * alongside `pipeline.inference-result` (video) for symmetry.\n */\n PipelineAudioInferenceResult = 'pipeline.audio-inference-result',\n DetectionPhaseTransition = 'detection.phase-transition',\n ProviderMotion = 'provider.motion',\n ProviderDetection = 'provider.detection',\n\n // Enrichment\n EnrichmentEmbeddingStored = 'enrichment.embedding.stored',\n EnrichmentSceneStateChanged = 'enrichment.scene.state-changed',\n EnrichmentActivitySummary = 'enrichment.activity.summary',\n\n // Pipeline analytics\n PipelineAnalyticsTrackStarted = 'pipeline-analytics.track-started',\n PipelineAnalyticsTrackEnded = 'pipeline-analytics.track-ended',\n PipelineAnalyticsDetectionEvent = 'pipeline-analytics.detection-event',\n PipelineAnalyticsFrameTracked = 'pipeline-analytics.frame-tracked',\n\n // Provider-specific\n FrigateLiveEvent = 'frigate.live-event',\n\n // Camera streams\n CameraStreamsProfileSlotsChanged = 'camera-streams.onProfileSlotsChanged',\n /**\n * Stream-broker health watchdog. Per-broker (deviceId/profile) emission.\n * `stream.offline` fires after STREAM_STALE_TIMEOUT_MS without an encoded\n * packet on an active broker. `stream.online` fires on first packet\n * after a stale gap (or on initial first packet). Payload includes\n * the assigned camStreamId as `profileKey`.\n */\n StreamOnline = 'stream.online',\n StreamOffline = 'stream.offline',\n\n // Network\n NetworkTunnelStarted = 'network.tunnel.started',\n NetworkTunnelStopped = 'network.tunnel.stopped',\n /** Fired by the `local-network` cap when the host's interface set\n * changes (new IP from DHCP, VPN connect, docker bridge added). */\n LocalNetworkChanged = 'network.local.changed',\n\n // Backup\n BackupCompleted = 'backup.completed',\n BackupRestored = 'backup.restored',\n\n // Notification\n NotificationDispatched = 'notification.dispatched',\n NotificationFailed = 'notification.failed',\n\n // Device extended\n DeviceUpdated = 'device.updated',\n /**\n * Transport-level connectivity. Emitted by the device driver when\n * the underlying control socket actually connects / disconnects\n * (Baichuan TCP, ONVIF probe response, RTSP DESCRIBE, …) — NOT for\n * power-state transitions on a battery camera. For battery wake /\n * doze cycles see `DeviceAwake` / `DeviceSleeping`.\n */\n DeviceOnline = 'device.online',\n /**\n * Stream-broker watchdog — emitted when no encoded packet has been\n * received for STREAM_STALE_TIMEOUT_MS on an active broker (rtsp or\n * push). Paired with DeviceOnline which fires on first packet after\n * a stale gap. Payload: DeviceStreamHealthPayload.\n */\n DeviceOffline = 'device.offline',\n /**\n * Battery cam woke up — physical power-state transition reported\n * by the device firmware. Distinct from `DeviceOnline` so a UI panel\n * watching power state doesn't flap on every UDP socket reconnect.\n */\n DeviceAwake = 'device.awake',\n /**\n * Battery cam went to sleep. See `DeviceAwake`.\n */\n DeviceSleeping = 'device.sleeping',\n\n // Retention\n RetentionCleanup = 'retention.cleanup',\n}\n","import type { FrameResult, AudioResult } from '../types/detection.js'\nimport type { PipelineExecutionTrace } from '../types/pipeline-step.js'\nimport type { MotionRegion } from '../capabilities/motion-detection.cap.js'\nimport type { CameraPipelineConfig } from '../types/camera-pipeline.js'\nimport type { CameraMetrics } from './api-shared.js'\nimport type { RunnerLocalLoad } from './pipeline-runner-capability.js'\nimport type { BrokerStats } from './stream-broker.js'\nimport type { PipelineEngineChoice } from '../types/pipeline.js'\nimport type { NodeProcess, SystemResourceSnapshot } from './metrics-provider.js'\n\n// ---------------------------------------------------------------------------\n// Event source\n// ---------------------------------------------------------------------------\n\nexport interface EventSource {\n /** Primary source type: 'core', 'addon', 'device', 'pipeline', etc. */\n type: string\n /** Primary identifier — addonId when type='addon', deviceId when type='device', etc. */\n id: string | number\n /** Agent/node that originated the event (e.g. 'hub', 'agent-a1b2c3'). */\n nodeId?: string\n /** Addon that originated the event (populated even when type='device'). */\n addonId?: string\n /** Device the event relates to (populated even when type='addon'). */\n deviceId?: number\n}\n\n// ---------------------------------------------------------------------------\n// Detection event payloads\n// ---------------------------------------------------------------------------\n\n// MotionAnalysisRegion removed (2026-04-14) — consumers now use\n// `MotionRegion` from the motion-detection capability directly. The two\n// interfaces had identical shapes; keeping both was a duplication leak.\n\n/**\n * One detection entry emitted by a camera's firmware (not by the local\n * AI pipeline). Carried inside `detection.camera-native` events —\n * native providers fan out to this category for each ring/motion/AI\n * alarm push they receive from the camera.\n */\nexport interface CameraNativeDetection {\n /**\n * Firmware-reported class. Loose string to accommodate heterogeneous\n * firmwares (Reolink uses 'people'/'vehicle'/'animal'/…; ONVIF uses\n * 'PeopleDetect'; etc.). Common values: 'motion', 'person', 'vehicle',\n * 'animal', 'face', 'package', 'other', 'doorbell'.\n */\n readonly class: string\n /** Ms epoch when the server observed the push from the camera. */\n readonly timestamp: number\n /** Optional firmware-provided confidence [0..1]. Most firmwares don't expose it. */\n readonly confidence?: number\n /** Optional bbox [x, y, w, h] normalised [0..1]. Rare on native events. */\n readonly bbox?: readonly [number, number, number, number]\n}\n\nexport interface MotionAnalysisPayload {\n detected: boolean\n regionCount: number\n regions: readonly MotionRegion[]\n frameWidth: number\n frameHeight: number\n analysisMs: number\n [key: string]: unknown\n}\n\n/**\n * Motion phase transition payload. Single source of truth lives in\n * `motion.cap.ts` as `MotionOnMotionChangedDataSchema` — this alias\n * just re-exports the inferred type with the `[key: string]: unknown`\n * loose-index pattern the bus uses for forward-compat. Don't add\n * fields here; extend the schema in motion.cap.ts.\n */\nexport type MotionOnMotionChangedPayload =\n import('../capabilities/motion.cap.js').MotionOnMotionChangedData\n & { [key: string]: unknown }\n\n/** Raw motion zone from CCL (before minArea filter). */\nexport interface MotionRawZone {\n readonly bbox: readonly [number, number, number, number] // [x1, y1, x2, y2]\n readonly pixelCount: number\n readonly changeScore: number // 0-1 intensity\n}\n\n/** All raw motion zones — emitted for UI debug overlay. */\nexport interface MotionZonesRawPayload {\n readonly deviceId: number\n readonly zones: readonly MotionRawZone[]\n readonly frameSize: { readonly width: number; readonly height: number }\n readonly timestamp: number\n [key: string]: unknown\n}\n\nexport interface DetectionResultPayload {\n /**\n * The post-processed frame result the frontend consumes. Already\n * contains `width`/`height` + `detections[]` + optional debug. No\n * pipeline-raw wrapper — the new `FrameResult` shape IS the payload.\n */\n frame: FrameResult\n /** Events emitted by the analysis pipeline (tracking, scene state, face rec). */\n analysisResults: readonly unknown[]\n [key: string]: unknown\n}\n\n/**\n * Raw inference output emitted by `addon-pipeline-runner` per detection\n * frame. Carries the FrameResult produced by the runner — the hub-side\n * wiring service subscribes, applies the analysis pipeline +\n * notifications, and re-emits as `detection.result`.\n */\nexport interface PipelineInferenceResultPayload {\n readonly deviceId: number\n readonly frame: FrameResult\n /** The Moleculer node id of the runner that produced this result. */\n readonly nodeId: string\n readonly [key: string]: unknown\n}\n\n/**\n * Live per-audio-chunk output emitted by the pipeline-orchestrator for\n * each camera with audio analysis enabled. Mirrors `PipelineInferenceResultPayload`\n * but for audio: carries the canonical `AudioResult` produced by running\n * the audio-analyzer + audio-classifier inline on the hub.\n *\n * Replaces the legacy split `detection.audio.level` /\n * `detection.audio.classification` events — consumers should read\n * `frame.level` for dBFS/RMS and `frame.detections` for classifier\n * matches (both on the same single event).\n */\nexport interface PipelineAudioInferenceResultPayload {\n readonly deviceId: number\n readonly frame: AudioResult\n /** Node id of the host that ran the analyzer — always `'hub'` today\n * because audio runs inline on the hub, but the field is kept so the\n * event shape matches `PipelineInferenceResultPayload`. */\n readonly nodeId: string\n readonly [key: string]: unknown\n}\n\n/**\n * Camera assignment lifecycle payload emitted by `addon-pipeline-orchestrator`\n * when a camera is assigned to or unassigned from an agent runner. Used by\n * the UI / metrics dashboard. No frame data.\n */\nexport interface PipelineCameraAssignmentPayload {\n readonly deviceId: number\n readonly agentNodeId: string\n /** True when the assignment was set manually (preferredAgent setting), false when chosen by the load balancer. */\n readonly pinned: boolean\n readonly reason: 'manual' | 'capacity' | 'hardware-affinity' | 'failover' | 'rebalance' | 'unassigned'\n readonly [key: string]: unknown\n}\n\n/**\n * Capability binding change payload emitted whenever an operator picks\n * a different addon to implement a capability on a node. Each kernel\n * process subscribes to this event and updates its local\n * `preferredProviderRegistry` so subsequent capability lookups return\n * the newly chosen provider.\n *\n * The event lives under `capability.binding-changed` because binding\n * is a kernel-level concept used by any cap provider (not just the\n * pipeline flow). It was renamed from `pipeline.binding-changed` to\n * reflect that broader scope.\n */\nexport interface CapabilityBindingChangedPayload {\n readonly nodeId: string\n readonly capName: string\n /** New preferred addon id, or null when the binding was cleared. */\n readonly addonId: string | null\n readonly [key: string]: unknown\n}\n\nexport interface PhaseTransitionPayload {\n deviceId: number\n from: 'watching' | 'active'\n to: 'watching' | 'active'\n reason: 'motion_detected' | 'cooldown_expired'\n /** Motion source that armed the cooldown — present on both ON and OFF. */\n source?: 'onboard' | 'analyzer'\n /** Cooldown window in ms that was/is active for this transition. */\n cooldownMs?: number\n /** Ms epoch of the transition. */\n timestamp?: number\n [key: string]: unknown\n}\n\n// Legacy `AudioLevelPayload` / `AudioClassificationPayload` were removed\n// when the live audio path was migrated to the canonical `AudioResult`\n// shape — consumers now subscribe to `pipeline.audio-inference-result`\n// and read level + detections from the single payload.\n\n// ---------------------------------------------------------------------------\n// System readiness protocol — see docs/superpowers/specs/2026-04-19-system-readiness-design.md\n// ---------------------------------------------------------------------------\n\n/**\n * Scope of a readiness transition — the \"who does this ready/down apply to\".\n *\n * Two shapes today:\n * - `global` for singletons that are cluster-wide (there is only one hub).\n * - `node` for per-process providers (one readiness per broker nodeID).\n *\n * `device` scope covers per-device native caps registered via\n * `DeviceContext.registerNativeCap(cap, provider)`. Consumers of cross-\n * process per-device caps (stream-broker, orchestrator, snapshot, webrtc)\n * `awaitReady(capName, {type:'device', deviceId})` before calling the\n * native-cap bridge so they don't race the service-advertise window.\n * On worker disconnect the hub-side registry's `agent.offline` demux\n * synthesises `down` for every device-scoped cap whose `sourceNodeId`\n * matches the offline node — same pattern as node-scoped synthesis.\n */\nexport type ReadinessScope =\n | { readonly type: 'global' }\n | { readonly type: 'node'; readonly nodeId: string }\n | { readonly type: 'device'; readonly deviceId: number }\n\n/** States a capability provider transitions through. */\nexport type ReadinessState = 'starting' | 'ready' | 'down'\n\n/**\n * Payload for `system.ready-state` events.\n *\n * `generation` is a per-process random identifier that stays constant\n * for the producer's lifetime and changes whenever the producer process\n * is restarted. The kernel's local `ReadinessRegistry` derives a\n * monotonic `epoch` per `(capName, scope)` by counting generation\n * transitions — emitters never stamp epoch themselves. Consumers that\n * hold long-lived state compare `transition.epoch > lastSeenEpoch` to\n * decide whether to rebuild.\n *\n * Same-generation repeated `ready` events are idempotent keepalives\n * (e.g. re-emitted on every `$node.connected`) — the registry keeps\n * epoch stable.\n */\nexport interface SystemReadyStatePayload {\n readonly capName: string\n readonly scope: ReadinessScope\n readonly state: ReadinessState\n /** Per-process random id; changes on producer restart. */\n readonly generation: string\n readonly sourceNodeId: string\n readonly ts: number\n readonly [key: string]: unknown\n}\n\n// ---------------------------------------------------------------------------\n// Cluster topology snapshot shape — matches `nodes.topology` cap output\n// ---------------------------------------------------------------------------\n\n/** One service entry inside a topology process — addon + capabilities. */\nexport interface TopologyService {\n readonly addonId: string\n readonly capabilities: readonly string[]\n readonly status: string\n}\n\n/** One process under a topology node (main process + isolated workers). */\nexport interface TopologyProcess {\n readonly pid: number\n readonly name: string\n readonly state: string\n readonly cpuPercent: number\n readonly memoryRss: number\n readonly uptimeSeconds: number\n readonly services: readonly TopologyService[]\n readonly groupId?: string\n}\n\n/** Top-level cluster node — hub or remote agent. */\nexport interface TopologyNode {\n readonly id: string\n readonly name: string\n readonly hostname: string\n readonly platform: string\n readonly arch: string\n readonly cpuModel: string | null\n readonly cpuCores: number\n readonly memoryMB: number\n readonly engines: readonly string[]\n readonly isHub: boolean\n readonly isOnline: boolean\n readonly cpuPercent: number\n readonly memoryPercent: number\n readonly uptime: number\n readonly lastSeen: string\n /** Local IP addresses visible on the network (non-internal, IPv4/IPv6). */\n readonly localIps: readonly string[]\n readonly addons: ReadonlyArray<{ readonly id: string; readonly capabilities: readonly string[]; readonly status: string }>\n readonly processes: readonly TopologyProcess[]\n}\n\n// ---------------------------------------------------------------------------\n// Known event catalog — typed category → payload mapping\n// ---------------------------------------------------------------------------\n\n/** All known event categories with their typed payloads */\nexport interface EventCatalog {\n // ── System ──────────────────────────────────────────────────────────────\n 'system.boot': { mode: string }\n 'system.addons-ready': { activeAddons: readonly string[] }\n 'system.restarting': Record<string, never>\n 'system.ready-state': SystemReadyStatePayload\n\n // ── Addon lifecycle ─────────────────────────────────────────────────────\n 'addon.started': { addonId: string; packageName?: string; packageVersion?: string; agent?: string }\n 'addon.stopped': { addonId: string; packageName?: string; packageVersion?: string; agent?: string }\n 'addon.restarted': { addonId: string; packageName?: string; packageVersion?: string; agent?: string }\n 'addon.updated': { addonId: string; packageName?: string; packageVersion?: string; agent?: string; fromVersion?: string; toVersion?: string }\n 'addon.installed': { addonId: string; packageName?: string; packageVersion?: string; agent?: string }\n 'addon.uninstalled': { addonId: string; packageName?: string; packageVersion?: string; agent?: string }\n 'addon.crashed': { addonId: string; packageName?: string; packageVersion?: string; agent?: string; error?: string }\n 'addon.error': { addonId: string; packageName?: string; packageVersion?: string; agent?: string; error?: string; action?: string; phase?: string }\n\n // ── Device ──────────────────────────────────────────────────────────────\n 'device.registered': { deviceId: number; providerId: string; name?: string }\n 'device.unregistered': { deviceId: number; providerId: string }\n 'device.streams-registered': { deviceId: number; providerId: string }\n 'device.streams-unregistered': { deviceId: number }\n 'device.enabled': { deviceId: number; integrationId: string }\n 'device.disabled': { deviceId: number; integrationId: string }\n 'device.settings-updated': { deviceId: number; integrationId: string; keys: string[] }\n /**\n * Device online/offline aggregate. Emitted by the device's owning\n * provider (rtsp / reolink / onvif / frigate) when the union of its\n * stream-broker profile health flips. `online` fires when at least\n * one profile becomes healthy after all were offline; `offline` fires\n * when the last healthy profile goes stale (no packets for\n * STREAM_STALE_TIMEOUT_MS).\n */\n 'device.online': { deviceId: number; providerId: string; profileCount?: number; reason?: string }\n 'device.offline': { deviceId: number; providerId: string; reason?: string }\n 'device.awake': { deviceId: number; providerId: string; reason?: string }\n 'device.sleeping': { deviceId: number; providerId: string; reason?: string }\n /**\n * Emitted whenever the set of (device, capability) native providers or\n * wrapper activations changes. Hub subscribers use this to keep a\n * cross-process view of which addon/node currently serves each cap for\n * each device. `nodeId` is the broker nodeID where the provider lives;\n * 'hub' for hub-local. `addonId` is the provider (native or wrapper).\n */\n 'device.bindings-changed': {\n deviceId: number\n capName: string\n reason: 'native-registered' | 'native-unregistered' | 'wrapper-activated' | 'wrapper-deactivated'\n addonId: string\n nodeId: string\n }\n 'device.meta-changed': {\n deviceId: number\n field: 'name' | 'location' | 'disabled'\n value: string | null | boolean\n }\n\n // ── Integration ────────────────────────────────────────────────────────\n 'integration.enabled': { integrationId: string; addonId: string }\n 'integration.disabled': { integrationId: string; addonId: string }\n\n // ── Provider ────────────────────────────────────────────────────────────\n 'provider.started': { providerId: string; type?: string }\n 'provider.stopped': { providerId: string; reason?: string }\n\n // ── Process ─────────────────────────────────────────────────────────────\n 'process.crashed': { processId: string; exitCode?: number; signal?: string }\n 'process.restart_scheduled': { processId: string; delayMs: number }\n 'process.restarted': { processId: string }\n\n // ── Recording ───────────────────────────────────────────────────────────\n 'recording.started': { deviceId: number; streamId?: string }\n 'recording.stopped': { deviceId: number; reason?: string }\n 'recording.error': { deviceId: number; error: string }\n 'recording.health.degraded': { deviceId: number; message: string }\n 'recording.storage.critical': { deviceId: number; usagePercent?: number }\n 'recording.segment.written': { deviceId: number; path?: string; durationSec?: number; sizeMB?: number }\n 'recording.policy.fallback': { deviceId: number; reason: string }\n 'recording.retention.completed': { deletedCount?: number; freedMB?: number }\n\n // ── Detection & analysis ────────────────────────────────────────────────\n 'detection.event': { deviceId: number; detections?: unknown[]; [key: string]: unknown }\n 'detection.result': DetectionResultPayload\n 'detection.motion-analysis': MotionAnalysisPayload\n 'detection.motion-zones-raw': MotionZonesRawPayload\n 'motion.on-motion-changed': MotionOnMotionChangedPayload\n /**\n * On-board detection coming straight from the camera firmware (Reolink\n * AI alarms, ONVIF analytics, etc.) — as opposed to `detection.result`\n * which is produced by the pipeline running locally.\n *\n * `source` discriminates the origin when multiple pipelines coexist for\n * the same camera: `'onboard'` for native firmware events, `'camera-native'`\n * kept as an alias for backwards compatibility with pre-Reolink emitters.\n *\n * `detections` is tightened from `unknown[]` to a structured shape. Each\n * entry describes one firmware-reported class ('motion', 'person',\n * 'vehicle', 'animal', 'face', 'package', 'other', …) at a point in time.\n */\n 'detection.camera-native': {\n cameraId: number\n detections: readonly CameraNativeDetection[]\n source: 'onboard' | 'camera-native'\n }\n 'pipeline.audio-inference-result': PipelineAudioInferenceResultPayload\n 'detection.phase-transition': PhaseTransitionPayload\n /**\n * Emitted whenever a per-device override is written or cleared for any\n * pipeline-participating addon (pipeline-orchestrator, motion-wasm,\n * detection-pipeline, audio-analyzer, audio-classifier). The wiring\n * service subscribes to this event and restarts detection for the\n * affected camera so the new values take effect without a full reboot.\n * `addonId` is informational — most consumers only care about\n * `deviceId` and will rebuild the full RunnerCameraConfig anyway.\n */\n 'orchestration.settings-updated': { deviceId: number; addonId?: string }\n 'provider.motion': { active: boolean }\n 'provider.detection': { detections: unknown }\n\n // ── Session tracking ────────────────────────────────────────────────────\n 'session.track.new': { deviceId: number; trackId: string; label?: string }\n 'session.track.expired': { deviceId: number; trackId: string; durationMs?: number }\n\n // ── Benchmark ───────────────────────────────────────────────────────────\n /**\n * Progress from a running benchmark (pipeline image test, decoder perf test,\n * …). The payload is intentionally open (`kind` discriminator + free-form\n * additional fields) so different benchmark shapes share the same bus\n * category and a single UI subscriber can filter by `kind` + `sessionId`.\n */\n 'benchmark.progress': {\n kind: string\n sessionId: string\n phase?: string\n tSec?: number\n sample?: Record<string, unknown>\n message?: string\n }\n\n // ── Pipeline & inference ────────────────────────────────────────────────\n 'pipeline.progress': {\n /** Node that executed the pipeline step. Same value is also set on `event.source.nodeId`\n * but carried in the payload so admin-ui filters don't need to peek at the source shape. */\n nodeId: string\n /** Stable identifier per `runPipeline()` invocation — every progress event from the same\n * run shares this id so the UI can correlate a burst of messages into a single run card. */\n sessionId: string\n step: string\n addonId?: string\n modelId?: string\n ms?: number\n message?: string\n }\n /** Per-frame execution trace emitted by the pipeline executor for live observability. */\n 'pipeline.trace': PipelineExecutionTrace\n /** Raw inference output emitted by addon-pipeline-runner (no frame buffer). */\n 'pipeline.inference-result': PipelineInferenceResultPayload\n /** Camera assigned to an agent runner by addon-pipeline-orchestrator. */\n 'pipeline.camera-assigned': PipelineCameraAssignmentPayload\n /** Camera released from an agent runner. */\n 'pipeline.camera-unassigned': PipelineCameraAssignmentPayload\n /** Per-camera pipeline config changed — orchestrator hot-reloads the assigned runner. */\n 'pipeline.camera-updated': {\n readonly deviceId: number\n readonly config: CameraPipelineConfig\n }\n /**\n * Periodic per-runner load snapshot (~1 Hz). Replaces UI polling on\n * `pipelineRunner.getLocalLoad`. UI subscribes per agent node.\n */\n 'pipeline.runner-load-snapshot': {\n readonly nodeId: string\n readonly load: RunnerLocalLoad\n /** Wall-clock ms when the snapshot was sampled. */\n readonly timestamp: number\n }\n /**\n * Periodic per-camera metrics snapshot (~1 Hz). Replaces UI polling\n * on `pipelineOrchestrator.getCameraMetrics` /\n * `pipelineRunner.getCameraMetrics`. One event per attached camera\n * per tick. UI consumers filter by `deviceId`.\n */\n 'pipeline.camera-metrics-snapshot': {\n readonly deviceId: number\n readonly nodeId: string\n readonly metrics: CameraMetrics\n readonly timestamp: number\n }\n /**\n * Periodic per-broker stats snapshot (~1 Hz). Replaces polling on\n * `streamBroker.getBrokerStats` and `streamBroker.listAllProfileSlots`.\n * Carries the full `BrokerStats` payload so UI consumers can render\n * the panel directly from the event without round-tripping the cap.\n */\n 'stream-broker.metrics-snapshot': {\n readonly brokerId: string\n readonly deviceId: number\n readonly profile: string\n readonly nodeId: string\n readonly stats: BrokerStats\n readonly timestamp: number\n }\n /**\n * Cap event: stream-broker signals that a profile slot now has at\n * least one consumer. Camera-provider addons (Reolink Baichuan push,\n * etc.) start their underlying transport on receipt — see\n * `EventCategory.StreamBrokerOnCamStreamDemand`.\n */\n 'stream-broker.onCamStreamDemand': {\n readonly deviceId: number\n readonly camStreamId: string\n readonly profile: 'high' | 'mid' | 'low'\n }\n /**\n * Cap event: stream-broker signals the last consumer left. Providers\n * tear down their transport — see `EventCategory.StreamBrokerOnCamStreamIdle`.\n */\n 'stream-broker.onCamStreamIdle': {\n readonly deviceId: number\n readonly camStreamId: string\n }\n /**\n * Cap event: device's battery status changed (firmware push or\n * provider-side observation). Mirrors `batteryCapability.onStatusChanged`.\n * Payload shape duplicated inline (rather than importing from\n * `capabilities/battery.cap`) to avoid a cycle between the cap\n * definitions and the event bus typing — they're kept in lock-step\n * by hand (see also `BatteryStatus` in `capabilities/battery.cap.ts`).\n */\n 'battery.onStatusChanged': {\n readonly deviceId: number\n readonly status: {\n readonly percentage: number\n readonly charging: 'dc' | 'solar' | 'none'\n readonly sleeping: boolean\n readonly lastUpdated: number\n }\n }\n /**\n * Doorbell button press — emitted by every device that registers the\n * `doorbell` capability. Mirrors `doorbellCapability.events.onPressed`.\n * Subscribers (UI toast, advanced-notifier) react to physical rings\n * without holding a cap reference.\n */\n 'doorbell.onPressed': {\n readonly deviceId: number\n readonly timestamp: number\n }\n /**\n * Periodic per-node engine inventory snapshot (~0.2 Hz). Replaces\n * polling on `pipelineExecutor.listLoadedEngines`. One event per\n * detection-pipeline process per tick, carrying every loaded engine\n * with the same shape the cap returns.\n */\n 'pipeline.engine-metrics-snapshot': {\n readonly nodeId: string\n readonly engines: ReadonlyArray<{\n readonly engineKey: string\n readonly engine: PipelineEngineChoice\n readonly modelsLoaded: readonly string[]\n readonly inUseByCameras: readonly number[]\n readonly kind: 'runtime' | 'warm-override'\n readonly poolPid: number | null\n readonly idleMs: number | null\n readonly idleTtlMs: number | null\n }>\n readonly timestamp: number\n }\n /**\n * Cluster topology snapshot — same payload shape that\n * `nodes.topology` returns. Emitted by the hub on any agent /\n * addon lifecycle change (debounced ~200ms) plus a periodic safety\n * net. UI dashboards subscribe to drive their cluster view\n * directly from the event payload — zero round-trip.\n */\n 'cluster.topology-snapshot': {\n readonly nodes: ReadonlyArray<TopologyNode>\n readonly timestamp: number\n }\n /**\n * Periodic per-node system metrics snapshot. Carries the full\n * `SystemResourceSnapshot` returned by\n * `metricsProvider.getCached()` so dashboards render CPU /\n * memory / GPU / disk / network without polling.\n */\n 'metrics.node-resources-snapshot': {\n readonly nodeId: string\n readonly snapshot: SystemResourceSnapshot\n readonly timestamp: number\n }\n /**\n * Periodic per-node process-tree snapshot (`NodeProcess[]` —\n * camstack-related pids with ghost / managed / root classification).\n * One event per node per tick. Replaces polling on\n * `metricsProvider.listNodeProcesses`.\n */\n 'metrics.node-processes-snapshot': {\n readonly nodeId: string\n readonly processes: ReadonlyArray<NodeProcess>\n readonly timestamp: number\n }\n /**\n * Per-agent hwaccel override changed (set / cleared / re-probed). Payload\n * carries the new effective preference so UI consumers can toast or\n * refresh their pipeline view without re-fetching. Decoders still pull\n * from `pipeline-orchestrator.getAgentSettings` on session creation;\n * this event is additive observability.\n */\n 'pipeline.agent-hwaccel-changed': {\n readonly agentNodeId: string\n readonly userChoice: 'auto' | 'none' | string | null\n readonly probedBest: string\n readonly reason: 'user-set' | 'user-cleared' | 'reprobed' | 'seeded'\n }\n /** Capability binding changed for a node — kernels update their preferred-provider registry. */\n 'capability.binding-changed': CapabilityBindingChangedPayload\n 'model.download.progress': { modelId: string; progress: number; totalMB?: number }\n\n // ── Agent ───────────────────────────────────────────────────────────────\n 'agent.online': { agentId: string }\n 'agent.offline': { agentId: string }\n 'worker.online': { workerId: string }\n 'worker.offline': { workerId: string }\n 'agent.task.assigned': { agentId: string; taskId: string; taskType: string; payload?: unknown }\n\n // ── Enrichment ──────────────────────────────────────────────────────────\n 'enrichment.embedding.stored': {\n readonly deviceId: number\n readonly trackId: string\n readonly class: string\n readonly embeddingId: string\n readonly modelId: string\n readonly embeddingDim: number\n readonly inferenceMs: number\n readonly timestamp: number\n }\n 'enrichment.scene.state-changed': {\n readonly deviceId: number\n readonly monitorId: string\n readonly monitorLabel: string\n readonly previousState: string\n readonly currentState: string\n readonly confidence: number\n readonly timestamp: number\n }\n 'enrichment.activity.summary': {\n readonly deviceId: number\n readonly periodStart: number\n readonly periodEnd: number\n readonly objectCounts: Readonly<Record<string, number>>\n readonly zoneActivity: readonly { readonly zoneId: string; readonly entries: number; readonly exits: number; readonly avgDwellMs: number }[]\n readonly stateChanges: readonly { readonly monitorId: string; readonly from: string; readonly to: string; readonly timestamp: number }[]\n readonly activityLevel: 'none' | 'low' | 'medium' | 'high'\n }\n\n // ── Alerts ──────────────────────────────────────────────────────────────\n 'alert.created': { id: string; category: string; severity: string; title: string; status: string }\n 'alert.updated': { alertId: string; patch: Record<string, unknown> }\n\n // ── Stream broker health ────────────────────────────────────────────────\n /**\n * Per-broker stream health transition. Fired by the stream-broker\n * watchdog. `camStreamId` is the canonical identity of the source\n * (every broker is keyed by its cam stream now); `profile` is the\n * profile slot currently bound to it (or `null` if the broker is\n * only kept alive by a manual activation).\n */\n 'stream.online': {\n readonly deviceId: number\n readonly camStreamId: string\n readonly profile: 'high' | 'mid' | 'low' | null\n readonly brokerId: string\n readonly sourceType: string\n readonly lastPacketAt: number\n readonly reason?: string\n }\n 'stream.offline': {\n readonly deviceId: number\n readonly camStreamId: string\n readonly profile: 'high' | 'mid' | 'low' | null\n readonly brokerId: string\n readonly sourceType: string\n readonly lastPacketAt: number\n readonly reason?: string\n }\n\n // ── Retention ───────────────────────────────────────────────────────────\n 'retention.cleanup': {\n readonly deletedEvents?: number\n readonly deletedAudioRecords?: number\n readonly deletedSnapshots?: number\n readonly deletedCount?: number\n readonly freedMB?: number\n }\n}\n\n/** All known event category strings (derived from EventCatalog keys) */\nexport type KnownEventCategory = keyof EventCatalog\n\n// ---------------------------------------------------------------------------\n// SystemEvent — backward-compatible base type\n// ---------------------------------------------------------------------------\n\n/**\n * System event — `data` is `Record<string, unknown>` for backward compatibility.\n * Use `TypedSystemEvent<C>` or `narrowEvent()` for typed access to data.\n */\nexport interface SystemEvent {\n id: string\n timestamp: Date\n source: EventSource\n category: string\n data: Record<string, unknown>\n /**\n * Propagation chain when the event was re-emitted by the\n * `DeviceEventPropagator`. `via[0]` is the originating source (the\n * child that produced the event); subsequent entries walk up the\n * parent chain. Absent on the original emission and on events that\n * don't belong to a device-rooted source.\n *\n * Consumers that want ONLY direct events filter `ev.via === undefined`.\n * Consumers that want everything reaching a device (incl. children)\n * filter by `source.id === parentId` and accept any `via`.\n */\n via?: readonly EventSource[]\n}\n\n/**\n * A system event with a known category — `data` is typed.\n * Use this when you know the exact category at compile time.\n */\nexport type TypedSystemEvent<C extends KnownEventCategory> = {\n id: string\n timestamp: Date\n source: EventSource\n category: C\n data: EventCatalog[C]\n via?: readonly EventSource[]\n}\n\n// ---------------------------------------------------------------------------\n// Event filter\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Event filter — accepts known categories (strict) or strings (globs/custom)\n// ---------------------------------------------------------------------------\n\n/** A category filter value: a known category, a glob string, or an array of either */\nexport type EventCategoryFilter = KnownEventCategory | (string & {}) | readonly (KnownEventCategory | (string & {}))[]\n\nexport interface EventFilter {\n source?: EventSource\n /** Filter by top-level agent (e.g. 'hub', 'agent-a1b2c3'). Matches source.nodeId with prefix match so 'hub' includes 'hub/pipeline'. Same semantics as logs tags.agentId. */\n agentId?: string\n /** Filter by addon that originated the event. Matches source.addonId or source.id when type='addon'. */\n addonId?: string\n /** Filter by device the event relates to. Matches source.deviceId or source.id when type='device'. */\n deviceId?: number\n /** Category — known category (typed), glob (e.g. 'device.*'), or array */\n category?: EventCategoryFilter\n since?: Date\n}\n\n// ---------------------------------------------------------------------------\n// Typed event filter — for subscribe overloads\n// ---------------------------------------------------------------------------\n\nexport interface TypedEventFilter<C extends KnownEventCategory> {\n source?: EventSource\n category: C | readonly C[]\n since?: Date\n}\n\n// ---------------------------------------------------------------------------\n// IEventBus — emit and subscribe are typed when using known categories\n// ---------------------------------------------------------------------------\n\nexport interface IEventBus {\n /** Emit a known-category event with typed payload */\n emit<C extends KnownEventCategory>(event: TypedSystemEvent<C>): void\n /** Emit an arbitrary event (custom/unknown category) */\n emit(event: SystemEvent): void\n\n /** Subscribe to a known category — handler receives typed event */\n subscribe<C extends KnownEventCategory>(filter: TypedEventFilter<C>, handler: (event: TypedSystemEvent<C>) => void): () => void\n /** Subscribe with a generic filter — handler receives base SystemEvent */\n subscribe(filter: EventFilter, handler: (event: SystemEvent) => void): () => void\n\n /** Get recent events */\n getRecent(filter?: EventFilter, limit?: number): readonly SystemEvent[]\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Narrow a SystemEvent to a typed event by checking its category.\n * Returns `true` (and narrows the type) if the category matches.\n *\n * @example\n * ```typescript\n * eventBus.subscribe({ category: 'addon.started' }, (event) => {\n * if (isEvent(event, 'addon.started')) {\n * event.data.addonId // ✓ typed as string\n * }\n * })\n * ```\n */\nexport function isEvent<C extends KnownEventCategory>(\n event: SystemEvent,\n category: C,\n): event is TypedSystemEvent<C> {\n return event.category === category\n}\n\n/**\n * Create a typed event with less boilerplate.\n *\n * @example\n * ```typescript\n * eventBus.emit(createEvent('addon.started', { type: 'addon', id: 'pipeline' }, {\n * addonId: 'pipeline',\n * packageVersion: '0.1.8',\n * }))\n * ```\n */\nexport function createEvent<C extends KnownEventCategory>(\n category: C,\n source: EventSource,\n data: EventCatalog[C],\n): TypedSystemEvent<C> {\n return {\n id: typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2),\n timestamp: new Date(),\n source,\n category,\n data,\n }\n}\n\n// ---------------------------------------------------------------------------\n// System readiness protocol — low-level producer emission helper.\n// Consumers (kernel `ReadinessRegistry`) and their `awaitReady` /\n// `onReadyState` APIs live in `@camstack/kernel/src/readiness/`.\n// ---------------------------------------------------------------------------\n\n/**\n * Emit a `system.ready-state` event for a capability. Caller supplies a\n * per-process `generation` string — stable across a single process\n * lifetime, changes on restart — which consumer-side registries use to\n * derive a monotonic `epoch` without requiring the emitter to\n * coordinate.\n */\nexport function emitReadiness(\n bus: IEventBus,\n params: {\n readonly capName: string\n readonly scope: ReadinessScope\n readonly state: ReadinessState\n readonly generation: string\n readonly sourceNodeId: string\n readonly ts?: number\n },\n): void {\n const ts = params.ts ?? Date.now()\n bus.emit(createEvent(\n 'system.ready-state',\n { type: 'capability', id: params.capName, nodeId: params.sourceNodeId },\n {\n capName: params.capName,\n scope: params.scope,\n state: params.state,\n generation: params.generation,\n sourceNodeId: params.sourceNodeId,\n ts,\n },\n ))\n}\n","import type { AddonContext, AddonInitResult, CapabilitiesAccess, ProviderRegistration } from '../interfaces/addon.js'\nimport { EventCategory } from '../enums/event-category.js'\nimport type {\n ConfigUISchema, ConfigUISchemaWithValues,\n ConfigField, ConfigSection,\n} from '../interfaces/config-ui.js'\nimport { hydrateSchema } from '../interfaces/config-ui.js'\nimport { emitReadiness } from '../interfaces/event-bus.js'\n\nexport type { CapabilitiesAccess }\n\n// ── Typed field helper ──────────────────────────────────────────────────\n/**\n * Utility type that constrains a ConfigField's `key` to only accept\n * keys of TConfig. Usage inside schema builders:\n *\n * ```ts\n * protected globalSettingsSchema() {\n * return this.schema({\n * sections: [{\n * id: 'main',\n * title: 'Settings',\n * fields: [\n * this.field({ type: 'number', key: 'maxRetries', label: 'Max Retries' }),\n * // ^^^^^^^^^^^ — autocomplete + compile error if wrong\n * ],\n * }],\n * })\n * }\n * ```\n */\ntype TypedConfigField<TConfig extends object> =\n ConfigField extends infer F\n ? F extends { readonly key: string }\n ? Omit<F, 'key'> & { readonly key: keyof TConfig & string }\n : F // valueless fields (separator, info) pass through\n : never\n\n/**\n * A ConfigSection whose fields are key-constrained to TConfig.\n */\ninterface TypedConfigSection<TConfig extends object> extends Omit<ConfigSection, 'fields'> {\n readonly fields: ReadonlyArray<TypedConfigField<TConfig> | ConfigField>\n}\n\n/**\n * A ConfigUISchema whose sections use typed fields.\n */\ninterface TypedConfigUISchema<TConfig extends object> extends Omit<ConfigUISchema, 'sections'> {\n readonly sections: ReadonlyArray<TypedConfigSection<TConfig>>\n}\n\n// ── BaseAddon ───────────────────────────────────────────────────────────\n\n/**\n * Base class for CamStack addons. Eliminates settings boilerplate:\n *\n * - Typed `config` property with automatic resolution from store + defaults\n * - `getGlobalSettings()` / `updateGlobalSettings()` auto-implemented\n * - `getAddonSettings()` / `updateAddonSettings()` auto-implemented\n * - `getDeviceSettings()` / `updateDeviceSettings()` auto-implemented\n * - `ctx` accessor for the AddonContext (no manual `this.ctxRef` storage)\n * - `field()` helper that constrains field keys to TConfig keys\n * - `schema()` helper that validates the full schema structure\n *\n * Subclasses override:\n * - `onInitialize()` — addon-specific init logic, return ProviderRegistration[]\n * - `onShutdown()` — cleanup\n * - `globalSettingsSchema()` / `deviceSettingsSchema()` — UI schemas\n *\n * @example\n * ```ts\n * interface MyConfig {\n * maxRetries: number\n * endpoint: string\n * }\n *\n * export default class MyAddon extends BaseAddon<MyConfig> {\n * constructor() {\n * super({ maxRetries: 3, endpoint: 'http://localhost' })\n * }\n *\n * protected globalSettingsSchema() {\n * return this.schema({\n * sections: [{\n * id: 'main', title: 'Settings',\n * fields: [\n * this.field({ type: 'number', key: 'maxRetries', label: 'Max Retries', default: 3 }),\n * this.field({ type: 'text', key: 'endpoint', label: 'Endpoint' }),\n * ],\n * }],\n * })\n * }\n *\n * protected async onInitialize(): Promise<ProviderRegistration[]> {\n * const client = new Client(this.config.endpoint, this.config.maxRetries)\n * return [{ capability: 'my-feature', provider: client }]\n * }\n * }\n * ```\n */\nexport abstract class BaseAddon<TConfig extends object = Record<string, unknown>> {\n private _ctx: AddonContext | null = null\n private _config: TConfig\n /**\n * Per-process random id used as the `generation` stamp on every\n * `system.ready-state` event this addon emits. Constant for the\n * lifetime of this addon instance (== one process boot); consumer-\n * side registries derive a monotonic `epoch` by watching for\n * generation transitions.\n */\n private readonly _readinessGeneration: string =\n typeof crypto !== 'undefined' && crypto.randomUUID\n ? crypto.randomUUID()\n : Math.random().toString(36).slice(2, 14)\n /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */\n private _registeredCapNames: readonly string[] = []\n\n /** Default config values. Provided via constructor. */\n protected readonly defaults: TConfig\n\n constructor(defaults: TConfig) {\n this.defaults = defaults\n this._config = { ...defaults }\n }\n\n /**\n * Override to opt out of the automatic `system.ready-state` emission.\n * Returns `true` by default — addons that don't map cleanly to the\n * readiness protocol (e.g. pure collection providers whose readiness\n * is already reported per-device by upstream) can override to `false`\n * and emit manually.\n */\n protected get autoEmitReadiness(): boolean { return true }\n\n // ── Public accessors ──────────────────────────────────────────────────\n\n /** The AddonContext, available after initialize(). */\n get ctx(): AddonContext {\n if (!this._ctx) throw new Error(`${this.constructor.name}: ctx accessed before initialize()`)\n return this._ctx\n }\n\n /**\n * Non-throwing ctx accessor for code paths that can legitimately run\n * before `initialize()` has resolved — most commonly cap handlers the\n * hub invokes eagerly at page load (device-details aggregator pings\n * every addon's `getDeviceSettingsContribution` as soon as the page\n * mounts). Prefer `ctx` for the normal case; reach for this only in\n * capability methods that may be queried before the addon is wired up.\n */\n protected get ctxIfReady(): AddonContext | null {\n return this._ctx\n }\n\n /** Current resolved config (defaults merged with persisted store values). */\n get config(): Readonly<TConfig> {\n return this._config\n }\n\n // ── ICamstackAddon lifecycle ──────────────────────────────────────────\n\n async initialize(context: AddonContext): Promise<AddonInitResult | void> {\n this._ctx = context\n await this.resolveConfig()\n const result = await this.onInitialize()\n this.emitLifecycle(EventCategory.AddonStarted)\n const normalized = normalizeAddonInitResult(result)\n const providers: readonly ProviderRegistration[] =\n normalized && 'providers' in normalized && normalized.providers ? normalized.providers : []\n this._registeredCapNames = providers.map(p => p.capability.name)\n return normalized\n }\n\n /**\n * Called by the isolated-process runner AFTER `broker.start()`.\n * In-process addons never need this because the hub broker is already\n * running when `initialize()` fires. For forked children the broker\n * starts AFTER `initialize()`, so the readiness emit inside initialize()\n * fires before the broker can broadcast it. This method re-emits the\n * ready state once the transport is live.\n */\n postBrokerStart(): void {\n if (this.autoEmitReadiness && this._registeredCapNames.length > 0) {\n this.emitReadinessForProviders('ready')\n }\n }\n\n /**\n * Called by the isolated-process runner / agent bootstrap once the broker\n * has started AND the hub node is connected — the moment `ctx.api.*` calls\n * to hub-provided capabilities become safe. Wrap any bootstrap work that\n * must query the hub (device restore, settings fetch, initial sync) in\n * `onHubConnected()` rather than `onInitialize()` — during `initialize()`\n * on a worker the broker is not yet connected and remote cap calls either\n * time out or throw \"Service not found\".\n *\n * Default implementation is a no-op. Override in subclasses that need it.\n * Never fires on hub-local in-process addons (the hub is its own node).\n */\n async onHubReachable(): Promise<void> {\n // no-op — override in subclasses\n }\n\n async shutdown(): Promise<void> {\n this.emitLifecycle(EventCategory.AddonStopped)\n if (this.autoEmitReadiness) this.emitReadinessForProviders('down')\n await this.onShutdown()\n for (const unsub of this._subscriptions) unsub()\n this._subscriptions = []\n this._ctx = null\n }\n\n // ── Subclass hooks ────────────────────────────────────────────────────\n\n /**\n * Addon-specific initialization. Called after config is resolved.\n *\n * Subclasses may return:\n * - an array of `ProviderRegistration` (back-compat, most common),\n * - an `AddonInitResult` envelope to also declare `customActions`,\n * - `void` for pure consumers.\n */\n protected abstract onInitialize(): Promise<ProviderRegistration[] | AddonInitResult | void>\n\n /** Addon-specific cleanup. Override if needed. */\n protected async onShutdown(): Promise<void> { /* no-op by default */ }\n\n /**\n * Called after config is resolved during updateGlobalSettings/updateAddonSettings.\n * Override to react to config changes (e.g. restart a sampler, reconnect a service).\n * Not called during initialize() — use onInitialize() for initial setup.\n */\n protected async onConfigChanged(): Promise<void> { /* no-op by default */ }\n\n // ── Schema helpers (typed) ────────────────────────────────────────────\n\n /**\n * Create a ConfigField with `key` constrained to keys of TConfig.\n * Provides autocomplete and compile-time validation.\n */\n protected field<F extends ConfigField>(\n field: F extends { readonly key: string }\n ? Omit<F, 'key'> & { readonly key: keyof TConfig & string }\n : F\n ): ConfigField {\n return field as ConfigField\n }\n\n /**\n * Create a full ConfigUISchema with typed sections.\n * Fields created via `this.field()` get key validation automatically.\n */\n protected schema(schema: TypedConfigUISchema<TConfig>): ConfigUISchema {\n return schema as unknown as ConfigUISchema\n }\n\n // ── Settings schemas (override to provide UI) ─────────────────────────\n\n /** Override to provide global-level settings UI schema. */\n protected globalSettingsSchema(): ConfigUISchema | null { return null }\n\n /** Override to provide device-level settings UI schema. */\n protected deviceSettingsSchema(): ConfigUISchema | null { return null }\n\n // ── Two-level settings API (auto-implemented) ────────────────────────\n //\n // Note: the former three-level split (addon / global / device) collapsed\n // to two. `addon` and `global` both wrote to the same `writeAddonStore`\n // blob and every addon used exactly one of them; the distinction was\n // never semantically load-bearing. `global` won because it was the\n // widely-used one and the name reads naturally (per-node addon config).\n\n async getGlobalSettings(overlay?: Record<string, unknown>): Promise<ConfigUISchemaWithValues> {\n const schema = this.globalSettingsSchema()\n if (!schema) return { sections: [] }\n const raw = (await this._ctx?.settings?.readAddonStore()) ?? {}\n return hydrateSchema(schema, overlay ? { ...raw, ...overlay } : raw)\n }\n\n async updateGlobalSettings(patch: Partial<TConfig>): Promise<void> {\n await this._ctx?.settings?.writeAddonStore(patch as Record<string, unknown>)\n await this.resolveConfig()\n await this.onConfigChanged()\n this.emitLifecycle(EventCategory.AddonUpdated, { level: 'global' })\n this.maybeAutoRestart(patch, this.globalSettingsSchema())\n }\n\n /**\n * If any field in `patch` is marked `requiresRestart` in `schema`,\n * schedule an addon restart for the next tick. Deferred via\n * `setImmediate` so the tRPC mutation that triggered the write has\n * time to return its response before the addon is torn down and\n * re-initialised by `AddonRegistryService.restartAddon`.\n */\n private maybeAutoRestart(\n patch: Partial<TConfig>,\n schema: ConfigUISchema | null,\n ): void {\n if (!schema) return\n const restartKeys = new Set<string>()\n for (const section of schema.sections) {\n for (const field of section.fields) {\n if (field.type === 'separator' || field.type === 'info') continue\n if ((field as { readonly requiresRestart?: boolean }).requiresRestart) {\n restartKeys.add((field as { readonly key: string }).key)\n }\n }\n }\n if (restartKeys.size === 0) return\n const changed = Object.keys(patch).some((k) => restartKeys.has(k))\n if (!changed) return\n const ctx = this._ctx\n if (!ctx) return\n const addonId = ctx.id\n setImmediate(() => {\n const api = ctx.api as unknown as {\n addons?: { restartAddon?: { mutate: (input: { addonId: string }) => Promise<unknown> } }\n }\n api.addons?.restartAddon?.mutate({ addonId })\n .then(() => {\n ctx.logger.info('addon auto-restart triggered by restart-required setting change', {\n meta: { changedFields: Object.keys(patch).filter((k) => restartKeys.has(k)) },\n })\n })\n .catch((err: unknown) => {\n ctx.logger.error('addon auto-restart failed', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n })\n }\n\n async getDeviceSettings(deviceId: number): Promise<ConfigUISchemaWithValues> {\n const schema = this.deviceSettingsSchema()\n if (!schema) return { sections: [] }\n const raw = (await this._ctx?.settings?.readDeviceStore(deviceId)) ?? {}\n return hydrateSchema(schema, raw)\n }\n\n async updateDeviceSettings(deviceId: number, patch: Record<string, unknown>): Promise<void> {\n await this._ctx?.settings?.writeDeviceStore(deviceId, patch)\n }\n\n // ── Event subscriptions (auto-cleanup on shutdown) ────────────────────\n\n private _subscriptions: Array<() => void> = []\n\n /**\n * Subscribe to an event bus category. The subscription is automatically\n * unsubscribed on shutdown — no manual cleanup needed.\n *\n * @example\n * ```ts\n * this.subscribe({ category: EventCategory.DeviceRegistered }, (event) => {\n * void this.handleDevice(event)\n * })\n * ```\n */\n /**\n * Subscribe to `system.ready-state` events for one or more capabilities.\n * Abstracts the boilerplate type-narrowing + nodeId extraction that every\n * resilience subscription duplicates. Cleanup is automatic (registered on\n * `_subscriptions` like a normal `subscribe` call).\n */\n protected watchCapability(\n capNames: string | readonly string[],\n handlers: {\n readonly onDown?: (nodeId: string, capName: string) => void\n readonly onReady?: (nodeId: string, capName: string) => void\n },\n ): void {\n const names = Array.isArray(capNames) ? capNames : [capNames]\n const nameSet = new Set<string>(names as string[])\n this.subscribe(\n { category: 'system.ready-state' },\n (event) => {\n const data = event.data as {\n readonly capName?: unknown\n readonly state?: unknown\n readonly scope?: { readonly type?: unknown; readonly nodeId?: unknown }\n }\n if (typeof data.capName !== 'string') return\n if (!nameSet.has(data.capName)) return\n if (data.scope?.type !== 'node') return\n const nodeId = data.scope.nodeId\n if (typeof nodeId !== 'string' || nodeId.length === 0) return\n const capName = data.capName\n if (data.state === 'down') {\n handlers.onDown?.(nodeId, capName)\n } else if (data.state === 'ready') {\n handlers.onReady?.(nodeId, capName)\n }\n },\n )\n }\n\n protected subscribe<C extends import('../interfaces/event-bus.js').KnownEventCategory>(\n filter: import('../interfaces/event-bus.js').TypedEventFilter<C>,\n handler: (event: import('../interfaces/event-bus.js').TypedSystemEvent<C>) => void,\n ): void\n protected subscribe(\n filter: import('../interfaces/event-bus.js').EventFilter,\n handler: (event: import('../interfaces/event-bus.js').SystemEvent) => void,\n ): void\n protected subscribe(\n filter: import('../interfaces/event-bus.js').EventFilter,\n handler: (event: import('../interfaces/event-bus.js').SystemEvent) => void,\n ): void {\n const unsub = this.ctx.eventBus.subscribe(filter, handler)\n this._subscriptions.push(unsub)\n }\n\n // ── Lifecycle event emission ────────────────────────────────────────────\n\n private emitLifecycle(category: EventCategory, data?: Record<string, unknown>): void {\n try {\n this._ctx?.eventBus.emit({\n id: `${this._ctx.id}-${Date.now()}`,\n timestamp: new Date(),\n source: { type: 'addon', id: this._ctx.id, nodeId: this._ctx.kernel.localNodeId ?? 'hub' },\n category,\n data: data ?? {},\n })\n } catch { /* best-effort — don't crash addon if event bus is unavailable */ }\n }\n\n /**\n * Emit a `system.ready-state` event for every capability this addon\n * registered at init. Scope is `{type:'node', nodeId}` — readiness is\n * tied to the node that hosts the provider. Consumers that care about\n * per-device readiness can subscribe with `{type:'device', ...}` if\n * the provider emits at finer granularity (collection addons may\n * opt-out via `autoEmitReadiness` and emit manually).\n */\n private emitReadinessForProviders(state: 'ready' | 'down'): void {\n const ctx = this._ctx\n if (!ctx) return\n if (this._registeredCapNames.length === 0) return\n // Forked child processes have a hierarchical nodeId (e.g. 'hub/detection-pipeline').\n // Readiness must be scoped to the logical parent node so orchestrators that\n // subscribe to 'hub' or 'dev-agent-0' receive the event from their children too.\n const rawNodeId = ctx.kernel?.localNodeId ?? 'hub'\n const nodeId = rawNodeId.includes('/') ? rawNodeId.split('/')[0]! : rawNodeId\n for (const capName of this._registeredCapNames) {\n try {\n emitReadiness(ctx.eventBus, {\n capName,\n scope: { type: 'node', nodeId },\n state,\n generation: this._readinessGeneration,\n sourceNodeId: nodeId,\n })\n } catch { /* best-effort — a broken bus must not take the addon down */ }\n }\n }\n\n // ── Common helpers ─────────────────────────────────────────────────────\n\n /**\n * Resolve the shared models directory path via the storage capability.\n * Falls back to `camstack-data/models` if storage is unavailable.\n * Used by inference addons (detection-pipeline, audio-classifier, embedding-encoder).\n */\n protected async resolveModelsDir(): Promise<string> {\n return this.ctx.api.storage.resolve.query({ location: 'models', relativePath: '' })\n .catch(() => 'camstack-data/models')\n }\n\n /**\n * Access the runtime capability registry for in-process provider lookups.\n * Returns null if the registry is not available (e.g. on agents).\n * Used by addons that consume other capabilities directly (snapshot, stream-broker, enrichment).\n */\n protected get capabilities(): CapabilitiesAccess | null {\n const capCtx = this.ctx as { capabilities?: CapabilitiesAccess }\n return capCtx.capabilities ?? null\n }\n\n // ── Config resolution ─────────────────────────────────────────────────\n\n /**\n * Resolve config by merging defaults with persisted store values.\n * Called automatically during initialize() and after every updateSettings().\n *\n * The merge is shallow: each key in `defaults` is checked against the store.\n * Only keys present in defaults are read — the store can contain extra keys\n * (e.g. from older versions) without polluting the typed config.\n */\n protected async resolveConfig(): Promise<void> {\n const stored = await this.readAddonStoreWithRetry()\n const resolved = { ...this.defaults }\n\n for (const key of Object.keys(this.defaults) as Array<keyof TConfig & string>) {\n const storedValue = stored[key]\n if (storedValue !== undefined && storedValue !== null) {\n const defaultType = typeof this.defaults[key]\n if (typeof storedValue === defaultType) {\n ;(resolved as Record<string, unknown>)[key] = storedValue\n }\n }\n }\n\n this._config = resolved\n }\n\n /**\n * Wrap `ctx.settings.readAddonStore()` with a short retry budget so a\n * transient settings-store outage (mid-restart of sqlite-settings, tsx-watch\n * swap, or any race where the SqliteSettingsBackend is between shutdown and\n * re-initialize) doesn't propagate up into `initialize()` and leave the\n * addon permanently broken.\n *\n * Retry only the two known infra fingerprints. Anything else propagates so\n * real bugs surface immediately. After the budget expires we fall back to\n * `{}` (defaults) — the addon's first successful patch will rehydrate.\n */\n private async readAddonStoreWithRetry(): Promise<Record<string, unknown>> {\n const settings = this._ctx?.settings\n if (!settings) return {}\n const delaysMs = [150, 350, 600, 900]\n let lastErr: unknown\n for (let attempt = 0; attempt <= delaysMs.length; attempt++) {\n try {\n return (await settings.readAddonStore()) ?? {}\n } catch (err) {\n lastErr = err\n const msg = err instanceof Error ? err.message : String(err)\n const transient =\n msg.includes('SqliteSettingsBackend not initialized') ||\n msg.includes('provider not available')\n if (!transient) throw err\n if (attempt === delaysMs.length) break\n await new Promise<void>((r) => setTimeout(r, delaysMs[attempt]))\n }\n }\n this._ctx?.logger?.warn?.('readAddonStore: settings-store unavailable after retries — using defaults', {\n meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) },\n })\n return {}\n }\n}\n\n/**\n * Normalize an `ICamstackAddon.initialize()` return value into the\n * `AddonInitResult` envelope. Arrays are wrapped into `{ providers }`;\n * envelopes pass through; void stays void.\n *\n * Exported so the kernel boot path and the backend addon registry can\n * share the same normalizer instead of duplicating the shim. Addons that\n * extend `BaseAddon` already emit the envelope, so this helper is only a\n * safety net for direct `ICamstackAddon` implementations (mostly tests).\n */\nexport function normalizeAddonInitResult(\n result: ProviderRegistration[] | AddonInitResult | void,\n): AddonInitResult | void {\n if (result == null) return\n if (Array.isArray(result)) return { providers: result }\n return result\n}\n\n","/**\n * ReadinessRegistry — kernel-side tracker for capability readiness\n * transitions. Spec: `docs/superpowers/specs/2026-04-19-system-readiness-design.md`.\n *\n * Each process owns one registry. The registry:\n * - Listens to `system.ready-state` events on the event bus.\n * - Maintains a `Map<key, ReadinessRecord>` snapshot of the most\n * recently observed state + generation + derived epoch.\n * - Exposes `emitReady` / `emitStarting` / `emitDown` helpers for\n * local producers (which stamp the process-wide `generation`).\n * - Exposes `awaitReady(capName, scope, {timeoutMs, signal})` and\n * `onReadyState(capName, scope, handler)` for consumers.\n *\n * Epoch derivation:\n * - First `ready` observed for a `(capName, scope)` → epoch = 1.\n * - `ready` received with a generation different from the last\n * `currentGeneration` → epoch++.\n * - Same-generation repeated `ready` events → epoch unchanged\n * (idempotent keepalive; e.g. re-emit on `$node.connected`).\n *\n * Not persisted. Rebuilt from the event stream + optional boot-time\n * snapshot rehydration by the caller.\n */\nimport type {\n IEventBus,\n SystemReadyStatePayload,\n ReadinessScope,\n ReadinessState,\n} from '../interfaces/event-bus.js'\nimport type { IScopedLogger } from '../interfaces/logging.js'\nimport type {\n IReadinessRegistry,\n IReadinessHandler,\n IAwaitReadyOptions,\n IReadinessTransition,\n} from '../interfaces/readiness.js'\nimport { createEvent } from '../interfaces/event-bus.js'\n\n/**\n * Concrete types live here; `interfaces/readiness.ts` declares the\n * matching narrow interfaces (`IReadinessTransition`,\n * `IReadinessHandler`, `IAwaitReadyOptions`) used by `AddonContext`\n * and other consumer-facing types.\n */\nexport type ReadinessTransition = IReadinessTransition\nexport type ReadinessHandler = IReadinessHandler\nexport type AwaitReadyOptions = IAwaitReadyOptions\n\nexport class ReadinessTimeoutError extends Error {\n readonly capName: string\n readonly scope: ReadinessScope\n readonly waitedMs: number\n constructor(capName: string, scope: ReadinessScope, waitedMs: number) {\n super(`Timed out waiting for ${capName} (${scopeKey(scope)}) to become ready after ${waitedMs}ms`)\n this.name = 'ReadinessTimeoutError'\n this.capName = capName\n this.scope = scope\n this.waitedMs = waitedMs\n }\n}\n\ninterface ReadinessRecord {\n capName: string\n scope: ReadinessScope\n state: ReadinessState\n generation: string\n epoch: number\n lastChange: number\n /**\n * Node that emitted the latest transition for this record. Needed for\n * `agent.offline` demux of `device`-scoped caps (scope itself carries\n * no node info). For `node`-scoped caps this matches `scope.nodeId`.\n */\n sourceNodeId: string\n}\n\ninterface Subscription {\n readonly capName: string\n readonly scope: ReadinessScope\n readonly handler: ReadinessHandler\n}\n\n/**\n * Build a canonical string key for a `(capName, scope)` pair. Used as\n * the snapshot map key and for log/debug output.\n */\nexport function readinessKey(capName: string, scope: ReadinessScope): string {\n return `${capName}|${scopeKey(scope)}`\n}\n\nfunction scopeKey(scope: ReadinessScope): string {\n switch (scope.type) {\n case 'global': return 'global'\n case 'node': return `node:${scope.nodeId}`\n case 'device': return `device:${scope.deviceId}`\n }\n}\n\nfunction scopesEqual(a: ReadinessScope, b: ReadinessScope): boolean {\n if (a.type !== b.type) return false\n if (a.type === 'global' || b.type === 'global') return true\n if (a.type === 'node' && b.type === 'node') return a.nodeId === b.nodeId\n if (a.type === 'device' && b.type === 'device') return a.deviceId === b.deviceId\n return false\n}\n\nexport interface ReadinessRegistryOptions {\n readonly eventBus: IEventBus\n readonly sourceNodeId: string\n /**\n * Per-process generation id — stamped on every emit made via this\n * registry's `emitReady`/`emitStarting`/`emitDown` helpers. Caller\n * owns the value so it can survive across subsystem re-bindings\n * within the same process. Default: a random 12-char id.\n */\n readonly generation?: string\n readonly logger?: IScopedLogger\n /**\n * Clock source. Injectable for tests.\n */\n readonly now?: () => number\n}\n\nexport class ReadinessRegistry implements IReadinessRegistry {\n private readonly bus: IEventBus\n private readonly sourceNodeId: string\n private readonly logger: IScopedLogger | undefined\n private readonly now: () => number\n private readonly generation: string\n private readonly snapshot = new Map<string, ReadinessRecord>()\n private readonly subscriptions = new Set<Subscription>()\n private readonly unsubscribeBus: () => void\n private readonly unsubscribeAgentOffline: () => void\n\n constructor(options: ReadinessRegistryOptions) {\n this.bus = options.eventBus\n this.sourceNodeId = options.sourceNodeId\n this.logger = options.logger\n this.now = options.now ?? (() => Date.now())\n this.generation = options.generation ?? randomGeneration()\n\n this.unsubscribeBus = this.bus.subscribe(\n { category: 'system.ready-state' },\n (event) => this.ingest(event.data as SystemReadyStatePayload),\n )\n // Auto-heal on agent disconnect: when `agent.offline` fires, walk\n // our own snapshot and synthesize `down` transitions for every\n // node-scoped cap bound to the disconnected nodeId. This covers\n // ungraceful producer deaths (process crash, network blip,\n // SIGKILL) where the producer's own shutdown hook never fires —\n // subscribers would otherwise keep waiting on a stale `ready`.\n // Synthesized transitions are local-only (we don't re-emit on the\n // bus) — every registry reacts independently to the same agent.offline.\n this.unsubscribeAgentOffline = this.bus.subscribe(\n { category: 'agent.offline' },\n (event) => this.synthesizeDownForNode((event.data as { agentId: string }).agentId),\n )\n // Hydrate from recent bus events so a late-starting registry\n // (e.g. an addon subscribed mid-session, or a hub reboot where\n // producers haven't re-emitted yet) picks up already-fired\n // readiness transitions. Safe to run after subscribe: ingest()\n // is idempotent for same-generation replays.\n if (typeof this.bus.getRecent === 'function') {\n try {\n const recent = this.bus.getRecent({ category: 'system.ready-state' })\n for (const event of recent) {\n this.ingest(event.data as SystemReadyStatePayload)\n }\n } catch {\n // getRecent is optional — a minimal test bus may not support it.\n }\n }\n }\n\n /** Release the event-bus subscription. Idempotent. */\n close(): void {\n this.unsubscribeBus()\n this.unsubscribeAgentOffline()\n this.subscriptions.clear()\n }\n\n /** Current snapshot for a `(capName, scope)` pair, or `null` if never seen. */\n get(capName: string, scope: ReadinessScope): ReadinessRecord | null {\n return this.snapshot.get(readinessKey(capName, scope)) ?? null\n }\n\n /**\n * Serializable snapshot for cross-process transport. Returns an array\n * of `ReadinessRecord` plain objects — safe for MsgPack / JSON\n * transport. Used by the hub's `$readiness.getSnapshot` Moleculer\n * action; consumers hydrate their local registry from the result.\n */\n getSnapshotForTransport(): readonly ReadinessRecord[] {\n return Array.from(this.snapshot.values())\n }\n\n /**\n * Hydrate the snapshot from an authoritative source. Entries already\n * present locally are skipped — live deltas (received via the event\n * bus subscription) always take precedence over the snapshot. For\n * each newly hydrated entry, a one-shot transition is dispatched to\n * matching subscriptions so pending `awaitReady` callers unblock\n * without having to wait for a fresh event.\n *\n * Local `epoch` is reset to 1 per entry — consumer-side epoch is\n * derived from observed generation transitions, so this mirrors the\n * value that would have been assigned had the consumer observed the\n * first `ready` event directly.\n */\n hydrate(records: readonly ReadinessRecord[]): void {\n const now = this.now()\n for (const record of records) {\n const key = readinessKey(record.capName, record.scope)\n if (this.snapshot.has(key)) continue\n const hydrated: ReadinessRecord = {\n capName: record.capName,\n scope: record.scope,\n state: record.state,\n generation: record.generation,\n epoch: 1,\n lastChange: now,\n sourceNodeId: record.sourceNodeId,\n }\n this.snapshot.set(key, hydrated)\n if (this.logger) {\n this.logger.debug(\n `readiness: ${record.capName} (${scopeKey(record.scope)}) → ${record.state} (hydrated, gen=${record.generation.slice(0, 6)})`,\n )\n }\n const transition: ReadinessTransition = {\n capName: record.capName,\n scope: record.scope,\n state: record.state,\n epoch: 1,\n generation: record.generation,\n sourceNodeId: 'hydrated',\n ts: now,\n durationInPrevState: 0,\n }\n for (const sub of this.subscriptions) {\n if (sub.capName !== record.capName) continue\n if (!scopesEqual(sub.scope, record.scope)) continue\n try {\n sub.handler(transition)\n } catch (err) {\n this.logger?.warn(\n `readiness hydrate handler threw for ${record.capName}: ${(err as Error).message ?? String(err)}`,\n )\n }\n }\n }\n }\n\n /** Shallow copy of the full snapshot — mainly for diagnostics/tests. */\n getAll(): ReadonlyMap<string, ReadinessRecord> {\n return new Map(this.snapshot)\n }\n\n /**\n * Emit a `ready` transition for a locally-owned capability. The\n * payload carries this registry's `generation` so remote registries\n * can derive their own `epoch`.\n */\n emitReady(capName: string, scope: ReadinessScope): void {\n this.emitTransition(capName, scope, 'ready')\n }\n\n emitStarting(capName: string, scope: ReadinessScope): void {\n this.emitTransition(capName, scope, 'starting')\n }\n\n emitDown(capName: string, scope: ReadinessScope): void {\n this.emitTransition(capName, scope, 'down')\n }\n\n /**\n * One-shot: resolve once the cap is `ready`. Reads the snapshot\n * first — if already ready, resolves synchronously on the next tick.\n *\n * Default behaviour is **wait indefinitely** (timeoutMs = `Infinity`):\n * the orchestrator and other callers never want to attach a camera\n * with empty steps just because a cap took longer than 30s to come\n * up. Pass an explicit finite `timeoutMs` to bound the wait, or an\n * `AbortSignal` to cancel externally. `Infinity` is honoured natively\n * — no `setTimeout` is registered (Node's setTimeout silently clamps\n * values above ~24.8d, so we must skip the timer entirely).\n */\n awaitReady(capName: string, scope: ReadinessScope, opts: AwaitReadyOptions = {}): Promise<void> {\n const timeoutMs = opts.timeoutMs ?? Number.POSITIVE_INFINITY\n const start = this.now()\n const current = this.get(capName, scope)\n if (current?.state === 'ready') {\n return Promise.resolve()\n }\n if (opts.signal?.aborted) {\n return Promise.reject(opts.signal.reason ?? new Error('aborted'))\n }\n return new Promise<void>((resolve, reject) => {\n let settled = false\n const unsubscribe = this.onReadyState(capName, scope, (t) => {\n if (t.state !== 'ready') return\n if (settled) return\n settled = true\n cleanup()\n resolve()\n })\n const isInfinite = !Number.isFinite(timeoutMs)\n const timer: ReturnType<typeof setTimeout> | null = isInfinite\n ? null\n : setTimeout(() => {\n if (settled) return\n settled = true\n cleanup()\n reject(new ReadinessTimeoutError(capName, scope, this.now() - start))\n }, timeoutMs)\n const onAbort = (): void => {\n if (settled) return\n settled = true\n cleanup()\n reject(opts.signal?.reason ?? new Error('aborted'))\n }\n opts.signal?.addEventListener('abort', onAbort, { once: true })\n function cleanup(): void {\n unsubscribe()\n if (timer !== null) clearTimeout(timer)\n opts.signal?.removeEventListener('abort', onAbort)\n }\n })\n }\n\n /**\n * Observable: every transition (starting/ready/down) dispatches to\n * `handler`. On subscription, if the snapshot has a current state,\n * the handler fires ONCE asynchronously with that state as the\n * initial transition (duration = 0) so late subscribers can pick up\n * current state without racing the next transition.\n *\n * Returns an unsubscribe function.\n */\n onReadyState(capName: string, scope: ReadinessScope, handler: ReadinessHandler): () => void {\n const sub: Subscription = { capName, scope, handler }\n this.subscriptions.add(sub)\n const current = this.get(capName, scope)\n if (current !== null) {\n queueMicrotask(() => {\n if (!this.subscriptions.has(sub)) return\n handler({\n capName,\n scope,\n state: current.state,\n epoch: current.epoch,\n generation: current.generation,\n sourceNodeId: this.sourceNodeId,\n ts: current.lastChange,\n durationInPrevState: 0,\n })\n })\n }\n return () => { this.subscriptions.delete(sub) }\n }\n\n // ── Internals ─────────────────────────────────────────────────────────\n\n private emitTransition(capName: string, scope: ReadinessScope, state: ReadinessState): void {\n const ts = this.now()\n this.bus.emit(createEvent(\n 'system.ready-state',\n { type: 'capability', id: capName, nodeId: this.sourceNodeId },\n {\n capName,\n scope,\n state,\n generation: this.generation,\n sourceNodeId: this.sourceNodeId,\n ts,\n },\n ))\n }\n\n /**\n * Update snapshot + dispatch to subscribers. Idempotent: same\n * `generation + state` replay is a no-op for both snapshot and\n * subscribers.\n *\n * Defensive against malformed payloads (legal reason: a mock bus's\n * `getRecent` may ignore the category filter and replay unrelated\n * events when we hydrate at construction time).\n */\n private ingest(payload: SystemReadyStatePayload): void {\n if (typeof payload?.capName !== 'string') return\n if (payload.state !== 'ready' && payload.state !== 'starting' && payload.state !== 'down') return\n if (typeof payload.generation !== 'string') return\n if (payload.scope?.type !== 'global' && payload.scope?.type !== 'node' && payload.scope?.type !== 'device') return\n\n const key = readinessKey(payload.capName, payload.scope)\n const prev = this.snapshot.get(key) ?? null\n const now = this.now()\n\n let epoch: number\n if (prev === null) {\n epoch = payload.state === 'ready' ? 1 : 0\n } else if (prev.generation !== payload.generation && payload.state === 'ready') {\n epoch = prev.epoch + 1\n } else {\n epoch = prev.epoch\n }\n\n // Idempotent replay: same generation + same state + we've already\n // recorded it — skip both snapshot write and dispatch.\n if (prev !== null && prev.generation === payload.generation && prev.state === payload.state) {\n return\n }\n\n const durationInPrevState = prev === null ? 0 : Math.max(0, now - prev.lastChange)\n const next: ReadinessRecord = {\n capName: payload.capName,\n scope: payload.scope,\n state: payload.state,\n generation: payload.generation,\n epoch,\n lastChange: now,\n sourceNodeId: payload.sourceNodeId,\n }\n this.snapshot.set(key, next)\n\n const transition: ReadinessTransition = {\n capName: payload.capName,\n scope: payload.scope,\n state: payload.state,\n epoch,\n generation: payload.generation,\n sourceNodeId: payload.sourceNodeId,\n ts: payload.ts,\n durationInPrevState,\n }\n\n if (this.logger) {\n this.logger.debug(\n `readiness: ${payload.capName} (${scopeKey(payload.scope)}) → ${payload.state} epoch=${epoch} gen=${payload.generation.slice(0, 6)} (prev ${durationInPrevState}ms)`,\n )\n }\n\n for (const sub of this.subscriptions) {\n if (sub.capName !== payload.capName) continue\n if (!scopesEqual(sub.scope, payload.scope)) continue\n try {\n sub.handler(transition)\n } catch (err) {\n this.logger?.warn(`readiness handler threw for ${payload.capName}: ${(err as Error).message ?? String(err)}`)\n }\n }\n }\n\n /**\n * `agent.offline` demux. When an agent disconnects ungracefully,\n * its BaseAddon's shutdown hook doesn't fire — consumers would\n * otherwise keep waiting on a stale `ready`. Walk the snapshot and\n * synthesize a `down` transition for every node-scoped record bound\n * to the disconnected `nodeId`, skipping records already `down`.\n *\n * Synthesis is LOCAL: we don't re-emit on the bus. Every registry\n * (one per process) receives the same `agent.offline` and reacts\n * independently, which means no duplicated downs and no central\n * supervisor required.\n */\n private synthesizeDownForNode(offlineNodeId: string): void {\n const now = this.now()\n for (const [key, record] of this.snapshot) {\n if (record.state === 'down') continue\n // Node-scoped records: match on scope.nodeId.\n // Device-scoped records: match on sourceNodeId (which node emitted\n // the latest ready) because the scope itself carries no node info.\n // Global-scoped records are skipped — they're not tied to a node.\n const matchesOfflineNode =\n (record.scope.type === 'node' && record.scope.nodeId === offlineNodeId) ||\n (record.scope.type === 'device' && record.sourceNodeId === offlineNodeId)\n if (!matchesOfflineNode) continue\n\n const durationInPrevState = Math.max(0, now - record.lastChange)\n const next: ReadinessRecord = {\n ...record,\n state: 'down',\n lastChange: now,\n }\n this.snapshot.set(key, next)\n\n const transition: ReadinessTransition = {\n capName: record.capName,\n scope: record.scope,\n state: 'down',\n epoch: record.epoch,\n generation: record.generation,\n sourceNodeId: this.sourceNodeId,\n ts: now,\n durationInPrevState,\n }\n if (this.logger) {\n this.logger.debug(\n `readiness: ${record.capName} (${scopeKey(record.scope)}) → down (synthesized from agent.offline ${offlineNodeId})`,\n )\n }\n for (const sub of this.subscriptions) {\n if (sub.capName !== record.capName) continue\n if (!scopesEqual(sub.scope, record.scope)) continue\n try {\n sub.handler(transition)\n } catch (err) {\n this.logger?.warn(`readiness handler threw during agent.offline demux for ${record.capName}: ${(err as Error).message ?? String(err)}`)\n }\n }\n }\n }\n}\n\nfunction randomGeneration(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID()\n }\n return Math.random().toString(36).slice(2, 14)\n}\n\n// ---------------------------------------------------------------------------\n// Helpers for `$node.disconnected` demultiplexing\n// ---------------------------------------------------------------------------\n\n/**\n * Given a list of `(capName, scope)` pairs that a just-disconnected\n * node owned, emit a `down` transition for each. Callers (kernel\n * supervisor, hub `$node.disconnected` handler) wire this up once they\n * know which caps the node held.\n */\nexport function emitDownForOwnedCaps(\n registry: ReadinessRegistry,\n owned: ReadonlyArray<{ readonly capName: string; readonly scope: ReadinessScope }>,\n): void {\n for (const { capName, scope } of owned) {\n registry.emitDown(capName, scope)\n }\n}\n\n// Re-export the scope key helper for consumers that want to\n// collate by key in their own maps (e.g. debug UIs).\nexport { scopeKey }\n","// =============================================================================\n// Storage Location Types\n// =============================================================================\n//\n// `StorageLocationType` is owned by `./storage-location.ts` (Zod enum is\n// the authoritative source). This file used to declare a parallel TS\n// alias which drifted from the Zod enum (the alias had 4 values the enum\n// didn't). Re-exporting the inferred type keeps the wire surface and the\n// legacy `IStorageProvider` interface in lockstep — adding/removing a\n// value in the Zod enum is now the single source of truth.\n\nexport type { StorageLocationType } from './storage-location.js'\nimport type { StorageLocationType } from './storage-location.js'\n\n/** All location types as an array (for iteration) */\nexport const STORAGE_LOCATION_TYPES: readonly StorageLocationType[] = [\n 'data',\n 'media',\n 'recordings',\n 'recordings-high',\n 'recordings-low',\n 'recordings-clips',\n 'event-images',\n 'models',\n 'addons-data',\n 'cache',\n 'logs',\n 'backups',\n] as const\n\n/** Default subdirectory names for filesystem storage */\nexport const DEFAULT_LOCATION_SUBDIRS: Readonly<Record<StorageLocationType, string>> = {\n 'data': 'db',\n 'media': 'media',\n 'recordings': 'recordings',\n 'recordings-high': 'recordings-high',\n 'recordings-low': 'recordings-low',\n 'recordings-clips': 'recordings-clips',\n 'event-images': 'event-images',\n 'models': 'models',\n 'addons-data': 'addons-data',\n 'cache': '/tmp/camstack-cache',\n 'logs': 'logs',\n 'backups': 'backups',\n}\n\n// =============================================================================\n// Storage Provider (capability: 'storage', mode: collection)\n// =============================================================================\n\n/**\n * A storage provider serves file-based storage for one or more location types.\n * Multiple providers can coexist (local filesystem, NAS, S3, etc.).\n * Each addon/system setting selects which provider to use per location type.\n */\n// ── Named input types for IStorageProvider methods ──────────────────────\n\nexport interface StorageResolveInput { readonly location: StorageLocationType; readonly relativePath: string }\nexport interface StorageWriteInput { readonly location: StorageLocationType; readonly relativePath: string; readonly data: Buffer | NodeJS.ReadableStream }\nexport interface StorageReadInput { readonly location: StorageLocationType; readonly relativePath: string }\nexport interface StorageExistsInput { readonly location: StorageLocationType; readonly relativePath: string }\nexport interface StorageListInput { readonly location: StorageLocationType; readonly prefix?: string }\nexport interface StorageDeleteInput { readonly location: StorageLocationType; readonly relativePath: string }\nexport interface StorageAvailableSpaceInput { readonly location: StorageLocationType }\n\nexport interface IStorageProvider {\n readonly id: string\n readonly name: string\n readonly supportedLocations: readonly StorageLocationType[]\n\n resolve(input: StorageResolveInput): string\n write(input: StorageWriteInput): Promise<void>\n read(input: StorageReadInput): Promise<Buffer>\n exists(input: StorageExistsInput): Promise<boolean>\n list(input: StorageListInput): Promise<readonly string[]>\n delete(input: StorageDeleteInput): Promise<void>\n getAvailableSpace(input: StorageAvailableSpaceInput): Promise<number | null>\n}\n\n// =============================================================================\n// Settings Backend (capability: 'settings-store', mode: singleton)\n// =============================================================================\n\n/**\n * All named collections that the settings backend manages.\n * Each collection maps to a logical table/bucket in the backend.\n */\nexport type SettingsCollection =\n | 'system-settings' // Global system config\n | 'addon-settings' // Per-addon config (keyed by addonId)\n | 'provider-settings' // Per-device-provider config (keyed by providerId)\n | 'device-settings' // Per-device config (keyed by deviceId)\n | 'detection-events' // Detection event records\n | 'audio-levels' // Audio level readings\n | 'track-trails' // Object tracking trails\n | 'recording-segments' // Recording segment metadata\n | 'recording-thumbnails' // Thumbnail metadata\n | 'recording-policies' // Per-device recording policies\n | 'recording-storage-config' // Per-device storage + retention\n | 'recording-cleanup-queue' // Cleanup task queue\n | 'known-faces' // Face recognition identities\n | (string & {}) // Allow arbitrary collection names (e.g. addon-defined)\n\n// ── Named input types for ISettingsBackend methods ──────────────────────\n\nexport interface SettingsGetInput { readonly namespace?: string; readonly collection: SettingsCollection; readonly key: string }\nexport interface SettingsSetInput { readonly namespace?: string; readonly collection: SettingsCollection; readonly key: string; readonly value: unknown }\nexport interface SettingsQueryInput { readonly namespace?: string; readonly collection: SettingsCollection; readonly filter?: QueryFilter }\nexport interface SettingsInsertInput<T extends object = Record<string, unknown>> { readonly namespace?: string; readonly collection: SettingsCollection; readonly record: SettingsRecord<T> }\nexport interface SettingsUpdateInput { readonly namespace?: string; readonly collection: SettingsCollection; readonly id: string; readonly data: Record<string, unknown> }\nexport interface SettingsDeleteInput { readonly namespace?: string; readonly collection: SettingsCollection; readonly key: string }\nexport interface SettingsCountInput { readonly namespace?: string; readonly collection: SettingsCollection; readonly filter?: QueryFilter }\nexport interface SettingsIsEmptyInput { readonly namespace?: string; readonly collection: SettingsCollection }\n\n/**\n * Settings backend — persistent key-value and document storage.\n * Default implementation: SQLite. Can be replaced by Postgres, etc.\n * All methods use object-arg signatures for extensibility.\n */\nexport interface ISettingsBackend {\n /** Get a single value by key from a collection */\n get(input: SettingsGetInput): Promise<unknown>\n\n /** Set a value by key in a collection (upsert) */\n set(input: SettingsSetInput): Promise<void>\n\n /** Get all entries matching an optional filter */\n query<T extends object = Record<string, unknown>>(input: SettingsQueryInput): Promise<readonly SettingsRecord<T>[]>\n\n /** Insert a new record */\n insert<T extends object = Record<string, unknown>>(input: SettingsInsertInput<T>): Promise<void>\n\n /** Update an existing record by ID */\n update(input: SettingsUpdateInput): Promise<void>\n\n /** Delete a record by key/ID */\n delete(input: SettingsDeleteInput): Promise<void>\n\n /** Count entries in a collection */\n count(input: SettingsCountInput): Promise<number>\n\n /** Check if a collection is empty (used for first-boot detection) */\n isEmpty(input: SettingsIsEmptyInput): Promise<boolean>\n\n /**\n * Declare a typed (SQL-backed) collection. After declaration, the\n * standard `insert` / `update` / `delete` / `query` methods route to\n * typed columns for this collection instead of the JSON-blob default.\n * Idempotent — re-declaring the same shape is a no-op. Backends\n * without typed-table support should implement this as a no-op so\n * the cap contract stays uniform.\n */\n declareCollection(input: {\n namespace?: string\n collection: string\n columns: readonly import('../capabilities/settings-store.cap.js').CollectionColumn[]\n indexes?: readonly import('../capabilities/settings-store.cap.js').CollectionIndex[]\n }): Promise<void>\n\n // ── Structured tables ────────────────────────────────────────────────\n // Generic SQL-like operations for custom tables with typed columns.\n // Implementations create the table on first use if it doesn't exist.\n\n /** Ensure a structured table exists (CREATE TABLE IF NOT EXISTS) */\n ensureTable?(table: string, schema: TableSchema): Promise<void>\n\n /** Insert a row into a structured table */\n tableInsert?(table: string, row: Record<string, unknown>): Promise<void>\n\n /** Update rows matching a filter */\n tableUpdate?(table: string, filter: Record<string, unknown>, updates: Record<string, unknown>): Promise<number>\n\n /** Delete rows matching a filter. Returns count of deleted rows. */\n tableDelete?(table: string, filter: Record<string, unknown>): Promise<number>\n\n /** Query rows from a structured table */\n tableQuery?(table: string, options?: TableQueryOptions): Promise<readonly Record<string, unknown>[]>\n\n /** Get a single row by primary key or filter */\n tableGet?(table: string, filter: Record<string, unknown>): Promise<Record<string, unknown> | null>\n\n /** Count rows matching a filter */\n tableCount?(table: string, filter?: Record<string, unknown>): Promise<number>\n}\n\nexport interface SettingsRecord<T extends object = Record<string, unknown>> {\n readonly id: string\n readonly data: T\n}\n\nexport interface QueryFilter {\n where?: Record<string, unknown>\n whereIn?: Record<string, unknown[]>\n whereBetween?: Record<string, [unknown, unknown]>\n orderBy?: { field: string; direction: 'asc' | 'desc' }\n limit?: number\n offset?: number\n}\n\n/** Column definition for structured tables */\nexport interface TableColumnSchema {\n readonly name: string\n readonly type: 'TEXT' | 'INTEGER' | 'REAL' | 'JSON'\n readonly primaryKey?: boolean\n readonly notNull?: boolean\n readonly unique?: boolean\n readonly defaultValue?: string | number | null\n}\n\n/** Table schema for structured tables */\nexport interface TableSchema {\n readonly columns: readonly TableColumnSchema[]\n readonly indexes?: readonly TableIndexSchema[]\n}\n\n/** Index definition for structured tables */\nexport interface TableIndexSchema {\n readonly name: string\n readonly columns: readonly string[]\n readonly unique?: boolean\n}\n\n/** Query options for structured tables */\nexport interface TableQueryOptions {\n readonly where?: Record<string, unknown>\n readonly orderBy?: { field: string; direction: 'asc' | 'desc' }\n readonly limit?: number\n readonly offset?: number\n}\n\n// =============================================================================\n// Embeddings Backend (capability: 'embeddings', mode: singleton)\n// =============================================================================\n\n/**\n * Embeddings backend — vector storage with approximate nearest neighbor search.\n * Used for CLIP frame search, face recognition, plate matching.\n * Default implementation: HNSW (usearch/hnswlib). Can be replaced by Qdrant, etc.\n */\nexport interface IEmbeddingsBackend {\n /** Create or open a named vector index */\n openIndex(name: string, dimensions: number): Promise<IVectorIndex>\n\n /** List all index names */\n listIndexes(): Promise<readonly string[]>\n\n /** Delete an index and its data */\n deleteIndex(name: string): Promise<void>\n\n /** Lifecycle */\n initialize(): Promise<void>\n shutdown(): Promise<void>\n}\n\nexport interface IVectorIndex {\n /** Index name */\n readonly name: string\n\n /** Vector dimensions */\n readonly dimensions: number\n\n /** Insert a vector with ID and optional metadata */\n insert(id: string, vector: Float32Array, metadata?: Record<string, unknown>): Promise<void>\n\n /** Batch insert for performance */\n insertBatch(items: readonly VectorEntry[]): Promise<void>\n\n /** Search for nearest neighbors */\n search(query: Float32Array, topK: number, filter?: EmbeddingFilter): Promise<readonly VectorSearchResult[]>\n\n /** Delete by ID */\n delete(id: string): Promise<void>\n\n /** Count entries in the index */\n count(): Promise<number>\n\n /** Flush pending writes to disk */\n flush(): Promise<void>\n}\n\nexport interface VectorEntry {\n readonly id: string\n readonly vector: Float32Array\n readonly metadata?: Record<string, unknown>\n}\n\nexport interface VectorSearchResult {\n readonly id: string\n /** Similarity score (0-1, higher is more similar) */\n readonly score: number\n readonly metadata?: Record<string, unknown>\n}\n\nexport interface EmbeddingFilter {\n /** Filter by metadata key-value before vector search */\n readonly where?: Record<string, unknown>\n /** Filter by timestamp range */\n readonly timestampAfter?: number\n readonly timestampBefore?: number\n /** Filter by device */\n readonly deviceId?: string\n}\n\n// =============================================================================\n// Legacy re-exports (deprecated — migrate to new interfaces)\n// =============================================================================\n\n\n/**\n * A document record in collection-based storage.\n * Used by IStructuredStorage and ElementConfigStore.\n * New code should prefer ISettingsBackend + SettingsRecord.\n */\nexport interface StorageRecord {\n collection: string\n id: string\n data: Record<string, unknown>\n}\n\n/**\n * Collection-based document storage (query, insert, update, delete).\n * Used by ElementConfigStore and StorageManager shim layer.\n * New code should prefer ISettingsBackend.\n */\nexport interface IStructuredStorage {\n query(collection: string, filter?: QueryFilter): Promise<readonly StorageRecord[]>\n insert(record: StorageRecord): Promise<StorageRecord>\n update(collection: string, id: string, data: Record<string, unknown>): Promise<StorageRecord>\n delete(collection: string, id: string): Promise<void>\n count(collection: string, filter?: QueryFilter): Promise<number>\n}\n\n/**\n * File-based storage operations.\n * Used by StorageManager and addons for media/model files.\n * New code should prefer IStorageProvider.\n */\nexport interface IFileStorage {\n readFile(path: string): Promise<Buffer>\n writeFile(path: string, data: Buffer): Promise<void>\n deleteFile(path: string): Promise<void>\n listFiles(prefix?: string): Promise<readonly string[]>\n getFileUrl(path: string): Promise<string>\n exists(path: string): Promise<boolean>\n}\n\n/**\n * Combined storage location with optional structured + file storage.\n * Used by StorageManager.getLocation() and ElementConfigStore.\n * New code should prefer IStorageProvider + ISettingsBackend.\n */\nexport interface IStorageLocation {\n structured?: IStructuredStorage\n files?: IFileStorage\n}\n","import type { StreamSourceEntry } from '../../device/camera-device.js'\n\n// ---------------------------------------------------------------------------\n// Stream metadata — probed (ffprobe) or declared by provider\n// ---------------------------------------------------------------------------\n\nexport interface StreamMetadata {\n width?: number // e.g. 1920\n height?: number // e.g. 1080\n codec?: string // e.g. 'h264', 'h265', 'mjpeg', 'vp8', 'vp9', 'av1'\n fps?: number // e.g. 25\n bitrateKbps?: number // e.g. 4000\n /** Probed audio track. Undefined when stream carries no audio. */\n audio?: StreamAudioMetadata\n}\n\nexport interface StreamAudioMetadata {\n /** Codec name as reported by the demuxer (e.g. 'aac', 'pcm_mulaw', 'opus'). */\n codec?: string\n /** Sample rate in Hz (e.g. 8000, 16000, 44100, 48000). */\n sampleRate?: number\n /** Channel count (1 = mono, 2 = stereo). */\n channels?: number\n /** Stated bitrate in kbps when the demuxer reports one. */\n bitrateKbps?: number\n /** Codec profile/aac-mode (e.g. 'LC', 'HE-AAC') when known. */\n profile?: string\n}\n\n// ---------------------------------------------------------------------------\n// Stream quality — computed from metadata\n// ---------------------------------------------------------------------------\n\nexport type StreamQuality = 'high' | 'mid' | 'low'\n\n/** Friendly display labels for stream quality IDs. */\nexport const STREAM_QUALITY_LABELS: Readonly<Record<string, string>> = {\n high: 'High',\n mid: 'Mid',\n low: 'Low',\n}\n\n/** Get a friendly label for a stream ID. Falls back to capitalised id. */\nexport function streamQualityLabel(id: string): string {\n return STREAM_QUALITY_LABELS[id] ?? (id.charAt(0).toUpperCase() + id.slice(1))\n}\n\n/**\n * Compute pixel count for sorting. Returns w*h, or 0 if unknown.\n */\nexport function streamPixels(meta: StreamMetadata): number {\n return (meta.width ?? 0) * (meta.height ?? 0)\n}\n\n/**\n * Classify streams relative to each other.\n * Highest resolution → high, lowest → low, everything else → mid.\n * With 1 stream: high. With 2: high + low.\n */\nexport function classifyStreams<T extends { metadata?: StreamMetadata }>(\n streams: readonly T[],\n): Array<T & { quality: StreamQuality }> {\n if (streams.length === 0) return []\n\n // Sort by pixel count descending\n const sorted = streams\n .map((s, i) => ({ s, i, px: streamPixels(s.metadata ?? {}) }))\n .sort((a, b) => b.px - a.px)\n\n const result: Array<T & { quality: StreamQuality }> = Array.from<T & { quality: StreamQuality }>({ length: streams.length })\n\n for (let rank = 0; rank < sorted.length; rank++) {\n const { s, i } = sorted[rank]!\n let quality: StreamQuality\n if (rank === 0) quality = 'high'\n else if (rank === sorted.length - 1) quality = 'low'\n else quality = 'mid'\n result[i] = { ...s, quality }\n }\n\n return result\n}\n\n/**\n * Classify a single stream in isolation (no relative context).\n * Use classifyStreams() when you have the full set.\n */\nexport function classifyStream(meta: StreamMetadata): StreamQuality {\n const h = meta.height ?? 0\n const w = meta.width ?? 0\n if (h >= 1080 || w >= 1920) return 'high'\n if (h >= 480 || w >= 640) return 'mid'\n return 'low'\n}\n\n// ---------------------------------------------------------------------------\n// Stream protocol — how to consume a stream\n// ---------------------------------------------------------------------------\n\nexport type StreamProtocolType = 'rtsp' | 'rtmp' | 'http' | 'flv' | 'rtp' | 'raw'\n\nexport interface StreamProtocol {\n type: StreamProtocolType\n /** Source URL. undefined for 'raw' (push-based, e.g. Reolink Baichuan). */\n url?: string\n}\n\n// ---------------------------------------------------------------------------\n// AvailableStream — a physical stream exposed by a camera\n// ---------------------------------------------------------------------------\n\nexport interface AvailableStream {\n /** Unique identifier within the device (e.g. 'main', 'sub', 'ch0_0') */\n id: string\n /** Ways to consume this stream */\n protocols: StreamProtocol[]\n /** Probed or provider-declared stream characteristics */\n metadata?: StreamMetadata\n /** Computed from metadata via classifyStream() */\n quality: StreamQuality\n /** Optional display hint */\n label?: string\n}\n\n/** Convert AvailableStream to StreamSourceEntry for stream-broker consumers. */\nexport function toStreamSourceEntry(stream: AvailableStream): StreamSourceEntry {\n // Pick first URL-bearing protocol, prefer rtsp\n const urlProto = stream.protocols.find((p) => p.type === 'rtsp' && p.url)\n ?? stream.protocols.find((p) => p.url)\n\n const protocolRaw = urlProto?.type\n const protocol: StreamSourceEntry['protocol'] =\n protocolRaw === 'rtsp' ? 'rtsp'\n : protocolRaw === 'http' || protocolRaw === 'flv' ? 'http-mjpeg'\n : 'custom'\n\n return {\n id: stream.id,\n label: stream.label ?? stream.id,\n protocol,\n url: urlProto?.url,\n profileHint: stream.quality,\n }\n}\n\n\n// ---------------------------------------------------------------------------\n// Camera stream settings — stored in device settings by providers\n// ---------------------------------------------------------------------------\n\n/** A single stream entry in device settings. */\nexport interface CameraStreamEntry {\n readonly url: string\n readonly label?: string\n}\n\nexport interface CameraStreamConfig {\n readonly streams: readonly CameraStreamEntry[]\n /**\n * Snapshot source URL (JPEG).\n * Used internally by the provider to implement ICamera.getSnapshot().\n * Clients should call snapshot.getSnapshot() instead of hitting this URL directly.\n */\n readonly snapshot_url?: string\n}\n\nfunction isString(v: unknown): v is string {\n return typeof v === 'string'\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n}\n\nfunction parseStreamEntry(v: unknown): CameraStreamEntry | null {\n // \"rtsp://...\" → { url }\n if (isString(v) && v.trim()) return { url: v }\n // { url: \"rtsp://...\", label?: \"Main\" } → { url, label }\n if (isRecord(v) && isString(v['url']) && v['url'].trim()) {\n return {\n url: v['url'],\n label: isString(v['label']) ? v['label'] : undefined,\n }\n }\n return null\n}\n\n/** Extract CameraStreamConfig from untyped settings record. */\nexport function parseCameraStreamConfig(raw: Record<string, unknown>): CameraStreamConfig {\n const snapshotUrl = isString(raw['snapshot_url']) ? raw['snapshot_url'] : undefined\n\n // Array format: [\"url\"] or [{url, label}]\n if (Array.isArray(raw['streams'])) {\n const streams: CameraStreamEntry[] = []\n for (const item of raw['streams']) {\n const entry = parseStreamEntry(item)\n if (entry) streams.push(entry)\n }\n return { streams, snapshot_url: snapshotUrl }\n }\n\n // Legacy key-based format: main_stream_url, sub_stream_url, third_stream_url\n const streams: CameraStreamEntry[] = []\n\n const mainUrl = isString(raw['main_stream_url']) ? raw['main_stream_url'] : undefined\n if (mainUrl) streams.push({ url: mainUrl, label: isString(raw['main_stream_name']) ? raw['main_stream_name'] : undefined })\n\n const subUrl = isString(raw['sub_stream_url']) ? raw['sub_stream_url'] : undefined\n if (subUrl) streams.push({ url: subUrl, label: isString(raw['sub_stream_name']) ? raw['sub_stream_name'] : undefined })\n\n const thirdUrl = isString(raw['third_stream_url']) ? raw['third_stream_url'] : undefined\n if (thirdUrl) streams.push({ url: thirdUrl, label: isString(raw['third_stream_name']) ? raw['third_stream_name'] : undefined })\n\n return {\n streams,\n snapshot_url: snapshotUrl,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stream Profile Assignment\n// ---------------------------------------------------------------------------\n\n/**\n * Per-device mapping of quality profile → streamId.\n * Cameras expose N physical streams; the user assigns which stream\n * serves each of the 3 standard CamStack profiles.\n */\nexport interface StreamProfileMap {\n high?: string\n mid?: string\n low?: string\n}\n\n/**\n * Auto-assign profiles based on quality classification.\n * With 1 stream: all profiles point to it.\n * With 2: high + low. With 3+: high + mid + low.\n */\nexport function autoAssignProfiles(streams: readonly AvailableStream[]): StreamProfileMap {\n if (streams.length === 0) return {}\n\n const classified = classifyStreams(streams)\n const map: StreamProfileMap = {}\n\n // Assign by quality label, not array position (classifyStreams preserves original order)\n const high = classified.find((s) => s.quality === 'high')\n const mid = classified.find((s) => s.quality === 'mid')\n const low = classified.find((s) => s.quality === 'low')\n\n if (high) map.high = high.id\n if (mid) map.mid = mid.id\n if (low) map.low = low.id\n\n // Fallback: fill missing profiles from what's available\n const fallback = high ?? mid ?? low ?? classified[0]\n if (fallback) {\n if (!map.high) map.high = fallback.id\n if (!map.mid) map.mid = fallback.id\n if (!map.low) map.low = fallback.id\n }\n\n return map\n}\n\n// ---------------------------------------------------------------------------\n","export interface FeatureManifest {\n streaming: boolean\n notifications: boolean\n objectDetection: boolean\n remoteAccess: boolean\n agentCluster: boolean\n smartHome: boolean\n recordings: boolean\n backup: boolean\n repl: boolean\n}\n\nexport type FeatureFlag = keyof FeatureManifest\n\nexport const DEFAULT_FEATURES: FeatureManifest = {\n streaming: true,\n notifications: true,\n objectDetection: false,\n remoteAccess: true,\n agentCluster: false,\n smartHome: true,\n recordings: true,\n backup: true,\n repl: true,\n}\n","/**\n * Detection Analysis Pipeline -- types for tracked detections, sub-detections,\n * recognition results, and audio classification.\n */\n\nimport type { VideoFrame } from './camera-pipeline.js'\n\n/**\n * A single detection entry consumed by the analysis pipeline (tracking,\n * zones, session aggregation).\n *\n * Intentionally DIFFERENT from `SpatialDetection` (the engine-level\n * detector output in `types/detection.ts`):\n * - `SpatialDetection` is raw model output: `{class, bbox:{x,y,w,h},\n * landmarks?, mask?}`.\n * - `AnalysisDetection` is the pipeline's view: tuple-bbox\n * `[x1,y1,x2,y2]`, optional enrichment (`id`, `label`, `zones`,\n * `previousZones`) added by upstream stages.\n *\n * Renamed from the ambiguous `Detection` on 2026-04-14 to disambiguate\n * from the other Detection types in the codebase (`ObjectDetection`\n * for UI payloads, `SpatialDetection` for engine output).\n */\nexport interface AnalysisDetection {\n className: string\n score: number\n boundingBox?: [number, number, number, number]\n id?: string\n label?: string\n labelScore?: number\n zones?: string[]\n previousZones?: string[]\n}\n\n// --- Detection type const unions ---\n\nexport const SUB_DETECTION_TYPES = ['face', 'plate'] as const\nexport type SubDetectionType = typeof SUB_DETECTION_TYPES[number]\n\nexport const RECOGNITION_TYPES = ['face', 'plate', 'clip', 'custom'] as const\nexport type RecognitionType = typeof RECOGNITION_TYPES[number]\n\n// --- Analysis Context ---\n\nexport interface AnalysisContext {\n readonly deviceId: string\n readonly frame: VideoFrame\n readonly timestamp: number\n readonly rawDetections: readonly AnalysisDetection[]\n readonly trackedDetections: readonly ServerTrackedDetection[]\n readonly events: readonly AnalysisEvent[]\n readonly metadata: Readonly<Record<string, unknown>>\n}\n\n// --- Tracked Detection ---\n\nexport interface ServerTrackedDetection {\n readonly trackId: string\n readonly detection: AnalysisDetection\n readonly crop?: Buffer\n readonly tracking: TrackingInfo\n readonly subDetections: readonly SubDetection[]\n readonly recognitions: readonly RecognitionResult[]\n readonly zones: readonly string[]\n readonly previousZones: readonly string[]\n}\n\nexport interface TrackingInfo {\n readonly age: number\n readonly state: 'moving' | 'stationary' | 'new' | 'lost'\n readonly stationaryDuration: number\n readonly velocity: { readonly dx: number; readonly dy: number }\n readonly positionHistory: ReadonlyArray<{ readonly x: number; readonly y: number; readonly t: number }>\n}\n\n// --- Sub-Detection ---\n\nexport interface SubDetection {\n readonly detectionType: SubDetectionType\n readonly boundingBox: readonly [number, number, number, number]\n readonly score: number\n readonly crop?: Buffer\n}\n\n// --- Recognition ---\n\nexport interface RecognitionResult {\n readonly recognitionType: RecognitionType\n readonly label: string\n readonly score: number\n readonly embedding?: readonly number[]\n readonly metadata?: Readonly<Record<string, unknown>>\n}\n\n// --- Analysis Events ---\n\nexport interface AnalysisEvent {\n readonly category: string\n readonly severity: 'info' | 'warning' | 'alert'\n readonly detection: ServerTrackedDetection\n readonly description: string\n readonly data: Readonly<Record<string, unknown>>\n}\n\n// --- Audio Classifier ---\n//\n// The canonical `IAudioClassifier` lives in `./audio-analyzer.ts` and is\n// derived from the capability definition. The legacy interface that lived\n// here had a different shape (readonly ready + release()) and was never\n// actually consumed outside of this file — removed to avoid confusion.\n","/**\n * Analysis Persistence Types — interfaces for event persistence, track trails,\n * retention, session tracking, and known faces.\n *\n * These interfaces define the contracts used by the server; concrete\n * implementations live in addon-analytics and are received via CapabilityRegistry.\n */\n\nimport type { ServerTrackedDetection } from './server-analysis.js'\n\n// ---------------------------------------------------------------------------\n// Event Persistence\n// ---------------------------------------------------------------------------\n\nexport interface ObjectSnapshotResult {\n readonly trackId: string\n readonly thumbnail?: Buffer\n readonly fullCrop?: Buffer\n}\n\nexport interface AnnotatedSnapshotResult {\n readonly thumbnail: Buffer\n readonly full: Buffer\n}\n\nexport interface PersistableEvent {\n readonly id: string\n readonly timestamp: number\n readonly deviceId: string\n readonly category: string\n readonly className: string\n readonly score: number\n readonly trackId: string\n readonly severity: string\n readonly description: string\n readonly data: Record<string, unknown>\n readonly mediaFiles: readonly string[]\n}\n\nexport interface EventBufferStatus {\n readonly eventCount: number\n readonly mediaCount: number\n readonly mediaSizeMB: number\n}\n\nexport type TrackMediaType =\n | 'crop-thumb'\n | 'crop-full'\n | 'debug-annotated-thumb'\n | 'debug-annotated-full'\n | 'original'\n | 'original-thumb'\n | 'inline-crop'\n | 'unknown'\n\nexport interface TrackMediaFile {\n readonly path: string\n readonly type: TrackMediaType\n readonly data: Buffer\n readonly source: 'buffer' | 'storage'\n}\n\n// ---------------------------------------------------------------------------\n// Track Trail\n// ---------------------------------------------------------------------------\n\n// F15c: `enabled` removed — trail capture on/off lives on the\n// `track-trail` wrapper binding. See capabilities/track-trail.cap.ts.\nexport interface TrackCaptureConfig {\n readonly snapshotIntervalMs: number\n readonly maxTrailLength: number\n readonly saveThumbnails: boolean\n readonly thumbnailSize: { readonly width: number; readonly height: number }\n}\n\nexport interface TrackPosition {\n readonly x: number\n readonly y: number\n readonly timestamp: number\n readonly bbox: [number, number, number, number]\n}\n\nexport interface TrackSnapshot {\n readonly timestamp: number\n readonly position: TrackPosition\n readonly thumbnailPath: string\n}\n\nexport interface TrackTrail {\n readonly trackId: string\n readonly deviceId: number\n readonly className: string\n readonly label?: string\n readonly firstSeen: number\n readonly lastSeen: number\n readonly positions: readonly TrackPosition[]\n readonly snapshots: readonly TrackSnapshot[]\n readonly totalDistance: number\n readonly zonesVisited: readonly string[]\n readonly active: boolean\n}\n\n// ---------------------------------------------------------------------------\n// Retention\n// ---------------------------------------------------------------------------\n\nexport interface RetentionConfig {\n readonly cleanupIntervalMs: number\n readonly detectionEventsDays: number\n readonly audioLevelsDays: number\n /** @deprecated — snapshots are tied to events, use detectionEventsDays */\n readonly snapshotsDays: number\n readonly deviceOverrides?: Readonly<Record<string, Partial<Pick<RetentionConfig, 'detectionEventsDays' | 'audioLevelsDays' | 'snapshotsDays'>>>>\n}\n\nexport const DEFAULT_RETENTION: RetentionConfig = {\n cleanupIntervalMs: 60 * 60 * 1000,\n detectionEventsDays: 30,\n audioLevelsDays: 7,\n snapshotsDays: 14,\n}\n\nexport interface RetentionReport {\n readonly deletedEvents: number\n readonly deletedAudioRecords: number\n readonly deletedSnapshots: number\n}\n\n// ---------------------------------------------------------------------------\n// Session Tracker\n// ---------------------------------------------------------------------------\n\nexport interface SessionTrack {\n readonly trackId: string\n readonly deviceId: string\n readonly className: string\n readonly label?: string\n readonly firstSeen: number\n readonly lastSeen: number\n readonly totalFrames: number\n readonly lastDetection: ServerTrackedDetection\n readonly state: string\n readonly positions: ReadonlyArray<{ readonly x: number; readonly y: number; readonly t: number }>\n readonly embedding?: Float32Array\n readonly globalId?: string\n readonly bestCrop?: Buffer\n}\n\nexport interface GlobalIdentity {\n readonly globalId: string\n readonly embedding: Float32Array\n readonly label?: string\n readonly firstSeen: number\n readonly lastSeen: number\n readonly deviceIds: ReadonlyArray<string>\n}\n\n// ---------------------------------------------------------------------------\n// Known Faces\n// ---------------------------------------------------------------------------\n\nexport interface ClipRecognizer {\n getEmbedding(imageBuffer: Buffer): Promise<Float32Array>\n}\n\nexport interface KnownFaceEntry {\n readonly id: string\n readonly label: string\n readonly group?: string\n readonly embedding: readonly number[]\n readonly cropBase64: string\n readonly createdAt: number\n readonly updatedAt: number\n readonly source?: string\n readonly metadata?: Readonly<Record<string, unknown>>\n}\n\n// ---------------------------------------------------------------------------\n// Recording Addon (generic interface for recording engine access)\n// ---------------------------------------------------------------------------\n\n/**\n * Interface for the recording addon — the server uses this instead of\n * importing RecordingAddon directly from addon-recording.\n */\nexport interface IRecordingAddon {\n getCoordinator(): IRecordingCoordinator\n getRecordingDb(): IRecordingDb\n}\n\n/**\n * Minimal coordinator interface for the server's recording router.\n */\nexport interface IRecordingCoordinator {\n enableRecording(deviceId: string, options: {\n policy: unknown\n ffmpegOverrides?: unknown\n }): Promise<void>\n disableRecording(deviceId: string): Promise<void>\n isRecording(deviceId: string): boolean\n readonly playlistGenerator: {\n generate(\n deviceId: string,\n streamId: string,\n startTime: number,\n endTime: number,\n options?: { live?: boolean },\n ): unknown\n }\n readonly storageEstimator: {\n estimateForDevice(\n deviceId: string,\n motionInput?: { avgEventsPerDay: number; avgDurationSec: number },\n ): { totalEstimatedGb: number; [key: string]: unknown }\n }\n}\n\n/**\n * Minimal recording database interface for the server's recording router.\n */\nexport interface IRecordingDb {\n upsertStorageConfig(config: {\n deviceId: string\n dataCategory: string\n storageName: string\n subDirectory: string\n retentionDays: number | null\n retentionGb: number | null\n }): void\n getPolicy(deviceId: string): { readonly enabled: boolean; readonly mode: string; [key: string]: unknown } | null\n getEnabledPolicies(): Array<{ deviceId: string; [key: string]: unknown }>\n querySegments(deviceId: string, streamId: string, startTime: number, endTime: number): unknown\n getAvailability(deviceId: string, startTime: number, endTime: number): unknown\n getStorageUsage(deviceId: string, streamId: string): unknown\n upsertPolicy(policy: unknown): void\n findNearestThumbnail(deviceId: string, timestamp: number, category: string): unknown\n getMotionStats(deviceId: string, startTime: number, endTime: number): {\n avgEventsPerDay: number\n avgDurationSec: number\n dutyCyclePercent: number\n totalEvents: number\n }\n resolveStorageConfig(deviceId: string, dataCategory: string): unknown\n}\n\n// ---------------------------------------------------------------------------\n// Sub-service interfaces — used by the server's thin NestJS wrappers\n// to receive delegates via CapabilityRegistry.\n// ---------------------------------------------------------------------------\n\nexport interface IEventPersistence {\n start(): void\n stop(): void\n saveDetectionCrops(eventId: string, crops: readonly ObjectSnapshotResult[]): void\n saveAnnotatedFrame(eventId: string, result: AnnotatedSnapshotResult): void\n saveOriginalFrame(eventId: string, frame: import('./camera-pipeline.js').VideoFrame): Promise<void>\n getEventMedia(eventId: string): Promise<readonly TrackMediaFile[]>\n getTrackMedia(trackId: string): Promise<readonly TrackMediaFile[]>\n getDeviceMedia(deviceId: string, since?: number, until?: number): Promise<readonly TrackMediaFile[]>\n getBufferStatus(): { pendingEvents: number; pendingMedia: number; lastFlush?: number }\n flush(): Promise<void>\n}\n\nexport interface ITrackTrail {\n setConfig(deviceId: string, config: Partial<TrackCaptureConfig>): void\n getConfig(deviceId: string): TrackCaptureConfig\n recordFrame(ctx: unknown): Promise<void>\n getActiveTrail(trackId: string): TrackTrail | null\n getActiveTrails(deviceId: string): readonly TrackTrail[]\n getPersistedTrail(trackId: string): Promise<TrackTrail | null>\n getTrail(trackId: string): Promise<TrackTrail | null>\n listTrails(deviceId: string, options?: {\n since?: number\n until?: number\n limit?: number\n className?: string\n }): Promise<readonly TrackTrail[]>\n}\n\nexport interface IRetention {\n start(): void\n stop(): void\n runCleanup(): Promise<unknown>\n setConfig(update: Partial<RetentionConfig>): void\n getConfig(): unknown\n forceCleanup(): Promise<unknown>\n}\n\nexport interface ISessionTracker {\n updateFromAnalysis(deviceId: string, ctx: unknown): void\n getActiveTracks(deviceId: string): readonly SessionTrack[]\n getAllActiveTracks(): readonly SessionTrack[]\n getTrack(deviceId: string, trackId: string): SessionTrack | undefined\n getTrackCounts(): Record<string, number>\n clearDevice(deviceId: string): void\n clearAll(): void\n getGlobalPool(): unknown\n}\n\nexport interface KnownFaceSummary {\n readonly id: string\n readonly label: string\n readonly group?: string\n readonly cropBase64: string\n readonly createdAt: number\n readonly updatedAt: number\n readonly source?: string\n readonly sourceType?: string\n readonly count?: number\n}\n\nexport interface IKnownFaces {\n register(entry: KnownFaceEntry): Promise<void>\n listAll(): Promise<readonly KnownFaceSummary[]>\n delete(id: string): Promise<void>\n update(id: string, updates: Partial<Pick<KnownFaceEntry, 'label' | 'group'>>): Promise<void>\n recalculateEmbedding(id: string, clipRecognizer: ClipRecognizer): Promise<void>\n findMatch(embedding: Float32Array, threshold: number): Promise<unknown>\n batchRegister(\n entries: ReadonlyArray<{ readonly label: string; readonly cropBase64: string; readonly group?: string }>,\n clipRecognizer: ClipRecognizer,\n ): Promise<unknown>\n}\n\n/**\n * Composite interface exposed via CapabilityRegistry as the\n * 'analysis-data-persistence' singleton. Each sub-service is wired\n * individually to the corresponding NestJS wrapper service.\n */\nexport interface IAnalysisDataPersistence {\n readonly eventPersistence: IEventPersistence\n readonly knownFaces: IKnownFaces\n readonly sessionTracker: ISessionTracker\n readonly retention: IRetention\n readonly trackTrail: ITrackTrail\n}\n\n// ---------------------------------------------------------------------------\n// Provider Connection Testing\n// ---------------------------------------------------------------------------\n\nexport interface TestConnectionResult {\n readonly success: boolean\n readonly version?: string\n readonly cameraCount?: number\n readonly error?: string\n}\n\n/**\n * Generic interface for testing provider connections.\n * Each provider addon can expose a connection tester.\n */\nexport interface IProviderConnectionTester {\n testConnection(): Promise<TestConnectionResult>\n}\n","export enum EventSourceType {\n Addon = 'addon',\n Core = 'core',\n Device = 'device',\n Provider = 'provider',\n System = 'system',\n}\n","export const HF_REPO = 'camstack/camstack-models'\nexport const HF_BASE_URL = `https://huggingface.co/${HF_REPO}/resolve/main`\n\n/**\n * Shape of {@link RUNTIME_DEFAULTS}. Every key/value is typed explicitly so\n * consumers (ConfigManager.raw, feature accessors, settings-store seeder)\n * can read fields without `as` casts.\n *\n * Iteration via `Object.entries` still yields `[string, RuntimeDefaultsShape[keyof RuntimeDefaultsShape]]`\n * which is assignable to `[string, unknown]` — no change for consumers that\n * only need the iteration behaviour.\n */\nexport interface RuntimeDefaultsProvider {\n id: string\n type: string\n name: string\n}\n\nexport interface RuntimeDefaultsShape {\n /** Index signature for dynamic string-path lookups (e.g. ConfigManager.get).\n * Explicit keys below override this with their precise types. */\n [key: string]: unknown\n 'features.streaming': boolean\n 'features.notifications': boolean\n 'features.objectDetection': boolean\n 'features.remoteAccess': boolean\n 'features.agentCluster': boolean\n 'features.smartHome': boolean\n 'features.recordings': boolean\n 'features.backup': boolean\n 'features.repl': boolean\n 'retention.detectionEventsDays': number\n 'retention.audioLevelsDays': number\n 'logging.level': string\n 'logging.retentionDays': number\n 'eventBus.ringBufferSize': number\n 'storage.provider': string\n 'storage.locations': Record<string, string>\n 'providers': RuntimeDefaultsProvider[]\n 'recording.segmentDurationSeconds': number\n 'recording.defaultRetentionDays': number\n 'ffmpeg.binaryPath': string\n 'ffmpeg.hwAccel': string\n 'ffmpeg.threadCount': number\n 'auth.tokenExpiry': string\n}\n\n/**\n * Runtime defaults -- used by ConfigManager.get() for backward compatibility\n * until Plan B wires all runtime settings to the system_settings SQL table.\n *\n * Moved from @camstack/kernel/config-schema to @camstack/types so that\n * @camstack/core can reference it without a cross-package kernel import.\n */\nexport const RUNTIME_DEFAULTS: RuntimeDefaultsShape = {\n 'features.streaming': true,\n 'features.notifications': true,\n 'features.objectDetection': false,\n 'features.remoteAccess': true,\n 'features.agentCluster': false,\n 'features.smartHome': true,\n 'features.recordings': true,\n 'features.backup': true,\n 'features.repl': true,\n 'retention.detectionEventsDays': 30,\n 'retention.audioLevelsDays': 7,\n 'logging.level': 'info',\n 'logging.retentionDays': 30,\n 'eventBus.ringBufferSize': 10000,\n 'storage.provider': 'sqlite-storage',\n 'storage.locations': {\n data: 'camstack-data/data',\n media: 'camstack-data/media',\n recordings: 'camstack-data/recordings',\n cache: '/tmp/camstack-cache',\n logs: 'camstack-data/logs',\n models: 'camstack-data/models',\n },\n 'providers': [],\n // Recording\n 'recording.segmentDurationSeconds': 4,\n 'recording.defaultRetentionDays': 30,\n // Streaming ports are addon-specific (go2rtc owns its defaults)\n // FFmpeg\n 'ffmpeg.binaryPath': 'ffmpeg',\n 'ffmpeg.hwAccel': 'auto',\n 'ffmpeg.threadCount': 0,\n // Detection / motion / audio settings are owned by their respective addons\n // (pipeline-orchestrator, motion-wasm, audio-analyzer/classifier, detection-pipeline\n // step schemas). No detection defaults at this level.\n // Backup retention is addon-specific (local-backup owns its default)\n // Auth (runtime)\n 'auth.tokenExpiry': '24h',\n}\n","/**\n * Type-safe JSON parsing helpers.\n *\n * `JSON.parse` is typed as `any` in lib.es5.d.ts, which triggers\n * `no-unsafe-*` ESLint rules and destroys downstream inference. These\n * wrappers return `unknown` — callers narrow structurally via type\n * guards, `typeof` checks, or helpers like `asRecord`/`asString`.\n */\n\n/**\n * Parse JSON and return it as `unknown` — the only entry point for untrusted JSON.\n *\n * The optional generic overload `parseJsonUnknown<T>(text)` returns `T` for\n * call sites that know the shape at parse time (e.g. MQTT payloads with a\n * known protocol schema). This is a **type-level bridge only** — no runtime\n * validation is performed. Callers that need runtime validation should parse\n * as `unknown` and narrow via Zod or structural guards.\n */\nexport function parseJsonUnknown<T = unknown>(text: string): T {\n // Isolate JSON.parse's `any` to this single statement.\n const parsed: unknown = JSON.parse(text)\n return parsed as T\n}\n\n/** Narrow an unknown value to a plain `Record<string, unknown>` or return null. */\nexport function asJsonObject(value: unknown): Record<string, unknown> | null {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n return null\n }\n return { ...value }\n}\n\n/** Narrow an unknown value to a `readonly unknown[]` or return an empty array. */\nexport function asJsonArray(value: unknown): readonly unknown[] {\n return Array.isArray(value) ? value : []\n}\n\n/** Safe string extraction from an unknown record field. */\nexport function asString(value: unknown, fallback = ''): string {\n return typeof value === 'string' ? value : fallback\n}\n\n/** Safe number extraction from an unknown record field. */\nexport function asNumber(value: unknown, fallback = 0): number {\n return typeof value === 'number' ? value : fallback\n}\n\n/** Safe boolean extraction from an unknown record field. */\nexport function asBoolean(value: unknown, fallback = false): boolean {\n return typeof value === 'boolean' ? value : fallback\n}\n\n/** Parse JSON + narrow to object in one step. */\nexport function parseJsonObject(text: string): Record<string, unknown> | null {\n try {\n return asJsonObject(parseJsonUnknown(text))\n } catch {\n return null\n }\n}\n\n/** Parse JSON + narrow to array in one step. */\nexport function parseJsonArray(text: string): readonly unknown[] | null {\n try {\n const parsed = parseJsonUnknown(text)\n return Array.isArray(parsed) ? parsed : null\n } catch {\n return null\n }\n}\n","export function hfModelUrl(repo: string, path: string): string {\n return `https://huggingface.co/${repo}/resolve/main/${path}`\n}\n","/** Cosine similarity between two embedding vectors */\nexport function cosineSimilarity(a: Float32Array, b: Float32Array): number {\n if (a.length !== b.length) return 0\n let dotProduct = 0\n let normA = 0\n let normB = 0\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i]! * b[i]!\n normA += a[i]! * a[i]!\n normB += b[i]! * b[i]!\n }\n const denom = Math.sqrt(normA) * Math.sqrt(normB)\n return denom === 0 ? 0 : dotProduct / denom\n}\n","import type { IElementConfig } from '../interfaces/context.js'\nimport type { ISettingsBackend } from '../interfaces/storage.js'\n\n/**\n * Persisted config store for a single element.\n * Reads/writes to the element's scoped storage under the 'config' collection.\n * Notifies listeners on every change.\n */\nexport class ElementConfigStore implements IElementConfig {\n private cache: Record<string, unknown> = {}\n private listeners: Set<(config: Record<string, unknown>) => void> = new Set()\n private loaded = false\n\n private readonly getBackend: () => ISettingsBackend | null\n\n constructor(\n private readonly elementId: string,\n settingsBackend: ISettingsBackend | (() => ISettingsBackend | null) | null,\n ) {\n // Accept either a direct backend or a lazy getter for late-wired backends\n this.getBackend = typeof settingsBackend === 'function'\n ? settingsBackend\n : () => settingsBackend\n }\n\n /** Load config from storage into cache. Called once on first access. */\n private async ensureLoaded(): Promise<void> {\n if (this.loaded) return\n const backend = this.getBackend()\n if (!backend) {\n throw new Error(`ElementConfigStore(${this.elementId}): settings backend not available`)\n }\n\n const records = await backend.query({ collection: 'config', filter: {\n where: { id: this.elementId },\n limit: 1,\n } })\n if (records.length > 0) {\n this.cache = records[0]!.data ?? {}\n }\n this.loaded = true\n }\n\n getAll(): Record<string, unknown> {\n return { ...this.cache }\n }\n\n get<T = unknown>(key: string): T | undefined {\n const parts = key.split('.')\n let current: unknown = this.cache\n for (const part of parts) {\n if (current === null || typeof current !== 'object' || Array.isArray(current)) return undefined\n current = Reflect.get(current, part)\n }\n // Type-level bridge: callers supply T based on their knowledge of the\n // stored schema — the runtime has no way to prove the shape matches.\n return current as T | undefined\n }\n\n async set(key: string, value: unknown): Promise<void> {\n await this.ensureLoaded()\n setNestedValue(this.cache, key, value)\n await this.persist()\n this.notifyListeners()\n }\n\n async setAll(config: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n this.cache = { ...config }\n await this.persist()\n this.notifyListeners()\n }\n\n onChange(callback: (config: Record<string, unknown>) => void): () => void {\n this.listeners.add(callback)\n return () => { this.listeners.delete(callback) }\n }\n\n /** Initialize from storage — called by ContextFactory after creation */\n async load(): Promise<void> {\n await this.ensureLoaded()\n }\n\n /** Initialize with default values (doesn't overwrite existing) */\n async loadDefaults(defaults: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n if (Object.keys(this.cache).length === 0) {\n this.cache = { ...defaults }\n await this.persist()\n }\n }\n\n private async persist(): Promise<void> {\n const backend = this.getBackend()\n if (!backend) {\n throw new Error(`ElementConfigStore(${this.elementId}): settings backend not available — cannot persist config`)\n }\n\n // Use set() for upsert semantics — avoids race condition where two\n // concurrent persist() calls both see \"no existing row\" and both insert.\n await backend.set({ collection: 'config', key: this.elementId, value: this.cache })\n }\n\n private notifyListeners(): void {\n const snapshot = this.getAll()\n for (const listener of this.listeners) {\n try {\n listener(snapshot)\n } catch {\n // Don't let one bad listener kill others\n }\n }\n }\n}\n\nfunction setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split('.')\n let current: Record<string, unknown> = obj\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!\n const existing = current[part]\n if (existing === null || typeof existing !== 'object' || Array.isArray(existing)) {\n const fresh: Record<string, unknown> = {}\n current[part] = fresh\n current = fresh\n } else {\n current = existing as Record<string, unknown>\n }\n }\n current[parts[parts.length - 1]!] = value\n}\n","/**\n * Canonical runtime/backend/format mapping utilities.\n *\n * This is the SINGLE SOURCE OF TRUTH for mapping between:\n * - Pipeline config (runtime: 'node'|'python', backend: 'cpu'|'coreml'|'cuda'|...)\n * - DetectionRuntime ('onnx'|'coreml'|'pytorch'|'openvino'|'tflite')\n * - ModelFormat ('onnx'|'coreml'|'openvino'|'tflite'|'pt')\n *\n * Import from '@camstack/types' — do NOT duplicate these mappings anywhere.\n */\nimport type { ModelFormat } from '../types/models.js'\nimport type { DetectionRuntime } from '../types/config.js'\n\n// ---------------------------------------------------------------------------\n// Pipeline runtime: the two-level abstraction used in pipeline config\n// ---------------------------------------------------------------------------\n\n/** Pipeline-level runtime: 'node' (ONNX Runtime in Node.js) or 'python' (Python inference) */\nexport type PipelineRuntime = 'node' | 'python'\n\n// ---------------------------------------------------------------------------\n// Backend → Format: what model file format does a backend require?\n// ---------------------------------------------------------------------------\n\n/**\n * Map a backend ID to the model format it requires.\n *\n * Node.js backends (cpu, coreml, cuda, tensorrt) all use ONNX format\n * because onnxruntime-node loads .onnx and uses execution providers internally.\n *\n * Python backends use their native format.\n */\nconst BACKEND_TO_FORMAT: Readonly<Record<string, ModelFormat>> = {\n // Node.js backends — all ONNX (onnxruntime-node execution providers)\n cpu: 'onnx',\n coreml: 'onnx', // onnxruntime CoreML EP loads .onnx, not .mlpackage\n cuda: 'onnx',\n tensorrt: 'onnx',\n 'onnx-py': 'onnx',\n // Python native backends\n pytorch: 'pt',\n openvino: 'openvino',\n}\n\n/**\n * Map DetectionRuntime to ModelFormat.\n * Used when the addon receives an explicit runtime (not 'auto').\n */\nconst RUNTIME_TO_FORMAT: Readonly<Record<DetectionRuntime, ModelFormat>> = {\n onnx: 'onnx',\n coreml: 'coreml',\n openvino: 'openvino',\n tflite: 'tflite',\n pytorch: 'pt',\n}\n\n/**\n * Map a Python backend to the Python inference script name.\n */\nconst PYTHON_SCRIPT: Readonly<Record<string, string>> = {\n coreml: 'coreml_inference.py',\n pytorch: 'pytorch_inference.py',\n openvino: 'openvino_inference.py',\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve pipeline config (runtime + backend) to a DetectionRuntime.\n *\n * Pipeline config uses two levels:\n * runtime: 'node' | 'python'\n * backend: 'cpu' | 'coreml' | 'cuda' | 'openvino' | 'pytorch' | ...\n *\n * Addons expect a DetectionRuntime:\n * 'onnx' | 'coreml' | 'pytorch' | 'openvino' | 'tflite'\n *\n * Mapping rules:\n * node + any backend → 'onnx' (onnxruntime-node handles backend via EP)\n * python + coreml → 'coreml' (native .mlpackage via coremltools)\n * python + openvino → 'openvino'\n * python + pytorch → 'pytorch'\n * python + onnx-py → 'onnx'\n * python + <other> → backend as-is\n */\nexport function resolveDetectionRuntime(\n pipelineRuntime: PipelineRuntime | string,\n backend: string,\n): DetectionRuntime {\n if (pipelineRuntime === 'node') return 'onnx'\n // Python: backend IS the runtime (coreml, openvino, pytorch, etc.)\n const map: Record<string, DetectionRuntime> = {\n coreml: 'coreml',\n openvino: 'openvino',\n pytorch: 'pytorch',\n 'onnx-py': 'onnx',\n cpu: 'onnx',\n }\n return map[backend] ?? (backend as DetectionRuntime)\n}\n\n/**\n * Resolve pipeline config (runtime + backend) to a ModelFormat.\n *\n * Determines which model file format is needed (.onnx, .mlpackage, etc.)\n */\nexport function resolveModelFormat(\n pipelineRuntime: PipelineRuntime | string,\n backend: string,\n): ModelFormat {\n if (pipelineRuntime === 'node') {\n return BACKEND_TO_FORMAT[backend] ?? 'onnx'\n }\n // Python: derive format from backend\n const map: Record<string, ModelFormat> = {\n coreml: 'coreml',\n openvino: 'openvino',\n pytorch: 'pt',\n 'onnx-py': 'onnx',\n cpu: 'onnx',\n }\n return map[backend] ?? 'onnx'\n}\n\n/**\n * Resolve pipeline runtime string to 'auto' | DetectionRuntime for addon consumption.\n * Used when building addon addonConfig from pipeline step config.\n *\n * 'python' + 'coreml' → 'coreml'\n * 'node' + 'coreml' → 'onnx'\n * 'node' + 'cpu' → 'onnx'\n * 'auto' → 'auto'\n */\nexport function resolveAddonRuntime(\n pipelineRuntime: string | undefined,\n backend: string | undefined,\n): DetectionRuntime | 'auto' {\n if (!pipelineRuntime || pipelineRuntime === 'auto') return 'auto'\n return resolveDetectionRuntime(pipelineRuntime, backend ?? 'cpu')\n}\n\n/**\n * Get the model format needed for a given backend.\n * Convenience wrapper for backend-only lookup (e.g., UI components).\n */\nexport function formatForBackend(backend: string): ModelFormat {\n return BACKEND_TO_FORMAT[backend] ?? 'onnx'\n}\n\n/**\n * Get the model format for a given DetectionRuntime.\n */\nexport function formatForRuntime(runtime: DetectionRuntime): ModelFormat {\n return RUNTIME_TO_FORMAT[runtime]\n}\n\n/**\n * Get the Python inference script name for a backend.\n * Returns undefined if no Python script is needed (e.g., 'cpu' uses ONNX).\n */\nexport function pythonScriptForBackend(backend: string): string | undefined {\n return PYTHON_SCRIPT[backend]\n}\n\n/**\n * Check if a DetectionRuntime requires a Python binary.\n * 'coreml', 'pytorch', 'openvino' need Python; 'onnx' and 'tflite' don't.\n */\nexport function requiresPython(runtime: DetectionRuntime | string): boolean {\n return runtime === 'coreml' || runtime === 'pytorch' || runtime === 'openvino'\n}\n\n// Re-export constants for consumers that need the raw maps (e.g., UI filtering)\nexport { BACKEND_TO_FORMAT, RUNTIME_TO_FORMAT, PYTHON_SCRIPT }\n","import { errMsg } from './err-msg.js'\n\n/**\n * runInferenceStep — shared utility for executing a single inference call\n * with timing, error capture, and optional timeout.\n *\n * Used by: pipeline-executor, benchmark-executor, benchmark.service,\n * inference-engine-adapter — eliminating duplicated timing/error patterns.\n */\n\nexport interface InferenceStepResult<T> {\n /** The inference output, or undefined if the call failed */\n readonly output: T | undefined\n /** Wall-clock duration in ms (rounded to 2 decimals) */\n readonly durationMs: number\n /** True if the call succeeded */\n readonly ok: boolean\n /** Error message if the call failed */\n readonly error?: string\n}\n\n/**\n * Execute an inference function with timing and error capture.\n *\n * @param fn - The inference call (e.g. `() => engine.infer(frame)`)\n * @param timeoutMs - Optional timeout in ms. If exceeded, rejects with timeout error.\n * @returns InferenceStepResult with output, timing, and error info\n */\nexport async function runInferenceStep<T>(\n fn: () => Promise<T>,\n timeoutMs?: number,\n): Promise<InferenceStepResult<T>> {\n const t0 = performance.now()\n\n try {\n const output = timeoutMs !== undefined\n ? await withTimeout(fn(), timeoutMs)\n : await fn()\n\n return {\n output,\n durationMs: roundMs(performance.now() - t0),\n ok: true,\n }\n } catch (err) {\n return {\n output: undefined,\n durationMs: roundMs(performance.now() - t0),\n ok: false,\n error: errMsg(err),\n }\n }\n}\n\n/** Round to 2 decimal places */\nfunction roundMs(ms: number): number {\n return Math.round(ms * 100) / 100\n}\n\n/** Reject a promise if it exceeds the given timeout */\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`Inference timeout after ${ms}ms`)), ms)\n promise\n .then((v) => { clearTimeout(timer); resolve(v) })\n .catch((e: unknown) => { clearTimeout(timer); reject(e) })\n })\n}\n","import type { LabelDefinition, ClassMapDefinition } from '../types/labels.js'\n\nexport const COCO_80_LABELS: readonly LabelDefinition[] = [\n { id: 'person', name: 'Person' },\n { id: 'bicycle', name: 'Bicycle' },\n { id: 'car', name: 'Car' },\n { id: 'motorcycle', name: 'Motorcycle' },\n { id: 'airplane', name: 'Airplane' },\n { id: 'bus', name: 'Bus' },\n { id: 'train', name: 'Train' },\n { id: 'truck', name: 'Truck' },\n { id: 'boat', name: 'Boat' },\n { id: 'traffic light', name: 'Traffic Light' },\n { id: 'fire hydrant', name: 'Fire Hydrant' },\n { id: 'stop sign', name: 'Stop Sign' },\n { id: 'parking meter', name: 'Parking Meter' },\n { id: 'bench', name: 'Bench' },\n { id: 'bird', name: 'Bird' },\n { id: 'cat', name: 'Cat' },\n { id: 'dog', name: 'Dog' },\n { id: 'horse', name: 'Horse' },\n { id: 'sheep', name: 'Sheep' },\n { id: 'cow', name: 'Cow' },\n { id: 'elephant', name: 'Elephant' },\n { id: 'bear', name: 'Bear' },\n { id: 'zebra', name: 'Zebra' },\n { id: 'giraffe', name: 'Giraffe' },\n { id: 'backpack', name: 'Backpack' },\n { id: 'umbrella', name: 'Umbrella' },\n { id: 'handbag', name: 'Handbag' },\n { id: 'tie', name: 'Tie' },\n { id: 'suitcase', name: 'Suitcase' },\n { id: 'frisbee', name: 'Frisbee' },\n { id: 'skis', name: 'Skis' },\n { id: 'snowboard', name: 'Snowboard' },\n { id: 'sports ball', name: 'Sports Ball' },\n { id: 'kite', name: 'Kite' },\n { id: 'baseball bat', name: 'Baseball Bat' },\n { id: 'baseball glove', name: 'Baseball Glove' },\n { id: 'skateboard', name: 'Skateboard' },\n { id: 'surfboard', name: 'Surfboard' },\n { id: 'tennis racket', name: 'Tennis Racket' },\n { id: 'bottle', name: 'Bottle' },\n { id: 'wine glass', name: 'Wine Glass' },\n { id: 'cup', name: 'Cup' },\n { id: 'fork', name: 'Fork' },\n { id: 'knife', name: 'Knife' },\n { id: 'spoon', name: 'Spoon' },\n { id: 'bowl', name: 'Bowl' },\n { id: 'banana', name: 'Banana' },\n { id: 'apple', name: 'Apple' },\n { id: 'sandwich', name: 'Sandwich' },\n { id: 'orange', name: 'Orange' },\n { id: 'broccoli', name: 'Broccoli' },\n { id: 'carrot', name: 'Carrot' },\n { id: 'hot dog', name: 'Hot Dog' },\n { id: 'pizza', name: 'Pizza' },\n { id: 'donut', name: 'Donut' },\n { id: 'cake', name: 'Cake' },\n { id: 'chair', name: 'Chair' },\n { id: 'couch', name: 'Couch' },\n { id: 'potted plant', name: 'Potted Plant' },\n { id: 'bed', name: 'Bed' },\n { id: 'dining table', name: 'Dining Table' },\n { id: 'toilet', name: 'Toilet' },\n { id: 'tv', name: 'TV' },\n { id: 'laptop', name: 'Laptop' },\n { id: 'mouse', name: 'Mouse' },\n { id: 'remote', name: 'Remote' },\n { id: 'keyboard', name: 'Keyboard' },\n { id: 'cell phone', name: 'Cell Phone' },\n { id: 'microwave', name: 'Microwave' },\n { id: 'oven', name: 'Oven' },\n { id: 'toaster', name: 'Toaster' },\n { id: 'sink', name: 'Sink' },\n { id: 'refrigerator', name: 'Refrigerator' },\n { id: 'book', name: 'Book' },\n { id: 'clock', name: 'Clock' },\n { id: 'vase', name: 'Vase' },\n { id: 'scissors', name: 'Scissors' },\n { id: 'teddy bear', name: 'Teddy Bear' },\n { id: 'hair drier', name: 'Hair Drier' },\n { id: 'toothbrush', name: 'Toothbrush' },\n] as const\n\nexport const MACRO_LABELS: readonly LabelDefinition[] = [\n { id: 'person', name: 'Person' },\n { id: 'vehicle', name: 'Vehicle' },\n { id: 'animal', name: 'Animal' },\n] as const\n\nexport const COCO_TO_MACRO: ClassMapDefinition = {\n mapping: {\n person: 'person',\n bicycle: 'vehicle',\n car: 'vehicle',\n motorcycle: 'vehicle',\n airplane: 'vehicle',\n bus: 'vehicle',\n train: 'vehicle',\n truck: 'vehicle',\n boat: 'vehicle',\n bird: 'animal',\n cat: 'animal',\n dog: 'animal',\n horse: 'animal',\n sheep: 'animal',\n cow: 'animal',\n elephant: 'animal',\n bear: 'animal',\n zebra: 'animal',\n giraffe: 'animal',\n },\n // Today the pipeline only supports the three macro classes. Any other\n // COCO class (carrot, umbrella, spoon, …) is dropped upstream in the\n // executor instead of leaking through as a `macroClass: \"carrot\"`\n // detection with no child routing. When support for additional COCO\n // classes lands, either add them to `mapping` (mapped to a new macro)\n // or flip this back to `true` and teach `matchesMacroFilter` to handle\n // the raw-COCO case.\n preserveOriginal: false,\n}\n","/**\n * @deprecated Use DeviceType from '@camstack/types/device' instead.\n * This file is kept for backwards compatibility during migration.\n */\n\n/* eslint-disable @typescript-eslint/no-deprecated */\n\nexport enum DeviceType {\n Camera = 'camera',\n}\n\nexport interface DeviceTypeInfo {\n readonly type: DeviceType\n readonly label: string\n readonly icon: string // lucide icon name\n}\n\nexport const DEVICE_TYPE_INFO: Record<DeviceType, DeviceTypeInfo> = {\n [DeviceType.Camera]: { type: DeviceType.Camera, label: 'Camera', icon: 'camera' },\n}\n","import { z } from 'zod'\n\n/**\n * Optional callback invoked when `fromSchema` had to drop one or\n * more persisted fields to recover from a Zod validation failure.\n * Used by `BaseDevice` to surface the action via the device logger.\n */\nexport type ConfigRecoveryListener = (info: {\n droppedKeys: readonly string[]\n issues: readonly z.core.$ZodIssue[]\n}) => void\n\nexport class DeviceConfig<T extends z.ZodObject<z.core.$ZodLooseShape>> {\n readonly schema: T\n private data: z.infer<T>\n private readonly persistFn: (data: z.infer<T>) => Promise<void>\n\n private constructor(\n schema: T,\n data: z.infer<T>,\n persist: (data: z.infer<T>) => Promise<void>,\n ) {\n this.schema = schema\n this.data = data\n this.persistFn = persist\n }\n\n /**\n * Build a `DeviceConfig` from a persisted blob, with automatic\n * recovery from schema-validation failures. Boot must never be\n * blocked by stale persisted values: if Zod rejects the blob,\n * we drop every offending top-level field, retry, and persist\n * the cleaned blob so the bad value is healed in the DB on next\n * write. The most common trigger is a tightened range constraint\n * (e.g. `max(100) → max(50)`) on a field that already has an\n * out-of-range value persisted from the previous schema. Without\n * this safety net, the device would fail to instantiate and end\n * up with no caps registered — exactly the failure mode that\n * stranded device 15 when `motionSensitivity: 90` no longer fit\n * the new `1..50` schema.\n *\n * Recovery rules:\n * 1. Try `safeParse(initialData)`. If it succeeds, done.\n * 2. On failure, walk `error.issues`, collect the top-level path\n * of each issue, and drop those keys from `initialData`.\n * 3. Re-run `safeParse`. If the cleaned blob now passes (Zod\n * fills the missing keys with schema defaults / undefined for\n * `.optional()`), persist it via `persist()` so the bad\n * values disappear from the DB, and return the device.\n * 4. If the cleaned blob STILL fails (very rare — would require\n * a non-recoverable required field), fall back to\n * `schema.parse({})` so the device still boots with pure\n * schema defaults. Persist nothing in that path so the next\n * successful `setAll` still writes a coherent blob.\n */\n static fromSchema<T extends z.ZodObject<z.core.$ZodLooseShape>>(\n schema: T,\n persist: (data: z.infer<T>) => Promise<void>,\n initialData: Record<string, unknown> = {},\n onRecover?: ConfigRecoveryListener,\n ): DeviceConfig<T> {\n const first = schema.safeParse(initialData)\n if (first.success) {\n return new DeviceConfig(schema, first.data as z.infer<T>, persist)\n }\n\n const droppedKeys = new Set<string>()\n for (const issue of first.error.issues) {\n const top = issue.path[0]\n if (typeof top === 'string') droppedKeys.add(top)\n }\n\n const cleaned: Record<string, unknown> = { ...initialData }\n for (const k of droppedKeys) delete cleaned[k]\n const second = schema.safeParse(cleaned)\n\n onRecover?.({\n droppedKeys: [...droppedKeys],\n issues: first.error.issues,\n })\n\n if (second.success) {\n // Heal the DB: persist the cleaned blob asynchronously so the\n // offending fields are gone on next read. Best-effort — a\n // persist failure here doesn't block boot, the device still\n // runs from the in-memory cleaned data.\n void persist(second.data as z.infer<T>).catch(() => { /* swallow */ })\n return new DeviceConfig(schema, second.data as z.infer<T>, persist)\n }\n\n // Last-resort fallback: pure schema defaults. This branch should\n // be unreachable for well-formed schemas (every required field\n // must already be in `initialData`, and `.optional()` fields\n // accept undefined), but is here so a malformed blob can't\n // strand the device.\n const defaults = schema.parse({}) as z.infer<T>\n return new DeviceConfig(schema, defaults, persist)\n }\n\n get values(): z.infer<T> {\n return this.data\n }\n\n get<K extends keyof z.infer<T>>(key: K): z.infer<T>[K] {\n return this.data[key]\n }\n\n async set<K extends keyof z.infer<T>>(key: K, value: z.infer<T>[K]): Promise<void> {\n const next = this.schema.parse({ ...this.data, [key]: value })\n this.data = next\n await this.persistFn(this.data)\n }\n\n /**\n * Merge an untyped patch onto the current config and persist. Accepts\n * `Record<string, unknown>` because the patch typically comes from the\n * UI form layer (a `ConfigField.key → value` map) where the caller\n * doesn't hold the Zod schema's static type. Runtime validation is\n * authoritative: `this.schema.parse` rejects unknown keys or invalid\n * shapes before touching storage.\n */\n async setAll(partial: Record<string, unknown>): Promise<void> {\n const next = this.schema.parse({ ...this.data, ...partial })\n this.data = next\n await this.persistFn(this.data)\n }\n\n async deleteKey<K extends keyof z.infer<T>>(key: K): Promise<void> {\n const { [key as string]: _, ...rest } = this.data as Record<string, unknown>\n const next = this.schema.parse(rest)\n this.data = next\n await this.persistFn(this.data)\n }\n\n entries(): Array<{\n key: string\n schema: z.ZodType\n value: unknown\n description?: string\n }> {\n const shape = this.schema.shape as Record<string, z.ZodType>\n return Object.entries(shape).map(([key, fieldSchema]) => ({\n key,\n schema: fieldSchema,\n value: this.data[key as keyof z.infer<T>],\n description: fieldSchema.description,\n }))\n }\n}\n","import { z } from 'zod'\n\n/**\n * Per-device runtime state — cap-keyed. Mutable signals discovered\n * after boot live here, separate from `DeviceConfig` (operator\n * intent) and from `deviceCache` (immutable autodetect / abilities).\n *\n * Shape: `Record<capName, capStateSlice>`. Each slice's schema is\n * declared by the capability itself (`CapabilityDefinition.runtimeState`)\n * so that any provider implementing the cap inherits the same shape —\n * the BatteryStatus shape a Reolink cam emits is the same a Frigate or\n * ONVIF cam emits, no driver-specific re-declaration.\n *\n * Schemas are registered DYNAMICALLY as the device's caps are\n * installed (see `BaseDevice.registerNativeCap`). A slice without a\n * registered schema is rejected on write — the cap must be wired\n * first. Reads return `undefined` for unknown slices.\n *\n * Persistence: only registered slices round-trip to disk. The kernel\n * layer debounces writes; explicit `flush()` awaits in-flight saves\n * (used by the shutdown hook).\n */\nexport interface IDeviceRuntimeState {\n /**\n * Read the slice for `capName`. Three overloads, picked by\n * argument shape:\n *\n * 1. Pass a known cap-name literal (`'battery'`, `'motion'`, …)\n * → return type is auto-inferred from\n * `CapNameToRuntimeStateMap` (codegen output). No generic\n * argument, no `as`-cast.\n * 2. Pass a cap-name with an explicit generic\n * (`getCapState<MyShape>('custom-cap')`) → return that\n * shape. Used for caps the codegen doesn't see (test-only,\n * addon-private).\n * 3. Pass a cap-name with no generic (default `Record<string,\n * unknown>`) → permissive untyped fallback.\n *\n * Returns `undefined` if no slice has been written yet.\n */\n getCapState<K extends keyof import('../generated/device-local-state.js').CapNameToRuntimeStateMap>(\n capName: K,\n ): Readonly<import('../generated/device-local-state.js').CapNameToRuntimeStateMap[K]> | undefined\n getCapState<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n ): Readonly<T> | undefined\n\n /** Read a single key inside a slice. Convenience for the common\n * case of reading a primitive (`battery.percentage`). */\n getCapField(capName: string, key: string): unknown\n\n /**\n * Replace the slice for `capName`. The schema bound to `capName`\n * (via `installCapSchema`) validates `value`; throws on mismatch.\n * Listeners fire synchronously after the write applies.\n */\n setCapState(capName: string, value: Record<string, unknown>): void\n\n /**\n * Shallow-merge `partial` into the current slice for `capName`.\n * Re-validates the merged result against the cap's schema.\n */\n patchCapState(capName: string, partial: Record<string, unknown>): void\n\n /** Subscribe to ALL state changes. Receives the cap names whose\n * slices changed and the full snapshot. */\n subscribe(cb: (changedCaps: ReadonlyArray<string>, snapshot: Snapshot) => void): () => void\n\n /** Subscribe to a single cap's slice changes. Fires only when that\n * cap's slice is touched; ignores writes against other caps. */\n subscribeCap<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n cb: (slice: Readonly<T> | undefined) => void,\n ): () => void\n\n /** Frozen snapshot of every cap's slice. Cap names without a\n * written slice are omitted (sparse). */\n snapshot(): Snapshot\n\n /** Flush any pending debounced disk writes. */\n flush(): Promise<void>\n\n /**\n * Register a cap's runtime-state schema. Called by the kernel\n * when `ctx.registerNativeCap(cap, …)` runs and `cap.runtimeState`\n * is defined. Idempotent — repeat installs with the same schema\n * are no-ops; mismatched schemas throw to surface a config bug.\n *\n * Lazily validates any existing in-memory slice for `capName` so\n * a slice loaded from disk before the cap was wired is checked\n * the moment the cap shows up.\n */\n installCapSchema(capName: string, schema: z.ZodObject<z.core.$ZodLooseShape>): void\n}\n\nexport type Snapshot = Readonly<Record<string, Readonly<Record<string, unknown>>>>\n\n/**\n * Concrete implementation. Routes every successful write through\n * `writer(capName, slice)` — the kernel hooks this up to\n * `device-state.setCapSlice`, the canonical cross-layer write\n * entrypoint, which handles disk persistence (debounced on the hub)\n * and mirror updates.\n *\n * Schema validation runs in-process before the writer is called —\n * the round-trip should never carry an invalid slice. `flush()`\n * awaits any in-flight writer promises so shutdown is lossless.\n *\n * `initial` is the persisted blob loaded at boot. Slices for caps\n * whose schema hasn't been installed yet are kept in-memory verbatim\n * and validated when the cap registers later.\n */\nexport class DeviceRuntimeState implements IDeviceRuntimeState {\n private readonly writer: (capName: string, slice: Record<string, unknown>) => Promise<void>\n /** In-flight writer promises tracked so `flush()` can await them. */\n private readonly pendingWrites = new Set<Promise<unknown>>()\n /** Per-cap committed slice — after schema validation when known. */\n private slices: Map<string, Record<string, unknown>>\n /** Per-cap registered schema (set by `installCapSchema`). */\n private readonly schemas = new Map<string, z.ZodObject<z.core.$ZodLooseShape>>()\n private readonly listeners = new Set<(changed: ReadonlyArray<string>, snap: Snapshot) => void>()\n private readonly capListeners = new Map<string, Set<(slice: Readonly<Record<string, unknown>> | undefined) => void>>()\n\n private constructor(\n initial: Record<string, unknown>,\n writer: (capName: string, slice: Record<string, unknown>) => Promise<void>,\n ) {\n this.writer = writer\n // Hydrate slices: every top-level key in `initial` is a cap name,\n // its value should be an object. Non-object values are dropped\n // with no error — the persisted blob is owned by us, so a\n // garbage shape is a kernel bug we'd rather surface elsewhere.\n this.slices = new Map()\n for (const [k, v] of Object.entries(initial)) {\n if (v && typeof v === 'object' && !Array.isArray(v)) {\n this.slices.set(k, { ...v as Record<string, unknown> })\n }\n }\n }\n\n static fromInitial(\n initial: Record<string, unknown>,\n writer: (capName: string, slice: Record<string, unknown>) => Promise<void>,\n ): DeviceRuntimeState {\n return new DeviceRuntimeState(initial, writer)\n }\n\n installCapSchema(capName: string, schema: z.ZodObject<z.core.$ZodLooseShape>): void {\n const existing = this.schemas.get(capName)\n if (existing) {\n // Idempotent: caps register multiple times during the device's\n // lifetime (constructor + post-login retroactive registration).\n // Ignore identical re-registrations; a different schema for the\n // same cap is a programming error worth throwing on.\n if (existing !== schema) {\n throw new Error(\n `[DeviceRuntimeState] capability \"${capName}\" registered a different runtime-state schema; `\n + `each cap must declare ONE shape across every provider`,\n )\n }\n return\n }\n this.schemas.set(capName, schema)\n // Validate any pre-loaded slice now that we have the schema.\n // Reject silently and clear: the persisted blob shouldn't crash\n // the device on boot, just lose stale data the schema rejects.\n const stored = this.slices.get(capName)\n if (stored) {\n const result = schema.safeParse(stored)\n if (result.success) {\n this.slices.set(capName, result.data as Record<string, unknown>)\n } else {\n this.slices.delete(capName)\n }\n }\n }\n\n getCapState<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n ): Readonly<T> | undefined {\n const slice = this.slices.get(capName)\n if (!slice) return undefined\n return Object.freeze({ ...slice }) as unknown as Readonly<T>\n }\n\n getCapField(capName: string, key: string): unknown {\n return this.slices.get(capName)?.[key]\n }\n\n setCapState(capName: string, value: Record<string, unknown>): void {\n this.applyCapWrite(capName, value, /* merge */ false)\n }\n\n patchCapState(capName: string, partial: Record<string, unknown>): void {\n this.applyCapWrite(capName, partial, /* merge */ true)\n }\n\n /**\n * Internal worker. `merge` controls whether `value` replaces or\n * shallow-merges into the existing slice. Schema validation runs\n * on the FINAL composed object regardless.\n */\n private applyCapWrite(capName: string, value: Record<string, unknown>, merge: boolean): void {\n const schema = this.schemas.get(capName)\n if (!schema) {\n // Refuse: a cap without a registered schema can't have its\n // slice written. Drivers that legitimately need extra\n // ad-hoc state should attach a `runtimeState` schema to the\n // cap definition. The strict refusal catches typos\n // (`'batery'` vs `'battery'`) loudly.\n throw new Error(\n `[DeviceRuntimeState] no schema registered for cap \"${capName}\" — `\n + `did the device register it via ctx.registerNativeCap before writing?`,\n )\n }\n const current = this.slices.get(capName) ?? {}\n const next = merge ? { ...current, ...value } : { ...value }\n const parsed = schema.parse(next) as Record<string, unknown>\n // Equality check before commit — back-to-back identical writes\n // (e.g. battery push every minute with the same percentage)\n // should NOT churn listeners or disk.\n if (shallowEqual(current, parsed)) return\n this.slices.set(capName, parsed)\n this.fireListeners([capName])\n // Kick off the write asynchronously — schema validation already\n // ran, so failure here means a transport / hub issue. We log\n // and drop the error rather than throwing, mirroring the\n // fire-and-forget contract `setCapState` had with the legacy\n // debounced persister.\n const writePromise = this.writer(capName, { ...parsed }).catch(() => {\n /* transport error — caller can re-issue if it cares */\n })\n this.pendingWrites.add(writePromise)\n void writePromise.finally(() => { this.pendingWrites.delete(writePromise) })\n }\n\n private fireListeners(changed: ReadonlyArray<string>): void {\n const snap = this.snapshot()\n for (const cb of this.listeners) {\n try { cb(changed, snap) } catch { /* listener errors don't break the writer */ }\n }\n for (const capName of changed) {\n const subs = this.capListeners.get(capName)\n if (!subs) continue\n const slice = this.getCapState(capName)\n for (const cb of subs) {\n try { cb(slice) } catch { /* idem */ }\n }\n }\n }\n\n subscribe(cb: (changed: ReadonlyArray<string>, snap: Snapshot) => void): () => void {\n this.listeners.add(cb)\n return () => { this.listeners.delete(cb) }\n }\n\n subscribeCap<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n cb: (slice: Readonly<T> | undefined) => void,\n ): () => void {\n let subs = this.capListeners.get(capName)\n if (!subs) {\n subs = new Set()\n this.capListeners.set(capName, subs)\n }\n const adapter = (slice: Readonly<Record<string, unknown>> | undefined): void => {\n cb(slice as Readonly<T> | undefined)\n }\n subs.add(adapter)\n return () => {\n const set = this.capListeners.get(capName)\n if (!set) return\n set.delete(adapter)\n if (set.size === 0) this.capListeners.delete(capName)\n }\n }\n\n snapshot(): Snapshot {\n const out: Record<string, Record<string, unknown>> = {}\n for (const [k, v] of this.slices) out[k] = Object.freeze({ ...v })\n return Object.freeze(out) as Snapshot\n }\n\n async flush(): Promise<void> {\n if (this.pendingWrites.size === 0) return\n // Snapshot the current set so writes added while we await\n // (e.g. listeners that re-trigger setCapState) settle in a\n // separate flush round if the caller wants strict drainage.\n const inflight = [...this.pendingWrites]\n await Promise.allSettled(inflight)\n }\n}\n\nfunction shallowEqual(a: Record<string, unknown>, b: Record<string, unknown>): boolean {\n const ak = Object.keys(a)\n const bk = Object.keys(b)\n if (ak.length !== bk.length) return false\n for (const k of ak) {\n if (a[k] !== b[k]) return false\n }\n return true\n}\n","import type { z } from 'zod'\nimport type { CapabilityDefinition } from '../capabilities/capability-definition.js'\nimport type { IDeviceRuntimeState } from './device-runtime-state.js'\n\nconst LAST_FETCHED_FIELD = 'lastFetchedAt'\n\n/**\n * Bridge a cap's `runtimeState` slice to its read-side methods.\n *\n * Pattern: provider keeps the camera fetch / write path, but delegates\n * stale-check + slice read + cold-start fallback to this helper. Cuts\n * the boilerplate that every provider would otherwise repeat across\n * `getStatus`, `getSettings`, and any other read-side cap method.\n *\n * Convention: the cap's `runtimeState` schema must include a numeric\n * `lastFetchedAt` field (ms epoch) so the helper can drive the\n * stale-check. The field is stripped from the value returned by\n * `getStatus` so the cap's status schema (which doesn't carry it)\n * still parses cleanly.\n *\n * Usage in a provider:\n * ```ts\n * const bridge = createRuntimeStateBridge({\n * runtimeState: this.runtimeState,\n * cap: ptzAutotrackCapability,\n * ownDeviceId: this.id,\n * refresh: () => this.refreshAutotrackFromCamera(),\n * staleMs: 10_000,\n * empty: () => emptyAutotrackStatus(),\n * })\n *\n * const provider: InferNativeProvider<typeof ptzAutotrackCapability> = {\n * getStatus: bridge.getStatus,\n * getSettings: async ({ deviceId }) => {\n * if (deviceId !== this.id) return null\n * await bridge.ensureFresh()\n * return this.runtimeState.getCapState<...>(...)?.currentSettings ?? null\n * },\n * setEnabled: async (...) => { ... ; await this.refreshAutotrackFromCamera() },\n * setSettings: async (...) => { ... ; await this.refreshAutotrackFromCamera() },\n * }\n * ```\n *\n * Single-flight (collapsing concurrent `refresh()` invocations) lives\n * INSIDE `refresh` — the camera client owns its own promise slot. This\n * helper stays thin so a single provider can host multiple cap-bridged\n * readers without fighting over a shared promise.\n */\nexport interface RuntimeStateBridge<TStatus> {\n /** Run a stale-check; if the slice is missing or older than\n * `staleMs`, await `refresh()`. Idempotent within the freshness\n * window — back-to-back calls touch zero camera I/O. */\n readonly ensureFresh: () => Promise<void>\n /** Drop-in `getStatus` provider method. Includes the cross-device\n * guard + freshness check + status projection from the slice. */\n readonly getStatus: (input: { readonly deviceId: number }) => Promise<TStatus>\n}\n\nexport function createRuntimeStateBridge<\n TCap extends CapabilityDefinition & { status: { schema: z.ZodType } },\n>(params: {\n readonly runtimeState: IDeviceRuntimeState\n readonly cap: TCap\n readonly ownDeviceId: number\n readonly refresh: () => Promise<void>\n readonly staleMs: number\n readonly empty: () => z.infer<TCap['status']['schema']>\n}): RuntimeStateBridge<z.infer<TCap['status']['schema']>> {\n const { runtimeState, cap, ownDeviceId, refresh, staleMs, empty } = params\n\n const ensureFresh = async (): Promise<void> => {\n const slice = runtimeState.getCapState<Record<string, unknown>>(cap.name)\n const fetchedAt = typeof slice?.[LAST_FETCHED_FIELD] === 'number'\n ? (slice[LAST_FETCHED_FIELD] as number)\n : 0\n if (!slice || Date.now() - fetchedAt > staleMs) await refresh()\n }\n\n const projectStatus = (): z.infer<TCap['status']['schema']> => {\n const slice = runtimeState.getCapState<Record<string, unknown>>(cap.name)\n if (!slice) return empty()\n // Strip lastFetchedAt (and any future helper-only fields) before\n // returning. Status schemas don't carry this field; runtime-state\n // schemas extend status with it for stale tracking.\n const { [LAST_FETCHED_FIELD]: _omit, ...rest } = slice\n return rest as z.infer<TCap['status']['schema']>\n }\n\n const getStatus = async ({ deviceId }: { readonly deviceId: number }): Promise<z.infer<TCap['status']['schema']>> => {\n if (deviceId !== ownDeviceId) {\n throw new Error(`${cap.name}: deviceId mismatch, expected ${ownDeviceId}, got ${deviceId}`)\n }\n await ensureFresh()\n return projectStatus()\n }\n\n return { ensureFresh, getStatus }\n}\n","// AUTO-GENERATED — do not edit manually.\n// Regenerate with: npx tsx scripts/generate-device-proxy.ts\n/* eslint-disable */\n\nimport type { CapabilityDefinition, InferRuntimeState } from '../capabilities/capability-definition.js'\nimport { audioMetricsCapability } from '../capabilities/audio-metrics.cap.js'\nimport { batteryCapability } from '../capabilities/battery.cap.js'\nimport { brightnessCapability } from '../capabilities/brightness.cap.js'\nimport { cameraStreamsCapability } from '../capabilities/camera-streams.cap.js'\nimport { deviceDiscoveryCapability } from '../capabilities/device-discovery.cap.js'\nimport { deviceStatusCapability } from '../capabilities/device-status.cap.js'\nimport { doorbellCapability } from '../capabilities/doorbell.cap.js'\nimport { featureProbeCapability } from '../capabilities/feature-probe.cap.js'\nimport { motionCapability } from '../capabilities/motion.cap.js'\nimport { motionTriggerCapability } from '../capabilities/motion-trigger.cap.js'\nimport { ptzAutotrackCapability } from '../capabilities/ptz-autotrack.cap.js'\nimport { switchCapability } from '../capabilities/switch.cap.js'\nimport { zoneAnalyticsCapability } from '../capabilities/zone-analytics.cap.js'\nimport { zoneRulesCapability } from '../capabilities/zone-rules.cap.js'\nimport { zonesCapability } from '../capabilities/zones.cap.js'\n\n/**\n * Server-side, write-capable per-cap state shape for any device that\n * extends `BaseDevice`. One entry per cap with `runtimeState:` declared.\n *\n * Drivers access via `this.state.<capName>.<field>` — both reads and\n * writes route through the device's runtime-state slice (validate,\n * persist, fire `<cap>.onChanged`). No string keys, no manual generic.\n *\n * Adding a new cap with `runtimeState:` auto-extends this interface —\n * regenerate with `npm run codegen`.\n */\nexport interface DeviceLocalState {\n audioMetrics: InferRuntimeState<typeof audioMetricsCapability>\n battery: InferRuntimeState<typeof batteryCapability>\n brightness: InferRuntimeState<typeof brightnessCapability>\n cameraStreams: InferRuntimeState<typeof cameraStreamsCapability>\n deviceDiscovery: InferRuntimeState<typeof deviceDiscoveryCapability>\n deviceStatus: InferRuntimeState<typeof deviceStatusCapability>\n doorbell: InferRuntimeState<typeof doorbellCapability>\n featureProbe: InferRuntimeState<typeof featureProbeCapability>\n motion: InferRuntimeState<typeof motionCapability>\n motionTrigger: InferRuntimeState<typeof motionTriggerCapability>\n ptzAutotrack: InferRuntimeState<typeof ptzAutotrackCapability>\n switch: InferRuntimeState<typeof switchCapability>\n zoneAnalytics: InferRuntimeState<typeof zoneAnalyticsCapability>\n zoneRules: InferRuntimeState<typeof zoneRulesCapability>\n zones: InferRuntimeState<typeof zonesCapability>\n}\n\n/**\n * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s\n * `state` getter looks up the cap definition here to construct a\n * `sliceProxy()` lazily on first access. Generated alongside the type\n * so type and runtime registry can never drift apart.\n */\nexport const DEVICE_LOCAL_STATE_CAPS: Record<keyof DeviceLocalState, CapabilityDefinition> = {\n audioMetrics: audioMetricsCapability,\n battery: batteryCapability,\n brightness: brightnessCapability,\n cameraStreams: cameraStreamsCapability,\n deviceDiscovery: deviceDiscoveryCapability,\n deviceStatus: deviceStatusCapability,\n doorbell: doorbellCapability,\n featureProbe: featureProbeCapability,\n motion: motionCapability,\n motionTrigger: motionTriggerCapability,\n ptzAutotrack: ptzAutotrackCapability,\n switch: switchCapability,\n zoneAnalytics: zoneAnalyticsCapability,\n zoneRules: zoneRulesCapability,\n zones: zonesCapability,\n}\n\n/**\n * Cap-name keyed runtime-state map (kebab-case keys, e.g. `'battery'`,\n * `'device-discovery'`). Companion to `DeviceLocalState` (which uses\n * camelCase property keys). Consumed by the\n * `IDeviceRuntimeState.getCapState` overload so callers passing a\n * cap-name literal get the typed slice back without an explicit\n * generic argument:\n *\n * const slice = this.runtimeState.getCapState('battery')\n * // ^^^^^ inferred as InferRuntimeState<typeof batteryCapability>\n *\n * Eliminates the `as`-cast at every `getCapState<X>('cap-name')` site.\n */\nexport interface CapNameToRuntimeStateMap {\n 'audio-metrics': InferRuntimeState<typeof audioMetricsCapability>\n 'battery': InferRuntimeState<typeof batteryCapability>\n 'brightness': InferRuntimeState<typeof brightnessCapability>\n 'camera-streams': InferRuntimeState<typeof cameraStreamsCapability>\n 'device-discovery': InferRuntimeState<typeof deviceDiscoveryCapability>\n 'device-status': InferRuntimeState<typeof deviceStatusCapability>\n 'doorbell': InferRuntimeState<typeof doorbellCapability>\n 'feature-probe': InferRuntimeState<typeof featureProbeCapability>\n 'motion': InferRuntimeState<typeof motionCapability>\n 'motion-trigger': InferRuntimeState<typeof motionTriggerCapability>\n 'ptz-autotrack': InferRuntimeState<typeof ptzAutotrackCapability>\n 'switch': InferRuntimeState<typeof switchCapability>\n 'zone-analytics': InferRuntimeState<typeof zoneAnalyticsCapability>\n 'zone-rules': InferRuntimeState<typeof zoneRulesCapability>\n 'zones': InferRuntimeState<typeof zonesCapability>\n}\n","import { z } from 'zod'\nimport { DeviceConfig } from './device-config.js'\nimport { DeviceRuntimeState } from './device-runtime-state.js'\nimport type { IDeviceRuntimeState } from './device-runtime-state.js'\nimport type { DeviceType, DeviceFeature, DeviceRole } from './device-type.js'\nimport type { DeviceContext } from './device-context.js'\nimport type { IDevice } from './device.js'\nimport type { ConfigUISchemaWithValues } from '../interfaces/config-ui.js'\nimport { deviceStatusCapability, type DeviceStatus } from '../capabilities/device-status.cap.js'\nimport { featureProbeCapability, type FeatureProbeStatus } from '../capabilities/feature-probe.cap.js'\nimport type { CapabilityDefinition, InferRuntimeState } from '../capabilities/capability-definition.js'\nimport { DEVICE_LOCAL_STATE_CAPS, type DeviceLocalState } from '../generated/device-local-state.js'\n\n// Zod 4's `ZodRawShape = $ZodShape = Record<string, $ZodType<unknown, unknown>>`\n// is too strict for concrete-shape schemas: a `z.object({ host: z.string(), … })`\n// produces `ZodObject<{ host: ZodString, … }>` whose fields' types\n// (`ZodString`, `ZodDefault<ZodNumber>`, …) don't widen to\n// `$ZodType<unknown, unknown>` once a subclass has 30+ fields, so\n// HikvisionCamera fails TS2344 on the constraint.\n//\n// Zod ships `$ZodLooseShape = Record<string, any>` for exactly this\n// pattern — generic constraints that should accept any concrete schema.\n// Using it on the type parameter keeps the typed inference path\n// (`z.infer<T>`, `T['shape']['host']`) intact while admitting concrete\n// schemas from subclasses.\nexport abstract class BaseDevice<T extends z.ZodObject<z.core.$ZodLooseShape> = z.ZodObject<z.ZodRawShape>> implements IDevice {\n readonly id: number\n readonly stableId: string\n readonly type: DeviceType\n readonly name: string\n readonly parentDeviceId: number | null\n readonly role?: DeviceRole\n /**\n * Cap-keyed runtime-state slice is the single source of truth for\n * `online`. Both getter and setter proxy to the slice — drivers can\n * write `this.online = true` ergonomically, and the cap event fires\n * automatically through the runtime-state writer. `markOnline()` is\n * kept as the explicit method form mandated by `IDevice`.\n */\n get online(): boolean {\n const slice = this.runtimeState.getCapState<DeviceStatus>('device-status')\n return slice?.online ?? false\n }\n set online(value: boolean) {\n this.markOnline(value)\n }\n\n /**\n * Generic per-cap runtime-state namespace. One entry per cap with\n * `runtimeState:` declared, auto-generated by codegen — see\n * `device-local-state.ts`. Drivers access via:\n *\n * `this.state.battery.sleeping = true` // patches the battery slice\n * `const pct = this.state.battery.percentage` // reads the battery slice\n * `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`\n *\n * Adding a new cap with `runtimeState:` automatically extends this\n * namespace — drivers don't have to declare proxies. Reads return\n * `undefined` when the slice hasn't been seeded; writes patch via\n * `runtimeState.patchCapState` and validate against the cap's schema\n * (so partial writes need the slice to be seeded with the required\n * fields first — drivers do this on cap registration).\n *\n * For caps not exposed in `DeviceLocalState`, drivers can build their\n * own typed proxy via `this.sliceProxy(cap)`.\n */\n get state(): DeviceLocalState {\n if (!this._stateProxyCache) {\n const cache: Record<string, unknown> = {}\n const handler: ProxyHandler<Record<string, unknown>> = {\n get: (_target, key) => {\n const k = key as string\n if (k in cache) return cache[k]\n const cap = (DEVICE_LOCAL_STATE_CAPS as Record<string, CapabilityDefinition | undefined>)[k]\n if (!cap) return undefined\n const proxy = this.sliceProxy(cap)\n cache[k] = proxy\n return proxy\n },\n }\n this._stateProxyCache = new Proxy(cache, handler) as unknown as DeviceLocalState\n }\n return this._stateProxyCache\n }\n private _stateProxyCache?: DeviceLocalState\n abstract readonly features: readonly DeviceFeature[]\n readonly config: DeviceConfig<T>\n /**\n * Per-device runtime state, cap-keyed. Always installed — slices\n * for individual caps materialise as those caps register their\n * native providers (`ctx.registerNativeCap`). The cap's own\n * `runtimeState` schema is the source of truth for the slice\n * shape; drivers don't redeclare it, they just write through.\n *\n * Read: `this.runtimeState.getCapState('battery')` →\n * `{percentage, charging, sleeping, lastUpdated}` for any\n * provider that registers `batteryCapability`.\n * Write: `this.runtimeState.setCapState('battery', { … })`.\n *\n * Cross-process consumers reach this state through the\n * `deviceState` cap router (or via cap-specific events the driver\n * emits — e.g. `battery.onStatusChanged`). The local handle is\n * accessed in-process by the driver to avoid roundtrips.\n */\n readonly runtimeState: IDeviceRuntimeState\n readonly ctx: DeviceContext\n\n /**\n * Operator-organisational location label (room / area / zone).\n * Read from `ctx.deviceMeta.location`; mutated via\n * `kernel.devices.setLocation(id, value)`. Free-text — providers\n * don't interpret it; the UI groups devices by this for filters\n * like \"show me all cameras in Kitchen\". `null` when unset.\n */\n readonly location: string | null\n /**\n * Soft-disabled flag. When `true`, the device class is still\n * instantiated and visible in the UI (so the operator can flip\n * back on without re-adding) but lifecycle hooks (publishToBroker,\n * alarm-stream subscribe, …) MUST be gated by the driver to skip\n * work. The `BaseDevice` enforces this by exposing the flag here;\n * it does NOT mutate cap behaviour automatically — drivers consult\n * `this.disabled` at the top of their lifecycle methods. Read from\n * `ctx.deviceMeta.disabled`; mutated via\n * `kernel.devices.setDisabled(id, value)`.\n */\n readonly disabled: boolean\n\n constructor(\n ctx: DeviceContext,\n schema: T,\n options: {\n type: DeviceType\n /** Optional semantic role within parent — see `DeviceRole`. */\n role?: DeviceRole\n },\n ) {\n this.ctx = ctx\n this.id = ctx.id\n this.stableId = ctx.stableId\n this.type = options.type\n // Operator-organisational fields (`name` / `location` / `disabled`)\n // come exclusively from the kernel-managed meta surface. Production\n // contexts always populate `ctx.deviceMeta`; test fixtures must\n // supply it explicitly. No fallback to `options.name` — the only\n // way to set the display name is through the meta API.\n if (!ctx.deviceMeta) {\n throw new Error(\n `BaseDevice constructor: ctx.deviceMeta is required (id=${ctx.id} stableId=${ctx.stableId})`,\n )\n }\n this.name = ctx.deviceMeta.name\n this.location = ctx.deviceMeta.location\n this.disabled = ctx.deviceMeta.disabled\n this.role = options.role\n this.parentDeviceId = ctx.parentDeviceId\n // DeviceContext.persistConfig is typed as (data: unknown) => Promise<void>\n // to break the circular dependency between DeviceContext and every schema type T.\n // DeviceConfig.fromSchema requires (data: z.infer<T>) => Promise<void>, which is a\n // subtype of (data: unknown) => Promise<void> at runtime (TypeScript variance rules\n // prevent direct assignment for function parameters). The wrapper below narrows the\n // type without losing safety — the actual data passed is always z.infer<T> because\n // DeviceConfig itself only calls persistFn with validated T values.\n //\n // Self-hydrate: the kernel pre-loads the per-device persisted\n // config blob from the DB and stuffs it into `ctx.persistedConfig`\n // before this constructor runs. We seed `DeviceConfig` from that\n // blob alone — operator-supplied initial config (Add-Device form\n // input) reaches us via the same DB read because providers\n // pre-persist via `kernel.devices.create(stableId, Class, config)`\n // (or `persistInitialConfig` in the manual flow), which writes\n // the blob to the DB BEFORE constructing the device. The device\n // class therefore never sees an `initialData` constructor arg —\n // every config read goes through `this.config.get(...)` which is\n // a thin in-memory cache over the DB row. Schema defaults fill\n // any missing keys via Zod's parse pass.\n const seedData = ctx.persistedConfig ?? {}\n this.config = DeviceConfig.fromSchema(\n schema,\n (data: z.infer<T>) => ctx.persistConfig(data),\n seedData,\n ({ droppedKeys, issues }) => {\n // Stale persisted values that no longer match the schema —\n // logged once at boot so operators can see what got reset.\n // The DB blob is healed asynchronously inside `fromSchema`\n // (it persists the cleaned data), so this log is the only\n // visible trace of the recovery path firing.\n ctx.logger.warn('Device config recovery: dropping invalid persisted fields', {\n tags: { deviceId: ctx.id, stableId: ctx.stableId },\n meta: {\n droppedKeys: [...droppedKeys],\n firstIssue: issues[0]?.message ?? null,\n },\n })\n },\n )\n\n // Always install runtime state — schemas attach lazily as the\n // device's caps register their native providers (see\n // `ctx.registerNativeCap` wiring in the kernel context factory).\n //\n // Writer routes through `fetchDevice(id).deviceState.setCapSlice`\n // so we share the same per-device facade admin-ui + addons use.\n // The DeviceProxy auto-injects deviceId; the dispatch falls\n // through to the local hub provider (or its Moleculer bridge\n // proxy when device-manager runs cross-process).\n //\n // The proxy is fetched lazily on the first write and cached for\n // the device's lifetime — `getBindings` is one cheap query, and\n // `setCapSlice` is a system-cap call that doesn't read binding\n // entries (so cache staleness is irrelevant for this writer).\n let cachedProxy: Promise<import('../generated/device-proxy.js').DeviceProxy> | null = null\n const writer = async (capName: string, slice: Record<string, unknown>): Promise<void> => {\n if (!cachedProxy) cachedProxy = ctx.fetchDevice(ctx.id)\n const dev = await cachedProxy\n await dev.deviceState.setCapSlice({ capName, slice })\n }\n const initial = ctx.initialRuntimeState ?? {}\n this.runtimeState = DeviceRuntimeState.fromInitial(initial, writer)\n // Hand the reference back to the context so subsequent\n // `ctx.registerNativeCap` calls (fired from this constructor's\n // own `registerNativeCapabilities` chain) can install each\n // cap's `runtimeState` schema. Optional hook — test contexts\n // skip it; caps with runtime state in those contexts go memory-\n // only without schema validation.\n ctx.bindRuntimeState?.(this.runtimeState)\n\n // Auto-register the generic `device-status` cap so every device\n // exposes a uniform cap-keyed slice for the base device-level\n // flags (`online`, `lastChangedAt`). Cross-process consumers read\n // via `device-state.getCapSlice({deviceId, capName: 'device-status'})`\n // instead of having to special-case `online` against the device\n // summary RPC. Driver-specific caps (`battery`, `doorbell`, …)\n // own their own slices.\n ctx.registerNativeCap?.(deviceStatusCapability, {})\n // Seed the slice synchronously with the canonical pre-firmware\n // default (`online: false`) so the `online` getter resolves and\n // the first cross-process read returns a populated record.\n const seed: DeviceStatus = { online: false, lastChangedAt: Date.now() }\n this.runtimeState.setCapState('device-status', seed)\n\n // Auto-register the generic `feature-probe` cap. Drivers populate it\n // from `onProbe()` (kernel calls it once after register, before\n // accessory reconciliation). The slice is the single source of truth\n // for `getAccessoryChildren()` decisions and the public `features`\n // array — it replaces the older driver-local `deviceCache.has*`\n // duplication.\n ctx.registerNativeCap?.(featureProbeCapability, {})\n const probeSeed: FeatureProbeStatus = {\n flags: {},\n deviceType: null,\n model: null,\n channelCount: null,\n lastProbedAt: 0,\n lastFetchedAt: 0,\n }\n this.runtimeState.setCapState('feature-probe', probeSeed)\n }\n\n async removeDevice(): Promise<void> {\n // Override in subclass for cleanup (disconnect, stop streams, etc.)\n }\n\n /**\n * Set the device's online flag. Called by `BaseDeviceProvider` after\n * aggregating per-profile stream-broker health, or directly by drivers\n * that have provider-side liveness signals (e.g. ONVIF heartbeats,\n * Reolink Baichuan firmware push events). Mirrors the new value into\n * the `device-status` runtime-state slice so cross-process consumers\n * pick it up via the standard cap-state channel. Subclasses can\n * override to gate side effects on the transition.\n */\n markOnline(online: boolean): void {\n if (this.online === online) return\n const next: DeviceStatus = { online, lastChangedAt: Date.now() }\n this.runtimeState.setCapState('device-status', next)\n }\n\n /**\n * Re-publish the device's current `features` array to the persisted\n * meta blob. Drivers call this after a probe finishes when the live\n * `features` getter has gained new flags (e.g. `hasIntercom` flips\n * to true → `DeviceFeature.TwoWayAudio` joins the list).\n *\n * Without this, only the construction-time snapshot is written —\n * `deviceManager.registerDevice` is invoked once per boot, so probe-\n * driven additions don't reach the persisted index until the next\n * server restart, and `getDevice` / `listAll` keep returning the\n * stale list for forked-worker devices (whose live IDevice instance\n * is invisible to the hub registry).\n *\n * Idempotent: re-calling with the same features just no-ops on the\n * persisted meta. Best-effort: lookup or write failures are logged\n * at debug and swallowed — the live `device.features` getter is\n * still authoritative within this process, so callers never block\n * device boot on a meta refresh.\n */\n protected async refreshFeatures(): Promise<void> {\n const api = this.ctx.api as unknown as {\n deviceManager?: {\n registerDevice?: { mutate: (input: {\n addonId: string\n stableId: string\n id: number\n type: string\n name: string\n parentDeviceId: number | null\n features?: readonly string[]\n config: Record<string, unknown>\n }) => Promise<void> }\n }\n }\n const action = api?.deviceManager?.registerDevice\n if (!action) return\n try {\n await action.mutate({\n addonId: this.ctx.deviceMeta.addonId,\n stableId: this.stableId,\n id: this.id,\n type: this.type,\n name: this.name,\n parentDeviceId: this.parentDeviceId,\n features: [...this.features],\n // Empty config — the kernel's registerDevice ignores the field\n // when there's no payload (the per-device store is the\n // canonical source after construction).\n config: {},\n })\n } catch (err) {\n // No logger on BaseDevice (would require ctx wiring) — drivers\n // that want visibility wrap the call themselves.\n void err\n }\n }\n\n /**\n * Typed read-through to a cap-keyed runtime-state slice. Drivers\n * call `this.getCapSlice(batteryCapability)` and the return type\n * is inferred from the cap's `runtimeState` Zod schema — no string\n * key, no manual generic. Returns `null` when the slice hasn't\n * been written yet (e.g. driver hasn't seeded battery yet).\n */\n protected getCapSlice<TCap extends CapabilityDefinition>(cap: TCap): InferRuntimeState<TCap> | null {\n const slice = this.runtimeState.getCapState(cap.name)\n return (slice as InferRuntimeState<TCap> | undefined) ?? null\n }\n\n /**\n * Typed writer to a cap-keyed runtime-state slice. Routes through\n * the runtime-state writer (validate → persist → emit cap event).\n * Equivalent to `this.runtimeState.setCapState(cap.name, value)`\n * but with the cap's `runtimeState` schema enforcing the value\n * shape at compile time. Mirrors the symmetry of\n * `getCapSlice` / `setCapSlice` for cross-cap consistency.\n */\n protected setCapSlice<TCap extends CapabilityDefinition>(\n cap: TCap,\n value: InferRuntimeState<TCap> & Record<string, unknown>,\n ): void {\n this.runtimeState.setCapState(cap.name, value)\n }\n\n /**\n * Field-level read/write proxy over a cap's runtime-state slice.\n * Drivers that want ergonomic per-field access declare:\n *\n * ```ts\n * protected battery = this.sliceProxy(batteryCapability)\n * // …\n * this.battery.sleeping = true // patches the slice\n * const charging = this.battery.charging // reads the slice\n * ```\n *\n * Reads return `undefined` when the slice hasn't been seeded yet\n * (cap not registered, or seeded but the field is absent). Writes\n * route through `runtimeState.patchCapState` so the cap's `runtimeState`\n * schema validates the merged result and the cap event fires.\n *\n * Pattern is generic — same shape works for `battery`, `device-status`,\n * `motion`, `doorbell`, anything with a `runtimeState:` schema. Drivers\n * declare one proxy per cap they read/write directly.\n */\n protected sliceProxy<TCap extends CapabilityDefinition>(cap: TCap): InferRuntimeState<TCap> {\n const target: Record<string, unknown> = {}\n const handler: ProxyHandler<Record<string, unknown>> = {\n get: (_, key) => {\n const slice = this.runtimeState.getCapState(cap.name)\n return slice?.[key as string]\n },\n set: (_, key, value: unknown) => {\n this.runtimeState.patchCapState(cap.name, { [key as string]: value })\n return true\n },\n has: (_, key) => {\n const slice = this.runtimeState.getCapState(cap.name)\n return slice ? key in slice : false\n },\n ownKeys: () => {\n const slice = this.runtimeState.getCapState(cap.name)\n return slice ? Object.keys(slice) : []\n },\n getOwnPropertyDescriptor: (_, key) => {\n const slice = this.runtimeState.getCapState(cap.name)\n if (!slice || !(key in slice)) return undefined\n return { configurable: true, enumerable: true, value: slice[key as string] }\n },\n }\n return new Proxy(target, handler) as InferRuntimeState<TCap>\n }\n\n /**\n * Default empty settings UI. Drivers override this to expose an\n * editable form in the device-details page. Returning an empty sections\n * array signals \"nothing to contribute\" — the aggregator drops the\n * contribution entirely rather than rendering a blank panel.\n */\n getSettingsUISchema(): ConfigUISchemaWithValues {\n return { sections: [] }\n }\n\n /**\n * Default write path: forward the flat patch directly to storage.\n * Drivers that project a UI shape different from storage (e.g. `RtspCamera`\n * exposing `mainStreamUrl`/`subStreamUrl` over `streams[]`) override this\n * to reshape before `config.setAll`.\n */\n async applySettingsPatch(patch: Record<string, unknown>): Promise<void> {\n await this.config.setAll(patch)\n }\n\n // ── Lifecycle hooks ────────────────────────────────────────────────\n //\n // The kernel orchestrates the device lifecycle in distinct phases.\n // Drivers override only the hooks they need:\n //\n // 1. Construct — `new DeviceClass(ctx)` (sync)\n // 2. RegisterIdentity — kernel writes meta + id to device-manager\n // 3. Probe — `onProbe()` populates `feature-probe` slice\n // 4. ReconcileAccessories — kernel calls `getAccessoryChildren()`\n // (sees post-probe flags), spawns missing,\n // removes orphans, refreshes meta features\n // 5. Activate — `onActivate()` does \"device is live\" work:\n // broker publish, snapshot tabs, watchdogs\n //\n // Failures in `onProbe` / `onActivate` are logged and swallowed — the\n // device IS already registered; partial init shouldn't unwind the\n // create the caller succeeded at.\n\n /**\n * Phase 3 — populate device-scoped state needed by downstream phases\n * (accessory reconciliation, public `features` array, optional cap\n * registration). Called ONCE per construction, after register but\n * before `getAccessoryChildren()`.\n *\n * Drivers write the `feature-probe` runtime-state slice via\n * `this.runtimeState.setCapState('feature-probe', {...})` — flag bag\n * is open (Reolink writes `hasPtz/hasIntercom`, Hikvision writes\n * `hasSupplementalLight/hasAlarmIo`, etc).\n *\n * Default: no-op (driver had no probe to run).\n */\n async onProbe(): Promise<void> {\n /* override in subclass */\n }\n\n /**\n * Phase 5 — fired after the device + its accessories are registered.\n * Drivers publish streams to the broker, kick off background tasks,\n * or subscribe to lib events that need a fully-registered device id.\n *\n * Default: no-op.\n *\n * RENAMED FROM `onCreated` (which still exists for back-compat in this\n * pass). The new name reflects the post-probe, post-accessory contract.\n */\n async onActivate(): Promise<void> {\n /* override in subclass */\n }\n\n /**\n * Re-run the probe + reconcile accessories + refresh features meta.\n * Drivers call this when device-side state changes (battery cam wakes,\n * firmware update, manual operator trigger).\n *\n * The kernel injects `_kernelReprobe` on registration so this method\n * delegates to the same orchestrator that runs the boot-time phase\n * 3 + 4 sequence. Drivers should NOT override this — they override\n * `onProbe()` instead.\n */\n async reprobe(): Promise<void> {\n if (this._kernelReprobe) await this._kernelReprobe()\n else await this.onProbe()\n }\n\n /**\n * Kernel-injected callback that runs the full post-probe orchestration\n * (onProbe → registerDevice meta refresh → accessory reconciliation).\n * Set by `device-cap-proxy.register()`. Drivers should not touch this\n * directly — call `reprobe()` instead.\n */\n _kernelReprobe?: () => Promise<void>\n\n /**\n * Declare accessory child devices the kernel should auto-spawn\n * after `onProbe()` resolves. Each spec fully describes one child\n * — stableId suffix (deterministic per kind for restore-safety),\n * meta (type / name / location), config (initial blob the child\n * self-hydrates), and a factory that constructs the concrete\n * class with whatever closure-captured refs it needs (typically\n * `this` for the parent reference).\n *\n * The kernel handles the rest: allocateDeviceId, persistInitialConfig\n * (skipped on restore when the row already exists),\n * persistInitialMeta, createContext, factory invocation, register,\n * and recursive lifecycle (probe + accessories + activate).\n *\n * Implementations should derive children from\n * `this.runtimeState.getCapState('feature-probe')` (post-probe truth).\n * Drivers can use the `getProbeFlags()` helper to read the flag bag\n * with a typed cast.\n *\n * Default: no children.\n */\n getAccessoryChildren(): readonly AccessoryChildSpec[] {\n return []\n }\n\n /**\n * Read the current feature-probe flag bag with a typed cast. Helper\n * for `getAccessoryChildren()` and `features` getters that derive\n * outputs from the probe results.\n */\n protected getProbeFlags<F extends Record<string, unknown> = Record<string, unknown>>(): F {\n const slice = this.runtimeState.getCapState<FeatureProbeStatus>('feature-probe')\n return (slice?.flags ?? {}) as F\n }\n\n /**\n * Returns true once `onProbe` has completed at least once\n * (`lastProbedAt > 0`). Drivers gate `getAccessoryChildren()` on this\n * to avoid spawning stale accessories on a fresh device whose probe\n * hasn't landed yet.\n */\n protected hasProbed(): boolean {\n const slice = this.runtimeState.getCapState<FeatureProbeStatus>('feature-probe')\n return (slice?.lastProbedAt ?? 0) > 0\n }\n}\n\n/**\n * Specification for one accessory child a parent device wants the\n * kernel to auto-spawn. Returned from `BaseDevice.getAccessoryChildren()`.\n */\nexport interface AccessoryChildSpec {\n /**\n * Stable suffix appended to the parent's stableId to derive the\n * child's stableId (e.g. `siren` → `${parent.stableId}-siren`).\n * Must be deterministic per (parent, accessory kind) so restore\n * resolves the same numeric id and re-instantiates without\n * orphaning the persisted config.\n */\n readonly stableIdSuffix: string\n /** Operator-organisational meta passed to `kernel.devices.create()`'s\n * `initialMeta` arg. The kernel pre-writes the meta row before\n * the child class constructor runs. */\n readonly meta: import('./device-management.js').InitialDeviceMeta\n /** Initial config blob the child self-hydrates from on first\n * create. Empty `{}` on restore (DB row carries the canonical\n * values) — the kernel detects the existing row and skips the\n * pre-persist step. */\n readonly config: Record<string, unknown>\n /**\n * Constructor closure — invoked with the kernel-built context\n * after meta + config are persisted. Closures over the parent\n * reference for child classes that need `parent` as a constructor\n * arg (e.g. accessories that share the parent's connection). The\n * returned IDevice is registered + `onCreated`-fired by the\n * kernel.\n */\n readonly factory: (ctx: DeviceContext) => IDevice\n}\n","import type {\n ProviderRegistration,\n ConfigUISchema,\n SavedDevice,\n IDevice,\n} from '../index.js'\nimport { BaseAddon } from '../addon/base-addon.js'\nimport { deviceProviderCapability } from '../capabilities/device-provider.cap.js'\nimport { DeviceType } from './device-type.js'\nimport type { CreateDeviceSpec } from './device-management.js'\nimport type { DeviceConstructor } from './device-context.js'\n\n// ── Shared interfaces (previously duplicated in each provider) ──────\n\nexport interface DiscoveryCandidate {\n readonly stableId: string\n readonly type: DeviceType\n readonly suggestedName: string\n readonly prefilledConfig: Record<string, unknown>\n}\n\nexport interface DeviceSummary {\n readonly id: number\n readonly stableId: string\n readonly addonId: string\n readonly type: string\n readonly name: string\n readonly parentDeviceId: number | null\n readonly online: boolean\n readonly features: readonly string[]\n readonly config: Record<string, unknown>\n}\n\nexport interface ProviderStatus {\n readonly connected: boolean\n readonly deviceCount: number\n readonly error?: string\n}\n\nexport interface FieldProbeResult {\n readonly status: 'ok' | 'error'\n readonly labels?: readonly string[]\n readonly error?: string\n /**\n * Optional values the provider suggests applying to other form fields\n * after a successful probe. The form-builder merges these into the\n * working snapshot ONLY for fields the operator hasn't filled yet,\n * never overwriting user input. Typical use: a host-probe that\n * autodetects the camera model and proposes it as the device `name`.\n */\n readonly suggestedValues?: Readonly<Record<string, unknown>>\n}\n\n// ── Utility ─────────────────────────────────────────────────────────\n\n/**\n * Convert an IDevice to the flat DeviceSummary shape expected by the\n * device-provider cap router. Shared across all providers.\n */\nexport function toDeviceSummary(device: IDevice, addonId: string): DeviceSummary {\n const config: Record<string, unknown> = {}\n for (const entry of device.config.entries()) {\n config[entry.key] = entry.value\n }\n return {\n id: device.id,\n stableId: device.stableId,\n addonId,\n type: String(device.type),\n name: device.name,\n parentDeviceId: device.parentDeviceId,\n online: device.online,\n features: [...device.features],\n config,\n }\n}\n\n// ── Base class ──────────────────────────────────────────────────────\n\n/**\n * Base class for device-provider addons (rtsp, onvif, frigate).\n *\n * Provides default implementations for the common device-provider cap\n * methods (`start`, `stop`, `getStatus`, `getDevices`, `supportsDiscovery`,\n * `supportsManualCreation`, `toDeviceSummary`). Subclasses override the\n * methods that differ per provider.\n *\n * @example\n * ```ts\n * class RtspProvider extends BaseDeviceProvider {\n * protected readonly addonId = 'provider-rtsp'\n * protected readonly providerName = 'RTSP'\n *\n * protected async onCreateDevice(input) { ... }\n * protected async onGetCreationSchema(type) { ... }\n * protected async onRestoreDevices(saved) { ... }\n * }\n * ```\n */\nexport abstract class BaseDeviceProvider<TConfig extends object = Record<string, unknown>> extends BaseAddon<TConfig> {\n /** Addon ID used in DeviceSummary. Must match package.json id. */\n protected abstract readonly addonId: string\n\n /** Human-readable provider name for log messages. */\n protected abstract readonly providerName: string\n\n // ── Lifecycle (BaseAddon hooks) ─────────────────────────────────────\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n this.ctx.logger.info(`${this.providerName} Provider initialized`)\n return [{ capability: deviceProviderCapability, provider: this }]\n }\n\n protected async onShutdown(): Promise<void> {\n // Decommission every device this provider owns BEFORE returning.\n // Mirrors what Moleculer SIGTERM + in-process restart paths\n // expect: each device's `removeDevice()` lifecycle hook fires\n // (closes alarm streams, ISAPI clients, polling timers, intercom\n // sessions, …), native caps unregister cleanly, registry entries\n // drop. The persisted device-meta / config rows stay intact so\n // the next boot's `restoreDevices` rehydrates the same set with\n // freshly-instantiated IDevice classes — that's the whole point\n // of \"decommission, don't delete\".\n //\n // Subclasses that need extra teardown (Moleculer service handle,\n // background workers) should override and call `super.onShutdown()`\n // first so devices are torn down before vendor-specific resources.\n const devices = await this.ctx.kernel.devices?.getAll() ?? []\n for (const device of devices) {\n try {\n await this.ctx.kernel.devices?.decommission(device.id)\n } catch (err) {\n this.ctx.logger.warn(`${this.providerName}: decommission failed`, {\n tags: { deviceId: device.id, stableId: device.stableId },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n this.ctx.logger.info(`${this.providerName} Provider shut down`, {\n meta: { decommissionedCount: devices.length },\n })\n }\n\n // ── device-provider cap — lifecycle + introspection ─────────────────\n // Default implementations. Override in subclass if needed.\n\n async start(): Promise<void> {\n /* no-op — providers are passive by default */\n }\n\n async stop(): Promise<void> {\n /* no-op */\n }\n\n async getStatus(): Promise<ProviderStatus> {\n const all = (await this.ctx.kernel.devices?.getAll()) ?? []\n return { connected: true, deviceCount: all.length }\n }\n\n async getDevices(): Promise<ReadonlyArray<{ id: string; name: string; type: string }>> {\n const all = (await this.ctx.kernel.devices?.getAll()) ?? []\n return all.map((d) => ({ id: d.stableId, name: d.name, type: String(d.type) }))\n }\n\n // ── device-provider cap — discovery ─────────────────────────────────\n // Default: no discovery. Override in providers that support it (ONVIF).\n\n async supportsDiscovery(): Promise<boolean> {\n return false\n }\n\n async discoverDevices(): Promise<readonly DiscoveryCandidate[]> {\n return []\n }\n\n async adoptDiscoveredDevice(_input: {\n candidate: DiscoveryCandidate\n }): Promise<DeviceSummary> {\n throw new Error(`${this.providerName} provider does not support discovery-based adoption`)\n }\n\n // ── device-provider cap — manual creation ───────────────────────────\n // Default: supports manual creation. Subclass must implement\n // onGetCreationSchema and onCreateDevice.\n\n async supportsManualCreation(): Promise<boolean> {\n return true\n }\n\n async getChildCreationSchema(input: { type: DeviceType }): Promise<ConfigUISchema | null> {\n return this.onGetCreationSchema(input.type)\n }\n\n /**\n * Default kernel-orchestrated `createDevice` implementation. The\n * subclass's `onCreateDevice` returns a declarative\n * `CreateDeviceSpec` (`{meta, config}`) — this method handles\n * stableId generation, class lookup, kernel.devices.create\n * dispatch, and DeviceSummary mapping. Subclasses should NOT\n * override this method; override `onCreateDevice` and\n * `deviceClasses` instead.\n */\n async createDevice(input: {\n type: DeviceType\n config: Record<string, unknown>\n }): Promise<DeviceSummary> {\n const spec = await this.onCreateDevice(input.type, input.config)\n const Class = this.deviceClasses[spec.meta.type] as DeviceConstructor<IDevice> | undefined\n if (!Class) {\n throw new Error(\n `${this.providerName} provider: no device class registered for type \"${spec.meta.type}\" — add it to the deviceClasses map`,\n )\n }\n const stableId = this.generateStableId(spec.meta.type, spec.config)\n const device = await this.ctx.kernel.devices!.create(\n stableId,\n Class,\n spec.config,\n null,\n spec.meta,\n )\n if (spec.onAfterCreate) {\n try {\n await spec.onAfterCreate(device)\n } catch (err) {\n this.ctx.logger.warn('createDevice: onAfterCreate hook threw — device is already registered', {\n tags: { deviceId: device.id, stableId },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n return this.toSummary(device)\n }\n\n /**\n * Generate a stableId for a newly-created device. Default uses the\n * `${addonId}-${Date.now()}` pattern as a unique-but-opaque\n * fallback; any provider that has access to durable hardware\n * identity (UID, MAC, serial) should override and derive from it\n * so re-adding the same physical device reuses its persisted row.\n *\n * `config` is the parsed CreateDeviceSpec.config the subclass\n * returned from `onCreateDevice` — the override has access to\n * every operator-supplied + autodetect-resolved field. Optional\n * for back-compat: existing overrides that take only `type`\n * keep working unchanged.\n */\n protected generateStableId(_type: DeviceType, _config?: Record<string, unknown>): string {\n return `${this.addonId}-${Date.now()}`\n }\n\n async testCreationField(_input: {\n type: DeviceType\n key: string\n value: unknown\n formValues?: Record<string, unknown>\n }): Promise<FieldProbeResult> {\n return { status: 'ok', labels: ['probe not implemented'] }\n }\n\n // ── Boot restore ────────────────────────────────────────────────────\n\n async restoreDevices(savedDevices: readonly SavedDevice[]): Promise<void> {\n await this.onRestoreDevices(savedDevices)\n if (savedDevices.length > 0) {\n this.ctx.logger.info(`Restored ${savedDevices.length} ${this.providerName} device(s)`)\n }\n }\n\n // ── Subclass hooks ──────────────────────────────────────────────────\n\n /**\n * Concrete device classes this provider can spawn, keyed by\n * `DeviceType`. Used by:\n * - `createDevice` to look up the class for the type the operator\n * selected in the Add-Device modal\n * - the default `onRestoreDevices` to instantiate persisted rows\n * based on their `type` field\n *\n * Required for the declarative create + restore flow. Providers\n * that need custom-arg constructors (accessory child devices) wire\n * those up via `BaseDevice.getAccessoryChildren()` instead — the\n * top-level type → class map only handles parent devices.\n */\n protected abstract readonly deviceClasses: Partial<Record<DeviceType, DeviceConstructor<IDevice>>>\n\n /** Return the creation form schema for a given device type, or null if unsupported. */\n protected abstract onGetCreationSchema(type: DeviceType): Promise<ConfigUISchema | null>\n\n /**\n * Build the create spec from operator-supplied form values. Returns\n * `{meta, config}` — kernel handles allocateId, persist, instantiate,\n * register, and accessory auto-spawn. Provider's only job is to:\n * - validate / probe the input\n * - emit the meta (`type`, `name`, optional `location`)\n * - emit the `config` blob the device class will self-hydrate\n */\n protected abstract onCreateDevice(type: DeviceType, config: Record<string, unknown>): Promise<CreateDeviceSpec>\n\n /**\n * Restore devices from persisted state. Two-pass:\n *\n * 1. **Top-level pass** — invokes `kernel.devices.create()` for every\n * `parentDeviceId === null` row using the `deviceClasses` map.\n * The kernel's register flow handles `getAccessoryChildren()` for\n * each parent (siren / floodlight / PIR / etc).\n *\n * 2. **Hub-adopted children pass** — for rows with\n * `parentDeviceId !== null` whose `type` IS in `deviceClasses`\n * (e.g. Reolink hub-adopted cameras under an NVR), spawn them\n * explicitly with the persisted `parentDeviceId`. These are\n * NOT accessory children — they're first-class adopted devices\n * that just happen to have a parent. Without this pass, every\n * server restart would lose hub-adopted cameras (their type is\n * in `deviceClasses` but parent's `getAccessoryChildren` doesn't\n * spawn them — that callback is only for purpose-built\n * accessory roles).\n *\n * Rows whose `type` is NOT in `deviceClasses` are skipped — those\n * are accessory children (siren/light/sensor) that the kernel's\n * accessory-spawn flow handles via the parent's\n * `getAccessoryChildren()`. Override only when the default doesn't\n * fit.\n */\n protected async onRestoreDevices(savedDevices: readonly SavedDevice[]): Promise<void> {\n // Pass 1 — top-level\n const restored = new Set<number>()\n for (const saved of savedDevices) {\n if (saved.parentDeviceId !== null) continue\n const Class = this.deviceClasses[saved.type] as DeviceConstructor<IDevice> | undefined\n if (!Class) {\n this.ctx.logger.warn('No device class registered for restored type — skipping', {\n tags: { stableId: saved.stableId },\n meta: { type: saved.type },\n })\n continue\n }\n try {\n await this.ctx.kernel.devices!.create(saved.stableId, Class, {})\n restored.add(saved.id)\n } catch (err) {\n this.ctx.logger.warn('Failed to restore device', {\n tags: { stableId: saved.stableId },\n meta: { type: saved.type, error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n // Pass 2 — hub-adopted children (first-class devices with a parent\n // that is itself a registered device class). Iterates in order of\n // declared `parentDeviceId` to handle nested hubs gracefully.\n const childRows = savedDevices.filter(s => s.parentDeviceId !== null)\n for (const saved of childRows) {\n const Class = this.deviceClasses[saved.type] as DeviceConstructor<IDevice> | undefined\n if (!Class) continue // not a first-class type → accessory, skip\n if (saved.parentDeviceId === null) continue\n if (!restored.has(saved.parentDeviceId)) {\n // Parent isn't a known top-level device this provider owns —\n // probably a stale row pointing at a removed parent. Leave it\n // alone; orphan reconciliation runs later.\n continue\n }\n try {\n await this.ctx.kernel.devices!.create(\n saved.stableId,\n Class,\n {},\n saved.parentDeviceId,\n )\n restored.add(saved.id)\n } catch (err) {\n this.ctx.logger.warn('Failed to restore hub-adopted child', {\n tags: { stableId: saved.stableId, parentDeviceId: saved.parentDeviceId },\n meta: { type: saved.type, error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n }\n\n // ── Utility ─────────────────────────────────────────────────────────\n\n /** Convert an IDevice to the flat DeviceSummary for the cap router. */\n protected toSummary(device: IDevice): DeviceSummary {\n return toDeviceSummary(device, this.addonId)\n }\n}\n","import { z } from 'zod'\nimport type {\n ConfigUISchema,\n ConfigField,\n ConfigTextField,\n ConfigNumberField,\n ConfigBooleanField,\n ConfigSelectField,\n ConfigPasswordField,\n ConfigTextAreaField,\n} from '../interfaces/config-ui.js'\n\n// --- Entry type matching DeviceConfig.entries() output ---\n\nexport interface DeviceConfigEntry {\n key: string\n schema: z.ZodType\n value: unknown\n description?: string\n}\n\n// --- Internal Zod v4 _def shapes (accessed at runtime, not exposed in typings) ---\n\ninterface ZodV4CheckDef {\n check: string\n value?: number\n inclusive?: boolean\n}\n\ninterface ZodV4Check {\n _zod: { def: ZodV4CheckDef }\n}\n\ninterface ZodV4NumberDef {\n type: string\n checks?: ZodV4Check[]\n}\n\ninterface ZodV4DefaultDef {\n type: string\n defaultValue: unknown\n innerType: z.ZodType\n}\n\n/** Access Zod v4 internal .def — not in public typings but stable at runtime */\nfunction zodDef<T>(schema: z.ZodType): T {\n return (schema as unknown as { def: T }).def\n}\n\n/** Access internal properties on a Zod schema instance */\nfunction zodInternals<T>(schema: z.ZodType): T {\n return schema as unknown as T\n}\n\n// --- Public API ---\n\n/**\n * Convert DeviceConfig.entries() output to ConfigUISchema for the admin UI FormBuilder.\n *\n * Each entry's Zod type is inspected to determine the correct ConfigField type:\n * - ZodString → 'text' (or 'password' when key contains \"password\"/\"secret\"/\"token\"/\"apikey\")\n * - ZodNumber → 'number' (extracts min/max/step from Zod v4 checks)\n * - ZodBoolean → 'boolean'\n * - ZodEnum → 'select' (options built from enum values)\n * - Anything else → 'text' fallback\n *\n * Wrapper types ZodDefault, ZodOptional, and ZodNullable are unwrapped transparently.\n * Default values are extracted from ZodDefault wrappers.\n */\nexport function zodEntriesToConfigUI(\n entries: readonly DeviceConfigEntry[],\n sectionTitle = 'Configuration',\n sectionId = 'main',\n): ConfigUISchema {\n const fields: ConfigField[] = entries.map(entry =>\n zodToConfigField(entry.key, entry.schema, entry.description),\n )\n\n return {\n sections: [\n {\n id: sectionId,\n title: sectionTitle,\n fields,\n },\n ],\n }\n}\n\n// --- Internal helpers ---\n\nfunction zodToConfigField(key: string, schema: z.ZodType, description?: string): ConfigField {\n const inner = unwrapZod(schema)\n const defaultValue = getZodDefault(schema)\n const label = description ?? humanizeKey(key)\n\n const base = {\n key,\n label,\n description,\n default: defaultValue,\n } as const\n\n if (inner instanceof z.ZodString) {\n return buildStringField(key, base)\n }\n\n if (inner instanceof z.ZodNumber) {\n return buildNumberField(inner, base)\n }\n\n if (inner instanceof z.ZodBoolean) {\n const field: ConfigBooleanField = { ...base, type: 'boolean' }\n return field\n }\n\n if (inner instanceof z.ZodEnum) {\n return buildEnumField(inner, base)\n }\n\n // Arrays + objects don't fit any primitive field — render as a JSON\n // textarea so the operator at least sees the shape and can copy/edit\n // it, instead of `[object Object],…` produced by the text fallback.\n // `isJson: true` tells the UI + hydrator to round-trip via JSON.\n if (inner instanceof z.ZodArray || inner instanceof z.ZodObject) {\n const field: ConfigTextAreaField = { ...base, type: 'textarea', rows: 6, isJson: true }\n return field\n }\n\n // Fallback — render as plain text\n const fallback: ConfigTextField = { ...base, type: 'text' }\n return fallback\n}\n\nfunction buildStringField(\n key: string,\n base: { key: string; label: string; description?: string; default?: unknown },\n): ConfigTextField | ConfigPasswordField {\n const lowerKey = key.toLowerCase()\n const isSecret =\n lowerKey.includes('password') ||\n lowerKey.includes('secret') ||\n lowerKey.includes('token') ||\n lowerKey.includes('apikey') ||\n lowerKey.includes('api_key')\n\n if (isSecret) {\n const field: ConfigPasswordField = { ...base, type: 'password', showToggle: true }\n return field\n }\n\n const field: ConfigTextField = { ...base, type: 'text' }\n return field\n}\n\nfunction buildNumberField(\n inner: z.ZodNumber,\n base: { key: string; label: string; description?: string; default?: unknown },\n): ConfigNumberField {\n // Zod v4 exposes min/max via public getters minValue/maxValue.\n // When unconstrained, these return -Infinity / Infinity — treat those as absent.\n const anyInner = zodInternals<{ minValue?: number | null; maxValue?: number | null }>(inner)\n const rawMin = anyInner.minValue\n const rawMax = anyInner.maxValue\n const min =\n rawMin != null && isFinite(rawMin) ? rawMin : undefined\n const max =\n rawMax != null && isFinite(rawMax) ? rawMax : undefined\n\n // Step is stored in checks with check name 'multiple_of'\n const step = getMultipleOfStep(inner)\n\n const field: ConfigNumberField = {\n ...base,\n type: 'number',\n ...(min !== undefined ? { min } : {}),\n ...(max !== undefined ? { max } : {}),\n ...(step !== undefined ? { step } : {}),\n }\n return field\n}\n\nfunction getMultipleOfStep(inner: z.ZodNumber): number | undefined {\n const def = zodDef<ZodV4NumberDef>(inner)\n const checks = def.checks ?? []\n for (const check of checks) {\n if (check._zod?.def?.check === 'multiple_of' && check._zod.def.value !== undefined) {\n return check._zod.def.value\n }\n }\n return undefined\n}\n\n// Zod v4: ZodEnum<T> where T extends Readonly<Record<string, string | number>>\n// options returns Array<T[keyof T]> which is (string | number)[]\nfunction buildEnumField(\n inner: z.ZodEnum,\n base: { key: string; label: string; description?: string; default?: unknown },\n): ConfigSelectField {\n const values = (inner.options as (string | number)[]).map(v => String(v))\n\n const field: ConfigSelectField = {\n ...base,\n type: 'select',\n options: values.map(v => ({ label: humanizeKey(v), value: v })),\n }\n return field\n}\n\nfunction unwrapZod(schema: z.ZodType): z.ZodType {\n if (schema instanceof z.ZodDefault) return unwrapZod(zodDef<ZodV4DefaultDef>(schema).innerType)\n if (schema instanceof z.ZodOptional) return unwrapZod(zodDef<{ innerType: z.ZodType }>(schema).innerType)\n if (schema instanceof z.ZodNullable) return unwrapZod(zodDef<{ innerType: z.ZodType }>(schema).innerType)\n return schema\n}\n\nfunction getZodDefault(schema: z.ZodType): unknown {\n if (schema instanceof z.ZodDefault) {\n // Zod v4: def.defaultValue is the literal value (not a function at runtime)\n return zodDef<ZodV4DefaultDef>(schema).defaultValue\n }\n return undefined\n}\n\nfunction humanizeKey(key: string): string {\n return key\n .replace(/([A-Z])/g, ' $1')\n .replace(/[_-]/g, ' ')\n .replace(/^\\w/, c => c.toUpperCase())\n .trim()\n}\n","/**\n * Reactive read handle for one cap-keyed slice of a device's\n * runtime state. Returned by `createDeviceProxy(...).state[capName]`.\n *\n * Three pieces:\n * - `value`: last-known slice. `undefined` until something has\n * populated it (an active subscription, an explicit `refresh()`,\n * or — in the case of the SystemManager mirror source — a\n * warm-boot `getAllSnapshots` payload). Read sync.\n * - `refresh()`: one-shot pull from the underlying source. For\n * the lazy tRPC source this is `deviceState.getCapSlice`; for\n * the SystemManager mirror it's a no-op (the mirror is push-only).\n * - `subscribe(cb)`: hooks into the source's notification channel,\n * filtered by `(deviceId, capName)`. Refcounted — the underlying\n * subscription closes when the last caller unsubscribes.\n *\n * Cross-environment: the same API works in browser (admin-ui via the\n * tRPC client) and on the server (in-process AddonApi). The transport\n * is whatever the supplied source exposes.\n */\nexport interface SliceHandle<T> {\n /** Latest cached slice. Reflects the most recent `refresh()` or\n * push event. `undefined` if neither has happened. */\n readonly value: T | undefined\n /** Force a re-fetch from the hub mirror. Updates `value` + notifies\n * every active subscriber. No-op for the SystemManager mirror\n * source (it's already push-driven). */\n refresh(): Promise<void>\n /** Subscribe to slice changes. Returns the unsubscribe fn.\n * Auto-fires the callback once with the current value (or\n * `undefined`) so the caller sees a snapshot immediately. */\n subscribe(cb: (slice: T | undefined) => void): () => void\n /**\n * Replace the slice on the hub. Routes through\n * `device-state.setCapSlice` — the canonical cross-layer write\n * entrypoint. The local mirror updates via the `onChanged` event\n * (round-trip), so callers should treat the write as eventually\n * consistent and read the new value via `subscribe` rather than\n * synchronously after `await`.\n */\n set(slice: T): Promise<void>\n /**\n * Shallow-merge `partial` into the current slice on the hub.\n * Implemented as read-modify-write client-side: pulls `value`\n * (or refreshes), merges, calls `set()`. Single-writer\n * conventions on each cap make atomic-merge semantics\n * unnecessary — concurrent patches on the same slice from\n * different processes are undefined and should be avoided.\n */\n patch(partial: Partial<T>): Promise<void>\n}\n\n/**\n * Minimal tRPC API surface used by the lazy source. Both `AddonApi`\n * (server) and the tRPC client proxy (browser) satisfy it structurally\n * — we don't import either to avoid pulling them in from a leaf type\n * module.\n */\nexport interface SliceHandleApi {\n readonly deviceState: {\n readonly getCapSlice: {\n query(input: { deviceId: number; capName: string }): Promise<Record<string, unknown> | null>\n }\n readonly setCapSlice: {\n mutate(input: { deviceId: number; capName: string; slice: Record<string, unknown> }): Promise<void>\n }\n }\n readonly live?: {\n readonly onEvent: {\n subscribe(\n input: { category: string },\n opts: { onData: (evt: { data: unknown }) => void; onError?: (err: unknown) => void },\n ): { unsubscribe: () => void }\n }\n }\n}\n\n/**\n * Pluggable state-source contract. `createSliceHandle` is now a thin\n * adapter over this interface — every implementation produces an\n * identical `SliceHandle<T>` shape. Two impls today:\n *\n * - `createLazyTrpcSource(api)`: per-handle local cache, refresh\n * via `deviceState.getCapSlice`, subscribe via `live.onEvent`.\n * Default behavior, used by `createDeviceProxy(api, binding)`\n * when no source is passed.\n * - `createMirrorSource(mirror, listeners)`: reads from a shared\n * `Map<deviceId, Map<capName, slice>>` populated by a\n * `SystemManager` warm-boot. Refresh is a no-op; subscribe\n * registers in a shared listener set that the SystemManager\n * fans out to on every push event.\n *\n * Both produce the same `SliceHandle<T>` API; consumers don't see\n * which source is behind the handle.\n */\nexport interface SliceHandleSource {\n /** Sync read of the last-known slice for `(deviceId, capName)`. */\n read(deviceId: number, capName: string): unknown | undefined\n /** Force a re-fetch (where applicable). No-op when the source is\n * push-only (e.g. the SystemManager mirror). */\n refresh(deviceId: number, capName: string): Promise<void>\n /** Register a listener for slice changes. Returns the unsubscribe\n * fn. Implementations should fan out the latest cached slice to\n * the callback synchronously when seeding is desired (the handle\n * always seeds via a separate `cb(read(...))` call). */\n watch(\n deviceId: number,\n capName: string,\n cb: (slice: unknown | undefined) => void,\n ): () => void\n /** Write the slice for `(deviceId, capName)` to the hub via the\n * canonical `device-state.setCapSlice` entrypoint. Throws if the\n * source has no write transport (e.g. a mirror source built\n * without an api reference). */\n write(deviceId: number, capName: string, slice: Record<string, unknown>): Promise<void>\n}\n\nconst DEVICE_STATE_EVENT_CATEGORY = 'device.state-changed'\n\n/**\n * Lazy tRPC source — the default behavior. Maintains a per-key local\n * cache (`{deviceId}:{capName}` → last slice), one shared\n * `live.onEvent` bridge that fans out into the listener map, and\n * `refresh` round-trips through `deviceState.getCapSlice`.\n *\n * The bridge is opened on first `watch()` and closed when the last\n * watcher unsubscribes — keeps idle handles cheap.\n */\nexport function createLazyTrpcSource(api: SliceHandleApi): SliceHandleSource {\n const cache = new Map<string, unknown>()\n const listeners = new Map<string, Set<(slice: unknown | undefined) => void>>()\n let bridge: { unsubscribe: () => void } | null = null\n\n const keyOf = (deviceId: number, capName: string): string => `${deviceId}:${capName}`\n\n const ensureBridge = (): void => {\n if (bridge) return\n if (!api.live?.onEvent) return\n bridge = api.live.onEvent.subscribe(\n { category: DEVICE_STATE_EVENT_CATEGORY },\n {\n onData: (evt) => {\n const data = evt.data as { deviceId?: number; capName?: string; slice?: unknown } | null\n if (!data || typeof data.deviceId !== 'number' || typeof data.capName !== 'string') return\n const k = keyOf(data.deviceId, data.capName)\n cache.set(k, data.slice)\n const set = listeners.get(k)\n if (!set) return\n for (const cb of set) {\n try { cb(data.slice) } catch { /* listener errors don't break the bridge */ }\n }\n },\n },\n )\n }\n\n const closeBridgeIfIdle = (): void => {\n if (!bridge) return\n if (listeners.size > 0) return\n bridge.unsubscribe()\n bridge = null\n }\n\n return {\n read(deviceId, capName) {\n return cache.get(keyOf(deviceId, capName))\n },\n async refresh(deviceId, capName) {\n const slice = await api.deviceState.getCapSlice.query({ deviceId, capName })\n const k = keyOf(deviceId, capName)\n cache.set(k, slice ?? undefined)\n const set = listeners.get(k)\n if (set) {\n for (const cb of set) {\n try { cb(slice ?? undefined) } catch { /* listener errors don't break refresh */ }\n }\n }\n },\n watch(deviceId, capName, cb) {\n const k = keyOf(deviceId, capName)\n let set = listeners.get(k)\n if (!set) { set = new Set(); listeners.set(k, set) }\n set.add(cb)\n ensureBridge()\n return () => {\n set!.delete(cb)\n if (set!.size === 0) listeners.delete(k)\n closeBridgeIfIdle()\n }\n },\n async write(deviceId, capName, slice) {\n await api.deviceState.setCapSlice.mutate({ deviceId, capName, slice })\n },\n }\n}\n\n/**\n * Mirror source — reads from a shared map populated by a\n * `SystemManager` warm-boot + push event handler. Refresh is a no-op\n * (the SystemManager owns the update loop). `watch()` registers in a\n * shared listener set — the SystemManager calls these from its single\n * `device.state-changed` subscription.\n *\n * The mirror map and listener map are passed in by reference so the\n * SystemManager can mutate both as events arrive.\n */\nexport function createMirrorSource(\n mirror: ReadonlyMap<number, ReadonlyMap<string, unknown>>,\n listeners: Map<string, Set<(slice: unknown | undefined) => void>>,\n api?: SliceHandleApi,\n): SliceHandleSource {\n const keyOf = (deviceId: number, capName: string): string => `${deviceId}:${capName}`\n return {\n read(deviceId, capName) {\n return mirror.get(deviceId)?.get(capName)\n },\n async refresh() {\n // No-op: the mirror is fed by `device.state-changed` events at\n // the SystemManager level. Forcing a re-fetch from a single\n // handle would race with the global update loop and is never\n // what the caller wants.\n },\n watch(deviceId, capName, cb) {\n const k = keyOf(deviceId, capName)\n let set = listeners.get(k)\n if (!set) { set = new Set(); listeners.set(k, set) }\n set.add(cb)\n return () => {\n set!.delete(cb)\n if (set!.size === 0) listeners.delete(k)\n }\n },\n async write(deviceId, capName, slice) {\n if (!api) {\n throw new Error('createMirrorSource: write requires an api reference — pass it as the third argument when constructing the source')\n }\n await api.deviceState.setCapSlice.mutate({ deviceId, capName, slice })\n },\n }\n}\n\n/**\n * Build a `SliceHandle<T>` bound to `(deviceId, capName)`. Caller\n * provides the type parameter — codegen passes\n * `InferRuntimeState<typeof <cap>>` so consumers see the cap's typed\n * shape:\n *\n * const dev = createDeviceProxy(api, binding)\n * dev.state.battery.value // BatteryStatus | undefined\n * dev.state.battery.value?.percentage\n *\n * Two-arg form (deprecated path, kept for spec compatibility):\n * passes the api directly, builds a per-handle lazy source. New\n * code should pre-build a single source and reuse it across handles\n * via the explicit `source` form.\n */\nexport function createSliceHandle<T>(\n source: SliceHandleSource | SliceHandleApi,\n deviceId: number,\n capName: string,\n): SliceHandle<T> {\n // Discriminate between source and api by structure: a source has\n // a `read` method, an api has a `deviceState` namespace. Spec-style\n // tests still pass `api`; production codegen passes a source.\n const src: SliceHandleSource = isSource(source)\n ? source\n : createLazyTrpcSource(source)\n\n return {\n get value() { return src.read(deviceId, capName) as T | undefined },\n refresh() { return src.refresh(deviceId, capName) },\n subscribe(cb) {\n const unwatch = src.watch(deviceId, capName, (slice) => {\n try { cb(slice as T | undefined) } catch { /* listener errors are isolated */ }\n })\n // Seed the listener with the current cached read — sync,\n // deterministic, no transport. Mirror sources serve the\n // canonical value here; lazy sources serve their per-key\n // cache (undefined on cold start).\n try { cb(src.read(deviceId, capName) as T | undefined) } catch { /* ignore */ }\n // Cold-start kick: when the read returned nothing AND the\n // source has a meaningful refresh path (lazy tRPC), pull once\n // in the background. Fan-out via `watch` delivers the result\n // to the same listener — no double-seeding. The mirror\n // source's refresh is a no-op, so this is a cheap miss for\n // SystemManager-backed handles.\n if (src.read(deviceId, capName) === undefined) {\n void src.refresh(deviceId, capName).catch(() => undefined)\n }\n return unwatch\n },\n async set(slice) {\n await src.write(deviceId, capName, slice as Record<string, unknown>)\n },\n async patch(partial) {\n const current = src.read(deviceId, capName) as Record<string, unknown> | undefined\n const next = { ...(current ?? {}), ...partial as Record<string, unknown> }\n await src.write(deviceId, capName, next)\n },\n }\n}\n\nfunction isSource(x: SliceHandleSource | SliceHandleApi): x is SliceHandleSource {\n return typeof (x as SliceHandleSource).read === 'function'\n && typeof (x as SliceHandleSource).watch === 'function'\n}\n","// AUTO-GENERATED — do not edit manually.\n// Regenerate with: npx tsx scripts/generate-device-proxy.ts\n/* eslint-disable */\n\nimport type { InferDeviceProxyCap, InferRuntimeState } from '../capabilities/capability-definition.js'\nimport type { DeviceBinding } from '../device/device-binding.js'\nimport { createSliceHandle, createLazyTrpcSource, type SliceHandle, type SliceHandleApi, type SliceHandleSource } from '../device/device-state-handle.js'\nimport type { AddonApi } from './addon-api.js'\nimport type { accessoriesCapability } from '../capabilities/accessories.cap.js'\nimport type { audioAnalysisCapability } from '../capabilities/audio-analysis.cap.js'\nimport type { audioMetricsCapability } from '../capabilities/audio-metrics.cap.js'\nimport type { batteryCapability } from '../capabilities/battery.cap.js'\nimport type { brightnessCapability } from '../capabilities/brightness.cap.js'\nimport type { cameraCredentialsCapability } from '../capabilities/camera-credentials.cap.js'\nimport type { cameraStreamsCapability } from '../capabilities/camera-streams.cap.js'\nimport type { detectionPipelineCapability } from '../capabilities/detection-pipeline.cap.js'\nimport type { deviceDiscoveryCapability } from '../capabilities/device-discovery.cap.js'\nimport type { deviceOpsCapability } from '../capabilities/device-ops.cap.js'\nimport type { deviceStatusCapability } from '../capabilities/device-status.cap.js'\nimport type { doorbellCapability } from '../capabilities/doorbell.cap.js'\nimport type { eventsCapability } from '../capabilities/events.cap.js'\nimport type { featureProbeCapability } from '../capabilities/feature-probe.cap.js'\nimport type { intercomCapability } from '../capabilities/intercom.cap.js'\nimport type { motionCapability } from '../capabilities/motion.cap.js'\nimport type { motionDetectionCapability } from '../capabilities/motion-detection.cap.js'\nimport type { motionTriggerCapability } from '../capabilities/motion-trigger.cap.js'\nimport type { nativeObjectDetectionCapability } from '../capabilities/native-object-detection.cap.js'\nimport type { osdCapability } from '../capabilities/osd.cap.js'\nimport type { pipelineAnalyticsCapability } from '../capabilities/pipeline-analytics.cap.js'\nimport type { ptzCapability } from '../capabilities/ptz.cap.js'\nimport type { ptzAutotrackCapability } from '../capabilities/ptz-autotrack.cap.js'\nimport type { rebootCapability } from '../capabilities/reboot.cap.js'\nimport type { recordingCapability } from '../capabilities/recording.cap.js'\nimport type { snapshotCapability } from '../capabilities/snapshot.cap.js'\nimport type { switchCapability } from '../capabilities/switch.cap.js'\nimport type { webrtcSessionCapability } from '../capabilities/webrtc-session.cap.js'\nimport type { zoneAnalyticsCapability } from '../capabilities/zone-analytics.cap.js'\nimport type { zoneRulesCapability } from '../capabilities/zone-rules.cap.js'\nimport type { zonesCapability } from '../capabilities/zones.cap.js'\nimport type { addonSettingsCapability } from '../capabilities/addon-settings.cap.js'\nimport type { deviceManagerCapability } from '../capabilities/device-manager.cap.js'\nimport type { deviceStateCapability } from '../capabilities/device-state.cap.js'\nimport type { networkQualityCapability } from '../capabilities/network-quality.cap.js'\nimport type { pipelineExecutorCapability } from '../capabilities/pipeline-executor.cap.js'\nimport type { pipelineOrchestratorCapability } from '../capabilities/pipeline-orchestrator.cap.js'\nimport type { pipelineRunnerCapability } from '../capabilities/pipeline-runner.cap.js'\nimport type { recordingEngineCapability } from '../capabilities/recording-engine.cap.js'\nimport type { snapshotProviderCapability } from '../capabilities/snapshot-provider.cap.js'\nimport type { streamBrokerCapability } from '../capabilities/stream-broker.cap.js'\n\n/**\n * Reactive read handle bag for device runtime state. Each entry mirrors the\n * cap's `runtimeState` schema and stays live via the `device-state` cap's\n * `onChanged` event. See `device-state-handle.ts` for the contract.\n */\nexport interface DeviceProxyState {\n readonly audioMetrics: SliceHandle<InferRuntimeState<typeof audioMetricsCapability>>\n readonly battery: SliceHandle<InferRuntimeState<typeof batteryCapability>>\n readonly brightness: SliceHandle<InferRuntimeState<typeof brightnessCapability>>\n readonly cameraStreams: SliceHandle<InferRuntimeState<typeof cameraStreamsCapability>>\n readonly deviceDiscovery: SliceHandle<InferRuntimeState<typeof deviceDiscoveryCapability>>\n readonly deviceStatus: SliceHandle<InferRuntimeState<typeof deviceStatusCapability>>\n readonly doorbell: SliceHandle<InferRuntimeState<typeof doorbellCapability>>\n readonly featureProbe: SliceHandle<InferRuntimeState<typeof featureProbeCapability>>\n readonly motion: SliceHandle<InferRuntimeState<typeof motionCapability>>\n readonly motionTrigger: SliceHandle<InferRuntimeState<typeof motionTriggerCapability>>\n readonly ptzAutotrack: SliceHandle<InferRuntimeState<typeof ptzAutotrackCapability>>\n readonly switch: SliceHandle<InferRuntimeState<typeof switchCapability>>\n readonly zoneAnalytics: SliceHandle<InferRuntimeState<typeof zoneAnalyticsCapability>>\n readonly zoneRules: SliceHandle<InferRuntimeState<typeof zoneRulesCapability>>\n readonly zones: SliceHandle<InferRuntimeState<typeof zonesCapability>>\n}\n\n/**\n * Unified per-device proxy interface. Each optional property is present at\n * runtime only when the device's binding includes that capability.\n *\n * The optional `binding` field is populated post-construction by callers\n * that own a binding cache (e.g. the SDK's `System` mirror). Consumers\n * that need the full per-device binding map can read it from there\n * without paying for a second `getBindings` round-trip — see Phase 5\n * dedup notes in `scripts/generate-device-proxy.ts`.\n */\nexport interface DeviceProxy {\n readonly deviceId: number\n /** Reactive runtime state, one slot per cap that declares `runtimeState`. */\n readonly state: DeviceProxyState\n /** Resolved binding entry list (or null if the proxy was constructed\n * without a cache-aware caller — defaults to null since\n * `createDeviceProxy` itself does not own the binding). */\n readonly binding: DeviceBinding | null\n readonly accessories?: InferDeviceProxyCap<typeof accessoriesCapability>\n readonly audioAnalysis?: InferDeviceProxyCap<typeof audioAnalysisCapability>\n readonly audioMetrics?: InferDeviceProxyCap<typeof audioMetricsCapability>\n readonly battery?: InferDeviceProxyCap<typeof batteryCapability>\n readonly brightness?: InferDeviceProxyCap<typeof brightnessCapability>\n readonly cameraCredentials?: InferDeviceProxyCap<typeof cameraCredentialsCapability>\n readonly cameraStreams?: InferDeviceProxyCap<typeof cameraStreamsCapability>\n readonly detectionPipeline?: InferDeviceProxyCap<typeof detectionPipelineCapability>\n readonly deviceDiscovery?: InferDeviceProxyCap<typeof deviceDiscoveryCapability>\n readonly deviceOps?: InferDeviceProxyCap<typeof deviceOpsCapability>\n readonly deviceStatus?: InferDeviceProxyCap<typeof deviceStatusCapability>\n readonly doorbell?: InferDeviceProxyCap<typeof doorbellCapability>\n readonly events?: InferDeviceProxyCap<typeof eventsCapability>\n readonly featureProbe?: InferDeviceProxyCap<typeof featureProbeCapability>\n readonly intercom?: InferDeviceProxyCap<typeof intercomCapability>\n readonly motion?: InferDeviceProxyCap<typeof motionCapability>\n readonly motionDetection?: InferDeviceProxyCap<typeof motionDetectionCapability>\n readonly motionTrigger?: InferDeviceProxyCap<typeof motionTriggerCapability>\n readonly nativeObjectDetection?: InferDeviceProxyCap<typeof nativeObjectDetectionCapability>\n readonly osd?: InferDeviceProxyCap<typeof osdCapability>\n readonly pipelineAnalytics?: InferDeviceProxyCap<typeof pipelineAnalyticsCapability>\n readonly ptz?: InferDeviceProxyCap<typeof ptzCapability>\n readonly ptzAutotrack?: InferDeviceProxyCap<typeof ptzAutotrackCapability>\n readonly reboot?: InferDeviceProxyCap<typeof rebootCapability>\n readonly recording?: InferDeviceProxyCap<typeof recordingCapability>\n readonly snapshot?: InferDeviceProxyCap<typeof snapshotCapability>\n readonly switch?: InferDeviceProxyCap<typeof switchCapability>\n readonly webrtcSession?: InferDeviceProxyCap<typeof webrtcSessionCapability>\n readonly zoneAnalytics?: InferDeviceProxyCap<typeof zoneAnalyticsCapability>\n readonly zoneRules?: InferDeviceProxyCap<typeof zoneRulesCapability>\n readonly zones?: InferDeviceProxyCap<typeof zonesCapability>\n readonly addonSettings: Pick<InferDeviceProxyCap<typeof addonSettingsCapability>, 'getDeviceSettings' | 'updateDeviceSettings'>\n readonly deviceManager: Pick<InferDeviceProxyCap<typeof deviceManagerCapability>, 'loadConfig' | 'loadRuntimeState' | 'loadMeta' | 'setName' | 'setLocation' | 'setMetadata' | 'setDisabled' | 'getDevice' | 'getStreamSources' | 'getConfigSchema' | 'getSettingsSchema' | 'updateConfig' | 'enable' | 'disable' | 'remove' | 'getStreamProfileMap' | 'setStreamProfileMap' | 'probeStreams' | 'getBindings' | 'getAllBindings' | 'setWrapperActive' | 'getDeviceSettingsAggregate' | 'getDeviceLiveInfoAggregate' | 'getDeviceAggregate' | 'updateDeviceField' | 'updateDeviceFieldsBatch' | 'testField' | 'getDeviceStatusAggregate'>\n readonly deviceState: Pick<InferDeviceProxyCap<typeof deviceStateCapability>, 'getSnapshot' | 'getCapSlice' | 'setCapSlice'>\n readonly networkQuality: Pick<InferDeviceProxyCap<typeof networkQualityCapability>, 'getDeviceStats' | 'reportClientStats'>\n readonly pipelineExecutor: Pick<InferDeviceProxyCap<typeof pipelineExecutorCapability>, 'runPipeline' | 'runPipelineBatch'>\n readonly pipelineOrchestrator: Pick<InferDeviceProxyCap<typeof pipelineOrchestratorCapability>, 'assignPipeline' | 'unassignPipeline' | 'getPipelineAssignment' | 'getCameraMetrics' | 'assignDecoder' | 'unassignDecoder' | 'assignAudio' | 'unassignAudio' | 'getAudioAssignment' | 'getAudioAssignments' | 'getDecoderAssignment' | 'getCameraSettings' | 'setCameraStepToggle' | 'getCameraStepOverrides' | 'setCameraStepOverride' | 'setCameraPipelineForAgent' | 'resolvePipeline' | 'getDeviceSettingsContribution' | 'getDeviceLiveContribution' | 'applyDeviceSettingsPatch'>\n readonly pipelineRunner: Pick<InferDeviceProxyCap<typeof pipelineRunnerCapability>, 'detachCamera' | 'getCameraMetrics'>\n readonly recordingEngine: Pick<InferDeviceProxyCap<typeof recordingEngineCapability>, 'getPolicyStatus'>\n readonly snapshotProvider: Pick<InferDeviceProxyCap<typeof snapshotProviderCapability>, 'supportsDevice' | 'getSnapshot'>\n readonly streamBroker: Pick<InferDeviceProxyCap<typeof streamBrokerCapability>, 'publishCameraStream' | 'retractCameraStream' | 'assignProfile' | 'unassignProfile' | 'restartProfile' | 'getDeviceSettingsContribution' | 'getDeviceLiveContribution' | 'applyDeviceSettingsPatch'>\n}\n\n/**\n * Build a DeviceProxy that pre-binds deviceId + nodeId on every method call\n * and dispatches through the existing cap-router tRPC procedures.\n *\n * Optional `opts.stateSource` lets a SystemManager pass a shared mirror\n * source so every device proxy reads from the same in-memory map and\n * shares the warm-boot/push-event update loop. When omitted, a per-proxy\n * lazy tRPC source is created — the path used by `ctx.fetchDevice` and\n * `BackendClient.fetchDevice` for one-off reads.\n *\n * The returned proxy's `binding` field is set to the input `binding`\n * by default — callers that want to expose a different value (e.g. the\n * SDK's `System` patches it from a shared cache) can overwrite the\n * field on the returned object.\n */\nexport function createDeviceProxy(\n api: AddonApi,\n binding: DeviceBinding,\n opts?: { stateSource?: SliceHandleSource },\n): DeviceProxy {\n const typedApi = api as unknown as Record<string, Record<string, Record<string, (input?: unknown) => unknown>>>\n // Default lazy source: builds its own cache + bridge on first watch().\n // The SystemManager passes a shared mirror source instead.\n const stateSource: SliceHandleSource = opts?.stateSource\n ?? createLazyTrpcSource(api as unknown as SliceHandleApi)\n\n // ── Dispatch helpers ────────────────────────────────────────────────\n //\n // Every per-cap method below collapses to a single `dispatch(...)` /\n // `dispatchSystem(...)` call. The helpers handle three concerns\n // uniformly so the per-method code reads as a one-line declaration of\n // intent (cap + method + kind) rather than 25 lines of plumbing.\n type Kind = 'query' | 'mutation' | 'subscription'\n\n /** Merge `{deviceId, nodeId?}` into the caller-supplied input. */\n function mergeInput(input: unknown, nodeId: string | undefined): Record<string, unknown> {\n const base = typeof input === 'object' && input !== null ? input as Record<string, unknown> : {}\n return nodeId !== undefined\n ? { ...base, deviceId: binding.deviceId, nodeId }\n : { ...base, deviceId: binding.deviceId }\n }\n\n /**\n * Invoke `api.<capProp>.<method>.{query|mutate|subscribe}(merged)`.\n * Returns `any` so the caller-side per-method declaration assigns\n * cleanly into the strongly-typed `InferDeviceProxyCap<...>` shape on\n * the `DeviceProxy` interface — the proxy's contract is enforced by\n * the interface, not the dispatch helper.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function callLeaf(capProp: string, method: string, kind: Kind, merged: unknown, push: unknown): any {\n const leaf = typedApi[capProp]?.[method]\n if (!leaf) throw new Error(`DeviceProxy: api has no '${capProp}.${method}'`)\n const fn = leaf as unknown as {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n query: (i: unknown) => any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mutate: (i: unknown) => any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n subscribe: (i: unknown, push: unknown) => any\n }\n if (kind === 'mutation') return fn.mutate(merged)\n if (kind === 'subscription') return fn.subscribe(merged, push)\n return fn.query(merged)\n }\n\n /**\n * Device-scoped cap dispatch. Looks up the binding entry for `capName`\n * to pin the call to the worker that owns the per-device provider; when\n * no entry exists (cluster-wide singletons like `zones` /\n * `zone-rules` / `audio-metrics` that don't register per-device\n * natives), nodeId is omitted and the cap-router's `resolveProvider`\n * falls through to the local provider — a Moleculer bridge proxy when\n * the actual singleton lives in a worker.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function dispatch(capName: string, capProp: string, method: string, kind: Kind, input?: unknown, push?: unknown): any {\n const entry = binding.entries.find((e) => e.capName === capName)\n return callLeaf(capProp, method, kind, mergeInput(input, entry?.providerNodeId), push)\n }\n\n /**\n * System-cap dispatch. No binding gate — system caps are cluster-wide\n * singletons; the nodeId is left absent so caps that load-balance (e.g.\n * `pipeline-runner`) can resolve their own target node.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function dispatchSystem(capProp: string, method: string, kind: Kind, input?: unknown, push?: unknown): any {\n return callLeaf(capProp, method, kind, mergeInput(input, undefined), push)\n }\n\n const accessoriesInterface = {\n getStatus: (input?: unknown) => dispatch('accessories', 'accessories', 'getStatus', 'query', input),\n }\n\n const audioAnalysisInterface = {\n resolveDeviceSettings: (input?: unknown) => dispatch('audio-analysis', 'audioAnalysis', 'resolveDeviceSettings', 'query', input),\n getDeviceSettingsContribution: (input?: unknown) => dispatch('audio-analysis', 'audioAnalysis', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatch('audio-analysis', 'audioAnalysis', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatch('audio-analysis', 'audioAnalysis', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n const audioMetricsInterface = {\n getCurrentSnapshot: (input?: unknown) => dispatch('audio-metrics', 'audioMetrics', 'getCurrentSnapshot', 'query', input),\n getHistory: (input?: unknown) => dispatch('audio-metrics', 'audioMetrics', 'getHistory', 'query', input),\n }\n\n const batteryInterface = {\n getStatus: (input?: unknown) => dispatch('battery', 'battery', 'getStatus', 'query', input),\n }\n\n const brightnessInterface = {\n setBrightness: (input?: unknown) => dispatch('brightness', 'brightness', 'setBrightness', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('brightness', 'brightness', 'getStatus', 'query', input),\n }\n\n const cameraCredentialsInterface = {\n getCredentials: (input?: unknown) => dispatch('camera-credentials', 'cameraCredentials', 'getCredentials', 'query', input),\n getStatus: (input?: unknown) => dispatch('camera-credentials', 'cameraCredentials', 'getStatus', 'query', input),\n }\n\n const cameraStreamsInterface = {\n getCameraStreams: (input?: unknown) => dispatch('camera-streams', 'cameraStreams', 'getCameraStreams', 'query', input),\n getBrokerStreams: (input?: unknown) => dispatch('camera-streams', 'cameraStreams', 'getBrokerStreams', 'query', input),\n getRtspEntries: (input?: unknown) => dispatch('camera-streams', 'cameraStreams', 'getRtspEntries', 'query', input),\n }\n\n const detectionPipelineInterface = {\n getDeviceSettingsContribution: (input?: unknown) => dispatch('detection-pipeline', 'detectionPipeline', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatch('detection-pipeline', 'detectionPipeline', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatch('detection-pipeline', 'detectionPipeline', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n const deviceDiscoveryInterface = {\n listDiscovered: (input?: unknown) => dispatch('device-discovery', 'deviceDiscovery', 'listDiscovered', 'query', input),\n refreshDiscovery: (input?: unknown) => dispatch('device-discovery', 'deviceDiscovery', 'refreshDiscovery', 'mutation', input),\n adoptDevice: (input?: unknown) => dispatch('device-discovery', 'deviceDiscovery', 'adoptDevice', 'mutation', input),\n releaseDevice: (input?: unknown) => dispatch('device-discovery', 'deviceDiscovery', 'releaseDevice', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('device-discovery', 'deviceDiscovery', 'getStatus', 'query', input),\n }\n\n const deviceOpsInterface = {\n getStreamSources: (input?: unknown) => dispatch('device-ops', 'deviceOps', 'getStreamSources', 'query', input),\n getConfigEntries: (input?: unknown) => dispatch('device-ops', 'deviceOps', 'getConfigEntries', 'query', input),\n setConfig: (input?: unknown) => dispatch('device-ops', 'deviceOps', 'setConfig', 'mutation', input),\n removeDevice: (input?: unknown) => dispatch('device-ops', 'deviceOps', 'removeDevice', 'mutation', input),\n getSettingsSchema: (input?: unknown) => dispatch('device-ops', 'deviceOps', 'getSettingsSchema', 'query', input),\n }\n\n const deviceStatusInterface = {\n getStatus: (input?: unknown) => dispatch('device-status', 'deviceStatus', 'getStatus', 'query', input),\n }\n\n const doorbellInterface = {\n getStatus: (input?: unknown) => dispatch('doorbell', 'doorbell', 'getStatus', 'query', input),\n }\n\n const eventsInterface = {\n getEvents: (input?: unknown) => dispatch('events', 'events', 'getEvents', 'query', input),\n getEventThumbnail: (input?: unknown) => dispatch('events', 'events', 'getEventThumbnail', 'query', input),\n getEventClipUrl: (input?: unknown) => dispatch('events', 'events', 'getEventClipUrl', 'query', input),\n }\n\n const featureProbeInterface = {\n getStatus: (input?: unknown) => dispatch('feature-probe', 'featureProbe', 'getStatus', 'query', input),\n }\n\n const intercomInterface = {\n startSession: (input?: unknown) => dispatch('intercom', 'intercom', 'startSession', 'mutation', input),\n handleAnswer: (input?: unknown) => dispatch('intercom', 'intercom', 'handleAnswer', 'mutation', input),\n stopSession: (input?: unknown) => dispatch('intercom', 'intercom', 'stopSession', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('intercom', 'intercom', 'getStatus', 'query', input),\n }\n\n const motionInterface = {\n isDetected: (input?: unknown) => dispatch('motion', 'motion', 'isDetected', 'query', input),\n getStatus: (input?: unknown) => dispatch('motion', 'motion', 'getStatus', 'query', input),\n }\n\n const motionDetectionInterface = {\n analyze: (input?: unknown) => dispatch('motion-detection', 'motionDetection', 'analyze', 'mutation', input),\n removeCamera: (input?: unknown) => dispatch('motion-detection', 'motionDetection', 'removeCamera', 'mutation', input),\n reset: (input?: unknown) => dispatch('motion-detection', 'motionDetection', 'reset', 'mutation', input),\n getDeviceSettingsContribution: (input?: unknown) => dispatch('motion-detection', 'motionDetection', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatch('motion-detection', 'motionDetection', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatch('motion-detection', 'motionDetection', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n const motionTriggerInterface = {\n setMotionTrigger: (input?: unknown) => dispatch('motion-trigger', 'motionTrigger', 'setMotionTrigger', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('motion-trigger', 'motionTrigger', 'getStatus', 'query', input),\n }\n\n const nativeObjectDetectionInterface = {\n getStatus: (input?: unknown) => dispatch('native-object-detection', 'nativeObjectDetection', 'getStatus', 'query', input),\n }\n\n const osdInterface = {\n setOverlay: (input?: unknown) => dispatch('osd', 'osd', 'setOverlay', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('osd', 'osd', 'getStatus', 'query', input),\n }\n\n const pipelineAnalyticsInterface = {\n getActiveTracks: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getActiveTracks', 'query', input),\n getTrack: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getTrack', 'query', input),\n listTracks: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'listTracks', 'query', input),\n clearTracks: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'clearTracks', 'mutation', input),\n getMotionEvents: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getMotionEvents', 'query', input),\n getObjectEvents: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getObjectEvents', 'query', input),\n getAudioEvents: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getAudioEvents', 'query', input),\n getEventMedia: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getEventMedia', 'query', input),\n getTrackMedia: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getTrackMedia', 'query', input),\n getDeviceSettingsContribution: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatch('pipeline-analytics', 'pipelineAnalytics', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n const ptzInterface = {\n move: (input?: unknown) => dispatch('ptz', 'ptz', 'move', 'mutation', input),\n continuousMove: (input?: unknown) => dispatch('ptz', 'ptz', 'continuousMove', 'mutation', input),\n stop: (input?: unknown) => dispatch('ptz', 'ptz', 'stop', 'mutation', input),\n getPresets: (input?: unknown) => dispatch('ptz', 'ptz', 'getPresets', 'query', input),\n goToPreset: (input?: unknown) => dispatch('ptz', 'ptz', 'goToPreset', 'mutation', input),\n goHome: (input?: unknown) => dispatch('ptz', 'ptz', 'goHome', 'mutation', input),\n getPosition: (input?: unknown) => dispatch('ptz', 'ptz', 'getPosition', 'query', input),\n getStatus: (input?: unknown) => dispatch('ptz', 'ptz', 'getStatus', 'query', input),\n }\n\n const ptzAutotrackInterface = {\n getStatus: (input?: unknown) => dispatch('ptz-autotrack', 'ptzAutotrack', 'getStatus', 'query', input),\n setEnabled: (input?: unknown) => dispatch('ptz-autotrack', 'ptzAutotrack', 'setEnabled', 'mutation', input),\n getSettings: (input?: unknown) => dispatch('ptz-autotrack', 'ptzAutotrack', 'getSettings', 'query', input),\n setSettings: (input?: unknown) => dispatch('ptz-autotrack', 'ptzAutotrack', 'setSettings', 'mutation', input),\n }\n\n const rebootInterface = {\n reboot: (input?: unknown) => dispatch('reboot', 'reboot', 'reboot', 'mutation', input),\n }\n\n const recordingInterface = {\n getSegments: (input?: unknown) => dispatch('recording', 'recording', 'getSegments', 'query', input),\n getPlaybackUrl: (input?: unknown) => dispatch('recording', 'recording', 'getPlaybackUrl', 'query', input),\n getThumbnailAt: (input?: unknown) => dispatch('recording', 'recording', 'getThumbnailAt', 'query', input),\n }\n\n const snapshotInterface = {\n getSnapshot: (input?: unknown) => dispatch('snapshot', 'snapshot', 'getSnapshot', 'query', input),\n invalidateCache: (input?: unknown) => dispatch('snapshot', 'snapshot', 'invalidateCache', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('snapshot', 'snapshot', 'getStatus', 'query', input),\n getDeviceSettingsContribution: (input?: unknown) => dispatch('snapshot', 'snapshot', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatch('snapshot', 'snapshot', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatch('snapshot', 'snapshot', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n const switchInterface = {\n setState: (input?: unknown) => dispatch('switch', 'switch', 'setState', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('switch', 'switch', 'getStatus', 'query', input),\n }\n\n const webrtcSessionInterface = {\n listStreams: (input?: unknown) => dispatch('webrtc-session', 'webrtcSession', 'listStreams', 'query', input),\n createSession: (input?: unknown) => dispatch('webrtc-session', 'webrtcSession', 'createSession', 'mutation', input),\n handleAnswer: (input?: unknown) => dispatch('webrtc-session', 'webrtcSession', 'handleAnswer', 'mutation', input),\n closeSession: (input?: unknown) => dispatch('webrtc-session', 'webrtcSession', 'closeSession', 'mutation', input),\n hasAdaptiveBitrate: (input?: unknown) => dispatch('webrtc-session', 'webrtcSession', 'hasAdaptiveBitrate', 'query', input),\n }\n\n const zoneAnalyticsInterface = {\n getCurrentSnapshot: (input?: unknown) => dispatch('zone-analytics', 'zoneAnalytics', 'getCurrentSnapshot', 'query', input),\n getZoneHistory: (input?: unknown) => dispatch('zone-analytics', 'zoneAnalytics', 'getZoneHistory', 'query', input),\n getCameraHistory: (input?: unknown) => dispatch('zone-analytics', 'zoneAnalytics', 'getCameraHistory', 'query', input),\n getUnzonedHistory: (input?: unknown) => dispatch('zone-analytics', 'zoneAnalytics', 'getUnzonedHistory', 'query', input),\n }\n\n const zoneRulesInterface = {\n listRules: (input?: unknown) => dispatch('zone-rules', 'zoneRules', 'listRules', 'query', input),\n setRules: (input?: unknown) => dispatch('zone-rules', 'zoneRules', 'setRules', 'mutation', input),\n }\n\n const zonesInterface = {\n listZones: (input?: unknown) => dispatch('zones', 'zones', 'listZones', 'query', input),\n addZone: (input?: unknown) => dispatch('zones', 'zones', 'addZone', 'mutation', input),\n removeZone: (input?: unknown) => dispatch('zones', 'zones', 'removeZone', 'mutation', input),\n updateZone: (input?: unknown) => dispatch('zones', 'zones', 'updateZone', 'mutation', input),\n }\n\n const addonSettingsInterface = {\n getDeviceSettings: (input?: unknown) => dispatchSystem('addonSettings', 'getDeviceSettings', 'query', input),\n updateDeviceSettings: (input?: unknown) => dispatchSystem('addonSettings', 'updateDeviceSettings', 'mutation', input),\n }\n\n const deviceManagerInterface = {\n loadConfig: (input?: unknown) => dispatchSystem('deviceManager', 'loadConfig', 'query', input),\n loadRuntimeState: (input?: unknown) => dispatchSystem('deviceManager', 'loadRuntimeState', 'query', input),\n loadMeta: (input?: unknown) => dispatchSystem('deviceManager', 'loadMeta', 'query', input),\n setName: (input?: unknown) => dispatchSystem('deviceManager', 'setName', 'mutation', input),\n setLocation: (input?: unknown) => dispatchSystem('deviceManager', 'setLocation', 'mutation', input),\n setMetadata: (input?: unknown) => dispatchSystem('deviceManager', 'setMetadata', 'mutation', input),\n setDisabled: (input?: unknown) => dispatchSystem('deviceManager', 'setDisabled', 'mutation', input),\n getDevice: (input?: unknown) => dispatchSystem('deviceManager', 'getDevice', 'query', input),\n getStreamSources: (input?: unknown) => dispatchSystem('deviceManager', 'getStreamSources', 'query', input),\n getConfigSchema: (input?: unknown) => dispatchSystem('deviceManager', 'getConfigSchema', 'query', input),\n getSettingsSchema: (input?: unknown) => dispatchSystem('deviceManager', 'getSettingsSchema', 'query', input),\n updateConfig: (input?: unknown) => dispatchSystem('deviceManager', 'updateConfig', 'mutation', input),\n enable: (input?: unknown) => dispatchSystem('deviceManager', 'enable', 'mutation', input),\n disable: (input?: unknown) => dispatchSystem('deviceManager', 'disable', 'mutation', input),\n remove: (input?: unknown) => dispatchSystem('deviceManager', 'remove', 'mutation', input),\n getStreamProfileMap: (input?: unknown) => dispatchSystem('deviceManager', 'getStreamProfileMap', 'query', input),\n setStreamProfileMap: (input?: unknown) => dispatchSystem('deviceManager', 'setStreamProfileMap', 'mutation', input),\n probeStreams: (input?: unknown) => dispatchSystem('deviceManager', 'probeStreams', 'mutation', input),\n getBindings: (input?: unknown) => dispatchSystem('deviceManager', 'getBindings', 'query', input),\n getAllBindings: (input?: unknown) => dispatchSystem('deviceManager', 'getAllBindings', 'query', input),\n setWrapperActive: (input?: unknown) => dispatchSystem('deviceManager', 'setWrapperActive', 'mutation', input),\n getDeviceSettingsAggregate: (input?: unknown) => dispatchSystem('deviceManager', 'getDeviceSettingsAggregate', 'query', input),\n getDeviceLiveInfoAggregate: (input?: unknown) => dispatchSystem('deviceManager', 'getDeviceLiveInfoAggregate', 'query', input),\n getDeviceAggregate: (input?: unknown) => dispatchSystem('deviceManager', 'getDeviceAggregate', 'query', input),\n updateDeviceField: (input?: unknown) => dispatchSystem('deviceManager', 'updateDeviceField', 'mutation', input),\n updateDeviceFieldsBatch: (input?: unknown) => dispatchSystem('deviceManager', 'updateDeviceFieldsBatch', 'mutation', input),\n testField: (input?: unknown) => dispatchSystem('deviceManager', 'testField', 'mutation', input),\n getDeviceStatusAggregate: (input?: unknown) => dispatchSystem('deviceManager', 'getDeviceStatusAggregate', 'query', input),\n }\n\n const deviceStateInterface = {\n getSnapshot: (input?: unknown) => dispatchSystem('deviceState', 'getSnapshot', 'query', input),\n getCapSlice: (input?: unknown) => dispatchSystem('deviceState', 'getCapSlice', 'query', input),\n setCapSlice: (input?: unknown) => dispatchSystem('deviceState', 'setCapSlice', 'mutation', input),\n }\n\n const networkQualityInterface = {\n getDeviceStats: (input?: unknown) => dispatchSystem('networkQuality', 'getDeviceStats', 'query', input),\n reportClientStats: (input?: unknown) => dispatchSystem('networkQuality', 'reportClientStats', 'mutation', input),\n }\n\n const pipelineExecutorInterface = {\n runPipeline: (input?: unknown) => dispatchSystem('pipelineExecutor', 'runPipeline', 'mutation', input),\n runPipelineBatch: (input?: unknown) => dispatchSystem('pipelineExecutor', 'runPipelineBatch', 'mutation', input),\n }\n\n const pipelineOrchestratorInterface = {\n assignPipeline: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'assignPipeline', 'mutation', input),\n unassignPipeline: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'unassignPipeline', 'mutation', input),\n getPipelineAssignment: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getPipelineAssignment', 'query', input),\n getCameraMetrics: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getCameraMetrics', 'query', input),\n assignDecoder: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'assignDecoder', 'mutation', input),\n unassignDecoder: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'unassignDecoder', 'mutation', input),\n assignAudio: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'assignAudio', 'mutation', input),\n unassignAudio: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'unassignAudio', 'mutation', input),\n getAudioAssignment: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getAudioAssignment', 'query', input),\n getAudioAssignments: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getAudioAssignments', 'query', input),\n getDecoderAssignment: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getDecoderAssignment', 'query', input),\n getCameraSettings: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getCameraSettings', 'query', input),\n setCameraStepToggle: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'setCameraStepToggle', 'mutation', input),\n getCameraStepOverrides: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getCameraStepOverrides', 'query', input),\n setCameraStepOverride: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'setCameraStepOverride', 'mutation', input),\n setCameraPipelineForAgent: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'setCameraPipelineForAgent', 'mutation', input),\n resolvePipeline: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'resolvePipeline', 'query', input),\n getDeviceSettingsContribution: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatchSystem('pipelineOrchestrator', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n const pipelineRunnerInterface = {\n detachCamera: (input?: unknown) => dispatchSystem('pipelineRunner', 'detachCamera', 'mutation', input),\n getCameraMetrics: (input?: unknown) => dispatchSystem('pipelineRunner', 'getCameraMetrics', 'query', input),\n }\n\n const recordingEngineInterface = {\n getPolicyStatus: (input?: unknown) => dispatchSystem('recordingEngine', 'getPolicyStatus', 'query', input),\n }\n\n const snapshotProviderInterface = {\n supportsDevice: (input?: unknown) => dispatchSystem('snapshotProvider', 'supportsDevice', 'query', input),\n getSnapshot: (input?: unknown) => dispatchSystem('snapshotProvider', 'getSnapshot', 'query', input),\n }\n\n const streamBrokerInterface = {\n publishCameraStream: (input?: unknown) => dispatchSystem('streamBroker', 'publishCameraStream', 'mutation', input),\n retractCameraStream: (input?: unknown) => dispatchSystem('streamBroker', 'retractCameraStream', 'mutation', input),\n assignProfile: (input?: unknown) => dispatchSystem('streamBroker', 'assignProfile', 'mutation', input),\n unassignProfile: (input?: unknown) => dispatchSystem('streamBroker', 'unassignProfile', 'mutation', input),\n restartProfile: (input?: unknown) => dispatchSystem('streamBroker', 'restartProfile', 'mutation', input),\n getDeviceSettingsContribution: (input?: unknown) => dispatchSystem('streamBroker', 'getDeviceSettingsContribution', 'query', input),\n getDeviceLiveContribution: (input?: unknown) => dispatchSystem('streamBroker', 'getDeviceLiveContribution', 'query', input),\n applyDeviceSettingsPatch: (input?: unknown) => dispatchSystem('streamBroker', 'applyDeviceSettingsPatch', 'mutation', input),\n }\n\n return {\n deviceId: binding.deviceId,\n binding,\n state: {\n audioMetrics: createSliceHandle<InferRuntimeState<typeof audioMetricsCapability>>(stateSource, binding.deviceId, 'audio-metrics'),\n battery: createSliceHandle<InferRuntimeState<typeof batteryCapability>>(stateSource, binding.deviceId, 'battery'),\n brightness: createSliceHandle<InferRuntimeState<typeof brightnessCapability>>(stateSource, binding.deviceId, 'brightness'),\n cameraStreams: createSliceHandle<InferRuntimeState<typeof cameraStreamsCapability>>(stateSource, binding.deviceId, 'camera-streams'),\n deviceDiscovery: createSliceHandle<InferRuntimeState<typeof deviceDiscoveryCapability>>(stateSource, binding.deviceId, 'device-discovery'),\n deviceStatus: createSliceHandle<InferRuntimeState<typeof deviceStatusCapability>>(stateSource, binding.deviceId, 'device-status'),\n doorbell: createSliceHandle<InferRuntimeState<typeof doorbellCapability>>(stateSource, binding.deviceId, 'doorbell'),\n featureProbe: createSliceHandle<InferRuntimeState<typeof featureProbeCapability>>(stateSource, binding.deviceId, 'feature-probe'),\n motion: createSliceHandle<InferRuntimeState<typeof motionCapability>>(stateSource, binding.deviceId, 'motion'),\n motionTrigger: createSliceHandle<InferRuntimeState<typeof motionTriggerCapability>>(stateSource, binding.deviceId, 'motion-trigger'),\n ptzAutotrack: createSliceHandle<InferRuntimeState<typeof ptzAutotrackCapability>>(stateSource, binding.deviceId, 'ptz-autotrack'),\n switch: createSliceHandle<InferRuntimeState<typeof switchCapability>>(stateSource, binding.deviceId, 'switch'),\n zoneAnalytics: createSliceHandle<InferRuntimeState<typeof zoneAnalyticsCapability>>(stateSource, binding.deviceId, 'zone-analytics'),\n zoneRules: createSliceHandle<InferRuntimeState<typeof zoneRulesCapability>>(stateSource, binding.deviceId, 'zone-rules'),\n zones: createSliceHandle<InferRuntimeState<typeof zonesCapability>>(stateSource, binding.deviceId, 'zones'),\n },\n accessories: accessoriesInterface,\n audioAnalysis: audioAnalysisInterface,\n audioMetrics: audioMetricsInterface,\n battery: batteryInterface,\n brightness: brightnessInterface,\n cameraCredentials: cameraCredentialsInterface,\n cameraStreams: cameraStreamsInterface,\n detectionPipeline: detectionPipelineInterface,\n deviceDiscovery: deviceDiscoveryInterface,\n deviceOps: deviceOpsInterface,\n deviceStatus: deviceStatusInterface,\n doorbell: doorbellInterface,\n events: eventsInterface,\n featureProbe: featureProbeInterface,\n intercom: intercomInterface,\n motion: motionInterface,\n motionDetection: motionDetectionInterface,\n motionTrigger: motionTriggerInterface,\n nativeObjectDetection: nativeObjectDetectionInterface,\n osd: osdInterface,\n pipelineAnalytics: pipelineAnalyticsInterface,\n ptz: ptzInterface,\n ptzAutotrack: ptzAutotrackInterface,\n reboot: rebootInterface,\n recording: recordingInterface,\n snapshot: snapshotInterface,\n switch: switchInterface,\n webrtcSession: webrtcSessionInterface,\n zoneAnalytics: zoneAnalyticsInterface,\n zoneRules: zoneRulesInterface,\n zones: zonesInterface,\n addonSettings: addonSettingsInterface,\n deviceManager: deviceManagerInterface,\n deviceState: deviceStateInterface,\n networkQuality: networkQualityInterface,\n pipelineExecutor: pipelineExecutorInterface,\n pipelineOrchestrator: pipelineOrchestratorInterface,\n pipelineRunner: pipelineRunnerInterface,\n recordingEngine: recordingEngineInterface,\n snapshotProvider: snapshotProviderInterface,\n streamBroker: streamBrokerInterface,\n }\n}\n","/**\n * Pure rules-driven zone evaluator. Shared between every consumer\n * that gates items (motion regions, detections, future audio events,\n * …) by `(zones, rules)` pairs. Lives in `@camstack/types/utils` so\n * addon-motion-wasm + addon-detection-pipeline share one canonical\n * implementation.\n *\n * Semantics — match the analytics zone-engine introduced in Phase 2a:\n * - any active include rule fires ⇒ whitelist mode (only items\n * satisfying at least one include rule pass)\n * - otherwise ⇒ blacklist mode (items in any exclude rule are\n * dropped, the rest pass)\n * - inactive rules (`enabled: false`) are skipped before the gate\n * - a rule's `classFilter` narrows it to the listed classes; an\n * item whose class isn't in the filter is invisible to that rule\n *\n * Test point: each item provides a normalised (0–1) center via the\n * `getCenter` accessor; rules fire when the center lies inside any\n * of the rule's referenced zone polygons (ray-cast point-in-polygon).\n * Bbox-area overlap would be marginally more intuitive on tiny\n * items but doubles the per-frame cost; the center test keeps the\n * hot path cheap and matches the operator's intuition for\n * \"where is the activity centered\".\n */\nimport type { Zone } from '../capabilities/zones.cap.js'\nimport type { ZoneRule } from '../capabilities/schemas/zone-rule.js'\n\nexport interface ZoneRuleEvalResult<T> {\n readonly passed: readonly T[]\n readonly excluded: readonly T[]\n}\n\n/**\n * Evaluate `rules` against `items`. Returns the items partitioned\n * into `passed` (kept by the gate) and `excluded` (dropped). When\n * `rules` is empty or `zones` is empty everything passes — the gate\n * is \"off\" without explicit configuration.\n *\n * `getCenter` returns the item's normalised (0–1) center point.\n * `getClassName` is optional — when omitted, every rule's\n * `classFilter` is treated as matching all classes (motion-stage\n * semantics). Provide it for object detections so per-class rules\n * narrow correctly.\n */\nexport function evaluateZoneRules<T>(\n items: readonly T[],\n zones: readonly Zone[],\n rules: readonly ZoneRule[],\n getCenter: (item: T) => { readonly x: number; readonly y: number },\n getClassName?: (item: T) => string | undefined,\n): ZoneRuleEvalResult<T> {\n if (rules.length === 0 || zones.length === 0) {\n return { passed: items, excluded: [] }\n }\n const activeRules = rules.filter(r => r.enabled !== false)\n const includeRules = activeRules.filter(r => r.mode === 'include')\n const excludeRules = activeRules.filter(r => r.mode === 'exclude')\n const whitelistMode = includeRules.length > 0\n const zonesById = new Map(zones.map(z => [z.id, z]))\n\n const passed: T[] = []\n const excluded: T[] = []\n\n for (const item of items) {\n const center = getCenter(item)\n const className = getClassName?.(item)\n const inInclude = includeRules.some(r => ruleApplies(r, center, className, zonesById))\n const inExclude = excludeRules.some(r => ruleApplies(r, center, className, zonesById))\n if (whitelistMode) {\n if (inInclude && !inExclude) passed.push(item)\n else excluded.push(item)\n } else {\n if (inExclude) excluded.push(item)\n else passed.push(item)\n }\n }\n return { passed, excluded }\n}\n\nfunction ruleApplies(\n rule: ZoneRule,\n center: { readonly x: number; readonly y: number },\n className: string | undefined,\n zonesById: ReadonlyMap<string, Zone>,\n): boolean {\n // Class narrowing — when className is undefined (motion stage),\n // we treat the filter as matching everything. When the item has a\n // class, missing it from the filter excludes the rule.\n if (rule.classFilter && rule.classFilter.length > 0) {\n if (className !== undefined && !rule.classFilter.includes(className)) return false\n }\n for (const zoneId of rule.zoneIds) {\n const zone = zonesById.get(zoneId)\n if (!zone) continue\n if (pointInPolygon(center, zone.polygon)) return true\n }\n return false\n}\n\n/**\n * Standard ray-casting point-in-polygon. Polygon vertices are\n * normalised (0–1); the test point is too — frame-resolution-\n * agnostic. Boundary points count as inside (treats `<` as `<=` on\n * the y-edge crossing).\n */\nfunction pointInPolygon(\n point: { readonly x: number; readonly y: number },\n polygon: ReadonlyArray<{ readonly x: number; readonly y: number }>,\n): boolean {\n if (polygon.length < 3) return false\n let inside = false\n const { x, y } = point\n for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {\n const a = polygon[i]!\n const b = polygon[j]!\n const intersect =\n ((a.y > y) !== (b.y > y)) &&\n (x < ((b.x - a.x) * (y - a.y)) / (b.y - a.y || Number.EPSILON) + a.x)\n if (intersect) inside = !inside\n }\n return inside\n}\n","// AUTO-GENERATED — do not edit manually.\n// Regenerate with: npx tsx scripts/generate-system-proxy.ts\n/* eslint-disable */\n\nimport type { InferProvider } from '../capabilities/capability-definition.js'\nimport type { AddonApi } from './addon-api.js'\nimport type { addonPagesCapability } from '../capabilities/addon-pages.cap.js'\nimport type { addonsCapability } from '../capabilities/addons.cap.js'\nimport type { addonSettingsCapability } from '../capabilities/addon-settings.cap.js'\nimport type { addonWidgetsCapability } from '../capabilities/addon-widgets.cap.js'\nimport type { alertsCapability } from '../capabilities/alerts.cap.js'\nimport type { audioAnalyzerCapability } from '../capabilities/audio-analyzer.cap.js'\nimport type { audioCodecCapability } from '../capabilities/audio-codec.cap.js'\nimport type { authenticationCapability } from '../capabilities/authentication.cap.js'\nimport type { backupCapability } from '../capabilities/backup.cap.js'\nimport type { decoderCapability } from '../capabilities/decoder.cap.js'\nimport type { deviceManagerCapability } from '../capabilities/device-manager.cap.js'\nimport type { deviceProviderCapability } from '../capabilities/device-provider.cap.js'\nimport type { deviceStateCapability } from '../capabilities/device-state.cap.js'\nimport type { integrationsCapability } from '../capabilities/integrations.cap.js'\nimport type { localNetworkCapability } from '../capabilities/local-network.cap.js'\nimport type { meshNetworkCapability } from '../capabilities/mesh-network.cap.js'\nimport type { meshOrchestratorCapability } from '../capabilities/mesh-orchestrator.cap.js'\nimport type { metricsProviderCapability } from '../capabilities/metrics-provider.cap.js'\nimport type { networkQualityCapability } from '../capabilities/network-quality.cap.js'\nimport type { nodesCapability } from '../capabilities/nodes.cap.js'\nimport type { notificationOutputCapability } from '../capabilities/notification-output.cap.js'\nimport type { pipelineExecutorCapability } from '../capabilities/pipeline-executor.cap.js'\nimport type { pipelineOrchestratorCapability } from '../capabilities/pipeline-orchestrator.cap.js'\nimport type { pipelineRunnerCapability } from '../capabilities/pipeline-runner.cap.js'\nimport type { platformProbeCapability } from '../capabilities/platform-probe.cap.js'\nimport type { recordingEngineCapability } from '../capabilities/recording-engine.cap.js'\nimport type { remoteAccessCapability } from '../capabilities/remote-access.cap.js'\nimport type { settingsStoreCapability } from '../capabilities/settings-store.cap.js'\nimport type { storageCapability } from '../capabilities/storage.cap.js'\nimport type { streamBrokerCapability } from '../capabilities/stream-broker.cap.js'\nimport type { systemCapability } from '../capabilities/system.cap.js'\nimport type { toastCapability } from '../capabilities/toast.cap.js'\nimport type { turnOrchestratorCapability } from '../capabilities/turn-orchestrator.cap.js'\nimport type { turnProviderCapability } from '../capabilities/turn-provider.cap.js'\nimport type { userManagementCapability } from '../capabilities/user-management.cap.js'\n\n/**\n * Cluster-wide facade over every `scope: 'system'` capability. Each entry\n * is a typed namespace exposing only the cap's non-device-bound methods —\n * device-scoped slices already surface on `DeviceProxy`.\n *\n * Consumed by the SDK's `System` class as the source of truth for\n * `system.<cap>.<method>(input)` calls.\n */\nexport interface SystemProxy {\n readonly addonPages: Pick<InferProvider<typeof addonPagesCapability>, 'listPages'>\n readonly addons: Pick<InferProvider<typeof addonsCapability>, 'list' | 'getLogs' | 'listPackages' | 'installPackage' | 'installFromWorkspace' | 'isWorkspaceAvailable' | 'listWorkspacePackages' | 'uninstallPackage' | 'reloadPackages' | 'searchAvailable' | 'listUpdates' | 'updatePackage' | 'rollbackPackage' | 'forceRefresh' | 'restartServer' | 'getVersions' | 'restartAddon' | 'retryLoad' | 'getAutoUpdateSettings' | 'setAutoUpdateSettings' | 'getAddonAutoUpdate' | 'setAddonAutoUpdate' | 'applyAutoUpdateToAll' | 'custom' | 'onAddonLogs'>\n readonly addonSettings: Pick<InferProvider<typeof addonSettingsCapability>, 'getGlobalSettings' | 'updateGlobalSettings'>\n readonly addonWidgets: Pick<InferProvider<typeof addonWidgetsCapability>, 'listWidgets'>\n readonly alerts: Pick<InferProvider<typeof alertsCapability>, 'emit' | 'update' | 'list' | 'getUnreadCount' | 'markRead' | 'markAllRead' | 'dismiss'>\n readonly audioAnalyzer: Pick<InferProvider<typeof audioAnalyzerCapability>, 'analyseChunk' | 'classify' | 'isReady' | 'dispose' | 'reprobeAudioEngine'>\n readonly audioCodec: Pick<InferProvider<typeof audioCodecCapability>, 'listSupportedCodecs' | 'canHandle' | 'createDecodeSession' | 'createEncodeSession' | 'closeSession' | 'pushEncodedFrame' | 'pullPcm' | 'pushPcm' | 'pullEncoded' | 'flushEncode' | 'listActiveSessions'>\n readonly authentication: Pick<InferProvider<typeof authenticationCapability>, 'listProviders' | 'setProviderEnabled'>\n readonly backup: Pick<InferProvider<typeof backupCapability>, 'listDestinations' | 'trigger' | 'list' | 'listLocations' | 'getEntries' | 'restore' | 'delete' | 'listArchives' | 'upsertDestinationPolicy' | 'previewSchedule'>\n readonly decoder: Pick<InferProvider<typeof decoderCapability>, 'supportsCodec' | 'getInfo' | 'createSession' | 'destroySession' | 'pushPacket' | 'openStream' | 'pullFrames' | 'updateConfig' | 'getStats' | 'listActiveSessions' | 'reprobeHwaccel'>\n readonly deviceManager: Pick<InferProvider<typeof deviceManagerCapability>, 'allocateDeviceId' | 'registerDevice' | 'removeDevice' | 'persistConfig' | 'listLocations' | 'addLocation' | 'removeLocation' | 'listPersistedByAddon' | 'listAll' | 'getChildren' | 'listWrappersForCap' | 'listBindableCapsForDeviceType' | 'discoverDevices' | 'adoptDevice' | 'getCreationSchema' | 'createDevice' | 'testCreationField'>\n readonly deviceProvider: Pick<InferProvider<typeof deviceProviderCapability>, 'start' | 'stop' | 'getStatus' | 'getDevices' | 'supportsDiscovery' | 'discoverDevices' | 'adoptDiscoveredDevice' | 'supportsManualCreation' | 'getChildCreationSchema' | 'createDevice' | 'testCreationField'>\n readonly deviceState: Pick<InferProvider<typeof deviceStateCapability>, 'getAllSnapshots'>\n readonly integrations: Pick<InferProvider<typeof integrationsCapability>, 'list' | 'get' | 'getByAddonId' | 'create' | 'update' | 'delete' | 'getSettings' | 'setSettings' | 'getAvailableTypes' | 'testConnection'>\n readonly localNetwork: Pick<InferProvider<typeof localNetworkCapability>, 'list' | 'getPreferred' | 'getConnectionEndpoints' | 'getAllowedAddresses' | 'setAllowedAddresses' | 'resetAllowlistToBestMatch'>\n readonly meshNetwork: Pick<InferProvider<typeof meshNetworkCapability>, 'getStatus' | 'join' | 'leave' | 'listPeers' | 'setPublicIngress' | 'setMeshIngress'>\n readonly meshOrchestrator: Pick<InferProvider<typeof meshOrchestratorCapability>, 'listProviders' | 'joinProvider' | 'leaveProvider'>\n readonly metricsProvider: Pick<InferProvider<typeof metricsProviderCapability>, 'collectSnapshot' | 'getCached' | 'getCurrent' | 'getDiskSpace' | 'getGpuInfo' | 'getCpuTemperature' | 'getProcessStats' | 'listAddonInstances' | 'getAddonStats' | 'listNodeProcesses' | 'killProcess'>\n readonly networkQuality: Pick<InferProvider<typeof networkQualityCapability>, 'getAllStats'>\n readonly nodes: Pick<InferProvider<typeof nodesCapability>, 'topology' | 'deployAddon' | 'undeployAddon' | 'restartAddon' | 'restartProcess' | 'restartNode' | 'shutdownNode' | 'renameNode' | 'clusterAddonStatus' | 'setProcessLogLevel' | 'executeQuery'>\n readonly notificationOutput: Pick<InferProvider<typeof notificationOutputCapability>, 'send' | 'sendTest'>\n readonly pipelineExecutor: Pick<InferProvider<typeof pipelineExecutorCapability>, 'getAvailableEngines' | 'getSelectedEngine' | 'getDefaultSteps' | 'reprobeEngine' | 'getVideoPipelineSteps' | 'setVideoPipelineSteps' | 'getSchema' | 'getGlobalSteps' | 'getGlobalPipelineConfig' | 'getOrchestratorConfigSchema' | 'listTemplates' | 'saveTemplate' | 'updateTemplate' | 'deleteTemplate' | 'getCapabilities' | 'getAddonModels' | 'downloadModel' | 'deleteModel' | 'detect' | 'cacheFrameInPool' | 'inferCached' | 'uncacheFrame' | 'getEffectiveTuning' | 'listLoadedEngines' | 'spinEngine' | 'killEngine' | 'listReferenceImages' | 'getReferenceImage' | 'getReferenceAudioFiles' | 'getReferenceAudio' | 'getAudioCapabilities' | 'runAudioTest' | 'getDetectionConfigSchema'>\n readonly pipelineOrchestrator: Pick<InferProvider<typeof pipelineOrchestratorCapability>, 'rebalance' | 'getPipelineAssignments' | 'getAgentLoad' | 'getGlobalMetrics' | 'getCapabilityBindings' | 'setCapabilityBinding' | 'getDecoderAssignments' | 'getAudioNodeLoad' | 'getAgentSettings' | 'listAgentSettings' | 'setAgentAddonDefaults' | 'removeAgentSettings' | 'listTemplates' | 'saveTemplate' | 'updateTemplate' | 'deleteTemplate'>\n readonly pipelineRunner: Pick<InferProvider<typeof pipelineRunnerCapability>, 'attachCamera' | 'reportMotion' | 'getLocalLoad' | 'getLocalMetrics' | 'getAllCameraMetrics' | 'getLocalCameras'>\n readonly platformProbe: Pick<InferProvider<typeof platformProbeCapability>, 'getCapabilities' | 'getHardware' | 'resolveInferenceConfig' | 'resolveHwAccel'>\n readonly recordingEngine: Pick<InferProvider<typeof recordingEngineCapability>, 'getStatus' | 'enable' | 'disable' | 'getConfig' | 'updateConfig' | 'getPlaylist' | 'getThumbnail' | 'getSegments' | 'getAvailability' | 'estimateStorage' | 'estimateGlobalStorage' | 'getStorageUsage' | 'setPolicy' | 'getPolicy' | 'getRetentionConfig' | 'updateRetentionConfig' | 'getMotionStats'>\n readonly remoteAccess: Pick<InferProvider<typeof remoteAccessCapability>, 'listProviders' | 'startProvider' | 'stopProvider'>\n readonly settingsStore: Pick<InferProvider<typeof settingsStoreCapability>, 'get' | 'set' | 'query' | 'insert' | 'update' | 'delete' | 'count' | 'isEmpty' | 'declareCollection'>\n readonly storage: Pick<InferProvider<typeof storageCapability>, 'resolve' | 'write' | 'read' | 'exists' | 'list' | 'delete' | 'getAvailableSpace' | 'beginUpload' | 'writeChunk' | 'finalizeUpload' | 'abortUpload' | 'beginDownload' | 'readChunk' | 'endDownload' | 'listLocations' | 'getDefaultLocation' | 'upsertLocation' | 'deleteLocation' | 'testLocation' | 'listProviders' | 'testConfig'>\n readonly streamBroker: Pick<InferProvider<typeof streamBrokerCapability>, 'listAllCameraStreams' | 'listAllProfileSlots' | 'getBrokerStats' | 'listClients' | 'killClient' | 'getStreamUrl' | 'getBroker' | 'setPreBufferDuration' | 'getPreBufferInfo' | 'getRtspPort' | 'getAllRtspEntries' | 'getRtspEntry' | 'regenerateRtspToken' | 'setRtspEnabled' | 'isRtspEnabled'>\n readonly system: Pick<InferProvider<typeof systemCapability>, 'info' | 'health' | 'featureFlags' | 'networkAddresses' | 'getRetentionConfig' | 'setRetentionConfig' | 'forceRetentionCleanup'>\n readonly toast: Pick<InferProvider<typeof toastCapability>, 'onToast'>\n readonly turnOrchestrator: Pick<InferProvider<typeof turnOrchestratorCapability>, 'listProviders' | 'getAllServers' | 'setProviderEnabled'>\n readonly turnProvider: Pick<InferProvider<typeof turnProviderCapability>, 'getTurnServers'>\n readonly userManagement: Pick<InferProvider<typeof userManagementCapability>, 'listUsers' | 'createUser' | 'updateUser' | 'deleteUser' | 'resetPassword' | 'validateCredentials' | 'listApiKeys' | 'createApiKey' | 'revokeApiKey' | 'validateApiKey' | 'createScopedToken' | 'revokeScopedToken' | 'validateScopedToken' | 'listScopedTokens'>\n}\n\n/**\n * Build a `SystemProxy` that dispatches every namespace method through\n * the existing system-cap tRPC procedures (`api.<capProp>.<method>.{query,mutate,subscribe}`).\n *\n * Pure function — no caching, no live state. The caller (typically\n * `System`) owns the `AddonApi` instance and disposes the proxy by\n * dropping the reference.\n */\nexport function createSystemProxy(api: AddonApi): SystemProxy {\n const typedApi = api as unknown as Record<string, Record<string, Record<string, (input?: unknown) => unknown>>>\n\n type Kind = 'query' | 'mutation' | 'subscription'\n\n /**\n * Invoke `api.<capProp>.<method>.{query|mutate|subscribe}(input)`.\n * Returns `any` so the caller-side per-method declaration assigns\n * cleanly into the strongly-typed `Pick<InferProvider<...>, ...>`\n * shape on the `SystemProxy` interface — the proxy's contract is\n * enforced by the interface, not the dispatch helper.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function dispatch(capProp: string, method: string, kind: Kind, input?: unknown, push?: unknown): any {\n const leaf = typedApi[capProp]?.[method]\n if (!leaf) throw new Error(`SystemProxy: api has no '${capProp}.${method}'`)\n const fn = leaf as unknown as {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n query: (i: unknown) => any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mutate: (i: unknown) => any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n subscribe: (i: unknown, push: unknown) => any\n }\n if (kind === 'mutation') return fn.mutate(input)\n if (kind === 'subscription') return fn.subscribe(input, push)\n return fn.query(input)\n }\n\n const addonPagesInterface = {\n listPages: (input?: unknown) => dispatch('addonPages', 'listPages', 'query', input),\n }\n\n const addonsInterface = {\n list: (input?: unknown) => dispatch('addons', 'list', 'query', input),\n getLogs: (input?: unknown) => dispatch('addons', 'getLogs', 'query', input),\n listPackages: (input?: unknown) => dispatch('addons', 'listPackages', 'query', input),\n installPackage: (input?: unknown) => dispatch('addons', 'installPackage', 'mutation', input),\n installFromWorkspace: (input?: unknown) => dispatch('addons', 'installFromWorkspace', 'mutation', input),\n isWorkspaceAvailable: (input?: unknown) => dispatch('addons', 'isWorkspaceAvailable', 'query', input),\n listWorkspacePackages: (input?: unknown) => dispatch('addons', 'listWorkspacePackages', 'query', input),\n uninstallPackage: (input?: unknown) => dispatch('addons', 'uninstallPackage', 'mutation', input),\n reloadPackages: (input?: unknown) => dispatch('addons', 'reloadPackages', 'mutation', input),\n searchAvailable: (input?: unknown) => dispatch('addons', 'searchAvailable', 'query', input),\n listUpdates: (input?: unknown) => dispatch('addons', 'listUpdates', 'query', input),\n updatePackage: (input?: unknown) => dispatch('addons', 'updatePackage', 'mutation', input),\n rollbackPackage: (input?: unknown) => dispatch('addons', 'rollbackPackage', 'mutation', input),\n forceRefresh: (input?: unknown) => dispatch('addons', 'forceRefresh', 'mutation', input),\n restartServer: (input?: unknown) => dispatch('addons', 'restartServer', 'mutation', input),\n getVersions: (input?: unknown) => dispatch('addons', 'getVersions', 'query', input),\n restartAddon: (input?: unknown) => dispatch('addons', 'restartAddon', 'mutation', input),\n retryLoad: (input?: unknown) => dispatch('addons', 'retryLoad', 'mutation', input),\n getAutoUpdateSettings: (input?: unknown) => dispatch('addons', 'getAutoUpdateSettings', 'query', input),\n setAutoUpdateSettings: (input?: unknown) => dispatch('addons', 'setAutoUpdateSettings', 'mutation', input),\n getAddonAutoUpdate: (input?: unknown) => dispatch('addons', 'getAddonAutoUpdate', 'query', input),\n setAddonAutoUpdate: (input?: unknown) => dispatch('addons', 'setAddonAutoUpdate', 'mutation', input),\n applyAutoUpdateToAll: (input?: unknown) => dispatch('addons', 'applyAutoUpdateToAll', 'mutation', input),\n custom: (input?: unknown) => dispatch('addons', 'custom', 'mutation', input),\n onAddonLogs: (input?: unknown, push?: unknown) => dispatch('addons', 'onAddonLogs', 'subscription', input, push),\n }\n\n const addonSettingsInterface = {\n getGlobalSettings: (input?: unknown) => dispatch('addonSettings', 'getGlobalSettings', 'query', input),\n updateGlobalSettings: (input?: unknown) => dispatch('addonSettings', 'updateGlobalSettings', 'mutation', input),\n }\n\n const addonWidgetsInterface = {\n listWidgets: (input?: unknown) => dispatch('addonWidgets', 'listWidgets', 'query', input),\n }\n\n const alertsInterface = {\n emit: (input?: unknown) => dispatch('alerts', 'emit', 'mutation', input),\n update: (input?: unknown) => dispatch('alerts', 'update', 'mutation', input),\n list: (input?: unknown) => dispatch('alerts', 'list', 'query', input),\n getUnreadCount: (input?: unknown) => dispatch('alerts', 'getUnreadCount', 'query', input),\n markRead: (input?: unknown) => dispatch('alerts', 'markRead', 'mutation', input),\n markAllRead: (input?: unknown) => dispatch('alerts', 'markAllRead', 'mutation', input),\n dismiss: (input?: unknown) => dispatch('alerts', 'dismiss', 'mutation', input),\n }\n\n const audioAnalyzerInterface = {\n analyseChunk: (input?: unknown) => dispatch('audioAnalyzer', 'analyseChunk', 'mutation', input),\n classify: (input?: unknown) => dispatch('audioAnalyzer', 'classify', 'query', input),\n isReady: (input?: unknown) => dispatch('audioAnalyzer', 'isReady', 'query', input),\n dispose: (input?: unknown) => dispatch('audioAnalyzer', 'dispose', 'mutation', input),\n reprobeAudioEngine: (input?: unknown) => dispatch('audioAnalyzer', 'reprobeAudioEngine', 'mutation', input),\n }\n\n const audioCodecInterface = {\n listSupportedCodecs: (input?: unknown) => dispatch('audioCodec', 'listSupportedCodecs', 'query', input),\n canHandle: (input?: unknown) => dispatch('audioCodec', 'canHandle', 'query', input),\n createDecodeSession: (input?: unknown) => dispatch('audioCodec', 'createDecodeSession', 'mutation', input),\n createEncodeSession: (input?: unknown) => dispatch('audioCodec', 'createEncodeSession', 'mutation', input),\n closeSession: (input?: unknown) => dispatch('audioCodec', 'closeSession', 'mutation', input),\n pushEncodedFrame: (input?: unknown) => dispatch('audioCodec', 'pushEncodedFrame', 'mutation', input),\n pullPcm: (input?: unknown) => dispatch('audioCodec', 'pullPcm', 'query', input),\n pushPcm: (input?: unknown) => dispatch('audioCodec', 'pushPcm', 'mutation', input),\n pullEncoded: (input?: unknown) => dispatch('audioCodec', 'pullEncoded', 'query', input),\n flushEncode: (input?: unknown) => dispatch('audioCodec', 'flushEncode', 'mutation', input),\n listActiveSessions: (input?: unknown) => dispatch('audioCodec', 'listActiveSessions', 'query', input),\n }\n\n const authenticationInterface = {\n listProviders: (input?: unknown) => dispatch('authentication', 'listProviders', 'query', input),\n setProviderEnabled: (input?: unknown) => dispatch('authentication', 'setProviderEnabled', 'mutation', input),\n }\n\n const backupInterface = {\n listDestinations: (input?: unknown) => dispatch('backup', 'listDestinations', 'query', input),\n trigger: (input?: unknown) => dispatch('backup', 'trigger', 'mutation', input),\n list: (input?: unknown) => dispatch('backup', 'list', 'query', input),\n listLocations: (input?: unknown) => dispatch('backup', 'listLocations', 'query', input),\n getEntries: (input?: unknown) => dispatch('backup', 'getEntries', 'query', input),\n restore: (input?: unknown) => dispatch('backup', 'restore', 'mutation', input),\n delete: (input?: unknown) => dispatch('backup', 'delete', 'mutation', input),\n listArchives: (input?: unknown) => dispatch('backup', 'listArchives', 'query', input),\n upsertDestinationPolicy: (input?: unknown) => dispatch('backup', 'upsertDestinationPolicy', 'mutation', input),\n previewSchedule: (input?: unknown) => dispatch('backup', 'previewSchedule', 'query', input),\n }\n\n const decoderInterface = {\n supportsCodec: (input?: unknown) => dispatch('decoder', 'supportsCodec', 'query', input),\n getInfo: (input?: unknown) => dispatch('decoder', 'getInfo', 'query', input),\n createSession: (input?: unknown) => dispatch('decoder', 'createSession', 'query', input),\n destroySession: (input?: unknown) => dispatch('decoder', 'destroySession', 'query', input),\n pushPacket: (input?: unknown) => dispatch('decoder', 'pushPacket', 'query', input),\n openStream: (input?: unknown) => dispatch('decoder', 'openStream', 'query', input),\n pullFrames: (input?: unknown) => dispatch('decoder', 'pullFrames', 'query', input),\n updateConfig: (input?: unknown) => dispatch('decoder', 'updateConfig', 'query', input),\n getStats: (input?: unknown) => dispatch('decoder', 'getStats', 'query', input),\n listActiveSessions: (input?: unknown) => dispatch('decoder', 'listActiveSessions', 'query', input),\n reprobeHwaccel: (input?: unknown) => dispatch('decoder', 'reprobeHwaccel', 'mutation', input),\n }\n\n const deviceManagerInterface = {\n allocateDeviceId: (input?: unknown) => dispatch('deviceManager', 'allocateDeviceId', 'mutation', input),\n registerDevice: (input?: unknown) => dispatch('deviceManager', 'registerDevice', 'mutation', input),\n removeDevice: (input?: unknown) => dispatch('deviceManager', 'removeDevice', 'mutation', input),\n persistConfig: (input?: unknown) => dispatch('deviceManager', 'persistConfig', 'mutation', input),\n listLocations: (input?: unknown) => dispatch('deviceManager', 'listLocations', 'query', input),\n addLocation: (input?: unknown) => dispatch('deviceManager', 'addLocation', 'mutation', input),\n removeLocation: (input?: unknown) => dispatch('deviceManager', 'removeLocation', 'mutation', input),\n listPersistedByAddon: (input?: unknown) => dispatch('deviceManager', 'listPersistedByAddon', 'query', input),\n listAll: (input?: unknown) => dispatch('deviceManager', 'listAll', 'query', input),\n getChildren: (input?: unknown) => dispatch('deviceManager', 'getChildren', 'query', input),\n listWrappersForCap: (input?: unknown) => dispatch('deviceManager', 'listWrappersForCap', 'query', input),\n listBindableCapsForDeviceType: (input?: unknown) => dispatch('deviceManager', 'listBindableCapsForDeviceType', 'query', input),\n discoverDevices: (input?: unknown) => dispatch('deviceManager', 'discoverDevices', 'mutation', input),\n adoptDevice: (input?: unknown) => dispatch('deviceManager', 'adoptDevice', 'mutation', input),\n getCreationSchema: (input?: unknown) => dispatch('deviceManager', 'getCreationSchema', 'query', input),\n createDevice: (input?: unknown) => dispatch('deviceManager', 'createDevice', 'mutation', input),\n testCreationField: (input?: unknown) => dispatch('deviceManager', 'testCreationField', 'mutation', input),\n }\n\n const deviceProviderInterface = {\n start: (input?: unknown) => dispatch('deviceProvider', 'start', 'mutation', input),\n stop: (input?: unknown) => dispatch('deviceProvider', 'stop', 'mutation', input),\n getStatus: (input?: unknown) => dispatch('deviceProvider', 'getStatus', 'query', input),\n getDevices: (input?: unknown) => dispatch('deviceProvider', 'getDevices', 'query', input),\n supportsDiscovery: (input?: unknown) => dispatch('deviceProvider', 'supportsDiscovery', 'query', input),\n discoverDevices: (input?: unknown) => dispatch('deviceProvider', 'discoverDevices', 'mutation', input),\n adoptDiscoveredDevice: (input?: unknown) => dispatch('deviceProvider', 'adoptDiscoveredDevice', 'mutation', input),\n supportsManualCreation: (input?: unknown) => dispatch('deviceProvider', 'supportsManualCreation', 'query', input),\n getChildCreationSchema: (input?: unknown) => dispatch('deviceProvider', 'getChildCreationSchema', 'query', input),\n createDevice: (input?: unknown) => dispatch('deviceProvider', 'createDevice', 'mutation', input),\n testCreationField: (input?: unknown) => dispatch('deviceProvider', 'testCreationField', 'mutation', input),\n }\n\n const deviceStateInterface = {\n getAllSnapshots: (input?: unknown) => dispatch('deviceState', 'getAllSnapshots', 'query', input),\n }\n\n const integrationsInterface = {\n list: (input?: unknown) => dispatch('integrations', 'list', 'query', input),\n get: (input?: unknown) => dispatch('integrations', 'get', 'query', input),\n getByAddonId: (input?: unknown) => dispatch('integrations', 'getByAddonId', 'query', input),\n create: (input?: unknown) => dispatch('integrations', 'create', 'mutation', input),\n update: (input?: unknown) => dispatch('integrations', 'update', 'mutation', input),\n delete: (input?: unknown) => dispatch('integrations', 'delete', 'mutation', input),\n getSettings: (input?: unknown) => dispatch('integrations', 'getSettings', 'query', input),\n setSettings: (input?: unknown) => dispatch('integrations', 'setSettings', 'mutation', input),\n getAvailableTypes: (input?: unknown) => dispatch('integrations', 'getAvailableTypes', 'query', input),\n testConnection: (input?: unknown) => dispatch('integrations', 'testConnection', 'mutation', input),\n }\n\n const localNetworkInterface = {\n list: (input?: unknown) => dispatch('localNetwork', 'list', 'query', input),\n getPreferred: (input?: unknown) => dispatch('localNetwork', 'getPreferred', 'query', input),\n getConnectionEndpoints: (input?: unknown) => dispatch('localNetwork', 'getConnectionEndpoints', 'query', input),\n getAllowedAddresses: (input?: unknown) => dispatch('localNetwork', 'getAllowedAddresses', 'query', input),\n setAllowedAddresses: (input?: unknown) => dispatch('localNetwork', 'setAllowedAddresses', 'mutation', input),\n resetAllowlistToBestMatch: (input?: unknown) => dispatch('localNetwork', 'resetAllowlistToBestMatch', 'mutation', input),\n }\n\n const meshNetworkInterface = {\n getStatus: (input?: unknown) => dispatch('meshNetwork', 'getStatus', 'query', input),\n join: (input?: unknown) => dispatch('meshNetwork', 'join', 'mutation', input),\n leave: (input?: unknown) => dispatch('meshNetwork', 'leave', 'mutation', input),\n listPeers: (input?: unknown) => dispatch('meshNetwork', 'listPeers', 'query', input),\n setPublicIngress: (input?: unknown) => dispatch('meshNetwork', 'setPublicIngress', 'mutation', input),\n setMeshIngress: (input?: unknown) => dispatch('meshNetwork', 'setMeshIngress', 'mutation', input),\n }\n\n const meshOrchestratorInterface = {\n listProviders: (input?: unknown) => dispatch('meshOrchestrator', 'listProviders', 'query', input),\n joinProvider: (input?: unknown) => dispatch('meshOrchestrator', 'joinProvider', 'mutation', input),\n leaveProvider: (input?: unknown) => dispatch('meshOrchestrator', 'leaveProvider', 'mutation', input),\n }\n\n const metricsProviderInterface = {\n collectSnapshot: (input?: unknown) => dispatch('metricsProvider', 'collectSnapshot', 'query', input),\n getCached: (input?: unknown) => dispatch('metricsProvider', 'getCached', 'query', input),\n getCurrent: (input?: unknown) => dispatch('metricsProvider', 'getCurrent', 'query', input),\n getDiskSpace: (input?: unknown) => dispatch('metricsProvider', 'getDiskSpace', 'query', input),\n getGpuInfo: (input?: unknown) => dispatch('metricsProvider', 'getGpuInfo', 'query', input),\n getCpuTemperature: (input?: unknown) => dispatch('metricsProvider', 'getCpuTemperature', 'query', input),\n getProcessStats: (input?: unknown) => dispatch('metricsProvider', 'getProcessStats', 'query', input),\n listAddonInstances: (input?: unknown) => dispatch('metricsProvider', 'listAddonInstances', 'query', input),\n getAddonStats: (input?: unknown) => dispatch('metricsProvider', 'getAddonStats', 'query', input),\n listNodeProcesses: (input?: unknown) => dispatch('metricsProvider', 'listNodeProcesses', 'query', input),\n killProcess: (input?: unknown) => dispatch('metricsProvider', 'killProcess', 'mutation', input),\n }\n\n const networkQualityInterface = {\n getAllStats: (input?: unknown) => dispatch('networkQuality', 'getAllStats', 'query', input),\n }\n\n const nodesInterface = {\n topology: (input?: unknown) => dispatch('nodes', 'topology', 'query', input),\n deployAddon: (input?: unknown) => dispatch('nodes', 'deployAddon', 'mutation', input),\n undeployAddon: (input?: unknown) => dispatch('nodes', 'undeployAddon', 'mutation', input),\n restartAddon: (input?: unknown) => dispatch('nodes', 'restartAddon', 'mutation', input),\n restartProcess: (input?: unknown) => dispatch('nodes', 'restartProcess', 'mutation', input),\n restartNode: (input?: unknown) => dispatch('nodes', 'restartNode', 'mutation', input),\n shutdownNode: (input?: unknown) => dispatch('nodes', 'shutdownNode', 'mutation', input),\n renameNode: (input?: unknown) => dispatch('nodes', 'renameNode', 'mutation', input),\n clusterAddonStatus: (input?: unknown) => dispatch('nodes', 'clusterAddonStatus', 'query', input),\n setProcessLogLevel: (input?: unknown) => dispatch('nodes', 'setProcessLogLevel', 'mutation', input),\n executeQuery: (input?: unknown) => dispatch('nodes', 'executeQuery', 'mutation', input),\n }\n\n const notificationOutputInterface = {\n send: (input?: unknown) => dispatch('notificationOutput', 'send', 'mutation', input),\n sendTest: (input?: unknown) => dispatch('notificationOutput', 'sendTest', 'mutation', input),\n }\n\n const pipelineExecutorInterface = {\n getAvailableEngines: (input?: unknown) => dispatch('pipelineExecutor', 'getAvailableEngines', 'query', input),\n getSelectedEngine: (input?: unknown) => dispatch('pipelineExecutor', 'getSelectedEngine', 'query', input),\n getDefaultSteps: (input?: unknown) => dispatch('pipelineExecutor', 'getDefaultSteps', 'query', input),\n reprobeEngine: (input?: unknown) => dispatch('pipelineExecutor', 'reprobeEngine', 'mutation', input),\n getVideoPipelineSteps: (input?: unknown) => dispatch('pipelineExecutor', 'getVideoPipelineSteps', 'query', input),\n setVideoPipelineSteps: (input?: unknown) => dispatch('pipelineExecutor', 'setVideoPipelineSteps', 'mutation', input),\n getSchema: (input?: unknown) => dispatch('pipelineExecutor', 'getSchema', 'query', input),\n getGlobalSteps: (input?: unknown) => dispatch('pipelineExecutor', 'getGlobalSteps', 'query', input),\n getGlobalPipelineConfig: (input?: unknown) => dispatch('pipelineExecutor', 'getGlobalPipelineConfig', 'query', input),\n getOrchestratorConfigSchema: (input?: unknown) => dispatch('pipelineExecutor', 'getOrchestratorConfigSchema', 'query', input),\n listTemplates: (input?: unknown) => dispatch('pipelineExecutor', 'listTemplates', 'query', input),\n saveTemplate: (input?: unknown) => dispatch('pipelineExecutor', 'saveTemplate', 'mutation', input),\n updateTemplate: (input?: unknown) => dispatch('pipelineExecutor', 'updateTemplate', 'mutation', input),\n deleteTemplate: (input?: unknown) => dispatch('pipelineExecutor', 'deleteTemplate', 'mutation', input),\n getCapabilities: (input?: unknown) => dispatch('pipelineExecutor', 'getCapabilities', 'query', input),\n getAddonModels: (input?: unknown) => dispatch('pipelineExecutor', 'getAddonModels', 'query', input),\n downloadModel: (input?: unknown) => dispatch('pipelineExecutor', 'downloadModel', 'mutation', input),\n deleteModel: (input?: unknown) => dispatch('pipelineExecutor', 'deleteModel', 'mutation', input),\n detect: (input?: unknown) => dispatch('pipelineExecutor', 'detect', 'query', input),\n cacheFrameInPool: (input?: unknown) => dispatch('pipelineExecutor', 'cacheFrameInPool', 'mutation', input),\n inferCached: (input?: unknown) => dispatch('pipelineExecutor', 'inferCached', 'mutation', input),\n uncacheFrame: (input?: unknown) => dispatch('pipelineExecutor', 'uncacheFrame', 'mutation', input),\n getEffectiveTuning: (input?: unknown) => dispatch('pipelineExecutor', 'getEffectiveTuning', 'query', input),\n listLoadedEngines: (input?: unknown) => dispatch('pipelineExecutor', 'listLoadedEngines', 'query', input),\n spinEngine: (input?: unknown) => dispatch('pipelineExecutor', 'spinEngine', 'mutation', input),\n killEngine: (input?: unknown) => dispatch('pipelineExecutor', 'killEngine', 'mutation', input),\n listReferenceImages: (input?: unknown) => dispatch('pipelineExecutor', 'listReferenceImages', 'query', input),\n getReferenceImage: (input?: unknown) => dispatch('pipelineExecutor', 'getReferenceImage', 'query', input),\n getReferenceAudioFiles: (input?: unknown) => dispatch('pipelineExecutor', 'getReferenceAudioFiles', 'query', input),\n getReferenceAudio: (input?: unknown) => dispatch('pipelineExecutor', 'getReferenceAudio', 'query', input),\n getAudioCapabilities: (input?: unknown) => dispatch('pipelineExecutor', 'getAudioCapabilities', 'query', input),\n runAudioTest: (input?: unknown) => dispatch('pipelineExecutor', 'runAudioTest', 'mutation', input),\n getDetectionConfigSchema: (input?: unknown) => dispatch('pipelineExecutor', 'getDetectionConfigSchema', 'query', input),\n }\n\n const pipelineOrchestratorInterface = {\n rebalance: (input?: unknown) => dispatch('pipelineOrchestrator', 'rebalance', 'mutation', input),\n getPipelineAssignments: (input?: unknown) => dispatch('pipelineOrchestrator', 'getPipelineAssignments', 'query', input),\n getAgentLoad: (input?: unknown) => dispatch('pipelineOrchestrator', 'getAgentLoad', 'query', input),\n getGlobalMetrics: (input?: unknown) => dispatch('pipelineOrchestrator', 'getGlobalMetrics', 'query', input),\n getCapabilityBindings: (input?: unknown) => dispatch('pipelineOrchestrator', 'getCapabilityBindings', 'query', input),\n setCapabilityBinding: (input?: unknown) => dispatch('pipelineOrchestrator', 'setCapabilityBinding', 'mutation', input),\n getDecoderAssignments: (input?: unknown) => dispatch('pipelineOrchestrator', 'getDecoderAssignments', 'query', input),\n getAudioNodeLoad: (input?: unknown) => dispatch('pipelineOrchestrator', 'getAudioNodeLoad', 'query', input),\n getAgentSettings: (input?: unknown) => dispatch('pipelineOrchestrator', 'getAgentSettings', 'query', input),\n listAgentSettings: (input?: unknown) => dispatch('pipelineOrchestrator', 'listAgentSettings', 'query', input),\n setAgentAddonDefaults: (input?: unknown) => dispatch('pipelineOrchestrator', 'setAgentAddonDefaults', 'mutation', input),\n removeAgentSettings: (input?: unknown) => dispatch('pipelineOrchestrator', 'removeAgentSettings', 'mutation', input),\n listTemplates: (input?: unknown) => dispatch('pipelineOrchestrator', 'listTemplates', 'query', input),\n saveTemplate: (input?: unknown) => dispatch('pipelineOrchestrator', 'saveTemplate', 'mutation', input),\n updateTemplate: (input?: unknown) => dispatch('pipelineOrchestrator', 'updateTemplate', 'mutation', input),\n deleteTemplate: (input?: unknown) => dispatch('pipelineOrchestrator', 'deleteTemplate', 'mutation', input),\n }\n\n const pipelineRunnerInterface = {\n attachCamera: (input?: unknown) => dispatch('pipelineRunner', 'attachCamera', 'mutation', input),\n reportMotion: (input?: unknown) => dispatch('pipelineRunner', 'reportMotion', 'mutation', input),\n getLocalLoad: (input?: unknown) => dispatch('pipelineRunner', 'getLocalLoad', 'query', input),\n getLocalMetrics: (input?: unknown) => dispatch('pipelineRunner', 'getLocalMetrics', 'query', input),\n getAllCameraMetrics: (input?: unknown) => dispatch('pipelineRunner', 'getAllCameraMetrics', 'query', input),\n getLocalCameras: (input?: unknown) => dispatch('pipelineRunner', 'getLocalCameras', 'query', input),\n }\n\n const platformProbeInterface = {\n getCapabilities: (input?: unknown) => dispatch('platformProbe', 'getCapabilities', 'query', input),\n getHardware: (input?: unknown) => dispatch('platformProbe', 'getHardware', 'query', input),\n resolveInferenceConfig: (input?: unknown) => dispatch('platformProbe', 'resolveInferenceConfig', 'query', input),\n resolveHwAccel: (input?: unknown) => dispatch('platformProbe', 'resolveHwAccel', 'query', input),\n }\n\n const recordingEngineInterface = {\n getStatus: (input?: unknown) => dispatch('recordingEngine', 'getStatus', 'query', input),\n enable: (input?: unknown) => dispatch('recordingEngine', 'enable', 'mutation', input),\n disable: (input?: unknown) => dispatch('recordingEngine', 'disable', 'mutation', input),\n getConfig: (input?: unknown) => dispatch('recordingEngine', 'getConfig', 'query', input),\n updateConfig: (input?: unknown) => dispatch('recordingEngine', 'updateConfig', 'mutation', input),\n getPlaylist: (input?: unknown) => dispatch('recordingEngine', 'getPlaylist', 'query', input),\n getThumbnail: (input?: unknown) => dispatch('recordingEngine', 'getThumbnail', 'query', input),\n getSegments: (input?: unknown) => dispatch('recordingEngine', 'getSegments', 'query', input),\n getAvailability: (input?: unknown) => dispatch('recordingEngine', 'getAvailability', 'query', input),\n estimateStorage: (input?: unknown) => dispatch('recordingEngine', 'estimateStorage', 'query', input),\n estimateGlobalStorage: (input?: unknown) => dispatch('recordingEngine', 'estimateGlobalStorage', 'query', input),\n getStorageUsage: (input?: unknown) => dispatch('recordingEngine', 'getStorageUsage', 'query', input),\n setPolicy: (input?: unknown) => dispatch('recordingEngine', 'setPolicy', 'mutation', input),\n getPolicy: (input?: unknown) => dispatch('recordingEngine', 'getPolicy', 'query', input),\n getRetentionConfig: (input?: unknown) => dispatch('recordingEngine', 'getRetentionConfig', 'query', input),\n updateRetentionConfig: (input?: unknown) => dispatch('recordingEngine', 'updateRetentionConfig', 'mutation', input),\n getMotionStats: (input?: unknown) => dispatch('recordingEngine', 'getMotionStats', 'query', input),\n }\n\n const remoteAccessInterface = {\n listProviders: (input?: unknown) => dispatch('remoteAccess', 'listProviders', 'query', input),\n startProvider: (input?: unknown) => dispatch('remoteAccess', 'startProvider', 'mutation', input),\n stopProvider: (input?: unknown) => dispatch('remoteAccess', 'stopProvider', 'mutation', input),\n }\n\n const settingsStoreInterface = {\n get: (input?: unknown) => dispatch('settingsStore', 'get', 'query', input),\n set: (input?: unknown) => dispatch('settingsStore', 'set', 'mutation', input),\n query: (input?: unknown) => dispatch('settingsStore', 'query', 'query', input),\n insert: (input?: unknown) => dispatch('settingsStore', 'insert', 'mutation', input),\n update: (input?: unknown) => dispatch('settingsStore', 'update', 'mutation', input),\n delete: (input?: unknown) => dispatch('settingsStore', 'delete', 'mutation', input),\n count: (input?: unknown) => dispatch('settingsStore', 'count', 'query', input),\n isEmpty: (input?: unknown) => dispatch('settingsStore', 'isEmpty', 'query', input),\n declareCollection: (input?: unknown) => dispatch('settingsStore', 'declareCollection', 'mutation', input),\n }\n\n const storageInterface = {\n resolve: (input?: unknown) => dispatch('storage', 'resolve', 'query', input),\n write: (input?: unknown) => dispatch('storage', 'write', 'mutation', input),\n read: (input?: unknown) => dispatch('storage', 'read', 'query', input),\n exists: (input?: unknown) => dispatch('storage', 'exists', 'query', input),\n list: (input?: unknown) => dispatch('storage', 'list', 'query', input),\n delete: (input?: unknown) => dispatch('storage', 'delete', 'mutation', input),\n getAvailableSpace: (input?: unknown) => dispatch('storage', 'getAvailableSpace', 'query', input),\n beginUpload: (input?: unknown) => dispatch('storage', 'beginUpload', 'mutation', input),\n writeChunk: (input?: unknown) => dispatch('storage', 'writeChunk', 'mutation', input),\n finalizeUpload: (input?: unknown) => dispatch('storage', 'finalizeUpload', 'mutation', input),\n abortUpload: (input?: unknown) => dispatch('storage', 'abortUpload', 'mutation', input),\n beginDownload: (input?: unknown) => dispatch('storage', 'beginDownload', 'mutation', input),\n readChunk: (input?: unknown) => dispatch('storage', 'readChunk', 'query', input),\n endDownload: (input?: unknown) => dispatch('storage', 'endDownload', 'mutation', input),\n listLocations: (input?: unknown) => dispatch('storage', 'listLocations', 'query', input),\n getDefaultLocation: (input?: unknown) => dispatch('storage', 'getDefaultLocation', 'query', input),\n upsertLocation: (input?: unknown) => dispatch('storage', 'upsertLocation', 'mutation', input),\n deleteLocation: (input?: unknown) => dispatch('storage', 'deleteLocation', 'mutation', input),\n testLocation: (input?: unknown) => dispatch('storage', 'testLocation', 'query', input),\n listProviders: (input?: unknown) => dispatch('storage', 'listProviders', 'query', input),\n testConfig: (input?: unknown) => dispatch('storage', 'testConfig', 'query', input),\n }\n\n const streamBrokerInterface = {\n listAllCameraStreams: (input?: unknown) => dispatch('streamBroker', 'listAllCameraStreams', 'query', input),\n listAllProfileSlots: (input?: unknown) => dispatch('streamBroker', 'listAllProfileSlots', 'query', input),\n getBrokerStats: (input?: unknown) => dispatch('streamBroker', 'getBrokerStats', 'query', input),\n listClients: (input?: unknown) => dispatch('streamBroker', 'listClients', 'query', input),\n killClient: (input?: unknown) => dispatch('streamBroker', 'killClient', 'mutation', input),\n getStreamUrl: (input?: unknown) => dispatch('streamBroker', 'getStreamUrl', 'query', input),\n getBroker: (input?: unknown) => dispatch('streamBroker', 'getBroker', 'query', input),\n setPreBufferDuration: (input?: unknown) => dispatch('streamBroker', 'setPreBufferDuration', 'mutation', input),\n getPreBufferInfo: (input?: unknown) => dispatch('streamBroker', 'getPreBufferInfo', 'query', input),\n getRtspPort: (input?: unknown) => dispatch('streamBroker', 'getRtspPort', 'query', input),\n getAllRtspEntries: (input?: unknown) => dispatch('streamBroker', 'getAllRtspEntries', 'query', input),\n getRtspEntry: (input?: unknown) => dispatch('streamBroker', 'getRtspEntry', 'query', input),\n regenerateRtspToken: (input?: unknown) => dispatch('streamBroker', 'regenerateRtspToken', 'mutation', input),\n setRtspEnabled: (input?: unknown) => dispatch('streamBroker', 'setRtspEnabled', 'mutation', input),\n isRtspEnabled: (input?: unknown) => dispatch('streamBroker', 'isRtspEnabled', 'query', input),\n }\n\n const systemInterface = {\n info: (input?: unknown) => dispatch('system', 'info', 'query', input),\n health: (input?: unknown) => dispatch('system', 'health', 'query', input),\n featureFlags: (input?: unknown) => dispatch('system', 'featureFlags', 'query', input),\n networkAddresses: (input?: unknown) => dispatch('system', 'networkAddresses', 'query', input),\n getRetentionConfig: (input?: unknown) => dispatch('system', 'getRetentionConfig', 'query', input),\n setRetentionConfig: (input?: unknown) => dispatch('system', 'setRetentionConfig', 'mutation', input),\n forceRetentionCleanup: (input?: unknown) => dispatch('system', 'forceRetentionCleanup', 'mutation', input),\n }\n\n const toastInterface = {\n onToast: (input?: unknown, push?: unknown) => dispatch('toast', 'onToast', 'subscription', input, push),\n }\n\n const turnOrchestratorInterface = {\n listProviders: (input?: unknown) => dispatch('turnOrchestrator', 'listProviders', 'query', input),\n getAllServers: (input?: unknown) => dispatch('turnOrchestrator', 'getAllServers', 'query', input),\n setProviderEnabled: (input?: unknown) => dispatch('turnOrchestrator', 'setProviderEnabled', 'mutation', input),\n }\n\n const turnProviderInterface = {\n getTurnServers: (input?: unknown) => dispatch('turnProvider', 'getTurnServers', 'query', input),\n }\n\n const userManagementInterface = {\n listUsers: (input?: unknown) => dispatch('userManagement', 'listUsers', 'query', input),\n createUser: (input?: unknown) => dispatch('userManagement', 'createUser', 'mutation', input),\n updateUser: (input?: unknown) => dispatch('userManagement', 'updateUser', 'mutation', input),\n deleteUser: (input?: unknown) => dispatch('userManagement', 'deleteUser', 'mutation', input),\n resetPassword: (input?: unknown) => dispatch('userManagement', 'resetPassword', 'mutation', input),\n validateCredentials: (input?: unknown) => dispatch('userManagement', 'validateCredentials', 'mutation', input),\n listApiKeys: (input?: unknown) => dispatch('userManagement', 'listApiKeys', 'query', input),\n createApiKey: (input?: unknown) => dispatch('userManagement', 'createApiKey', 'mutation', input),\n revokeApiKey: (input?: unknown) => dispatch('userManagement', 'revokeApiKey', 'mutation', input),\n validateApiKey: (input?: unknown) => dispatch('userManagement', 'validateApiKey', 'mutation', input),\n createScopedToken: (input?: unknown) => dispatch('userManagement', 'createScopedToken', 'mutation', input),\n revokeScopedToken: (input?: unknown) => dispatch('userManagement', 'revokeScopedToken', 'mutation', input),\n validateScopedToken: (input?: unknown) => dispatch('userManagement', 'validateScopedToken', 'query', input),\n listScopedTokens: (input?: unknown) => dispatch('userManagement', 'listScopedTokens', 'query', input),\n }\n\n return {\n addonPages: addonPagesInterface,\n addons: addonsInterface,\n addonSettings: addonSettingsInterface,\n addonWidgets: addonWidgetsInterface,\n alerts: alertsInterface,\n audioAnalyzer: audioAnalyzerInterface,\n audioCodec: audioCodecInterface,\n authentication: authenticationInterface,\n backup: backupInterface,\n decoder: decoderInterface,\n deviceManager: deviceManagerInterface,\n deviceProvider: deviceProviderInterface,\n deviceState: deviceStateInterface,\n integrations: integrationsInterface,\n localNetwork: localNetworkInterface,\n meshNetwork: meshNetworkInterface,\n meshOrchestrator: meshOrchestratorInterface,\n metricsProvider: metricsProviderInterface,\n networkQuality: networkQualityInterface,\n nodes: nodesInterface,\n notificationOutput: notificationOutputInterface,\n pipelineExecutor: pipelineExecutorInterface,\n pipelineOrchestrator: pipelineOrchestratorInterface,\n pipelineRunner: pipelineRunnerInterface,\n platformProbe: platformProbeInterface,\n recordingEngine: recordingEngineInterface,\n remoteAccess: remoteAccessInterface,\n settingsStore: settingsStoreInterface,\n storage: storageInterface,\n streamBroker: streamBrokerInterface,\n system: systemInterface,\n toast: toastInterface,\n turnOrchestrator: turnOrchestratorInterface,\n turnProvider: turnProviderInterface,\n userManagement: userManagementInterface,\n }\n}\n","/**\n * SystemMirror — system-wide reactive view of every device, like\n * Scrypted's `systemManager.getDeviceById`. One warm-boot fetches the\n * full bindings + runtime-state snapshot + device metadata; live\n * `device.state-changed`, `capability.binding-changed`,\n * `device.registered`, `device.unregistered`, and `device.updated`\n * events keep the in-memory mirrors fresh. Subsequent\n * `getDeviceById(id)` calls are sync.\n *\n * Renamed from `SystemManager` (and its file `system-manager.ts`) when\n * the SDK introduced a top-level `System` facade. The cluster-wide\n * mirror is one of `System`'s collaborators — calling it a \"mirror\"\n * disambiguates from the new public class. The old export name is\n * still available as a deprecated alias from this file.\n *\n * Cap-agnostic by construction: the state mirror is `Map<deviceId,\n * Map<capName, slice>>` and the proxies are produced by the\n * codegen'd `createDeviceProxy` we already use everywhere — so a\n * brand-new cap with `runtimeState:` lights up here without a single\n * line of additional code.\n *\n * Two transports today, same shape:\n * - tRPC client (browser, Electron, SDK consumers).\n * - In-process AddonApi (server REPL, addons that want a global view).\n *\n * Boot:\n *\n * const sm = new SystemMirror(api)\n * await sm.init()\n *\n * const dev = sm.getDeviceById(8) // sync\n * dev?.state.battery.value?.sleeping // sync read from mirror\n * await dev?.snapshot?.getSnapshot({...}) // method dispatch via cap-router\n *\n * sm.query({ addonId: 'reolink', online: true })\n * sm.whereState('battery', s => s.percentage < 20)\n * sm.listenCap('motion', (id, slice) => …)\n * await sm.waitForState(8, 'battery', s => !s.sleeping, 30_000)\n *\n * Lifecycle: caller is responsible for `dispose()` when done — closes\n * the live event subscriptions.\n */\n\nimport type { DeviceBinding } from './device-binding.js'\nimport { createDeviceProxy, type DeviceProxy } from '../generated/device-proxy.js'\nimport type { AddonApi } from '../generated/addon-api.js'\nimport type { DeviceInfo } from '../capabilities/device-manager.cap.js'\nimport { DeviceType } from './device-type.js'\nimport { createMirrorSource, type SliceHandleApi, type SliceHandleSource } from './device-state-handle.js'\n\nconst STATE_CHANGED_CATEGORY = 'device.state-changed'\nconst BINDING_CHANGED_CATEGORY = 'capability.binding-changed'\nconst DEVICE_REGISTERED_CATEGORY = 'device.registered'\nconst DEVICE_UNREGISTERED_CATEGORY = 'device.unregistered'\nconst DEVICE_UPDATED_CATEGORY = 'device.updated'\n\n/**\n * Minimal subset of `AddonApi` the SystemMirror needs.\n */\nexport interface SystemMirrorApi extends SliceHandleApi {\n readonly deviceManager: {\n readonly getAllBindings: {\n query(input: Record<string, never>): Promise<ReadonlyArray<DeviceBinding>>\n }\n readonly listAll: {\n query(input: { addonId?: string }): Promise<ReadonlyArray<DeviceInfo>>\n }\n }\n readonly deviceState: SliceHandleApi['deviceState'] & {\n readonly getAllSnapshots: {\n query(input: Record<string, never>): Promise<Record<string, Record<string, Record<string, unknown>>>>\n }\n }\n}\n\nexport type SystemMirrorListener = (\n deviceId: number,\n capName: string,\n slice: Record<string, unknown> | undefined,\n) => void\n\nexport type DeviceCapListener = (\n deviceId: number,\n slice: Record<string, unknown> | undefined,\n) => void\n\nexport type DeviceLifecycleListener = (\n deviceId: number,\n info: DeviceInfo | null,\n) => void\n\n/**\n * Match shape — `string` is exact match, `RegExp` is regex match,\n * `{ contains }` is case-insensitive substring, `{ exact }` is the\n * verbose form of plain string. Useful for `query({ name: ... })`\n * where the same field accepts multiple match modes.\n */\nexport type StringMatch =\n | string\n | RegExp\n | { readonly exact: string }\n | { readonly contains: string }\n\n/**\n * Filter set for `query()`. All fields are optional and ANDed\n * together. Array values are ORed within a single field\n * (`addonId: ['a', 'b']` matches devices owned by either). The\n * `where` escape hatch runs LAST so the structured filters do the\n * cheap rejection first.\n */\nexport interface DeviceQueryFilters {\n /** Match by exact deviceId. Single value or array (OR'd). */\n readonly id?: number | readonly number[]\n /** Match by exact stableId. Single value or array. */\n readonly stableId?: string | readonly string[]\n /** Match by owning addonId. Single value or array. */\n readonly addonId?: string | readonly string[]\n /** Match by `DeviceType` enum value. Single value or array. */\n readonly type?: DeviceType | readonly DeviceType[]\n /** Match devices that bind ALL listed caps. Single capName or array. */\n readonly caps?: string | readonly string[]\n /** Match devices that bind ANY of the listed caps. Mutually exclusive\n * with `caps`. */\n readonly anyCap?: string | readonly string[]\n /** Match by device name. */\n readonly name?: StringMatch\n /** Match the `online` flag. */\n readonly online?: boolean\n /** Match by parent device id. Pass `null` for top-level devices. */\n readonly parentDeviceId?: number | null\n /** Match devices that expose a feature (e.g. `'BatteryOperated'`). */\n readonly feature?: string | readonly string[]\n /** Match `isCamera` flag. */\n readonly isCamera?: boolean\n /** Custom predicate. Runs last; receives both metadata and proxy. */\n readonly where?: (info: DeviceInfo, proxy: DeviceProxy) => boolean\n}\n\nexport class SystemMirror {\n /** deviceId → capName → slice (push-driven mirror). */\n private readonly stateMirror: Map<number, Map<string, Record<string, unknown>>> = new Map()\n /** deviceId → binding (warm-boot + binding-changed events). */\n private readonly bindings: Map<number, DeviceBinding> = new Map()\n /** deviceId → metadata (warm-boot + lifecycle events). */\n private readonly devices: Map<number, DeviceInfo> = new Map()\n /** Listener map for individual `(deviceId, capName)` watches —\n * shared with every `MirrorSource` we hand out. */\n private readonly handleListeners = new Map<string, Set<(slice: unknown | undefined) => void>>()\n /** Global state-change listeners registered via `listen()`. */\n private readonly globalStateListeners = new Set<SystemMirrorListener>()\n /** Per-cap state listeners registered via `listenCap(capName, …)`. */\n private readonly capListeners = new Map<string, Set<DeviceCapListener>>()\n /** Per-device state listeners registered via `listenDevice(id, …)`. */\n private readonly deviceListeners = new Map<number, Set<SystemMirrorListener>>()\n /** Lifecycle listeners. */\n private readonly addedListeners = new Set<DeviceLifecycleListener>()\n private readonly removedListeners = new Set<DeviceLifecycleListener>()\n /** waitForState pending requests — keyed by an internal counter. */\n private readonly pendingWaits = new Set<{ check: () => boolean; resolve: () => void }>()\n /** Memoised state source — every proxy shares it. */\n private readonly stateSource: SliceHandleSource\n /** Live event bridges — closed in `dispose()`. */\n private bridges: Array<{ unsubscribe: () => void }> = []\n private initialized = false\n private initPromise: Promise<void> | null = null\n\n constructor(private readonly api: SystemMirrorApi) {\n this.stateSource = createMirrorSource(this.stateMirror, this.handleListeners, this.api)\n }\n\n // ── Lifecycle ──────────────────────────────────────────────────────\n\n /**\n * Warm-boot: three round-trips (bindings + snapshots + listAll) and\n * then the live event subscriptions. Idempotent — concurrent callers\n * await the same in-flight Promise.\n *\n * Each warm-boot query is wrapped in a 15s timeout so a missing\n * endpoint or stuck broker poll surfaces as a clear error instead\n * of silently hanging the caller forever. The default is generous\n * enough for cluster-wide setups but short enough for ops to\n * notice and fix the underlying transport issue.\n */\n async init(timeoutMs: number = 15_000): Promise<void> {\n if (this.initialized) return\n if (this.initPromise) return this.initPromise\n\n this.initPromise = (async () => {\n const withTimeout = <T>(p: Promise<T>, label: string): Promise<T> => {\n if (!Number.isFinite(timeoutMs)) return p\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(\n () => reject(new Error(`SystemMirror.init: '${label}' timed out after ${timeoutMs}ms`)),\n timeoutMs,\n )\n p.then(\n (v) => { clearTimeout(timer); resolve(v) },\n (err) => { clearTimeout(timer); reject(err as Error) },\n )\n })\n }\n\n const [allBindings, allSnapshots, allDevices] = await Promise.all([\n withTimeout(this.api.deviceManager.getAllBindings.query({}), 'deviceManager.getAllBindings'),\n withTimeout(this.api.deviceState.getAllSnapshots.query({}), 'deviceState.getAllSnapshots'),\n withTimeout(this.api.deviceManager.listAll.query({}), 'deviceManager.listAll'),\n ])\n\n for (const b of allBindings) this.bindings.set(b.deviceId, b)\n\n for (const [deviceIdStr, perCap] of Object.entries(allSnapshots)) {\n const deviceId = Number(deviceIdStr)\n if (!Number.isFinite(deviceId)) continue\n const capMap = new Map<string, Record<string, unknown>>()\n for (const [capName, slice] of Object.entries(perCap)) capMap.set(capName, slice)\n this.stateMirror.set(deviceId, capMap)\n }\n\n for (const info of allDevices) this.devices.set(info.id, info)\n\n // Subscriptions are best-effort — `live.onEvent` may not be\n // wired in some environments (e.g. when the api is a partial\n // adapter). Catch errors so init still completes; the mirror\n // just stops getting live updates instead of hanging the\n // entire REPL/SDK boot.\n try {\n this.subscribeBus()\n } catch (err) {\n // Swallow — `subscribeBus` itself is safe (it checks\n // `api.live?.onEvent`), but defend against transport\n // wrappers that throw on subscribe.\n void err\n }\n this.initialized = true\n })()\n\n try {\n await this.initPromise\n } finally {\n this.initPromise = null\n }\n }\n\n /** True after `init()` resolves. */\n isReady(): boolean {\n return this.initialized\n }\n\n /** Promise that resolves once `init()` has completed. */\n async awaitReady(): Promise<void> {\n if (this.initialized) return\n if (this.initPromise) return this.initPromise\n return this.init()\n }\n\n /**\n * Tear down the event bridges + clear listener registries. Existing\n * proxy references become inert (slice reads keep working off the\n * stale mirror, but no further updates arrive).\n */\n dispose(): void {\n for (const b of this.bridges) {\n try { b.unsubscribe() } catch { /* ignore */ }\n }\n this.bridges = []\n this.handleListeners.clear()\n this.globalStateListeners.clear()\n this.capListeners.clear()\n this.deviceListeners.clear()\n this.addedListeners.clear()\n this.removedListeners.clear()\n this.pendingWaits.clear()\n this.initialized = false\n }\n\n // ── Lookup ─────────────────────────────────────────────────────────\n\n /** Sync lookup by numeric id. `null` if unknown. */\n getDeviceById(deviceId: number): DeviceProxy | null {\n const binding = this.bindings.get(deviceId)\n if (!binding) return null\n return createDeviceProxy(this.api as AddonApi, binding, { stateSource: this.stateSource })\n }\n\n /** Sync lookup by display name (exact match). `null` if unknown. */\n getDeviceByName(name: string): DeviceProxy | null {\n for (const info of this.devices.values()) {\n if (info.name === name) return this.getDeviceById(info.id)\n }\n return null\n }\n\n /** Sync lookup by stableId (the addon-scoped external id). */\n getDeviceByStableId(stableId: string): DeviceProxy | null {\n for (const info of this.devices.values()) {\n if (info.stableId === stableId) return this.getDeviceById(info.id)\n }\n return null\n }\n\n /** All devices owned by an addon. */\n getDevicesByAddon(addonId: string): readonly DeviceProxy[] {\n return this.query({ addonId })\n }\n\n /** All devices of a given `DeviceType`. */\n getDevicesByType(type: DeviceType): readonly DeviceProxy[] {\n return this.query({ type })\n }\n\n /**\n * Live metadata snapshot for a device. Same shape as\n * `deviceManager.getDevice(id)`; sync from the mirror.\n */\n getDeviceInfo(deviceId: number): DeviceInfo | null {\n return this.devices.get(deviceId) ?? null\n }\n\n /** Snapshot of every known device as a typed proxy. */\n getAllDevices(): readonly DeviceProxy[] {\n const out: DeviceProxy[] = []\n for (const id of this.bindings.keys()) {\n const proxy = this.getDeviceById(id)\n if (proxy) out.push(proxy)\n }\n return out\n }\n\n /** Filter devices by cap presence — shorthand for `query({caps: capName})`. */\n filterByCap(capName: string): readonly DeviceProxy[] {\n return this.query({ caps: capName })\n }\n\n // ── Unified query ──────────────────────────────────────────────────\n\n /**\n * Filter the fleet against a structured filter set. All fields ANDed,\n * arrays ORed within a single field. Returns proxies — chain with\n * cap-method calls or state reads as needed.\n *\n * Examples:\n *\n * sm.query({ addonId: 'reolink', online: true })\n * sm.query({ type: DeviceType.Camera, caps: ['snapshot', 'motion'] })\n * sm.query({ id: [1, 2, 3] })\n * sm.query({ name: { contains: 'sala' } })\n * sm.query({ name: /^cam-\\d+$/ })\n * sm.query({ parentDeviceId: 8 }) // accessories of cam 8\n * sm.query({ where: (info, dev) => dev.state.battery.value?.sleeping })\n */\n query(filters: DeviceQueryFilters = {}): readonly DeviceProxy[] {\n const out: DeviceProxy[] = []\n for (const [deviceId, binding] of this.bindings) {\n const info = this.devices.get(deviceId)\n // Most filters need DeviceInfo. If we don't have it, only `id` /\n // `caps` / `anyCap` filters can resolve — for everything else\n // skip the device. (This window is brief: warm-boot races\n // bindings vs metadata for a few ms.)\n if (filters.id !== undefined && !inSet(deviceId, filters.id)) continue\n if (filters.stableId !== undefined) {\n if (!info) continue\n if (!inSet(info.stableId, filters.stableId)) continue\n }\n if (filters.addonId !== undefined) {\n if (!info) continue\n if (!inSet(info.addonId, filters.addonId)) continue\n }\n if (filters.type !== undefined) {\n if (!info) continue\n if (!inSet(info.type, filters.type)) continue\n }\n if (filters.caps !== undefined) {\n const required = toArray(filters.caps)\n const bound = new Set(binding.entries.map((e) => e.capName))\n if (!required.every((c) => bound.has(c))) continue\n }\n if (filters.anyCap !== undefined) {\n const wanted = toArray(filters.anyCap)\n const bound = new Set(binding.entries.map((e) => e.capName))\n if (!wanted.some((c) => bound.has(c))) continue\n }\n if (filters.name !== undefined) {\n if (!info) continue\n if (!matchesString(info.name, filters.name)) continue\n }\n if (filters.online !== undefined) {\n if (!info) continue\n if (info.online !== filters.online) continue\n }\n if (filters.parentDeviceId !== undefined) {\n if (!info) continue\n const want = filters.parentDeviceId\n const got = info.parentDeviceId ?? null\n if (got !== want) continue\n }\n if (filters.feature !== undefined) {\n if (!info) continue\n const want = toArray(filters.feature)\n if (!want.every((f) => info.features.includes(f))) continue\n }\n if (filters.isCamera !== undefined) {\n if (!info) continue\n if (info.isCamera !== filters.isCamera) continue\n }\n const proxy = this.getDeviceById(deviceId)\n if (!proxy) continue\n if (filters.where && info) {\n if (!filters.where(info, proxy)) continue\n } else if (filters.where && !info) {\n continue\n }\n out.push(proxy)\n }\n return out\n }\n\n // ── State-driven queries ───────────────────────────────────────────\n\n /**\n * Return every proxy whose runtime-state slice for `capName` matches\n * `predicate`. Devices without the cap or without a slice are\n * skipped. The predicate receives a typed slice reference — pass\n * the typed cap (e.g. `'battery'`) and TypeScript widens to\n * `Record<string, unknown>`; cast inside the predicate body if the\n * caller knows the cap shape.\n */\n whereState<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n predicate: (slice: T, deviceId: number) => boolean,\n ): readonly DeviceProxy[] {\n const out: DeviceProxy[] = []\n for (const [deviceId, perCap] of this.stateMirror) {\n const slice = perCap.get(capName) as T | undefined\n if (!slice) continue\n if (!predicate(slice, deviceId)) continue\n const proxy = this.getDeviceById(deviceId)\n if (proxy) out.push(proxy)\n }\n return out\n }\n\n /** Map every device's slice for `capName` to a derived value. Devices\n * without the cap or without a slice are skipped. */\n mapState<T extends Record<string, unknown> = Record<string, unknown>, R = unknown>(\n capName: string,\n mapper: (slice: T, deviceId: number) => R,\n ): readonly R[] {\n const out: R[] = []\n for (const [deviceId, perCap] of this.stateMirror) {\n const slice = perCap.get(capName) as T | undefined\n if (!slice) continue\n out.push(mapper(slice, deviceId))\n }\n return out\n }\n\n /** First device whose slice matches `predicate`, or null. */\n findState<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n predicate: (slice: T, deviceId: number) => boolean,\n ): DeviceProxy | null {\n for (const [deviceId, perCap] of this.stateMirror) {\n const slice = perCap.get(capName) as T | undefined\n if (!slice) continue\n if (!predicate(slice, deviceId)) continue\n return this.getDeviceById(deviceId)\n }\n return null\n }\n\n /** Count devices that bind a cap. Faster than `filterByCap(...).length`. */\n countByCap(capName: string): number {\n let n = 0\n for (const binding of this.bindings.values()) {\n if (binding.entries.some((e) => e.capName === capName)) n++\n }\n return n\n }\n\n /** Count devices whose slice for `capName` matches `predicate`. */\n countByState<T extends Record<string, unknown> = Record<string, unknown>>(\n capName: string,\n predicate: (slice: T, deviceId: number) => boolean,\n ): number {\n let n = 0\n for (const [deviceId, perCap] of this.stateMirror) {\n const slice = perCap.get(capName) as T | undefined\n if (!slice) continue\n if (predicate(slice, deviceId)) n++\n }\n return n\n }\n\n // ── Listeners ──────────────────────────────────────────────────────\n\n /**\n * Global listener — fires for every `device.state-changed` event the\n * mirror absorbs.\n */\n listen(cb: SystemMirrorListener): () => void {\n this.globalStateListeners.add(cb)\n return () => { this.globalStateListeners.delete(cb) }\n }\n\n /**\n * Per-cap listener — fires only for state changes on `capName`,\n * across every device. The callback receives the deviceId so the\n * caller can route.\n */\n listenCap(capName: string, cb: DeviceCapListener): () => void {\n let set = this.capListeners.get(capName)\n if (!set) { set = new Set(); this.capListeners.set(capName, set) }\n set.add(cb)\n return () => {\n set!.delete(cb)\n if (set!.size === 0) this.capListeners.delete(capName)\n }\n }\n\n /**\n * Per-device listener — fires for every cap change on `deviceId`.\n */\n listenDevice(deviceId: number, cb: SystemMirrorListener): () => void {\n let set = this.deviceListeners.get(deviceId)\n if (!set) { set = new Set(); this.deviceListeners.set(deviceId, set) }\n set.add(cb)\n return () => {\n set!.delete(cb)\n if (set!.size === 0) this.deviceListeners.delete(deviceId)\n }\n }\n\n /** Fires when `device.registered` lands. Receives the new metadata. */\n onDeviceAdded(cb: DeviceLifecycleListener): () => void {\n this.addedListeners.add(cb)\n return () => { this.addedListeners.delete(cb) }\n }\n\n /** Fires when `device.unregistered` lands. `info` is the LAST-known\n * metadata (or null if the device was never seen). */\n onDeviceRemoved(cb: DeviceLifecycleListener): () => void {\n this.removedListeners.add(cb)\n return () => { this.removedListeners.delete(cb) }\n }\n\n // ── Wait primitives ────────────────────────────────────────────────\n\n /**\n * Resolve when `predicate` over the runtime-state slice for\n * `(deviceId, capName)` becomes true. Resolves immediately if the\n * current slice already matches. Rejects with `Error('timeout')`\n * after `timeoutMs` (default 30s; pass `Infinity` to wait forever).\n *\n * Returns the matching slice — caller can read it directly without\n * a second mirror lookup.\n */\n waitForState<T extends Record<string, unknown> = Record<string, unknown>>(\n deviceId: number,\n capName: string,\n predicate: (slice: T) => boolean,\n timeoutMs: number = 30_000,\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const check = (): T | null => {\n const slice = this.stateMirror.get(deviceId)?.get(capName) as T | undefined\n if (slice && predicate(slice)) return slice\n return null\n }\n const initial = check()\n if (initial) {\n resolve(initial)\n return\n }\n\n let timer: ReturnType<typeof setTimeout> | null = null\n const off = this.listenDevice(deviceId, (_id, cap, slice) => {\n if (cap !== capName) return\n if (!slice) return\n if (predicate(slice as T)) {\n if (timer) clearTimeout(timer)\n off()\n resolve(slice as T)\n }\n })\n if (Number.isFinite(timeoutMs)) {\n timer = setTimeout(() => {\n off()\n reject(new Error(`waitForState timed out after ${timeoutMs}ms (deviceId=${deviceId}, capName=${capName})`))\n }, timeoutMs)\n }\n })\n }\n\n /**\n * Resolve when a device with `deviceId` becomes available (a\n * binding exists). Resolves immediately if already known. Rejects\n * with timeout.\n */\n waitForDevice(deviceId: number, timeoutMs: number = 30_000): Promise<DeviceProxy> {\n return new Promise<DeviceProxy>((resolve, reject) => {\n const existing = this.getDeviceById(deviceId)\n if (existing) {\n resolve(existing)\n return\n }\n let timer: ReturnType<typeof setTimeout> | null = null\n const off = this.onDeviceAdded((id) => {\n if (id !== deviceId) return\n const proxy = this.getDeviceById(id)\n if (!proxy) return\n if (timer) clearTimeout(timer)\n off()\n resolve(proxy)\n })\n if (Number.isFinite(timeoutMs)) {\n timer = setTimeout(() => {\n off()\n reject(new Error(`waitForDevice timed out after ${timeoutMs}ms (deviceId=${deviceId})`))\n }, timeoutMs)\n }\n })\n }\n\n // ── Batch actions ──────────────────────────────────────────────────\n\n /**\n * Iterate every device that binds `capName`. Awaits each callback\n * sequentially — for parallel use `invokeCap` with explicit\n * parallelism.\n */\n async forEachCap(\n capName: string,\n cb: (proxy: DeviceProxy) => void | Promise<void>,\n ): Promise<void> {\n for (const proxy of this.filterByCap(capName)) {\n await cb(proxy)\n }\n }\n\n /**\n * Invoke a cap method on every device that binds the cap. Returns\n * one result per device, success or failure isolated. Optional\n * parallelism cap — useful for \"snapshot all cameras but only 4\n * at a time so battery cams don't all wake at once\".\n *\n * Example:\n *\n * const results = await sm.invokeCap('snapshot', 'getSnapshot', {}, { parallelism: 4 })\n * const failed = results.filter(r => !r.ok)\n */\n async invokeCap<R = unknown>(\n capName: string,\n methodName: string,\n args: Record<string, unknown>,\n opts: { parallelism?: number } = {},\n ): Promise<ReadonlyArray<{ deviceId: number; ok: boolean; result?: R; error?: unknown }>> {\n const targets = this.filterByCap(capName)\n const parallelism = Math.max(1, opts.parallelism ?? targets.length)\n const out: Array<{ deviceId: number; ok: boolean; result?: R; error?: unknown }> = []\n\n for (let i = 0; i < targets.length; i += parallelism) {\n const chunk = targets.slice(i, i + parallelism)\n const settled = await Promise.allSettled(\n chunk.map(async (proxy) => {\n const cap = (proxy as unknown as Record<string, Record<string, (input: unknown) => unknown>>)[capName]\n if (!cap || typeof cap[methodName] !== 'function') {\n throw new Error(`device '${proxy.deviceId}' does not expose '${capName}.${methodName}'`)\n }\n return await cap[methodName]!(args) as R\n }),\n )\n for (let j = 0; j < settled.length; j++) {\n const proxy = chunk[j]!\n const r = settled[j]!\n if (r.status === 'fulfilled') {\n out.push({ deviceId: proxy.deviceId, ok: true, result: r.value })\n } else {\n out.push({ deviceId: proxy.deviceId, ok: false, error: r.reason })\n }\n }\n }\n\n return out\n }\n\n // ── Diagnostics ────────────────────────────────────────────────────\n\n /**\n * One-shot summary — fleet size, breakdown by cap / addon / type.\n * Designed for REPL inspection (`sm.summary()`).\n */\n summary(): {\n totalDevices: number\n online: number\n offline: number\n byCap: Record<string, number>\n byAddon: Record<string, number>\n byType: Record<string, number>\n statedDevices: number\n } {\n const byCap: Record<string, number> = {}\n const byAddon: Record<string, number> = {}\n const byType: Record<string, number> = {}\n let online = 0\n let offline = 0\n\n for (const binding of this.bindings.values()) {\n for (const entry of binding.entries) {\n byCap[entry.capName] = (byCap[entry.capName] ?? 0) + 1\n }\n }\n\n for (const info of this.devices.values()) {\n byAddon[info.addonId] = (byAddon[info.addonId] ?? 0) + 1\n byType[info.type] = (byType[info.type] ?? 0) + 1\n if (info.online) online++; else offline++\n }\n\n return {\n totalDevices: this.bindings.size,\n online,\n offline,\n byCap,\n byAddon,\n byType,\n statedDevices: this.stateMirror.size,\n }\n }\n\n /**\n * Debug-friendly dump — full state + binding + metadata for one\n * device or all devices. Cheap deep clone so caller mutations don't\n * leak into the mirror.\n */\n dump(deviceId?: number): unknown {\n const dumpOne = (id: number): unknown => {\n const info = this.devices.get(id) ?? null\n const binding = this.bindings.get(id) ?? null\n const state: Record<string, Record<string, unknown>> = {}\n const perCap = this.stateMirror.get(id)\n if (perCap) {\n for (const [cap, slice] of perCap) state[cap] = { ...slice }\n }\n return {\n deviceId: id,\n info,\n binding: binding ? { ...binding, entries: binding.entries.map((e) => ({ ...e })) } : null,\n state,\n }\n }\n\n if (deviceId !== undefined) return dumpOne(deviceId)\n\n const out: unknown[] = []\n for (const id of this.bindings.keys()) out.push(dumpOne(id))\n return out\n }\n\n /**\n * Direct read-only access to the underlying state mirror. Use\n * sparingly — `getSystemState` returns a deep copy that's safer for\n * exploratory work; this avoids the clone cost when iterating\n * thousands of slices.\n */\n getRawMirror(): ReadonlyMap<number, ReadonlyMap<string, Record<string, unknown>>> {\n return this.stateMirror\n }\n\n /**\n * Snapshot of the full state mirror — same shape as the warm-boot\n * payload. Deep-cloned; safe to mutate.\n */\n getSystemState(): Map<number, Map<string, Record<string, unknown>>> {\n const copy = new Map<number, Map<string, Record<string, unknown>>>()\n for (const [id, perCap] of this.stateMirror) {\n const dup = new Map<string, Record<string, unknown>>()\n for (const [k, v] of perCap) dup.set(k, { ...v })\n copy.set(id, dup)\n }\n return copy\n }\n\n // ── Internals ──────────────────────────────────────────────────────\n\n private subscribeBus(): void {\n if (!this.api.live?.onEvent) return\n const sub = this.api.live.onEvent\n\n this.bridges.push(\n sub.subscribe(\n { category: STATE_CHANGED_CATEGORY },\n {\n onData: (evt) => {\n const data = evt.data as { deviceId?: number; capName?: string; slice?: Record<string, unknown> } | null\n if (!data || typeof data.deviceId !== 'number' || typeof data.capName !== 'string') return\n this.applyStateUpdate(data.deviceId, data.capName, data.slice)\n },\n },\n ),\n )\n\n this.bridges.push(\n sub.subscribe(\n { category: BINDING_CHANGED_CATEGORY },\n {\n onData: (evt) => {\n const data = evt.data as { deviceId?: number; source?: { type?: string; id?: number | string } } | null\n const deviceId =\n typeof data?.deviceId === 'number'\n ? data.deviceId\n : data?.source?.type === 'device' && typeof data.source.id === 'number'\n ? data.source.id\n : null\n if (deviceId === null) return\n void this.refreshBinding(deviceId)\n },\n },\n ),\n )\n\n this.bridges.push(\n sub.subscribe(\n { category: DEVICE_REGISTERED_CATEGORY },\n {\n onData: (evt) => {\n const data = evt.data as { deviceId?: number } | null\n if (typeof data?.deviceId !== 'number') return\n void this.refreshDeviceMetadata(data.deviceId, 'added')\n },\n },\n ),\n )\n\n this.bridges.push(\n sub.subscribe(\n { category: DEVICE_UNREGISTERED_CATEGORY },\n {\n onData: (evt) => {\n const data = evt.data as { deviceId?: number } | null\n if (typeof data?.deviceId !== 'number') return\n this.applyDeviceRemoval(data.deviceId)\n },\n },\n ),\n )\n\n this.bridges.push(\n sub.subscribe(\n { category: DEVICE_UPDATED_CATEGORY },\n {\n onData: (evt) => {\n const data = evt.data as { deviceId?: number } | null\n if (typeof data?.deviceId !== 'number') return\n void this.refreshDeviceMetadata(data.deviceId, 'updated')\n },\n },\n ),\n )\n }\n\n private applyStateUpdate(\n deviceId: number,\n capName: string,\n slice: Record<string, unknown> | undefined,\n ): void {\n let perCap = this.stateMirror.get(deviceId)\n if (!perCap) {\n perCap = new Map()\n this.stateMirror.set(deviceId, perCap)\n }\n if (slice === undefined) {\n perCap.delete(capName)\n } else {\n perCap.set(capName, slice)\n }\n\n const handleKey = `${deviceId}:${capName}`\n const handleSet = this.handleListeners.get(handleKey)\n if (handleSet) {\n for (const cb of handleSet) {\n try { cb(slice) } catch { /* isolated */ }\n }\n }\n\n for (const cb of this.globalStateListeners) {\n try { cb(deviceId, capName, slice) } catch { /* isolated */ }\n }\n\n const capSet = this.capListeners.get(capName)\n if (capSet) {\n for (const cb of capSet) {\n try { cb(deviceId, slice) } catch { /* isolated */ }\n }\n }\n\n const devSet = this.deviceListeners.get(deviceId)\n if (devSet) {\n for (const cb of devSet) {\n try { cb(deviceId, capName, slice) } catch { /* isolated */ }\n }\n }\n }\n\n private applyDeviceRemoval(deviceId: number): void {\n const lastInfo = this.devices.get(deviceId) ?? null\n this.bindings.delete(deviceId)\n this.devices.delete(deviceId)\n this.stateMirror.delete(deviceId)\n for (const cb of this.removedListeners) {\n try { cb(deviceId, lastInfo) } catch { /* isolated */ }\n }\n }\n\n private async refreshBinding(deviceId: number): Promise<void> {\n try {\n const all = await this.api.deviceManager.getAllBindings.query({})\n const fresh = all.find((b) => b.deviceId === deviceId)\n if (fresh) {\n this.bindings.set(deviceId, fresh)\n } else {\n // Device was removed — emit lifecycle hook so consumers can\n // clean up. Use the same path as `device.unregistered` events.\n this.applyDeviceRemoval(deviceId)\n }\n } catch {\n // Transient failure — leave stale binding; next event retries.\n }\n }\n\n private async refreshDeviceMetadata(deviceId: number, kind: 'added' | 'updated'): Promise<void> {\n try {\n // listAll is cheap and idempotent. Single-device fetch\n // (`getDevice(id)`) would be lighter but means another method\n // on the api surface for tests; sticking with listAll keeps the\n // contract small.\n const all = await this.api.deviceManager.listAll.query({})\n const info = all.find((d) => d.id === deviceId)\n if (!info) return\n\n const wasNew = !this.devices.has(deviceId)\n this.devices.set(deviceId, info)\n\n if (kind === 'added' && wasNew) {\n // `device.registered` event ran but the bindings/state for\n // the new device may still be in flight from the worker.\n // Refresh bindings so consumers can immediately use the\n // device proxy. Lifecycle listener fires after binding lands.\n await this.refreshBinding(deviceId)\n for (const cb of this.addedListeners) {\n try { cb(deviceId, info) } catch { /* isolated */ }\n }\n }\n } catch {\n /* transient — next event retries */\n }\n }\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────\n\nfunction inSet<T>(value: T, set: T | readonly T[]): boolean {\n if (Array.isArray(set)) return (set as readonly T[]).includes(value)\n return value === set\n}\n\nfunction toArray<T>(value: T | readonly T[]): readonly T[] {\n return Array.isArray(value) ? (value as readonly T[]) : [value as T]\n}\n\nfunction matchesString(haystack: string, match: StringMatch): boolean {\n if (typeof match === 'string') return haystack === match\n if (match instanceof RegExp) return match.test(haystack)\n if ('exact' in match) return haystack === match.exact\n if ('contains' in match) return haystack.toLowerCase().includes(match.contains.toLowerCase())\n return false\n}\n\n","/**\n * Custom-action contracts — a mechanism for addons to expose system-level\n * extensibility endpoints (e.g. benchmark addon API) through a single\n * generic `api.addons.custom` tRPC endpoint.\n *\n * The contract shape mirrors `method()` from capability-definition.ts —\n * input/output Zod schemas, kind, auth — plus a `scope` discriminated union\n * (today only 'system' is runtime-supported; 'device' is typed for forward\n * compat).\n */\n\nimport type { z } from 'zod'\nimport type { DeviceType } from '../device/device-type.js'\nimport type { CapabilityMethodAuth } from './capability-definition.js'\n\n/** Scope of a custom action. Today only `system` is supported by the runtime. */\nexport type CustomActionScope =\n | { readonly kind: 'system' }\n | { readonly kind: 'device'; readonly deviceTypes?: readonly DeviceType[] }\n\nexport interface CustomActionSpec<\n TIn extends z.ZodType = z.ZodType,\n TOut extends z.ZodType = z.ZodType,\n TKind extends 'query' | 'mutation' | 'subscription' = 'query' | 'mutation' | 'subscription',\n TAuth extends CapabilityMethodAuth = CapabilityMethodAuth,\n TScope extends CustomActionScope = CustomActionScope,\n> {\n readonly input: TIn\n readonly output: TOut\n readonly kind: TKind\n readonly auth: TAuth\n readonly scope: TScope\n}\n\nexport type CustomActionsSpec = Record<string, CustomActionSpec>\n\n/**\n * Identity — preserves literal types for downstream inference.\n *\n * The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so\n * TypeScript does not widen each entry's literal `kind`/`auth` fields to\n * the broader unions declared on `CustomActionSpec`'s default generics.\n * Shape validity is enforced separately by the `customAction(...)` helper\n * whose return type is already a `CustomActionSpec<...>`.\n */\nexport function defineCustomActions<T extends Record<string, unknown>>(spec: T): T {\n return spec\n}\n\nexport interface CustomActionOptions<\n TKind extends 'query' | 'mutation' | 'subscription' = 'query',\n TAuth extends CapabilityMethodAuth = 'protected',\n> {\n readonly kind?: TKind\n readonly auth?: TAuth\n readonly scope?: CustomActionScope\n}\n\nexport function customAction<\n TIn extends z.ZodType,\n TOut extends z.ZodType,\n TKind extends 'query' | 'mutation' | 'subscription' = 'query',\n TAuth extends CapabilityMethodAuth = 'protected',\n>(\n input: TIn,\n output: TOut,\n options?: CustomActionOptions<TKind, TAuth>,\n): CustomActionSpec<TIn, TOut, TKind, TAuth, { readonly kind: 'system' }> {\n return {\n input,\n output,\n kind: (options?.kind ?? 'query') as TKind,\n auth: (options?.auth ?? 'protected') as TAuth,\n scope: (options?.scope ?? { kind: 'system' as const }) as { readonly kind: 'system' },\n }\n}\n","// AUTO-GENERATED by scripts/generate-capability-router-types.ts — DO NOT EDIT\n// Re-run after adding/modifying capability definitions.\n//\n// Generated: 2026-05-12T07:14:23.533Z\n// Capabilities: 80\n\n// ── Capability Definition Imports ────────────────────────────────────\n\nexport { accessoriesCapability } from '../capabilities/accessories.cap.js'\nexport { addonPagesCapability } from '../capabilities/addon-pages.cap.js'\nexport { addonPagesSourceCapability } from '../capabilities/addon-pages-source.cap.js'\nexport { addonRoutesCapability } from '../capabilities/addon-routes.cap.js'\nexport { addonSettingsCapability } from '../capabilities/addon-settings.cap.js'\nexport { addonWidgetsCapability } from '../capabilities/addon-widgets.cap.js'\nexport { addonWidgetsSourceCapability } from '../capabilities/addon-widgets-source.cap.js'\nexport { addonsCapability } from '../capabilities/addons.cap.js'\nexport { adminUiCapability } from '../capabilities/admin-ui.cap.js'\nexport { advancedNotifierCapability } from '../capabilities/advanced-notifier.cap.js'\nexport { alertsCapability } from '../capabilities/alerts.cap.js'\nexport { audioAnalysisCapability } from '../capabilities/audio-analysis.cap.js'\nexport { audioAnalyzerCapability } from '../capabilities/audio-analyzer.cap.js'\nexport { audioCodecCapability } from '../capabilities/audio-codec.cap.js'\nexport { audioMetricsCapability } from '../capabilities/audio-metrics.cap.js'\nexport { authProviderCapability } from '../capabilities/auth-provider.cap.js'\nexport { authenticationCapability } from '../capabilities/authentication.cap.js'\nexport { backupCapability } from '../capabilities/backup.cap.js'\nexport { batteryCapability } from '../capabilities/battery.cap.js'\nexport { brightnessCapability } from '../capabilities/brightness.cap.js'\nexport { cameraCredentialsCapability } from '../capabilities/camera-credentials.cap.js'\nexport { cameraStreamsCapability } from '../capabilities/camera-streams.cap.js'\nexport { decoderCapability } from '../capabilities/decoder.cap.js'\nexport { detectionPipelineCapability } from '../capabilities/detection-pipeline.cap.js'\nexport { deviceDiscoveryCapability } from '../capabilities/device-discovery.cap.js'\nexport { deviceManagerCapability } from '../capabilities/device-manager.cap.js'\nexport { deviceOpsCapability } from '../capabilities/device-ops.cap.js'\nexport { deviceProviderCapability } from '../capabilities/device-provider.cap.js'\nexport { deviceStateCapability } from '../capabilities/device-state.cap.js'\nexport { deviceStatusCapability } from '../capabilities/device-status.cap.js'\nexport { doorbellCapability } from '../capabilities/doorbell.cap.js'\nexport { embeddingEncoderCapability } from '../capabilities/embedding-encoder.cap.js'\nexport { eventsCapability } from '../capabilities/events.cap.js'\nexport { featureProbeCapability } from '../capabilities/feature-probe.cap.js'\nexport { integrationsCapability } from '../capabilities/integrations.cap.js'\nexport { intercomCapability } from '../capabilities/intercom.cap.js'\nexport { localNetworkCapability } from '../capabilities/local-network.cap.js'\nexport { logDestinationCapability } from '../capabilities/log-destination.cap.js'\nexport { meshNetworkCapability } from '../capabilities/mesh-network.cap.js'\nexport { meshOrchestratorCapability } from '../capabilities/mesh-orchestrator.cap.js'\nexport { metricsProviderCapability } from '../capabilities/metrics-provider.cap.js'\nexport { motionCapability } from '../capabilities/motion.cap.js'\nexport { motionDetectionCapability } from '../capabilities/motion-detection.cap.js'\nexport { motionTriggerCapability } from '../capabilities/motion-trigger.cap.js'\nexport { nativeObjectDetectionCapability } from '../capabilities/native-object-detection.cap.js'\nexport { networkAccessCapability } from '../capabilities/network-access.cap.js'\nexport { networkQualityCapability } from '../capabilities/network-quality.cap.js'\nexport { nodesCapability } from '../capabilities/nodes.cap.js'\nexport { notificationOutputCapability } from '../capabilities/notification-output.cap.js'\nexport { osdCapability } from '../capabilities/osd.cap.js'\nexport { pipelineAnalyticsCapability } from '../capabilities/pipeline-analytics.cap.js'\nexport { pipelineExecutorCapability } from '../capabilities/pipeline-executor.cap.js'\nexport { pipelineOrchestratorCapability } from '../capabilities/pipeline-orchestrator.cap.js'\nexport { pipelineRunnerCapability } from '../capabilities/pipeline-runner.cap.js'\nexport { platformProbeCapability } from '../capabilities/platform-probe.cap.js'\nexport { ptzCapability } from '../capabilities/ptz.cap.js'\nexport { ptzAutotrackCapability } from '../capabilities/ptz-autotrack.cap.js'\nexport { rebootCapability } from '../capabilities/reboot.cap.js'\nexport { recordingCapability } from '../capabilities/recording.cap.js'\nexport { recordingEngineCapability } from '../capabilities/recording-engine.cap.js'\nexport { remoteAccessCapability } from '../capabilities/remote-access.cap.js'\nexport { restreamerCapability } from '../capabilities/restreamer.cap.js'\nexport { settingsStoreCapability } from '../capabilities/settings-store.cap.js'\nexport { snapshotCapability } from '../capabilities/snapshot.cap.js'\nexport { snapshotProviderCapability } from '../capabilities/snapshot-provider.cap.js'\nexport { storageCapability } from '../capabilities/storage.cap.js'\nexport { storageProviderCapability } from '../capabilities/storage-provider.cap.js'\nexport { streamBrokerCapability } from '../capabilities/stream-broker.cap.js'\nexport { streamingEngineCapability } from '../capabilities/streaming-engine.cap.js'\nexport { switchCapability } from '../capabilities/switch.cap.js'\nexport { systemCapability } from '../capabilities/system.cap.js'\nexport { toastCapability } from '../capabilities/toast.cap.js'\nexport { turnOrchestratorCapability } from '../capabilities/turn-orchestrator.cap.js'\nexport { turnProviderCapability } from '../capabilities/turn-provider.cap.js'\nexport { userManagementCapability } from '../capabilities/user-management.cap.js'\nexport { webrtcCapability } from '../capabilities/webrtc.cap.js'\nexport { webrtcSessionCapability } from '../capabilities/webrtc-session.cap.js'\nexport { zoneAnalyticsCapability } from '../capabilities/zone-analytics.cap.js'\nexport { zoneRulesCapability } from '../capabilities/zone-rules.cap.js'\nexport { zonesCapability } from '../capabilities/zones.cap.js'\n\n// ── Capability Names (for registry key constants) ────────────────────\n\nexport const CAPABILITY_NAMES = {\n accessories: 'accessories' as const,\n addonPages: 'addon-pages' as const,\n addonPagesSource: 'addon-pages-source' as const,\n addonRoutes: 'addon-routes' as const,\n addonSettings: 'addon-settings' as const,\n addonWidgets: 'addon-widgets' as const,\n addonWidgetsSource: 'addon-widgets-source' as const,\n addons: 'addons' as const,\n adminUi: 'admin-ui' as const,\n advancedNotifier: 'advanced-notifier' as const,\n alerts: 'alerts' as const,\n audioAnalysis: 'audio-analysis' as const,\n audioAnalyzer: 'audio-analyzer' as const,\n audioCodec: 'audio-codec' as const,\n audioMetrics: 'audio-metrics' as const,\n authProvider: 'auth-provider' as const,\n authentication: 'authentication' as const,\n backup: 'backup' as const,\n battery: 'battery' as const,\n brightness: 'brightness' as const,\n cameraCredentials: 'camera-credentials' as const,\n cameraStreams: 'camera-streams' as const,\n decoder: 'decoder' as const,\n detectionPipeline: 'detection-pipeline' as const,\n deviceDiscovery: 'device-discovery' as const,\n deviceManager: 'device-manager' as const,\n deviceOps: 'device-ops' as const,\n deviceProvider: 'device-provider' as const,\n deviceState: 'device-state' as const,\n deviceStatus: 'device-status' as const,\n doorbell: 'doorbell' as const,\n embeddingEncoder: 'embedding-encoder' as const,\n events: 'events' as const,\n featureProbe: 'feature-probe' as const,\n integrations: 'integrations' as const,\n intercom: 'intercom' as const,\n localNetwork: 'local-network' as const,\n logDestination: 'log-destination' as const,\n meshNetwork: 'mesh-network' as const,\n meshOrchestrator: 'mesh-orchestrator' as const,\n metricsProvider: 'metrics-provider' as const,\n motion: 'motion' as const,\n motionDetection: 'motion-detection' as const,\n motionTrigger: 'motion-trigger' as const,\n nativeObjectDetection: 'native-object-detection' as const,\n networkAccess: 'network-access' as const,\n networkQuality: 'network-quality' as const,\n nodes: 'nodes' as const,\n notificationOutput: 'notification-output' as const,\n osd: 'osd' as const,\n pipelineAnalytics: 'pipeline-analytics' as const,\n pipelineExecutor: 'pipeline-executor' as const,\n pipelineOrchestrator: 'pipeline-orchestrator' as const,\n pipelineRunner: 'pipeline-runner' as const,\n platformProbe: 'platform-probe' as const,\n ptz: 'ptz' as const,\n ptzAutotrack: 'ptz-autotrack' as const,\n reboot: 'reboot' as const,\n recording: 'recording' as const,\n recordingEngine: 'recording-engine' as const,\n remoteAccess: 'remote-access' as const,\n restreamer: 'restreamer' as const,\n settingsStore: 'settings-store' as const,\n snapshot: 'snapshot' as const,\n snapshotProvider: 'snapshot-provider' as const,\n storage: 'storage' as const,\n storageProvider: 'storage-provider' as const,\n streamBroker: 'stream-broker' as const,\n streamingEngine: 'streaming-engine' as const,\n switch: 'switch' as const,\n system: 'system' as const,\n toast: 'toast' as const,\n turnOrchestrator: 'turn-orchestrator' as const,\n turnProvider: 'turn-provider' as const,\n userManagement: 'user-management' as const,\n webrtc: 'webrtc' as const,\n webrtcSession: 'webrtc-session' as const,\n zoneAnalytics: 'zone-analytics' as const,\n zoneRules: 'zone-rules' as const,\n zones: 'zones' as const,\n} as const\n\n/** All known capability names. */\nexport type CapabilityName = typeof CAPABILITY_NAMES[keyof typeof CAPABILITY_NAMES]\n\n/** Router key → capability name mapping for auto-mount. */\nexport const CAPABILITY_ROUTER_KEYS: ReadonlyArray<{ readonly key: string; readonly name: string }> = [\n { key: 'accessories', name: 'accessories' },\n { key: 'addonPages', name: 'addon-pages' },\n { key: 'addonPagesSource', name: 'addon-pages-source' },\n { key: 'addonRoutes', name: 'addon-routes' },\n { key: 'addonSettings', name: 'addon-settings' },\n { key: 'addonWidgets', name: 'addon-widgets' },\n { key: 'addonWidgetsSource', name: 'addon-widgets-source' },\n { key: 'addons', name: 'addons' },\n { key: 'adminUi', name: 'admin-ui' },\n { key: 'advancedNotifier', name: 'advanced-notifier' },\n { key: 'alerts', name: 'alerts' },\n { key: 'audioAnalysis', name: 'audio-analysis' },\n { key: 'audioAnalyzer', name: 'audio-analyzer' },\n { key: 'audioCodec', name: 'audio-codec' },\n { key: 'audioMetrics', name: 'audio-metrics' },\n { key: 'authProvider', name: 'auth-provider' },\n { key: 'authentication', name: 'authentication' },\n { key: 'backup', name: 'backup' },\n { key: 'battery', name: 'battery' },\n { key: 'brightness', name: 'brightness' },\n { key: 'cameraCredentials', name: 'camera-credentials' },\n { key: 'cameraStreams', name: 'camera-streams' },\n { key: 'decoder', name: 'decoder' },\n { key: 'detectionPipeline', name: 'detection-pipeline' },\n { key: 'deviceDiscovery', name: 'device-discovery' },\n { key: 'deviceManager', name: 'device-manager' },\n { key: 'deviceOps', name: 'device-ops' },\n { key: 'deviceProvider', name: 'device-provider' },\n { key: 'deviceState', name: 'device-state' },\n { key: 'deviceStatus', name: 'device-status' },\n { key: 'doorbell', name: 'doorbell' },\n { key: 'embeddingEncoder', name: 'embedding-encoder' },\n { key: 'events', name: 'events' },\n { key: 'featureProbe', name: 'feature-probe' },\n { key: 'integrations', name: 'integrations' },\n { key: 'intercom', name: 'intercom' },\n { key: 'localNetwork', name: 'local-network' },\n { key: 'logDestination', name: 'log-destination' },\n { key: 'meshNetwork', name: 'mesh-network' },\n { key: 'meshOrchestrator', name: 'mesh-orchestrator' },\n { key: 'metricsProvider', name: 'metrics-provider' },\n { key: 'motion', name: 'motion' },\n { key: 'motionDetection', name: 'motion-detection' },\n { key: 'motionTrigger', name: 'motion-trigger' },\n { key: 'nativeObjectDetection', name: 'native-object-detection' },\n { key: 'networkAccess', name: 'network-access' },\n { key: 'networkQuality', name: 'network-quality' },\n { key: 'nodes', name: 'nodes' },\n { key: 'notificationOutput', name: 'notification-output' },\n { key: 'osd', name: 'osd' },\n { key: 'pipelineAnalytics', name: 'pipeline-analytics' },\n { key: 'pipelineExecutor', name: 'pipeline-executor' },\n { key: 'pipelineOrchestrator', name: 'pipeline-orchestrator' },\n { key: 'pipelineRunner', name: 'pipeline-runner' },\n { key: 'platformProbe', name: 'platform-probe' },\n { key: 'ptz', name: 'ptz' },\n { key: 'ptzAutotrack', name: 'ptz-autotrack' },\n { key: 'reboot', name: 'reboot' },\n { key: 'recording', name: 'recording' },\n { key: 'recordingEngine', name: 'recording-engine' },\n { key: 'remoteAccess', name: 'remote-access' },\n { key: 'restreamer', name: 'restreamer' },\n { key: 'settingsStore', name: 'settings-store' },\n { key: 'snapshot', name: 'snapshot' },\n { key: 'snapshotProvider', name: 'snapshot-provider' },\n { key: 'storage', name: 'storage' },\n { key: 'storageProvider', name: 'storage-provider' },\n { key: 'streamBroker', name: 'stream-broker' },\n { key: 'streamingEngine', name: 'streaming-engine' },\n { key: 'switch', name: 'switch' },\n { key: 'system', name: 'system' },\n { key: 'toast', name: 'toast' },\n { key: 'turnOrchestrator', name: 'turn-orchestrator' },\n { key: 'turnProvider', name: 'turn-provider' },\n { key: 'userManagement', name: 'user-management' },\n { key: 'webrtc', name: 'webrtc' },\n { key: 'webrtcSession', name: 'webrtc-session' },\n { key: 'zoneAnalytics', name: 'zone-analytics' },\n { key: 'zoneRules', name: 'zone-rules' },\n { key: 'zones', name: 'zones' },\n]\n// ── Typed Router Map (for AppRouter) ─────────────────────────────────\n//\n// Import CapabilityTRPCRouter from the server to type the map.\n// This type is consumed by trpc.router.ts for typed spread.\n\n/**\n * Map of {name} → TRouter for typed AppRouter spread.\n * Generic TRouter defaults to unknown — the server layer resolves it\n * to the generated capability router type.\n */\nexport interface CapabilityRouterMap<TRouter = unknown> {\n readonly accessories: TRouter\n readonly addonPages: TRouter\n readonly addonPagesSource: TRouter\n readonly addonRoutes: TRouter\n readonly addonSettings: TRouter\n readonly addonWidgets: TRouter\n readonly addonWidgetsSource: TRouter\n readonly addons: TRouter\n readonly adminUi: TRouter\n readonly advancedNotifier: TRouter\n readonly alerts: TRouter\n readonly audioAnalysis: TRouter\n readonly audioAnalyzer: TRouter\n readonly audioCodec: TRouter\n readonly audioMetrics: TRouter\n readonly authProvider: TRouter\n readonly authentication: TRouter\n readonly backup: TRouter\n readonly battery: TRouter\n readonly brightness: TRouter\n readonly cameraCredentials: TRouter\n readonly cameraStreams: TRouter\n readonly decoder: TRouter\n readonly detectionPipeline: TRouter\n readonly deviceDiscovery: TRouter\n readonly deviceManager: TRouter\n readonly deviceOps: TRouter\n readonly deviceProvider: TRouter\n readonly deviceState: TRouter\n readonly deviceStatus: TRouter\n readonly doorbell: TRouter\n readonly embeddingEncoder: TRouter\n readonly events: TRouter\n readonly featureProbe: TRouter\n readonly integrations: TRouter\n readonly intercom: TRouter\n readonly localNetwork: TRouter\n readonly logDestination: TRouter\n readonly meshNetwork: TRouter\n readonly meshOrchestrator: TRouter\n readonly metricsProvider: TRouter\n readonly motion: TRouter\n readonly motionDetection: TRouter\n readonly motionTrigger: TRouter\n readonly nativeObjectDetection: TRouter\n readonly networkAccess: TRouter\n readonly networkQuality: TRouter\n readonly nodes: TRouter\n readonly notificationOutput: TRouter\n readonly osd: TRouter\n readonly pipelineAnalytics: TRouter\n readonly pipelineExecutor: TRouter\n readonly pipelineOrchestrator: TRouter\n readonly pipelineRunner: TRouter\n readonly platformProbe: TRouter\n readonly ptz: TRouter\n readonly ptzAutotrack: TRouter\n readonly reboot: TRouter\n readonly recording: TRouter\n readonly recordingEngine: TRouter\n readonly remoteAccess: TRouter\n readonly restreamer: TRouter\n readonly settingsStore: TRouter\n readonly snapshot: TRouter\n readonly snapshotProvider: TRouter\n readonly storage: TRouter\n readonly storageProvider: TRouter\n readonly streamBroker: TRouter\n readonly streamingEngine: TRouter\n readonly switch: TRouter\n readonly system: TRouter\n readonly toast: TRouter\n readonly turnOrchestrator: TRouter\n readonly turnProvider: TRouter\n readonly userManagement: TRouter\n readonly webrtc: TRouter\n readonly webrtcSession: TRouter\n readonly zoneAnalytics: TRouter\n readonly zoneRules: TRouter\n readonly zones: TRouter\n}\n\n// ── Singleton / Collection split (derived from cap.mode) ─────────────\n\n/** Capability names whose mode is `singleton` (65 caps). */\nexport const SINGLETON_CAPABILITY_NAMES = [\n 'accessories',\n 'addon-pages',\n 'addon-settings',\n 'addon-widgets',\n 'addons',\n 'admin-ui',\n 'advanced-notifier',\n 'alerts',\n 'audio-analysis',\n 'audio-analyzer',\n 'audio-codec',\n 'audio-metrics',\n 'authentication',\n 'backup',\n 'battery',\n 'brightness',\n 'camera-credentials',\n 'camera-streams',\n 'decoder',\n 'detection-pipeline',\n 'device-discovery',\n 'device-manager',\n 'device-ops',\n 'device-state',\n 'device-status',\n 'doorbell',\n 'events',\n 'feature-probe',\n 'integrations',\n 'intercom',\n 'local-network',\n 'mesh-orchestrator',\n 'metrics-provider',\n 'motion',\n 'motion-detection',\n 'motion-trigger',\n 'native-object-detection',\n 'network-quality',\n 'nodes',\n 'osd',\n 'pipeline-analytics',\n 'pipeline-executor',\n 'pipeline-orchestrator',\n 'pipeline-runner',\n 'platform-probe',\n 'ptz',\n 'ptz-autotrack',\n 'reboot',\n 'recording',\n 'recording-engine',\n 'remote-access',\n 'settings-store',\n 'snapshot',\n 'storage',\n 'stream-broker',\n 'streaming-engine',\n 'switch',\n 'system',\n 'toast',\n 'turn-orchestrator',\n 'user-management',\n 'webrtc-session',\n 'zone-analytics',\n 'zone-rules',\n 'zones',\n] as const\n\n/** Union of singleton capability names (literal string union). */\nexport type SingletonCapabilityName = typeof SINGLETON_CAPABILITY_NAMES[number]\n\n/** Capability names whose mode is `collection` (15 caps). */\nexport const COLLECTION_CAPABILITY_NAMES = [\n 'addon-pages-source',\n 'addon-routes',\n 'addon-widgets-source',\n 'auth-provider',\n 'device-provider',\n 'embedding-encoder',\n 'log-destination',\n 'mesh-network',\n 'network-access',\n 'notification-output',\n 'restreamer',\n 'snapshot-provider',\n 'storage-provider',\n 'turn-provider',\n 'webrtc',\n] as const\n\n/** Union of collection capability names (literal string union). */\nexport type CollectionCapabilityName = typeof COLLECTION_CAPABILITY_NAMES[number]\n\n/** Capability mode lookup at runtime — mirrors the `.cap.ts` definitions. */\nexport const CAPABILITY_MODE: Readonly<Record<string, 'singleton' | 'collection'>> = {\n 'accessories': 'singleton',\n 'addon-pages': 'singleton',\n 'addon-pages-source': 'collection',\n 'addon-routes': 'collection',\n 'addon-settings': 'singleton',\n 'addon-widgets': 'singleton',\n 'addon-widgets-source': 'collection',\n 'addons': 'singleton',\n 'admin-ui': 'singleton',\n 'advanced-notifier': 'singleton',\n 'alerts': 'singleton',\n 'audio-analysis': 'singleton',\n 'audio-analyzer': 'singleton',\n 'audio-codec': 'singleton',\n 'audio-metrics': 'singleton',\n 'auth-provider': 'collection',\n 'authentication': 'singleton',\n 'backup': 'singleton',\n 'battery': 'singleton',\n 'brightness': 'singleton',\n 'camera-credentials': 'singleton',\n 'camera-streams': 'singleton',\n 'decoder': 'singleton',\n 'detection-pipeline': 'singleton',\n 'device-discovery': 'singleton',\n 'device-manager': 'singleton',\n 'device-ops': 'singleton',\n 'device-provider': 'collection',\n 'device-state': 'singleton',\n 'device-status': 'singleton',\n 'doorbell': 'singleton',\n 'embedding-encoder': 'collection',\n 'events': 'singleton',\n 'feature-probe': 'singleton',\n 'integrations': 'singleton',\n 'intercom': 'singleton',\n 'local-network': 'singleton',\n 'log-destination': 'collection',\n 'mesh-network': 'collection',\n 'mesh-orchestrator': 'singleton',\n 'metrics-provider': 'singleton',\n 'motion': 'singleton',\n 'motion-detection': 'singleton',\n 'motion-trigger': 'singleton',\n 'native-object-detection': 'singleton',\n 'network-access': 'collection',\n 'network-quality': 'singleton',\n 'nodes': 'singleton',\n 'notification-output': 'collection',\n 'osd': 'singleton',\n 'pipeline-analytics': 'singleton',\n 'pipeline-executor': 'singleton',\n 'pipeline-orchestrator': 'singleton',\n 'pipeline-runner': 'singleton',\n 'platform-probe': 'singleton',\n 'ptz': 'singleton',\n 'ptz-autotrack': 'singleton',\n 'reboot': 'singleton',\n 'recording': 'singleton',\n 'recording-engine': 'singleton',\n 'remote-access': 'singleton',\n 'restreamer': 'collection',\n 'settings-store': 'singleton',\n 'snapshot': 'singleton',\n 'snapshot-provider': 'collection',\n 'storage': 'singleton',\n 'storage-provider': 'collection',\n 'stream-broker': 'singleton',\n 'streaming-engine': 'singleton',\n 'switch': 'singleton',\n 'system': 'singleton',\n 'toast': 'singleton',\n 'turn-orchestrator': 'singleton',\n 'turn-provider': 'collection',\n 'user-management': 'singleton',\n 'webrtc': 'collection',\n 'webrtc-session': 'singleton',\n 'zone-analytics': 'singleton',\n 'zone-rules': 'singleton',\n 'zones': 'singleton',\n}\n\n// ── All Capability Definitions (for boot-time declaration) ───────────\n\nimport type { CapabilityDefinition } from '../capabilities/capability-definition.js'\nimport { accessoriesCapability as _accessoriesCapability } from '../capabilities/accessories.cap.js'\nimport { addonPagesCapability as _addonPagesCapability } from '../capabilities/addon-pages.cap.js'\nimport { addonPagesSourceCapability as _addonPagesSourceCapability } from '../capabilities/addon-pages-source.cap.js'\nimport { addonRoutesCapability as _addonRoutesCapability } from '../capabilities/addon-routes.cap.js'\nimport { addonSettingsCapability as _addonSettingsCapability } from '../capabilities/addon-settings.cap.js'\nimport { addonWidgetsCapability as _addonWidgetsCapability } from '../capabilities/addon-widgets.cap.js'\nimport { addonWidgetsSourceCapability as _addonWidgetsSourceCapability } from '../capabilities/addon-widgets-source.cap.js'\nimport { addonsCapability as _addonsCapability } from '../capabilities/addons.cap.js'\nimport { adminUiCapability as _adminUiCapability } from '../capabilities/admin-ui.cap.js'\nimport { advancedNotifierCapability as _advancedNotifierCapability } from '../capabilities/advanced-notifier.cap.js'\nimport { alertsCapability as _alertsCapability } from '../capabilities/alerts.cap.js'\nimport { audioAnalysisCapability as _audioAnalysisCapability } from '../capabilities/audio-analysis.cap.js'\nimport { audioAnalyzerCapability as _audioAnalyzerCapability } from '../capabilities/audio-analyzer.cap.js'\nimport { audioCodecCapability as _audioCodecCapability } from '../capabilities/audio-codec.cap.js'\nimport { audioMetricsCapability as _audioMetricsCapability } from '../capabilities/audio-metrics.cap.js'\nimport { authProviderCapability as _authProviderCapability } from '../capabilities/auth-provider.cap.js'\nimport { authenticationCapability as _authenticationCapability } from '../capabilities/authentication.cap.js'\nimport { backupCapability as _backupCapability } from '../capabilities/backup.cap.js'\nimport { batteryCapability as _batteryCapability } from '../capabilities/battery.cap.js'\nimport { brightnessCapability as _brightnessCapability } from '../capabilities/brightness.cap.js'\nimport { cameraCredentialsCapability as _cameraCredentialsCapability } from '../capabilities/camera-credentials.cap.js'\nimport { cameraStreamsCapability as _cameraStreamsCapability } from '../capabilities/camera-streams.cap.js'\nimport { decoderCapability as _decoderCapability } from '../capabilities/decoder.cap.js'\nimport { detectionPipelineCapability as _detectionPipelineCapability } from '../capabilities/detection-pipeline.cap.js'\nimport { deviceDiscoveryCapability as _deviceDiscoveryCapability } from '../capabilities/device-discovery.cap.js'\nimport { deviceManagerCapability as _deviceManagerCapability } from '../capabilities/device-manager.cap.js'\nimport { deviceOpsCapability as _deviceOpsCapability } from '../capabilities/device-ops.cap.js'\nimport { deviceProviderCapability as _deviceProviderCapability } from '../capabilities/device-provider.cap.js'\nimport { deviceStateCapability as _deviceStateCapability } from '../capabilities/device-state.cap.js'\nimport { deviceStatusCapability as _deviceStatusCapability } from '../capabilities/device-status.cap.js'\nimport { doorbellCapability as _doorbellCapability } from '../capabilities/doorbell.cap.js'\nimport { embeddingEncoderCapability as _embeddingEncoderCapability } from '../capabilities/embedding-encoder.cap.js'\nimport { eventsCapability as _eventsCapability } from '../capabilities/events.cap.js'\nimport { featureProbeCapability as _featureProbeCapability } from '../capabilities/feature-probe.cap.js'\nimport { integrationsCapability as _integrationsCapability } from '../capabilities/integrations.cap.js'\nimport { intercomCapability as _intercomCapability } from '../capabilities/intercom.cap.js'\nimport { localNetworkCapability as _localNetworkCapability } from '../capabilities/local-network.cap.js'\nimport { logDestinationCapability as _logDestinationCapability } from '../capabilities/log-destination.cap.js'\nimport { meshNetworkCapability as _meshNetworkCapability } from '../capabilities/mesh-network.cap.js'\nimport { meshOrchestratorCapability as _meshOrchestratorCapability } from '../capabilities/mesh-orchestrator.cap.js'\nimport { metricsProviderCapability as _metricsProviderCapability } from '../capabilities/metrics-provider.cap.js'\nimport { motionCapability as _motionCapability } from '../capabilities/motion.cap.js'\nimport { motionDetectionCapability as _motionDetectionCapability } from '../capabilities/motion-detection.cap.js'\nimport { motionTriggerCapability as _motionTriggerCapability } from '../capabilities/motion-trigger.cap.js'\nimport { nativeObjectDetectionCapability as _nativeObjectDetectionCapability } from '../capabilities/native-object-detection.cap.js'\nimport { networkAccessCapability as _networkAccessCapability } from '../capabilities/network-access.cap.js'\nimport { networkQualityCapability as _networkQualityCapability } from '../capabilities/network-quality.cap.js'\nimport { nodesCapability as _nodesCapability } from '../capabilities/nodes.cap.js'\nimport { notificationOutputCapability as _notificationOutputCapability } from '../capabilities/notification-output.cap.js'\nimport { osdCapability as _osdCapability } from '../capabilities/osd.cap.js'\nimport { pipelineAnalyticsCapability as _pipelineAnalyticsCapability } from '../capabilities/pipeline-analytics.cap.js'\nimport { pipelineExecutorCapability as _pipelineExecutorCapability } from '../capabilities/pipeline-executor.cap.js'\nimport { pipelineOrchestratorCapability as _pipelineOrchestratorCapability } from '../capabilities/pipeline-orchestrator.cap.js'\nimport { pipelineRunnerCapability as _pipelineRunnerCapability } from '../capabilities/pipeline-runner.cap.js'\nimport { platformProbeCapability as _platformProbeCapability } from '../capabilities/platform-probe.cap.js'\nimport { ptzCapability as _ptzCapability } from '../capabilities/ptz.cap.js'\nimport { ptzAutotrackCapability as _ptzAutotrackCapability } from '../capabilities/ptz-autotrack.cap.js'\nimport { rebootCapability as _rebootCapability } from '../capabilities/reboot.cap.js'\nimport { recordingCapability as _recordingCapability } from '../capabilities/recording.cap.js'\nimport { recordingEngineCapability as _recordingEngineCapability } from '../capabilities/recording-engine.cap.js'\nimport { remoteAccessCapability as _remoteAccessCapability } from '../capabilities/remote-access.cap.js'\nimport { restreamerCapability as _restreamerCapability } from '../capabilities/restreamer.cap.js'\nimport { settingsStoreCapability as _settingsStoreCapability } from '../capabilities/settings-store.cap.js'\nimport { snapshotCapability as _snapshotCapability } from '../capabilities/snapshot.cap.js'\nimport { snapshotProviderCapability as _snapshotProviderCapability } from '../capabilities/snapshot-provider.cap.js'\nimport { storageCapability as _storageCapability } from '../capabilities/storage.cap.js'\nimport { storageProviderCapability as _storageProviderCapability } from '../capabilities/storage-provider.cap.js'\nimport { streamBrokerCapability as _streamBrokerCapability } from '../capabilities/stream-broker.cap.js'\nimport { streamingEngineCapability as _streamingEngineCapability } from '../capabilities/streaming-engine.cap.js'\nimport { switchCapability as _switchCapability } from '../capabilities/switch.cap.js'\nimport { systemCapability as _systemCapability } from '../capabilities/system.cap.js'\nimport { toastCapability as _toastCapability } from '../capabilities/toast.cap.js'\nimport { turnOrchestratorCapability as _turnOrchestratorCapability } from '../capabilities/turn-orchestrator.cap.js'\nimport { turnProviderCapability as _turnProviderCapability } from '../capabilities/turn-provider.cap.js'\nimport { userManagementCapability as _userManagementCapability } from '../capabilities/user-management.cap.js'\nimport { webrtcCapability as _webrtcCapability } from '../capabilities/webrtc.cap.js'\nimport { webrtcSessionCapability as _webrtcSessionCapability } from '../capabilities/webrtc-session.cap.js'\nimport { zoneAnalyticsCapability as _zoneAnalyticsCapability } from '../capabilities/zone-analytics.cap.js'\nimport { zoneRulesCapability as _zoneRulesCapability } from '../capabilities/zone-rules.cap.js'\nimport { zonesCapability as _zonesCapability } from '../capabilities/zones.cap.js'\n\n/**\n * Every CapabilityDefinition shipped by `@camstack/types`. The hub\n * iterates this array at boot to declare each cap on the registry\n * before addons (in-process or via the Moleculer bridge) attempt\n * `registerProvider`. Adding a new cap means dropping a `*.cap.ts`\n * file in `packages/types/src/capabilities/` and re-running\n * `npx tsx scripts/generate-capability-router-types.ts` — no manual\n * edit to `main.ts` required.\n */\nexport const ALL_CAPABILITY_DEFINITIONS: readonly CapabilityDefinition[] = [\n _accessoriesCapability as CapabilityDefinition,\n _addonPagesCapability as CapabilityDefinition,\n _addonPagesSourceCapability as CapabilityDefinition,\n _addonRoutesCapability as CapabilityDefinition,\n _addonSettingsCapability as CapabilityDefinition,\n _addonWidgetsCapability as CapabilityDefinition,\n _addonWidgetsSourceCapability as CapabilityDefinition,\n _addonsCapability as CapabilityDefinition,\n _adminUiCapability as CapabilityDefinition,\n _advancedNotifierCapability as CapabilityDefinition,\n _alertsCapability as CapabilityDefinition,\n _audioAnalysisCapability as CapabilityDefinition,\n _audioAnalyzerCapability as CapabilityDefinition,\n _audioCodecCapability as CapabilityDefinition,\n _audioMetricsCapability as CapabilityDefinition,\n _authProviderCapability as CapabilityDefinition,\n _authenticationCapability as CapabilityDefinition,\n _backupCapability as CapabilityDefinition,\n _batteryCapability as CapabilityDefinition,\n _brightnessCapability as CapabilityDefinition,\n _cameraCredentialsCapability as CapabilityDefinition,\n _cameraStreamsCapability as CapabilityDefinition,\n _decoderCapability as CapabilityDefinition,\n _detectionPipelineCapability as CapabilityDefinition,\n _deviceDiscoveryCapability as CapabilityDefinition,\n _deviceManagerCapability as CapabilityDefinition,\n _deviceOpsCapability as CapabilityDefinition,\n _deviceProviderCapability as CapabilityDefinition,\n _deviceStateCapability as CapabilityDefinition,\n _deviceStatusCapability as CapabilityDefinition,\n _doorbellCapability as CapabilityDefinition,\n _embeddingEncoderCapability as CapabilityDefinition,\n _eventsCapability as CapabilityDefinition,\n _featureProbeCapability as CapabilityDefinition,\n _integrationsCapability as CapabilityDefinition,\n _intercomCapability as CapabilityDefinition,\n _localNetworkCapability as CapabilityDefinition,\n _logDestinationCapability as CapabilityDefinition,\n _meshNetworkCapability as CapabilityDefinition,\n _meshOrchestratorCapability as CapabilityDefinition,\n _metricsProviderCapability as CapabilityDefinition,\n _motionCapability as CapabilityDefinition,\n _motionDetectionCapability as CapabilityDefinition,\n _motionTriggerCapability as CapabilityDefinition,\n _nativeObjectDetectionCapability as CapabilityDefinition,\n _networkAccessCapability as CapabilityDefinition,\n _networkQualityCapability as CapabilityDefinition,\n _nodesCapability as CapabilityDefinition,\n _notificationOutputCapability as CapabilityDefinition,\n _osdCapability as CapabilityDefinition,\n _pipelineAnalyticsCapability as CapabilityDefinition,\n _pipelineExecutorCapability as CapabilityDefinition,\n _pipelineOrchestratorCapability as CapabilityDefinition,\n _pipelineRunnerCapability as CapabilityDefinition,\n _platformProbeCapability as CapabilityDefinition,\n _ptzCapability as CapabilityDefinition,\n _ptzAutotrackCapability as CapabilityDefinition,\n _rebootCapability as CapabilityDefinition,\n _recordingCapability as CapabilityDefinition,\n _recordingEngineCapability as CapabilityDefinition,\n _remoteAccessCapability as CapabilityDefinition,\n _restreamerCapability as CapabilityDefinition,\n _settingsStoreCapability as CapabilityDefinition,\n _snapshotCapability as CapabilityDefinition,\n _snapshotProviderCapability as CapabilityDefinition,\n _storageCapability as CapabilityDefinition,\n _storageProviderCapability as CapabilityDefinition,\n _streamBrokerCapability as CapabilityDefinition,\n _streamingEngineCapability as CapabilityDefinition,\n _switchCapability as CapabilityDefinition,\n _systemCapability as CapabilityDefinition,\n _toastCapability as CapabilityDefinition,\n _turnOrchestratorCapability as CapabilityDefinition,\n _turnProviderCapability as CapabilityDefinition,\n _userManagementCapability as CapabilityDefinition,\n _webrtcCapability as CapabilityDefinition,\n _webrtcSessionCapability as CapabilityDefinition,\n _zoneAnalyticsCapability as CapabilityDefinition,\n _zoneRulesCapability as CapabilityDefinition,\n _zonesCapability as CapabilityDefinition,\n]\n\n","/* AUTO-GENERATED by scripts/generate-cap-status-types.ts. DO NOT EDIT. */\n/* eslint-disable */\n\nimport type { z } from 'zod'\n\nimport { AccessoriesStatusSchema } from '../capabilities/accessories.cap.js'\nimport { BatteryStatusSchema } from '../capabilities/battery.cap.js'\nimport { BrightnessStatusSchema } from '../capabilities/brightness.cap.js'\nimport { CameraCredentialsStatusSchema } from '../capabilities/camera-credentials.cap.js'\nimport { DeviceDiscoveryStatusSchema } from '../capabilities/device-discovery.cap.js'\nimport { deviceStatusCapability } from '../capabilities/device-status.cap.js'\nimport { DoorbellStatusSchema } from '../capabilities/doorbell.cap.js'\nimport { FeatureProbeStatusSchema } from '../capabilities/feature-probe.cap.js'\nimport { IntercomStatusSchema } from '../capabilities/intercom.cap.js'\nimport { MotionStatusSchema } from '../capabilities/motion.cap.js'\nimport { MotionTriggerStatusSchema } from '../capabilities/motion-trigger.cap.js'\nimport { NativeObjectDetectionStatusSchema } from '../capabilities/native-object-detection.cap.js'\nimport { OsdStatusSchema } from '../capabilities/osd.cap.js'\nimport { ptzCapability } from '../capabilities/ptz.cap.js'\nimport { PtzAutotrackStatusSchema } from '../capabilities/ptz-autotrack.cap.js'\nimport { SnapshotStatusSchema } from '../capabilities/snapshot.cap.js'\nimport { SwitchStatusSchema } from '../capabilities/switch.cap.js'\n\n/**\n * Lookup from cap name (literal) → the TypeScript type of that\n * capability's `status.schema`. Populated at codegen time from every\n * `*.cap.ts` file that declares a `status` block.\n */\nexport type CapStatusTypeMap = {\n readonly 'accessories': z.infer<typeof AccessoriesStatusSchema>\n readonly 'battery': z.infer<typeof BatteryStatusSchema>\n readonly 'brightness': z.infer<typeof BrightnessStatusSchema>\n readonly 'camera-credentials': z.infer<typeof CameraCredentialsStatusSchema>\n readonly 'device-discovery': z.infer<typeof DeviceDiscoveryStatusSchema>\n readonly 'device-status': z.infer<(typeof deviceStatusCapability)['status']['schema']>\n readonly 'doorbell': z.infer<typeof DoorbellStatusSchema>\n readonly 'feature-probe': z.infer<typeof FeatureProbeStatusSchema>\n readonly 'intercom': z.infer<typeof IntercomStatusSchema>\n readonly 'motion': z.infer<typeof MotionStatusSchema>\n readonly 'motion-trigger': z.infer<typeof MotionTriggerStatusSchema>\n readonly 'native-object-detection': z.infer<typeof NativeObjectDetectionStatusSchema>\n readonly 'osd': z.infer<typeof OsdStatusSchema>\n readonly 'ptz': z.infer<(typeof ptzCapability)['status']['schema']>\n readonly 'ptz-autotrack': z.infer<typeof PtzAutotrackStatusSchema>\n readonly 'snapshot': z.infer<typeof SnapshotStatusSchema>\n readonly 'switch': z.infer<typeof SwitchStatusSchema>\n}\n\n/** Union of every cap name that has a typed status block. */\nexport type CapNameWithStatus = keyof CapStatusTypeMap\n\n/**\n * Runtime list of cap names with status. Used by the settings\n * aggregator to enumerate caps whose `status` should be polled +\n * streamed via `subscribeDeviceStatusAggregate`.\n */\nexport const CAP_NAMES_WITH_STATUS = [\n 'accessories',\n 'battery',\n 'brightness',\n 'camera-credentials',\n 'device-discovery',\n 'device-status',\n 'doorbell',\n 'feature-probe',\n 'intercom',\n 'motion',\n 'motion-trigger',\n 'native-object-detection',\n 'osd',\n 'ptz',\n 'ptz-autotrack',\n 'snapshot',\n 'switch',\n] as const satisfies readonly CapNameWithStatus[]\n","/**\n * Strip the `userinfo` (`user:password@`) component from any URL we\n * surface in logs or telemetry. Camera credentials live inside RTSP /\n * RTMP / HTTP URLs by convention; emitting them verbatim leaks\n * plaintext secrets into log files, support transcripts, and shared\n * diagnostics. Returns the original string when the input is not a\n * parseable URL — a log helper must never throw.\n */\nexport function maskUrlCredentials(rawUrl: string): string {\n try {\n const u = new URL(rawUrl)\n if (!u.username && !u.password) return rawUrl\n u.username = ''\n u.password = ''\n return u.toString()\n } catch {\n return rawUrl\n }\n}\n","/**\n * Fixed-capacity ring buffer. When full, push() overwrites the oldest entry.\n * drain() returns up to maxCount items in FIFO order and removes them.\n */\nexport class RingBuffer<T> {\n private readonly items: Array<T | undefined>\n private head = 0\n private tail = 0\n private count = 0\n\n constructor(private readonly capacity: number) {\n this.items = Array.from<T | undefined>({ length: capacity })\n }\n\n get size(): number { return this.count }\n\n push(item: T): void {\n this.items[this.tail] = item\n this.tail = (this.tail + 1) % this.capacity\n if (this.count < this.capacity) {\n this.count++\n } else {\n this.head = (this.head + 1) % this.capacity\n }\n }\n\n drain(maxCount: number): T[] {\n const result: T[] = []\n const n = Math.min(maxCount, this.count)\n for (let i = 0; i < n; i++) {\n result.push(this.items[this.head]!)\n this.items[this.head] = undefined\n this.head = (this.head + 1) % this.capacity\n }\n this.count -= n\n return result\n }\n}\n","import type { z } from 'zod'\nimport type { CustomActionsSpec } from '../capabilities/custom-actions.js'\nimport type { AddonApi } from '../generated/addon-api.js'\n\n/**\n * Bind an addon's custom-action catalog to its tRPC surface, returning a\n * typed object with one method per action. Each method:\n * - Dispatches via `api.addons.custom.mutate({ addonId, action, input })`\n * - Inputs are typed as `z.infer<spec.input>`\n * - Outputs are typed as `z.infer<spec.output>`\n *\n * Example:\n * import { benchmarkActions } from '@camstack/addon-benchmark'\n * const benchmark = bindAddonActions(ctx.api, 'benchmark', benchmarkActions)\n * const { runId } = await benchmark.runBenchmark({ iterations: 100, target: 'cam-01' })\n */\nexport function bindAddonActions<T extends CustomActionsSpec>(\n api: AddonApi,\n addonId: string,\n catalog: T,\n): { [K in keyof T]: (input: z.infer<T[K]['input']>) => Promise<z.infer<T[K]['output']>> } {\n const out: Record<string, (input: unknown) => Promise<unknown>> = {}\n for (const action of Object.keys(catalog)) {\n out[action] = (input: unknown) => {\n const dispatcher = (\n api as unknown as {\n addons: {\n custom: {\n mutate: (args: { addonId: string; action: string; input: unknown }) => Promise<unknown>\n }\n }\n }\n ).addons.custom\n return dispatcher.mutate({ addonId, action, input })\n }\n }\n return out as never\n}\n"],"names":["EventCategory","hydrateSchema","streams","EventSourceType","errMsg","audioMetricsCapability","batteryCapability","brightnessCapability","cameraStreamsCapability","deviceDiscoveryCapability","deviceStatusCapability","doorbellCapability","featureProbeCapability","motionCapability","motionTriggerCapability","ptzAutotrackCapability","switchCapability","zoneAnalyticsCapability","zoneRulesCapability","zonesCapability","deviceProviderCapability","z","field","withTimeout","_accessoriesCapability","_addonPagesCapability","_addonPagesSourceCapability","_addonRoutesCapability","_addonSettingsCapability","_addonWidgetsCapability","_addonWidgetsSourceCapability","_addonsCapability","_adminUiCapability","_advancedNotifierCapability","_alertsCapability","_audioAnalysisCapability","_audioAnalyzerCapability","_audioCodecCapability","_audioMetricsCapability","_authProviderCapability","_authenticationCapability","_backupCapability","_batteryCapability","_brightnessCapability","_cameraCredentialsCapability","_cameraStreamsCapability","_decoderCapability","_detectionPipelineCapability","_deviceDiscoveryCapability","_deviceManagerCapability","_deviceOpsCapability","_deviceProviderCapability","_deviceStateCapability","_deviceStatusCapability","_doorbellCapability","_embeddingEncoderCapability","_eventsCapability","_featureProbeCapability","_integrationsCapability","_intercomCapability","_localNetworkCapability","_logDestinationCapability","_meshNetworkCapability","_meshOrchestratorCapability","_metricsProviderCapability","_motionCapability","_motionDetectionCapability","_motionTriggerCapability","_nativeObjectDetectionCapability","_networkAccessCapability","_networkQualityCapability","_nodesCapability","_notificationOutputCapability","_osdCapability","_pipelineAnalyticsCapability","_pipelineExecutorCapability","_pipelineOrchestratorCapability","_pipelineRunnerCapability","_platformProbeCapability","_ptzCapability","_ptzAutotrackCapability","_rebootCapability","_recordingCapability","_recordingEngineCapability","_remoteAccessCapability","_restreamerCapability","_settingsStoreCapability","_snapshotCapability","_snapshotProviderCapability","_storageCapability","_storageProviderCapability","_streamBrokerCapability","_streamingEngineCapability","_switchCapability","_systemCapability","_toastCapability","_turnOrchestratorCapability","_turnProviderCapability","_userManagementCapability","_webrtcCapability","_webrtcSessionCapability","_zoneAnalyticsCapability","_zoneRulesCapability","_zonesCapability"],"mappings":"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"}
|