@camstack/kernel 0.1.2 → 0.1.4

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/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/addon-loader.ts","../src/addon-engine-manager.ts","../src/addon-installer.ts","../src/fs-utils.ts","../src/workspace-detect.ts","../src/capability-registry.ts","../src/infra-capabilities.ts","../src/config-manager.ts","../src/config-schema.ts","../src/worker/addon-worker-host.ts","../src/worker/worker-process-manager.ts"],"sourcesContent":["// Addon loading\nexport type { RegisteredAddon } from './addon-loader.js'\nexport { AddonLoader } from './addon-loader.js'\nexport { AddonEngineManager } from './addon-engine-manager.js'\nexport type { AddonInstallerConfig, InstalledPackage } from './addon-installer.js'\nexport { AddonInstaller } from './addon-installer.js'\n\n// Shared filesystem utilities\nexport {\n copyDirRecursive,\n ensureDir,\n stripCamstackDeps,\n copyExtraFileDirs,\n symlinkAddonsToNodeModules,\n ensureLibraryBuilt,\n installPackageFromNpmSync,\n isSourceNewer,\n} from './fs-utils.js'\n\n// Workspace detection\nexport { detectWorkspacePackagesDir } from './workspace-detect.js'\n\n// Capability\nexport { CapabilityRegistry } from './capability-registry.js'\nexport { INFRA_CAPABILITIES, isInfraCapability } from './infra-capabilities.js'\nexport type { InfraCapability } from './infra-capabilities.js'\n\n// Config\nexport { ConfigManager } from './config-manager.js'\nexport type { ISettingsStore } from './config-manager.js'\nexport { bootstrapSchema, RUNTIME_DEFAULTS, DEFAULT_DATA_PATH } from './config-schema.js'\nexport type { BootstrapConfig, AppConfig, ServerMode } from './config-schema.js'\n\n// Worker (process isolation)\nexport { AddonWorkerHost, type WorkerHostOptions } from './worker/addon-worker-host.js'\nexport { WorkerProcessManager } from './worker/worker-process-manager.js'\n","import type { AddonDeclaration, AddonPackageManifest, ICamstackAddon } from '@camstack/types'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\nexport interface RegisteredAddon {\n readonly declaration: AddonDeclaration\n readonly packageName: string\n readonly packageVersion: string\n /** Human-readable package name from camstack.displayName in package.json */\n readonly packageDisplayName?: 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 /** Scan addons directory and load all addon packages.\n * Supports scoped layout: addons/@scope/package-name/ */\n async loadFromDirectory(addonsDir: string): Promise<void> {\n if (!fs.existsSync(addonsDir)) return\n\n const entries = fs.readdirSync(addonsDir, { withFileTypes: true })\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n\n // Scoped directory (e.g., @camstack/) — scan one level deeper\n if (entry.name.startsWith('@')) {\n const scopeDir = path.join(addonsDir, entry.name)\n const scopeEntries = fs.readdirSync(scopeDir, { withFileTypes: true })\n for (const scopeEntry of scopeEntries) {\n if (!scopeEntry.isDirectory()) continue\n await this.tryLoadAddon(path.join(scopeDir, scopeEntry.name))\n }\n continue\n }\n\n // Non-scoped (legacy or flat layout)\n await this.tryLoadAddon(path.join(addonsDir, entry.name))\n }\n }\n\n private async tryLoadAddon(addonDir: string): Promise<void> {\n const pkgJsonPath = path.join(addonDir, 'package.json')\n if (!fs.existsSync(pkgJsonPath)) return\n try {\n await this.loadFromAddonDir(addonDir)\n } catch (err) {\n console.warn(`Failed to load addon from ${addonDir}: ${err}`)\n }\n }\n\n /** Load addon from a specific directory (package.json + dist/) */\n async loadFromAddonDir(addonDir: string): Promise<void> {\n const pkgJsonPath = path.join(addonDir, 'package.json')\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as Record<string, unknown>\n const packageName = pkgJson['name'] as string\n const packageVersion = (pkgJson['version'] as string) ?? '0.0.0'\n const manifest = pkgJson['camstack'] as AddonPackageManifest | undefined\n\n if (!manifest?.addons?.length) return\n\n for (const declaration of manifest.addons) {\n // Resolve entry: try dist/ first\n const entryFile = declaration.entry\n .replace(/^\\.\\//, '')\n .replace(/^src\\//, 'dist/')\n .replace(/\\.ts$/, '.js')\n\n let entryPath = path.resolve(addonDir, entryFile)\n\n // Fallback: try other locations and extensions\n if (!fs.existsSync(entryPath)) {\n const base = entryPath.replace(/\\.(js|cjs|mjs)$/, '')\n const alternatives = [\n `${base}.cjs`,\n `${base}.mjs`,\n path.resolve(addonDir, 'dist', 'index.js'),\n path.resolve(addonDir, 'dist', 'index.cjs'),\n path.resolve(addonDir, 'dist', 'index.mjs'),\n path.resolve(addonDir, declaration.entry),\n ]\n entryPath = alternatives.find(p => fs.existsSync(p)) ?? entryPath\n }\n\n if (!fs.existsSync(entryPath)) {\n throw new Error(`Entry not found: ${entryPath}`)\n }\n\n const mod = (await import(entryPath)) as Record<string, unknown>\n const AddonClass = resolveAddonClass(mod)\n\n if (!AddonClass) {\n throw new Error(`No addon class in ${entryPath}`)\n }\n\n this.addons.set(declaration.id, {\n declaration,\n packageName,\n packageVersion,\n packageDisplayName: manifest.displayName,\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 packageVersion = '0.0.0',\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 packageVersion,\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","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'\nimport * as os from 'node:os'\nimport { copyDirRecursive, copyExtraFileDirs, ensureDir, ensureLibraryBuilt, isSourceNewer, stripCamstackDeps } from './fs-utils.js'\n\nconst execFileAsync = promisify(execFile)\n\nexport interface AddonInstallerConfig {\n /** Directory where addons are stored (e.g., {dataDir}/addons) — each addon is a subdirectory */\n readonly addonsDir: string\n /** npm registry URL (default: https://registry.npmjs.org) */\n readonly registry?: string\n /** Workspace packages directory (e.g., /path/to/camstack-server/packages) — for dev installs */\n readonly workspacePackagesDir?: string\n}\n\nimport type { InstalledPackage } from '@camstack/types'\nexport type { InstalledPackage }\n\nexport class AddonInstaller {\n private readonly addonsDir: string\n private readonly registry: string | undefined\n private readonly workspacePackagesDir: string | undefined\n\n constructor(config: AddonInstallerConfig) {\n this.addonsDir = config.addonsDir\n this.registry = config.registry\n this.workspacePackagesDir = config.workspacePackagesDir\n }\n\n /** Required addon packages that must be installed for the server to function */\n static readonly REQUIRED_PACKAGES = [\n '@camstack/core',\n '@camstack/addon-pipeline',\n '@camstack/addon-vision',\n '@camstack/addon-admin-ui',\n '@camstack/addon-webrtc-adaptive',\n '@camstack/addon-pipeline-analysis',\n '@camstack/addon-scene-intelligence',\n '@camstack/addon-advanced-notifier',\n ] as const\n\n /** Ensure the addons directory exists */\n async initialize(): Promise<void> {\n ensureDir(this.addonsDir)\n }\n\n /**\n * Ensure all required packages are installed in the addons directory.\n * This replaces the standalone first-boot-installer.ts.\n */\n async ensureRequiredPackages(): Promise<void> {\n ensureDir(this.addonsDir)\n\n // In workspace mode, ensure library dependencies are built first\n if (this.workspacePackagesDir) {\n console.log(`[AddonInstaller] Workspace detected: ${this.workspacePackagesDir}`)\n ensureLibraryBuilt('@camstack/kernel', this.workspacePackagesDir)\n ensureLibraryBuilt('@camstack/types', this.workspacePackagesDir)\n }\n\n for (const packageName of AddonInstaller.REQUIRED_PACKAGES) {\n const addonDir = path.join(this.addonsDir, packageName)\n const pkgJsonPath = path.join(addonDir, 'package.json')\n\n // CAMSTACK_INSTALL_SOURCE=npm forces npm install even in workspace mode\n const forceNpm = process.env['CAMSTACK_INSTALL_SOURCE'] === 'npm'\n const useWorkspace = this.workspacePackagesDir && !forceNpm\n\n // Skip if already installed and up-to-date\n if (fs.existsSync(pkgJsonPath)) {\n if (!useWorkspace) {\n // Production or npm-forced: installed = done\n continue\n }\n // Workspace: skip if source dist/ isn't newer than target\n const srcPkgDir = this.findWorkspacePackage(packageName)\n if (srcPkgDir && !isSourceNewer(srcPkgDir)) {\n continue\n }\n }\n\n try {\n // Try workspace install first (dev mode), fall back to npm\n if (useWorkspace) {\n const pkgDir = this.findWorkspacePackage(packageName)\n if (pkgDir) {\n await this.installFromWorkspace(packageName)\n continue\n }\n }\n\n await this.installFromNpm(packageName)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n // Core is essential — abort if it fails\n if (packageName === '@camstack/core') {\n throw new Error(`Required package ${packageName} failed to install: ${msg}`)\n }\n console.error(`[AddonInstaller] Failed to install ${packageName}: ${msg}`)\n }\n }\n }\n\n private findWorkspacePackage(packageName: string): string | null {\n if (!this.workspacePackagesDir) return null\n const shortName = packageName.replace('@camstack/', '')\n // Check common naming patterns\n for (const dirName of [shortName, `addon-${shortName}`, shortName.replace('addon-', '')]) {\n const candidate = path.join(this.workspacePackagesDir, dirName)\n if (fs.existsSync(path.join(candidate, 'package.json'))) {\n try {\n const pkg = JSON.parse(fs.readFileSync(path.join(candidate, 'package.json'), 'utf-8'))\n if (pkg.name === packageName) return candidate\n } catch { /* ignore */ }\n }\n }\n return null\n }\n\n /** Install addon from a tgz file (uploaded or downloaded) */\n async installFromTgz(tgzPath: string): Promise<{ name: string; version: string }> {\n // 1. Extract tgz to temp dir\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'camstack-addon-install-'))\n\n try {\n await execFileAsync('tar', ['-xzf', tgzPath, '-C', tmpDir], { timeout: 30_000 })\n\n // npm pack creates a package/ directory inside the tgz\n const extractedDir = path.join(tmpDir, 'package')\n const pkgJsonPath = fs.existsSync(path.join(extractedDir, 'package.json'))\n ? path.join(extractedDir, 'package.json')\n : path.join(tmpDir, 'package.json')\n\n if (!fs.existsSync(pkgJsonPath)) {\n throw new Error('No package.json found in tgz archive')\n }\n\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as {\n name: string\n version: string\n files?: string[]\n camstack?: { addons?: unknown[] }\n }\n\n if (!pkgJson.camstack?.addons) {\n throw new Error(`Package ${pkgJson.name} has no camstack.addons manifest`)\n }\n\n // 2. Determine target directory name (strip scope)\n const targetDir = path.join(this.addonsDir, pkgJson.name)\n\n // 3. Remove old version if present\n fs.rmSync(targetDir, { recursive: true, force: true })\n ensureDir(targetDir)\n\n // 4. Copy package.json\n const sourceDir = path.dirname(pkgJsonPath)\n fs.copyFileSync(pkgJsonPath, path.join(targetDir, 'package.json'))\n\n // 5. Copy dist/ directory\n const sourceDist = path.join(sourceDir, 'dist')\n if (fs.existsSync(sourceDist)) {\n copyDirRecursive(sourceDist, path.join(targetDir, 'dist'))\n }\n\n // 6. Copy extra directories from \"files\" in package.json (e.g., python/, reference-images/)\n copyExtraFileDirs(pkgJson, sourceDir, targetDir)\n\n return { name: pkgJson.name, version: pkgJson.version }\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true })\n }\n }\n\n /**\n * Install addon — prefers workspace if available, falls back to npm.\n * This ensures dev builds (with vite output, etc.) are used when in workspace mode.\n */\n async install(packageName: string, version?: string): Promise<{ name: string; version: string }> {\n if (this.workspacePackagesDir) {\n // Workspace dirs use short names (e.g., packages/addon-benchmark)\n const workspaceDirName = packageName.replace('@camstack/', '')\n const sourceDir = path.join(this.workspacePackagesDir, workspaceDirName)\n if (fs.existsSync(path.join(sourceDir, 'package.json'))) {\n return this.installFromWorkspace(packageName)\n }\n }\n return this.installFromNpm(packageName, version)\n }\n\n /**\n * Install addon from workspace by copying package.json + dist/ to addons dir.\n * Does NOT build — expects dist/ to already exist (run `npm run build` separately).\n */\n async installFromWorkspace(packageName: string): Promise<{ name: string; version: string }> {\n if (!this.workspacePackagesDir) {\n throw new Error('Workspace packages directory not configured')\n }\n\n const workspaceDirName = packageName.replace('@camstack/', '')\n const sourceDir = path.join(this.workspacePackagesDir, workspaceDirName)\n const sourcePkgJson = path.join(sourceDir, 'package.json')\n\n if (!fs.existsSync(sourcePkgJson)) {\n throw new Error(`Workspace package not found: ${sourceDir}`)\n }\n\n const distDir = path.join(sourceDir, 'dist')\n if (!fs.existsSync(distDir)) {\n throw new Error(`${packageName} has no dist/ directory — run 'npm run build' first`)\n }\n\n // Copy package.json + dist/ to target (use full scoped name as dir)\n const targetDir = path.join(this.addonsDir, packageName)\n\n fs.rmSync(targetDir, { recursive: true, force: true })\n ensureDir(targetDir)\n\n // Strip @camstack/* deps from package.json\n const pkgData = JSON.parse(fs.readFileSync(sourcePkgJson, 'utf-8'))\n const strippedPkg = stripCamstackDeps(pkgData)\n fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(strippedPkg, null, 2))\n\n copyDirRecursive(distDir, path.join(targetDir, 'dist'))\n\n // Copy extra directories from \"files\" in package.json (e.g., python/, reference-images/)\n copyExtraFileDirs(pkgData, sourceDir, targetDir)\n\n // Install production dependencies (non-fatal if fails)\n try {\n await execFileAsync('npm', ['install', '--omit=dev', '--ignore-scripts=false'], {\n cwd: targetDir,\n timeout: 120_000,\n })\n } catch {\n // Non-fatal — addon may not have external deps\n }\n\n return { name: pkgData.name, version: pkgData.version }\n }\n\n /** Install addon from npm (download tgz, then extract) */\n async installFromNpm(packageName: string, version?: string): Promise<{ name: string; version: string }> {\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'camstack-addon-npm-'))\n\n const packageSpec = version ? `${packageName}@${version}` : packageName\n const args = ['pack', packageSpec, '--pack-destination', tmpDir]\n if (this.registry) {\n args.push('--registry', this.registry)\n }\n\n try {\n const { stdout } = await execFileAsync('npm', args, {\n timeout: 120_000,\n })\n\n const tgzFiles = fs.readdirSync(tmpDir).filter(f => f.endsWith('.tgz'))\n if (tgzFiles.length === 0) {\n throw new Error(`npm pack produced no tgz file for ${packageSpec}. stdout: ${stdout.trim()}`)\n }\n\n const tgzPath = path.join(tmpDir, tgzFiles[0]!)\n const result = await this.installFromTgz(tgzPath)\n return result\n } finally {\n // Clean up AFTER installFromTgz has fully completed\n fs.rmSync(tmpDir, { recursive: true, force: true })\n }\n }\n\n /** Uninstall addon (delete directory) */\n async uninstall(packageName: string): Promise<void> {\n // Support both full scoped name and short name\n const addonDir = path.join(this.addonsDir, packageName)\n if (fs.existsSync(addonDir)) {\n fs.rmSync(addonDir, { recursive: true, force: true })\n return\n }\n // Fallback: try without scope (legacy layout)\n const legacyDir = path.join(this.addonsDir, packageName.replace(/^@[^/]+\\//, ''))\n if (fs.existsSync(legacyDir)) {\n fs.rmSync(legacyDir, { recursive: true, force: true })\n }\n }\n\n /** List installed addons (directories with package.json containing camstack.addons) */\n listInstalled(): InstalledPackage[] {\n if (!fs.existsSync(this.addonsDir)) return []\n\n return fs.readdirSync(this.addonsDir, { withFileTypes: true })\n .filter(d => d.isDirectory())\n .map(d => {\n const pkgPath = path.join(this.addonsDir, d.name, 'package.json')\n if (!fs.existsSync(pkgPath)) return null\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as {\n name: string\n version: string\n camstack?: { addons?: unknown[] }\n }\n if (!pkg.camstack?.addons) return null\n return { name: pkg.name, version: pkg.version, dir: path.join(this.addonsDir, d.name) }\n } catch {\n return null\n }\n })\n .filter((item): item is InstalledPackage => item !== null)\n }\n\n /** Check if an addon is installed */\n isInstalled(packageName: string): boolean {\n // Check scoped path first, then legacy\n if (fs.existsSync(path.join(this.addonsDir, packageName, 'package.json'))) return true\n const legacy = packageName.replace(/^@[^/]+\\//, '')\n return fs.existsSync(path.join(this.addonsDir, legacy, 'package.json'))\n }\n\n /** Get installed package info */\n getInstalledPackage(packageName: string): InstalledPackage | null {\n let pkgPath = path.join(this.addonsDir, packageName, 'package.json')\n if (!fs.existsSync(pkgPath)) {\n pkgPath = path.join(this.addonsDir, packageName.replace(/^@[^/]+\\//, ''), 'package.json')\n }\n if (!fs.existsSync(pkgPath)) return null\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as { name: string; version: string }\n return { name: pkg.name, version: pkg.version, dir: path.dirname(pkgPath) }\n } catch {\n return null\n }\n }\n}\n\n// copyDirRecursive, copyExtraFileDirs, ensureDir, stripCamstackDeps\n// are now imported from ./fs-utils.js (single source of truth)\n","import { execFileSync } from 'node:child_process'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\n/**\n * Ensure a directory exists (recursive).\n * Single source of truth — replaces scattered mkdirSync calls.\n */\nexport function ensureDir(dirPath: string): void {\n fs.mkdirSync(dirPath, { recursive: true })\n}\n\n/**\n * Copy a directory recursively.\n * Single source of truth — extracted from addon-installer + first-boot-installer.\n */\nexport function copyDirRecursive(src: string, dest: string): void {\n ensureDir(dest)\n const entries = fs.readdirSync(src, { withFileTypes: true })\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name)\n const destPath = path.join(dest, entry.name)\n if (entry.isDirectory()) {\n copyDirRecursive(srcPath, destPath)\n } else {\n fs.copyFileSync(srcPath, destPath)\n }\n }\n}\n\n/**\n * Strip @camstack/* dependencies and devDependencies from a package.json object.\n * Used when installing addons into the addons directory — @camstack packages\n * are provided by the host runtime, not installed per-addon.\n *\n * Returns a new object (immutable).\n */\nexport function stripCamstackDeps(\n pkg: Record<string, unknown>,\n): Record<string, unknown> {\n const result = { ...pkg }\n\n for (const depType of ['dependencies', 'peerDependencies', 'devDependencies']) {\n const deps = result[depType] as Record<string, string> | undefined\n if (deps) {\n const filtered: Record<string, string> = {}\n for (const [name, version] of Object.entries(deps)) {\n if (!name.startsWith('@camstack/')) {\n filtered[name] = version\n }\n }\n result[depType] = Object.keys(filtered).length > 0 ? filtered : undefined\n }\n }\n\n // Always strip devDependencies — addons don't need them at runtime\n delete result.devDependencies\n\n return result\n}\n\n/**\n * Copy extra file directories declared in package.json \"files\" field.\n * Copies directories (not individual files) from source to destination.\n * Skips \"dist\" (already handled) and glob patterns.\n */\nexport function copyExtraFileDirs(\n pkgJson: Record<string, unknown>,\n sourceDir: string,\n destDir: string,\n): void {\n const files = pkgJson.files as string[] | undefined\n if (!files) return\n\n for (const fileEntry of files) {\n if (fileEntry === 'dist' || fileEntry.includes('*')) continue\n const srcPath = path.join(sourceDir, fileEntry)\n if (!fs.existsSync(srcPath)) continue\n\n const destPath = path.join(destDir, fileEntry)\n const stat = fs.statSync(srcPath)\n if (stat.isDirectory()) {\n copyDirRecursive(srcPath, destPath)\n } else if (stat.isFile()) {\n ensureDir(path.dirname(destPath))\n fs.copyFileSync(srcPath, destPath)\n }\n }\n}\n\n/**\n * Create symlinks in node_modules so that static `import from '@camstack/core'` works.\n * Links: node_modules/@camstack/{pkg} -> data/addons/{pkg}/\n * (not just dist -- need package.json for Node's exports resolution)\n */\nexport function symlinkAddonsToNodeModules(addonsDir: string, nodeModulesDir: string): void {\n const camstackDir = path.join(nodeModulesDir, '@camstack')\n ensureDir(camstackDir)\n\n // Link each addon that's in data/addons/ and also imported by the server\n const packagesToLink = ['core']\n\n for (const pkg of packagesToLink) {\n const addonDir = path.join(addonsDir, '@camstack', pkg)\n const linkPath = path.join(camstackDir, pkg)\n\n if (!fs.existsSync(addonDir)) continue\n\n // Remove existing link/directory\n try {\n const stat = fs.lstatSync(linkPath)\n if (stat.isSymbolicLink() || stat.isDirectory()) {\n fs.rmSync(linkPath, { recursive: true, force: true })\n }\n } catch {\n // Doesn't exist yet -- nothing to remove\n }\n\n fs.symlinkSync(addonDir, linkPath, 'dir')\n console.log(`[symlink] node_modules/@camstack/${pkg} -> ${addonDir}`)\n }\n}\n\n/**\n * Check if any file in src/ is newer than dist/.\n * Returns true if rebuild is needed.\n */\nexport function isSourceNewer(packageDir: string): boolean {\n const srcDir = path.join(packageDir, 'src')\n const distDir = path.join(packageDir, 'dist')\n if (!fs.existsSync(srcDir) || !fs.existsSync(distDir)) return true\n try {\n const distMtime = fs.statSync(distDir).mtimeMs\n const entries = fs.readdirSync(srcDir, { withFileTypes: true, recursive: true })\n for (const entry of entries) {\n if (!entry.isFile()) continue\n const filePath = path.join(entry.parentPath ?? (entry as any).path, entry.name)\n if (fs.statSync(filePath).mtimeMs > distMtime) return true\n }\n return false\n } catch {\n return true\n }\n}\n\n/**\n * Ensure a library dependency (not an addon) has a dist/ directory.\n * Does NOT rebuild — in dev mode, `npm run build` should be run separately.\n * Only checks that dist/ exists and has an index file.\n */\nexport function ensureLibraryBuilt(packageName: string, packagesDir: string): void {\n const dirName = packageName.replace('@camstack/', '')\n const sourceDir = path.join(packagesDir, dirName)\n if (!fs.existsSync(sourceDir)) return\n\n const distDir = path.join(sourceDir, 'dist')\n const hasIndex = fs.existsSync(path.join(distDir, 'index.js')) || fs.existsSync(path.join(distDir, 'index.mjs'))\n if (hasIndex) return\n\n console.warn(`[ensureLibraryBuilt] ${packageName} has no dist/ — run 'npm run build' first`)\n}\n\n/**\n * Install a single npm package into a target directory (package.json + dist/).\n * No validation on camstack.addons -- works for any @camstack/* package.\n * Uses synchronous child_process calls (suitable for first-boot and update paths).\n */\nexport function installPackageFromNpmSync(packageName: string, targetDir: string): void {\n const os = require('node:os') as typeof import('node:os')\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'camstack-install-'))\n\n try {\n // npm pack downloads the tgz without installing\n const stdout = execFileSync('npm', ['pack', packageName, '--pack-destination', tmpDir], {\n timeout: 120_000,\n encoding: 'utf-8',\n })\n\n const tgzFilename = stdout.trim().split('\\n').pop()?.trim()\n if (!tgzFilename) throw new Error('npm pack produced no output')\n\n const tgzPath = path.join(tmpDir, tgzFilename)\n\n // Extract tgz\n const extractDir = path.join(tmpDir, 'extracted')\n ensureDir(extractDir)\n execFileSync('tar', ['-xzf', tgzPath, '-C', extractDir], { timeout: 30_000 })\n\n // npm pack creates a package/ subdirectory\n const packageSubDir = path.join(extractDir, 'package')\n const srcPkgJsonDir = fs.existsSync(path.join(packageSubDir, 'package.json'))\n ? packageSubDir\n : extractDir\n\n // Copy package.json + dist/ to target\n fs.rmSync(targetDir, { recursive: true, force: true })\n ensureDir(targetDir)\n fs.copyFileSync(path.join(srcPkgJsonDir, 'package.json'), path.join(targetDir, 'package.json'))\n\n const distSrc = path.join(srcPkgJsonDir, 'dist')\n if (fs.existsSync(distSrc)) {\n copyDirRecursive(distSrc, path.join(targetDir, 'dist'))\n }\n\n // Copy extra directories from \"files\" in package.json (e.g., python/)\n try {\n const npmPkg = JSON.parse(fs.readFileSync(path.join(srcPkgJsonDir, 'package.json'), 'utf-8'))\n copyExtraFileDirs(npmPkg, srcPkgJsonDir, targetDir)\n } catch { /* non-critical */ }\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true })\n }\n}\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\n\n/**\n * Detect whether we're running inside the monorepo workspace.\n * Returns the absolute path to the packages/ directory if found, otherwise null.\n *\n * Single source of truth — replaces identical implementations in:\n * - first-boot-installer.ts\n * - addon-bridge.service.ts\n */\nexport function detectWorkspacePackagesDir(startDir: string): string | null {\n let current = path.resolve(startDir)\n for (let i = 0; i < 6; i++) {\n current = path.dirname(current)\n const packagesDir = path.join(current, 'packages')\n const rootPkgJson = path.join(current, 'package.json')\n if (fs.existsSync(packagesDir) && fs.existsSync(rootPkgJson)) {\n try {\n const rootPkg = JSON.parse(fs.readFileSync(rootPkgJson, 'utf-8')) as Record<string, unknown>\n if (rootPkg.workspaces || rootPkg.name === 'camstack-server' || rootPkg.name === 'camstack') {\n return packagesDir\n }\n } catch {\n // Ignore parse errors\n }\n }\n }\n return null\n}\n","// packages/kernel/src/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/kernel. 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/kernel/src/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: 'settings-store', 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 * as fs from 'node:fs'\nimport * as yaml from 'js-yaml'\nimport { bootstrapSchema, RUNTIME_DEFAULTS, type BootstrapConfig, type AppConfig } from './config-schema.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/** Feature flags manifest — inlined to avoid dependency on core's feature module */\ninterface 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\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: '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 // 1. Try SQL system_settings\n if (this.settingsStore !== null) {\n const nested = this.getNestedFromSystemSettings(section)\n if (nested !== undefined) return nested as Record<string, unknown>\n }\n // 2. Try bootstrap config (YAML) — for sections like 'server', 'auth' that live in config.yaml\n const bootstrapValue = (this.bootstrapConfig as Record<string, unknown>)[section]\n if (bootstrapValue !== undefined && bootstrapValue !== null && typeof bootstrapValue === 'object') {\n return bootstrapValue as Record<string, unknown>\n }\n // 3. 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 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 /** Sections that live in config.yaml. Everything else goes to SQL. */\n private static readonly BOOTSTRAP_SECTIONS = new Set(['server', 'auth', 'mode'])\n\n /**\n * Atomically update one top-level section of config.yaml and sync in-memory.\n * Only bootstrap sections (server, auth, mode) are written to YAML.\n * Runtime settings must use setSection() which writes to SQL.\n */\n update(section: string, data: Record<string, unknown>): void {\n if (!ConfigManager.BOOTSTRAP_SECTIONS.has(section)) {\n throw new Error(\n `[ConfigManager] Section \"${section}\" is a runtime setting — use setSection() to write to DB, not update() which writes to config.yaml`,\n )\n }\n\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 validation = bootstrapSchema.safeParse(raw)\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/** Default data directory — used when CAMSTACK_DATA env var is not set */\nexport const DEFAULT_DATA_PATH = 'camstack-data'\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(DEFAULT_DATA_PATH),\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 /** TLS configuration */\n tls: z.object({\n /** Enable HTTPS (default: true) */\n enabled: z.boolean().default(true),\n /** Path to custom cert file (PEM). If not set, auto-generates self-signed. */\n certPath: z.string().optional(),\n /** Path to custom key file (PEM). Required if certPath is set. */\n keyPath: z.string().optional(),\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: 'camstack-data/data',\n media: 'camstack-data/media',\n recordings: 'camstack-data/recordings',\n cache: '/tmp/camstack-cache',\n logs: 'camstack-data/logs',\n models: 'camstack-data/models',\n },\n 'providers': [],\n // Recording\n 'recording.segmentDurationSeconds': 4,\n 'recording.defaultRetentionDays': 30,\n // Streaming ports are addon-specific (go2rtc owns its defaults)\n // FFmpeg\n 'ffmpeg.binaryPath': 'ffmpeg',\n 'ffmpeg.hwAccel': 'auto',\n 'ffmpeg.threadCount': 0,\n // Detection defaults\n 'detection.defaultMotionFps': 2,\n 'detection.defaultDetectionFps': 5,\n 'detection.defaultCooldownSeconds': 10,\n 'detection.defaultConfidenceThreshold': 0.4,\n 'detection.trackerMaxAgeFrames': 30,\n 'detection.trackerMinHits': 3,\n 'detection.trackerIouThreshold': 0.3,\n // Backup retention is addon-specific (local-backup owns its default)\n // Auth (runtime)\n 'auth.tokenExpiry': '24h',\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 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 { fork, type ChildProcess } from 'node:child_process'\nimport type {\n WorkerInfo,\n WorkerState,\n WorkerToMainMessage,\n} from '@camstack/types'\n\n/** Infra addon IDs that must stay in-process */\nconst INFRA_ADDON_IDS = new Set([\n 'filesystem-storage',\n 'sqlite-settings',\n 'winston-logging',\n])\n\nexport interface WorkerHostOptions {\n readonly workerEntryPath: string\n readonly devMode?: boolean\n /** Force all addons in-process (emergency fallback) */\n readonly forceInProcess?: boolean\n readonly heartbeatIntervalMs?: number\n readonly heartbeatTimeoutMs?: number\n readonly maxCrashesInWindow?: number\n readonly crashWindowMs?: number\n readonly shutdownTimeoutMs?: number\n /** Log writer function — receives structured log entries from workers */\n readonly onWorkerLog?: (entry: { addonId: string; level: string; message: string; scope: readonly string[]; meta?: Record<string, unknown> }) => void\n}\n\ninterface ManagedWorker {\n addonId: string\n process: ChildProcess\n state: WorkerState\n startedAt: number\n lastHeartbeat: number\n restartCount: number\n crashTimestamps: number[]\n cpuPercent: number\n memoryRss: number\n workerToken?: string\n}\n\ntype ResolvedWorkerHostOptions = Required<Omit<WorkerHostOptions, 'onWorkerLog'>> & Pick<WorkerHostOptions, 'onWorkerLog'>\n\nexport class AddonWorkerHost {\n private readonly workers = new Map<string, ManagedWorker>()\n private readonly options: ResolvedWorkerHostOptions\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null\n\n /** Set of addons that failed to fork and fell back to in-process */\n private readonly fallbackInProcess = new Set<string>()\n\n constructor(options: WorkerHostOptions) {\n this.options = {\n workerEntryPath: options.workerEntryPath,\n devMode: options.devMode ?? false,\n forceInProcess: options.forceInProcess ?? (process.env.CAMSTACK_FORCE_INPROCESS === 'true'),\n heartbeatIntervalMs: options.heartbeatIntervalMs ?? 5000,\n heartbeatTimeoutMs: options.heartbeatTimeoutMs ?? 30000,\n maxCrashesInWindow: options.maxCrashesInWindow ?? 3,\n crashWindowMs: options.crashWindowMs ?? 60000,\n shutdownTimeoutMs: options.shutdownTimeoutMs ?? 10000,\n onWorkerLog: options.onWorkerLog,\n }\n }\n\n /** Check if an addon is infrastructure (must stay in-process) */\n isInfraAddon(addonId: string): boolean {\n return INFRA_ADDON_IDS.has(addonId)\n }\n\n /** Check if an addon fell back to in-process after fork failure */\n isFallbackInProcess(addonId: string): boolean {\n return this.fallbackInProcess.has(addonId)\n }\n\n /** Mark an addon as fallen back to in-process */\n markFallbackInProcess(addonId: string): void {\n this.fallbackInProcess.add(addonId)\n }\n\n /**\n * Determine if an addon should be forked.\n * Default: fork everything except infra addons.\n * Addons can opt out with `inProcess: true` in declaration.\n * Emergency fallback: CAMSTACK_FORCE_INPROCESS=true disables all forking.\n */\n shouldFork(addonId: string, declaration?: { forkable?: boolean; inProcess?: boolean }): boolean {\n if (this.options.forceInProcess) return false\n if (this.options.devMode) return false\n if (this.isInfraAddon(addonId)) return false\n if (this.fallbackInProcess.has(addonId)) return false\n // Addon can explicitly opt out of forking\n if (declaration?.inProcess === true) return false\n // Must explicitly declare forkable: true to fork\n if (declaration?.forkable !== true) return false\n return true\n }\n\n /** Fork a worker for an addon */\n async forkWorker(\n addonId: string,\n addonDir: string,\n config: Record<string, unknown>,\n storagePaths: Record<string, string>,\n dataDir?: string,\n locationPaths?: Record<string, string>,\n workerToken?: string,\n ): Promise<void> {\n if (this.workers.has(addonId)) {\n throw new Error(`Worker for addon \"${addonId}\" already exists`)\n }\n\n // Worker entry is always a compiled .js file — no tsx needed.\n // Pass all config via environment variables (not IPC INIT message).\n const workerEnv: Record<string, string> = {\n PATH: process.env.PATH ?? '',\n HOME: process.env.HOME ?? '',\n NODE_ENV: process.env.NODE_ENV ?? 'production',\n NODE_TLS_REJECT_UNAUTHORIZED: '0', // Accept self-signed cert\n CAMSTACK_WORKER_HUB_URL: `wss://localhost:${process.env.CAMSTACK_PORT ?? '4443'}/trpc`,\n CAMSTACK_WORKER_TOKEN: workerToken ?? '',\n CAMSTACK_ADDON_ID: addonId,\n CAMSTACK_ADDON_DIR: addonDir,\n CAMSTACK_ADDON_CONFIG: JSON.stringify(config),\n CAMSTACK_DATA_DIR: dataDir ?? `camstack-data/addons-data/${addonId}`,\n CAMSTACK_LOCATION_PATHS: JSON.stringify(locationPaths ?? storagePaths),\n }\n\n const forkOptions: Record<string, unknown> = {\n stdio: ['inherit', 'inherit', 'inherit', 'ipc'],\n execArgv: [],\n env: workerEnv,\n }\n\n const child = fork(this.options.workerEntryPath, [], forkOptions as any)\n\n const worker: ManagedWorker = {\n addonId,\n process: child,\n state: 'starting',\n startedAt: Date.now(),\n lastHeartbeat: Date.now(),\n restartCount: 0,\n crashTimestamps: [],\n cpuPercent: 0,\n memoryRss: 0,\n workerToken,\n }\n\n this.workers.set(addonId, worker)\n\n // Route structured IPC messages from worker\n child.on('message', (msg: WorkerToMainMessage) => {\n if (msg.type === 'LOG' && this.options.onWorkerLog) {\n this.options.onWorkerLog({\n addonId,\n level: msg.level,\n message: msg.message,\n scope: ['hub', addonId],\n meta: msg.context,\n })\n } else if (msg.type === 'STATS') {\n worker.cpuPercent = msg.cpu\n worker.memoryRss = msg.memory\n }\n })\n\n // Capture stdout/stderr from worker as fallback (unstructured logs, crash output)\n child.stdout?.on('data', (chunk: Buffer) => {\n const lines = chunk.toString().trim()\n if (!lines) return\n if (this.options.onWorkerLog) {\n this.options.onWorkerLog({\n addonId,\n level: 'info',\n message: lines,\n scope: ['hub', addonId, '__stdout'],\n })\n } else {\n console.log(`[Worker:${addonId}] ${lines}`)\n }\n })\n child.stderr?.on('data', (chunk: Buffer) => {\n const lines = chunk.toString().trim()\n if (!lines) return\n if (this.options.onWorkerLog) {\n this.options.onWorkerLog({\n addonId,\n level: 'error',\n message: lines,\n scope: ['hub', addonId, '__stderr'],\n })\n } else {\n console.error(`[Worker:${addonId}:ERR] ${lines}`)\n }\n })\n\n child.on('exit', (code) => {\n this.handleWorkerExit(addonId, code, addonDir, config, storagePaths, dataDir, locationPaths, workerToken)\n })\n\n child.on('error', (err) => {\n console.error(`[WorkerHost] Worker ${addonId} error:`, err.message)\n })\n\n // No IPC INIT message — config is passed via env vars.\n // Wait for READY signal via IPC (process.send({ type: 'READY' }) from worker).\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(`Worker ${addonId} did not become ready within 30s`))\n }, 30000)\n\n const readyCheck = (msg: { type: string }) => {\n if (msg.type === 'READY') {\n clearTimeout(timeout)\n child.off('message', readyCheck)\n worker.state = 'running'\n resolve()\n }\n }\n child.on('message', readyCheck)\n })\n }\n\n /** List all workers with stats */\n listWorkers(): readonly WorkerInfo[] {\n return [...this.workers.values()].map((w) => ({\n addonId: w.addonId,\n pid: w.process.pid ?? 0,\n state: w.state,\n cpuPercent: w.cpuPercent,\n memoryRss: w.memoryRss,\n uptimeSeconds: Math.round((Date.now() - w.startedAt) / 1000),\n restartCount: w.restartCount,\n subProcesses: [],\n }))\n }\n\n /** Gracefully shutdown a single worker by addon ID */\n async shutdownWorkerById(addonId: string): Promise<void> {\n const worker = this.workers.get(addonId)\n if (!worker) {\n throw new Error(`No worker found for addon \"${addonId}\"`)\n }\n await this.shutdownWorker(addonId, worker)\n this.workers.delete(addonId)\n }\n\n /** Gracefully shutdown all workers */\n async shutdownAll(): Promise<void> {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = null\n }\n\n const shutdowns = [...this.workers.entries()].map(([addonId, worker]) =>\n this.shutdownWorker(addonId, worker),\n )\n await Promise.allSettled(shutdowns)\n this.workers.clear()\n }\n\n /** Start heartbeat monitoring — workers now report heartbeat via tRPC, not IPC PING */\n startHeartbeatMonitoring(): void {\n // With tRPC-based workers, heartbeat is handled by the agent.heartbeat mutation.\n // Keep this method for API compatibility but no IPC pings are needed.\n this.heartbeatTimer = setInterval(() => {\n const now = Date.now()\n for (const [addonId, worker] of this.workers) {\n if (worker.state !== 'running') continue\n // Workers report heartbeat via tRPC — if no update in timeout window, mark crashed\n if (now - worker.lastHeartbeat > this.options.heartbeatTimeoutMs) {\n console.warn(`[WorkerHost] Worker ${addonId} heartbeat timeout — marking unresponsive`)\n worker.state = 'crashed'\n worker.process.kill('SIGKILL')\n }\n }\n }, this.options.heartbeatIntervalMs)\n }\n\n /** Update heartbeat timestamp for a worker (called when agent.heartbeat arrives) */\n updateWorkerHeartbeat(addonId: string, cpuPercent?: number, memoryRss?: number): void {\n const worker = this.workers.get(addonId)\n if (!worker) return\n worker.lastHeartbeat = Date.now()\n if (cpuPercent !== undefined) worker.cpuPercent = cpuPercent\n if (memoryRss !== undefined) worker.memoryRss = memoryRss\n }\n\n private handleWorkerExit(\n addonId: string,\n code: number | null,\n addonDir: string,\n config: Record<string, unknown>,\n storagePaths: Record<string, string>,\n dataDir?: string,\n locationPaths?: Record<string, string>,\n workerToken?: string,\n ): void {\n const worker = this.workers.get(addonId)\n if (!worker) return\n\n if (worker.state === 'stopping') {\n worker.state = 'stopped'\n return\n }\n\n worker.state = 'crashed'\n console.error(`[WorkerHost] Worker ${addonId} exited with code ${code}`)\n\n // Check crash rate\n const now = Date.now()\n worker.crashTimestamps.push(now)\n const recentCrashes = worker.crashTimestamps.filter(\n (t) => now - t < this.options.crashWindowMs,\n )\n worker.crashTimestamps = recentCrashes\n\n if (recentCrashes.length >= this.options.maxCrashesInWindow) {\n console.error(`[WorkerHost] Worker ${addonId} crashed ${recentCrashes.length} times in ${this.options.crashWindowMs}ms — not restarting`)\n return\n }\n\n // Restart after backoff\n const backoff = Math.min(2000 * (worker.restartCount + 1), 30000)\n console.log(`[WorkerHost] Restarting worker ${addonId} in ${backoff}ms`)\n setTimeout(() => {\n worker.restartCount++\n this.workers.delete(addonId)\n this.forkWorker(addonId, addonDir, config, storagePaths, dataDir, locationPaths, workerToken).catch((err) => {\n console.error(`[WorkerHost] Failed to restart worker ${addonId}:`, err)\n })\n }, backoff)\n }\n\n private async shutdownWorker(addonId: string, worker: ManagedWorker): Promise<void> {\n worker.state = 'stopping'\n // Send SIGTERM — worker handles graceful shutdown internally\n worker.process.kill('SIGTERM')\n\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(() => {\n console.warn(`[WorkerHost] Worker ${addonId} did not exit within ${this.options.shutdownTimeoutMs}ms — SIGKILL`)\n worker.process.kill('SIGKILL')\n resolve()\n }, this.options.shutdownTimeoutMs)\n\n worker.process.on('exit', () => {\n clearTimeout(timeout)\n resolve()\n })\n })\n }\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport type {\n WorkerToMainMessage,\n WorkerProcessStats,\n SubProcessInfo,\n} from '@camstack/types'\nimport type {\n IAddonProcessManager,\n SubProcessConfig,\n IManagedSubProcess,\n} from '@camstack/types'\n\nexport class WorkerProcessManager implements IAddonProcessManager {\n private readonly processes = new Map<number, ManagedProcess>()\n\n constructor(\n private readonly sendToMain: (msg: WorkerToMainMessage) => void,\n ) {}\n\n async spawn(config: SubProcessConfig): Promise<IManagedSubProcess> {\n const child = spawn(config.command, [...(config.args ?? [])], {\n cwd: config.cwd,\n env: config.env ? { ...process.env, ...config.env } : undefined,\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n\n const managed = new ManagedProcess(child, config, this.sendToMain)\n this.processes.set(child.pid!, managed)\n\n this.sendToMain({\n type: 'SUB_PROCESS_SPAWNED',\n pid: child.pid!,\n name: config.name,\n command: config.command,\n })\n\n child.on('exit', (code) => {\n this.sendToMain({\n type: 'SUB_PROCESS_EXITED',\n pid: child.pid!,\n code,\n })\n\n if (config.autoRestart && managed.restartCount < (config.maxRestarts ?? 3)) {\n managed.restartCount++\n setTimeout(() => {\n this.spawn(config).catch(() => {})\n }, 2000)\n }\n })\n\n return managed\n }\n\n listProcesses(): readonly SubProcessInfo[] {\n return [...this.processes.values()].map((p) => p.toInfo())\n }\n\n getWorkerStats(): WorkerProcessStats {\n const mem = process.memoryUsage()\n const cpu = process.cpuUsage()\n return {\n pid: process.pid,\n cpuPercent: (cpu.user + cpu.system) / 1_000_000,\n memoryRss: mem.rss,\n heapUsed: mem.heapUsed,\n uptimeSeconds: Math.round(process.uptime()),\n restartCount: 0,\n state: 'running',\n }\n }\n\n async killAll(): Promise<void> {\n for (const [, proc] of this.processes) {\n proc.kill('SIGTERM')\n }\n this.processes.clear()\n }\n}\n\nclass ManagedProcess implements IManagedSubProcess {\n readonly pid: number\n readonly name: string\n restartCount = 0\n private readonly startedAt = Date.now()\n private exitHandlers: Array<(code: number | null) => void> = []\n private errorHandlers: Array<(error: Error) => void> = []\n\n constructor(\n private readonly child: ChildProcess,\n private readonly config: SubProcessConfig,\n private readonly sendToMain: (msg: WorkerToMainMessage) => void,\n ) {\n this.pid = child.pid!\n this.name = config.name\n\n child.on('exit', (code) => {\n for (const handler of this.exitHandlers) handler(code)\n })\n\n child.on('error', (err) => {\n for (const handler of this.errorHandlers) handler(err)\n })\n }\n\n getStats(): WorkerProcessStats {\n return {\n pid: this.pid,\n cpuPercent: 0,\n memoryRss: 0,\n uptimeSeconds: Math.round((Date.now() - this.startedAt) / 1000),\n restartCount: this.restartCount,\n state: this.child.exitCode !== null ? 'stopped' : 'running',\n }\n }\n\n write(data: Buffer): void {\n this.child.stdin?.write(data)\n }\n\n get stdout(): AsyncIterable<Buffer> {\n return this.child.stdout as AsyncIterable<Buffer>\n }\n\n get stderr(): AsyncIterable<Buffer> {\n return this.child.stderr as AsyncIterable<Buffer>\n }\n\n kill(signal: NodeJS.Signals = 'SIGTERM'): void {\n this.child.kill(signal)\n }\n\n wait(): Promise<{ code: number | null; signal: string | null }> {\n if (this.child.exitCode !== null) {\n return Promise.resolve({ code: this.child.exitCode, signal: null })\n }\n return new Promise((resolve) => {\n this.child.on('exit', (code, signal) => {\n resolve({ code, signal })\n })\n })\n }\n\n onExit(handler: (code: number | null) => void): void {\n this.exitHandlers.push(handler)\n }\n\n onError(handler: (error: Error) => void): void {\n this.errorHandlers.push(handler)\n }\n\n toInfo(): SubProcessInfo {\n return {\n pid: this.pid,\n name: this.name,\n command: this.config.command,\n state: this.child.exitCode !== null ? 'stopped' : 'running',\n cpuPercent: 0,\n memoryRss: 0,\n uptimeSeconds: Math.round((Date.now() - this.startedAt) / 1000),\n }\n }\n}\n"],"mappings":"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","names":["import_node_child_process","fs","path","fs","path","os","fs","path","fs","path","import_node_child_process","resolve","import_node_child_process","resolve"]}
1
+ {"version":3,"sources":["../src/addon-loader.ts","../src/addon-engine-manager.ts","../src/fs-utils.ts","../src/addon-installer.ts","../src/workspace-detect.ts","../src/capability-registry.ts","../src/infra-capabilities.ts","../src/config-schema.ts","../src/config-manager.ts","../src/worker/addon-worker-host.ts","../src/worker/worker-process-manager.ts","../src/index.ts"],"sourcesContent":["import type { AddonDeclaration, AddonPackageManifest, ICamstackAddon } from '@camstack/types'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\nexport interface RegisteredAddon {\n readonly declaration: AddonDeclaration\n readonly packageName: string\n readonly packageVersion: string\n /** Human-readable package name from camstack.displayName in package.json */\n readonly packageDisplayName?: 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 /** Scan addons directory and load all addon packages.\n * Supports scoped layout: addons/@scope/package-name/ */\n async loadFromDirectory(addonsDir: string): Promise<void> {\n if (!fs.existsSync(addonsDir)) return\n\n const entries = fs.readdirSync(addonsDir, { withFileTypes: true })\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n\n // Scoped directory (e.g., @camstack/) — scan one level deeper\n if (entry.name.startsWith('@')) {\n const scopeDir = path.join(addonsDir, entry.name)\n const scopeEntries = fs.readdirSync(scopeDir, { withFileTypes: true })\n for (const scopeEntry of scopeEntries) {\n if (!scopeEntry.isDirectory()) continue\n await this.tryLoadAddon(path.join(scopeDir, scopeEntry.name))\n }\n continue\n }\n\n // Non-scoped (legacy or flat layout)\n await this.tryLoadAddon(path.join(addonsDir, entry.name))\n }\n }\n\n private async tryLoadAddon(addonDir: string): Promise<void> {\n const pkgJsonPath = path.join(addonDir, 'package.json')\n if (!fs.existsSync(pkgJsonPath)) return\n try {\n await this.loadFromAddonDir(addonDir)\n } catch (err) {\n console.warn(`Failed to load addon from ${addonDir}: ${err}`)\n }\n }\n\n /** Load addon from a specific directory (package.json + dist/) */\n async loadFromAddonDir(addonDir: string): Promise<void> {\n const pkgJsonPath = path.join(addonDir, 'package.json')\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as Record<string, unknown>\n const packageName = pkgJson['name'] as string\n const packageVersion = (pkgJson['version'] as string) ?? '0.0.0'\n const manifest = pkgJson['camstack'] as AddonPackageManifest | undefined\n\n if (!manifest?.addons?.length) return\n\n for (const declaration of manifest.addons) {\n // Resolve entry: try dist/ first\n const entryFile = declaration.entry\n .replace(/^\\.\\//, '')\n .replace(/^src\\//, 'dist/')\n .replace(/\\.ts$/, '.js')\n\n let entryPath = path.resolve(addonDir, entryFile)\n\n // Fallback: try other locations and extensions\n if (!fs.existsSync(entryPath)) {\n const base = entryPath.replace(/\\.(js|cjs|mjs)$/, '')\n const alternatives = [\n `${base}.cjs`,\n `${base}.mjs`,\n path.resolve(addonDir, 'dist', 'index.js'),\n path.resolve(addonDir, 'dist', 'index.cjs'),\n path.resolve(addonDir, 'dist', 'index.mjs'),\n path.resolve(addonDir, declaration.entry),\n ]\n entryPath = alternatives.find(p => fs.existsSync(p)) ?? entryPath\n }\n\n if (!fs.existsSync(entryPath)) {\n throw new Error(`Entry not found: ${entryPath}`)\n }\n\n const mod = (await import(entryPath)) as Record<string, unknown>\n const AddonClass = resolveAddonClass(mod)\n\n if (!AddonClass) {\n throw new Error(`No addon class in ${entryPath}`)\n }\n\n this.addons.set(declaration.id, {\n declaration,\n packageName,\n packageVersion,\n packageDisplayName: manifest.displayName,\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 packageVersion = '0.0.0',\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 packageVersion,\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","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 { execFileSync } from 'node:child_process'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\n/**\n * Ensure a directory exists (recursive).\n * Single source of truth — replaces scattered mkdirSync calls.\n */\nexport function ensureDir(dirPath: string): void {\n fs.mkdirSync(dirPath, { recursive: true })\n}\n\n/**\n * Copy a directory recursively.\n * Single source of truth — extracted from addon-installer + first-boot-installer.\n */\nexport function copyDirRecursive(src: string, dest: string): void {\n ensureDir(dest)\n const entries = fs.readdirSync(src, { withFileTypes: true })\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name)\n const destPath = path.join(dest, entry.name)\n if (entry.isDirectory()) {\n copyDirRecursive(srcPath, destPath)\n } else {\n fs.copyFileSync(srcPath, destPath)\n }\n }\n}\n\n/**\n * Strip @camstack/* dependencies and devDependencies from a package.json object.\n * Used when installing addons into the addons directory — @camstack packages\n * are provided by the host runtime, not installed per-addon.\n *\n * Returns a new object (immutable).\n */\nexport function stripCamstackDeps(\n pkg: Record<string, unknown>,\n): Record<string, unknown> {\n const result = { ...pkg }\n\n for (const depType of ['dependencies', 'peerDependencies', 'devDependencies']) {\n const deps = result[depType] as Record<string, string> | undefined\n if (deps) {\n const filtered: Record<string, string> = {}\n for (const [name, version] of Object.entries(deps)) {\n if (!name.startsWith('@camstack/')) {\n filtered[name] = version\n }\n }\n result[depType] = Object.keys(filtered).length > 0 ? filtered : undefined\n }\n }\n\n // Always strip devDependencies — addons don't need them at runtime\n delete result.devDependencies\n\n return result\n}\n\n/**\n * Copy extra file directories declared in package.json \"files\" field.\n * Copies directories (not individual files) from source to destination.\n * Skips \"dist\" (already handled) and glob patterns.\n */\nexport function copyExtraFileDirs(\n pkgJson: Record<string, unknown>,\n sourceDir: string,\n destDir: string,\n): void {\n const files = pkgJson.files as string[] | undefined\n if (!files) return\n\n for (const fileEntry of files) {\n if (fileEntry === 'dist' || fileEntry.includes('*')) continue\n const srcPath = path.join(sourceDir, fileEntry)\n if (!fs.existsSync(srcPath)) continue\n\n const destPath = path.join(destDir, fileEntry)\n const stat = fs.statSync(srcPath)\n if (stat.isDirectory()) {\n copyDirRecursive(srcPath, destPath)\n } else if (stat.isFile()) {\n ensureDir(path.dirname(destPath))\n fs.copyFileSync(srcPath, destPath)\n }\n }\n}\n\n/**\n * Create symlinks in node_modules so that static `import from '@camstack/core'` works.\n * Links: node_modules/@camstack/{pkg} -> data/addons/{pkg}/\n * (not just dist -- need package.json for Node's exports resolution)\n */\nexport function symlinkAddonsToNodeModules(addonsDir: string, nodeModulesDir: string): void {\n const camstackDir = path.join(nodeModulesDir, '@camstack')\n ensureDir(camstackDir)\n\n // Link each addon that's in data/addons/ and also imported by the server\n const packagesToLink = ['core']\n\n for (const pkg of packagesToLink) {\n const addonDir = path.join(addonsDir, '@camstack', pkg)\n const linkPath = path.join(camstackDir, pkg)\n\n if (!fs.existsSync(addonDir)) continue\n\n // Remove existing link/directory\n try {\n const stat = fs.lstatSync(linkPath)\n if (stat.isSymbolicLink() || stat.isDirectory()) {\n fs.rmSync(linkPath, { recursive: true, force: true })\n }\n } catch {\n // Doesn't exist yet -- nothing to remove\n }\n\n fs.symlinkSync(addonDir, linkPath, 'dir')\n console.log(`[symlink] node_modules/@camstack/${pkg} -> ${addonDir}`)\n }\n}\n\n/**\n * Check if any file in src/ is newer than dist/.\n * Returns true if rebuild is needed.\n */\nexport function isSourceNewer(packageDir: string): boolean {\n const srcDir = path.join(packageDir, 'src')\n const distDir = path.join(packageDir, 'dist')\n if (!fs.existsSync(srcDir) || !fs.existsSync(distDir)) return true\n try {\n const distMtime = fs.statSync(distDir).mtimeMs\n const entries = fs.readdirSync(srcDir, { withFileTypes: true, recursive: true })\n for (const entry of entries) {\n if (!entry.isFile()) continue\n const filePath = path.join(entry.parentPath ?? (entry as any).path, entry.name)\n if (fs.statSync(filePath).mtimeMs > distMtime) return true\n }\n return false\n } catch {\n return true\n }\n}\n\n/**\n * Ensure a library dependency (not an addon) has a dist/ directory.\n * Does NOT rebuild — in dev mode, `npm run build` should be run separately.\n * Only checks that dist/ exists and has an index file.\n */\nexport function ensureLibraryBuilt(packageName: string, packagesDir: string): void {\n const dirName = packageName.replace('@camstack/', '')\n const sourceDir = path.join(packagesDir, dirName)\n if (!fs.existsSync(sourceDir)) return\n\n const distDir = path.join(sourceDir, 'dist')\n const hasIndex = fs.existsSync(path.join(distDir, 'index.js')) || fs.existsSync(path.join(distDir, 'index.mjs'))\n if (hasIndex) return\n\n console.warn(`[ensureLibraryBuilt] ${packageName} has no dist/ — run 'npm run build' first`)\n}\n\n/**\n * Install a single npm package into a target directory (package.json + dist/).\n * No validation on camstack.addons -- works for any @camstack/* package.\n * Uses synchronous child_process calls (suitable for first-boot and update paths).\n */\nexport function installPackageFromNpmSync(packageName: string, targetDir: string): void {\n const os = require('node:os') as typeof import('node:os')\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'camstack-install-'))\n\n try {\n // npm pack downloads the tgz without installing\n const stdout = execFileSync('npm', ['pack', packageName, '--pack-destination', tmpDir], {\n timeout: 120_000,\n encoding: 'utf-8',\n })\n\n const tgzFilename = stdout.trim().split('\\n').pop()?.trim()\n if (!tgzFilename) throw new Error('npm pack produced no output')\n\n const tgzPath = path.join(tmpDir, tgzFilename)\n\n // Extract tgz\n const extractDir = path.join(tmpDir, 'extracted')\n ensureDir(extractDir)\n execFileSync('tar', ['-xzf', tgzPath, '-C', extractDir], { timeout: 30_000 })\n\n // npm pack creates a package/ subdirectory\n const packageSubDir = path.join(extractDir, 'package')\n const srcPkgJsonDir = fs.existsSync(path.join(packageSubDir, 'package.json'))\n ? packageSubDir\n : extractDir\n\n // Copy package.json + dist/ to target\n fs.rmSync(targetDir, { recursive: true, force: true })\n ensureDir(targetDir)\n fs.copyFileSync(path.join(srcPkgJsonDir, 'package.json'), path.join(targetDir, 'package.json'))\n\n const distSrc = path.join(srcPkgJsonDir, 'dist')\n if (fs.existsSync(distSrc)) {\n copyDirRecursive(distSrc, path.join(targetDir, 'dist'))\n }\n\n // Copy extra directories from \"files\" in package.json (e.g., python/)\n try {\n const npmPkg = JSON.parse(fs.readFileSync(path.join(srcPkgJsonDir, 'package.json'), 'utf-8'))\n copyExtraFileDirs(npmPkg, srcPkgJsonDir, targetDir)\n } catch { /* non-critical */ }\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true })\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'\nimport * as os from 'node:os'\nimport { copyDirRecursive, copyExtraFileDirs, ensureDir, ensureLibraryBuilt, isSourceNewer, stripCamstackDeps } from './fs-utils.js'\n\nconst execFileAsync = promisify(execFile)\n\nexport interface AddonInstallerConfig {\n /** Directory where addons are stored (e.g., {dataDir}/addons) — each addon is a subdirectory */\n readonly addonsDir: string\n /** npm registry URL (default: https://registry.npmjs.org) */\n readonly registry?: string\n /** Workspace packages directory (e.g., /path/to/camstack-server/packages) — for dev installs */\n readonly workspacePackagesDir?: string\n}\n\nimport type { InstalledPackage } from '@camstack/types'\nexport type { InstalledPackage }\n\nexport class AddonInstaller {\n private readonly addonsDir: string\n private readonly registry: string | undefined\n private readonly workspacePackagesDir: string | undefined\n\n constructor(config: AddonInstallerConfig) {\n this.addonsDir = config.addonsDir\n this.registry = config.registry\n this.workspacePackagesDir = config.workspacePackagesDir\n }\n\n /** Required addon packages that must be installed for the server to function */\n static readonly REQUIRED_PACKAGES = [\n '@camstack/core',\n '@camstack/addon-pipeline',\n '@camstack/addon-vision',\n '@camstack/addon-admin-ui',\n '@camstack/addon-webrtc-adaptive',\n '@camstack/addon-pipeline-analysis',\n '@camstack/addon-scene-intelligence',\n '@camstack/addon-advanced-notifier',\n ] as const\n\n /** Ensure the addons directory exists */\n async initialize(): Promise<void> {\n ensureDir(this.addonsDir)\n }\n\n /**\n * Ensure all required packages are installed in the addons directory.\n * This replaces the standalone first-boot-installer.ts.\n */\n async ensureRequiredPackages(): Promise<void> {\n ensureDir(this.addonsDir)\n\n // In workspace mode, ensure library dependencies are built first\n if (this.workspacePackagesDir) {\n console.log(`[AddonInstaller] Workspace detected: ${this.workspacePackagesDir}`)\n ensureLibraryBuilt('@camstack/kernel', this.workspacePackagesDir)\n ensureLibraryBuilt('@camstack/types', this.workspacePackagesDir)\n }\n\n for (const packageName of AddonInstaller.REQUIRED_PACKAGES) {\n const addonDir = path.join(this.addonsDir, packageName)\n const pkgJsonPath = path.join(addonDir, 'package.json')\n\n // CAMSTACK_INSTALL_SOURCE=npm forces npm install even in workspace mode\n const forceNpm = process.env['CAMSTACK_INSTALL_SOURCE'] === 'npm'\n const useWorkspace = this.workspacePackagesDir && !forceNpm\n\n // Skip if already installed and up-to-date\n if (fs.existsSync(pkgJsonPath)) {\n if (!useWorkspace) {\n // Production or npm-forced: installed = done\n console.log(`[AddonInstaller] ${packageName} — already installed (npm), skipping`)\n continue\n }\n // Workspace: skip if source dist/ isn't newer than target\n const srcPkgDir = this.findWorkspacePackage(packageName)\n if (srcPkgDir && !isSourceNewer(srcPkgDir)) {\n console.log(`[AddonInstaller] ${packageName} — workspace up-to-date, skipping`)\n continue\n }\n }\n\n try {\n // Try workspace install first (dev mode), fall back to npm\n if (useWorkspace) {\n const pkgDir = this.findWorkspacePackage(packageName)\n if (pkgDir) {\n console.log(`[AddonInstaller] ${packageName} — installing from workspace`)\n await this.installFromWorkspace(packageName)\n continue\n }\n }\n\n console.log(`[AddonInstaller] ${packageName} — installing from npm`)\n await this.installFromNpm(packageName)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n // Core is essential — abort if it fails\n if (packageName === '@camstack/core') {\n throw new Error(`Required package ${packageName} failed to install: ${msg}`)\n }\n console.error(`[AddonInstaller] Failed to install ${packageName}: ${msg}`)\n }\n }\n }\n\n private findWorkspacePackage(packageName: string): string | null {\n if (!this.workspacePackagesDir) return null\n const shortName = packageName.replace('@camstack/', '')\n // Check common naming patterns\n for (const dirName of [shortName, `addon-${shortName}`, shortName.replace('addon-', '')]) {\n const candidate = path.join(this.workspacePackagesDir, dirName)\n if (fs.existsSync(path.join(candidate, 'package.json'))) {\n try {\n const pkg = JSON.parse(fs.readFileSync(path.join(candidate, 'package.json'), 'utf-8'))\n if (pkg.name === packageName) return candidate\n } catch { /* ignore */ }\n }\n }\n return null\n }\n\n /** Install addon from a tgz file (uploaded or downloaded) */\n async installFromTgz(tgzPath: string): Promise<{ name: string; version: string }> {\n // 1. Extract tgz to temp dir\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'camstack-addon-install-'))\n\n try {\n await execFileAsync('tar', ['-xzf', tgzPath, '-C', tmpDir], { timeout: 30_000 })\n\n // npm pack creates a package/ directory inside the tgz\n const extractedDir = path.join(tmpDir, 'package')\n const pkgJsonPath = fs.existsSync(path.join(extractedDir, 'package.json'))\n ? path.join(extractedDir, 'package.json')\n : path.join(tmpDir, 'package.json')\n\n if (!fs.existsSync(pkgJsonPath)) {\n throw new Error('No package.json found in tgz archive')\n }\n\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as {\n name: string\n version: string\n files?: string[]\n camstack?: { addons?: unknown[] }\n }\n\n if (!pkgJson.camstack?.addons) {\n throw new Error(`Package ${pkgJson.name} has no camstack.addons manifest`)\n }\n\n // 2. Determine target directory name (strip scope)\n const targetDir = path.join(this.addonsDir, pkgJson.name)\n\n // 3. Remove old version if present\n fs.rmSync(targetDir, { recursive: true, force: true })\n ensureDir(targetDir)\n\n // 4. Copy package.json\n const sourceDir = path.dirname(pkgJsonPath)\n fs.copyFileSync(pkgJsonPath, path.join(targetDir, 'package.json'))\n\n // 5. Copy dist/ directory\n const sourceDist = path.join(sourceDir, 'dist')\n if (fs.existsSync(sourceDist)) {\n copyDirRecursive(sourceDist, path.join(targetDir, 'dist'))\n }\n\n // 6. Copy extra directories from \"files\" in package.json (e.g., python/, reference-images/)\n copyExtraFileDirs(pkgJson, sourceDir, targetDir)\n\n return { name: pkgJson.name, version: pkgJson.version }\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true })\n }\n }\n\n /**\n * Install addon — prefers workspace if available, falls back to npm.\n * This ensures dev builds (with vite output, etc.) are used when in workspace mode.\n */\n async install(packageName: string, version?: string): Promise<{ name: string; version: string }> {\n if (this.workspacePackagesDir) {\n // Workspace dirs use short names (e.g., packages/addon-benchmark)\n const workspaceDirName = packageName.replace('@camstack/', '')\n const sourceDir = path.join(this.workspacePackagesDir, workspaceDirName)\n if (fs.existsSync(path.join(sourceDir, 'package.json'))) {\n return this.installFromWorkspace(packageName)\n }\n }\n return this.installFromNpm(packageName, version)\n }\n\n /**\n * Install addon from workspace by copying package.json + dist/ to addons dir.\n * Does NOT build — expects dist/ to already exist (run `npm run build` separately).\n */\n async installFromWorkspace(packageName: string): Promise<{ name: string; version: string }> {\n if (!this.workspacePackagesDir) {\n throw new Error('Workspace packages directory not configured')\n }\n\n const workspaceDirName = packageName.replace('@camstack/', '')\n const sourceDir = path.join(this.workspacePackagesDir, workspaceDirName)\n const sourcePkgJson = path.join(sourceDir, 'package.json')\n\n if (!fs.existsSync(sourcePkgJson)) {\n throw new Error(`Workspace package not found: ${sourceDir}`)\n }\n\n const distDir = path.join(sourceDir, 'dist')\n if (!fs.existsSync(distDir)) {\n throw new Error(`${packageName} has no dist/ directory — run 'npm run build' first`)\n }\n\n // Copy package.json + dist/ to target (use full scoped name as dir)\n const targetDir = path.join(this.addonsDir, packageName)\n\n fs.rmSync(targetDir, { recursive: true, force: true })\n ensureDir(targetDir)\n\n // Strip @camstack/* deps from package.json\n const pkgData = JSON.parse(fs.readFileSync(sourcePkgJson, 'utf-8'))\n const strippedPkg = stripCamstackDeps(pkgData)\n fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(strippedPkg, null, 2))\n\n copyDirRecursive(distDir, path.join(targetDir, 'dist'))\n\n // Copy extra directories from \"files\" in package.json (e.g., python/, reference-images/)\n copyExtraFileDirs(pkgData, sourceDir, targetDir)\n\n // Mark install source for UI display\n fs.writeFileSync(path.join(targetDir, '.install-source'), 'workspace')\n\n // Install production dependencies (non-fatal if fails)\n try {\n await execFileAsync('npm', ['install', '--omit=dev', '--ignore-scripts=false'], {\n cwd: targetDir,\n timeout: 120_000,\n })\n } catch {\n // Non-fatal — addon may not have external deps\n }\n\n return { name: pkgData.name, version: pkgData.version }\n }\n\n /** Install addon from npm (download tgz, then extract) */\n async installFromNpm(packageName: string, version?: string): Promise<{ name: string; version: string }> {\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'camstack-addon-npm-'))\n\n const packageSpec = version ? `${packageName}@${version}` : packageName\n const args = ['pack', packageSpec, '--pack-destination', tmpDir]\n if (this.registry) {\n args.push('--registry', this.registry)\n }\n\n console.log(`[AddonInstaller] npm pack ${packageSpec} → ${tmpDir}`)\n\n try {\n const { stdout } = await execFileAsync('npm', args, {\n timeout: 120_000,\n })\n\n const tgzFiles = fs.readdirSync(tmpDir).filter(f => f.endsWith('.tgz'))\n console.log(`[AddonInstaller] npm pack stdout: ${stdout.trim()}, tgzFiles: ${tgzFiles.join(', ')}`)\n if (tgzFiles.length === 0) {\n throw new Error(`npm pack produced no tgz file for ${packageSpec}. stdout: ${stdout.trim()}`)\n }\n\n const tgzPath = path.join(tmpDir, tgzFiles[0]!)\n const result = await this.installFromTgz(tgzPath)\n console.log(`[AddonInstaller] installFromTgz result: ${result.name}@${result.version} → ${path.join(this.addonsDir, result.name)}`)\n\n // Mark as npm install\n const targetDir = path.join(this.addonsDir, result.name)\n fs.writeFileSync(path.join(targetDir, '.install-source'), 'npm')\n\n return result\n } finally {\n // Clean up AFTER installFromTgz has fully completed\n fs.rmSync(tmpDir, { recursive: true, force: true })\n }\n }\n\n /** Uninstall addon (delete directory) */\n async uninstall(packageName: string): Promise<void> {\n // Support both full scoped name and short name\n const addonDir = path.join(this.addonsDir, packageName)\n if (fs.existsSync(addonDir)) {\n fs.rmSync(addonDir, { recursive: true, force: true })\n return\n }\n // Fallback: try without scope (legacy layout)\n const legacyDir = path.join(this.addonsDir, packageName.replace(/^@[^/]+\\//, ''))\n if (fs.existsSync(legacyDir)) {\n fs.rmSync(legacyDir, { recursive: true, force: true })\n }\n }\n\n /** List installed addons (directories with package.json containing camstack.addons) */\n listInstalled(): InstalledPackage[] {\n if (!fs.existsSync(this.addonsDir)) return []\n\n return fs.readdirSync(this.addonsDir, { withFileTypes: true })\n .filter(d => d.isDirectory())\n .map(d => {\n const pkgPath = path.join(this.addonsDir, d.name, 'package.json')\n if (!fs.existsSync(pkgPath)) return null\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as {\n name: string\n version: string\n camstack?: { addons?: unknown[] }\n }\n if (!pkg.camstack?.addons) return null\n const sourceFile = path.join(this.addonsDir, d.name, '.install-source')\n const installSource = fs.existsSync(sourceFile)\n ? fs.readFileSync(sourceFile, 'utf-8').trim() as 'npm' | 'workspace' | 'upload'\n : undefined\n const result: InstalledPackage = { name: pkg.name, version: pkg.version, dir: path.join(this.addonsDir, d.name), ...(installSource ? { installSource } : {}) }\n return result\n } catch {\n return null\n }\n })\n .filter((item): item is InstalledPackage => item !== null)\n }\n\n /** Check if an addon is installed */\n isInstalled(packageName: string): boolean {\n // Check scoped path first, then legacy\n if (fs.existsSync(path.join(this.addonsDir, packageName, 'package.json'))) return true\n const legacy = packageName.replace(/^@[^/]+\\//, '')\n return fs.existsSync(path.join(this.addonsDir, legacy, 'package.json'))\n }\n\n /** Get installed package info */\n getInstalledPackage(packageName: string): InstalledPackage | null {\n let pkgPath = path.join(this.addonsDir, packageName, 'package.json')\n if (!fs.existsSync(pkgPath)) {\n pkgPath = path.join(this.addonsDir, packageName.replace(/^@[^/]+\\//, ''), 'package.json')\n }\n if (!fs.existsSync(pkgPath)) return null\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as { name: string; version: string }\n return { name: pkg.name, version: pkg.version, dir: path.dirname(pkgPath) }\n } catch {\n return null\n }\n }\n}\n\n// copyDirRecursive, copyExtraFileDirs, ensureDir, stripCamstackDeps\n// are now imported from ./fs-utils.js (single source of truth)\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\n\n/**\n * Detect whether we're running inside the monorepo workspace.\n * Returns the absolute path to the packages/ directory if found, otherwise null.\n *\n * Single source of truth — replaces identical implementations in:\n * - first-boot-installer.ts\n * - addon-bridge.service.ts\n */\nexport function detectWorkspacePackagesDir(startDir: string): string | null {\n let current = path.resolve(startDir)\n for (let i = 0; i < 6; i++) {\n current = path.dirname(current)\n const packagesDir = path.join(current, 'packages')\n const rootPkgJson = path.join(current, 'package.json')\n if (fs.existsSync(packagesDir) && fs.existsSync(rootPkgJson)) {\n try {\n const rootPkg = JSON.parse(fs.readFileSync(rootPkgJson, 'utf-8')) as Record<string, unknown>\n if (rootPkg.workspaces || rootPkg.name === 'camstack-server' || rootPkg.name === 'camstack') {\n return packagesDir\n }\n } catch {\n // Ignore parse errors\n }\n }\n }\n return null\n}\n","// packages/kernel/src/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/kernel. 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/kernel/src/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: 'settings-store', 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 { z } from 'zod'\n\n/** Default data directory — used when CAMSTACK_DATA env var is not set */\nexport const DEFAULT_DATA_PATH = 'camstack-data'\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(DEFAULT_DATA_PATH),\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 /** TLS configuration */\n tls: z.object({\n /** Enable HTTPS (default: true) */\n enabled: z.boolean().default(true),\n /** Path to custom cert file (PEM). If not set, auto-generates self-signed. */\n certPath: z.string().optional(),\n /** Path to custom key file (PEM). Required if certPath is set. */\n keyPath: z.string().optional(),\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: 'camstack-data/data',\n media: 'camstack-data/media',\n recordings: 'camstack-data/recordings',\n cache: '/tmp/camstack-cache',\n logs: 'camstack-data/logs',\n models: 'camstack-data/models',\n },\n 'providers': [],\n // Recording\n 'recording.segmentDurationSeconds': 4,\n 'recording.defaultRetentionDays': 30,\n // Streaming ports are addon-specific (go2rtc owns its defaults)\n // FFmpeg\n 'ffmpeg.binaryPath': 'ffmpeg',\n 'ffmpeg.hwAccel': 'auto',\n 'ffmpeg.threadCount': 0,\n // Detection defaults\n 'detection.defaultMotionFps': 2,\n 'detection.defaultDetectionFps': 5,\n 'detection.defaultCooldownSeconds': 10,\n 'detection.defaultConfidenceThreshold': 0.4,\n 'detection.trackerMaxAgeFrames': 30,\n 'detection.trackerMinHits': 3,\n 'detection.trackerIouThreshold': 0.3,\n // Backup retention is addon-specific (local-backup owns its default)\n // Auth (runtime)\n 'auth.tokenExpiry': '24h',\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 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 * as fs from 'node:fs'\nimport * as yaml from 'js-yaml'\nimport { bootstrapSchema, RUNTIME_DEFAULTS, type BootstrapConfig, type AppConfig } from './config-schema.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/** Feature flags manifest — inlined to avoid dependency on core's feature module */\ninterface 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\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: '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 // 1. Try SQL system_settings\n if (this.settingsStore !== null) {\n const nested = this.getNestedFromSystemSettings(section)\n if (nested !== undefined) return nested as Record<string, unknown>\n }\n // 2. Try bootstrap config (YAML) — for sections like 'server', 'auth' that live in config.yaml\n const bootstrapValue = (this.bootstrapConfig as Record<string, unknown>)[section]\n if (bootstrapValue !== undefined && bootstrapValue !== null && typeof bootstrapValue === 'object') {\n return bootstrapValue as Record<string, unknown>\n }\n // 3. 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 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 /** Sections that live in config.yaml. Everything else goes to SQL. */\n private static readonly BOOTSTRAP_SECTIONS = new Set(['server', 'auth', 'mode'])\n\n /**\n * Atomically update one top-level section of config.yaml and sync in-memory.\n * Only bootstrap sections (server, auth, mode) are written to YAML.\n * Runtime settings must use setSection() which writes to SQL.\n */\n update(section: string, data: Record<string, unknown>): void {\n if (!ConfigManager.BOOTSTRAP_SECTIONS.has(section)) {\n throw new Error(\n `[ConfigManager] Section \"${section}\" is a runtime setting — use setSection() to write to DB, not update() which writes to config.yaml`,\n )\n }\n\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 validation = bootstrapSchema.safeParse(raw)\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 { fork, type ChildProcess } from 'node:child_process'\nimport type {\n WorkerInfo,\n WorkerState,\n WorkerToMainMessage,\n} from '@camstack/types'\n\n/** Infra addon IDs that must stay in-process */\nconst INFRA_ADDON_IDS = new Set([\n 'filesystem-storage',\n 'sqlite-settings',\n 'winston-logging',\n])\n\nexport interface WorkerHostOptions {\n readonly workerEntryPath: string\n readonly devMode?: boolean\n /** Force all addons in-process (emergency fallback) */\n readonly forceInProcess?: boolean\n readonly heartbeatIntervalMs?: number\n readonly heartbeatTimeoutMs?: number\n readonly maxCrashesInWindow?: number\n readonly crashWindowMs?: number\n readonly shutdownTimeoutMs?: number\n /** Log writer function — receives structured log entries from workers */\n readonly onWorkerLog?: (entry: { addonId: string; level: string; message: string; scope: readonly string[]; meta?: Record<string, unknown> }) => void\n}\n\ninterface ForkConfig {\n readonly addonDir: string\n readonly config: Record<string, unknown>\n readonly storagePaths: Record<string, string>\n readonly dataDir?: string\n readonly locationPaths?: Record<string, string>\n readonly workerToken?: string\n}\n\ninterface ManagedWorker {\n addonId: string\n process: ChildProcess\n state: WorkerState\n startedAt: number\n lastHeartbeat: number\n restartCount: number\n crashTimestamps: number[]\n cpuPercent: number\n memoryRss: number\n workerToken?: string\n /** Stored fork config for restart support */\n forkConfig?: ForkConfig\n}\n\ntype ResolvedWorkerHostOptions = Required<Omit<WorkerHostOptions, 'onWorkerLog'>> & Pick<WorkerHostOptions, 'onWorkerLog'>\n\nexport class AddonWorkerHost {\n private readonly workers = new Map<string, ManagedWorker>()\n private readonly options: ResolvedWorkerHostOptions\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null\n\n /** Set of addons that failed to fork and fell back to in-process */\n private readonly fallbackInProcess = new Set<string>()\n\n constructor(options: WorkerHostOptions) {\n this.options = {\n workerEntryPath: options.workerEntryPath,\n devMode: options.devMode ?? false,\n forceInProcess: options.forceInProcess ?? (process.env.CAMSTACK_FORCE_INPROCESS === 'true'),\n heartbeatIntervalMs: options.heartbeatIntervalMs ?? 5000,\n heartbeatTimeoutMs: options.heartbeatTimeoutMs ?? 30000,\n maxCrashesInWindow: options.maxCrashesInWindow ?? 3,\n crashWindowMs: options.crashWindowMs ?? 60000,\n shutdownTimeoutMs: options.shutdownTimeoutMs ?? 10000,\n onWorkerLog: options.onWorkerLog,\n }\n }\n\n /** Check if an addon is infrastructure (must stay in-process) */\n isInfraAddon(addonId: string): boolean {\n return INFRA_ADDON_IDS.has(addonId)\n }\n\n /** Check if an addon fell back to in-process after fork failure */\n isFallbackInProcess(addonId: string): boolean {\n return this.fallbackInProcess.has(addonId)\n }\n\n /** Mark an addon as fallen back to in-process */\n markFallbackInProcess(addonId: string): void {\n this.fallbackInProcess.add(addonId)\n }\n\n /**\n * Determine if an addon should be forked.\n * Default: fork everything except infra addons.\n * Addons can opt out with `inProcess: true` in declaration.\n * Emergency fallback: CAMSTACK_FORCE_INPROCESS=true disables all forking.\n */\n shouldFork(addonId: string, declaration?: { forkable?: boolean; inProcess?: boolean }): boolean {\n if (this.options.forceInProcess) return false\n if (this.options.devMode) return false\n if (this.isInfraAddon(addonId)) return false\n if (this.fallbackInProcess.has(addonId)) return false\n // Addon can explicitly opt out of forking\n if (declaration?.inProcess === true) return false\n // Must explicitly declare forkable: true to fork\n if (declaration?.forkable !== true) return false\n return true\n }\n\n /** Fork a worker for an addon */\n async forkWorker(\n addonId: string,\n addonDir: string,\n config: Record<string, unknown>,\n storagePaths: Record<string, string>,\n dataDir?: string,\n locationPaths?: Record<string, string>,\n workerToken?: string,\n ): Promise<void> {\n if (this.workers.has(addonId)) {\n throw new Error(`Worker for addon \"${addonId}\" already exists`)\n }\n\n // Worker entry is always a compiled .js file — no tsx needed.\n // Pass all config via environment variables (not IPC INIT message).\n const workerEnv: Record<string, string> = {\n PATH: process.env.PATH ?? '',\n HOME: process.env.HOME ?? '',\n NODE_ENV: process.env.NODE_ENV ?? 'production',\n NODE_TLS_REJECT_UNAUTHORIZED: '0', // Accept self-signed cert\n CAMSTACK_WORKER_HUB_URL: `wss://localhost:${process.env.CAMSTACK_PORT ?? '4443'}/trpc`,\n CAMSTACK_WORKER_TOKEN: workerToken ?? '',\n CAMSTACK_ADDON_ID: addonId,\n CAMSTACK_ADDON_DIR: addonDir,\n CAMSTACK_ADDON_CONFIG: JSON.stringify(config),\n CAMSTACK_DATA_DIR: dataDir ?? `camstack-data/addons-data/${addonId}`,\n CAMSTACK_LOCATION_PATHS: JSON.stringify(locationPaths ?? storagePaths),\n }\n\n const forkOptions: Record<string, unknown> = {\n stdio: ['inherit', 'inherit', 'inherit', 'ipc'],\n execArgv: [],\n env: workerEnv,\n }\n\n const child = fork(this.options.workerEntryPath, [], forkOptions as any)\n\n const worker: ManagedWorker = {\n addonId,\n process: child,\n state: 'starting',\n startedAt: Date.now(),\n lastHeartbeat: Date.now(),\n restartCount: 0,\n crashTimestamps: [],\n cpuPercent: 0,\n memoryRss: 0,\n workerToken,\n forkConfig: { addonDir, config, storagePaths, dataDir, locationPaths, workerToken },\n }\n\n this.workers.set(addonId, worker)\n\n // Route structured IPC messages from worker\n child.on('message', (msg: WorkerToMainMessage) => {\n if (msg.type === 'LOG' && this.options.onWorkerLog) {\n this.options.onWorkerLog({\n addonId,\n level: msg.level,\n message: msg.message,\n scope: ['hub', addonId],\n meta: msg.context,\n })\n } else if (msg.type === 'STATS') {\n worker.cpuPercent = msg.cpu\n worker.memoryRss = msg.memory\n }\n })\n\n // Capture stdout/stderr from worker as fallback (unstructured logs, crash output)\n child.stdout?.on('data', (chunk: Buffer) => {\n const lines = chunk.toString().trim()\n if (!lines) return\n if (this.options.onWorkerLog) {\n this.options.onWorkerLog({\n addonId,\n level: 'info',\n message: lines,\n scope: ['hub', addonId, '__stdout'],\n })\n } else {\n console.log(`[Worker:${addonId}] ${lines}`)\n }\n })\n child.stderr?.on('data', (chunk: Buffer) => {\n const lines = chunk.toString().trim()\n if (!lines) return\n if (this.options.onWorkerLog) {\n this.options.onWorkerLog({\n addonId,\n level: 'error',\n message: lines,\n scope: ['hub', addonId, '__stderr'],\n })\n } else {\n console.error(`[Worker:${addonId}:ERR] ${lines}`)\n }\n })\n\n child.on('exit', (code) => {\n this.handleWorkerExit(addonId, code, addonDir, config, storagePaths, dataDir, locationPaths, workerToken)\n })\n\n child.on('error', (err) => {\n console.error(`[WorkerHost] Worker ${addonId} error:`, err.message)\n })\n\n // No IPC INIT message — config is passed via env vars.\n // Wait for READY signal via IPC (process.send({ type: 'READY' }) from worker).\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(`Worker ${addonId} did not become ready within 30s`))\n }, 30000)\n\n const readyCheck = (msg: { type: string }) => {\n if (msg.type === 'READY') {\n clearTimeout(timeout)\n child.off('message', readyCheck)\n worker.state = 'running'\n resolve()\n }\n }\n child.on('message', readyCheck)\n })\n }\n\n /** List all workers with stats */\n listWorkers(): readonly WorkerInfo[] {\n return [...this.workers.values()].map((w) => ({\n addonId: w.addonId,\n pid: w.process.pid ?? 0,\n state: w.state,\n cpuPercent: w.cpuPercent,\n memoryRss: w.memoryRss,\n uptimeSeconds: Math.round((Date.now() - w.startedAt) / 1000),\n restartCount: w.restartCount,\n subProcesses: [],\n }))\n }\n\n /** Gracefully shutdown a single worker by addon ID */\n async shutdownWorkerById(addonId: string): Promise<void> {\n const worker = this.workers.get(addonId)\n if (!worker) {\n throw new Error(`No worker found for addon \"${addonId}\"`)\n }\n await this.shutdownWorker(addonId, worker)\n this.workers.delete(addonId)\n }\n\n /** Gracefully restart a single worker by addon ID (stop then re-fork with same config) */\n async restartWorker(addonId: string): Promise<void> {\n const worker = this.workers.get(addonId)\n if (!worker) {\n throw new Error(`No worker found for addon \"${addonId}\"`)\n }\n\n const forkConfig = worker.forkConfig\n if (!forkConfig) {\n throw new Error(`No fork config stored for addon \"${addonId}\" — cannot restart`)\n }\n\n // Gracefully stop the existing worker\n await this.shutdownWorker(addonId, worker)\n this.workers.delete(addonId)\n\n // Re-fork with the same configuration\n await this.forkWorker(\n addonId,\n forkConfig.addonDir,\n forkConfig.config,\n forkConfig.storagePaths,\n forkConfig.dataDir,\n forkConfig.locationPaths,\n forkConfig.workerToken,\n )\n }\n\n /** Gracefully shutdown all workers */\n async shutdownAll(): Promise<void> {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = null\n }\n\n const shutdowns = [...this.workers.entries()].map(([addonId, worker]) =>\n this.shutdownWorker(addonId, worker),\n )\n await Promise.allSettled(shutdowns)\n this.workers.clear()\n }\n\n /** Start heartbeat monitoring — workers now report heartbeat via tRPC, not IPC PING */\n startHeartbeatMonitoring(): void {\n // With tRPC-based workers, heartbeat is handled by the agent.heartbeat mutation.\n // Keep this method for API compatibility but no IPC pings are needed.\n this.heartbeatTimer = setInterval(() => {\n const now = Date.now()\n for (const [addonId, worker] of this.workers) {\n if (worker.state !== 'running') continue\n // Workers report heartbeat via tRPC — if no update in timeout window, mark crashed\n if (now - worker.lastHeartbeat > this.options.heartbeatTimeoutMs) {\n console.warn(`[WorkerHost] Worker ${addonId} heartbeat timeout — marking unresponsive`)\n worker.state = 'crashed'\n worker.process.kill('SIGKILL')\n }\n }\n }, this.options.heartbeatIntervalMs)\n }\n\n /** Update heartbeat timestamp for a worker (called when agent.heartbeat arrives) */\n updateWorkerHeartbeat(addonId: string, cpuPercent?: number, memoryRss?: number): void {\n const worker = this.workers.get(addonId)\n if (!worker) return\n worker.lastHeartbeat = Date.now()\n if (cpuPercent !== undefined) worker.cpuPercent = cpuPercent\n if (memoryRss !== undefined) worker.memoryRss = memoryRss\n }\n\n private handleWorkerExit(\n addonId: string,\n code: number | null,\n addonDir: string,\n config: Record<string, unknown>,\n storagePaths: Record<string, string>,\n dataDir?: string,\n locationPaths?: Record<string, string>,\n workerToken?: string,\n ): void {\n const worker = this.workers.get(addonId)\n if (!worker) return\n\n if (worker.state === 'stopping') {\n worker.state = 'stopped'\n return\n }\n\n worker.state = 'crashed'\n console.error(`[WorkerHost] Worker ${addonId} exited with code ${code}`)\n\n // Check crash rate\n const now = Date.now()\n worker.crashTimestamps.push(now)\n const recentCrashes = worker.crashTimestamps.filter(\n (t) => now - t < this.options.crashWindowMs,\n )\n worker.crashTimestamps = recentCrashes\n\n if (recentCrashes.length >= this.options.maxCrashesInWindow) {\n console.error(`[WorkerHost] Worker ${addonId} crashed ${recentCrashes.length} times in ${this.options.crashWindowMs}ms — not restarting`)\n return\n }\n\n // Restart after backoff\n const backoff = Math.min(2000 * (worker.restartCount + 1), 30000)\n console.log(`[WorkerHost] Restarting worker ${addonId} in ${backoff}ms`)\n setTimeout(() => {\n worker.restartCount++\n this.workers.delete(addonId)\n this.forkWorker(addonId, addonDir, config, storagePaths, dataDir, locationPaths, workerToken).catch((err) => {\n console.error(`[WorkerHost] Failed to restart worker ${addonId}:`, err)\n })\n }, backoff)\n }\n\n private async shutdownWorker(addonId: string, worker: ManagedWorker): Promise<void> {\n worker.state = 'stopping'\n // Send SIGTERM — worker handles graceful shutdown internally\n worker.process.kill('SIGTERM')\n\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(() => {\n console.warn(`[WorkerHost] Worker ${addonId} did not exit within ${this.options.shutdownTimeoutMs}ms — SIGKILL`)\n worker.process.kill('SIGKILL')\n resolve()\n }, this.options.shutdownTimeoutMs)\n\n worker.process.on('exit', () => {\n clearTimeout(timeout)\n resolve()\n })\n })\n }\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport type {\n WorkerToMainMessage,\n WorkerProcessStats,\n SubProcessInfo,\n} from '@camstack/types'\nimport type {\n IAddonProcessManager,\n SubProcessConfig,\n IManagedSubProcess,\n} from '@camstack/types'\n\nexport class WorkerProcessManager implements IAddonProcessManager {\n private readonly processes = new Map<number, ManagedProcess>()\n\n constructor(\n private readonly sendToMain: (msg: WorkerToMainMessage) => void,\n ) {}\n\n async spawn(config: SubProcessConfig): Promise<IManagedSubProcess> {\n const child = spawn(config.command, [...(config.args ?? [])], {\n cwd: config.cwd,\n env: config.env ? { ...process.env, ...config.env } : undefined,\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n\n const managed = new ManagedProcess(child, config, this.sendToMain)\n this.processes.set(child.pid!, managed)\n\n this.sendToMain({\n type: 'SUB_PROCESS_SPAWNED',\n pid: child.pid!,\n name: config.name,\n command: config.command,\n })\n\n child.on('exit', (code) => {\n this.sendToMain({\n type: 'SUB_PROCESS_EXITED',\n pid: child.pid!,\n code,\n })\n\n if (config.autoRestart && managed.restartCount < (config.maxRestarts ?? 3)) {\n managed.restartCount++\n setTimeout(() => {\n this.spawn(config).catch(() => {})\n }, 2000)\n }\n })\n\n return managed\n }\n\n listProcesses(): readonly SubProcessInfo[] {\n return [...this.processes.values()].map((p) => p.toInfo())\n }\n\n getWorkerStats(): WorkerProcessStats {\n const mem = process.memoryUsage()\n const cpu = process.cpuUsage()\n return {\n pid: process.pid,\n cpuPercent: (cpu.user + cpu.system) / 1_000_000,\n memoryRss: mem.rss,\n heapUsed: mem.heapUsed,\n uptimeSeconds: Math.round(process.uptime()),\n restartCount: 0,\n state: 'running',\n }\n }\n\n async killAll(): Promise<void> {\n for (const [, proc] of this.processes) {\n proc.kill('SIGTERM')\n }\n this.processes.clear()\n }\n}\n\nclass ManagedProcess implements IManagedSubProcess {\n readonly pid: number\n readonly name: string\n restartCount = 0\n private readonly startedAt = Date.now()\n private exitHandlers: Array<(code: number | null) => void> = []\n private errorHandlers: Array<(error: Error) => void> = []\n\n constructor(\n private readonly child: ChildProcess,\n private readonly config: SubProcessConfig,\n private readonly sendToMain: (msg: WorkerToMainMessage) => void,\n ) {\n this.pid = child.pid!\n this.name = config.name\n\n child.on('exit', (code) => {\n for (const handler of this.exitHandlers) handler(code)\n })\n\n child.on('error', (err) => {\n for (const handler of this.errorHandlers) handler(err)\n })\n }\n\n getStats(): WorkerProcessStats {\n return {\n pid: this.pid,\n cpuPercent: 0,\n memoryRss: 0,\n uptimeSeconds: Math.round((Date.now() - this.startedAt) / 1000),\n restartCount: this.restartCount,\n state: this.child.exitCode !== null ? 'stopped' : 'running',\n }\n }\n\n write(data: Buffer): void {\n this.child.stdin?.write(data)\n }\n\n get stdout(): AsyncIterable<Buffer> {\n return this.child.stdout as AsyncIterable<Buffer>\n }\n\n get stderr(): AsyncIterable<Buffer> {\n return this.child.stderr as AsyncIterable<Buffer>\n }\n\n kill(signal: NodeJS.Signals = 'SIGTERM'): void {\n this.child.kill(signal)\n }\n\n wait(): Promise<{ code: number | null; signal: string | null }> {\n if (this.child.exitCode !== null) {\n return Promise.resolve({ code: this.child.exitCode, signal: null })\n }\n return new Promise((resolve) => {\n this.child.on('exit', (code, signal) => {\n resolve({ code, signal })\n })\n })\n }\n\n onExit(handler: (code: number | null) => void): void {\n this.exitHandlers.push(handler)\n }\n\n onError(handler: (error: Error) => void): void {\n this.errorHandlers.push(handler)\n }\n\n toInfo(): SubProcessInfo {\n return {\n pid: this.pid,\n name: this.name,\n command: this.config.command,\n state: this.child.exitCode !== null ? 'stopped' : 'running',\n cpuPercent: 0,\n memoryRss: 0,\n uptimeSeconds: Math.round((Date.now() - this.startedAt) / 1000),\n }\n }\n}\n","// Addon loading\nexport type { RegisteredAddon } from './addon-loader.js'\nexport { AddonLoader } from './addon-loader.js'\nexport { AddonEngineManager } from './addon-engine-manager.js'\nexport type { AddonInstallerConfig, InstalledPackage } from './addon-installer.js'\nexport { AddonInstaller } from './addon-installer.js'\n\n// Shared filesystem utilities\nexport {\n copyDirRecursive,\n ensureDir,\n stripCamstackDeps,\n copyExtraFileDirs,\n symlinkAddonsToNodeModules,\n ensureLibraryBuilt,\n installPackageFromNpmSync,\n isSourceNewer,\n} from './fs-utils.js'\n\n// Workspace detection\nexport { detectWorkspacePackagesDir } from './workspace-detect.js'\n\n// Capability\nexport { CapabilityRegistry } from './capability-registry.js'\nexport { INFRA_CAPABILITIES, isInfraCapability } from './infra-capabilities.js'\nexport type { InfraCapability } from './infra-capabilities.js'\n\n// Config\nexport { ConfigManager } from './config-manager.js'\nexport type { ISettingsStore } from './config-manager.js'\nexport { bootstrapSchema, RUNTIME_DEFAULTS, DEFAULT_DATA_PATH } from './config-schema.js'\nexport type { BootstrapConfig, AppConfig, ServerMode } from './config-schema.js'\n\n// Worker (process isolation)\nexport { AddonWorkerHost, type WorkerHostOptions } from './worker/addon-worker-host.js'\nexport { WorkerProcessManager } from './worker/worker-process-manager.js'\n"],"mappings":"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","names":["AddonLoader","exports","AddonEngineManager","exports","exports","ensureDir","copyDirRecursive","stripCamstackDeps","copyExtraFileDirs","symlinkAddonsToNodeModules","isSourceNewer","ensureLibraryBuilt","installPackageFromNpmSync","AddonInstaller","exports","exports","detectWorkspacePackagesDir","CapabilityRegistry","exports","exports","isInfraCapability","exports","ConfigManager","exports","AddonWorkerHost","exports","WorkerProcessManager","exports"]}