@camstack/core 0.1.12 → 0.1.13

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/builtins/sqlite-storage/sqlite-settings-backend.ts","../src/builtins/sqlite-storage/sqlite-settings.addon.ts"],"sourcesContent":["import Database from 'better-sqlite3'\nimport { randomUUID } from 'node:crypto'\nimport type {\n ISettingsBackend,\n SettingsCollection,\n SettingsRecord,\n QueryFilter,\n TableSchema,\n TableQueryOptions,\n} from '@camstack/types'\n\n/**\n * SQLite implementation of ISettingsBackend.\n *\n * One table per collection, auto-created on first access.\n * All data stored as JSON in a TEXT column.\n * WAL mode for concurrent reads.\n */\nexport class SqliteSettingsBackend implements ISettingsBackend {\n private db: Database.Database | null = null\n private readonly ensuredTables = new Set<string>()\n private readonly structuredTables = new Set<string>()\n private readonly runtimeDefaults: Record<string, unknown>\n\n constructor(private readonly dbPath: string, runtimeDefaults?: Record<string, unknown>) {\n this.runtimeDefaults = runtimeDefaults ?? {}\n }\n\n async initialize(): Promise<void> {\n // Ensure parent directory exists\n const dir = this.dbPath.substring(0, this.dbPath.lastIndexOf('/'))\n if (dir) {\n const fs = await import('node:fs')\n fs.mkdirSync(dir, { recursive: true })\n }\n this.db = new Database(this.dbPath)\n this.db.pragma('journal_mode = WAL')\n this.db.pragma('foreign_keys = ON')\n\n // Seed system-settings on first boot\n const isEmpty = await this.isEmpty('system-settings')\n if (isEmpty) {\n await this.seedDefaults()\n }\n }\n\n async shutdown(): Promise<void> {\n this.db?.close()\n this.db = null\n }\n\n // ---------------------------------------------------------------------------\n // ISettingsBackend implementation\n // ---------------------------------------------------------------------------\n\n async get(collection: SettingsCollection, key: string): Promise<unknown> {\n this.ensureCollectionTable(collection)\n const row = this.getDb()\n .prepare<[string], { data: string }>(`SELECT data FROM \"${collection}\" WHERE id = ?`)\n .get(key)\n if (!row) return undefined\n return JSON.parse(row.data)\n }\n\n async set(collection: SettingsCollection, key: string, value: unknown): Promise<void> {\n this.ensureCollectionTable(collection)\n this.getDb()\n .prepare(`INSERT INTO \"${collection}\" (id, data) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET data = excluded.data`)\n .run(key, JSON.stringify(value))\n }\n\n async query(collection: SettingsCollection, filter?: QueryFilter): Promise<readonly SettingsRecord[]> {\n this.ensureCollectionTable(collection)\n let sql = `SELECT id, data FROM \"${collection}\"`\n const params: unknown[] = []\n\n const whereClauses: string[] = []\n\n if (filter?.where) {\n for (const [field, value] of Object.entries(filter.where)) {\n if (field === 'id') {\n whereClauses.push('id = ?')\n params.push(value)\n } else {\n whereClauses.push(`json_extract(data, '$.${field}') = ?`)\n params.push(typeof value === 'object' ? JSON.stringify(value) : value)\n }\n }\n }\n\n if (filter?.whereIn) {\n for (const [field, values] of Object.entries(filter.whereIn)) {\n const placeholders = values.map(() => '?').join(', ')\n if (field === 'id') {\n whereClauses.push(`id IN (${placeholders})`)\n } else {\n whereClauses.push(`json_extract(data, '$.${field}') IN (${placeholders})`)\n }\n params.push(...values)\n }\n }\n\n if (filter?.whereBetween) {\n for (const [field, [low, high]] of Object.entries(filter.whereBetween)) {\n if (field === 'id') {\n whereClauses.push('id BETWEEN ? AND ?')\n } else {\n whereClauses.push(`json_extract(data, '$.${field}') BETWEEN ? AND ?`)\n }\n params.push(low, high)\n }\n }\n\n if (whereClauses.length > 0) {\n sql += ` WHERE ${whereClauses.join(' AND ')}`\n }\n\n if (filter?.orderBy) {\n const dir = filter.orderBy.direction === 'desc' ? 'DESC' : 'ASC'\n if (filter.orderBy.field === 'id') {\n sql += ` ORDER BY id ${dir}`\n } else {\n sql += ` ORDER BY json_extract(data, '$.${filter.orderBy.field}') ${dir}`\n }\n }\n\n if (filter?.limit) {\n sql += ` LIMIT ?`\n params.push(filter.limit)\n }\n\n if (filter?.offset) {\n sql += ` OFFSET ?`\n params.push(filter.offset)\n }\n\n const rows = this.getDb()\n .prepare<unknown[], { id: string; data: string }>(sql)\n .all(...params)\n\n return rows.map(row => ({\n id: row.id,\n data: JSON.parse(row.data),\n }))\n }\n\n async insert(collection: SettingsCollection, record: SettingsRecord): Promise<void> {\n this.ensureCollectionTable(collection)\n const id = record.id || randomUUID()\n this.getDb()\n .prepare(`INSERT INTO \"${collection}\" (id, data) VALUES (?, ?)`)\n .run(id, JSON.stringify(record.data))\n }\n\n async update(collection: SettingsCollection, id: string, data: Record<string, unknown>): Promise<void> {\n this.ensureCollectionTable(collection)\n this.getDb()\n .prepare(`UPDATE \"${collection}\" SET data = ? WHERE id = ?`)\n .run(JSON.stringify(data), id)\n }\n\n async delete(collection: SettingsCollection, key: string): Promise<void> {\n this.ensureCollectionTable(collection)\n this.getDb()\n .prepare(`DELETE FROM \"${collection}\" WHERE id = ?`)\n .run(key)\n }\n\n async count(collection: SettingsCollection, filter?: QueryFilter): Promise<number> {\n this.ensureCollectionTable(collection)\n if (!filter) {\n const row = this.getDb()\n .prepare<[], { cnt: number }>(`SELECT COUNT(*) AS cnt FROM \"${collection}\"`)\n .get()\n return row?.cnt ?? 0\n }\n const rows = await this.query(collection, { ...filter, limit: undefined, offset: undefined })\n return rows.length\n }\n\n async isEmpty(collection: SettingsCollection): Promise<boolean> {\n this.ensureCollectionTable(collection)\n const row = this.getDb()\n .prepare<[], { cnt: number }>(`SELECT COUNT(*) AS cnt FROM \"${collection}\"`)\n .get()\n return (row?.cnt ?? 0) === 0\n }\n\n // ---------------------------------------------------------------------------\n // Legacy SettingsStore compatibility (used by ConfigManager.setSettingsStore)\n // ---------------------------------------------------------------------------\n\n /** Get a system setting by dot-notation key */\n getSystem(key: string): unknown {\n this.ensureCollectionTable('system-settings')\n const row = this.getDb()\n .prepare<[string], { data: string }>('SELECT data FROM \"system-settings\" WHERE id = ?')\n .get(key)\n if (!row) return undefined\n return JSON.parse(row.data)\n }\n\n /** Set a system setting */\n setSystem(key: string, value: unknown): void {\n this.ensureCollectionTable('system-settings')\n this.getDb()\n .prepare('INSERT INTO \"system-settings\" (id, data) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET data = excluded.data')\n .run(key, JSON.stringify(value))\n }\n\n /** Get all system settings as flat key-value */\n getAllSystem(): Record<string, unknown> {\n this.ensureCollectionTable('system-settings')\n const rows = this.getDb()\n .prepare<[], { id: string; data: string }>('SELECT id, data FROM \"system-settings\"')\n .all()\n return Object.fromEntries(rows.map(r => [r.id, JSON.parse(r.data)]))\n }\n\n /** Get all settings for an addon */\n getAllAddon(addonId: string): Record<string, unknown> {\n this.ensureCollectionTable('addon-settings')\n const rows = this.getDb()\n .prepare<[string], { id: string; data: string }>('SELECT id, data FROM \"addon-settings\" WHERE json_extract(data, \\'$.addonId\\') = ?')\n .all(addonId)\n if (rows.length === 0) return {}\n // addon-settings stores all keys for an addon as separate rows with id = \"addonId.key\"\n const result: Record<string, unknown> = {}\n for (const row of rows) {\n const parsed = JSON.parse(row.data)\n const key = row.id.startsWith(`${addonId}.`) ? row.id.slice(addonId.length + 1) : row.id\n result[key] = parsed.value ?? parsed\n }\n return result\n }\n\n /** Bulk-set all settings for an addon */\n setAllAddon(addonId: string, config: Record<string, unknown>): void {\n this.ensureCollectionTable('addon-settings')\n const db = this.getDb()\n const deleteStmt = db.prepare('DELETE FROM \"addon-settings\" WHERE id LIKE ? || \\'%\\'')\n const insertStmt = db.prepare('INSERT INTO \"addon-settings\" (id, data) VALUES (?, ?)')\n db.transaction(() => {\n deleteStmt.run(`${addonId}.`)\n for (const [key, value] of Object.entries(config)) {\n insertStmt.run(`${addonId}.${key}`, JSON.stringify({ addonId, key, value }))\n }\n })()\n }\n\n /** Get all settings for a provider */\n getAllProvider(providerId: string): Record<string, unknown> {\n return this.getAllScoped('provider-settings', providerId)\n }\n\n /** Set a provider setting */\n setProvider(providerId: string, key: string, value: unknown): void {\n this.setScopedKey('provider-settings', providerId, key, value)\n }\n\n /** Get all settings for a device */\n getAllDevice(deviceId: string): Record<string, unknown> {\n return this.getAllScoped('device-settings', deviceId)\n }\n\n /** Set a device setting */\n setDevice(deviceId: string, key: string, value: unknown): void {\n this.setScopedKey('device-settings', deviceId, key, value)\n }\n\n /** Seed system-settings with runtime defaults (first boot) */\n private async seedDefaults(): Promise<void> {\n this.ensureCollectionTable('system-settings')\n const insert = this.getDb().prepare(\n 'INSERT OR IGNORE INTO \"system-settings\" (id, data) VALUES (?, ?)',\n )\n this.getDb().transaction(() => {\n for (const [key, value] of Object.entries(this.runtimeDefaults)) {\n insert.run(key, JSON.stringify(value))\n }\n })()\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private getDb(): Database.Database {\n if (!this.db) throw new Error('SqliteSettingsBackend not initialized — call initialize() first')\n return this.db\n }\n\n private ensureCollectionTable(collection: string): void {\n if (this.ensuredTables.has(collection)) return\n this.getDb().exec(\n `CREATE TABLE IF NOT EXISTS \"${collection}\" (id TEXT PRIMARY KEY, data TEXT NOT NULL)`,\n )\n this.ensuredTables.add(collection)\n }\n\n private getAllScoped(collection: string, scopeId: string): Record<string, unknown> {\n this.ensureCollectionTable(collection)\n const rows = this.getDb()\n .prepare<[string], { id: string; data: string }>(`SELECT id, data FROM \"${collection}\" WHERE id LIKE ? || '.%'`)\n .all(scopeId)\n const result: Record<string, unknown> = {}\n for (const row of rows) {\n const key = row.id.slice(scopeId.length + 1)\n const parsed = JSON.parse(row.data)\n result[key] = parsed.value ?? parsed\n }\n return result\n }\n\n private setScopedKey(collection: string, scopeId: string, key: string, value: unknown): void {\n this.ensureCollectionTable(collection)\n this.getDb()\n .prepare(`INSERT INTO \"${collection}\" (id, data) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET data = excluded.data`)\n .run(`${scopeId}.${key}`, JSON.stringify({ scopeId, key, value }))\n }\n\n // ── Structured table operations ────────────────────────────────────\n\n async ensureTable(table: string, schema?: TableSchema): Promise<void> {\n if (!schema) {\n // Legacy: ensure JSON-blob collection table\n if (!this.ensuredTables.has(table)) {\n this.getDb().exec(`CREATE TABLE IF NOT EXISTS \"${table}\" (id TEXT PRIMARY KEY, data TEXT NOT NULL)`)\n this.ensuredTables.add(table)\n }\n return\n }\n if (this.structuredTables.has(table)) return\n\n const colDefs = schema.columns.map(col => {\n const parts = [`\"${col.name}\" ${col.type}`]\n if (col.primaryKey) parts.push('PRIMARY KEY')\n if (col.notNull) parts.push('NOT NULL')\n if (col.unique) parts.push('UNIQUE')\n if (col.defaultValue !== undefined) {\n parts.push(`DEFAULT ${typeof col.defaultValue === 'string' ? `'${col.defaultValue}'` : col.defaultValue === null ? 'NULL' : String(col.defaultValue)}`)\n }\n return parts.join(' ')\n })\n\n this.getDb().exec(`CREATE TABLE IF NOT EXISTS \"${table}\" (${colDefs.join(', ')})`)\n\n if (schema.indexes) {\n for (const idx of schema.indexes) {\n const unique = idx.unique ? 'UNIQUE ' : ''\n const cols = idx.columns.map(c => `\"${c}\"`).join(', ')\n this.getDb().exec(`CREATE ${unique}INDEX IF NOT EXISTS \"${idx.name}\" ON \"${table}\" (${cols})`)\n }\n }\n\n this.structuredTables.add(table)\n }\n\n async tableInsert(table: string, row: Record<string, unknown>): Promise<void> {\n const keys = Object.keys(row)\n const cols = keys.map(k => `\"${k}\"`).join(', ')\n const placeholders = keys.map(() => '?').join(', ')\n const values = keys.map(k => {\n const v = row[k]\n return typeof v === 'object' && v !== null ? JSON.stringify(v) : v\n })\n this.getDb().prepare(`INSERT INTO \"${table}\" (${cols}) VALUES (${placeholders})`).run(...values)\n }\n\n async tableUpdate(table: string, filter: Record<string, unknown>, updates: Record<string, unknown>): Promise<number> {\n const setClauses: string[] = []\n const setValues: unknown[] = []\n for (const [k, v] of Object.entries(updates)) {\n setClauses.push(`\"${k}\" = ?`)\n setValues.push(typeof v === 'object' && v !== null ? JSON.stringify(v) : v)\n }\n\n const { whereSql, whereValues } = this.buildWhere(filter)\n const result = this.getDb()\n .prepare(`UPDATE \"${table}\" SET ${setClauses.join(', ')}${whereSql}`)\n .run(...setValues, ...whereValues)\n return result.changes\n }\n\n async tableDelete(table: string, filter: Record<string, unknown>): Promise<number> {\n const { whereSql, whereValues } = this.buildWhere(filter)\n const result = this.getDb().prepare(`DELETE FROM \"${table}\"${whereSql}`).run(...whereValues)\n return result.changes\n }\n\n async tableQuery(table: string, options?: TableQueryOptions): Promise<readonly Record<string, unknown>[]> {\n let sql = `SELECT * FROM \"${table}\"`\n const values: unknown[] = []\n\n if (options?.where) {\n const { whereSql, whereValues } = this.buildWhere(options.where)\n sql += whereSql\n values.push(...whereValues)\n }\n\n if (options?.orderBy) {\n sql += ` ORDER BY \"${options.orderBy.field}\" ${options.orderBy.direction === 'desc' ? 'DESC' : 'ASC'}`\n }\n\n if (options?.limit !== undefined) {\n sql += ` LIMIT ?`\n values.push(options.limit)\n }\n\n if (options?.offset !== undefined) {\n sql += ` OFFSET ?`\n values.push(options.offset)\n }\n\n return this.getDb().prepare(sql).all(...values) as Record<string, unknown>[]\n }\n\n async tableGet(table: string, filter: Record<string, unknown>): Promise<Record<string, unknown> | null> {\n const { whereSql, whereValues } = this.buildWhere(filter)\n const row = this.getDb().prepare(`SELECT * FROM \"${table}\"${whereSql} LIMIT 1`).get(...whereValues)\n return (row as Record<string, unknown>) ?? null\n }\n\n async tableCount(table: string, filter?: Record<string, unknown>): Promise<number> {\n let sql = `SELECT COUNT(*) as count FROM \"${table}\"`\n const values: unknown[] = []\n\n if (filter) {\n const { whereSql, whereValues } = this.buildWhere(filter)\n sql += whereSql\n values.push(...whereValues)\n }\n\n const row = this.getDb().prepare(sql).get(...values) as { count: number }\n return row.count\n }\n\n private buildWhere(filter: Record<string, unknown>): { whereSql: string; whereValues: unknown[] } {\n const conditions: string[] = []\n const values: unknown[] = []\n for (const [k, v] of Object.entries(filter)) {\n conditions.push(`\"${k}\" = ?`)\n values.push(typeof v === 'object' && v !== null ? JSON.stringify(v) : v)\n }\n const whereSql = conditions.length > 0 ? ` WHERE ${conditions.join(' AND ')}` : ''\n return { whereSql, whereValues: values }\n }\n}\n\nexport default SqliteSettingsBackend\n","import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n IConfigurable,\n ConfigUISchema,\n CapabilityProviderMap,\n} from '@camstack/types'\nimport { SqliteSettingsBackend } from './sqlite-settings-backend'\n\n/**\n * SQLite Settings addon — provides persistent settings storage.\n * Capability: 'settings-store' (singleton)\n *\n * Depends on 'storage' capability for resolving the DB file path.\n */\nexport class SqliteSettingsAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'sqlite-settings',\n name: 'SQLite Settings',\n version: '1.0.0',\n capabilities: [{ name: 'settings-store', mode: 'singleton' }],\n }\n\n private backend: SqliteSettingsBackend | null = null\n\n async initialize(context: AddonContext): Promise<void> {\n // Resolve DB path from storage provider (addons-data location)\n const dbPath = context.storageProvider\n ? context.storageProvider.resolve('addons-data', `${context.id.replace('addon:', '')}/camstack.db`)\n : context.dataDir\n ? `${context.dataDir}/camstack.db`\n : 'camstack-data/addons-data/sqlite-settings/camstack.db'\n\n const runtimeDefaults = (context.addonConfig._runtimeDefaults as Record<string, unknown>) ?? {}\n this.backend = new SqliteSettingsBackend(dbPath, runtimeDefaults)\n await this.backend.initialize()\n context.logger.info(`SQLite settings initialized at ${dbPath}`)\n }\n\n async shutdown(): Promise<void> {\n await this.backend?.shutdown()\n }\n\n getBackend(): SqliteSettingsBackend | null {\n return this.backend\n }\n\n getCapabilityProvider<K extends keyof CapabilityProviderMap>(\n name: K,\n ): CapabilityProviderMap[K] | null {\n if (name === ('settings-store' as string) && this.backend) {\n return this.backend as unknown as CapabilityProviderMap[K]\n }\n return null\n }\n\n getConfigSchema(): ConfigUISchema {\n return { sections: [] }\n }\n\n getConfig(): Record<string, unknown> {\n return {}\n }\n\n async onConfigChange(_config: Record<string, unknown>): Promise<void> {\n // DB path changes require restart\n }\n}\n\nexport default SqliteSettingsAddon\n"],"mappings":"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","names":[]}
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { IScopedLogger, ModelDownloadOptions, ModelDownloadResult, IAddonModelManager, ModelCatalogEntry, ModelFormat, IPythonEnvironment, PythonProbeResult, PythonEnvReady, PipelineConfig, ValidationResult, FrameInput, PipelineResult, AudioChunkInput, PipelineNode, ElementState as ElementState$1, LoggerFactory, ProcessConfig, ManagedProcessStatus, INetworkQualityTracker, ClientNetworkStats, DeviceNetworkStats, ReplResult, AgentInfo, AgentEntry, AgentStatus, AgentTask, TaskDispatchOptions, AgentTaskResult, LogEntry, LogFilter, AgentRegistrationInfo, AgentToHubMessage, HubToAgentMessage, AgentRuntimeStatus, ITaskHandler, TaskContext, IEventBus, FeatureManifest, FeatureFlag, SystemEvent, EventFilter, IStorageProvider as IStorageProvider$1, ISettingsBackend, TokenScope, ScopedToken, TokenPayload, UserRole, UserRecord, INotificationOutput, Notification, Toast, IAddonRouteProvider, IAddonHttpRoute, PlatformCapabilities, HardwareInfo, PlatformScore, ModelRequirement, ResolvedInferenceConfig, ProviderListItem } from '@camstack/types';
1
+ import { IScopedLogger, ModelDownloadOptions, ModelDownloadResult, IAddonModelManager, ModelCatalogEntry, ModelFormat, IPythonEnvironment, PythonProbeResult, PythonEnvReady, PipelineConfig, ValidationResult, FrameInput, PipelineResult, AudioChunkInput, PipelineNode, ElementState as ElementState$1, LoggerFactory, ProcessConfig, ManagedProcessStatus, INetworkQualityTracker, ClientNetworkStats, DeviceNetworkStats, ReplResult, AgentInfo, AgentEntry, AgentStatus, AgentTask, TaskDispatchOptions, AgentTaskResult, LogEntry, LogFilter, AgentRegistrationInfo, AgentToHubMessage, HubToAgentMessage, AgentRuntimeStatus, ITaskHandler, TaskContext, IEventBus, FeatureManifest, FeatureFlag, SystemEvent, EventFilter, IStorageProvider as IStorageProvider$1, ISettingsBackend, TokenScope, ScopedToken, TokenPayload, UserRole, UserRecord, INotificationOutput, Notification, Toast, IAddonRouteProvider, IAddonHttpRoute, PlatformCapabilities, HardwareInfo, PlatformScore, ModelRequirement, ResolvedInferenceConfig, IIntegrationRegistry, CreateIntegrationInput, Integration, CreateDeviceInput, Device, ProviderListItem } from '@camstack/types';
2
2
  export { AgentEntry, AgentInfo, AgentResources, AgentStatus, AgentTask, AgentTaskResult, BackupManifest, ClientNetworkStats, DeviceNetworkStats, EventFilter, EventSource, FeatureFlag, FeatureManifest, INetworkQualityTracker, IScopedLogger, LogEntry, LogFilter, LogLevel, ManagedProcessStatus, ProcessConfig, ProcessStats, ProviderListItem, ReplResult, StreamNetworkStats, SystemEvent, TaskDispatchOptions, TokenPayload, UserRecord, UserRole, ValidationIssue, ValidationResult } from '@camstack/types';
3
3
  import { ChildProcess } from 'node:child_process';
4
4
  import { AddonLoader, AddonEngineManager, CapabilityRegistry } from '@camstack/kernel';
@@ -1055,6 +1055,33 @@ declare class InferenceConfigResolver {
1055
1055
  resolve(requirements: readonly ModelRequirement[]): ResolvedInferenceConfig;
1056
1056
  }
1057
1057
 
1058
+ declare class IntegrationRegistry implements IIntegrationRegistry {
1059
+ private readonly backend;
1060
+ constructor(backend: ISettingsBackend);
1061
+ initialize(): Promise<void>;
1062
+ createIntegration(input: CreateIntegrationInput): Integration;
1063
+ getIntegration(id: string): Integration | null;
1064
+ getIntegrationByAddonId(addonId: string): Integration | null;
1065
+ listIntegrations(): readonly Integration[];
1066
+ updateIntegration(id: string, updates: Partial<Pick<Integration, 'name' | 'enabled' | 'info'>>): Integration | null;
1067
+ deleteIntegration(id: string): boolean;
1068
+ getIntegrationSettings(integrationId: string): Record<string, unknown>;
1069
+ setIntegrationSetting(integrationId: string, key: string, value: unknown): void;
1070
+ setIntegrationSettings(integrationId: string, settings: Record<string, unknown>): void;
1071
+ createDevice(input: CreateDeviceInput): Device;
1072
+ getDevice(id: string): Device | null;
1073
+ getDeviceByStableId(stableId: string): Device | null;
1074
+ listDevices(integrationId?: string): readonly Device[];
1075
+ listCameras(): readonly Device[];
1076
+ updateDevice(id: string, updates: Partial<Pick<Device, 'name' | 'enabled' | 'info'>>): Device | null;
1077
+ deleteDevice(id: string): boolean;
1078
+ getDeviceSettings(deviceId: string): Record<string, unknown>;
1079
+ setDeviceSetting(deviceId: string, key: string, value: unknown): void;
1080
+ setDeviceSettings(deviceId: string, settings: Record<string, unknown>): void;
1081
+ private mapIntegration;
1082
+ private mapDevice;
1083
+ }
1084
+
1058
1085
  /** Minimal device registry interface for provider management */
1059
1086
  type ProviderDeviceRegistry = {
1060
1087
  registerProviderDevices(providerId: string, devices: readonly IRegistrableDevice[]): void;
@@ -1107,4 +1134,4 @@ declare class ProviderManager<P extends IManagedProvider = IManagedProvider> {
1107
1134
  shutdownAll(): Promise<void>;
1108
1135
  }
1109
1136
 
1110
- export { AddonApiFactory, AddonRouteRegistry, AgentClient, type AgentClientConfig, type AgentEventEmitter, AgentRegistry, AgentTaskRunner, ApiKeyManager, type ApiKeyRecord, type ApiKeyStorageAccess, type AuthConfigReader, AuthManager, type BinaryHandler, type CapabilityBinding, CapabilityResolver, type CertOptions, type ConnectionHandler, DecodeTaskHandler, DetectTaskHandler, DeviceRegistry, type DirectCallerOptions, type ElementState, type ElementStatus, type EnsureTlsResult, EventBus, type FeatureConfigReader, FeatureManager, FsStorageBackend, type IAddonRegistryAccess, type IStorageProvider as ICoreStorageProvider, type IDeviceCapability, type IFileStorage, type ILogDestination, type IManagedProvider, type IRegisteredDevice, type IReplContextProvider, type IReplEngine, type IResolvableDevice, type IStorageBackend, type IStorageLocation, type IStructuredStorage, InferenceConfigResolver, LifecycleStateMachine, LogManager, LogRingBuffer, ManagedProcess, type MessageHandler, ModelDownloadService, NetworkQualityTracker, NotificationService, PYTHON_VERSION, PipelineRunner, PipelineValidator, PlatformScorer, type ProcessEventEmitter, type ProcessLoggerFactory, ProcessManager, type ProcessState, type ProviderDeviceRegistry, ProviderManager, PythonEnvManager, type QueryFilter, RecordTaskHandler, ReplEngine, type ReplScope, type ReplSessionContext, ScopedLogger, ScopedTokenManager, StorageLocationManager, type StorageLocationName, StorageManager, type StorageRecord, SystemEventBus, TaskDispatcher, type TlsCertPair, ToastService, type Unsubscribe, type UserConfigReader, UserManager, type UserStorageAccess, type WssClientOptions, buildBinaryPath, downloadBinary, downloadFile, downloadModel, ensureBinary, ensureFfmpeg, ensurePython, ensureTlsCert, fetchJson, findInPath, getFfmpegDownloadUrl, getPlatformInfo, getPythonDownloadUrl, installPythonPackages, loadTlsCert };
1137
+ export { AddonApiFactory, AddonRouteRegistry, AgentClient, type AgentClientConfig, type AgentEventEmitter, AgentRegistry, AgentTaskRunner, ApiKeyManager, type ApiKeyRecord, type ApiKeyStorageAccess, type AuthConfigReader, AuthManager, type BinaryHandler, type CapabilityBinding, CapabilityResolver, type CertOptions, type ConnectionHandler, DecodeTaskHandler, DetectTaskHandler, DeviceRegistry, type DirectCallerOptions, type ElementState, type ElementStatus, type EnsureTlsResult, EventBus, type FeatureConfigReader, FeatureManager, FsStorageBackend, type IAddonRegistryAccess, type IStorageProvider as ICoreStorageProvider, type IDeviceCapability, type IFileStorage, type ILogDestination, type IManagedProvider, type IRegisteredDevice, type IReplContextProvider, type IReplEngine, type IResolvableDevice, type IStorageBackend, type IStorageLocation, type IStructuredStorage, InferenceConfigResolver, IntegrationRegistry, LifecycleStateMachine, LogManager, LogRingBuffer, ManagedProcess, type MessageHandler, ModelDownloadService, NetworkQualityTracker, NotificationService, PYTHON_VERSION, PipelineRunner, PipelineValidator, PlatformScorer, type ProcessEventEmitter, type ProcessLoggerFactory, ProcessManager, type ProcessState, type ProviderDeviceRegistry, ProviderManager, PythonEnvManager, type QueryFilter, RecordTaskHandler, ReplEngine, type ReplScope, type ReplSessionContext, ScopedLogger, ScopedTokenManager, StorageLocationManager, type StorageLocationName, StorageManager, type StorageRecord, SystemEventBus, TaskDispatcher, type TlsCertPair, ToastService, type Unsubscribe, type UserConfigReader, UserManager, type UserStorageAccess, type WssClientOptions, buildBinaryPath, downloadBinary, downloadFile, downloadModel, ensureBinary, ensureFfmpeg, ensurePython, ensureTlsCert, fetchJson, findInPath, getFfmpegDownloadUrl, getPlatformInfo, getPythonDownloadUrl, installPythonPackages, loadTlsCert };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { IScopedLogger, ModelDownloadOptions, ModelDownloadResult, IAddonModelManager, ModelCatalogEntry, ModelFormat, IPythonEnvironment, PythonProbeResult, PythonEnvReady, PipelineConfig, ValidationResult, FrameInput, PipelineResult, AudioChunkInput, PipelineNode, ElementState as ElementState$1, LoggerFactory, ProcessConfig, ManagedProcessStatus, INetworkQualityTracker, ClientNetworkStats, DeviceNetworkStats, ReplResult, AgentInfo, AgentEntry, AgentStatus, AgentTask, TaskDispatchOptions, AgentTaskResult, LogEntry, LogFilter, AgentRegistrationInfo, AgentToHubMessage, HubToAgentMessage, AgentRuntimeStatus, ITaskHandler, TaskContext, IEventBus, FeatureManifest, FeatureFlag, SystemEvent, EventFilter, IStorageProvider as IStorageProvider$1, ISettingsBackend, TokenScope, ScopedToken, TokenPayload, UserRole, UserRecord, INotificationOutput, Notification, Toast, IAddonRouteProvider, IAddonHttpRoute, PlatformCapabilities, HardwareInfo, PlatformScore, ModelRequirement, ResolvedInferenceConfig, ProviderListItem } from '@camstack/types';
1
+ import { IScopedLogger, ModelDownloadOptions, ModelDownloadResult, IAddonModelManager, ModelCatalogEntry, ModelFormat, IPythonEnvironment, PythonProbeResult, PythonEnvReady, PipelineConfig, ValidationResult, FrameInput, PipelineResult, AudioChunkInput, PipelineNode, ElementState as ElementState$1, LoggerFactory, ProcessConfig, ManagedProcessStatus, INetworkQualityTracker, ClientNetworkStats, DeviceNetworkStats, ReplResult, AgentInfo, AgentEntry, AgentStatus, AgentTask, TaskDispatchOptions, AgentTaskResult, LogEntry, LogFilter, AgentRegistrationInfo, AgentToHubMessage, HubToAgentMessage, AgentRuntimeStatus, ITaskHandler, TaskContext, IEventBus, FeatureManifest, FeatureFlag, SystemEvent, EventFilter, IStorageProvider as IStorageProvider$1, ISettingsBackend, TokenScope, ScopedToken, TokenPayload, UserRole, UserRecord, INotificationOutput, Notification, Toast, IAddonRouteProvider, IAddonHttpRoute, PlatformCapabilities, HardwareInfo, PlatformScore, ModelRequirement, ResolvedInferenceConfig, IIntegrationRegistry, CreateIntegrationInput, Integration, CreateDeviceInput, Device, ProviderListItem } from '@camstack/types';
2
2
  export { AgentEntry, AgentInfo, AgentResources, AgentStatus, AgentTask, AgentTaskResult, BackupManifest, ClientNetworkStats, DeviceNetworkStats, EventFilter, EventSource, FeatureFlag, FeatureManifest, INetworkQualityTracker, IScopedLogger, LogEntry, LogFilter, LogLevel, ManagedProcessStatus, ProcessConfig, ProcessStats, ProviderListItem, ReplResult, StreamNetworkStats, SystemEvent, TaskDispatchOptions, TokenPayload, UserRecord, UserRole, ValidationIssue, ValidationResult } from '@camstack/types';
3
3
  import { ChildProcess } from 'node:child_process';
4
4
  import { AddonLoader, AddonEngineManager, CapabilityRegistry } from '@camstack/kernel';
@@ -1055,6 +1055,33 @@ declare class InferenceConfigResolver {
1055
1055
  resolve(requirements: readonly ModelRequirement[]): ResolvedInferenceConfig;
1056
1056
  }
1057
1057
 
1058
+ declare class IntegrationRegistry implements IIntegrationRegistry {
1059
+ private readonly backend;
1060
+ constructor(backend: ISettingsBackend);
1061
+ initialize(): Promise<void>;
1062
+ createIntegration(input: CreateIntegrationInput): Integration;
1063
+ getIntegration(id: string): Integration | null;
1064
+ getIntegrationByAddonId(addonId: string): Integration | null;
1065
+ listIntegrations(): readonly Integration[];
1066
+ updateIntegration(id: string, updates: Partial<Pick<Integration, 'name' | 'enabled' | 'info'>>): Integration | null;
1067
+ deleteIntegration(id: string): boolean;
1068
+ getIntegrationSettings(integrationId: string): Record<string, unknown>;
1069
+ setIntegrationSetting(integrationId: string, key: string, value: unknown): void;
1070
+ setIntegrationSettings(integrationId: string, settings: Record<string, unknown>): void;
1071
+ createDevice(input: CreateDeviceInput): Device;
1072
+ getDevice(id: string): Device | null;
1073
+ getDeviceByStableId(stableId: string): Device | null;
1074
+ listDevices(integrationId?: string): readonly Device[];
1075
+ listCameras(): readonly Device[];
1076
+ updateDevice(id: string, updates: Partial<Pick<Device, 'name' | 'enabled' | 'info'>>): Device | null;
1077
+ deleteDevice(id: string): boolean;
1078
+ getDeviceSettings(deviceId: string): Record<string, unknown>;
1079
+ setDeviceSetting(deviceId: string, key: string, value: unknown): void;
1080
+ setDeviceSettings(deviceId: string, settings: Record<string, unknown>): void;
1081
+ private mapIntegration;
1082
+ private mapDevice;
1083
+ }
1084
+
1058
1085
  /** Minimal device registry interface for provider management */
1059
1086
  type ProviderDeviceRegistry = {
1060
1087
  registerProviderDevices(providerId: string, devices: readonly IRegistrableDevice[]): void;
@@ -1107,4 +1134,4 @@ declare class ProviderManager<P extends IManagedProvider = IManagedProvider> {
1107
1134
  shutdownAll(): Promise<void>;
1108
1135
  }
1109
1136
 
1110
- export { AddonApiFactory, AddonRouteRegistry, AgentClient, type AgentClientConfig, type AgentEventEmitter, AgentRegistry, AgentTaskRunner, ApiKeyManager, type ApiKeyRecord, type ApiKeyStorageAccess, type AuthConfigReader, AuthManager, type BinaryHandler, type CapabilityBinding, CapabilityResolver, type CertOptions, type ConnectionHandler, DecodeTaskHandler, DetectTaskHandler, DeviceRegistry, type DirectCallerOptions, type ElementState, type ElementStatus, type EnsureTlsResult, EventBus, type FeatureConfigReader, FeatureManager, FsStorageBackend, type IAddonRegistryAccess, type IStorageProvider as ICoreStorageProvider, type IDeviceCapability, type IFileStorage, type ILogDestination, type IManagedProvider, type IRegisteredDevice, type IReplContextProvider, type IReplEngine, type IResolvableDevice, type IStorageBackend, type IStorageLocation, type IStructuredStorage, InferenceConfigResolver, LifecycleStateMachine, LogManager, LogRingBuffer, ManagedProcess, type MessageHandler, ModelDownloadService, NetworkQualityTracker, NotificationService, PYTHON_VERSION, PipelineRunner, PipelineValidator, PlatformScorer, type ProcessEventEmitter, type ProcessLoggerFactory, ProcessManager, type ProcessState, type ProviderDeviceRegistry, ProviderManager, PythonEnvManager, type QueryFilter, RecordTaskHandler, ReplEngine, type ReplScope, type ReplSessionContext, ScopedLogger, ScopedTokenManager, StorageLocationManager, type StorageLocationName, StorageManager, type StorageRecord, SystemEventBus, TaskDispatcher, type TlsCertPair, ToastService, type Unsubscribe, type UserConfigReader, UserManager, type UserStorageAccess, type WssClientOptions, buildBinaryPath, downloadBinary, downloadFile, downloadModel, ensureBinary, ensureFfmpeg, ensurePython, ensureTlsCert, fetchJson, findInPath, getFfmpegDownloadUrl, getPlatformInfo, getPythonDownloadUrl, installPythonPackages, loadTlsCert };
1137
+ export { AddonApiFactory, AddonRouteRegistry, AgentClient, type AgentClientConfig, type AgentEventEmitter, AgentRegistry, AgentTaskRunner, ApiKeyManager, type ApiKeyRecord, type ApiKeyStorageAccess, type AuthConfigReader, AuthManager, type BinaryHandler, type CapabilityBinding, CapabilityResolver, type CertOptions, type ConnectionHandler, DecodeTaskHandler, DetectTaskHandler, DeviceRegistry, type DirectCallerOptions, type ElementState, type ElementStatus, type EnsureTlsResult, EventBus, type FeatureConfigReader, FeatureManager, FsStorageBackend, type IAddonRegistryAccess, type IStorageProvider as ICoreStorageProvider, type IDeviceCapability, type IFileStorage, type ILogDestination, type IManagedProvider, type IRegisteredDevice, type IReplContextProvider, type IReplEngine, type IResolvableDevice, type IStorageBackend, type IStorageLocation, type IStructuredStorage, InferenceConfigResolver, IntegrationRegistry, LifecycleStateMachine, LogManager, LogRingBuffer, ManagedProcess, type MessageHandler, ModelDownloadService, NetworkQualityTracker, NotificationService, PYTHON_VERSION, PipelineRunner, PipelineValidator, PlatformScorer, type ProcessEventEmitter, type ProcessLoggerFactory, ProcessManager, type ProcessState, type ProviderDeviceRegistry, ProviderManager, PythonEnvManager, type QueryFilter, RecordTaskHandler, ReplEngine, type ReplScope, type ReplSessionContext, ScopedLogger, ScopedTokenManager, StorageLocationManager, type StorageLocationName, StorageManager, type StorageRecord, SystemEventBus, TaskDispatcher, type TlsCertPair, ToastService, type Unsubscribe, type UserConfigReader, UserManager, type UserStorageAccess, type WssClientOptions, buildBinaryPath, downloadBinary, downloadFile, downloadModel, ensureBinary, ensureFfmpeg, ensurePython, ensureTlsCert, fetchJson, findInPath, getFfmpegDownloadUrl, getPlatformInfo, getPythonDownloadUrl, installPythonPackages, loadTlsCert };
package/dist/index.js CHANGED
@@ -2375,6 +2375,7 @@ __export(src_exports, {
2375
2375
  FileSystemStorage: () => FileSystemStorage,
2376
2376
  FsStorageBackend: () => FsStorageBackend,
2377
2377
  InferenceConfigResolver: () => InferenceConfigResolver,
2378
+ IntegrationRegistry: () => IntegrationRegistry,
2378
2379
  LifecycleStateMachine: () => LifecycleStateMachine,
2379
2380
  LocalBackupAddon: () => LocalBackupAddon,
2380
2381
  LocalBackupService: () => LocalBackupService,
@@ -6604,6 +6605,320 @@ var InferenceConfigResolver = class {
6604
6605
  }
6605
6606
  };
6606
6607
 
6608
+ // src/builtins/sqlite-storage/integration-registry.ts
6609
+ function serializeSetting(value) {
6610
+ if (typeof value === "number") return { value: String(value), valueType: "number" };
6611
+ if (typeof value === "boolean") return { value: String(value), valueType: "boolean" };
6612
+ if (typeof value === "string") return { value, valueType: "string" };
6613
+ return { value: JSON.stringify(value), valueType: "json" };
6614
+ }
6615
+ function deserializeSetting(value, valueType) {
6616
+ switch (valueType) {
6617
+ case "number":
6618
+ return Number(value);
6619
+ case "boolean":
6620
+ return value === "true";
6621
+ case "json":
6622
+ try {
6623
+ return JSON.parse(value);
6624
+ } catch {
6625
+ return value;
6626
+ }
6627
+ default:
6628
+ return value;
6629
+ }
6630
+ }
6631
+ var integrationCounter = 0;
6632
+ var deviceCounter = 0;
6633
+ function nextIntegrationId() {
6634
+ return `int_${String(++integrationCounter).padStart(4, "0")}`;
6635
+ }
6636
+ function nextDeviceId() {
6637
+ return `dev_${String(++deviceCounter).padStart(4, "0")}`;
6638
+ }
6639
+ var INTEGRATIONS_SCHEMA = {
6640
+ columns: [
6641
+ { name: "id", type: "TEXT", primaryKey: true },
6642
+ { name: "addon_id", type: "TEXT", notNull: true },
6643
+ { name: "name", type: "TEXT", notNull: true },
6644
+ { name: "enabled", type: "INTEGER", notNull: true, defaultValue: 1 },
6645
+ { name: "info", type: "TEXT", notNull: true, defaultValue: "{}" },
6646
+ { name: "created_at", type: "INTEGER", notNull: true },
6647
+ { name: "updated_at", type: "INTEGER", notNull: true }
6648
+ ],
6649
+ indexes: [
6650
+ { name: "idx_integrations_addon", columns: ["addon_id"] }
6651
+ ]
6652
+ };
6653
+ var INTEGRATION_SETTINGS_SCHEMA = {
6654
+ columns: [
6655
+ { name: "integration_id", type: "TEXT", notNull: true },
6656
+ { name: "key", type: "TEXT", notNull: true },
6657
+ { name: "value", type: "TEXT", notNull: true },
6658
+ { name: "value_type", type: "TEXT", notNull: true, defaultValue: "string" }
6659
+ ]
6660
+ };
6661
+ var DEVICES_SCHEMA = {
6662
+ columns: [
6663
+ { name: "id", type: "TEXT", primaryKey: true },
6664
+ { name: "integration_id", type: "TEXT", notNull: true },
6665
+ { name: "stable_id", type: "TEXT", notNull: true, unique: true },
6666
+ { name: "type", type: "TEXT", notNull: true, defaultValue: "camera" },
6667
+ { name: "name", type: "TEXT", notNull: true },
6668
+ { name: "enabled", type: "INTEGER", notNull: true, defaultValue: 1 },
6669
+ { name: "info", type: "TEXT", notNull: true, defaultValue: "{}" },
6670
+ { name: "created_at", type: "INTEGER", notNull: true },
6671
+ { name: "updated_at", type: "INTEGER", notNull: true }
6672
+ ],
6673
+ indexes: [
6674
+ { name: "idx_devices_integration", columns: ["integration_id"] },
6675
+ { name: "idx_devices_stable", columns: ["stable_id"], unique: true },
6676
+ { name: "idx_devices_type", columns: ["type"] }
6677
+ ]
6678
+ };
6679
+ var DEVICE_SETTINGS_SCHEMA = {
6680
+ columns: [
6681
+ { name: "device_id", type: "TEXT", notNull: true },
6682
+ { name: "key", type: "TEXT", notNull: true },
6683
+ { name: "value", type: "TEXT", notNull: true },
6684
+ { name: "value_type", type: "TEXT", notNull: true, defaultValue: "string" }
6685
+ ]
6686
+ };
6687
+ var IntegrationRegistry = class {
6688
+ backend;
6689
+ constructor(backend) {
6690
+ this.backend = backend;
6691
+ }
6692
+ async initialize() {
6693
+ await this.backend.ensureTable?.("integrations", INTEGRATIONS_SCHEMA);
6694
+ await this.backend.ensureTable?.("integration_settings", INTEGRATION_SETTINGS_SCHEMA);
6695
+ await this.backend.ensureTable?.("devices", DEVICES_SCHEMA);
6696
+ await this.backend.ensureTable?.("device_settings_kv", DEVICE_SETTINGS_SCHEMA);
6697
+ const ints = await this.backend.tableQuery?.("integrations", { orderBy: { field: "id", direction: "desc" }, limit: 1 }) ?? [];
6698
+ if (ints[0]) {
6699
+ const num = parseInt(String(ints[0]["id"]).replace("int_", ""), 10);
6700
+ if (!isNaN(num)) integrationCounter = num;
6701
+ }
6702
+ const devs = await this.backend.tableQuery?.("devices", { orderBy: { field: "id", direction: "desc" }, limit: 1 }) ?? [];
6703
+ if (devs[0]) {
6704
+ const num = parseInt(String(devs[0]["id"]).replace("dev_", ""), 10);
6705
+ if (!isNaN(num)) deviceCounter = num;
6706
+ }
6707
+ }
6708
+ // --- Integrations ---
6709
+ createIntegration(input) {
6710
+ const id = nextIntegrationId();
6711
+ const now = Math.floor(Date.now() / 1e3);
6712
+ void this.backend.tableInsert?.("integrations", {
6713
+ id,
6714
+ addon_id: input.addonId,
6715
+ name: input.name,
6716
+ enabled: input.enabled !== false ? 1 : 0,
6717
+ info: JSON.stringify(input.info ?? {}),
6718
+ created_at: now,
6719
+ updated_at: now
6720
+ });
6721
+ if (input.settings) {
6722
+ this.setIntegrationSettings(id, input.settings);
6723
+ }
6724
+ return {
6725
+ id,
6726
+ addonId: input.addonId,
6727
+ name: input.name,
6728
+ enabled: input.enabled !== false,
6729
+ info: input.info ?? {},
6730
+ createdAt: now,
6731
+ updatedAt: now
6732
+ };
6733
+ }
6734
+ getIntegration(id) {
6735
+ let result = null;
6736
+ void this.backend.tableGet?.("integrations", { id }).then((row) => {
6737
+ if (row) result = this.mapIntegration(row);
6738
+ });
6739
+ return result;
6740
+ }
6741
+ getIntegrationByAddonId(addonId) {
6742
+ let result = null;
6743
+ void this.backend.tableGet?.("integrations", { addon_id: addonId }).then((row) => {
6744
+ if (row) result = this.mapIntegration(row);
6745
+ });
6746
+ return result;
6747
+ }
6748
+ listIntegrations() {
6749
+ let result = [];
6750
+ void this.backend.tableQuery?.("integrations", { orderBy: { field: "created_at", direction: "asc" } }).then((rows) => {
6751
+ result = rows.map((r) => this.mapIntegration(r));
6752
+ });
6753
+ return result;
6754
+ }
6755
+ updateIntegration(id, updates) {
6756
+ const updateRow = { updated_at: Math.floor(Date.now() / 1e3) };
6757
+ if (updates.name !== void 0) updateRow["name"] = updates.name;
6758
+ if (updates.enabled !== void 0) updateRow["enabled"] = updates.enabled ? 1 : 0;
6759
+ if (updates.info !== void 0) updateRow["info"] = JSON.stringify(updates.info);
6760
+ void this.backend.tableUpdate?.("integrations", { id }, updateRow);
6761
+ return this.getIntegration(id);
6762
+ }
6763
+ deleteIntegration(id) {
6764
+ const devices = this.listDevices(id);
6765
+ for (const d of devices) {
6766
+ void this.backend.tableDelete?.("device_settings_kv", { device_id: d.id });
6767
+ }
6768
+ void this.backend.tableDelete?.("devices", { integration_id: id });
6769
+ void this.backend.tableDelete?.("integration_settings", { integration_id: id });
6770
+ void this.backend.tableDelete?.("integrations", { id });
6771
+ return true;
6772
+ }
6773
+ // --- Integration Settings ---
6774
+ getIntegrationSettings(integrationId) {
6775
+ const result = {};
6776
+ void this.backend.tableQuery?.("integration_settings", { where: { integration_id: integrationId } }).then((rows) => {
6777
+ for (const row of rows) {
6778
+ result[String(row["key"])] = deserializeSetting(String(row["value"]), String(row["value_type"]));
6779
+ }
6780
+ });
6781
+ return result;
6782
+ }
6783
+ setIntegrationSetting(integrationId, key, value) {
6784
+ const s = serializeSetting(value);
6785
+ void this.backend.tableDelete?.("integration_settings", { integration_id: integrationId, key });
6786
+ void this.backend.tableInsert?.("integration_settings", {
6787
+ integration_id: integrationId,
6788
+ key,
6789
+ value: s.value,
6790
+ value_type: s.valueType
6791
+ });
6792
+ }
6793
+ setIntegrationSettings(integrationId, settings) {
6794
+ for (const [key, value] of Object.entries(settings)) {
6795
+ this.setIntegrationSetting(integrationId, key, value);
6796
+ }
6797
+ }
6798
+ // --- Devices ---
6799
+ createDevice(input) {
6800
+ const id = nextDeviceId();
6801
+ const now = Math.floor(Date.now() / 1e3);
6802
+ void this.backend.tableInsert?.("devices", {
6803
+ id,
6804
+ integration_id: input.integrationId,
6805
+ stable_id: input.stableId,
6806
+ type: input.type,
6807
+ name: input.name,
6808
+ enabled: input.enabled !== false ? 1 : 0,
6809
+ info: JSON.stringify(input.info ?? {}),
6810
+ created_at: now,
6811
+ updated_at: now
6812
+ });
6813
+ if (input.settings) {
6814
+ this.setDeviceSettings(id, input.settings);
6815
+ }
6816
+ return {
6817
+ id,
6818
+ integrationId: input.integrationId,
6819
+ stableId: input.stableId,
6820
+ type: input.type,
6821
+ name: input.name,
6822
+ enabled: input.enabled !== false,
6823
+ info: input.info ?? {},
6824
+ createdAt: now,
6825
+ updatedAt: now
6826
+ };
6827
+ }
6828
+ getDevice(id) {
6829
+ let result = null;
6830
+ void this.backend.tableGet?.("devices", { id }).then((row) => {
6831
+ if (row) result = this.mapDevice(row);
6832
+ });
6833
+ return result;
6834
+ }
6835
+ getDeviceByStableId(stableId) {
6836
+ let result = null;
6837
+ void this.backend.tableGet?.("devices", { stable_id: stableId }).then((row) => {
6838
+ if (row) result = this.mapDevice(row);
6839
+ });
6840
+ return result;
6841
+ }
6842
+ listDevices(integrationId) {
6843
+ let result = [];
6844
+ const options = integrationId ? { where: { integration_id: integrationId }, orderBy: { field: "created_at", direction: "asc" } } : { orderBy: { field: "created_at", direction: "asc" } };
6845
+ void this.backend.tableQuery?.("devices", options).then((rows) => {
6846
+ result = rows.map((r) => this.mapDevice(r));
6847
+ });
6848
+ return result;
6849
+ }
6850
+ listCameras() {
6851
+ let result = [];
6852
+ void this.backend.tableQuery?.("devices", { where: { type: "camera" }, orderBy: { field: "created_at", direction: "asc" } }).then((rows) => {
6853
+ result = rows.map((r) => this.mapDevice(r));
6854
+ });
6855
+ return result;
6856
+ }
6857
+ updateDevice(id, updates) {
6858
+ const updateRow = { updated_at: Math.floor(Date.now() / 1e3) };
6859
+ if (updates.name !== void 0) updateRow["name"] = updates.name;
6860
+ if (updates.enabled !== void 0) updateRow["enabled"] = updates.enabled ? 1 : 0;
6861
+ if (updates.info !== void 0) updateRow["info"] = JSON.stringify(updates.info);
6862
+ void this.backend.tableUpdate?.("devices", { id }, updateRow);
6863
+ return this.getDevice(id);
6864
+ }
6865
+ deleteDevice(id) {
6866
+ void this.backend.tableDelete?.("device_settings_kv", { device_id: id });
6867
+ void this.backend.tableDelete?.("devices", { id });
6868
+ return true;
6869
+ }
6870
+ // --- Device Settings ---
6871
+ getDeviceSettings(deviceId) {
6872
+ const result = {};
6873
+ void this.backend.tableQuery?.("device_settings_kv", { where: { device_id: deviceId } }).then((rows) => {
6874
+ for (const row of rows) {
6875
+ result[String(row["key"])] = deserializeSetting(String(row["value"]), String(row["value_type"]));
6876
+ }
6877
+ });
6878
+ return result;
6879
+ }
6880
+ setDeviceSetting(deviceId, key, value) {
6881
+ const s = serializeSetting(value);
6882
+ void this.backend.tableDelete?.("device_settings_kv", { device_id: deviceId, key });
6883
+ void this.backend.tableInsert?.("device_settings_kv", {
6884
+ device_id: deviceId,
6885
+ key,
6886
+ value: s.value,
6887
+ value_type: s.valueType
6888
+ });
6889
+ }
6890
+ setDeviceSettings(deviceId, settings) {
6891
+ for (const [key, value] of Object.entries(settings)) {
6892
+ this.setDeviceSetting(deviceId, key, value);
6893
+ }
6894
+ }
6895
+ // --- Mappers ---
6896
+ mapIntegration(row) {
6897
+ return {
6898
+ id: String(row["id"]),
6899
+ addonId: String(row["addon_id"]),
6900
+ name: String(row["name"]),
6901
+ enabled: row["enabled"] === 1,
6902
+ info: typeof row["info"] === "string" ? JSON.parse(row["info"]) : {},
6903
+ createdAt: Number(row["created_at"]),
6904
+ updatedAt: Number(row["updated_at"])
6905
+ };
6906
+ }
6907
+ mapDevice(row) {
6908
+ return {
6909
+ id: String(row["id"]),
6910
+ integrationId: String(row["integration_id"]),
6911
+ stableId: String(row["stable_id"]),
6912
+ type: String(row["type"]),
6913
+ name: String(row["name"]),
6914
+ enabled: row["enabled"] === 1,
6915
+ info: typeof row["info"] === "string" ? JSON.parse(row["info"]) : {},
6916
+ createdAt: Number(row["created_at"]),
6917
+ updatedAt: Number(row["updated_at"])
6918
+ };
6919
+ }
6920
+ };
6921
+
6607
6922
  // src/provider/provider-manager.ts
6608
6923
  var import_node_crypto9 = require("crypto");
6609
6924
  var ProviderManager = class {
@@ -6759,6 +7074,7 @@ var ProviderManager = class {
6759
7074
  FileSystemStorage,
6760
7075
  FsStorageBackend,
6761
7076
  InferenceConfigResolver,
7077
+ IntegrationRegistry,
6762
7078
  LifecycleStateMachine,
6763
7079
  LocalBackupAddon,
6764
7080
  LocalBackupService,