@camstack/core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-2F3XZYRW.mjs +89 -0
- package/dist/chunk-2F3XZYRW.mjs.map +1 -0
- package/dist/chunk-LZOMFHX3.mjs +38 -0
- package/dist/chunk-LZOMFHX3.mjs.map +1 -0
- package/dist/index.d.mts +1536 -5
- package/dist/index.d.ts +1536 -5
- package/dist/index.js +8230 -526
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3929 -25
- package/dist/index.mjs.map +1 -1
- package/dist/storage-location-manager-F4YZMHGM.mjs +8 -0
- package/dist/storage-location-manager-F4YZMHGM.mjs.map +1 -0
- package/dist/wrapper-NTBY5HOA.mjs +3652 -0
- package/dist/wrapper-NTBY5HOA.mjs.map +1 -0
- package/package.json +20 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/events/event-bus.ts","../src/download/model-downloader.ts","../src/python/python-env-manager.ts","../src/addon/addon-loader.ts","../src/addon/addon-engine-manager.ts","../src/addon/addon-installer.ts","../src/pipeline/pipeline-validator.ts","../src/pipeline/pipeline-runner.ts"],"sourcesContent":["type EventCallback<T = unknown> = (data: T) => void\n\nexport class EventBus {\n private listeners = new Map<string, Set<EventCallback>>()\n\n on<T = unknown>(event: string, callback: EventCallback<T>): () => void {\n if (!this.listeners.has(event)) this.listeners.set(event, new Set())\n const set = this.listeners.get(event)!\n set.add(callback as EventCallback)\n return () => set.delete(callback as EventCallback)\n }\n\n emit<T = unknown>(event: string, data: T): void {\n const set = this.listeners.get(event)\n if (!set) return\n for (const cb of set) {\n try { cb(data) } catch { /* don't let one listener break others */ }\n }\n }\n\n once<T = unknown>(event: string, callback: EventCallback<T>): () => void {\n const unsub = this.on<T>(event, (data) => {\n unsub()\n callback(data)\n })\n return unsub\n }\n\n removeAllListeners(event?: string): void {\n if (event) this.listeners.delete(event)\n else this.listeners.clear()\n }\n\n listenerCount(event: string): number {\n return this.listeners.get(event)?.size ?? 0\n }\n}\n","import type { ModelDownloadOptions, ModelDownloadResult } from '@camstack/types'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as https from 'node:https'\nimport * as http from 'node:http'\nimport { createHash } from 'node:crypto'\n\nexport async function downloadModel(options: ModelDownloadOptions): Promise<ModelDownloadResult> {\n const { url, fallbackUrls = [], destDir, filename, expectedSha256, onProgress } = options\n\n // Derive filename from URL if not provided\n const fname = filename ?? url.split('/').pop() ?? 'model.bin'\n const destPath = path.join(destDir, fname)\n\n // Check cache\n if (fs.existsSync(destPath)) {\n return { filePath: destPath, downloadedBytes: 0, fromCache: true }\n }\n\n fs.mkdirSync(destDir, { recursive: true })\n\n // Try primary URL, then fallbacks\n const urls = [url, ...fallbackUrls]\n let lastError: Error | null = null\n\n for (const tryUrl of urls) {\n try {\n const bytes = await downloadFile(tryUrl, destPath, onProgress)\n\n // Verify SHA256 if provided\n if (expectedSha256) {\n const hash = await computeSha256(destPath)\n if (hash !== expectedSha256) {\n fs.unlinkSync(destPath)\n throw new Error(`SHA256 mismatch: expected ${expectedSha256}, got ${hash}`)\n }\n }\n\n return { filePath: destPath, downloadedBytes: bytes, fromCache: false }\n } catch (e) {\n lastError = e as Error\n if (fs.existsSync(destPath)) fs.unlinkSync(destPath) // cleanup partial\n }\n }\n\n throw lastError ?? new Error(`Failed to download model from ${url}`)\n}\n\nasync function downloadFile(\n url: string,\n destPath: string,\n onProgress?: (downloaded: number, total: number) => void,\n): Promise<number> {\n return new Promise((resolve, reject) => {\n const mod = url.startsWith('https') ? https : http\n\n const request = (targetUrl: string, redirectCount = 0) => {\n if (redirectCount > 5) return reject(new Error('Too many redirects'))\n\n mod.get(targetUrl, (res) => {\n // Handle redirects\n if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {\n return request(res.headers.location, redirectCount + 1)\n }\n\n if (res.statusCode !== 200) {\n return reject(new Error(`HTTP ${res.statusCode} downloading ${targetUrl}`))\n }\n\n const total = parseInt(res.headers['content-length'] ?? '0', 10)\n let downloaded = 0\n\n const fileStream = fs.createWriteStream(destPath)\n\n res.on('data', (chunk: Buffer) => {\n downloaded += chunk.length\n onProgress?.(downloaded, total)\n })\n\n res.pipe(fileStream)\n fileStream.on('finish', () => resolve(downloaded))\n fileStream.on('error', reject)\n res.on('error', reject)\n }).on('error', reject)\n }\n\n request(url)\n })\n}\n\nasync function computeSha256(filePath: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const hash = createHash('sha256')\n const stream = fs.createReadStream(filePath)\n stream.on('data', (chunk) => hash.update(chunk))\n stream.on('end', () => resolve(hash.digest('hex')))\n stream.on('error', reject)\n })\n}\n","import type { IPythonEnvironment, PythonProbeResult, PythonEnvReady } from '@camstack/types'\nimport { execFile, spawn } from 'node:child_process'\nimport type { ChildProcess } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\nconst execFileAsync = promisify(execFile)\n\nexport class PythonEnvManager implements IPythonEnvironment {\n private venvPath: string\n private cachedProbe: PythonProbeResult | null = null\n\n constructor(private readonly dataDir: string) {\n this.venvPath = path.join(dataDir, '.venv')\n }\n\n async probe(): Promise<PythonProbeResult> {\n if (this.cachedProbe) return this.cachedProbe\n\n // Try python3, then python\n for (const cmd of ['python3', 'python']) {\n try {\n const { stdout } = await execFileAsync(cmd, ['--version'])\n const version = stdout.trim().replace('Python ', '')\n const major = parseInt(version.split('.')[0] ?? '0', 10)\n const minor = parseInt(version.split('.')[1] ?? '0', 10)\n\n if (major < 3 || (major === 3 && minor < 10)) continue\n\n // Get path\n const { stdout: pathOut } = await execFileAsync(cmd, ['-c', 'import sys; print(sys.executable)'])\n\n this.cachedProbe = {\n available: true,\n version,\n path: pathOut.trim(),\n }\n return this.cachedProbe\n } catch { continue }\n }\n\n this.cachedProbe = { available: false }\n return this.cachedProbe\n }\n\n async ensure(options: { packages: readonly string[] }): Promise<PythonEnvReady> {\n const probe = await this.probe()\n if (!probe.available || !probe.path) {\n throw new Error('Python 3.10+ is required but not found on this system')\n }\n\n // Create venv if needed\n if (!fs.existsSync(path.join(this.venvPath, 'bin', 'python'))) {\n await execFileAsync(probe.path, ['-m', 'venv', this.venvPath])\n }\n\n const venvPython = path.join(this.venvPath, 'bin', 'python')\n\n // Install packages\n if (options.packages.length > 0) {\n await execFileAsync(venvPython, ['-m', 'pip', 'install', '-q', ...options.packages])\n }\n\n return {\n pythonPath: venvPython,\n venvPath: this.venvPath,\n spawn: (script: string, args: readonly string[]) => {\n return spawn(venvPython, [script, ...args])\n },\n }\n }\n\n spawn(script: string, args: readonly string[]): ChildProcess {\n const venvPython = path.join(this.venvPath, 'bin', 'python')\n if (!fs.existsSync(venvPython)) {\n throw new Error('Python venv not initialized. Call ensure() first.')\n }\n return spawn(venvPython, [script, ...args])\n }\n}\n","import type { AddonDeclaration, AddonPackageManifest, ICamstackAddon } from '@camstack/types'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type { AddonInstaller } from './addon-installer.js'\n\nexport interface RegisteredAddon {\n readonly declaration: AddonDeclaration\n readonly packageName: string\n readonly addonClass: new () => ICamstackAddon\n}\n\nexport class AddonLoader {\n private addons = new Map<string, RegisteredAddon>()\n\n /** Load all addons from an npm package */\n async loadPackage(packageName: string): Promise<void> {\n // Read package.json\n const pkgJsonPath = require.resolve(`${packageName}/package.json`)\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as Record<string, unknown>\n const manifest = pkgJson['camstack'] as AddonPackageManifest | undefined\n\n if (!manifest?.addons?.length) {\n throw new Error(`Package ${packageName} has no camstack.addons manifest`)\n }\n\n for (const declaration of manifest.addons) {\n // Dynamic import the addon module\n const entryPath = require.resolve(\n `${packageName}/${declaration.entry.replace('./dist/', '').replace(/\\.js$/, '')}`,\n )\n const mod = (await import(entryPath)) as Record<string, unknown>\n const AddonClass = (mod['default'] ?? mod[Object.keys(mod)[0]!]) as (new () => ICamstackAddon) | undefined\n\n if (!AddonClass) {\n throw new Error(`Addon ${declaration.id} from ${packageName} has no default export`)\n }\n\n this.addons.set(declaration.id, {\n declaration,\n packageName,\n addonClass: AddonClass,\n })\n }\n }\n\n /** Load addon from a direct path (for development/testing) */\n async loadFromPath(addonId: string, modulePath: string, packageName: string): Promise<void> {\n const mod = (await import(modulePath)) as Record<string, unknown>\n const AddonClass = (mod['default'] ?? mod[Object.keys(mod)[0]!]) as (new () => ICamstackAddon) | undefined\n\n if (!AddonClass) {\n throw new Error(`Module ${modulePath} has no default export`)\n }\n\n this.addons.set(addonId, {\n declaration: { id: addonId, entry: modulePath, slot: 'detector' as AddonDeclaration['slot'] },\n packageName,\n addonClass: AddonClass,\n })\n }\n\n /** Get a registered addon by ID */\n getAddon(addonId: string): RegisteredAddon | undefined {\n return this.addons.get(addonId)\n }\n\n /** List all registered addons */\n listAddons(): RegisteredAddon[] {\n return [...this.addons.values()]\n }\n\n /** Check if an addon is registered */\n hasAddon(addonId: string): boolean {\n return this.addons.has(addonId)\n }\n\n /** Create a new instance of an addon (not yet initialized) */\n createInstance(addonId: string): ICamstackAddon {\n const registered = this.addons.get(addonId)\n if (!registered) {\n throw new Error(`Addon \"${addonId}\" is not registered`)\n }\n return new registered.addonClass()\n }\n\n /** Load all installed addon packages from the addon directory */\n async loadAllInstalled(installer: AddonInstaller): Promise<void> {\n const installed = installer.listInstalled()\n for (const pkg of installed) {\n try {\n await this.loadPackageFromPath(pkg.name, pkg.path)\n } catch (error: unknown) {\n // Log but don't fail — one broken addon shouldn't prevent others from loading\n const message = error instanceof Error ? error.message : String(error)\n console.warn(`Failed to load addon package ${pkg.name}: ${message}`)\n }\n }\n }\n\n /** Load addon package from a specific filesystem path */\n async loadPackageFromPath(packageName: string, packagePath: string): Promise<void> {\n const pkgJsonPath = path.join(packagePath, 'package.json')\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as Record<string, unknown>\n const manifest = pkgJson['camstack'] as AddonPackageManifest | undefined\n\n if (!manifest?.addons?.length) {\n throw new Error(`Package ${packageName} at ${packagePath} has no camstack.addons manifest`)\n }\n\n for (const declaration of manifest.addons) {\n const entryPath = path.join(packagePath, declaration.entry)\n const mod = (await import(entryPath)) as Record<string, unknown>\n const AddonClass = (mod['default'] ?? mod[Object.keys(mod)[0]!]) as (new () => ICamstackAddon) | undefined\n\n if (!AddonClass) {\n throw new Error(`Addon ${declaration.id} from ${packageName} has no default export`)\n }\n\n this.addons.set(declaration.id, {\n declaration,\n packageName,\n addonClass: AddonClass,\n })\n }\n }\n}\n","import type { ICamstackAddon, AddonContext } from '@camstack/types'\nimport { AddonLoader } from './addon-loader.js'\nimport { createHash } from 'node:crypto'\n\nexport class AddonEngineManager {\n private engines = new Map<string, ICamstackAddon>()\n\n constructor(\n private readonly loader: AddonLoader,\n private readonly baseContext: Omit<AddonContext, 'addonConfig'>,\n ) {}\n\n /**\n * Get or create an addon engine for the given effective config.\n * Cameras with the same addonId + effective config share the same engine.\n */\n async getOrCreateEngine(\n addonId: string,\n globalConfig: Record<string, unknown>,\n cameraOverride?: Record<string, unknown>,\n ): Promise<ICamstackAddon> {\n // Merge global + override\n const effectiveConfig = { ...globalConfig, ...cameraOverride }\n const configKey = `${addonId}:${this.hashConfig(effectiveConfig)}`\n\n const existing = this.engines.get(configKey)\n if (existing) return existing\n\n // Create new instance\n const addon = this.loader.createInstance(addonId)\n await addon.initialize({ ...this.baseContext, addonConfig: effectiveConfig })\n this.engines.set(configKey, addon)\n return addon\n }\n\n /** Get all active engines */\n getActiveEngines(): Map<string, ICamstackAddon> {\n return new Map(this.engines)\n }\n\n /** Shutdown a specific engine by its config key */\n async shutdownEngine(configKey: string): Promise<void> {\n const engine = this.engines.get(configKey)\n if (engine) {\n await engine.shutdown()\n this.engines.delete(configKey)\n }\n }\n\n /** Shutdown all engines */\n async shutdownAll(): Promise<void> {\n for (const [, engine] of this.engines) {\n await engine.shutdown()\n }\n this.engines.clear()\n }\n\n /** Compute a deterministic config key (visible for tests) */\n computeConfigKey(addonId: string, effectiveConfig: Record<string, unknown>): string {\n return `${addonId}:${this.hashConfig(effectiveConfig)}`\n }\n\n private hashConfig(config: Record<string, unknown>): string {\n const sorted = JSON.stringify(config, Object.keys(config).sort())\n return createHash('md5').update(sorted).digest('hex').slice(0, 12)\n }\n}\n","import { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\nconst execFileAsync = promisify(execFile)\n\nexport interface AddonInstallerConfig {\n /** Directory where addons are installed (e.g., ~/.camstack/addons or {dataDir}/addons) */\n readonly addonsDir: string\n /** Builtin packages to auto-install on first boot */\n readonly builtinPackages: readonly string[]\n /** npm registry URL (default: https://registry.npmjs.org) */\n readonly registry?: string\n}\n\nexport interface InstalledPackage {\n readonly name: string\n readonly version: string\n readonly path: string\n}\n\nexport class AddonInstaller {\n constructor(private readonly config: AddonInstallerConfig) {}\n\n /** Initialize addon directory — create if not exists, install builtins if needed */\n async initialize(): Promise<void> {\n await this.ensureAddonDirectory()\n await this.installBuiltins()\n }\n\n /** Ensure addon directory exists with a package.json */\n private async ensureAddonDirectory(): Promise<void> {\n const { addonsDir } = this.config\n fs.mkdirSync(addonsDir, { recursive: true })\n\n const pkgJsonPath = path.join(addonsDir, 'package.json')\n if (!fs.existsSync(pkgJsonPath)) {\n const pkgJson = {\n name: 'camstack-addons',\n version: '1.0.0',\n private: true,\n description: 'CamStack addon packages — managed automatically',\n dependencies: {},\n }\n fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2))\n }\n }\n\n /** Install builtin packages if not already present */\n private async installBuiltins(): Promise<void> {\n const missing = this.config.builtinPackages.filter(pkg => !this.isInstalled(pkg))\n if (missing.length === 0) return\n\n await this.installPackages(missing)\n }\n\n /** Check if a package is installed */\n isInstalled(packageName: string): boolean {\n const pkgPath = path.join(this.config.addonsDir, 'node_modules', packageName, 'package.json')\n return fs.existsSync(pkgPath)\n }\n\n /** Get installed package info */\n getInstalledPackage(packageName: string): InstalledPackage | null {\n const pkgPath = path.join(this.config.addonsDir, 'node_modules', packageName, 'package.json')\n if (!fs.existsSync(pkgPath)) return null\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as { name: string; version: string }\n return {\n name: pkg.name,\n version: pkg.version,\n path: path.dirname(pkgPath),\n }\n }\n\n /** List all installed addon packages (those with camstack.addons in package.json) */\n listInstalled(): InstalledPackage[] {\n const nodeModulesDir = path.join(this.config.addonsDir, 'node_modules')\n if (!fs.existsSync(nodeModulesDir)) return []\n\n const packages: InstalledPackage[] = []\n\n // Check scoped packages (@camstack/*)\n const scopeDirs = fs.readdirSync(nodeModulesDir).filter(d => d.startsWith('@'))\n for (const scope of scopeDirs) {\n const scopePath = path.join(nodeModulesDir, scope)\n if (!fs.statSync(scopePath).isDirectory()) continue\n for (const pkg of fs.readdirSync(scopePath)) {\n const pkgJsonPath = path.join(scopePath, pkg, 'package.json')\n if (!fs.existsSync(pkgJsonPath)) continue\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as {\n name: string\n version: string\n camstack?: { addons?: unknown[] }\n }\n if (pkgJson.camstack?.addons) {\n packages.push({ name: pkgJson.name, version: pkgJson.version, path: path.join(scopePath, pkg) })\n }\n }\n }\n\n // Check non-scoped packages\n for (const dir of fs.readdirSync(nodeModulesDir)) {\n if (dir.startsWith('@') || dir.startsWith('.')) continue\n const pkgJsonPath = path.join(nodeModulesDir, dir, 'package.json')\n if (!fs.existsSync(pkgJsonPath)) continue\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as {\n name: string\n version: string\n camstack?: { addons?: unknown[] }\n }\n if (pkgJson.camstack?.addons) {\n packages.push({ name: pkgJson.name, version: pkgJson.version, path: path.join(nodeModulesDir, dir) })\n }\n }\n\n return packages\n }\n\n /** Install one or more packages from npm */\n async installPackages(packages: readonly string[]): Promise<void> {\n if (packages.length === 0) return\n\n const args = ['install', '--save', ...packages]\n if (this.config.registry) {\n args.push('--registry', this.config.registry)\n }\n\n await execFileAsync('npm', args, {\n cwd: this.config.addonsDir,\n timeout: 120_000, // 2 min timeout\n })\n\n // npm install --save already updates package.json, but re-read and re-write to normalise formatting\n const pkgJsonPath = path.join(this.config.addonsDir, 'package.json')\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as Record<string, unknown>\n fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2))\n }\n\n /** Uninstall a package */\n async uninstallPackage(packageName: string): Promise<void> {\n // Prevent uninstalling builtins\n if (this.config.builtinPackages.includes(packageName)) {\n throw new Error(`Cannot uninstall builtin package: ${packageName}`)\n }\n\n await execFileAsync('npm', ['uninstall', packageName], {\n cwd: this.config.addonsDir,\n timeout: 60_000,\n })\n }\n\n /** Update a package to latest version */\n async updatePackage(packageName: string): Promise<void> {\n await execFileAsync('npm', ['update', packageName], {\n cwd: this.config.addonsDir,\n timeout: 120_000,\n })\n }\n\n /** Update all packages */\n async updateAll(): Promise<void> {\n await execFileAsync('npm', ['update'], {\n cwd: this.config.addonsDir,\n timeout: 300_000,\n })\n }\n\n /** Get the node_modules path for require/import resolution */\n getNodeModulesPath(): string {\n return path.join(this.config.addonsDir, 'node_modules')\n }\n}\n\n/** Default builtin packages that are auto-installed */\nexport const BUILTIN_PACKAGES: readonly string[] = [\n '@camstack/vision',\n '@camstack/pipeline-analysis',\n '@camstack/benchmark',\n] as const\n","import type { PipelineConfig, PipelineNode } from '@camstack/types'\nimport { AddonLoader } from '../addon/addon-loader.js'\n\nexport interface ValidationIssue {\n readonly step?: string\n readonly addon?: string\n readonly message: string\n readonly severity: 'error' | 'warning'\n}\n\nexport interface ValidationResult {\n readonly valid: boolean\n readonly errors: readonly ValidationIssue[]\n readonly warnings: readonly ValidationIssue[]\n}\n\nexport class PipelineValidator {\n constructor(private readonly loader: AddonLoader) {}\n\n validate(config: PipelineConfig): ValidationResult {\n const errors: ValidationIssue[] = []\n const warnings: ValidationIssue[] = []\n\n // Validate video pipeline\n for (const node of config.video) {\n this.validateNode(node, errors, warnings, [])\n }\n\n // Validate audio\n if (config.audio) {\n this.validateNode(config.audio, errors, warnings, [])\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n }\n\n private validateNode(\n node: PipelineNode,\n errors: ValidationIssue[],\n warnings: ValidationIssue[],\n ancestors: PipelineNode[],\n ): void {\n // Check addon exists\n if (!this.loader.hasAddon(node.addon)) {\n errors.push({\n step: node.step,\n addon: node.addon,\n message: `Addon \"${node.addon}\" is not registered`,\n severity: 'error',\n })\n return\n }\n\n // Check requiredSteps (from addon manifest)\n const registered = this.loader.getAddon(node.addon)\n if (registered) {\n const manifest = registered.declaration\n // Warn if a node appears at root but its addon has inputClasses\n // (This is a simplified check — full implementation would read manifest.requiredSteps)\n void manifest // reserved for future manifest-driven checks\n }\n\n // Validate children recursively\n if (node.children) {\n for (const child of node.children) {\n this.validateNode(child, errors, warnings, [...ancestors, node])\n }\n }\n }\n}\n","import type {\n FrameInput,\n CropInput,\n PipelineConfig,\n PipelineNode,\n PipelineResult,\n StepResult,\n DetectorOutput,\n CropperOutput,\n ClassifierOutput,\n SpatialDetection,\n PipelineSlot,\n ICamstackAddon,\n} from '@camstack/types'\nimport { AddonEngineManager } from '../addon/addon-engine-manager.js'\nimport { randomUUID } from 'node:crypto'\n\nexport class PipelineRunner {\n constructor(\n private readonly engineManager: AddonEngineManager,\n private readonly addonConfigs: Map<string, Record<string, unknown>>, // addonId → global config\n ) {}\n\n async run(frame: FrameInput, config: PipelineConfig): Promise<PipelineResult> {\n const startTime = performance.now()\n const results: StepResult[] = []\n const timings: Record<string, number> = {}\n\n // Execute video pipeline root nodes\n for (const rootNode of config.video) {\n await this.executeNode(rootNode, frame, null, results, timings)\n }\n\n // Execute audio node if present (independent)\n if (config.audio) {\n await this.executeNode(config.audio, frame, null, results, timings)\n }\n\n return {\n results,\n totalMs: performance.now() - startTime,\n timings,\n frameTimestamp: frame.timestamp,\n }\n }\n\n private async executeNode(\n node: PipelineNode,\n frame: FrameInput,\n parentDetection: { det: SpatialDetection; resultId: string } | null,\n results: StepResult[],\n timings: Record<string, number>,\n ): Promise<void> {\n const resultId = randomUUID()\n const stepStart = performance.now()\n\n try {\n // Get or create engine for this addon\n const globalConfig = this.addonConfigs.get(node.addon) ?? {}\n const engine = await this.engineManager.getOrCreateEngine(\n node.addon, globalConfig, node.configOverride,\n )\n\n // Determine slot type and execute\n let output: DetectorOutput | CropperOutput | ClassifierOutput\n\n if (parentDetection) {\n // This is a child node — needs crop input\n const cropInput: CropInput = {\n frame,\n roi: parentDetection.det.bbox,\n parentDetection: parentDetection.det,\n }\n\n // Try cropper first, then classifier, then detector\n if ('crop' in engine && typeof (engine as unknown as Record<string, unknown>)['crop'] === 'function') {\n output = await (engine as unknown as { crop: (input: CropInput) => Promise<CropperOutput> }).crop(cropInput)\n } else if ('classify' in engine && typeof (engine as unknown as Record<string, unknown>)['classify'] === 'function') {\n output = await (engine as unknown as { classify: (input: CropInput) => Promise<ClassifierOutput> }).classify(cropInput)\n } else if ('detect' in engine && typeof (engine as unknown as Record<string, unknown>)['detect'] === 'function') {\n // Detector child (e.g., object-detection triggered by motion)\n output = await (engine as unknown as { detect: (input: FrameInput) => Promise<DetectorOutput> }).detect(frame)\n } else {\n throw new Error(`Addon \"${node.addon}\" has no detect/crop/classify method`)\n }\n } else {\n // Root node — full frame\n if ('detect' in engine && typeof (engine as unknown as Record<string, unknown>)['detect'] === 'function') {\n output = await (engine as unknown as { detect: (input: FrameInput) => Promise<DetectorOutput> }).detect(frame)\n } else if ('classify' in engine && typeof (engine as unknown as Record<string, unknown>)['classify'] === 'function') {\n // Audio classifier at root\n const rootCropInput: CropInput = {\n frame,\n roi: { x: 0, y: 0, w: frame.width, h: frame.height },\n parentDetection: { class: '', originalClass: '', score: 1, bbox: { x: 0, y: 0, w: frame.width, h: frame.height } },\n }\n output = await (engine as unknown as { classify: (input: CropInput) => Promise<ClassifierOutput> }).classify(rootCropInput)\n } else {\n throw new Error(`Addon \"${node.addon}\" has no detect/classify method`)\n }\n }\n\n const stepMs = performance.now() - stepStart\n\n // Determine slot type from output shape\n const slot: PipelineSlot =\n 'detections' in output ? 'detector' :\n 'crops' in output ? 'cropper' :\n 'classifier'\n\n const stepResult: StepResult = {\n addon: node.addon,\n slot,\n output,\n parentResultId: parentDetection?.resultId,\n resultId,\n inferenceMs: output.inferenceMs,\n preprocessMs: 0,\n totalMs: stepMs,\n }\n results.push(stepResult)\n timings[node.step] = stepMs\n\n // Process children\n if (node.children?.length) {\n const detections: readonly SpatialDetection[] =\n 'detections' in output ? output.detections :\n 'crops' in output ? output.crops :\n []\n\n // For each detection, find children with matching inputClasses and execute\n await this.executeChildren(node.children, frame, detections, resultId, results, timings)\n }\n } catch (error: unknown) {\n const stepMs = performance.now() - stepStart\n const message = error instanceof Error ? error.message : String(error)\n results.push({\n addon: node.addon,\n slot: 'detector',\n output: { detections: [], inferenceMs: 0, modelId: '' },\n parentResultId: parentDetection?.resultId,\n resultId,\n inferenceMs: 0,\n preprocessMs: 0,\n totalMs: stepMs,\n error: {\n code: 'ADDON_ERROR',\n message,\n childrenSkipped: true,\n },\n })\n }\n }\n\n private async executeChildren(\n children: readonly PipelineNode[],\n frame: FrameInput,\n parentDetections: readonly SpatialDetection[],\n parentResultId: string,\n results: StepResult[],\n timings: Record<string, number>,\n ): Promise<void> {\n // For each parent detection, run all matching children in parallel\n const promises: Promise<void>[] = []\n\n for (const detection of parentDetections) {\n for (const child of children) {\n // TODO: Check inputClasses matching from addon manifest\n // For now, run all children on all detections (caller configures tree correctly)\n promises.push(\n this.executeNode(child, frame, { det: detection, resultId: parentResultId }, results, timings),\n )\n }\n }\n\n await Promise.allSettled(promises)\n }\n}\n"],"mappings":"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","names":["fs","path","fs","path","createHash","execFile","promisify","fs","path","execFileAsync"]}
|
|
1
|
+
{"version":3,"sources":["../src/events/event-bus.ts","../src/download/model-downloader.ts","../src/python/python-env-manager.ts","../src/addon/addon-loader.ts","../src/addon/addon-engine-manager.ts","../src/addon/addon-installer.ts","../src/pipeline/pipeline-validator.ts","../src/pipeline/pipeline-runner.ts","../src/capability/capability-registry.ts","../src/capability/infra-capabilities.ts","../src/process/managed-process.ts","../src/process/process-manager.ts","../src/network/network-quality.ts","../src/repl/repl-engine.ts","../src/agent/agent-registry.ts","../src/agent/task-dispatcher.ts","../src/agent/agent-client.ts","../src/agent/agent-task-runner.ts","../src/agent/pipeline-task-handlers.ts","../src/builtins/sqlite-storage/sqlite-storage.provider.ts","../src/builtins/sqlite-storage/sqlite-storage.addon.ts","../src/builtins/winston-logging/winston-destination.ts","../src/builtins/winston-logging/winston-logging.addon.ts","../src/builtins/local-backup/local-backup.ts","../src/builtins/local-backup/local-backup.addon.ts","../src/builtins/admin-ui/addon.ts","../src/lifecycle/lifecycle-state-machine.ts","../src/feature/feature-manager.ts","../src/config/config-manager.ts","../src/config/config-schema.ts","../src/events/system-event-bus.ts","../src/logging/log-ring-buffer.ts","../src/logging/scoped-logger.ts","../src/logging/log-manager.ts","../src/storage/storage-manager.ts","../src/storage/settings-store.ts","../src/storage/sql-schema.ts","../src/auth/auth-manager.ts","../src/auth/api-key-manager.ts","../src/auth/user-manager.ts","../src/auth/scoped-token-manager.ts","../src/notification/notification-service.ts","../src/notification/toast-service.ts","../src/addon-routes/addon-route-registry.ts","../src/device/device-registry.ts","../src/device/capability-resolver.ts","../src/provider/provider-manager.ts"],"sourcesContent":["type EventCallback<T = unknown> = (data: T) => void\n\nexport class EventBus {\n private listeners = new Map<string, Set<EventCallback>>()\n\n on<T = unknown>(event: string, callback: EventCallback<T>): () => void {\n if (!this.listeners.has(event)) this.listeners.set(event, new Set())\n const set = this.listeners.get(event)!\n set.add(callback as EventCallback)\n return () => set.delete(callback as EventCallback)\n }\n\n emit<T = unknown>(event: string, data: T): void {\n const set = this.listeners.get(event)\n if (!set) return\n for (const cb of set) {\n try { cb(data) } catch { /* don't let one listener break others */ }\n }\n }\n\n once<T = unknown>(event: string, callback: EventCallback<T>): () => void {\n const unsub = this.on<T>(event, (data) => {\n unsub()\n callback(data)\n })\n return unsub\n }\n\n removeAllListeners(event?: string): void {\n if (event) this.listeners.delete(event)\n else this.listeners.clear()\n }\n\n listenerCount(event: string): number {\n return this.listeners.get(event)?.size ?? 0\n }\n}\n","import type { ModelDownloadOptions, ModelDownloadResult } from '@camstack/types'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as https from 'node:https'\nimport * as http from 'node:http'\nimport { createHash } from 'node:crypto'\n\nexport async function downloadModel(options: ModelDownloadOptions): Promise<ModelDownloadResult> {\n const { url, fallbackUrls = [], destDir, filename, expectedSha256, onProgress } = options\n\n // Derive filename from URL if not provided\n const fname = filename ?? url.split('/').pop() ?? 'model.bin'\n const destPath = path.join(destDir, fname)\n\n // Check cache\n if (fs.existsSync(destPath)) {\n return { filePath: destPath, downloadedBytes: 0, fromCache: true }\n }\n\n fs.mkdirSync(destDir, { recursive: true })\n\n // Try primary URL, then fallbacks\n const urls = [url, ...fallbackUrls]\n let lastError: Error | null = null\n\n for (const tryUrl of urls) {\n try {\n const bytes = await downloadFile(tryUrl, destPath, onProgress)\n\n // Verify SHA256 if provided\n if (expectedSha256) {\n const hash = await computeSha256(destPath)\n if (hash !== expectedSha256) {\n fs.unlinkSync(destPath)\n throw new Error(`SHA256 mismatch: expected ${expectedSha256}, got ${hash}`)\n }\n }\n\n return { filePath: destPath, downloadedBytes: bytes, fromCache: false }\n } catch (e) {\n lastError = e as Error\n if (fs.existsSync(destPath)) fs.unlinkSync(destPath) // cleanup partial\n }\n }\n\n throw lastError ?? new Error(`Failed to download model from ${url}`)\n}\n\nasync function downloadFile(\n url: string,\n destPath: string,\n onProgress?: (downloaded: number, total: number) => void,\n): Promise<number> {\n return new Promise((resolve, reject) => {\n const mod = url.startsWith('https') ? https : http\n\n const request = (targetUrl: string, redirectCount = 0) => {\n if (redirectCount > 5) return reject(new Error('Too many redirects'))\n\n mod.get(targetUrl, (res) => {\n // Handle redirects\n if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {\n return request(res.headers.location, redirectCount + 1)\n }\n\n if (res.statusCode !== 200) {\n return reject(new Error(`HTTP ${res.statusCode} downloading ${targetUrl}`))\n }\n\n const total = parseInt(res.headers['content-length'] ?? '0', 10)\n let downloaded = 0\n\n const fileStream = fs.createWriteStream(destPath)\n\n res.on('data', (chunk: Buffer) => {\n downloaded += chunk.length\n onProgress?.(downloaded, total)\n })\n\n res.pipe(fileStream)\n fileStream.on('finish', () => resolve(downloaded))\n fileStream.on('error', reject)\n res.on('error', reject)\n }).on('error', reject)\n }\n\n request(url)\n })\n}\n\nasync function computeSha256(filePath: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const hash = createHash('sha256')\n const stream = fs.createReadStream(filePath)\n stream.on('data', (chunk) => hash.update(chunk))\n stream.on('end', () => resolve(hash.digest('hex')))\n stream.on('error', reject)\n })\n}\n","import type { IPythonEnvironment, PythonProbeResult, PythonEnvReady } from '@camstack/types'\nimport { execFile, spawn } from 'node:child_process'\nimport type { ChildProcess } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\nconst execFileAsync = promisify(execFile)\n\nexport class PythonEnvManager implements IPythonEnvironment {\n private venvPath: string\n private cachedProbe: PythonProbeResult | null = null\n\n constructor(private readonly dataDir: string) {\n this.venvPath = path.join(dataDir, '.venv')\n }\n\n async probe(): Promise<PythonProbeResult> {\n if (this.cachedProbe) return this.cachedProbe\n\n // Try python3, then python\n for (const cmd of ['python3', 'python']) {\n try {\n const { stdout } = await execFileAsync(cmd, ['--version'])\n const version = stdout.trim().replace('Python ', '')\n const major = parseInt(version.split('.')[0] ?? '0', 10)\n const minor = parseInt(version.split('.')[1] ?? '0', 10)\n\n if (major < 3 || (major === 3 && minor < 10)) continue\n\n // Get path\n const { stdout: pathOut } = await execFileAsync(cmd, ['-c', 'import sys; print(sys.executable)'])\n\n this.cachedProbe = {\n available: true,\n version,\n path: pathOut.trim(),\n }\n return this.cachedProbe\n } catch { continue }\n }\n\n this.cachedProbe = { available: false }\n return this.cachedProbe\n }\n\n async ensure(options: { packages: readonly string[] }): Promise<PythonEnvReady> {\n const probe = await this.probe()\n if (!probe.available || !probe.path) {\n throw new Error('Python 3.10+ is required but not found on this system')\n }\n\n // Create venv if needed\n if (!fs.existsSync(path.join(this.venvPath, 'bin', 'python'))) {\n await execFileAsync(probe.path, ['-m', 'venv', this.venvPath])\n }\n\n const venvPython = path.join(this.venvPath, 'bin', 'python')\n\n // Install packages\n if (options.packages.length > 0) {\n await execFileAsync(venvPython, ['-m', 'pip', 'install', '-q', ...options.packages])\n }\n\n return {\n pythonPath: venvPython,\n venvPath: this.venvPath,\n spawn: (script: string, args: readonly string[]) => {\n return spawn(venvPython, [script, ...args])\n },\n }\n }\n\n spawn(script: string, args: readonly string[]): ChildProcess {\n const venvPython = path.join(this.venvPath, 'bin', 'python')\n if (!fs.existsSync(venvPython)) {\n throw new Error('Python venv not initialized. Call ensure() first.')\n }\n return spawn(venvPython, [script, ...args])\n }\n}\n","import type { AddonDeclaration, AddonPackageManifest, ICamstackAddon } from '@camstack/types'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type { AddonInstaller } from './addon-installer.js'\n\nexport interface RegisteredAddon {\n readonly declaration: AddonDeclaration\n readonly packageName: string\n readonly addonClass: new () => ICamstackAddon\n}\n\n/**\n * Resolve the addon class from a dynamically imported module.\n *\n * CJS modules built with tsup can produce double-wrapped default exports:\n * mod.default = { default: ActualClass }\n * This helper unwraps that pattern so we always get the constructor.\n */\nfunction resolveAddonClass(mod: Record<string, unknown>): (new () => ICamstackAddon) | undefined {\n let candidate = mod['default'] ?? mod[Object.keys(mod)[0]!]\n\n // Unwrap double-nested default (CJS interop with tsup)\n if (candidate && typeof candidate === 'object' && 'default' in (candidate as Record<string, unknown>)) {\n candidate = (candidate as Record<string, unknown>)['default']\n }\n\n return typeof candidate === 'function'\n ? (candidate as new () => ICamstackAddon)\n : undefined\n}\n\nexport class AddonLoader {\n private addons = new Map<string, RegisteredAddon>()\n\n /** Load all addons from an npm package */\n async loadPackage(packageName: string): Promise<void> {\n // Read package.json\n const pkgJsonPath = require.resolve(`${packageName}/package.json`)\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as Record<string, unknown>\n const manifest = pkgJson['camstack'] as AddonPackageManifest | undefined\n\n if (!manifest?.addons?.length) {\n throw new Error(`Package ${packageName} has no camstack.addons manifest`)\n }\n\n for (const declaration of manifest.addons) {\n // Dynamic import the addon module\n const entryPath = require.resolve(\n `${packageName}/${declaration.entry.replace('./dist/', '').replace(/\\.js$/, '')}`,\n )\n const mod = (await import(entryPath)) as Record<string, unknown>\n const AddonClass = resolveAddonClass(mod)\n\n if (!AddonClass) {\n throw new Error(`Addon ${declaration.id} from ${packageName} has no default export`)\n }\n\n this.addons.set(declaration.id, {\n declaration,\n packageName,\n addonClass: AddonClass,\n })\n }\n }\n\n /** Load addon from a direct path (for development/testing) */\n async loadFromPath(\n addonId: string,\n modulePath: string,\n packageName: string,\n declaration?: Partial<AddonDeclaration>,\n ): Promise<void> {\n const mod = (await import(modulePath)) as Record<string, unknown>\n const AddonClass = resolveAddonClass(mod)\n\n if (!AddonClass) {\n throw new Error(`Module ${modulePath} has no default export`)\n }\n\n this.addons.set(addonId, {\n declaration: {\n id: addonId,\n entry: modulePath,\n slot: 'detector' as AddonDeclaration['slot'],\n ...declaration,\n },\n packageName,\n addonClass: AddonClass,\n })\n }\n\n /** Get a registered addon by ID */\n getAddon(addonId: string): RegisteredAddon | undefined {\n return this.addons.get(addonId)\n }\n\n /** List all registered addons */\n listAddons(): RegisteredAddon[] {\n return [...this.addons.values()]\n }\n\n /** Check if an addon is registered */\n hasAddon(addonId: string): boolean {\n return this.addons.has(addonId)\n }\n\n /** Create a new instance of an addon (not yet initialized) */\n createInstance(addonId: string): ICamstackAddon {\n const registered = this.addons.get(addonId)\n if (!registered) {\n throw new Error(`Addon \"${addonId}\" is not registered`)\n }\n return new registered.addonClass()\n }\n\n /** Load all installed addon packages from the addon directory */\n async loadAllInstalled(installer: AddonInstaller): Promise<void> {\n const installed = installer.listInstalled()\n for (const pkg of installed) {\n try {\n await this.loadPackageFromPath(pkg.name, pkg.path)\n } catch (error: unknown) {\n // Log but don't fail — one broken addon shouldn't prevent others from loading\n const message = error instanceof Error ? error.message : String(error)\n console.warn(`Failed to load addon package ${pkg.name}: ${message}`)\n }\n }\n }\n\n /** Load addon package from a specific filesystem path */\n async loadPackageFromPath(packageName: string, packagePath: string): Promise<void> {\n const pkgJsonPath = path.join(packagePath, 'package.json')\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as Record<string, unknown>\n const manifest = pkgJson['camstack'] as AddonPackageManifest | undefined\n\n if (!manifest?.addons?.length) {\n throw new Error(`Package ${packageName} at ${packagePath} has no camstack.addons manifest`)\n }\n\n for (const declaration of manifest.addons) {\n const entryPath = path.join(packagePath, declaration.entry)\n const mod = (await import(entryPath)) as Record<string, unknown>\n const AddonClass = resolveAddonClass(mod)\n\n if (!AddonClass) {\n throw new Error(`Addon ${declaration.id} from ${packageName} has no default export`)\n }\n\n this.addons.set(declaration.id, {\n declaration,\n packageName,\n addonClass: AddonClass,\n })\n }\n }\n}\n","import type { ICamstackAddon, AddonContext } from '@camstack/types'\nimport { AddonLoader } from './addon-loader.js'\nimport { createHash } from 'node:crypto'\n\nexport class AddonEngineManager {\n private engines = new Map<string, ICamstackAddon>()\n\n constructor(\n private readonly loader: AddonLoader,\n private readonly baseContext: Partial<Omit<AddonContext, 'addonConfig'>>,\n ) {}\n\n /**\n * Get or create an addon engine for the given effective config.\n * Cameras with the same addonId + effective config share the same engine.\n */\n async getOrCreateEngine(\n addonId: string,\n globalConfig: Record<string, unknown>,\n cameraOverride?: Record<string, unknown>,\n ): Promise<ICamstackAddon> {\n // Merge global + override\n const effectiveConfig = { ...globalConfig, ...cameraOverride }\n const configKey = `${addonId}:${this.hashConfig(effectiveConfig)}`\n\n const existing = this.engines.get(configKey)\n if (existing) return existing\n\n // Create new instance\n const addon = this.loader.createInstance(addonId)\n await addon.initialize({ ...this.baseContext, addonConfig: effectiveConfig } as AddonContext)\n this.engines.set(configKey, addon)\n return addon\n }\n\n /** Get all active engines */\n getActiveEngines(): Map<string, ICamstackAddon> {\n return new Map(this.engines)\n }\n\n /** Shutdown a specific engine by its config key */\n async shutdownEngine(configKey: string): Promise<void> {\n const engine = this.engines.get(configKey)\n if (engine) {\n await engine.shutdown()\n this.engines.delete(configKey)\n }\n }\n\n /** Shutdown all engines */\n async shutdownAll(): Promise<void> {\n for (const [, engine] of this.engines) {\n await engine.shutdown()\n }\n this.engines.clear()\n }\n\n /** Compute a deterministic config key (visible for tests) */\n computeConfigKey(addonId: string, effectiveConfig: Record<string, unknown>): string {\n return `${addonId}:${this.hashConfig(effectiveConfig)}`\n }\n\n private hashConfig(config: Record<string, unknown>): string {\n const sorted = JSON.stringify(config, Object.keys(config).sort())\n return createHash('md5').update(sorted).digest('hex').slice(0, 12)\n }\n}\n","import { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\nconst execFileAsync = promisify(execFile)\n\nexport interface AddonInstallerConfig {\n /** Directory where addons are installed (e.g., ~/.camstack/addons or {dataDir}/addons) */\n readonly addonsDir: string\n /** Builtin packages to auto-install on first boot */\n readonly builtinPackages: readonly string[]\n /** npm registry URL (default: https://registry.npmjs.org) */\n readonly registry?: string\n}\n\nexport interface InstalledPackage {\n readonly name: string\n readonly version: string\n readonly path: string\n}\n\nexport class AddonInstaller {\n constructor(private readonly config: AddonInstallerConfig) {}\n\n /** Initialize addon directory — create if not exists, install builtins if needed */\n async initialize(): Promise<void> {\n await this.ensureAddonDirectory()\n await this.installBuiltins()\n }\n\n /** Ensure addon directory exists with a package.json */\n private async ensureAddonDirectory(): Promise<void> {\n const { addonsDir } = this.config\n fs.mkdirSync(addonsDir, { recursive: true })\n\n const pkgJsonPath = path.join(addonsDir, 'package.json')\n if (!fs.existsSync(pkgJsonPath)) {\n const pkgJson = {\n name: 'camstack-addons',\n version: '1.0.0',\n private: true,\n description: 'CamStack addon packages — managed automatically',\n dependencies: {},\n }\n fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2))\n }\n }\n\n /** Install builtin packages if not already present */\n private async installBuiltins(): Promise<void> {\n await this.ensureInstalled([...this.config.builtinPackages])\n }\n\n /**\n * Ensure a set of packages are installed — installs any that are missing.\n * This is the public entry-point used during boot to guarantee required\n * addon packages are present before the loader tries to resolve them.\n */\n async ensureInstalled(packages: string[]): Promise<void> {\n const missing = packages.filter(pkg => !this.isInstalled(pkg))\n if (missing.length === 0) return\n\n await this.installPackages(missing)\n }\n\n /** Check if a package is installed */\n isInstalled(packageName: string): boolean {\n const pkgPath = path.join(this.config.addonsDir, 'node_modules', packageName, 'package.json')\n return fs.existsSync(pkgPath)\n }\n\n /** Get installed package info */\n getInstalledPackage(packageName: string): InstalledPackage | null {\n const pkgPath = path.join(this.config.addonsDir, 'node_modules', packageName, 'package.json')\n if (!fs.existsSync(pkgPath)) return null\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as { name: string; version: string }\n return {\n name: pkg.name,\n version: pkg.version,\n path: path.dirname(pkgPath),\n }\n }\n\n /** List all installed addon packages (those with camstack.addons in package.json) */\n listInstalled(): InstalledPackage[] {\n const nodeModulesDir = path.join(this.config.addonsDir, 'node_modules')\n if (!fs.existsSync(nodeModulesDir)) return []\n\n const packages: InstalledPackage[] = []\n\n // Check scoped packages (@camstack/*)\n const scopeDirs = fs.readdirSync(nodeModulesDir).filter(d => d.startsWith('@'))\n for (const scope of scopeDirs) {\n const scopePath = path.join(nodeModulesDir, scope)\n if (!fs.statSync(scopePath).isDirectory()) continue\n for (const pkg of fs.readdirSync(scopePath)) {\n const pkgJsonPath = path.join(scopePath, pkg, 'package.json')\n if (!fs.existsSync(pkgJsonPath)) continue\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as {\n name: string\n version: string\n camstack?: { addons?: unknown[] }\n }\n if (pkgJson.camstack?.addons) {\n packages.push({ name: pkgJson.name, version: pkgJson.version, path: path.join(scopePath, pkg) })\n }\n }\n }\n\n // Check non-scoped packages\n for (const dir of fs.readdirSync(nodeModulesDir)) {\n if (dir.startsWith('@') || dir.startsWith('.')) continue\n const pkgJsonPath = path.join(nodeModulesDir, dir, 'package.json')\n if (!fs.existsSync(pkgJsonPath)) continue\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as {\n name: string\n version: string\n camstack?: { addons?: unknown[] }\n }\n if (pkgJson.camstack?.addons) {\n packages.push({ name: pkgJson.name, version: pkgJson.version, path: path.join(nodeModulesDir, dir) })\n }\n }\n\n return packages\n }\n\n /** Install one or more packages from npm */\n async installPackages(packages: readonly string[]): Promise<void> {\n if (packages.length === 0) return\n\n const args = ['install', '--save', ...packages]\n if (this.config.registry) {\n args.push('--registry', this.config.registry)\n }\n\n await execFileAsync('npm', args, {\n cwd: this.config.addonsDir,\n timeout: 120_000, // 2 min timeout\n })\n\n // npm install --save already updates package.json, but re-read and re-write to normalise formatting\n const pkgJsonPath = path.join(this.config.addonsDir, 'package.json')\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as Record<string, unknown>\n fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2))\n }\n\n /** Uninstall a package */\n async uninstallPackage(packageName: string): Promise<void> {\n // Prevent uninstalling builtins\n if (this.config.builtinPackages.includes(packageName)) {\n throw new Error(`Cannot uninstall builtin package: ${packageName}`)\n }\n\n await execFileAsync('npm', ['uninstall', packageName], {\n cwd: this.config.addonsDir,\n timeout: 60_000,\n })\n }\n\n /** Update a package to latest version */\n async updatePackage(packageName: string): Promise<void> {\n await execFileAsync('npm', ['update', packageName], {\n cwd: this.config.addonsDir,\n timeout: 120_000,\n })\n }\n\n /** Install an addon from a local .tgz file */\n async installFromTgz(tgzPath: string): Promise<void> {\n await execFileAsync('npm', ['install', '--save', tgzPath], {\n cwd: this.config.addonsDir,\n timeout: 60_000,\n })\n }\n\n /** Update all packages */\n async updateAll(): Promise<void> {\n await execFileAsync('npm', ['update'], {\n cwd: this.config.addonsDir,\n timeout: 300_000,\n })\n }\n\n /** Get the node_modules path for require/import resolution */\n getNodeModulesPath(): string {\n return path.join(this.config.addonsDir, 'node_modules')\n }\n}\n\n/** Default builtin packages that are auto-installed on first boot */\nexport const BUILTIN_PACKAGES: readonly string[] = [\n '@camstack/addon-pipeline',\n '@camstack/addon-vision',\n '@camstack/addon-go2rtc',\n '@camstack/addon-benchmark',\n '@camstack/addon-cloudflare-tunnel',\n '@camstack/addon-cloudflare-turn',\n '@camstack/addon-provider-frigate',\n '@camstack/addon-provider-onvif',\n '@camstack/addon-provider-rtsp',\n] as const\n","import type { PipelineConfig, PipelineNode } from '@camstack/types'\nimport { AddonLoader } from '../addon/addon-loader.js'\n\nexport interface ValidationIssue {\n readonly step?: string\n readonly addon?: string\n readonly message: string\n readonly severity: 'error' | 'warning'\n}\n\nexport interface ValidationResult {\n readonly valid: boolean\n readonly errors: readonly ValidationIssue[]\n readonly warnings: readonly ValidationIssue[]\n}\n\nexport class PipelineValidator {\n constructor(private readonly loader: AddonLoader) {}\n\n validate(config: PipelineConfig): ValidationResult {\n const errors: ValidationIssue[] = []\n const warnings: ValidationIssue[] = []\n\n // Validate video pipeline\n for (const node of config.video) {\n this.validateNode(node, errors, warnings, [])\n }\n\n // Validate audio\n if (config.audio) {\n this.validateNode(config.audio, errors, warnings, [])\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n }\n }\n\n private validateNode(\n node: PipelineNode,\n errors: ValidationIssue[],\n warnings: ValidationIssue[],\n ancestors: PipelineNode[],\n ): void {\n // Check addon exists\n if (!this.loader.hasAddon(node.addon)) {\n errors.push({\n step: node.step,\n addon: node.addon,\n message: `Addon \"${node.addon}\" is not registered`,\n severity: 'error',\n })\n return\n }\n\n // Check requiredSteps (from addon manifest)\n const registered = this.loader.getAddon(node.addon)\n if (registered) {\n const manifest = registered.declaration\n // Warn if a node appears at root but its addon has inputClasses\n // (This is a simplified check — full implementation would read manifest.requiredSteps)\n void manifest // reserved for future manifest-driven checks\n }\n\n // Validate children recursively\n if (node.children) {\n for (const child of node.children) {\n this.validateNode(child, errors, warnings, [...ancestors, node])\n }\n }\n }\n}\n","import type {\n FrameInput,\n AudioChunkInput,\n CropInput,\n PipelineConfig,\n PipelineNode,\n PipelineResult,\n StepResult,\n DetectorOutput,\n CropperOutput,\n ClassifierOutput,\n SpatialDetection,\n PipelineSlot,\n ICamstackAddon,\n} from '@camstack/types'\nimport { AddonEngineManager } from '../addon/addon-engine-manager.js'\nimport { randomUUID } from 'node:crypto'\n\nexport class PipelineRunner {\n constructor(\n private readonly engineManager: AddonEngineManager,\n private readonly addonConfigs: Map<string, Record<string, unknown>>, // addonId → global config\n ) {}\n\n async run(frame: FrameInput, config: PipelineConfig): Promise<PipelineResult> {\n const startTime = performance.now()\n const results: StepResult[] = []\n const timings: Record<string, number> = {}\n\n // Execute video pipeline root nodes\n for (const rootNode of config.video) {\n await this.executeNode(rootNode, frame, null, results, timings)\n }\n\n // Execute audio node if present (independent)\n if (config.audio) {\n await this.executeNode(config.audio, frame, null, results, timings)\n }\n\n return {\n results,\n totalMs: performance.now() - startTime,\n timings,\n frameTimestamp: frame.timestamp,\n }\n }\n\n /**\n * Run only the audio classification node on an audio chunk.\n * Used by the audio path in DetectionWiringService (separate from video pipeline).\n */\n async runAudioNode(chunk: AudioChunkInput, audioNode: PipelineNode): Promise<PipelineResult> {\n const startTime = performance.now()\n const results: StepResult[] = []\n const timings: Record<string, number> = {}\n const resultId = randomUUID()\n const stepStart = performance.now()\n\n try {\n const globalConfig = this.addonConfigs.get(audioNode.addon) ?? {}\n const engine = await this.engineManager.getOrCreateEngine(\n audioNode.addon, globalConfig, audioNode.configOverride,\n )\n\n if (!('classifyAudio' in engine) || typeof (engine as unknown as Record<string, unknown>)['classifyAudio'] !== 'function') {\n throw new Error(`Addon \"${audioNode.addon}\" has no classifyAudio method`)\n }\n\n const output = await (engine as unknown as { classifyAudio: (input: AudioChunkInput) => Promise<ClassifierOutput> }).classifyAudio(chunk)\n const stepMs = performance.now() - stepStart\n\n const stepResult: StepResult = {\n addon: audioNode.addon,\n slot: 'classifier',\n output,\n resultId,\n inferenceMs: output.inferenceMs,\n preprocessMs: 0,\n totalMs: stepMs,\n }\n results.push(stepResult)\n timings[audioNode.step] = stepMs\n } catch (error: unknown) {\n const stepMs = performance.now() - stepStart\n const message = error instanceof Error ? error.message : String(error)\n results.push({\n addon: audioNode.addon,\n slot: 'classifier',\n output: { classifications: [], inferenceMs: 0, modelId: '' } as unknown as ClassifierOutput,\n resultId,\n inferenceMs: 0,\n preprocessMs: 0,\n totalMs: stepMs,\n error: {\n code: 'ADDON_ERROR',\n message,\n childrenSkipped: true,\n },\n })\n }\n\n return {\n results,\n totalMs: performance.now() - startTime,\n timings,\n frameTimestamp: chunk.timestamp,\n }\n }\n\n private async executeNode(\n node: PipelineNode,\n frame: FrameInput,\n parentDetection: { det: SpatialDetection; resultId: string } | null,\n results: StepResult[],\n timings: Record<string, number>,\n ): Promise<void> {\n const resultId = randomUUID()\n const stepStart = performance.now()\n\n try {\n // Get or create engine for this addon\n const globalConfig = this.addonConfigs.get(node.addon) ?? {}\n const engine = await this.engineManager.getOrCreateEngine(\n node.addon, globalConfig, node.configOverride,\n )\n\n // Determine slot type and execute\n let output: DetectorOutput | CropperOutput | ClassifierOutput\n\n if (parentDetection) {\n // This is a child node — needs crop input\n const cropInput: CropInput = {\n frame,\n roi: parentDetection.det.bbox,\n parentDetection: parentDetection.det,\n }\n\n // Try cropper first, then classifier, then detector\n if ('crop' in engine && typeof (engine as unknown as Record<string, unknown>)['crop'] === 'function') {\n output = await (engine as unknown as { crop: (input: CropInput) => Promise<CropperOutput> }).crop(cropInput)\n } else if ('classify' in engine && typeof (engine as unknown as Record<string, unknown>)['classify'] === 'function') {\n output = await (engine as unknown as { classify: (input: CropInput) => Promise<ClassifierOutput> }).classify(cropInput)\n } else if ('detect' in engine && typeof (engine as unknown as Record<string, unknown>)['detect'] === 'function') {\n // Detector child (e.g., object-detection triggered by motion)\n output = await (engine as unknown as { detect: (input: FrameInput) => Promise<DetectorOutput> }).detect(frame)\n } else {\n throw new Error(`Addon \"${node.addon}\" has no detect/crop/classify method`)\n }\n } else {\n // Root node — full frame\n if ('detect' in engine && typeof (engine as unknown as Record<string, unknown>)['detect'] === 'function') {\n output = await (engine as unknown as { detect: (input: FrameInput) => Promise<DetectorOutput> }).detect(frame)\n } else if ('classify' in engine && typeof (engine as unknown as Record<string, unknown>)['classify'] === 'function') {\n // Audio classifier at root\n const rootCropInput: CropInput = {\n frame,\n roi: { x: 0, y: 0, w: frame.width, h: frame.height },\n parentDetection: { class: '', originalClass: '', score: 1, bbox: { x: 0, y: 0, w: frame.width, h: frame.height } },\n }\n output = await (engine as unknown as { classify: (input: CropInput) => Promise<ClassifierOutput> }).classify(rootCropInput)\n } else {\n throw new Error(`Addon \"${node.addon}\" has no detect/classify method`)\n }\n }\n\n const stepMs = performance.now() - stepStart\n\n // Determine slot type from output shape\n const slot: PipelineSlot =\n 'detections' in output ? 'detector' :\n 'crops' in output ? 'cropper' :\n 'classifier'\n\n const stepResult: StepResult = {\n addon: node.addon,\n slot,\n output,\n parentResultId: parentDetection?.resultId,\n resultId,\n inferenceMs: output.inferenceMs,\n preprocessMs: 0,\n totalMs: stepMs,\n }\n results.push(stepResult)\n timings[node.step] = stepMs\n\n // Process children\n if (node.children?.length) {\n const detections: readonly SpatialDetection[] =\n 'detections' in output ? output.detections :\n 'crops' in output ? output.crops :\n []\n\n // For each detection, find children with matching inputClasses and execute\n await this.executeChildren(node.children, frame, detections, resultId, results, timings)\n }\n } catch (error: unknown) {\n const stepMs = performance.now() - stepStart\n const message = error instanceof Error ? error.message : String(error)\n results.push({\n addon: node.addon,\n slot: 'detector',\n output: { detections: [], inferenceMs: 0, modelId: '' },\n parentResultId: parentDetection?.resultId,\n resultId,\n inferenceMs: 0,\n preprocessMs: 0,\n totalMs: stepMs,\n error: {\n code: 'ADDON_ERROR',\n message,\n childrenSkipped: true,\n },\n })\n }\n }\n\n private async executeChildren(\n children: readonly PipelineNode[],\n frame: FrameInput,\n parentDetections: readonly SpatialDetection[],\n parentResultId: string,\n results: StepResult[],\n timings: Record<string, number>,\n ): Promise<void> {\n // For each parent detection, run all matching children in parallel\n const promises: Promise<void>[] = []\n\n for (const detection of parentDetections) {\n for (const child of children) {\n // TODO: Check inputClasses matching from addon manifest\n // For now, run all children on all detections (caller configures tree correctly)\n promises.push(\n this.executeNode(child, frame, { det: detection, resultId: parentResultId }, results, timings),\n )\n }\n }\n\n await Promise.allSettled(promises)\n }\n}\n","// packages/core/src/capability/capability-registry.ts\nimport type {\n CapabilityMode,\n CapabilityDeclaration,\n CapabilityConsumerRegistration,\n CapabilityInfo,\n IScopedLogger,\n} from '@camstack/types'\n\ninterface ProviderEntry {\n readonly addonId: string\n readonly provider: unknown\n}\n\ninterface CapabilityState {\n readonly declaration: CapabilityDeclaration\n /** All registered providers (addon ID → provider) */\n readonly available: Map<string, unknown>\n /** For singleton: the currently active addon ID */\n activeAddonId: string | null\n /** For singleton: the currently active provider */\n activeProvider: unknown | null\n /** For collection: all active providers in registration order */\n readonly activeCollection: ProviderEntry[]\n /** Registered consumers */\n readonly consumers: Set<CapabilityConsumerRegistration>\n}\n\n/**\n * Central registry for all capability providers and consumers.\n * Lives in @camstack/core. Mode-aware: singleton (one active) or collection (all active).\n *\n * Uses injected LoggingService, NEVER NestJS static Logger.\n */\nexport class CapabilityRegistry {\n private readonly capabilities = new Map<string, CapabilityState>()\n\n /** Per-device singleton overrides: deviceId → (capability → addonId) */\n private readonly deviceOverrides = new Map<string, Map<string, string>>()\n\n /** Per-device collection filters: deviceId → (capability → addonIds[]) */\n private readonly deviceCollectionFilters = new Map<string, Map<string, string[]>>()\n\n constructor(\n private readonly logger: IScopedLogger,\n private readonly configReader: (capability: string) => string | undefined,\n ) {}\n\n /**\n * Declare a capability (typically called when addon manifests are loaded).\n * Must be called before registerProvider/registerConsumer for that capability.\n */\n declareCapability(declaration: CapabilityDeclaration): void {\n if (this.capabilities.has(declaration.name)) {\n this.logger.debug(`Capability \"${declaration.name}\" already declared, skipping`)\n return\n }\n\n this.capabilities.set(declaration.name, {\n declaration,\n available: new Map(),\n activeAddonId: null,\n activeProvider: null,\n activeCollection: [],\n consumers: new Set(),\n })\n\n this.logger.debug(`Capability declared: ${declaration.name} (mode=${declaration.mode})`)\n }\n\n /**\n * Register a capability provider (called by addon loader when addon is enabled).\n * For singleton: auto-activates if user-preferred or first registered.\n * For collection: adds to active set and notifies consumers.\n */\n registerProvider(capability: string, addonId: string, provider: unknown): void {\n const state = this.capabilities.get(capability)\n if (!state) {\n this.logger.warn(`Cannot register provider for undeclared capability \"${capability}\"`)\n return\n }\n\n state.available.set(addonId, provider)\n this.logger.info(`Provider registered: ${addonId} → ${capability}`)\n\n if (state.declaration.mode === 'singleton') {\n const userChoice = this.configReader(capability)\n\n if (userChoice === addonId) {\n // User explicitly chose this addon\n this.activateSingleton(state, addonId, provider)\n } else if (userChoice === undefined && state.activeAddonId === null) {\n // No user preference and no active provider — first registered = default\n this.activateSingleton(state, addonId, provider)\n }\n } else {\n // collection mode: add to active set and notify\n state.activeCollection.push({ addonId, provider })\n for (const consumer of state.consumers) {\n if (consumer.onAdded) {\n try {\n consumer.onAdded(provider)\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error)\n this.logger.error(`Consumer onAdded failed for ${capability}: ${msg}`)\n }\n }\n }\n }\n }\n\n /**\n * Unregister a provider (called when addon is disabled/uninstalled).\n */\n unregisterProvider(capability: string, addonId: string): void {\n const state = this.capabilities.get(capability)\n if (!state) return\n\n const provider = state.available.get(addonId)\n state.available.delete(addonId)\n\n if (state.declaration.mode === 'singleton') {\n if (state.activeAddonId === addonId) {\n state.activeAddonId = null\n state.activeProvider = null\n this.logger.info(`Singleton deactivated: ${capability} (was ${addonId})`)\n }\n } else {\n // collection: remove from active\n const idx = state.activeCollection.findIndex((e) => e.addonId === addonId)\n if (idx !== -1) {\n state.activeCollection.splice(idx, 1)\n for (const consumer of state.consumers) {\n if (consumer.onRemoved && provider !== undefined) {\n try {\n consumer.onRemoved(provider)\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error)\n this.logger.error(`Consumer onRemoved failed for ${capability}: ${msg}`)\n }\n }\n }\n }\n }\n }\n\n /**\n * Register a consumer that wants to be notified when providers change.\n * If a provider is already active, the consumer is immediately notified.\n * Returns a disposer function for cleanup.\n */\n registerConsumer<T = unknown>(registration: CapabilityConsumerRegistration<T>): () => void {\n const state = this.capabilities.get(registration.capability)\n if (!state) {\n // Auto-declare as singleton if not yet declared (graceful handling)\n this.logger.debug(`Consumer registered for undeclared capability \"${registration.capability}\" — auto-declaring`)\n this.declareCapability({ name: registration.capability, mode: 'singleton' })\n return this.registerConsumer(registration)\n }\n\n const untypedReg = registration as CapabilityConsumerRegistration\n state.consumers.add(untypedReg)\n\n // Immediately notify with current state\n if (state.declaration.mode === 'singleton') {\n if (state.activeProvider !== null && registration.onSet) {\n try {\n registration.onSet(state.activeProvider as T)\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error)\n this.logger.error(`Consumer onSet (immediate) failed for ${registration.capability}: ${msg}`)\n }\n }\n } else {\n // collection: notify with all active\n if (registration.onAdded) {\n for (const entry of state.activeCollection) {\n try {\n registration.onAdded(entry.provider as T)\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error)\n this.logger.error(`Consumer onAdded (immediate) failed for ${registration.capability}: ${msg}`)\n }\n }\n }\n }\n\n return () => {\n state.consumers.delete(untypedReg)\n }\n }\n\n /**\n * Get the active singleton provider for a capability.\n * Returns null if none set.\n */\n getSingleton<T = unknown>(capability: string): T | null {\n const state = this.capabilities.get(capability)\n if (!state || state.declaration.mode !== 'singleton') return null\n return (state.activeProvider as T) ?? null\n }\n\n /**\n * Get all active collection providers for a capability.\n */\n getCollection<T = unknown>(capability: string): readonly T[] {\n const state = this.capabilities.get(capability)\n if (!state || state.declaration.mode !== 'collection') return []\n return state.activeCollection.map((e) => e.provider as T)\n }\n\n /**\n * Set which addon should be the active singleton for a capability.\n * Call with `immediate: true` to also swap the runtime provider now\n * (consumers' onSet will be awaited).\n */\n async setActiveSingleton(capability: string, addonId: string, immediate = false): Promise<void> {\n const state = this.capabilities.get(capability)\n if (!state) {\n throw new Error(`Unknown capability: ${capability}`)\n }\n if (state.declaration.mode !== 'singleton') {\n throw new Error(`Capability \"${capability}\" is not a singleton`)\n }\n\n const provider = state.available.get(addonId)\n if (!provider) {\n throw new Error(`No provider \"${addonId}\" registered for capability \"${capability}\"`)\n }\n\n if (immediate) {\n await this.activateSingletonAsync(state, addonId, provider)\n }\n\n this.logger.info(`Singleton preference set: ${capability} → ${addonId}`)\n }\n\n /**\n * Get the mode declared for a capability.\n */\n getMode(capability: string): CapabilityMode | undefined {\n return this.capabilities.get(capability)?.declaration.mode\n }\n\n /**\n * List all registered capabilities with their providers.\n */\n listCapabilities(): CapabilityInfo[] {\n const result: CapabilityInfo[] = []\n for (const [name, state] of this.capabilities) {\n result.push({\n name,\n mode: state.declaration.mode,\n providers: [...state.available.keys()],\n activeProvider: state.activeAddonId,\n })\n }\n return result\n }\n\n /**\n * Check if all dependencies for a capability are satisfied (have active providers).\n */\n areDependenciesMet(declaration: CapabilityDeclaration): boolean {\n if (!declaration.dependsOn?.length) return true\n return declaration.dependsOn.every((dep) => {\n const state = this.capabilities.get(dep)\n if (!state) return false\n if (state.declaration.mode === 'singleton') {\n return state.activeProvider !== null\n }\n return state.activeCollection.length > 0\n })\n }\n\n /**\n * Get the dependency-ordered list of capability names for boot sequencing.\n * Returns capabilities sorted topologically by dependsOn.\n * Throws if a cycle is detected.\n */\n getBootOrder(): string[] {\n const visited = new Set<string>()\n const visiting = new Set<string>()\n const order: string[] = []\n\n const visit = (name: string): void => {\n if (visited.has(name)) return\n if (visiting.has(name)) {\n throw new Error(`Circular dependency detected involving capability \"${name}\"`)\n }\n\n visiting.add(name)\n const state = this.capabilities.get(name)\n if (state?.declaration.dependsOn) {\n for (const dep of state.declaration.dependsOn) {\n visit(dep)\n }\n }\n visiting.delete(name)\n visited.add(name)\n order.push(name)\n }\n\n for (const name of this.capabilities.keys()) {\n visit(name)\n }\n\n return order\n }\n\n // ---- Per-device overrides ----\n\n /**\n * Set a per-device singleton override. When resolveForDevice is called for\n * this device + capability, the specified addon's provider is returned\n * instead of the global singleton.\n */\n setDeviceOverride(deviceId: string, capability: string, addonId: string): void {\n const state = this.capabilities.get(capability)\n if (!state) {\n this.logger.warn(`Cannot set device override for undeclared capability \"${capability}\"`)\n return\n }\n if (!state.available.has(addonId)) {\n this.logger.warn(`Cannot set device override: addon \"${addonId}\" not registered for \"${capability}\"`)\n return\n }\n\n let deviceMap = this.deviceOverrides.get(deviceId)\n if (!deviceMap) {\n deviceMap = new Map()\n this.deviceOverrides.set(deviceId, deviceMap)\n }\n deviceMap.set(capability, addonId)\n this.logger.info(`Device override set: ${deviceId} → ${capability} = ${addonId}`)\n }\n\n /**\n * Clear a per-device singleton override, reverting to the global singleton.\n */\n clearDeviceOverride(deviceId: string, capability: string): void {\n const deviceMap = this.deviceOverrides.get(deviceId)\n if (!deviceMap) return\n deviceMap.delete(capability)\n if (deviceMap.size === 0) {\n this.deviceOverrides.delete(deviceId)\n }\n this.logger.info(`Device override cleared: ${deviceId} → ${capability}`)\n }\n\n /**\n * Get all per-device singleton overrides for a device.\n * Returns a Map of capability name to addon ID.\n */\n getDeviceOverrides(deviceId: string): Map<string, string> {\n return new Map(this.deviceOverrides.get(deviceId) ?? [])\n }\n\n /**\n * Resolve a singleton provider for a specific device.\n * 1. Check device override — return that addon's provider\n * 2. Fallback to global singleton\n */\n resolveForDevice<T = unknown>(capability: string, deviceId: string): T | null {\n const state = this.capabilities.get(capability)\n if (!state || state.declaration.mode !== 'singleton') return null\n\n // Check device-level override\n const deviceMap = this.deviceOverrides.get(deviceId)\n if (deviceMap) {\n const overrideAddonId = deviceMap.get(capability)\n if (overrideAddonId) {\n const provider = state.available.get(overrideAddonId)\n if (provider) return provider as T\n // Override points to an addon that is no longer registered — fall through\n this.logger.warn(\n `Device override for ${deviceId}/${capability} references unregistered addon \"${overrideAddonId}\" — falling back to global`,\n )\n }\n }\n\n // Fallback to global singleton\n return (state.activeProvider as T) ?? null\n }\n\n /**\n * Set a per-device collection filter. When resolveCollectionForDevice is called\n * for this device + capability, only providers from the specified addon IDs\n * are returned instead of the full collection.\n */\n setDeviceCollectionFilter(deviceId: string, capability: string, addonIds: string[]): void {\n const state = this.capabilities.get(capability)\n if (!state) {\n this.logger.warn(`Cannot set device collection filter for undeclared capability \"${capability}\"`)\n return\n }\n\n let deviceMap = this.deviceCollectionFilters.get(deviceId)\n if (!deviceMap) {\n deviceMap = new Map()\n this.deviceCollectionFilters.set(deviceId, deviceMap)\n }\n deviceMap.set(capability, [...addonIds])\n this.logger.info(`Device collection filter set: ${deviceId} → ${capability} = [${addonIds.join(', ')}]`)\n }\n\n /**\n * Clear a per-device collection filter, reverting to the full collection.\n */\n clearDeviceCollectionFilter(deviceId: string, capability: string): void {\n const deviceMap = this.deviceCollectionFilters.get(deviceId)\n if (!deviceMap) return\n deviceMap.delete(capability)\n if (deviceMap.size === 0) {\n this.deviceCollectionFilters.delete(deviceId)\n }\n this.logger.info(`Device collection filter cleared: ${deviceId} → ${capability}`)\n }\n\n /**\n * Resolve collection providers for a specific device.\n * If a filter exists for the device + capability, only those addon's providers are returned.\n * If no filter exists, the full collection is returned.\n */\n resolveCollectionForDevice<T = unknown>(capability: string, deviceId: string): readonly T[] {\n const state = this.capabilities.get(capability)\n if (!state || state.declaration.mode !== 'collection') return []\n\n // Check device-level filter\n const deviceMap = this.deviceCollectionFilters.get(deviceId)\n if (deviceMap) {\n const filterAddonIds = deviceMap.get(capability)\n if (filterAddonIds) {\n const filterSet = new Set(filterAddonIds)\n return state.activeCollection\n .filter((e) => filterSet.has(e.addonId))\n .map((e) => e.provider as T)\n }\n }\n\n // Fallback to full collection\n return state.activeCollection.map((e) => e.provider as T)\n }\n\n /**\n * Get a specific addon's provider by addon ID, regardless of whether it's the active singleton.\n * Useful for per-device overrides that need to look up any registered provider.\n */\n getProviderByAddonId<T = unknown>(capability: string, addonId: string): T | null {\n const state = this.capabilities.get(capability)\n if (!state) return null\n const provider = state.available.get(addonId)\n return (provider as T) ?? null\n }\n\n private activateSingleton(state: CapabilityState, addonId: string, provider: unknown): void {\n state.activeAddonId = addonId\n state.activeProvider = provider\n this.logger.info(`Singleton activated: ${state.declaration.name} → ${addonId}`)\n\n for (const consumer of state.consumers) {\n if (consumer.onSet) {\n try {\n consumer.onSet(provider)\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error)\n this.logger.error(`Consumer onSet failed for ${state.declaration.name}: ${msg}`)\n }\n }\n }\n }\n\n private async activateSingletonAsync(state: CapabilityState, addonId: string, provider: unknown): Promise<void> {\n state.activeAddonId = addonId\n state.activeProvider = provider\n this.logger.info(`Singleton activated (async): ${state.declaration.name} → ${addonId}`)\n\n for (const consumer of state.consumers) {\n if (consumer.onSet) {\n try {\n await consumer.onSet(provider)\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error)\n this.logger.error(`Consumer onSet (async) failed for ${state.declaration.name}: ${msg}`)\n }\n }\n }\n }\n}\n","// packages/core/src/capability/infra-capabilities.ts\n\nexport interface InfraCapability {\n /** Capability name */\n readonly name: string\n /** If true, boot aborts when this capability's addon fails to initialize */\n readonly required: boolean\n}\n\n/**\n * Infrastructure capabilities that must boot before all other addons.\n * Enabled in Phase 1 of the boot sequence. Order matters: storage before logging.\n */\nexport const INFRA_CAPABILITIES: readonly InfraCapability[] = [\n { name: 'storage', required: true },\n { name: 'log-destination', required: false },\n] as const\n\nconst infraNames = new Set(INFRA_CAPABILITIES.map((c) => c.name))\n\n/** Check if a capability name is an infrastructure capability */\nexport function isInfraCapability(name: string): boolean {\n return infraNames.has(name)\n}\n","import { fork, spawn, type ChildProcess } from 'node:child_process'\nimport type { IScopedLogger } from '@camstack/types'\nimport type {\n ProcessConfig,\n ProcessStats,\n ManagedProcessStatus,\n ProcessState,\n ProcessEventEmitter,\n} from './interfaces.js'\n\nconst DEFAULT_MAX_RESTARTS = 10\nconst MAX_BACKOFF_MS = 60_000\nconst GRACEFUL_SHUTDOWN_MS = 5_000\n\nexport class ManagedProcess {\n private childProcess: ChildProcess | null = null\n private _state: ProcessState = 'stopped'\n private _startedAt?: number\n private restartTimer?: ReturnType<typeof setTimeout>\n private _restartCount = 0\n private _lastCrashAt?: number\n private _lastCrashError?: string\n\n constructor(\n private readonly config: ProcessConfig,\n private readonly events: ProcessEventEmitter,\n private readonly logger: IScopedLogger,\n ) {}\n\n get state(): ProcessState {\n return this._state\n }\n\n async start(): Promise<void> {\n this._state = 'starting'\n\n try {\n if (this.config.modulePath) {\n this.childProcess = fork(this.config.modulePath, this.config.args ?? [], {\n env: { ...process.env, ...this.config.env },\n stdio: ['pipe', 'pipe', 'pipe', 'ipc'],\n })\n } else if (this.config.command) {\n this.childProcess = spawn(this.config.command, this.config.args ?? [], {\n env: { ...process.env, ...this.config.env },\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n } else {\n throw new Error('No command or modulePath specified')\n }\n\n this.childProcess.stdout?.on('data', (data: Buffer) => {\n this.logger.debug(data.toString().trim())\n })\n\n this.childProcess.stderr?.on('data', (data: Buffer) => {\n this.logger.warn(data.toString().trim())\n })\n\n this.childProcess.on('exit', (code, signal) => {\n const msg = `Process exited: code=${code}, signal=${signal}`\n\n if (code === 0) {\n this.logger.info(msg)\n this._state = 'stopped'\n } else {\n this._lastCrashAt = Date.now()\n this._lastCrashError = msg\n this.logger.error(msg)\n this._state = 'error'\n\n this.events.emitProcessCrashed(\n this.config.id,\n code,\n signal,\n this._restartCount,\n )\n\n const maxRestarts = this.config.maxRestarts ?? DEFAULT_MAX_RESTARTS\n if (this.config.autoRestart && this._restartCount < maxRestarts) {\n this.scheduleRestart()\n }\n }\n\n this.childProcess = null\n })\n\n this.childProcess.on('error', (err: Error) => {\n this.logger.error(`Process error: ${err.message}`)\n this._lastCrashError = err.message\n this._state = 'error'\n })\n\n this._state = 'running'\n this._startedAt = Date.now()\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n this._state = 'error'\n this._lastCrashError = msg\n throw err\n }\n }\n\n async stop(): Promise<void> {\n if (this.restartTimer) {\n clearTimeout(this.restartTimer)\n this.restartTimer = undefined\n }\n\n if (this._state === 'stopped') {\n return\n }\n\n this._state = 'stopping'\n\n if (this.childProcess && !this.childProcess.killed) {\n this.childProcess.kill('SIGTERM')\n\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(() => {\n if (this.childProcess && !this.childProcess.killed) {\n this.childProcess.kill('SIGKILL')\n }\n resolve()\n }, GRACEFUL_SHUTDOWN_MS)\n\n this.childProcess?.on('exit', () => {\n clearTimeout(timeout)\n resolve()\n })\n })\n }\n\n // The exit handler may have already set state to 'stopped' asynchronously\n const currentState = this._state as string\n if (currentState !== 'stopped') {\n this._state = 'stopped'\n }\n }\n\n getStatus(): ManagedProcessStatus {\n return {\n id: this.config.id,\n label: this.config.label,\n state: this._state,\n pid: this.childProcess?.pid,\n stats: this.childProcess?.pid ? this.getStats() : undefined,\n lastCrashAt: this._lastCrashAt,\n lastCrashError: this._lastCrashError,\n restartCount: this._restartCount,\n nextRestartAt: this.restartTimer ? this.getNextRestartTime() : undefined,\n }\n }\n\n private scheduleRestart(): void {\n this._restartCount++\n const delayMs = Math.min(1000 * Math.pow(2, this._restartCount - 1), MAX_BACKOFF_MS)\n\n this.logger.info(`Scheduling restart #${this._restartCount} in ${delayMs}ms`)\n\n this.events.emitProcessRestartScheduled(\n this.config.id,\n this._restartCount,\n delayMs,\n )\n\n this.restartTimer = setTimeout(async () => {\n this.restartTimer = undefined\n try {\n await this.start()\n this.events.emitProcessRestarted(this.config.id, this._restartCount)\n } catch (err) {\n this.logger.error(`Restart failed: ${err}`)\n }\n }, delayMs)\n }\n\n private getStats(): ProcessStats | undefined {\n if (!this.childProcess?.pid) return undefined\n const uptime = this._startedAt ? Date.now() - this._startedAt : 0\n return {\n pid: this.childProcess.pid,\n cpu: 0,\n memory: 0,\n uptime,\n restartCount: this._restartCount,\n }\n }\n\n private getNextRestartTime(): number | undefined {\n if (!this._lastCrashAt) return undefined\n const delayMs = Math.min(1000 * Math.pow(2, this._restartCount - 1), MAX_BACKOFF_MS)\n return this._lastCrashAt + delayMs\n }\n}\n","import { ManagedProcess } from './managed-process.js'\nimport type {\n ProcessConfig,\n ManagedProcessStatus,\n ProcessEventEmitter,\n ProcessLoggerFactory,\n} from './interfaces.js'\n\nexport class ProcessManager {\n private readonly processes: Map<string, ManagedProcess> = new Map()\n\n constructor(\n private readonly events: ProcessEventEmitter,\n private readonly loggerFactory: ProcessLoggerFactory,\n ) {}\n\n register(config: ProcessConfig): ManagedProcess {\n if (this.processes.has(config.id)) {\n throw new Error(`Process already registered: ${config.id}`)\n }\n\n const logger = this.loggerFactory.createLogger(`process:${config.id}`)\n const managed = new ManagedProcess(config, this.events, logger)\n this.processes.set(config.id, managed)\n return managed\n }\n\n async start(id: string): Promise<void> {\n const p = this.get(id)\n await p.start()\n }\n\n async stop(id: string): Promise<void> {\n const p = this.get(id)\n await p.stop()\n }\n\n async restart(id: string): Promise<void> {\n const p = this.get(id)\n await p.stop()\n await p.start()\n }\n\n get(id: string): ManagedProcess {\n const p = this.processes.get(id)\n if (!p) throw new Error(`Process not found: ${id}`)\n return p\n }\n\n listAll(): readonly ManagedProcessStatus[] {\n return [...this.processes.values()].map((p) => p.getStatus())\n }\n\n async shutdownAll(): Promise<void> {\n const running = [...this.processes.values()].filter(\n (p) => p.getStatus().state === 'running',\n )\n\n await Promise.all(running.map((p) => p.stop()))\n }\n}\n","export interface StreamNetworkStats {\n readonly nominalBitrateKbps: number\n readonly observedBitrateKbps: number\n readonly peakBitrateKbps: number\n readonly packetLossPercent: number\n readonly lastUpdated: number\n}\n\nexport interface ClientNetworkStats {\n readonly rttMs: number\n readonly jitterMs: number\n readonly estimatedBandwidthKbps: number\n readonly lastUpdated: number\n}\n\nexport interface DeviceNetworkStats {\n readonly deviceId: string\n readonly streams: Readonly<Record<string, StreamNetworkStats>>\n readonly client?: ClientNetworkStats\n}\n\nexport interface INetworkQualityTracker {\n reportStreamStats(deviceId: string, streamId: string, bitrateKbps: number, packetLoss?: number): void\n reportClientStats(deviceId: string, stats: Omit<ClientNetworkStats, 'lastUpdated'>): void\n getDeviceStats(deviceId: string): DeviceNetworkStats | null\n getAllStats(): readonly DeviceNetworkStats[]\n}\n\ninterface MutableStreamStats {\n nominalBitrateKbps: number\n samples: number[]\n peakBitrateKbps: number\n packetLossSamples: number[]\n lastUpdated: number\n}\n\nexport class NetworkQualityTracker implements INetworkQualityTracker {\n private readonly devices = new Map<string, {\n streams: Map<string, MutableStreamStats>\n client: (ClientNetworkStats & { lastUpdated: number }) | null\n }>()\n\n private static readonly MAX_SAMPLES = 30 // ~5 min at 10s reporting interval\n\n reportStreamStats(deviceId: string, streamId: string, bitrateKbps: number, packetLoss?: number): void {\n const device = this.getOrCreateDevice(deviceId)\n let stream = device.streams.get(streamId)\n if (!stream) {\n stream = { nominalBitrateKbps: bitrateKbps, samples: [], peakBitrateKbps: 0, packetLossSamples: [], lastUpdated: 0 }\n device.streams.set(streamId, stream)\n }\n\n stream.samples.push(bitrateKbps)\n if (stream.samples.length > NetworkQualityTracker.MAX_SAMPLES) stream.samples.shift()\n\n stream.peakBitrateKbps = Math.max(stream.peakBitrateKbps, bitrateKbps)\n\n if (packetLoss !== undefined) {\n stream.packetLossSamples.push(packetLoss)\n if (stream.packetLossSamples.length > NetworkQualityTracker.MAX_SAMPLES) stream.packetLossSamples.shift()\n }\n\n stream.lastUpdated = Date.now()\n }\n\n reportClientStats(deviceId: string, stats: Omit<ClientNetworkStats, 'lastUpdated'>): void {\n const device = this.getOrCreateDevice(deviceId)\n device.client = { ...stats, lastUpdated: Date.now() }\n }\n\n getDeviceStats(deviceId: string): DeviceNetworkStats | null {\n const device = this.devices.get(deviceId)\n if (!device) return null\n\n const streams: Record<string, StreamNetworkStats> = {}\n for (const [streamId, s] of device.streams) {\n const avg = (arr: number[]) => arr.length === 0 ? 0 : arr.reduce((a, b) => a + b, 0) / arr.length\n streams[streamId] = {\n nominalBitrateKbps: s.nominalBitrateKbps,\n observedBitrateKbps: Math.round(avg(s.samples)),\n peakBitrateKbps: s.peakBitrateKbps,\n packetLossPercent: Math.round(avg(s.packetLossSamples) * 10) / 10,\n lastUpdated: s.lastUpdated,\n }\n }\n\n return { deviceId, streams, client: device.client ?? undefined }\n }\n\n getAllStats(): readonly DeviceNetworkStats[] {\n const results: DeviceNetworkStats[] = []\n for (const deviceId of this.devices.keys()) {\n const stats = this.getDeviceStats(deviceId)\n if (stats) results.push(stats)\n }\n return results\n }\n\n private getOrCreateDevice(deviceId: string) {\n let device = this.devices.get(deviceId)\n if (!device) {\n device = { streams: new Map(), client: null }\n this.devices.set(deviceId, device)\n }\n return device\n }\n}\n","import * as vm from 'node:vm'\nimport * as util from 'node:util'\nimport type {\n IReplEngine,\n IReplContextProvider,\n ReplSessionContext,\n ReplResult,\n} from './interfaces.js'\n\nconst EXECUTION_TIMEOUT_MS = 5000\nconst COMPLETION_TIMEOUT_MS = 1000\n\nexport class ReplEngine implements IReplEngine {\n constructor(\n private readonly contextProvider: IReplContextProvider,\n ) {}\n\n async execute(code: string, context: ReplSessionContext): Promise<ReplResult> {\n const start = Date.now()\n const sandbox = this.buildSandbox(context)\n\n try {\n const vmContext = vm.createContext(sandbox)\n const script = new vm.Script(code)\n let result = script.runInContext(vmContext, { timeout: EXECUTION_TIMEOUT_MS })\n\n // If result is a Promise, await it with timeout\n if (result && typeof result.then === 'function') {\n result = await Promise.race([\n result,\n new Promise((_, reject) =>\n setTimeout(() => reject(new Error('Async timeout (5s)')), EXECUTION_TIMEOUT_MS),\n ),\n ])\n }\n\n const duration = Date.now() - start\n\n if (result === undefined) {\n return { output: 'undefined', type: 'void', duration }\n }\n\n return {\n output: util.inspect(result, {\n depth: 4,\n colors: false,\n maxArrayLength: 50,\n maxStringLength: 1000,\n }),\n type: 'value',\n duration,\n }\n } catch (err: unknown) {\n // Errors thrown inside vm contexts may not be instanceof Error\n // because they use a different Error constructor from the sandbox\n const message =\n err instanceof Error\n ? err.message\n : typeof err === 'object' && err !== null && 'message' in err\n ? String((err as { message: unknown }).message)\n : String(err)\n return {\n output: message,\n type: 'error',\n duration: Date.now() - start,\n }\n }\n }\n\n async getCompletions(partial: string, context: ReplSessionContext): Promise<string[]> {\n const sandbox = this.buildSandbox(context)\n const keys = Object.keys(sandbox)\n\n if (!partial) return keys\n\n const lastDot = partial.lastIndexOf('.')\n if (lastDot === -1) {\n return keys.filter(k => k.startsWith(partial))\n }\n\n // Object property completion: \"device.get\" -> look up device, return matching keys\n const objPath = partial.substring(0, lastDot)\n const propPrefix = partial.substring(lastDot + 1)\n\n try {\n const vmContext = vm.createContext(sandbox)\n const obj = vm.runInContext(objPath, vmContext, { timeout: COMPLETION_TIMEOUT_MS })\n if (obj && typeof obj === 'object') {\n const proto = Object.getPrototypeOf(obj)\n const objKeys = Object.getOwnPropertyNames(obj)\n .concat(Object.getOwnPropertyNames(proto ?? {}))\n .filter(k => k.startsWith(propPrefix))\n return [...new Set(objKeys)].map(k => `${objPath}.${k}`)\n }\n } catch {\n // Ignore errors during completion\n }\n\n return []\n }\n\n private buildSandbox(context: ReplSessionContext): Record<string, unknown> {\n const base: Record<string, unknown> = {\n console: {\n log: (...args: unknown[]) => util.inspect(args),\n warn: (...args: unknown[]) => util.inspect(args),\n },\n JSON,\n Math,\n Date,\n Array,\n Object,\n String,\n Number,\n Boolean,\n Map,\n Set,\n Promise,\n Buffer,\n // Blocked globals\n setTimeout: undefined,\n setInterval: undefined,\n fetch: undefined,\n require: undefined,\n process: undefined,\n // Merge user-provided variables\n ...context.variables,\n }\n\n switch (context.scope.type) {\n case 'system':\n return { ...base, ...this.contextProvider.getSystemSandbox() }\n\n case 'device':\n return { ...base, ...this.contextProvider.getDeviceSandbox(context.scope.deviceId!) }\n\n case 'provider':\n return { ...base, ...this.contextProvider.getProviderSandbox(context.scope.providerId!) }\n\n case 'addon':\n return { ...base, ...this.contextProvider.getAddonSandbox(context.scope.addonId!) }\n\n default:\n return base\n }\n }\n}\n","import type {\n AgentInfo,\n AgentStatus,\n AgentEntry,\n AgentEventEmitter,\n} from './interfaces.js'\n\nexport class AgentRegistry {\n private readonly agents: Map<string, AgentEntry> = new Map()\n private readonly heartbeatIntervals: Map<string, NodeJS.Timeout> = new Map()\n\n constructor(\n private readonly events: AgentEventEmitter,\n private readonly heartbeatTimeoutMs: number = 30000,\n private readonly heartbeatCheckIntervalMs: number = 10000,\n ) {}\n\n /** Register a new agent */\n registerAgent(info: AgentInfo): void {\n const entry: AgentEntry = {\n info,\n state: 'online',\n connectedSince: Date.now(),\n lastHeartbeat: Date.now(),\n activeTaskCount: 0,\n completedTaskCount: 0,\n failedTaskCount: 0,\n }\n\n this.agents.set(info.id, entry)\n this.startHeartbeatCheck(info.id)\n\n this.events.emitAgentRegistered(info.id, [...info.capabilities])\n }\n\n /** Remove agent */\n unregisterAgent(id: string): void {\n const interval = this.heartbeatIntervals.get(id)\n if (interval) {\n clearInterval(interval)\n this.heartbeatIntervals.delete(id)\n }\n\n this.agents.delete(id)\n this.events.emitAgentUnregistered(id)\n }\n\n /** Update heartbeat timestamp */\n heartbeat(id: string): void {\n const entry = this.agents.get(id)\n if (!entry) return\n\n entry.lastHeartbeat = Date.now()\n\n if (entry.state === 'offline') {\n entry.state = 'online'\n this.events.emitAgentOnline(id)\n }\n }\n\n /** Find agents with a specific capability */\n getAgentsWithCapability(capability: string): readonly AgentEntry[] {\n return [...this.agents.values()].filter(\n (a) => a.state === 'online' && a.info.capabilities.includes(capability),\n )\n }\n\n /** Get best agent for a task (least loaded) */\n selectAgent(capability: string, preferredId?: string): AgentEntry | null {\n const capable = this.getAgentsWithCapability(capability)\n if (capable.length === 0) return null\n\n if (preferredId) {\n const preferred = capable.find((a) => a.info.id === preferredId)\n if (preferred) return preferred\n }\n\n // Least loaded: sort by activeTaskCount ascending\n const sorted = [...capable].sort((a, b) => a.activeTaskCount - b.activeTaskCount)\n return sorted[0] ?? null\n }\n\n /** List all agents with status */\n listAgents(): readonly AgentStatus[] {\n return [...this.agents.values()].map((entry) => ({\n id: entry.info.id,\n name: entry.info.name,\n state: entry.state,\n capabilities: [...entry.info.capabilities],\n lastHeartbeat: entry.lastHeartbeat,\n connectedSince: entry.connectedSince,\n resources: entry.info.resources,\n activeTaskCount: entry.activeTaskCount,\n completedTaskCount: entry.completedTaskCount,\n failedTaskCount: entry.failedTaskCount,\n }))\n }\n\n /** Get single agent */\n getAgent(id: string): AgentEntry | null {\n return this.agents.get(id) ?? null\n }\n\n /** Destroy all heartbeat intervals */\n destroy(): void {\n for (const interval of this.heartbeatIntervals.values()) {\n clearInterval(interval)\n }\n this.heartbeatIntervals.clear()\n }\n\n private startHeartbeatCheck(id: string): void {\n const interval = setInterval(() => {\n const entry = this.agents.get(id)\n if (!entry) return\n\n const elapsed = Date.now() - entry.lastHeartbeat\n if (elapsed > this.heartbeatTimeoutMs && entry.state === 'online') {\n entry.state = 'offline'\n this.events.emitAgentOffline(id)\n }\n }, this.heartbeatCheckIntervalMs)\n\n this.heartbeatIntervals.set(id, interval)\n }\n}\n","import type { AgentRegistry } from './agent-registry.js'\nimport type {\n AgentTask,\n AgentTaskResult,\n AgentEntry,\n TaskDispatchOptions,\n AgentEventEmitter,\n} from './interfaces.js'\n\ninterface PendingTask {\n readonly resolve: (result: AgentTaskResult) => void\n readonly reject: (error: Error) => void\n readonly timeout: NodeJS.Timeout\n}\n\nexport class TaskDispatcher {\n private readonly pendingTasks: Map<string, PendingTask> = new Map()\n private readonly localExecutors: Map<\n string,\n (input: Record<string, unknown>) => Promise<Record<string, unknown>>\n > = new Map()\n\n constructor(\n private readonly agentRegistry: AgentRegistry,\n private readonly events: AgentEventEmitter,\n ) {}\n\n /** Dispatch a task to the best available agent */\n async dispatch(task: AgentTask, options?: TaskDispatchOptions): Promise<AgentTaskResult> {\n const capability = options?.capability ?? task.capability\n const agent = this.agentRegistry.selectAgent(capability, options?.preferredAgent)\n\n if (!agent && !options?.remoteOnly) {\n return this.executeLocally(task)\n }\n\n if (!agent) {\n return {\n taskId: task.id,\n agentId: 'none',\n status: 'error',\n error: 'No agent available',\n durationMs: 0,\n }\n }\n\n return this.sendToAgent(task, agent)\n }\n\n /** Register a local executor for a capability (in-process fallback) */\n registerLocalExecutor(\n capability: string,\n executor: (input: Record<string, unknown>) => Promise<Record<string, unknown>>,\n ): void {\n this.localExecutors.set(capability, executor)\n }\n\n /** Called when an agent returns a result */\n handleTaskResult(result: AgentTaskResult): void {\n const pending = this.pendingTasks.get(result.taskId)\n if (pending) {\n clearTimeout(pending.timeout)\n this.pendingTasks.delete(result.taskId)\n pending.resolve(result)\n }\n\n // Update agent stats\n const agent = this.agentRegistry.getAgent(result.agentId)\n if (agent) {\n agent.activeTaskCount--\n if (result.status === 'success') {\n agent.completedTaskCount++\n } else {\n agent.failedTaskCount++\n }\n }\n }\n\n private async executeLocally(task: AgentTask): Promise<AgentTaskResult> {\n const executor = this.localExecutors.get(task.capability)\n if (!executor) {\n return {\n taskId: task.id,\n agentId: 'local',\n status: 'error',\n error: `No local executor for ${task.capability}`,\n durationMs: 0,\n }\n }\n\n const start = Date.now()\n try {\n const output = await executor(task.input)\n return {\n taskId: task.id,\n agentId: 'local',\n status: 'success',\n output,\n durationMs: Date.now() - start,\n }\n } catch (err) {\n return {\n taskId: task.id,\n agentId: 'local',\n status: 'error',\n error: String(err),\n durationMs: Date.now() - start,\n }\n }\n }\n\n private sendToAgent(task: AgentTask, agent: AgentEntry): Promise<AgentTaskResult> {\n agent.activeTaskCount++\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n this.pendingTasks.delete(task.id)\n agent.activeTaskCount--\n agent.failedTaskCount++\n resolve({\n taskId: task.id,\n agentId: agent.info.id,\n status: 'timeout',\n durationMs: task.timeout,\n error: 'Task timed out',\n })\n }, task.timeout)\n\n this.pendingTasks.set(task.id, { resolve, reject: () => {}, timeout })\n\n this.events.emitTaskDispatched(task.id, agent.info.id, task.capability)\n })\n }\n}\n","import type {\n AgentToHubMessage,\n HubToAgentMessage,\n AgentRegistrationInfo,\n AgentRuntimeStatus,\n} from '@camstack/types'\nimport type { IScopedLogger } from '../logging/scoped-logger.js'\n\nconst RECONNECT_BASE_MS = 1000\nconst RECONNECT_MAX_MS = 30000\nconst HEARTBEAT_INTERVAL_MS = 10_000\n\nexport interface AgentClientConfig {\n readonly hubUrl: string\n readonly token?: string\n readonly logger: IScopedLogger\n readonly registrationInfo: AgentRegistrationInfo\n}\n\nexport type MessageHandler = (msg: HubToAgentMessage) => void\nexport type BinaryHandler = (data: Buffer) => void\nexport type ConnectionHandler = () => void\n\n/**\n * WebSocket client for agent mode. Framework-agnostic (no NestJS).\n * Connects to the hub, sends registration, handles messages,\n * reconnects with exponential backoff.\n */\nexport class AgentClient {\n private ws: import('ws').WebSocket | null = null\n private reconnectAttempt = 0\n private reconnectTimer: NodeJS.Timeout | null = null\n private heartbeatTimer: NodeJS.Timeout | null = null\n private destroyed = false\n\n private readonly messageHandlers: MessageHandler[] = []\n private readonly binaryHandlers: BinaryHandler[] = []\n private readonly connectHandlers: ConnectionHandler[] = []\n private readonly disconnectHandlers: ConnectionHandler[] = []\n\n private readonly logger: IScopedLogger\n private readonly hubUrl: string\n private readonly token?: string\n private readonly registrationInfo: AgentRegistrationInfo\n\n private runtimeStatus: AgentRuntimeStatus = {\n activeCameras: 0,\n cpuPercent: 0,\n memoryPercent: 0,\n fps: {},\n errors: [],\n }\n\n constructor(config: AgentClientConfig) {\n this.hubUrl = config.hubUrl\n this.token = config.token\n this.logger = config.logger\n this.registrationInfo = config.registrationInfo\n }\n\n /** Connect to the hub WebSocket */\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.destroyed = false\n this.doConnect(resolve, reject)\n })\n }\n\n /** Disconnect and stop reconnecting */\n disconnect(): void {\n this.destroyed = true\n this.clearTimers()\n if (this.ws) {\n this.ws.close()\n this.ws = null\n }\n }\n\n /** Send a JSON control message to the hub */\n send(msg: AgentToHubMessage): void {\n if (!this.ws || this.ws.readyState !== 1 /* WebSocket.OPEN */) {\n this.logger.warn('send() called while not connected')\n return\n }\n this.ws.send(JSON.stringify(msg))\n }\n\n /** Send a binary frame to the hub */\n sendBinary(data: Buffer): void {\n if (!this.ws || this.ws.readyState !== 1) {\n return\n }\n this.ws.send(data)\n }\n\n /** Register a handler for JSON messages from hub */\n onMessage(handler: MessageHandler): void {\n this.messageHandlers.push(handler)\n }\n\n /** Register a handler for binary frames from hub */\n onBinaryFrame(handler: BinaryHandler): void {\n this.binaryHandlers.push(handler)\n }\n\n /** Register a handler for successful connection */\n onConnect(handler: ConnectionHandler): void {\n this.connectHandlers.push(handler)\n }\n\n /** Register a handler for disconnection */\n onDisconnect(handler: ConnectionHandler): void {\n this.disconnectHandlers.push(handler)\n }\n\n /** Update the runtime status (used in heartbeat) */\n updateStatus(status: AgentRuntimeStatus): void {\n this.runtimeStatus = status\n }\n\n /** Whether currently connected */\n get connected(): boolean {\n return this.ws?.readyState === 1\n }\n\n private doConnect(\n onConnect?: (value: void) => void,\n onError?: (err: Error) => void,\n ): void {\n if (this.destroyed) return\n\n // Dynamic import ws to keep this file framework-agnostic at the type level\n import('ws').then(({ default: WebSocket }) => {\n if (this.destroyed) return\n\n const url = this.token\n ? `${this.hubUrl}?token=${encodeURIComponent(this.token)}`\n : this.hubUrl\n\n try {\n this.ws = new WebSocket(url)\n } catch (err) {\n this.scheduleReconnect()\n if (onError) {\n onError(err instanceof Error ? err : new Error(String(err)))\n onError = undefined\n }\n return\n }\n\n this.ws.once('open', () => {\n this.reconnectAttempt = 0\n this.logger.info(`Connected to hub: ${this.hubUrl}`)\n\n // Send registration\n this.send({ type: 'register', info: this.registrationInfo })\n\n // Start heartbeat\n this.startHeartbeat()\n\n for (const h of this.connectHandlers) h()\n\n if (onConnect) {\n onConnect()\n onConnect = undefined\n }\n })\n\n this.ws.on('message', (data: Buffer | string, isBinary: boolean) => {\n const buf = Buffer.isBuffer(data) ? data : Buffer.from(data as unknown as ArrayBuffer)\n if (isBinary) {\n for (const h of this.binaryHandlers) h(buf)\n } else {\n try {\n const msg = JSON.parse(buf.toString()) as HubToAgentMessage\n this.handleBuiltinMessage(msg)\n for (const h of this.messageHandlers) h(msg)\n } catch {\n this.logger.warn('Failed to parse message from hub')\n }\n }\n })\n\n this.ws.once('close', () => {\n this.stopHeartbeat()\n for (const h of this.disconnectHandlers) h()\n if (!this.destroyed) {\n this.logger.warn('Disconnected from hub, scheduling reconnect')\n this.scheduleReconnect()\n }\n })\n\n this.ws.once('error', (err) => {\n this.logger.error('WebSocket error', { message: err.message })\n if (onError) {\n onError(err)\n onError = undefined\n }\n })\n }).catch((err) => {\n this.logger.error('Failed to import ws module', { message: String(err) })\n if (onError) {\n onError(err instanceof Error ? err : new Error(String(err)))\n }\n })\n }\n\n private handleBuiltinMessage(msg: HubToAgentMessage): void {\n if (msg.type === 'ping') {\n this.send({ type: 'pong' })\n }\n }\n\n private scheduleReconnect(): void {\n if (this.destroyed) return\n const delay = Math.min(\n RECONNECT_BASE_MS * Math.pow(2, this.reconnectAttempt),\n RECONNECT_MAX_MS,\n )\n this.reconnectAttempt++\n this.logger.info(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempt})`)\n this.reconnectTimer = setTimeout(() => {\n this.doConnect()\n }, delay)\n }\n\n private startHeartbeat(): void {\n this.stopHeartbeat()\n this.heartbeatTimer = setInterval(() => {\n this.send({ type: 'heartbeat', status: this.runtimeStatus })\n }, HEARTBEAT_INTERVAL_MS)\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = null\n }\n }\n\n private clearTimers(): void {\n this.stopHeartbeat()\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer)\n this.reconnectTimer = null\n }\n }\n}\n","import type { ITaskHandler, TaskContext, TaskProgress } from '@camstack/types'\nimport type { IScopedLogger } from '../logging/scoped-logger.js'\nimport type { AgentClient } from './agent-client.js'\n\ninterface RunningTask {\n readonly taskId: string\n readonly taskType: string\n cancelled: boolean\n}\n\n/**\n * Receives task assignments from hub, routes to registered ITaskHandler instances.\n * Maintains handler registry, manages task lifecycle.\n * Framework-agnostic (no NestJS).\n */\nexport class AgentTaskRunner {\n private readonly handlers = new Map<string, ITaskHandler>()\n private readonly runningTasks = new Map<string, RunningTask>()\n private readonly logger: IScopedLogger\n\n constructor(\n private readonly agentId: string,\n private readonly client: AgentClient,\n logger: IScopedLogger,\n ) {\n this.logger = logger.child('TaskRunner')\n }\n\n /** Register a task handler for a given task type */\n registerHandler(handler: ITaskHandler): void {\n if (this.handlers.has(handler.taskType)) {\n this.logger.warn(`Overwriting handler for task type: ${handler.taskType}`)\n }\n this.handlers.set(handler.taskType, handler)\n this.logger.debug(`Registered handler: ${handler.taskType}`)\n }\n\n /** Unregister a task handler */\n unregisterHandler(taskType: string): void {\n this.handlers.delete(taskType)\n }\n\n /** Get all registered task types */\n getTaskTypes(): readonly string[] {\n return [...this.handlers.keys()]\n }\n\n /** Get a handler by task type */\n getHandler(taskType: string): ITaskHandler | undefined {\n return this.handlers.get(taskType)\n }\n\n /**\n * Execute a task dispatched from the hub.\n * Sends result or error back to hub via the AgentClient.\n */\n async executeTask(taskId: string, taskType: string, payload: unknown): Promise<void> {\n const handler = this.handlers.get(taskType)\n if (!handler) {\n this.client.send({\n type: 'task.result',\n taskId,\n success: false,\n error: `No handler registered for task type: ${taskType}`,\n })\n return\n }\n\n const runningTask: RunningTask = { taskId, taskType, cancelled: false }\n this.runningTasks.set(taskId, runningTask)\n\n const context: TaskContext = {\n taskId,\n agentId: this.agentId,\n logger: this.logger.child(taskType),\n reportProgress: (progress: TaskProgress) => {\n this.client.send({ type: 'task.progress', taskId, progress })\n },\n isCancelled: () => runningTask.cancelled,\n }\n\n try {\n const result = await handler.handle(payload, context)\n this.runningTasks.delete(taskId)\n\n if (!runningTask.cancelled) {\n this.client.send({\n type: 'task.result',\n taskId,\n success: true,\n result,\n })\n }\n } catch (err) {\n this.runningTasks.delete(taskId)\n\n this.logger.error(`Task ${taskType} (${taskId}) failed`, {\n message: err instanceof Error ? err.message : String(err),\n })\n\n this.client.send({\n type: 'task.result',\n taskId,\n success: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n /** Cancel a running task */\n async cancelTask(taskId: string): Promise<void> {\n const running = this.runningTasks.get(taskId)\n if (!running) return\n\n running.cancelled = true\n\n const handler = this.handlers.get(running.taskType)\n if (handler?.cancel) {\n try {\n await handler.cancel()\n } catch (err) {\n this.logger.error(`Error cancelling task ${taskId}`, {\n message: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n this.runningTasks.delete(taskId)\n }\n\n /** Number of currently running tasks */\n get activeTaskCount(): number {\n return this.runningTasks.size\n }\n\n /** Destroy: cancel all running tasks */\n async destroy(): Promise<void> {\n const taskIds = [...this.runningTasks.keys()]\n for (const taskId of taskIds) {\n await this.cancelTask(taskId)\n }\n this.handlers.clear()\n }\n}\n","import type { ITaskHandler, TaskContext } from '@camstack/types'\n\n/**\n * Stub task handler for pipeline.decode.\n * In the full implementation, this would start a StreamBroker for a camera\n * and send decoded frames to the hub via the AgentClient.\n */\nexport class DecodeTaskHandler implements ITaskHandler {\n readonly taskType = 'pipeline.decode'\n readonly description = 'Decode an RTSP stream and produce JPEG frames'\n\n async handle(payload: unknown, context: TaskContext): Promise<unknown> {\n const { logger } = context\n const config = payload as { cameraId?: string; rtspUrl?: string; fps?: number }\n\n if (!config.cameraId || !config.rtspUrl) {\n throw new Error('pipeline.decode requires cameraId and rtspUrl in payload')\n }\n\n logger.info(`Decode task started: camera=${config.cameraId} url=${config.rtspUrl} fps=${config.fps ?? 1}`)\n\n // Stub: actual implementation would start ffmpeg, produce frames,\n // and send them via the AgentClient's sendBinary method.\n return { status: 'started', cameraId: config.cameraId }\n }\n\n async cancel(): Promise<void> {\n // Stub: stop ffmpeg process\n }\n}\n\n/**\n * Stub task handler for pipeline.detect.\n * In the full implementation, this would receive frames (via binary WS),\n * run them through the detection engine, and send results back.\n */\nexport class DetectTaskHandler implements ITaskHandler {\n readonly taskType = 'pipeline.detect'\n readonly description = 'Run object detection on received frames'\n\n async handle(payload: unknown, context: TaskContext): Promise<unknown> {\n const { logger } = context\n const config = payload as {\n cameraId?: string\n modelId?: string\n runtime?: string\n confidence?: number\n }\n\n if (!config.cameraId) {\n throw new Error('pipeline.detect requires cameraId in payload')\n }\n\n logger.info(`Detect task started: camera=${config.cameraId} model=${config.modelId ?? 'default'} runtime=${config.runtime ?? 'auto'}`)\n\n // Stub: actual implementation would subscribe to binary frame stream,\n // run inference, and send detection results back to hub.\n return { status: 'started', cameraId: config.cameraId }\n }\n\n async cancel(): Promise<void> {\n // Stub: stop detection loop\n }\n}\n\n/**\n * Stub task handler for pipeline.record.\n * In the full implementation, this would start recording an RTSP stream\n * to disk in segments and report segment metadata back to the hub.\n */\nexport class RecordTaskHandler implements ITaskHandler {\n readonly taskType = 'pipeline.record'\n readonly description = 'Record an RTSP stream to disk segments'\n\n async handle(payload: unknown, context: TaskContext): Promise<unknown> {\n const { logger } = context\n const config = payload as { cameraId?: string; rtspUrl?: string; storagePath?: string }\n\n if (!config.cameraId || !config.rtspUrl) {\n throw new Error('pipeline.record requires cameraId and rtspUrl in payload')\n }\n\n logger.info(`Record task started: camera=${config.cameraId} url=${config.rtspUrl}`)\n\n // Stub: actual implementation would start ffmpeg copy-mode recording.\n return { status: 'started', cameraId: config.cameraId }\n }\n\n async cancel(): Promise<void> {\n // Stub: stop recording process\n }\n}\n","import Database from 'better-sqlite3'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { randomUUID } from 'node:crypto'\nimport type {\n IStorageProvider,\n IStorageLocation,\n IStructuredStorage,\n IFileStorage,\n StorageLocationName,\n StorageRecord,\n QueryFilter,\n} from '@camstack/types'\n\ntype LocationType = 'structured' | 'files' | 'both'\n\n/** Which locations need structured (SQL) storage, files storage, or both.\n * All structured locations share a SINGLE SQLite database (camstack.db).\n * File locations use the filesystem at their configured path. */\nconst LOCATION_TYPES: Record<string, LocationType> = {\n // New location names (from StorageLocationManager)\n data: 'structured', // settings, events, trails — SQL only\n media: 'files', // crops, snapshots, thumbnails — files only\n recordings: 'files', // video segments — files only\n models: 'files', // ONNX/TFLite models — files only\n cache: 'files', // temp files — files only\n logs: 'files', // Winston log files — files only\n // Legacy location names (backward compat)\n config: 'structured',\n events: 'structured',\n addon: 'both',\n}\n\nclass SqliteStructuredStorage implements IStructuredStorage {\n private readonly ensuredTables = new Set<string>()\n\n constructor(private readonly db: Database.Database) {}\n\n private ensureTable(collection: string): void {\n if (this.ensuredTables.has(collection)) return\n this.db.exec(\n `CREATE TABLE IF NOT EXISTS \"${collection}\" (id TEXT PRIMARY KEY, data TEXT)`,\n )\n this.ensuredTables.add(collection)\n }\n\n async insert(record: StorageRecord): Promise<StorageRecord> {\n this.ensureTable(record.collection)\n const id = record.id || randomUUID()\n const newRecord: StorageRecord = {\n collection: record.collection,\n id,\n data: record.data,\n }\n this.db\n .prepare(`INSERT INTO \"${record.collection}\" (id, data) VALUES (?, ?)`)\n .run(id, JSON.stringify(newRecord.data))\n return newRecord\n }\n\n async query(collection: string, filter?: QueryFilter): Promise<readonly StorageRecord[]> {\n this.ensureTable(collection)\n const { sql, params } = this.buildSelect(collection, filter)\n const rows = this.db.prepare(sql).all(...params) as Array<{ id: string; data: string }>\n return rows.map((row) => ({\n collection,\n id: row.id,\n data: JSON.parse(row.data) as Record<string, unknown>,\n }))\n }\n\n async update(\n collection: string,\n id: string,\n data: Record<string, unknown>,\n ): Promise<StorageRecord> {\n this.ensureTable(collection)\n this.db\n .prepare(`UPDATE \"${collection}\" SET data = ? WHERE id = ?`)\n .run(JSON.stringify(data), id)\n return { collection, id, data }\n }\n\n async delete(collection: string, id: string): Promise<void> {\n this.ensureTable(collection)\n this.db.prepare(`DELETE FROM \"${collection}\" WHERE id = ?`).run(id)\n }\n\n async count(collection: string, filter?: QueryFilter): Promise<number> {\n this.ensureTable(collection)\n const { sql, params } = this.buildCount(collection, filter)\n const row = this.db.prepare(sql).get(...params) as { cnt: number }\n return row.cnt\n }\n\n private buildWhereClause(filter?: QueryFilter): { clause: string; params: unknown[] } {\n if (!filter) return { clause: '', params: [] }\n\n const conditions: string[] = []\n const params: unknown[] = []\n\n if (filter.where) {\n for (const [field, value] of Object.entries(filter.where)) {\n if (field === 'id') {\n conditions.push('id = ?')\n params.push(value)\n } else {\n conditions.push(`json_extract(data, '$.${field}') = ?`)\n params.push(value)\n }\n }\n }\n\n if (filter.whereIn) {\n for (const [field, values] of Object.entries(filter.whereIn)) {\n const placeholders = values.map(() => '?').join(', ')\n if (field === 'id') {\n conditions.push(`id IN (${placeholders})`)\n } else {\n conditions.push(`json_extract(data, '$.${field}') IN (${placeholders})`)\n }\n params.push(...values)\n }\n }\n\n if (filter.whereBetween) {\n for (const [field, [low, high]] of Object.entries(filter.whereBetween)) {\n if (field === 'id') {\n conditions.push('id BETWEEN ? AND ?')\n } else {\n conditions.push(`json_extract(data, '$.${field}') BETWEEN ? AND ?`)\n }\n params.push(low, high)\n }\n }\n\n const clause = conditions.length > 0 ? ` WHERE ${conditions.join(' AND ')}` : ''\n return { clause, params }\n }\n\n private buildSelect(collection: string, filter?: QueryFilter): { sql: string; params: unknown[] } {\n const { clause, params } = this.buildWhereClause(filter)\n let sql = `SELECT id, data FROM \"${collection}\"${clause}`\n\n if (filter?.orderBy) {\n const dir = filter.orderBy.direction === 'desc' ? 'DESC' : 'ASC'\n if (filter.orderBy.field === 'id') {\n sql += ` ORDER BY id ${dir}`\n } else {\n sql += ` ORDER BY json_extract(data, '$.${filter.orderBy.field}') ${dir}`\n }\n }\n\n if (filter?.limit !== undefined) {\n sql += ` LIMIT ?`\n params.push(filter.limit)\n }\n\n if (filter?.offset !== undefined) {\n sql += ` OFFSET ?`\n params.push(filter.offset)\n }\n\n return { sql, params }\n }\n\n private buildCount(collection: string, filter?: QueryFilter): { sql: string; params: unknown[] } {\n const { clause, params } = this.buildWhereClause(filter)\n return { sql: `SELECT COUNT(*) as cnt FROM \"${collection}\"${clause}`, params }\n }\n}\n\nexport class FileSystemStorage implements IFileStorage {\n constructor(private readonly basePath: string) {}\n\n async readFile(filePath: string): Promise<Buffer> {\n const fullPath = path.join(this.basePath, filePath)\n return fs.promises.readFile(fullPath)\n }\n\n async writeFile(filePath: string, data: Buffer): Promise<void> {\n const fullPath = path.join(this.basePath, filePath)\n fs.mkdirSync(path.dirname(fullPath), { recursive: true })\n await fs.promises.writeFile(fullPath, data)\n }\n\n async deleteFile(filePath: string): Promise<void> {\n const fullPath = path.join(this.basePath, filePath)\n await fs.promises.unlink(fullPath)\n }\n\n async listFiles(prefix?: string): Promise<readonly string[]> {\n const searchDir = prefix ? path.join(this.basePath, prefix) : this.basePath\n try {\n const entries = await fs.promises.readdir(searchDir, { recursive: true })\n const files: string[] = []\n for (const entry of entries) {\n const entryStr = String(entry)\n const relative = prefix ? path.join(prefix, entryStr) : entryStr\n const fullPath = path.join(this.basePath, relative)\n try {\n const stat = await fs.promises.stat(fullPath)\n if (stat.isFile()) {\n files.push(relative)\n }\n } catch {\n // Skip entries that can't be stat'd\n }\n }\n return files\n } catch {\n return []\n }\n }\n\n async getFileUrl(_path: string): Promise<string> {\n return null as unknown as string\n }\n\n async exists(filePath: string): Promise<boolean> {\n const fullPath = path.join(this.basePath, filePath)\n try {\n await fs.promises.access(fullPath, fs.constants.F_OK)\n return true\n } catch {\n return false\n }\n }\n}\n\nexport class SqliteStorageProvider implements IStorageProvider {\n private mainDb: Database.Database | null = null\n private sharedStructured: SqliteStructuredStorage | null = null\n private readonly locations = new Map<StorageLocationName, IStorageLocation>()\n\n async initialize(): Promise<void> {\n // Called by interface contract; actual setup done via configure()\n }\n\n /**\n * Configure all storage locations.\n * ONE single SQLite database (camstack.db) is used for ALL structured storage.\n * File-based locations use the filesystem at their configured path.\n */\n async configure(config: { locations: Record<string, string> }): Promise<void> {\n // Find the 'data' location path for the single DB (fall back to first structured location)\n const dataPath = config.locations['data'] ?? config.locations['config'] ?? Object.values(config.locations)[0]\n if (!dataPath) throw new Error('No data path configured for SQLite storage')\n\n // Create single database\n fs.mkdirSync(dataPath, { recursive: true })\n const dbPath = path.join(dataPath, 'camstack.db')\n this.mainDb = new Database(dbPath)\n this.mainDb.pragma('journal_mode = WAL')\n this.sharedStructured = new SqliteStructuredStorage(this.mainDb)\n\n // Configure each location\n for (const [name, dirPath] of Object.entries(config.locations)) {\n const locationName = name as StorageLocationName\n const locationType = LOCATION_TYPES[name] ?? 'files'\n\n fs.mkdirSync(dirPath, { recursive: true })\n\n const location: IStorageLocation = {}\n\n // All structured storage shares the single DB\n if (locationType === 'structured' || locationType === 'both') {\n location.structured = this.sharedStructured\n }\n\n if (locationType === 'files' || locationType === 'both') {\n location.files = new FileSystemStorage(dirPath)\n }\n\n this.locations.set(locationName, location)\n }\n }\n\n getLocation(name: StorageLocationName): IStorageLocation {\n const location = this.locations.get(name)\n if (!location) {\n throw new Error(`Storage location \"${name}\" not found`)\n }\n return location\n }\n\n async shutdown(): Promise<void> {\n if (this.mainDb) {\n this.mainDb.close()\n this.mainDb = null\n this.sharedStructured = null\n }\n this.locations.clear()\n }\n\n async export(_locationName: StorageLocationName): Promise<Buffer> {\n throw new Error('Export not yet implemented')\n }\n\n async import(_locationName: StorageLocationName, _data: Buffer): Promise<void> {\n throw new Error('Import not yet implemented')\n }\n}\n","import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n IConfigurable,\n ConfigUISchema,\n CapabilityProviderMap,\n} from '@camstack/types'\nimport { SqliteStorageProvider } from './sqlite-storage.provider'\n\nexport class SqliteStorageAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'sqlite-storage',\n name: 'SQLite Storage',\n version: '1.0.0',\n capabilities: ['storage'],\n }\n\n private provider: SqliteStorageProvider | null = null\n\n async initialize(context: AddonContext): Promise<void> {\n const storageConfig = {\n locations: { ...context.locationPaths } as Record<string, string>,\n }\n this.provider = new SqliteStorageProvider()\n await this.provider.configure(storageConfig)\n context.logger.info('SQLite storage initialized')\n }\n\n async shutdown(): Promise<void> {\n await this.provider?.shutdown()\n }\n\n getProvider(): SqliteStorageProvider {\n if (!this.provider) throw new Error('SQLite storage not initialized')\n return this.provider\n }\n\n getCapabilityProvider<K extends keyof CapabilityProviderMap>(\n name: K,\n ): CapabilityProviderMap[K] | null {\n if (name === 'storage' && this.provider) {\n return this.provider as unknown as CapabilityProviderMap[K]\n }\n return null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [],\n }\n }\n\n getConfig(): Record<string, unknown> {\n return {}\n }\n\n async onConfigChange(_config: Record<string, unknown>): Promise<void> {\n // No configurable fields\n }\n}\n","import * as winston from 'winston'\nimport DailyRotateFile from 'winston-daily-rotate-file'\nimport * as path from 'node:path'\nimport type { ILogDestination, LogEntry, LogFilter } from '@camstack/types'\n\ninterface WinstonConfig {\n readonly level: string\n readonly retentionDays: number\n /** Resolved absolute path to the logs directory (replaces the old dataPath field). */\n readonly logsDir: string\n}\n\nfunction formatScope(scope: readonly string[]): string {\n if (scope.length === 0) return ''\n const [first, ...rest] = scope\n const restFormatted = rest.map((s) => `[${s}]`).join('')\n return `(${first})${restFormatted}`\n}\n\nexport class WinstonDestination implements ILogDestination {\n private logger: winston.Logger | null = null\n\n async initialize(config?: WinstonConfig): Promise<void> {\n const {\n level = 'info',\n retentionDays = 30,\n logsDir = path.join('./data', 'logs'),\n } = config ?? {}\n\n const consoleFormat = winston.format.combine(\n winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),\n winston.format.colorize(),\n winston.format.printf(({ timestamp, level: lvl, message, scope }) => {\n const scopeStr = scope ? ` ${scope}` : ''\n return `${timestamp} [${lvl}]${scopeStr} - ${message}`\n }),\n )\n\n const fileFormat = winston.format.combine(\n winston.format.timestamp(),\n winston.format.json(),\n )\n\n this.logger = winston.createLogger({\n level,\n transports: [\n new winston.transports.Console({\n format: consoleFormat,\n }),\n new DailyRotateFile({\n dirname: logsDir,\n filename: 'camstack-%DATE%.log',\n datePattern: 'YYYY-MM-DD',\n maxFiles: `${retentionDays}d`,\n format: fileFormat,\n }),\n ],\n })\n }\n\n write(entry: LogEntry): void {\n if (!this.logger) return\n\n const scope = formatScope(entry.scope)\n const meta = entry.meta ?? {}\n\n this.logger.log({\n level: entry.level,\n message: entry.message,\n scope,\n timestamp: entry.timestamp.toISOString(),\n ...meta,\n })\n }\n\n async query(_filter: LogFilter): Promise<readonly LogEntry[]> {\n // File-based log querying is not implemented; use structured storage for log queries\n return []\n }\n\n async shutdown(): Promise<void> {\n if (!this.logger) return\n\n await new Promise<void>((resolve) => {\n this.logger!.on('finish', resolve)\n this.logger!.end()\n })\n this.logger = null\n }\n}\n","import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n IConfigurable,\n ConfigUISchema,\n CapabilityProviderMap,\n} from '@camstack/types'\nimport { WinstonDestination } from './winston-destination'\n\nexport class WinstonLoggingAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'winston-logging',\n name: 'Winston Logging',\n version: '1.0.0',\n capabilities: ['log-destination'],\n }\n\n private destination: WinstonDestination | null = null\n private currentConfig = {\n level: 'info',\n retentionDays: 30,\n }\n\n async initialize(context: AddonContext): Promise<void> {\n this.currentConfig = {\n level: (context.addonConfig.level as string) ?? this.currentConfig.level,\n retentionDays: (context.addonConfig.retentionDays as number) ?? this.currentConfig.retentionDays,\n }\n const logsDir = context.locationPaths.logs\n this.destination = new WinstonDestination()\n await this.destination.initialize({ ...this.currentConfig, logsDir })\n context.logger.info('Winston logging initialized')\n }\n\n async shutdown(): Promise<void> {\n await this.destination?.shutdown()\n }\n\n getDestination(): WinstonDestination {\n if (!this.destination) throw new Error('Winston not initialized')\n return this.destination\n }\n\n getCapabilityProvider<K extends keyof CapabilityProviderMap>(\n name: K,\n ): CapabilityProviderMap[K] | null {\n if (name === 'log-destination' && this.destination) {\n return this.destination as unknown as CapabilityProviderMap[K]\n }\n return null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'logging',\n title: 'Logging Settings',\n columns: 2,\n fields: [\n {\n type: 'select',\n key: 'level',\n label: 'Log Level',\n description: 'Minimum severity of messages that will be written to log files',\n options: [\n { value: 'error', label: 'Error', description: 'Only critical errors' },\n { value: 'warn', label: 'Warning', description: 'Errors and warnings' },\n { value: 'info', label: 'Info', description: 'General operational messages (recommended)' },\n { value: 'debug', label: 'Debug', description: 'Verbose output for debugging' },\n ],\n },\n {\n type: 'number',\n key: 'retentionDays',\n label: 'Log Retention',\n description: 'Number of days to keep log files before automatic deletion',\n min: 1,\n max: 365,\n step: 1,\n unit: 'days',\n },\n ],\n },\n ],\n }\n }\n\n getConfig(): Record<string, unknown> {\n return { ...this.currentConfig }\n }\n\n async onConfigChange(config: Record<string, unknown>): Promise<void> {\n this.currentConfig = {\n level: (config.level as string) ?? this.currentConfig.level,\n retentionDays: (config.retentionDays as number) ?? this.currentConfig.retentionDays,\n }\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport type { IScopedLogger, IEventBus, IStorageLocation } from '@camstack/types'\n\nexport interface BackupManifest {\n readonly id: string\n readonly timestamp: number\n readonly label?: string\n readonly locations: readonly string[]\n readonly sizeMB: number\n readonly path: string\n}\n\nexport interface BackupConfig {\n readonly backupDir: string\n readonly retentionCount: number\n}\n\nexport class LocalBackupService {\n private manifests: BackupManifest[] = []\n\n constructor(\n private readonly config: BackupConfig,\n private readonly logger: IScopedLogger,\n private readonly eventBus: IEventBus,\n private readonly storage: IStorageLocation,\n ) {}\n\n /** Create a backup of specified locations */\n async backup(options?: {\n locations?: string[]\n label?: string\n }): Promise<BackupManifest> {\n const id = randomUUID()\n const timestamp = Date.now()\n const locations = options?.locations ?? ['config', 'events', 'logs']\n\n this.logger.info(`Starting backup ${id} (${locations.join(', ')})`)\n\n const manifest: BackupManifest = {\n id,\n timestamp,\n label: options?.label,\n locations,\n sizeMB: 0,\n path: `${this.config.backupDir}/${id}`,\n }\n\n const updated = [...this.manifests, manifest]\n this.manifests = updated\n\n await this.pruneOldBackups()\n\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'addon', id: 'local-backup' },\n category: 'backup.completed',\n data: { backupId: id, locations: [...locations], sizeMB: manifest.sizeMB },\n })\n\n this.logger.info(`Backup ${id} completed`)\n return manifest\n }\n\n /** Restore from a backup */\n async restore(backupId: string): Promise<void> {\n const manifest = this.manifests.find((m) => m.id === backupId)\n if (!manifest) throw new Error(`Backup ${backupId} not found`)\n\n this.logger.info(`Restoring from backup ${backupId}`)\n\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'addon', id: 'local-backup' },\n category: 'backup.restored',\n data: { backupId },\n })\n }\n\n /** List all backups sorted by timestamp descending */\n list(): readonly BackupManifest[] {\n return [...this.manifests].sort((a, b) => b.timestamp - a.timestamp)\n }\n\n /** Delete a specific backup */\n async delete(backupId: string): Promise<void> {\n this.manifests = this.manifests.filter((m) => m.id !== backupId)\n }\n\n private async pruneOldBackups(): Promise<void> {\n const sorted = [...this.manifests].sort((a, b) => a.timestamp - b.timestamp)\n\n while (sorted.length > this.config.retentionCount) {\n const oldest = sorted.shift()\n if (oldest) {\n this.logger.info(`Pruning old backup ${oldest.id}`)\n this.manifests = this.manifests.filter((m) => m.id !== oldest.id)\n }\n }\n }\n}\n","import * as path from 'node:path'\nimport type {\n ICamstackAddon, AddonManifest, AddonContext,\n IConfigurable, ConfigUISchema, CapabilityProviderMap,\n} from '@camstack/types'\nimport { LocalBackupService } from './local-backup'\nimport type { BackupConfig } from './local-backup'\n\nexport class LocalBackupAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'local-backup',\n name: 'Local Backup',\n version: '1.0.0',\n capabilities: ['backup'],\n }\n\n private service: LocalBackupService | null = null\n private currentConfig = {\n retentionCount: 7,\n }\n\n async initialize(context: AddonContext): Promise<void> {\n this.currentConfig = {\n retentionCount: (context.addonConfig.retentionCount as number) ?? this.currentConfig.retentionCount,\n }\n const backupConfig: BackupConfig = {\n backupDir: path.join(context.locationPaths.data, 'backups'),\n retentionCount: this.currentConfig.retentionCount,\n }\n this.service = new LocalBackupService(\n backupConfig,\n context.logger,\n context.eventBus,\n context.storage,\n )\n context.logger.info('Local Backup initialized')\n }\n\n async shutdown(): Promise<void> {\n this.service = null\n }\n\n getService(): LocalBackupService {\n if (!this.service) throw new Error('Local Backup not initialized')\n return this.service\n }\n\n getCapabilityProvider<K extends keyof CapabilityProviderMap>(\n name: K,\n ): CapabilityProviderMap[K] | null {\n if (name === 'backup' as string && this.service) {\n return this.service as unknown as CapabilityProviderMap[K]\n }\n return null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'backup',\n title: 'Backup Settings',\n fields: [\n {\n type: 'number',\n key: 'retentionCount',\n label: 'Backups to Keep',\n description: 'Maximum number of backup archives to keep; oldest are deleted first',\n min: 1,\n max: 100,\n step: 1,\n unit: 'backups',\n },\n ],\n },\n ],\n }\n }\n\n getConfig(): Record<string, unknown> {\n return { ...this.currentConfig }\n }\n\n async onConfigChange(config: Record<string, unknown>): Promise<void> {\n this.currentConfig = {\n retentionCount: (config.retentionCount as number) ?? this.currentConfig.retentionCount,\n }\n }\n}\n","import path from 'node:path'\nimport type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n IAdminUI,\n CapabilityProviderMap,\n} from '@camstack/types'\n\n/**\n * AdminUIAddon — now lives inside @camstack/core as a builtin provider.\n * The frontend build artifact (dist/) is still produced by the separate\n * @camstack/addon-admin-ui package. This addon resolves the static dir\n * by looking for the admin-ui package relative to the workspace root.\n */\n\nfunction resolveAdminUiDistDir(): string {\n try {\n // Try to resolve @camstack/addon-admin-ui from node_modules\n const adminUiPkg = require.resolve('@camstack/addon-admin-ui/package.json')\n return path.join(path.dirname(adminUiPkg), 'dist')\n } catch {\n // Fallback: assume workspace layout\n return path.resolve(__dirname, '..', '..', '..', '..', 'addon-admin-ui', 'dist')\n }\n}\n\nexport class AdminUIAddon implements ICamstackAddon {\n readonly id = 'admin-ui'\n\n readonly manifest: AddonManifest = {\n id: 'admin-ui',\n name: 'CamStack Admin UI',\n version: '0.1.0',\n description: 'Web-based administration interface for CamStack',\n packageName: '@camstack/addon-admin-ui',\n capabilities: [{ name: 'admin-ui', mode: 'singleton' }],\n }\n\n async initialize(_ctx: AddonContext): Promise<void> {}\n async shutdown(): Promise<void> {}\n\n getCapabilityProvider<K extends keyof CapabilityProviderMap>(\n name: K,\n ): CapabilityProviderMap[K] | null {\n if (name === 'admin-ui') {\n const provider: IAdminUI = {\n getStaticDir: () => resolveAdminUiDistDir(),\n getVersion: () => this.manifest.version,\n }\n return provider as CapabilityProviderMap[K]\n }\n return null\n }\n}\n\nexport default AdminUIAddon\n","import { randomUUID } from 'node:crypto'\nimport type { IScopedLogger, IEventBus } from '@camstack/types'\n\nexport type ElementState = 'stopped' | 'starting' | 'running' | 'stopping' | 'error' | 'disabled'\n\nexport interface ElementStatus {\n state: ElementState\n error?: string\n startedAt?: number\n stoppedAt?: number\n restartCount: number\n uptime: number\n}\n\nconst VALID_TRANSITIONS: Readonly<Record<ElementState, readonly ElementState[]>> = {\n stopped: ['starting', 'disabled'],\n starting: ['running', 'error', 'stopping'],\n running: ['stopping', 'error'],\n stopping: ['stopped', 'error'],\n error: ['starting', 'stopped', 'disabled'],\n disabled: ['stopped'],\n}\n\nexport class LifecycleStateMachine {\n private _state: ElementState = 'stopped'\n private _error?: string\n private _startedAt?: number\n private _stoppedAt?: number\n private _restartCount = 0\n private _hasStartedOnce = false\n\n constructor(\n private readonly elementId: string,\n private readonly elementType: 'provider' | 'device' | 'addon' | 'process',\n private readonly eventBus: IEventBus,\n private readonly logger: IScopedLogger,\n ) {}\n\n get state(): ElementState {\n return this._state\n }\n\n getStatus(): ElementStatus {\n return {\n state: this._state,\n error: this._error,\n startedAt: this._startedAt,\n stoppedAt: this._stoppedAt,\n restartCount: this._restartCount,\n uptime:\n this._state === 'running' && this._startedAt\n ? Date.now() - this._startedAt\n : 0,\n }\n }\n\n transition(to: ElementState, error?: string): boolean {\n const from = this._state\n\n if (!this.isValidTransition(from, to)) {\n this.logger.warn(`Invalid state transition: ${from} → ${to}`)\n return false\n }\n\n this._state = to\n this._error = error\n\n if (to === 'running') {\n if (this._hasStartedOnce) {\n this._restartCount++\n }\n this._hasStartedOnce = true\n this._startedAt = Date.now()\n this._stoppedAt = undefined\n }\n\n if (to === 'stopped' || to === 'error' || to === 'disabled') {\n this._stoppedAt = Date.now()\n }\n\n this.logger.info(`State: ${from} → ${to}${error ? ` (${error})` : ''}`)\n\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: this.elementType, id: this.elementId },\n category: `${this.elementType}.state.${to}`,\n data: { from, to, error, elementId: this.elementId },\n })\n\n return true\n }\n\n incrementRestartCount(): void {\n this._restartCount++\n }\n\n private isValidTransition(from: ElementState, to: ElementState): boolean {\n return VALID_TRANSITIONS[from]?.includes(to) ?? false\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 type FeatureConfigReader = {\n readonly features: FeatureManifest\n}\n\nexport class FeatureManager {\n constructor(private readonly configReader: FeatureConfigReader) {}\n\n isEnabled(flag: FeatureFlag): boolean {\n return this.configReader.features[flag]\n }\n\n getManifest(): FeatureManifest {\n return { ...this.configReader.features }\n }\n}\n","import * as fs from 'node:fs'\nimport * as yaml from 'js-yaml'\nimport { bootstrapSchema, RUNTIME_DEFAULTS, type BootstrapConfig, type AppConfig } from './config-schema.js'\nimport type { FeatureManifest } from '../feature/feature-manager.js'\n\nexport interface ISettingsStore {\n getSystem(key: string): unknown\n setSystem(key: string, value: unknown): void\n getAllSystem(): Record<string, unknown>\n getAllAddon(addonId: string): Record<string, unknown>\n setAllAddon(addonId: string, config: Record<string, unknown>): void\n getAllProvider(providerId: string): Record<string, unknown>\n setProvider(providerId: string, key: string, value: unknown): void\n getAllDevice(deviceId: string): Record<string, unknown>\n setDevice(deviceId: string, key: string, value: unknown): void\n}\n\n/**\n * Maps environment variable names to dot-notation config paths.\n * Only bootstrap-level env vars -- runtime settings come from SQL (Plan B).\n */\nconst ENV_VAR_MAP: Record<string, string> = {\n CAMSTACK_PORT: 'server.port',\n CAMSTACK_HOST: 'server.host',\n CAMSTACK_DATA_PATH: 'server.dataPath',\n CAMSTACK_JWT_SECRET: 'auth.jwtSecret',\n CAMSTACK_ADMIN_USER: 'auth.adminUsername',\n CAMSTACK_ADMIN_PASS: 'auth.adminPassword',\n}\n\nexport class ConfigManager {\n // Non-readonly so update() can sync the in-memory view after a write.\n private bootstrapConfig: BootstrapConfig\n private settingsStore: ISettingsStore | null = null\n\n constructor(private readonly configPath: string) {\n const rawYaml = this.loadYaml()\n const merged = this.applyEnvOverrides(rawYaml as Record<string, unknown>)\n this.bootstrapConfig = bootstrapSchema.parse(merged)\n this.warnDefaultCredentials()\n }\n\n /** Called by main.ts after the SQLite DB is ready (Phase 2). */\n setSettingsStore(store: ISettingsStore): void {\n this.settingsStore = store\n }\n\n /**\n * Get a config value by dot-notation path.\n * Priority: bootstrap config -> SQL system_settings -> RUNTIME_DEFAULTS fallback.\n */\n get<T>(path: string): T {\n // First: check bootstrap config (server, auth)\n const bootstrapValue = this.getFromBootstrap(path)\n if (bootstrapValue !== undefined) {\n return bootstrapValue as T\n }\n\n // Second: check SQL system_settings (if store is wired)\n if (this.settingsStore !== null) {\n const sqlValue = this.settingsStore.getSystem(path)\n if (sqlValue !== undefined) {\n return sqlValue as T\n }\n // Also try prefix-based nested lookup against SQL keys\n const sqlNested = this.getNestedFromSystemSettings(path)\n if (sqlNested !== undefined) {\n return sqlNested as T\n }\n }\n\n // Third: check runtime defaults (backward compat when store not yet wired)\n if (path in RUNTIME_DEFAULTS) {\n return RUNTIME_DEFAULTS[path] as T\n }\n\n // Fourth: try nested lookup in runtime defaults\n const nested = this.getFromRuntimeDefaults(path)\n if (nested !== undefined) {\n return nested as T\n }\n\n return undefined as T\n }\n\n /**\n * Write a value to SQL system_settings.\n * Throws if the settings store is not yet wired.\n */\n set(key: string, value: unknown): void {\n if (this.settingsStore === null) {\n throw new Error('[ConfigManager] SettingsStore not initialized -- call setSettingsStore() first')\n }\n this.settingsStore.setSystem(key, value)\n }\n\n /**\n * Bulk-read all system_settings keys that belong to a logical section.\n * A \"section\" is the first segment of a dot-notation key (e.g. 'features', 'logging').\n */\n getSection(section: string): Record<string, unknown> {\n if (this.settingsStore !== null) {\n const nested = this.getNestedFromSystemSettings(section)\n if (nested !== undefined) return nested as Record<string, unknown>\n }\n // Fallback to RUNTIME_DEFAULTS\n return (this.getFromRuntimeDefaults(section) as Record<string, unknown>) ?? {}\n }\n\n /**\n * Bulk-write a section of runtime settings to SQL system_settings.\n * Each entry in `data` is stored as `section.key`.\n */\n setSection(section: string, data: Record<string, unknown>): void {\n if (this.settingsStore === null) {\n throw new Error('[ConfigManager] SettingsStore not initialized -- call setSettingsStore() first')\n }\n for (const [key, value] of Object.entries(data)) {\n this.settingsStore.setSystem(`${section}.${key}`, value)\n }\n }\n\n // ---------------------------------------------------------------------------\n // Addon / Provider / Device scoped config\n // ---------------------------------------------------------------------------\n\n /** Read all config for an addon from addon_settings. */\n getAddonConfig(addonId: string): Record<string, unknown> {\n if (this.settingsStore !== null) {\n return this.settingsStore.getAllAddon(addonId)\n }\n // Fallback: read from bootstrap (legacy)\n return (this.getFromBootstrap(`addons.${addonId}`) as Record<string, unknown>) ?? {}\n }\n\n /** Write (bulk-replace) config for an addon to addon_settings. */\n setAddonConfig(addonId: string, config: Record<string, unknown>): void {\n if (this.settingsStore === null) {\n throw new Error('[ConfigManager] SettingsStore not initialized -- call setSettingsStore() first')\n }\n this.settingsStore.setAllAddon(addonId, config)\n }\n\n /** Read all config for a provider from provider_settings. */\n getProviderConfig(providerId: string): Record<string, unknown> {\n if (this.settingsStore !== null) {\n return this.settingsStore.getAllProvider(providerId)\n }\n return {}\n }\n\n /** Write (upsert) a single key for a provider to provider_settings. */\n setProviderConfig(providerId: string, key: string, value: unknown): void {\n if (this.settingsStore === null) {\n throw new Error('[ConfigManager] SettingsStore not initialized -- call setSettingsStore() first')\n }\n this.settingsStore.setProvider(providerId, key, value)\n }\n\n /** Read all config for a device from device_settings. */\n getDeviceConfig(deviceId: string): Record<string, unknown> {\n if (this.settingsStore !== null) {\n return this.settingsStore.getAllDevice(deviceId)\n }\n return {}\n }\n\n /** Write (upsert) a single key for a device to device_settings. */\n setDeviceConfig(deviceId: string, key: string, value: unknown): void {\n if (this.settingsStore === null) {\n throw new Error('[ConfigManager] SettingsStore not initialized -- call setSettingsStore() first')\n }\n this.settingsStore.setDevice(deviceId, key, value)\n }\n\n /** Get a value from the parsed bootstrap config */\n getBootstrap<T>(path: string): T {\n return this.getFromBootstrap(path) as T\n }\n\n /** Features accessor -- reads from SQL when available, falls back to RUNTIME_DEFAULTS */\n get features(): FeatureManifest {\n const g = (key: string): boolean =>\n (this.get<boolean>(`features.${key}`) ?? RUNTIME_DEFAULTS[`features.${key}`]) as boolean\n return {\n streaming: g('streaming'),\n notifications: g('notifications'),\n objectDetection: g('objectDetection'),\n remoteAccess: g('remoteAccess'),\n agentCluster: g('agentCluster'),\n smartHome: g('smartHome'),\n recordings: g('recordings'),\n backup: g('backup'),\n repl: g('repl'),\n }\n }\n\n /**\n * Returns a merged view of bootstrap config + runtime defaults for backward compat.\n */\n get raw(): AppConfig {\n const features = {\n streaming: RUNTIME_DEFAULTS['features.streaming'] as boolean,\n notifications: RUNTIME_DEFAULTS['features.notifications'] as boolean,\n objectDetection: RUNTIME_DEFAULTS['features.objectDetection'] as boolean,\n remoteAccess: RUNTIME_DEFAULTS['features.remoteAccess'] as boolean,\n agentCluster: RUNTIME_DEFAULTS['features.agentCluster'] as boolean,\n smartHome: RUNTIME_DEFAULTS['features.smartHome'] as boolean,\n recordings: RUNTIME_DEFAULTS['features.recordings'] as boolean,\n backup: RUNTIME_DEFAULTS['features.backup'] as boolean,\n repl: RUNTIME_DEFAULTS['features.repl'] as boolean,\n }\n return {\n ...this.bootstrapConfig,\n features,\n storage: RUNTIME_DEFAULTS['storage.locations'] !== undefined\n ? {\n provider: RUNTIME_DEFAULTS['storage.provider'] as string,\n locations: RUNTIME_DEFAULTS['storage.locations'] as Record<string, string>,\n }\n : { provider: 'sqlite-storage', locations: {} },\n logging: {\n level: RUNTIME_DEFAULTS['logging.level'] as string,\n retentionDays: RUNTIME_DEFAULTS['logging.retentionDays'] as number,\n },\n eventBus: {\n ringBufferSize: RUNTIME_DEFAULTS['eventBus.ringBufferSize'] as number,\n },\n addons: {\n enabled: RUNTIME_DEFAULTS['addons.enabled'] as string[],\n },\n retention: {\n detectionEventsDays: RUNTIME_DEFAULTS['retention.detectionEventsDays'] as number,\n audioLevelsDays: RUNTIME_DEFAULTS['retention.audioLevelsDays'] as number,\n },\n providers: RUNTIME_DEFAULTS['providers'] as AppConfig['providers'],\n }\n }\n\n /**\n * Atomically update one top-level section of config.yaml and sync in-memory.\n * Only bootstrap sections (server, auth) are persisted. Runtime settings should\n * go to SQL (Plan B).\n */\n update(section: string, data: Record<string, unknown>): void {\n // Load latest raw YAML to avoid overwriting concurrent changes\n let raw: Record<string, unknown> = {}\n if (fs.existsSync(this.configPath)) {\n raw = (yaml.load(fs.readFileSync(this.configPath, 'utf-8')) as Record<string, unknown>) ?? {}\n }\n\n // Shallow merge within the section\n const existing = (raw[section] as Record<string, unknown>) ?? {}\n raw[section] = { ...existing, ...data }\n\n // Validate bootstrap sections\n const bootstrapSections: Record<string, unknown> = {}\n if (raw.server) bootstrapSections.server = raw.server\n if (raw.auth) bootstrapSections.auth = raw.auth\n\n const validation = bootstrapSchema.safeParse(bootstrapSections)\n if (!validation.success) {\n throw new Error(`[ConfigManager] Invalid config update for section \"${section}\": ${validation.error.message}`)\n }\n\n // Atomic write: temp file -> rename\n const tmpPath = `${this.configPath}.tmp`\n fs.writeFileSync(tmpPath, yaml.dump(raw, { lineWidth: 120, indent: 2, quotingType: '\"' }), 'utf-8')\n fs.renameSync(tmpPath, this.configPath)\n\n // Sync in-memory bootstrap state\n this.bootstrapConfig = validation.data\n }\n\n /**\n * Deep-set a value in a nested plain object using a dot-notation path.\n * Returns a new object (immutable).\n */\n private setNested(obj: Record<string, unknown>, path: string, value: unknown): Record<string, unknown> {\n const [head, ...rest] = path.split('.')\n if (!head) return obj\n if (rest.length === 0) {\n return { ...obj, [head]: value }\n }\n const child = (obj[head] ?? {}) as Record<string, unknown>\n return { ...obj, [head]: this.setNested(child, rest.join('.'), value) }\n }\n\n /**\n * Apply env var overrides onto the raw YAML object.\n * Only bootstrap-level env vars are applied.\n */\n private applyEnvOverrides(raw: Record<string, unknown>): Record<string, unknown> {\n let result = { ...raw }\n\n for (const [envKey, configPath] of Object.entries(ENV_VAR_MAP)) {\n const envValue = process.env[envKey]\n if (envValue === undefined || envValue === '') continue\n\n // Coerce to number for known numeric fields\n const coerced: unknown = configPath === 'server.port' ? Number(envValue) : envValue\n result = this.setNested(result, configPath, coerced)\n console.log(`[ConfigManager] Env override applied: ${envKey} → ${configPath}`)\n }\n\n return result\n }\n\n private loadYaml(): unknown {\n if (!fs.existsSync(this.configPath)) {\n console.warn(\n `[ConfigManager] Config file not found at: ${this.configPath}\\n` +\n ` → Using built-in defaults. Set CONFIG_PATH env var or create the file.\\n` +\n ` → Example path from project root: ./server/backend/data/config.yaml`,\n )\n return {}\n }\n\n const content = fs.readFileSync(this.configPath, 'utf-8')\n const parsed = yaml.load(content) ?? {}\n console.log(`[ConfigManager] Loaded config from: ${this.configPath}`)\n return parsed\n }\n\n private warnDefaultCredentials(): void {\n if (this.bootstrapConfig.auth.adminPassword === 'changeme') {\n console.warn(\n `[ConfigManager] Warning: Using default admin password \"changeme\". ` +\n `Set auth.adminPassword in your config.yaml or the ADMIN_PASSWORD env var.`,\n )\n }\n }\n\n private getFromBootstrap(path: string): unknown {\n const keys = path.split('.')\n let current: unknown = this.bootstrapConfig\n\n for (const key of keys) {\n if (current === null || current === undefined || typeof current !== 'object') {\n return undefined\n }\n current = (current as Record<string, unknown>)[key]\n }\n\n return current\n }\n\n private getFromRuntimeDefaults(path: string): unknown {\n // Look for prefix matches (e.g. 'features' -> checks 'features.*' entries)\n const prefix = path + '.'\n const result: Record<string, unknown> = {}\n let found = false\n\n for (const [key, value] of Object.entries(RUNTIME_DEFAULTS)) {\n if (key.startsWith(prefix)) {\n const subKey = key.slice(prefix.length)\n result[subKey] = value\n found = true\n }\n }\n\n return found ? result : undefined\n }\n\n /**\n * Perform a prefix-based nested lookup against SQL system_settings.\n * e.g. path='features' matches keys 'features.streaming', 'features.notifications', etc.\n * Returns an object keyed by the sub-key, or undefined if nothing is found.\n */\n private getNestedFromSystemSettings(path: string): unknown {\n if (this.settingsStore === null) return undefined\n const all = this.settingsStore.getAllSystem()\n const prefix = path + '.'\n const result: Record<string, unknown> = {}\n let found = false\n for (const [key, value] of Object.entries(all)) {\n if (key.startsWith(prefix)) {\n result[key.slice(prefix.length)] = value\n found = true\n }\n }\n return found ? result : undefined\n }\n}\n","import { z } from 'zod'\n\n/** Bootstrap config -- loaded from config.yaml ONLY at startup.\n * All other settings live in SQL (system_settings table). */\nexport const bootstrapSchema = z.object({\n /** Server mode: 'hub' (full server) or 'agent' (worker node) */\n mode: z.enum(['hub', 'agent']).default('hub'),\n server: z.object({\n port: z.number().default(4443),\n host: z.string().default('0.0.0.0'),\n dataPath: z.string().default('./data'),\n }).default({}),\n auth: z.object({\n jwtSecret: z.string().nullable().default(null),\n adminUsername: z.string().default('admin'),\n adminPassword: z.string().default(process.env.ADMIN_PASSWORD ?? 'changeme'),\n }).default({}),\n /** Hub connection config — only used when mode='agent' */\n hub: z.object({\n url: z.string().default('ws://localhost:4443/agent'),\n token: z.string().default(''),\n }).default({}),\n /** Agent-specific config — only used when mode='agent' */\n agent: z.object({\n name: z.string().default(''),\n /** Port for the agent status page (minimal HTML) */\n statusPort: z.number().default(4444),\n }).default({}),\n})\n\nexport type BootstrapConfig = z.infer<typeof bootstrapSchema>\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 */\nexport const RUNTIME_DEFAULTS: Record<string, unknown> = {\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: './data/data',\n media: './data/media',\n recordings: './data/recordings',\n cache: '/tmp/camstack-cache',\n logs: './data/logs',\n models: './data/models',\n },\n 'addons.enabled': [\n 'sqlite-storage',\n 'winston-logging',\n 'go2rtc',\n 'recording-engine',\n 'local-backup',\n 'cloudflare-tunnel',\n 'cloudflare-turn',\n ],\n 'providers': [],\n}\n\nexport type ServerMode = 'hub' | 'agent'\n\nexport type AppConfig = BootstrapConfig & {\n features: {\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 storage: {\n provider: string\n locations: Record<string, string>\n }\n logging: {\n level: string\n retentionDays: number\n }\n eventBus: {\n ringBufferSize: number\n }\n addons: {\n enabled: string[]\n }\n retention: {\n detectionEventsDays: number\n audioLevelsDays: number\n }\n providers: Array<{\n id: string\n type: string\n name: string\n url?: string\n username?: string\n password?: string\n mqtt?: {\n brokerUrl: string\n username?: string\n password?: string\n topicPrefix: string\n }\n }>\n}\n","import type {\n EventSource,\n SystemEvent,\n EventFilter,\n IEventBus,\n} from '@camstack/types'\n\nexport type { EventSource, SystemEvent, EventFilter }\n\ninterface Subscriber {\n readonly filter: EventFilter\n readonly callback: (event: SystemEvent) => void\n}\n\nclass EventRingBuffer {\n private readonly buffer: Array<SystemEvent | undefined>\n private head = 0\n private count = 0\n\n constructor(private readonly capacity: number) {\n this.buffer = new Array(capacity)\n }\n\n push(event: SystemEvent): void {\n this.buffer[this.head] = event\n this.head = (this.head + 1) % this.capacity\n if (this.count < this.capacity) {\n this.count++\n }\n }\n\n getAll(): readonly SystemEvent[] {\n const result: SystemEvent[] = []\n for (let i = 0; i < this.count; i++) {\n const index = (this.head - 1 - i + this.capacity) % this.capacity\n result.push(this.buffer[index]!)\n }\n return result\n }\n\n query(filter?: EventFilter, limit?: number): readonly SystemEvent[] {\n let result = [...this.getAll()]\n\n if (filter) {\n result = result.filter((event) => matchesFilter(event, filter))\n }\n\n if (limit !== undefined && limit > 0) {\n return result.slice(0, limit)\n }\n\n return result\n }\n}\n\nfunction matchesCategory(eventCategory: string, filterCategory: string): boolean {\n if (filterCategory.endsWith('.*')) {\n const prefix = filterCategory.slice(0, -2)\n return eventCategory.startsWith(prefix + '.') || eventCategory === prefix\n }\n return eventCategory === filterCategory\n}\n\nfunction matchesFilter(event: SystemEvent, filter: EventFilter): boolean {\n if (filter.category && !matchesCategory(event.category, filter.category)) {\n return false\n }\n\n if (filter.source) {\n if (event.source.type !== filter.source.type || event.source.id !== filter.source.id) {\n return false\n }\n }\n\n if (filter.since && event.timestamp < filter.since) {\n return false\n }\n\n return true\n}\n\nexport class SystemEventBus implements IEventBus {\n private readonly ringBuffer: EventRingBuffer\n private readonly subscribers: Subscriber[] = []\n\n constructor(bufferSize: number = 10000) {\n this.ringBuffer = new EventRingBuffer(bufferSize)\n }\n\n emit(event: SystemEvent): void {\n this.ringBuffer.push(event)\n\n for (const subscriber of this.subscribers) {\n if (matchesFilter(event, subscriber.filter)) {\n subscriber.callback(event)\n }\n }\n }\n\n subscribe(filter: EventFilter, handler: (event: SystemEvent) => void): () => void {\n const subscriber: Subscriber = { filter, callback: handler }\n this.subscribers.push(subscriber)\n\n return () => {\n const index = this.subscribers.indexOf(subscriber)\n if (index !== -1) {\n this.subscribers.splice(index, 1)\n }\n }\n }\n\n getRecent(filter?: EventFilter, limit?: number): readonly SystemEvent[] {\n return this.ringBuffer.query(filter, limit)\n }\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\nexport interface LogEntry {\n timestamp: Date\n level: LogLevel\n scope: string[]\n message: string\n meta?: Record<string, unknown>\n}\n\nexport interface LogFilter {\n scope?: string[]\n level?: LogLevel\n since?: Date\n until?: Date\n limit?: number\n}\n\nexport class LogRingBuffer {\n private readonly buffer: Array<LogEntry | undefined>\n private head = 0\n private count = 0\n\n constructor(private readonly capacity: number = 10000) {\n this.buffer = new Array(capacity)\n }\n\n push(entry: LogEntry): void {\n this.buffer[this.head] = entry\n this.head = (this.head + 1) % this.capacity\n if (this.count < this.capacity) {\n this.count++\n }\n }\n\n getAll(): LogEntry[] {\n const result: LogEntry[] = []\n\n for (let i = 0; i < this.count; i++) {\n const index = (this.head - 1 - i + this.capacity) % this.capacity\n result.push(this.buffer[index]!)\n }\n\n return result\n }\n\n query(filter: LogFilter): LogEntry[] {\n const all = this.getAll()\n let result = all\n\n if (filter.scope && filter.scope.length > 0) {\n result = result.filter((entry) => {\n for (let i = 0; i < filter.scope!.length; i++) {\n if (entry.scope[i] !== filter.scope![i]) return false\n }\n return true\n })\n }\n\n if (filter.level) {\n result = result.filter((entry) => entry.level === filter.level)\n }\n\n if (filter.since) {\n result = result.filter((entry) => entry.timestamp >= filter.since!)\n }\n\n if (filter.until) {\n result = result.filter((entry) => entry.timestamp <= filter.until!)\n }\n\n if (filter.limit !== undefined && filter.limit > 0) {\n result = result.slice(0, filter.limit)\n }\n\n return result\n }\n}\n","import type { IScopedLogger } from '@camstack/types'\nimport type { LogEntry, LogLevel } from './log-ring-buffer.js'\n\nexport type { IScopedLogger }\n\nexport class ScopedLogger implements IScopedLogger {\n constructor(\n private readonly scope: string[],\n private readonly writeFn: (entry: LogEntry) => void,\n ) {}\n\n debug(message: string, meta?: Record<string, unknown>): void {\n this.write('debug', message, meta)\n }\n\n info(message: string, meta?: Record<string, unknown>): void {\n this.write('info', message, meta)\n }\n\n warn(message: string, meta?: Record<string, unknown>): void {\n this.write('warn', message, meta)\n }\n\n error(message: string, meta?: Record<string, unknown>): void {\n this.write('error', message, meta)\n }\n\n child(childScope: string): IScopedLogger {\n return new ScopedLogger([...this.scope, childScope], this.writeFn)\n }\n\n private write(level: LogLevel, message: string, meta?: Record<string, unknown>): void {\n const entry: LogEntry = {\n timestamp: new Date(),\n level,\n scope: this.scope,\n message,\n ...(meta !== undefined ? { meta } : {}),\n }\n this.writeFn(entry)\n }\n}\n","import { LogRingBuffer, type LogEntry, type LogFilter } from './log-ring-buffer.js'\nimport { ScopedLogger, type IScopedLogger } from './scoped-logger.js'\n\nexport interface ILogDestination {\n initialize(): Promise<void>\n shutdown(): Promise<void>\n write(entry: LogEntry): void\n query?(filter: LogFilter): Promise<readonly LogEntry[]>\n}\n\nexport class LogManager {\n private readonly ringBuffer: LogRingBuffer\n private readonly destinations: ILogDestination[] = []\n\n constructor(bufferSize: number = 10000) {\n this.ringBuffer = new LogRingBuffer(bufferSize)\n }\n\n createLogger(scope: string): IScopedLogger {\n return new ScopedLogger([scope], (entry: LogEntry) => {\n this.ringBuffer.push(entry)\n for (const dest of this.destinations) {\n dest.write(entry)\n }\n })\n }\n\n addDestination(dest: ILogDestination): void {\n this.destinations.push(dest)\n }\n\n removeDestination(dest: ILogDestination): void {\n const index = this.destinations.indexOf(dest)\n if (index !== -1) {\n this.destinations.splice(index, 1)\n }\n }\n\n query(filter: LogFilter): LogEntry[] {\n return this.ringBuffer.query(filter)\n }\n}\n","import type { StorageLocationManager, StorageLocationName } from './storage-location-manager.js'\n\nexport interface IStorageProvider {\n initialize(): Promise<void>\n shutdown(): Promise<void>\n getLocation(name: StorageLocationName): IStorageLocation\n export(locationName: StorageLocationName): Promise<Buffer>\n import(locationName: StorageLocationName, data: Buffer): Promise<void>\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\nexport interface StorageRecord {\n collection: string\n id: string\n data: Record<string, unknown>\n}\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\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\nexport interface IStorageLocation {\n structured?: IStructuredStorage\n files?: IFileStorage\n}\n\nexport class StorageManager {\n private provider: IStorageProvider | null = null\n private locationManager: StorageLocationManager | null = null\n\n setProvider(provider: IStorageProvider): void {\n this.provider = provider\n }\n\n getProvider(): IStorageProvider {\n if (!this.provider) {\n throw new Error('No storage provider configured')\n }\n return this.provider\n }\n\n /**\n * Set the StorageLocationManager (called from main.ts during Phase 2 boot,\n * before NestJS lifecycle hooks run).\n */\n setLocationManager(manager: StorageLocationManager): void {\n this.locationManager = manager\n }\n\n /**\n * Get the StorageLocationManager (for file path resolution).\n * Available after Phase 2 boot (main.ts calls setLocationManager).\n */\n getLocationManager(): StorageLocationManager {\n if (!this.locationManager) {\n throw new Error('StorageLocationManager not initialized -- ensure Phase 2 boot ran before accessing this')\n }\n return this.locationManager\n }\n\n /**\n * Initialize the StorageLocationManager with a dataPath and set it on this service.\n * Called during 3-phase boot from main.ts (Phase 2).\n */\n async initializeLocations(dataPath: string): Promise<void> {\n const { StorageLocationManager } = await import('./storage-location-manager.js')\n const manager = new StorageLocationManager(dataPath)\n await manager.initializeDefaults()\n this.locationManager = manager\n }\n\n /**\n * Return the base filesystem path for a named storage location.\n * Convenience wrapper around locationManager.getBackend(name).basePath.\n * Available after Phase 2 boot (main.ts calls setLocationManager).\n */\n getLocationPath(name: StorageLocationName): string {\n return this.getLocationManager().getBackend(name).basePath\n }\n\n /**\n * Get a storage location, optionally namespaced.\n * Without namespace: returns the raw location (e.g., 'config' -> config DB)\n * With namespace: returns a scoped view where all collections/paths are prefixed\n * e.g., getLocation('addon', 'providers/frigate-1') -> collections prefixed with 'providers/frigate-1/'\n */\n getLocation(name: StorageLocationName | string, namespace?: string): IStorageLocation {\n // Map legacy location names to new LocationManager names\n const LEGACY_MAP: Record<string, string> = {\n config: 'data', // old 'config' -> new 'data' (SQLite DB location)\n events: 'data', // old 'events' -> new 'data'\n addon: 'data', // old 'addon' -> new 'data'\n }\n const mappedName = (LEGACY_MAP[name] ?? name) as StorageLocationName\n const location = this.getProvider().getLocation(mappedName)\n if (!namespace) return location\n\n // Wrap with namespace prefix\n return this.createNamespacedLocation(location, namespace)\n }\n\n private createNamespacedLocation(location: IStorageLocation, namespace: string): IStorageLocation {\n const prefix = namespace.endsWith('/') ? namespace : `${namespace}/`\n\n return {\n structured: location.structured ? {\n async query(collection: string, filter?: QueryFilter) {\n return location.structured!.query(`${prefix}${collection}`, filter)\n },\n async insert(record: StorageRecord) {\n return location.structured!.insert({ ...record, collection: `${prefix}${record.collection}` })\n },\n async update(collection: string, id: string, data: Record<string, unknown>) {\n return location.structured!.update(`${prefix}${collection}`, id, data)\n },\n async delete(collection: string, id: string) {\n return location.structured!.delete(`${prefix}${collection}`, id)\n },\n async count(collection: string, filter?: QueryFilter) {\n return location.structured!.count(`${prefix}${collection}`, filter)\n },\n } : undefined,\n\n files: location.files ? {\n async readFile(path: string) {\n return location.files!.readFile(`${prefix}${path}`)\n },\n async writeFile(path: string, data: Buffer) {\n return location.files!.writeFile(`${prefix}${path}`, data)\n },\n async deleteFile(path: string) {\n return location.files!.deleteFile(`${prefix}${path}`)\n },\n async listFiles(filePrefix?: string) {\n return location.files!.listFiles(`${prefix}${filePrefix ?? ''}`)\n },\n async getFileUrl(path: string) {\n return location.files!.getFileUrl(`${prefix}${path}`)\n },\n async exists(path: string) {\n return location.files!.exists(`${prefix}${path}`)\n },\n } : undefined,\n }\n }\n}\n","import Database from 'better-sqlite3'\nimport { CORE_TABLE_DDL } from './sql-schema.js'\nimport { RUNTIME_DEFAULTS } from '../config/config-schema.js'\n\n/**\n * Thin wrapper over better-sqlite3 that manages the four settings tables:\n * system_settings, addon_settings, provider_settings, device_settings.\n *\n * All values are stored as JSON text and deserialized on read.\n */\nexport class SettingsStore {\n private readonly db: Database.Database\n\n constructor(dbPath: string) {\n this.db = new Database(dbPath)\n this.db.pragma('journal_mode = WAL')\n this.db.pragma('foreign_keys = ON')\n this.initTables()\n }\n\n // ---------------------------------------------------------------------------\n // System settings\n // ---------------------------------------------------------------------------\n\n getSystem(key: string): unknown {\n const row = this.db\n .prepare<[string], { value: string }>('SELECT value FROM system_settings WHERE key = ?')\n .get(key)\n if (row === undefined) return undefined\n return JSON.parse(row.value)\n }\n\n setSystem(key: string, value: unknown): void {\n this.db\n .prepare(\n `INSERT INTO system_settings (key, value, updated_at) VALUES (?, json(?), unixepoch())\n ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`,\n )\n .run(key, JSON.stringify(value))\n }\n\n getAllSystem(): Record<string, unknown> {\n const rows = this.db\n .prepare<[], { key: string; value: string }>('SELECT key, value FROM system_settings')\n .all()\n return Object.fromEntries(rows.map(r => [r.key, JSON.parse(r.value)]))\n }\n\n // ---------------------------------------------------------------------------\n // Addon settings\n // ---------------------------------------------------------------------------\n\n getAddon(addonId: string, key: string): unknown {\n const row = this.db\n .prepare<[string, string], { value: string }>(\n 'SELECT value FROM addon_settings WHERE addon_id = ? AND key = ?',\n )\n .get(addonId, key)\n if (row === undefined) return undefined\n return JSON.parse(row.value)\n }\n\n setAddon(addonId: string, key: string, value: unknown): void {\n this.db\n .prepare(\n `INSERT INTO addon_settings (addon_id, key, value, updated_at) VALUES (?, ?, json(?), unixepoch())\n ON CONFLICT(addon_id, key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`,\n )\n .run(addonId, key, JSON.stringify(value))\n }\n\n getAllAddon(addonId: string): Record<string, unknown> {\n const rows = this.db\n .prepare<[string], { key: string; value: string }>(\n 'SELECT key, value FROM addon_settings WHERE addon_id = ?',\n )\n .all(addonId)\n return Object.fromEntries(rows.map(r => [r.key, JSON.parse(r.value)]))\n }\n\n /** Bulk-replace all keys for an addon (within a transaction). */\n setAllAddon(addonId: string, config: Record<string, unknown>): void {\n const deleteStmt = this.db.prepare('DELETE FROM addon_settings WHERE addon_id = ?')\n const insertStmt = this.db.prepare(\n `INSERT INTO addon_settings (addon_id, key, value, updated_at) VALUES (?, ?, json(?), unixepoch())`,\n )\n this.db.transaction(() => {\n deleteStmt.run(addonId)\n for (const [key, value] of Object.entries(config)) {\n insertStmt.run(addonId, key, JSON.stringify(value))\n }\n })()\n }\n\n // ---------------------------------------------------------------------------\n // Provider settings\n // ---------------------------------------------------------------------------\n\n getProvider(providerId: string, key: string): unknown {\n const row = this.db\n .prepare<[string, string], { value: string }>(\n 'SELECT value FROM provider_settings WHERE provider_id = ? AND key = ?',\n )\n .get(providerId, key)\n if (row === undefined) return undefined\n return JSON.parse(row.value)\n }\n\n setProvider(providerId: string, key: string, value: unknown): void {\n this.db\n .prepare(\n `INSERT INTO provider_settings (provider_id, key, value, updated_at) VALUES (?, ?, json(?), unixepoch())\n ON CONFLICT(provider_id, key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`,\n )\n .run(providerId, key, JSON.stringify(value))\n }\n\n getAllProvider(providerId: string): Record<string, unknown> {\n const rows = this.db\n .prepare<[string], { key: string; value: string }>(\n 'SELECT key, value FROM provider_settings WHERE provider_id = ?',\n )\n .all(providerId)\n return Object.fromEntries(rows.map(r => [r.key, JSON.parse(r.value)]))\n }\n\n // ---------------------------------------------------------------------------\n // Device settings\n // ---------------------------------------------------------------------------\n\n getDevice(deviceId: string, key: string): unknown {\n const row = this.db\n .prepare<[string, string], { value: string }>(\n 'SELECT value FROM device_settings WHERE device_id = ? AND key = ?',\n )\n .get(deviceId, key)\n if (row === undefined) return undefined\n return JSON.parse(row.value)\n }\n\n setDevice(deviceId: string, key: string, value: unknown): void {\n this.db\n .prepare(\n `INSERT INTO device_settings (device_id, key, value, updated_at) VALUES (?, ?, json(?), unixepoch())\n ON CONFLICT(device_id, key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`,\n )\n .run(deviceId, key, JSON.stringify(value))\n }\n\n getAllDevice(deviceId: string): Record<string, unknown> {\n const rows = this.db\n .prepare<[string], { key: string; value: string }>(\n 'SELECT key, value FROM device_settings WHERE device_id = ?',\n )\n .all(deviceId)\n return Object.fromEntries(rows.map(r => [r.key, JSON.parse(r.value)]))\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n /** Close the SQLite connection (call on shutdown). */\n close(): void {\n this.db.close()\n }\n\n /** Check if system_settings is empty (used for first-boot seeding). */\n isSystemSettingsEmpty(): boolean {\n const row = this.db\n .prepare<[], { cnt: number }>('SELECT COUNT(*) AS cnt FROM system_settings')\n .get()\n return (row?.cnt ?? 0) === 0\n }\n\n /** Seed system_settings with RUNTIME_DEFAULTS (only on first boot). */\n seedDefaults(): void {\n const insert = this.db.prepare(\n `INSERT OR IGNORE INTO system_settings (key, value, updated_at) VALUES (?, json(?), unixepoch())`,\n )\n this.db.transaction(() => {\n for (const [key, value] of Object.entries(RUNTIME_DEFAULTS)) {\n insert.run(key, JSON.stringify(value))\n }\n })()\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private initTables(): void {\n this.db.transaction(() => {\n for (const stmt of CORE_TABLE_DDL) {\n this.db.prepare(stmt).run()\n }\n })()\n }\n}\n","/** Core table DDL statements -- executed on first boot */\nexport const CORE_TABLE_DDL: readonly string[] = [\n // Settings tables\n `CREATE TABLE IF NOT EXISTS system_settings (\n key TEXT PRIMARY KEY,\n value JSON NOT NULL,\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\n )`,\n `CREATE TABLE IF NOT EXISTS addon_settings (\n addon_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value JSON NOT NULL,\n updated_at INTEGER NOT NULL DEFAULT (unixepoch()),\n PRIMARY KEY (addon_id, key)\n )`,\n `CREATE TABLE IF NOT EXISTS provider_settings (\n provider_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value JSON NOT NULL,\n updated_at INTEGER NOT NULL DEFAULT (unixepoch()),\n PRIMARY KEY (provider_id, key)\n )`,\n `CREATE TABLE IF NOT EXISTS device_settings (\n device_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value JSON NOT NULL,\n updated_at INTEGER NOT NULL DEFAULT (unixepoch()),\n PRIMARY KEY (device_id, key)\n )`,\n\n // Detection events\n `CREATE TABLE IF NOT EXISTS detection_events (\n id TEXT PRIMARY KEY,\n timestamp INTEGER NOT NULL,\n device_id TEXT NOT NULL,\n class_name TEXT NOT NULL,\n score REAL NOT NULL,\n severity TEXT NOT NULL,\n track_id TEXT,\n zones JSON,\n recognition JSON,\n media_files JSON,\n data JSON\n )`,\n `CREATE INDEX IF NOT EXISTS idx_det_device_ts ON detection_events(device_id, timestamp)`,\n `CREATE INDEX IF NOT EXISTS idx_det_class_ts ON detection_events(class_name, timestamp)`,\n\n // Audio levels\n `CREATE TABLE IF NOT EXISTS audio_levels (\n id TEXT PRIMARY KEY,\n timestamp INTEGER NOT NULL,\n device_id TEXT NOT NULL,\n dbfs REAL NOT NULL,\n rms REAL NOT NULL,\n state TEXT NOT NULL\n )`,\n `CREATE INDEX IF NOT EXISTS idx_audio_device_ts ON audio_levels(device_id, timestamp)`,\n\n // Track trails\n `CREATE TABLE IF NOT EXISTS track_trails (\n track_id TEXT PRIMARY KEY,\n device_id TEXT NOT NULL,\n class_name TEXT NOT NULL,\n first_seen INTEGER NOT NULL,\n last_seen INTEGER NOT NULL,\n positions JSON NOT NULL,\n snapshots JSON,\n total_distance REAL,\n zones_visited JSON\n )`,\n `CREATE INDEX IF NOT EXISTS idx_trails_device_ts ON track_trails(device_id, first_seen)`,\n]\n\n/** Addon table schema declaration */\nexport interface AddonTableSchema {\n readonly name: string\n readonly columns: ReadonlyArray<{\n readonly name: string\n readonly type: 'TEXT' | 'INTEGER' | 'REAL' | 'JSON'\n readonly primaryKey?: boolean\n readonly notNull?: boolean\n }>\n readonly indexes?: ReadonlyArray<{\n readonly name: string\n readonly columns: readonly string[]\n readonly unique?: boolean\n }>\n}\n\n/** Generate CREATE TABLE DDL from addon schema */\nexport function addonTableToDdl(schema: AddonTableSchema): string[] {\n const pks = schema.columns.filter(c => c.primaryKey).map(c => c.name)\n const colDefs = schema.columns.map(c => {\n const parts = [c.name, c.type]\n if (c.notNull) parts.push('NOT NULL')\n return parts.join(' ')\n })\n\n let ddl = `CREATE TABLE IF NOT EXISTS ${schema.name} (\\n ${colDefs.join(',\\n ')}`\n if (pks.length > 0) {\n ddl += `,\\n PRIMARY KEY (${pks.join(', ')})`\n }\n ddl += '\\n)'\n\n const stmts = [ddl]\n for (const idx of schema.indexes ?? []) {\n const unique = idx.unique ? 'UNIQUE ' : ''\n stmts.push(`CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${schema.name}(${idx.columns.join(', ')})`)\n }\n return stmts\n}\n","import * as jwt from 'jsonwebtoken'\nimport * as bcrypt from 'bcryptjs'\nimport * as crypto from 'node:crypto'\nimport type { ScopedToken } from '@camstack/types'\nimport type { ScopedTokenManager } from './scoped-token-manager.js'\n\nexport type UserRole = 'super_admin' | 'admin' | 'viewer'\n\nexport interface TokenPayload {\n type?: 'api_key'\n keyId?: string\n userId?: string\n username?: string\n role: UserRole\n allowedProviders: string[] | '*'\n allowedDevices: Record<string, string[] | '*'>\n iat?: number\n exp?: number\n}\n\nexport type AuthConfigReader = {\n get<T>(path: string): T\n update(section: string, data: Record<string, unknown>): void\n}\n\nexport class AuthManager {\n private readonly jwtSecret: string\n private scopedTokenManager: ScopedTokenManager | null = null\n\n constructor(private readonly config: AuthConfigReader) {\n const configured = this.config.get<string>('auth.jwtSecret')\n if (configured) {\n this.jwtSecret = configured\n } else {\n const secret = crypto.randomBytes(32).toString('hex')\n // Persist directly into config.yaml so it survives restarts\n this.config.update('auth', { jwtSecret: secret })\n console.log('[AuthManager] Generated JWT secret and saved to config.yaml (auth.jwtSecret)')\n this.jwtSecret = secret\n }\n }\n\n signToken(payload: Omit<TokenPayload, 'iat' | 'exp'>): string {\n return jwt.sign({ ...payload }, this.jwtSecret, { expiresIn: '24h' })\n }\n\n verifyToken(token: string): TokenPayload {\n return jwt.verify(token, this.jwtSecret) as TokenPayload\n }\n\n async hashPassword(password: string): Promise<string> {\n return bcrypt.hash(password, 10)\n }\n\n async comparePassword(password: string, hash: string): Promise<boolean> {\n return bcrypt.compare(password, hash)\n }\n\n generateApiKey(): { token: string; hash: string; prefix: string } {\n const token = crypto.randomBytes(32).toString('hex')\n const hash = crypto.createHash('sha256').update(token).digest('hex')\n const prefix = token.slice(0, 8)\n return { token, hash, prefix }\n }\n\n validateApiKey(token: string, storedHash: string): boolean {\n const hash = crypto.createHash('sha256').update(token).digest('hex')\n return hash === storedHash\n }\n\n /**\n * Set the scoped token manager for the auth chain.\n */\n setScopedTokenManager(manager: ScopedTokenManager): void {\n this.scopedTokenManager = manager\n }\n\n /**\n * Validate a scoped token string.\n * Returns the token record if valid, null otherwise.\n */\n async validateScopedToken(rawToken: string): Promise<ScopedToken | null> {\n if (!this.scopedTokenManager) {\n return null\n }\n return this.scopedTokenManager.validate(rawToken)\n }\n\n /**\n * Check whether a scoped token grants access to a given addon/route/capability.\n */\n matchesScopedTokenScope(\n token: ScopedToken,\n addonId?: string,\n routePath?: string,\n capability?: string,\n ): boolean {\n if (!this.scopedTokenManager) {\n return false\n }\n return this.scopedTokenManager.matchesScope(token, addonId, routePath, capability)\n }\n}\n","import * as crypto from 'node:crypto'\nimport type { AuthManager, UserRole } from './auth-manager.js'\nimport type { IStructuredStorage, StorageRecord, QueryFilter } from '../storage/storage-manager.js'\n\nexport interface ApiKeyRecord {\n id: string\n label: string\n role: UserRole\n allowedProviders: string[] | '*'\n allowedDevices: Record<string, string[] | '*'>\n tokenHash: string\n tokenPrefix: string\n createdAt: number\n lastUsedAt?: number\n}\n\ninterface CreateApiKeyInput {\n label: string\n role: UserRole\n allowedProviders?: string[] | '*'\n allowedDevices?: Record<string, string[] | '*'>\n}\n\nconst API_KEYS_COLLECTION = 'api_keys'\n\nexport type ApiKeyStorageAccess = {\n getStructuredStorage(): IStructuredStorage\n}\n\nexport class ApiKeyManager {\n constructor(\n private readonly storageAccess: ApiKeyStorageAccess,\n private readonly auth: AuthManager,\n ) {}\n\n private get structured(): IStructuredStorage {\n return this.storageAccess.getStructuredStorage()\n }\n\n async create(input: CreateApiKeyInput): Promise<{ record: ApiKeyRecord; token: string }> {\n const { token, hash, prefix } = this.auth.generateApiKey()\n const now = Date.now()\n\n const record: ApiKeyRecord = {\n id: crypto.randomUUID(),\n label: input.label,\n role: input.role,\n allowedProviders: input.allowedProviders ?? '*',\n allowedDevices: input.allowedDevices ?? {},\n tokenHash: hash,\n tokenPrefix: prefix,\n createdAt: now,\n }\n\n await this.structured.insert({\n collection: API_KEYS_COLLECTION,\n id: record.id,\n data: record as unknown as Record<string, unknown>,\n })\n\n return { record, token }\n }\n\n async validateToken(token: string): Promise<ApiKeyRecord | null> {\n const allKeys = await this.structured.query(API_KEYS_COLLECTION)\n\n for (const entry of allKeys) {\n const record = entry.data as unknown as ApiKeyRecord\n if (this.auth.validateApiKey(token, record.tokenHash)) {\n const updatedData: Record<string, unknown> = {\n ...record,\n lastUsedAt: Date.now(),\n }\n await this.structured.update(API_KEYS_COLLECTION, record.id, updatedData)\n return { ...record, lastUsedAt: updatedData.lastUsedAt as number }\n }\n }\n\n return null\n }\n\n async listAll(): Promise<Omit<ApiKeyRecord, 'tokenHash'>[]> {\n const results = await this.structured.query(API_KEYS_COLLECTION)\n return results.map((r) => {\n const { tokenHash, ...rest } = r.data as unknown as ApiKeyRecord\n return rest\n })\n }\n\n async revoke(id: string): Promise<void> {\n await this.structured.delete(API_KEYS_COLLECTION, id)\n }\n\n async findById(id: string): Promise<ApiKeyRecord | null> {\n const results = await this.structured.query(API_KEYS_COLLECTION, {\n where: { id },\n })\n\n if (results.length === 0) {\n return null\n }\n\n return results[0]!.data as unknown as ApiKeyRecord\n }\n}\n","import * as crypto from 'node:crypto'\nimport type { AuthManager, UserRole } from './auth-manager.js'\nimport type { IStructuredStorage } from '../storage/storage-manager.js'\n\nexport interface UserRecord {\n id: string\n username: string\n passwordHash: string\n role: UserRole\n allowedProviders: string[] | '*'\n allowedDevices: Record<string, string[] | '*'>\n createdAt: number\n updatedAt: number\n}\n\ninterface CreateUserInput {\n username: string\n password: string\n role: UserRole\n allowedProviders?: string[] | '*'\n allowedDevices?: Record<string, string[] | '*'>\n}\n\ntype UpdatableUserFields = Partial<Pick<UserRecord, 'role' | 'allowedProviders' | 'allowedDevices'>>\n\nconst USERS_COLLECTION = 'users'\n\nexport type UserStorageAccess = {\n getStructuredStorage(): IStructuredStorage\n}\n\nexport type UserConfigReader = {\n get<T>(path: string): T\n}\n\nexport class UserManager {\n constructor(\n private readonly storageAccess: UserStorageAccess,\n private readonly auth: AuthManager,\n private readonly config: UserConfigReader,\n ) {}\n\n private get structured(): IStructuredStorage {\n return this.storageAccess.getStructuredStorage()\n }\n\n async create(input: CreateUserInput): Promise<UserRecord> {\n const existing = await this.findByUsername(input.username)\n if (existing) {\n throw new Error(`User with username \"${input.username}\" already exists`)\n }\n\n const passwordHash = await this.auth.hashPassword(input.password)\n const now = Date.now()\n const record: UserRecord = {\n id: crypto.randomUUID(),\n username: input.username,\n passwordHash,\n role: input.role,\n allowedProviders: input.allowedProviders ?? '*',\n allowedDevices: input.allowedDevices ?? {},\n createdAt: now,\n updatedAt: now,\n }\n\n await this.structured.insert({\n collection: USERS_COLLECTION,\n id: record.id,\n data: record as unknown as Record<string, unknown>,\n })\n\n return record\n }\n\n async findByUsername(username: string): Promise<UserRecord | null> {\n const results = await this.structured.query(USERS_COLLECTION, {\n where: { username },\n })\n\n if (results.length === 0) {\n return null\n }\n\n return results[0]!.data as unknown as UserRecord\n }\n\n async findById(id: string): Promise<UserRecord | null> {\n const results = await this.structured.query(USERS_COLLECTION, {\n where: { id },\n })\n\n if (results.length === 0) {\n return null\n }\n\n return results[0]!.data as unknown as UserRecord\n }\n\n async validateCredentials(username: string, password: string): Promise<UserRecord | null> {\n const user = await this.findByUsername(username)\n if (!user) {\n return null\n }\n\n const valid = await this.auth.comparePassword(password, user.passwordHash)\n return valid ? user : null\n }\n\n async listAll(): Promise<Omit<UserRecord, 'passwordHash'>[]> {\n const results = await this.structured.query(USERS_COLLECTION)\n return results.map((r) => {\n const { passwordHash, ...rest } = r.data as unknown as UserRecord\n return rest\n })\n }\n\n async update(id: string, data: UpdatableUserFields): Promise<void> {\n const existing = await this.findById(id)\n if (!existing) {\n throw new Error(`User with id \"${id}\" not found`)\n }\n\n const updatedData: Record<string, unknown> = {\n ...existing,\n ...data,\n updatedAt: Date.now(),\n }\n\n await this.structured.update(USERS_COLLECTION, id, updatedData)\n }\n\n async delete(id: string): Promise<void> {\n await this.structured.delete(USERS_COLLECTION, id)\n }\n\n async resetPassword(id: string, newPassword: string): Promise<void> {\n const existing = await this.findById(id)\n if (!existing) {\n throw new Error(`User with id \"${id}\" not found`)\n }\n\n const passwordHash = await this.auth.hashPassword(newPassword)\n const updatedData: Record<string, unknown> = {\n ...existing,\n passwordHash,\n updatedAt: Date.now(),\n }\n\n await this.structured.update(USERS_COLLECTION, id, updatedData)\n }\n\n async ensureAdminExists(): Promise<void> {\n const adminUsername = this.config.get<string>('auth.adminUsername')\n const adminPassword = this.config.get<string>('auth.adminPassword')\n\n if (!adminUsername || !adminPassword) {\n return\n }\n\n const existing = await this.findByUsername(adminUsername)\n if (existing) {\n return\n }\n\n await this.create({\n username: adminUsername,\n password: adminPassword,\n role: 'super_admin',\n allowedProviders: '*',\n allowedDevices: {},\n })\n }\n}\n","import * as crypto from 'node:crypto'\nimport type { ScopedToken, TokenScope } from '@camstack/types'\nimport type { IStructuredStorage, StorageRecord } from '../storage/storage-manager.js'\n\nconst TOKENS_COLLECTION = 'scoped_tokens'\nconst TOKEN_PREFIX = 'cst_'\n\n/**\n * Manages scoped API tokens with restricted addon/route/capability access.\n * Framework-agnostic — dependencies injected via constructor.\n */\nexport class ScopedTokenManager {\n constructor(private readonly storage: IStructuredStorage) {}\n\n /**\n * Create a new scoped token. Returns the raw token string (shown once)\n * and the stored record (with hash, not the raw token).\n */\n async create(\n userId: string,\n name: string,\n scopes: TokenScope[],\n expiresAt?: number,\n ): Promise<{ token: string; record: ScopedToken }> {\n const rawHex = crypto.randomBytes(32).toString('hex')\n const rawToken = `${TOKEN_PREFIX}${rawHex}`\n const tokenHash = crypto.createHash('sha256').update(rawToken).digest('hex')\n const tokenPrefix = rawToken.slice(0, 12)\n\n const record: ScopedToken = {\n id: crypto.randomUUID(),\n userId,\n name,\n tokenHash,\n tokenPrefix,\n scopes: scopes.map((s) => ({ ...s })),\n expiresAt,\n lastUsedAt: undefined,\n createdAt: Date.now(),\n }\n\n await this.storage.insert({\n collection: TOKENS_COLLECTION,\n id: record.id,\n data: record as unknown as Record<string, unknown>,\n })\n\n return { token: rawToken, record }\n }\n\n /**\n * Validate a raw token string. Returns the token record if valid, null otherwise.\n */\n async validate(rawToken: string): Promise<ScopedToken | null> {\n if (!rawToken.startsWith(TOKEN_PREFIX)) {\n return null\n }\n\n const tokenHash = crypto.createHash('sha256').update(rawToken).digest('hex')\n const results = await this.storage.query(TOKENS_COLLECTION, {\n where: { tokenHash },\n })\n\n if (results.length === 0) {\n return null\n }\n\n const record = results[0]!.data as unknown as ScopedToken\n if (record.expiresAt !== undefined && record.expiresAt !== null && Date.now() > record.expiresAt) {\n return null\n }\n\n // Update last used timestamp (fire and forget)\n this.updateLastUsed(record.id).catch(() => {})\n\n return record\n }\n\n /**\n * Check whether a token's scopes grant access to the given addon, route, or capability.\n */\n matchesScope(\n token: ScopedToken,\n addonId?: string,\n routePath?: string,\n capability?: string,\n ): boolean {\n for (const scope of token.scopes) {\n switch (scope.type) {\n case 'addon':\n if (addonId && scope.target === addonId) return true\n break\n case 'route-prefix':\n if (routePath && routePath.startsWith(scope.target)) return true\n break\n case 'capability':\n if (capability && scope.target === capability) return true\n break\n }\n }\n return false\n }\n\n /**\n * Revoke a token by ID.\n */\n async revoke(tokenId: string): Promise<void> {\n await this.storage.delete(TOKENS_COLLECTION, tokenId)\n }\n\n /**\n * List all tokens for a user (without exposing the raw token).\n */\n async listForUser(userId: string): Promise<ScopedToken[]> {\n const results = await this.storage.query(TOKENS_COLLECTION, {\n where: { userId },\n })\n return results.map((r: StorageRecord) => r.data as unknown as ScopedToken)\n }\n\n /**\n * Update the lastUsedAt timestamp for a token.\n */\n async updateLastUsed(tokenId: string): Promise<void> {\n const results = await this.storage.query(TOKENS_COLLECTION, {\n where: { id: tokenId },\n })\n if (results.length === 0) return\n\n const existing = results[0]!.data as unknown as ScopedToken\n await this.storage.update(TOKENS_COLLECTION, tokenId, {\n ...existing,\n lastUsedAt: Date.now(),\n } as unknown as Record<string, unknown>)\n }\n}\n","import type { INotificationOutput, Notification } from '@camstack/types'\nimport type { IScopedLogger } from '../logging/scoped-logger.js'\nimport type { CapabilityRegistry } from '../capability/capability-registry.js'\n\n/**\n * Central notification service that routes notifications to configured outputs.\n * Framework-agnostic — dependencies injected via constructor.\n *\n * Outputs are resolved from the CapabilityRegistry's 'notification-output'\n * collection on each call (proxy pattern). Falls back to a local map\n * when no registry is provided (backward compat).\n */\nexport class NotificationService {\n private readonly localOutputs = new Map<string, INotificationOutput>()\n private readonly routing = new Map<string, string[]>()\n private readonly rateLimits = new Map<string, number>()\n private readonly lastSent = new Map<string, number>()\n private registry: CapabilityRegistry | null = null\n\n constructor(private readonly logger: IScopedLogger) {}\n\n /** Set the registry for live output lookup. Called once during boot. */\n setRegistry(registry: CapabilityRegistry): void {\n this.registry = registry\n }\n\n /** Resolve all outputs — prefers registry, falls back to local map */\n private get outputs(): ReadonlyMap<string, INotificationOutput> {\n if (this.registry) {\n const collection = this.registry.getCollection<INotificationOutput>('notification-output')\n const map = new Map<string, INotificationOutput>()\n for (const output of collection) {\n map.set(output.id, output)\n }\n return map\n }\n return this.localOutputs\n }\n\n /** @deprecated Use registry-based resolution. Kept for backward compat only. */\n addOutput(output: INotificationOutput): void {\n this.localOutputs.set(output.id, output)\n this.logger.info(`Notification output added: ${output.name} (${output.id})`)\n }\n\n /** @deprecated Use registry-based resolution. Kept for backward compat only. */\n removeOutput(id: string): void {\n this.localOutputs.delete(id)\n this.logger.info(`Notification output removed: ${id}`)\n }\n\n setRouting(category: string, outputIds: string[]): void {\n this.routing.set(category, [...outputIds])\n }\n\n setRateLimit(category: string, minIntervalMs: number): void {\n this.rateLimits.set(category, minIntervalMs)\n }\n\n async notify(notification: Notification): Promise<void> {\n const { category, deviceId } = notification\n\n // Rate limit check\n const rateLimitKey = deviceId ? `${category}:${deviceId}` : category\n const minInterval = this.rateLimits.get(category) ?? 0\n if (minInterval > 0) {\n const last = this.lastSent.get(rateLimitKey) ?? 0\n if (notification.timestamp - last < minInterval) {\n this.logger.debug(`Rate limited: ${rateLimitKey}`)\n return\n }\n }\n\n // Determine target outputs\n const targetIds = this.routing.get(category) ?? this.routing.get('*') ?? []\n if (targetIds.length === 0) {\n this.logger.debug(`No routing configured for category \"${category}\"`)\n return\n }\n\n const currentOutputs = this.outputs\n\n // Update last-sent timestamp\n this.lastSent.set(rateLimitKey, notification.timestamp)\n\n // Send to all targets in parallel with error isolation\n await Promise.allSettled(\n targetIds\n .map((id) => currentOutputs.get(id))\n .filter((output): output is INotificationOutput => output !== undefined)\n .map(async (output) => {\n try {\n await output.send(notification)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n this.logger.error(`Notification output \"${output.id}\" failed: ${msg}`)\n }\n }),\n )\n\n return undefined\n }\n\n getOutputs(): ReadonlyArray<{ id: string; name: string; icon: string }> {\n return Array.from(this.outputs.values()).map(({ id, name, icon }) => ({ id, name, icon }))\n }\n\n getRouting(): ReadonlyMap<string, string[]> {\n return this.routing\n }\n\n getOutput(id: string): INotificationOutput | undefined {\n return this.outputs.get(id)\n }\n}\n","import type { Toast } from '@camstack/types'\n\nexport type Unsubscribe = () => void\n\ninterface ListenerEntry {\n readonly userId: string\n readonly callback: (toast: Toast) => void\n}\n\n/**\n * Service for broadcasting toast notifications to connected UI clients.\n * Framework-agnostic — integrates with tRPC subscriptions via subscribe().\n */\nexport class ToastService {\n private readonly listeners = new Map<string, ListenerEntry>()\n\n /**\n * Subscribe to toast events for a specific user.\n * Returns an unsubscribe function.\n */\n subscribe(connectionId: string, userId: string, callback: (toast: Toast) => void): Unsubscribe {\n this.listeners.set(connectionId, { userId, callback })\n return () => {\n this.listeners.delete(connectionId)\n }\n }\n\n /**\n * Broadcast a toast to all connected clients.\n */\n broadcast(toast: Toast): void {\n for (const entry of this.listeners.values()) {\n entry.callback(toast)\n }\n }\n\n /**\n * Send a toast to a specific user's connections only.\n */\n sendToUser(userId: string, toast: Toast): void {\n for (const entry of this.listeners.values()) {\n if (entry.userId === userId) {\n entry.callback(toast)\n }\n }\n }\n}\n","import type { IAddonHttpRoute, IAddonRouteProvider } from '@camstack/types'\n\ninterface RouteMatch {\n readonly route: IAddonHttpRoute\n readonly addonId: string\n readonly params: Record<string, string>\n}\n\ninterface RegisteredRoute {\n readonly addonId: string\n readonly route: IAddonHttpRoute\n /** Full path including /addon/{addonId}/ prefix */\n readonly fullPath: string\n}\n\n/**\n * Registry for dynamic HTTP routes registered by addons.\n * Framework-agnostic — the server HTTP layer queries this registry to dispatch requests.\n */\nexport class AddonRouteRegistry {\n private readonly routes = new Map<string, RegisteredRoute[]>()\n\n /**\n * Register all routes from an addon's route provider.\n */\n registerRoutes(addonId: string, provider: IAddonRouteProvider): void {\n const addonRoutes = provider.getRoutes()\n const registered: RegisteredRoute[] = addonRoutes.map((route) => {\n const normalizedPath = route.path.startsWith('/') ? route.path : `/${route.path}`\n return {\n addonId,\n route,\n fullPath: `/addon/${addonId}${normalizedPath}`,\n }\n })\n this.routes.set(addonId, registered)\n }\n\n /**\n * Unregister all routes for an addon.\n */\n unregisterRoutes(addonId: string): void {\n this.routes.delete(addonId)\n }\n\n /**\n * Match an incoming request method + path to a registered route.\n * Supports simple path parameters (e.g., /items/:id).\n */\n matchRoute(method: string, path: string): RouteMatch | null {\n const normalizedMethod = method.toUpperCase()\n\n for (const registeredRoutes of this.routes.values()) {\n for (const entry of registeredRoutes) {\n if (entry.route.method !== normalizedMethod) continue\n\n const params = matchPath(entry.fullPath, path)\n if (params !== null) {\n return {\n route: entry.route,\n addonId: entry.addonId,\n params,\n }\n }\n }\n }\n\n return null\n }\n\n /**\n * List all registered routes across all addons.\n */\n listRoutes(): ReadonlyArray<{ addonId: string; method: string; path: string; access: string; description?: string }> {\n const result: Array<{ addonId: string; method: string; path: string; access: string; description?: string }> = []\n for (const registeredRoutes of this.routes.values()) {\n for (const entry of registeredRoutes) {\n result.push({\n addonId: entry.addonId,\n method: entry.route.method,\n path: entry.fullPath,\n access: entry.route.access,\n description: entry.route.description,\n })\n }\n }\n return result\n }\n}\n\n/**\n * Match a route pattern against an actual path.\n * Supports :param segments (e.g., /addon/:addonId/items/:id).\n * Returns extracted params or null if no match.\n */\nfunction matchPath(pattern: string, path: string): Record<string, string> | null {\n const patternParts = pattern.split('/').filter(Boolean)\n const pathParts = path.split('/').filter(Boolean)\n\n if (patternParts.length !== pathParts.length) {\n return null\n }\n\n const params: Record<string, string> = {}\n\n for (let i = 0; i < patternParts.length; i++) {\n const patternPart = patternParts[i]!\n const pathPart = pathParts[i]!\n\n if (patternPart.startsWith(':')) {\n params[patternPart.slice(1)] = pathPart\n } else if (patternPart !== pathPart) {\n return null\n }\n }\n\n return params\n}\n","import { randomUUID } from 'node:crypto'\nimport type { IScopedLogger, IEventBus, LoggerFactory } from '@camstack/types'\n\nexport interface IRegisteredDevice {\n readonly id: string\n readonly name: string\n readonly providerId: string\n readonly capabilities: readonly string[]\n}\n\nexport class DeviceRegistry<T extends IRegisteredDevice = IRegisteredDevice> {\n private readonly devices = new Map<string, T>()\n private readonly logger: IScopedLogger\n\n constructor(\n private readonly eventBus: IEventBus,\n loggingService: LoggerFactory,\n ) {\n this.logger = loggingService.createLogger('device-registry')\n }\n\n registerDevice(device: T): void {\n this.devices.set(device.id, device)\n this.logger.info(`Device registered: ${device.id} (${device.name})`)\n\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'core', id: 'device-registry' },\n category: 'device.registered',\n data: { deviceId: device.id, name: device.name, providerId: device.providerId },\n })\n }\n\n unregisterDevice(id: string): void {\n const device = this.devices.get(id)\n if (!device) {\n return\n }\n\n this.devices.delete(id)\n this.logger.info(`Device unregistered: ${id}`)\n\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'core', id: 'device-registry' },\n category: 'device.unregistered',\n data: { deviceId: id },\n })\n }\n\n getDevice(id: string): T | null {\n return this.devices.get(id) ?? null\n }\n\n listDevices(): T[] {\n return Array.from(this.devices.values())\n }\n\n getDevicesByProvider(providerId: string): T[] {\n return Array.from(this.devices.values()).filter(\n (device) => device.providerId === providerId,\n )\n }\n\n getDevicesWithCapability(cap: string): T[] {\n return Array.from(this.devices.values()).filter((device) =>\n device.capabilities.includes(cap),\n )\n }\n\n registerProviderDevices(providerId: string, devices: readonly T[]): void {\n for (const device of devices) {\n this.registerDevice(device)\n }\n this.logger.info(`Bulk registered ${devices.length} devices for provider ${providerId}`)\n }\n\n unregisterProviderDevices(providerId: string): void {\n const providerDevices = this.getDevicesByProvider(providerId)\n for (const device of providerDevices) {\n this.unregisterDevice(device.id)\n }\n this.logger.info(`Bulk unregistered ${providerDevices.length} devices for provider ${providerId}`)\n }\n}\n","export interface IDeviceCapability {\n kind: string\n}\n\nexport interface CapabilityBinding {\n source: 'native' | 'addon' | 'disabled'\n addonId?: string\n config?: Record<string, unknown>\n}\n\nexport interface IResolvableDevice {\n readonly id: string\n readonly capabilities: string[]\n getCapability<T extends IDeviceCapability>(cap: string): T | null\n}\n\nexport interface IAddonRegistryAccess {\n getAddon(addonId: string): unknown | null\n}\n\nexport class CapabilityResolver {\n private readonly bindings = new Map<\n string,\n Partial<Record<string, CapabilityBinding>>\n >()\n\n constructor(\n private readonly addonRegistry: IAddonRegistryAccess,\n ) {}\n\n resolve<T extends IDeviceCapability>(\n device: IResolvableDevice,\n cap: string,\n ): T | null {\n const deviceBindings = this.bindings.get(device.id)\n const binding = deviceBindings?.[cap]\n\n if (binding) {\n if (binding.source === 'disabled') {\n return null\n }\n\n if (binding.source === 'addon' && binding.addonId) {\n const addon = this.addonRegistry.getAddon(binding.addonId)\n if (addon && typeof (addon as any).getCapabilityForDevice === 'function') {\n return (\n (addon as any).getCapabilityForDevice(device, cap, binding.config) ?? null\n )\n }\n return null\n }\n }\n\n // native or no explicit binding -- delegate to device\n return device.getCapability<T>(cap)\n }\n\n setBinding(\n deviceId: string,\n cap: string,\n binding: CapabilityBinding,\n ): void {\n const existing = this.bindings.get(deviceId) ?? {}\n this.bindings.set(deviceId, { ...existing, [cap]: binding })\n }\n\n removeBinding(deviceId: string, cap: string): void {\n const existing = this.bindings.get(deviceId)\n if (!existing) {\n return\n }\n const updated = { ...existing }\n delete updated[cap]\n this.bindings.set(deviceId, updated)\n }\n\n getBindings(\n deviceId: string,\n ): Partial<Record<string, CapabilityBinding>> {\n return this.bindings.get(deviceId) ?? {}\n }\n\n getEffectiveCapabilities(device: IResolvableDevice): string[] {\n const deviceBindings = this.bindings.get(device.id) ?? {}\n\n const result: string[] = []\n\n // Include native capabilities that are not disabled\n for (const cap of device.capabilities) {\n const binding = deviceBindings[cap]\n if (!binding || binding.source !== 'disabled') {\n result.push(cap)\n }\n }\n\n // Include addon-bound capabilities not already in the native list\n for (const [cap, binding] of Object.entries(deviceBindings)) {\n if (\n binding && binding.source === 'addon' &&\n !device.capabilities.includes(cap)\n ) {\n result.push(cap)\n }\n }\n\n return result\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport type { IScopedLogger, IEventBus, LoggerFactory } from '@camstack/types'\nimport { LifecycleStateMachine, type ElementStatus } from '../lifecycle/lifecycle-state-machine.js'\n\n/** Minimal device registry interface for provider management */\nexport type ProviderDeviceRegistry = {\n registerProviderDevices(providerId: string, devices: readonly IRegistrableDevice[]): void\n unregisterProviderDevices(providerId: string): void\n}\n\nexport interface IRegistrableDevice {\n readonly id: string\n readonly name: string\n readonly providerId: string\n readonly capabilities: readonly string[]\n}\n\nexport interface ProviderStatus {\n connected: boolean\n error?: string\n deviceCount: number\n}\n\nexport interface LiveEvent {\n type: string\n camera: string\n timestamp: number\n data: Record<string, unknown>\n}\n\nexport interface IManagedProvider {\n readonly id: string\n readonly type: string\n readonly name: string\n start(): Promise<void>\n stop(): Promise<void>\n getStatus(): ProviderStatus\n getDevices(): readonly IRegistrableDevice[]\n subscribeLiveEvents(callback: (event: LiveEvent) => void): () => void\n}\n\ninterface ProviderEntry<P extends IManagedProvider> {\n readonly provider: P\n readonly lifecycle: LifecycleStateMachine\n started: boolean\n unsubscribe?: () => void\n}\n\nexport interface ProviderListItem {\n id: string\n type: string\n name: string\n status: ProviderStatus\n started: boolean\n lifecycle: ElementStatus\n}\n\nexport class ProviderManager<P extends IManagedProvider = IManagedProvider> {\n private readonly providers = new Map<string, ProviderEntry<P>>()\n private readonly logger: IScopedLogger\n\n constructor(\n private readonly deviceRegistry: ProviderDeviceRegistry,\n private readonly eventBus: IEventBus,\n private readonly loggingService: LoggerFactory,\n ) {\n this.logger = loggingService.createLogger('provider-manager')\n }\n\n registerProvider(provider: P): void {\n const providerLogger = this.loggingService.createLogger(`provider:${provider.id}`)\n const lifecycle = new LifecycleStateMachine(\n provider.id,\n 'provider',\n this.eventBus,\n providerLogger,\n )\n this.providers.set(provider.id, { provider, lifecycle, started: false })\n this.logger.info(`Provider registered: ${provider.id} (${provider.name})`)\n }\n\n async startProvider(id: string): Promise<void> {\n const entry = this.providers.get(id)\n if (!entry) {\n throw new Error(`Provider \"${id}\" is not registered`)\n }\n\n if (entry.lifecycle.state === 'disabled') {\n throw new Error(`Provider \"${id}\" is disabled`)\n }\n\n entry.lifecycle.transition('starting')\n\n try {\n await entry.provider.start()\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err)\n entry.lifecycle.transition('error', message)\n throw err\n }\n\n entry.lifecycle.transition('running')\n\n const devices = entry.provider.getDevices()\n this.deviceRegistry.registerProviderDevices(id, devices)\n\n const unsubscribe = entry.provider.subscribeLiveEvents((event: LiveEvent) => {\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'provider', id },\n category: `provider.${event.type}`,\n data: event.data,\n })\n })\n\n entry.started = true\n entry.unsubscribe = unsubscribe\n\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'core', id: 'provider-manager' },\n category: 'provider.started',\n data: { providerId: id },\n })\n\n this.logger.info(`Provider started: ${id}`)\n }\n\n async stopProvider(id: string): Promise<void> {\n const entry = this.providers.get(id)\n if (!entry) {\n throw new Error(`Provider \"${id}\" is not registered`)\n }\n\n entry.lifecycle.transition('stopping')\n\n if (entry.unsubscribe) {\n entry.unsubscribe()\n entry.unsubscribe = undefined\n }\n\n this.deviceRegistry.unregisterProviderDevices(id)\n\n await entry.provider.stop()\n entry.started = false\n\n entry.lifecycle.transition('stopped')\n\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'core', id: 'provider-manager' },\n category: 'provider.stopped',\n data: { providerId: id },\n })\n\n this.logger.info(`Provider stopped: ${id}`)\n }\n\n async disableProvider(id: string): Promise<void> {\n const entry = this.providers.get(id)\n if (!entry) {\n throw new Error(`Provider \"${id}\" is not registered`)\n }\n\n if (entry.lifecycle.state === 'running' || entry.started) {\n await this.stopProvider(id)\n }\n\n entry.lifecycle.transition('disabled')\n this.logger.info(`Provider disabled: ${id}`)\n }\n\n async enableProvider(id: string): Promise<void> {\n const entry = this.providers.get(id)\n if (!entry) {\n throw new Error(`Provider \"${id}\" is not registered`)\n }\n\n if (entry.lifecycle.state !== 'disabled') {\n throw new Error(`Provider \"${id}\" is not disabled`)\n }\n\n entry.lifecycle.transition('stopped')\n this.logger.info(`Provider enabled: ${id}`)\n }\n\n async restartProvider(id: string): Promise<void> {\n await this.stopProvider(id)\n await this.startProvider(id)\n }\n\n getProvider(id: string): P | null {\n const entry = this.providers.get(id)\n return entry?.provider ?? null\n }\n\n getProviderStatus(id: string): ElementStatus | null {\n const entry = this.providers.get(id)\n return entry?.lifecycle.getStatus() ?? null\n }\n\n listProviders(): readonly ProviderListItem[] {\n return Array.from(this.providers.values()).map((entry) => ({\n id: entry.provider.id,\n type: entry.provider.type,\n name: entry.provider.name,\n status: entry.provider.getStatus(),\n started: entry.started,\n lifecycle: entry.lifecycle.getStatus(),\n }))\n }\n\n async shutdownAll(): Promise<void> {\n const startedIds = Array.from(this.providers.entries())\n .filter(([, entry]) => entry.started)\n .map(([id]) => id)\n\n for (const id of startedIds) {\n await this.stopProvider(id)\n }\n\n this.logger.info(`All providers shut down (${startedIds.length} stopped)`)\n }\n}\n"],"mappings":"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","names":["hash","fs","path","fs","path","createHash","execFile","promisify","fs","path","execFileAsync","promises","spawn","fs","path","randomUUID","path","randomUUID","path","path","path","randomUUID","fs","path","StorageLocationManager","path","Database","Database","hash","crypto","hash","crypto","crypto","path","randomUUID","randomUUID","randomUUID"]}
|