@camstack/types 1.2.20 → 1.2.22

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 CHANGED
@@ -964,14 +964,7 @@ var RecordingConfigSchema = zod.z.object({
964
964
  * windows only — existing sheets are immutable, and each window's index
965
965
  * carries its own tile dims so mixed-preset history renders correctly.
966
966
  */
967
- scrubThumbnails: ScrubThumbnailPresetSchema.optional(),
968
- /**
969
- * OPT-IN thumbnail-strip generation for this camera: every keyframe of the
970
- * low recording saved as a JPEG (the fast-drag scrub depth), a derived
971
- * cache that eviction reclaims with the footage. Absent/false = no strips
972
- * are written and scrub reads exact keyframes at every velocity.
973
- */
974
- stripsEnabled: zod.z.boolean().optional()
967
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
975
968
  }).strict();
976
969
  /**
977
970
  * Derive the {@link RecordingStorageModeSchema} summary from the authoritative
@@ -1060,7 +1053,7 @@ var OPS_LOG_DEFAULT_LIMIT = 200;
1060
1053
  /**
1061
1054
  * Entity-relocation job state (storage entity-routing spec, Phase 4).
1062
1055
  *
1063
- * One shape shared by the recorder's `relocateFootage` (segments + strips) and
1056
+ * One shape shared by the recorder's `relocateFootage` (segments) and
1064
1057
  * pipeline-analytics' `relocateMedia` (event media blobs) so the admin Data
1065
1058
  * page renders both movers with one component. Jobs are in-RAM (a restart
1066
1059
  * forgets them — re-running is safe by construction: copy-if-absent, delete
@@ -1082,7 +1075,7 @@ var RelocateJobSchema = zod.z.object({
1082
1075
  toLocationId: zod.z.string(),
1083
1076
  /** Scoped device, or null = every device. */
1084
1077
  deviceId: zod.z.number().nullable(),
1085
- /** What the job moves (owner-addon specific: segments/strips or media). */
1078
+ /** What the job moves (owner-addon specific: segments or media). */
1086
1079
  entities: zod.z.array(zod.z.string()),
1087
1080
  filesMoved: zod.z.number().int(),
1088
1081
  bytesMoved: zod.z.number().int(),
@@ -1096,7 +1089,7 @@ var RelocateFootageInputSchema = zod.z.object({
1096
1089
  deviceId: zod.z.number().optional(),
1097
1090
  fromLocationId: zod.z.string(),
1098
1091
  toLocationId: zod.z.string(),
1099
- entities: zod.z.array(zod.z.enum(["segments", "strips"])).optional(),
1092
+ entities: zod.z.array(zod.z.enum(["segments"])).optional(),
1100
1093
  /** Copy throttle in MB/s (default 40) — the drain is a background chore,
1101
1094
  * never allowed to starve live writers. */
1102
1095
  throttleMbps: zod.z.number().min(1).max(1e3).optional()
@@ -14928,6 +14921,8 @@ function createSystemProxy(api) {
14928
14921
  insert: (input) => dispatch("settingsStore", "insert", "mutation", input),
14929
14922
  update: (input) => dispatch("settingsStore", "update", "mutation", input),
14930
14923
  delete: (input) => dispatch("settingsStore", "delete", "mutation", input),
14924
+ deleteWhere: (input) => dispatch("settingsStore", "deleteWhere", "mutation", input),
14925
+ updateWhere: (input) => dispatch("settingsStore", "updateWhere", "mutation", input),
14931
14926
  count: (input) => dispatch("settingsStore", "count", "query", input),
14932
14927
  histogram: (input) => dispatch("settingsStore", "histogram", "query", input),
14933
14928
  isEmpty: (input) => dispatch("settingsStore", "isEmpty", "query", input),
@@ -17431,6 +17426,353 @@ var decoderCapability = {
17431
17426
  }
17432
17427
  };
17433
17428
  //#endregion
17429
+ //#region src/capabilities/settings-store.cap.ts
17430
+ /**
17431
+ * Query filter for settings-store collections.
17432
+ */
17433
+ var QueryFilterSchema = zod.z.object({
17434
+ where: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
17435
+ whereIn: zod.z.record(zod.z.string(), zod.z.array(zod.z.unknown())).optional(),
17436
+ whereBetween: zod.z.record(zod.z.string(), zod.z.tuple([zod.z.unknown(), zod.z.unknown()])).optional(),
17437
+ orderBy: zod.z.object({
17438
+ field: zod.z.string(),
17439
+ direction: zod.z.enum(["asc", "desc"])
17440
+ }).optional(),
17441
+ limit: zod.z.number().optional(),
17442
+ offset: zod.z.number().optional()
17443
+ });
17444
+ /**
17445
+ * The predicate half of a filter, for BULK MUTATIONS.
17446
+ *
17447
+ * Deliberately not `QueryFilterSchema`: `orderBy` / `limit` / `offset` have no
17448
+ * meaning for a statement that rewrites a set, and accepting them would invite
17449
+ * a caller to believe `limit` bounds the damage. Every field is optional here
17450
+ * only so the shape stays composable — the implementation REJECTS a filter
17451
+ * that compiles to no predicate, because that is the whole collection.
17452
+ */
17453
+ var MutationFilterSchema = zod.z.object({
17454
+ where: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
17455
+ whereIn: zod.z.record(zod.z.string(), zod.z.array(zod.z.unknown())).optional(),
17456
+ whereBetween: zod.z.record(zod.z.string(), zod.z.tuple([zod.z.unknown(), zod.z.unknown()])).optional()
17457
+ });
17458
+ /** A single stored record: `{ id, data }`. */
17459
+ var SettingsRecordSchema = zod.z.object({
17460
+ id: zod.z.string(),
17461
+ data: zod.z.record(zod.z.string(), zod.z.unknown())
17462
+ });
17463
+ /**
17464
+ * Column declaration for a structured (SQL-backed) collection.
17465
+ *
17466
+ * Logical types — the backend translates each to the matching SQLite
17467
+ * storage class and handles per-type marshaling:
17468
+ * - `TEXT` / `INTEGER` / `REAL` — native SQLite types, pass-through
17469
+ * - `JSON` — TEXT under the hood; serialised on write, parsed on read
17470
+ * - `BOOLEAN` — INTEGER 0/1 under the hood; coerced both directions
17471
+ */
17472
+ var CollectionColumnSchema = zod.z.object({
17473
+ name: zod.z.string(),
17474
+ type: zod.z.enum([
17475
+ "TEXT",
17476
+ "INTEGER",
17477
+ "REAL",
17478
+ "JSON",
17479
+ "BOOLEAN"
17480
+ ]),
17481
+ primaryKey: zod.z.boolean().optional(),
17482
+ notNull: zod.z.boolean().optional(),
17483
+ unique: zod.z.boolean().optional()
17484
+ });
17485
+ var CollectionIndexSchema = zod.z.object({
17486
+ name: zod.z.string(),
17487
+ columns: zod.z.array(zod.z.string()).readonly(),
17488
+ unique: zod.z.boolean().optional()
17489
+ });
17490
+ /**
17491
+ * settings-store — singleton capability for addon-scoped persistence.
17492
+ *
17493
+ * Every method operates within a `collection`. An optional `namespace`
17494
+ * field provides access to additional data spaces beyond the default
17495
+ * addon settings — useful for business data (events, tracks, faces, etc.).
17496
+ *
17497
+ * **Scoping is the CALLER's, and it is opt-in.** The table is
17498
+ * `namespace ? `${namespace}:${collection}` : collection` — nothing
17499
+ * consults the identity of the caller. An earlier version of this comment
17500
+ * claimed the implementation prefixes every collection with the calling
17501
+ * addon's ID and that "addons never see each other's data"; that was never
17502
+ * true, and this same file contradicted it under `declareCollection`. What
17503
+ * exists is `addon-context-factory`, which passes `namespace: addonId` ON
17504
+ * THE ADDON'S BEHALF for the `addon-settings` / `addon-devices` paths only.
17505
+ * Business collections use bare names, and any caller may name any
17506
+ * namespace, or none. Making the door enforce it is tracked separately —
17507
+ * it renames tables, so it needs a migration.
17508
+ *
17509
+ * - `{ collection: 'addon-settings' }` → table `addon-settings`
17510
+ * - `{ namespace: 'my-addon', collection: 'addon-settings' }` →
17511
+ * table `my-addon:addon-settings`
17512
+ *
17513
+ * Served by the **storage-orchestrator** builtin, which dispatches to the
17514
+ * `data-store-provider` engine registered for the collection
17515
+ * (`sqlite-settings`, a SQLite WAL backend, today). One addon owns the
17516
+ * data door; engines sit behind it
17517
+ * ([D44](../../../../docs/decisions/adr-0044.md)).
17518
+ * Addons access it via `ctx.api.settingsStore.*`.
17519
+ */
17520
+ var settingsStoreCapability = {
17521
+ name: "settings-store",
17522
+ scope: "system",
17523
+ mode: "singleton",
17524
+ methods: {
17525
+ /** Get a single value by key from a collection. */
17526
+ get: require_sleep.method(zod.z.object({
17527
+ namespace: zod.z.string().optional(),
17528
+ collection: zod.z.string(),
17529
+ key: zod.z.string()
17530
+ }), zod.z.unknown()),
17531
+ /** Set a value by key in a collection (upsert). */
17532
+ set: require_sleep.method(zod.z.object({
17533
+ namespace: zod.z.string().optional(),
17534
+ collection: zod.z.string(),
17535
+ key: zod.z.string(),
17536
+ value: zod.z.unknown()
17537
+ }), zod.z.void(), { kind: "mutation" }),
17538
+ /** Get all entries matching an optional filter. */
17539
+ query: require_sleep.method(zod.z.object({
17540
+ namespace: zod.z.string().optional(),
17541
+ collection: zod.z.string(),
17542
+ filter: QueryFilterSchema.optional()
17543
+ }), zod.z.array(SettingsRecordSchema).readonly()),
17544
+ /** Insert a new record. */
17545
+ insert: require_sleep.method(zod.z.object({
17546
+ namespace: zod.z.string().optional(),
17547
+ collection: zod.z.string(),
17548
+ record: SettingsRecordSchema
17549
+ }), zod.z.void(), { kind: "mutation" }),
17550
+ /** Update an existing record by ID. */
17551
+ update: require_sleep.method(zod.z.object({
17552
+ namespace: zod.z.string().optional(),
17553
+ collection: zod.z.string(),
17554
+ id: zod.z.string(),
17555
+ data: zod.z.record(zod.z.string(), zod.z.unknown())
17556
+ }), zod.z.void(), { kind: "mutation" }),
17557
+ /** Delete a record by key/ID. */
17558
+ delete: require_sleep.method(zod.z.object({
17559
+ namespace: zod.z.string().optional(),
17560
+ collection: zod.z.string(),
17561
+ key: zod.z.string()
17562
+ }), zod.z.void(), { kind: "mutation" }),
17563
+ /**
17564
+ * Delete every record matching `filter`, in ONE statement, returning how
17565
+ * many rows went. This exists because its absence made every retention
17566
+ * path in the system an N+1 drain loop: `delete` takes a key, so a sweep
17567
+ * had to SELECT a page of full rows — every column, including the fat
17568
+ * ones — purely to learn their ids, then issue one call per row.
17569
+ *
17570
+ * **The filter is required and must resolve.** A predicate naming
17571
+ * something the collection cannot express is an ERROR here, not a
17572
+ * widening as it is on `query`, and a filter with no predicates is an
17573
+ * error rather than "every row". Deleting a whole collection is a
17574
+ * legitimate intent, but it must be asked for by name — not reached by
17575
+ * an empty object.
17576
+ */
17577
+ deleteWhere: require_sleep.method(zod.z.object({
17578
+ namespace: zod.z.string().optional(),
17579
+ collection: zod.z.string(),
17580
+ filter: MutationFilterSchema
17581
+ }), zod.z.object({ deleted: zod.z.number().int() }), { kind: "mutation" }),
17582
+ /**
17583
+ * Apply `data` to every record matching `filter`, in one statement,
17584
+ * returning how many rows changed. Same filter contract as
17585
+ * {@link deleteWhere} — an unresolvable predicate is an error.
17586
+ */
17587
+ updateWhere: require_sleep.method(zod.z.object({
17588
+ namespace: zod.z.string().optional(),
17589
+ collection: zod.z.string(),
17590
+ filter: MutationFilterSchema,
17591
+ data: zod.z.record(zod.z.string(), zod.z.unknown())
17592
+ }), zod.z.object({ updated: zod.z.number().int() }), { kind: "mutation" }),
17593
+ /** Count entries in a collection, optionally filtered. */
17594
+ count: require_sleep.method(zod.z.object({
17595
+ namespace: zod.z.string().optional(),
17596
+ collection: zod.z.string(),
17597
+ filter: QueryFilterSchema.optional()
17598
+ }), zod.z.number()),
17599
+ /** Grouped counts per ((field-origin)/bucketSize) bucket, filtered. */
17600
+ histogram: require_sleep.method(zod.z.object({
17601
+ namespace: zod.z.string().optional(),
17602
+ collection: zod.z.string(),
17603
+ field: zod.z.string(),
17604
+ bucketSize: zod.z.number().int().positive(),
17605
+ origin: zod.z.number().int(),
17606
+ filter: QueryFilterSchema.optional()
17607
+ }), zod.z.array(zod.z.object({
17608
+ bucket: zod.z.number().int(),
17609
+ count: zod.z.number().int()
17610
+ })).readonly()),
17611
+ /** Check if a collection is empty. */
17612
+ isEmpty: require_sleep.method(zod.z.object({
17613
+ namespace: zod.z.string().optional(),
17614
+ collection: zod.z.string()
17615
+ }), zod.z.boolean()),
17616
+ /**
17617
+ * Declare a typed (SQL-backed) collection with explicit columns +
17618
+ * indexes. Idempotent: re-declaring an existing collection with the
17619
+ * same shape is a no-op; shape changes (new columns, new indexes)
17620
+ * are applied additively. Subsequent `insert` / `update` / `delete`
17621
+ * / `query` calls on this collection use typed columns instead of
17622
+ * JSON-blob storage — `record.data` fields are spread across
17623
+ * columns, `query.filter.where` matches real columns (no
17624
+ * `json_extract` overhead), `orderBy` uses column indexes.
17625
+ *
17626
+ * Addons call this in `onInitialize` before their first read/write.
17627
+ * Collection names should be namespaced by addon (e.g.
17628
+ * `pipeline-analytics:object-events`) to avoid cross-addon
17629
+ * clashes — no automatic prefix is added.
17630
+ */
17631
+ declareCollection: require_sleep.method(zod.z.object({
17632
+ namespace: zod.z.string().optional(),
17633
+ collection: zod.z.string(),
17634
+ columns: zod.z.array(CollectionColumnSchema).readonly(),
17635
+ indexes: zod.z.array(CollectionIndexSchema).readonly().optional()
17636
+ }), zod.z.void(), { kind: "mutation" })
17637
+ }
17638
+ };
17639
+ //#endregion
17640
+ //#region src/capabilities/data-store-provider.cap.ts
17641
+ /**
17642
+ * What one engine says about itself. The orchestrator uses `kind` to pick
17643
+ * a registrant for a collection; `engineId` is what a log line names when
17644
+ * a call is routed or refused.
17645
+ */
17646
+ var EngineInfoSchema = zod.z.object({
17647
+ engineId: zod.z.string(),
17648
+ /**
17649
+ * `relational` — rows, columns, indexes, the surface `settings-store`
17650
+ * has always described. `vector` — an embedding store answering
17651
+ * similarity queries. A registrant declares exactly one; an engine that
17652
+ * does both registers twice, because "both" would make the routing
17653
+ * decision ambiguous at exactly the point it must not be.
17654
+ */
17655
+ kind: zod.z.enum(["relational", "vector"]),
17656
+ displayName: zod.z.string()
17657
+ });
17658
+ /**
17659
+ * data-store-provider — the engine contract behind the data door.
17660
+ *
17661
+ * The sibling of `storage-provider`, for rows instead of bytes. The
17662
+ * orchestrator (singleton `settings-store` cap, owned by the
17663
+ * storage-orchestrator builtin) dispatches every call to the registrant
17664
+ * that serves the collection.
17665
+ *
17666
+ * `internal: true` — consumed only by the orchestrator. Public consumers
17667
+ * go through `settings-store`; they never see this cap, and an engine
17668
+ * never sees a caller.
17669
+ *
17670
+ * Design notes:
17671
+ * - **Stateless dispatch.** Every method carries its own
17672
+ * `namespace` + `collection`, so an engine keeps no per-caller state
17673
+ * and the orchestrator forwards the payload verbatim. Scoping is a
17674
+ * property of the input, not of the connection — see the note on
17675
+ * `settings-store` about what that does and does not guarantee.
17676
+ * - **One registrant today** (`sqlite-settings`). The collection shape
17677
+ * exists so a second engine is a registration rather than a second
17678
+ * door ([D44](../../../../docs/decisions/adr-0044.md)).
17679
+ *
17680
+ * The method set is deliberately identical to `settings-store`'s: the
17681
+ * orchestrator is a router, not a translator. A capability the door
17682
+ * gains, an engine must be able to answer.
17683
+ */
17684
+ var dataStoreProviderCapability = {
17685
+ name: "data-store-provider",
17686
+ scope: "system",
17687
+ mode: "collection",
17688
+ internal: true,
17689
+ methods: {
17690
+ /** Self-description — how the orchestrator picks a registrant. */
17691
+ getEngineInfo: require_sleep.method(zod.z.void(), EngineInfoSchema),
17692
+ /** Get a single value by key from a collection. */
17693
+ get: require_sleep.method(zod.z.object({
17694
+ namespace: zod.z.string().optional(),
17695
+ collection: zod.z.string(),
17696
+ key: zod.z.string()
17697
+ }), zod.z.unknown()),
17698
+ /** Set a value by key in a collection (upsert). */
17699
+ set: require_sleep.method(zod.z.object({
17700
+ namespace: zod.z.string().optional(),
17701
+ collection: zod.z.string(),
17702
+ key: zod.z.string(),
17703
+ value: zod.z.unknown()
17704
+ }), zod.z.void(), { kind: "mutation" }),
17705
+ /** Get all entries matching an optional filter. */
17706
+ query: require_sleep.method(zod.z.object({
17707
+ namespace: zod.z.string().optional(),
17708
+ collection: zod.z.string(),
17709
+ filter: QueryFilterSchema.optional()
17710
+ }), zod.z.array(SettingsRecordSchema).readonly()),
17711
+ /** Insert a new record. */
17712
+ insert: require_sleep.method(zod.z.object({
17713
+ namespace: zod.z.string().optional(),
17714
+ collection: zod.z.string(),
17715
+ record: SettingsRecordSchema
17716
+ }), zod.z.void(), { kind: "mutation" }),
17717
+ /** Update an existing record by ID. */
17718
+ update: require_sleep.method(zod.z.object({
17719
+ namespace: zod.z.string().optional(),
17720
+ collection: zod.z.string(),
17721
+ id: zod.z.string(),
17722
+ data: zod.z.record(zod.z.string(), zod.z.unknown())
17723
+ }), zod.z.void(), { kind: "mutation" }),
17724
+ /** Delete a record by key/ID. */
17725
+ delete: require_sleep.method(zod.z.object({
17726
+ namespace: zod.z.string().optional(),
17727
+ collection: zod.z.string(),
17728
+ key: zod.z.string()
17729
+ }), zod.z.void(), { kind: "mutation" }),
17730
+ /** Delete every record matching `filter`, in one statement. */
17731
+ deleteWhere: require_sleep.method(zod.z.object({
17732
+ namespace: zod.z.string().optional(),
17733
+ collection: zod.z.string(),
17734
+ filter: MutationFilterSchema
17735
+ }), zod.z.object({ deleted: zod.z.number().int() }), { kind: "mutation" }),
17736
+ /** Apply `data` to every record matching `filter`, in one statement. */
17737
+ updateWhere: require_sleep.method(zod.z.object({
17738
+ namespace: zod.z.string().optional(),
17739
+ collection: zod.z.string(),
17740
+ filter: MutationFilterSchema,
17741
+ data: zod.z.record(zod.z.string(), zod.z.unknown())
17742
+ }), zod.z.object({ updated: zod.z.number().int() }), { kind: "mutation" }),
17743
+ /** Count entries in a collection, optionally filtered. */
17744
+ count: require_sleep.method(zod.z.object({
17745
+ namespace: zod.z.string().optional(),
17746
+ collection: zod.z.string(),
17747
+ filter: QueryFilterSchema.optional()
17748
+ }), zod.z.number()),
17749
+ /** Grouped counts per ((field-origin)/bucketSize) bucket, filtered. */
17750
+ histogram: require_sleep.method(zod.z.object({
17751
+ namespace: zod.z.string().optional(),
17752
+ collection: zod.z.string(),
17753
+ field: zod.z.string(),
17754
+ bucketSize: zod.z.number().int().positive(),
17755
+ origin: zod.z.number().int(),
17756
+ filter: QueryFilterSchema.optional()
17757
+ }), zod.z.array(zod.z.object({
17758
+ bucket: zod.z.number().int(),
17759
+ count: zod.z.number().int()
17760
+ })).readonly()),
17761
+ /** Check if a collection is empty. */
17762
+ isEmpty: require_sleep.method(zod.z.object({
17763
+ namespace: zod.z.string().optional(),
17764
+ collection: zod.z.string()
17765
+ }), zod.z.boolean()),
17766
+ /** Declare a typed (SQL-backed) collection with columns + indexes. */
17767
+ declareCollection: require_sleep.method(zod.z.object({
17768
+ namespace: zod.z.string().optional(),
17769
+ collection: zod.z.string(),
17770
+ columns: zod.z.array(CollectionColumnSchema).readonly(),
17771
+ indexes: zod.z.array(CollectionIndexSchema).readonly().optional()
17772
+ }), zod.z.void(), { kind: "mutation" })
17773
+ }
17774
+ };
17775
+ //#endregion
17434
17776
  //#region src/capabilities/detection-pipeline.cap.ts
17435
17777
  /**
17436
17778
  * detection-pipeline — device-scoped facade over the system
@@ -21913,6 +22255,52 @@ var ServerUpdateStateSchema = zod.z.enum([
21913
22255
  "pending-restart",
21914
22256
  "awaiting-confirmation"
21915
22257
  ]);
22258
+ /**
22259
+ * Verdict of comparing the node's IMMUTABLE baked seed (the Docker image / the
22260
+ * desktop app bundle) against the release the deployment contract would
22261
+ * deliver today. `applyServerUpdate` swaps the data-root closure and NEVER
22262
+ * touches the image, so `runningVersion` can be perfectly current while the
22263
+ * container is weeks old, from a renamed repository nothing rebuilds — which
22264
+ * is exactly what happened on 2026-08-02 (hub on `camstack-server:intel-1.1.74`
22265
+ * while every existing surface said "up to date"). The seed version is the one
22266
+ * image fingerprint a container can see from inside (no docker socket).
22267
+ *
22268
+ * - `in-sync` — seed equals the current release; the node runs the
22269
+ * contract image.
22270
+ * - `behind-patch` — same release series, older patch: the image predates
22271
+ * the current release. Normal between deliberate image
22272
+ * refreshes, but the starter/entrypoint are still old.
22273
+ * - `behind-series` — the seed's major/minor predates the current release
22274
+ * series. The shape of a pinned tag or a dead image
22275
+ * repository; a `docker pull` may fix nothing.
22276
+ * - `ahead` — seed newer than the best-known release (stale registry
22277
+ * check).
22278
+ * - `unknown` — no seed (dev workspace) or nothing to compare against
22279
+ * yet.
22280
+ */
22281
+ var ImageContractStateSchema = zod.z.enum([
22282
+ "in-sync",
22283
+ "behind-patch",
22284
+ "behind-series",
22285
+ "ahead",
22286
+ "unknown"
22287
+ ]);
22288
+ var ImageContractSchema = zod.z.object({
22289
+ state: ImageContractStateSchema,
22290
+ /** The baked seed closure version — the image/app-bundle fingerprint. */
22291
+ seedVersion: zod.z.string().nullable(),
22292
+ /** Best-known version the deployment contract delivers today. */
22293
+ contractVersion: zod.z.string().nullable(),
22294
+ /**
22295
+ * Where `contractVersion` came from: a real registry check (`registry`), or
22296
+ * the node's own running version (`running` — a node can never run code
22297
+ * newer than the newest release, so `seed < running` proves image staleness
22298
+ * even before any registry check has run).
22299
+ */
22300
+ contractSource: zod.z.enum(["registry", "running"]).nullable(),
22301
+ /** One operator-grade sentence: this node runs image X; the contract says Y. */
22302
+ message: zod.z.string()
22303
+ });
21916
22304
  var ServerRollbackInfoSchema = zod.z.object({
21917
22305
  /** The version that failed (or was manually rolled back). */
21918
22306
  fromVersion: zod.z.string(),
@@ -21949,7 +22337,12 @@ var ServerPackageStatusSchema = zod.z.object({
21949
22337
  * versions are being IGNORED. Surfaced as a warning in the UI.
21950
22338
  */
21951
22339
  stateFileCorrupt: zod.z.boolean(),
21952
- lastCheckedAtMs: zod.z.number().nullable()
22340
+ lastCheckedAtMs: zod.z.number().nullable(),
22341
+ /**
22342
+ * Seed-vs-contract verdict (see {@link ImageContractSchema}). Optional for
22343
+ * version skew: an older provider's payload simply omits it.
22344
+ */
22345
+ imageContract: ImageContractSchema.optional()
21953
22346
  });
21954
22347
  var ServerUpdateCheckResultSchema = zod.z.object({
21955
22348
  packageName: zod.z.string(),
@@ -22004,160 +22397,6 @@ version: zod.z.string().optional() }), ServerUpdateActionResultSchema, {
22004
22397
  mount: { kind: "server-provided" }
22005
22398
  };
22006
22399
  //#endregion
22007
- //#region src/capabilities/settings-store.cap.ts
22008
- /**
22009
- * Query filter for settings-store collections.
22010
- */
22011
- var QueryFilterSchema = zod.z.object({
22012
- where: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
22013
- whereIn: zod.z.record(zod.z.string(), zod.z.array(zod.z.unknown())).optional(),
22014
- whereBetween: zod.z.record(zod.z.string(), zod.z.tuple([zod.z.unknown(), zod.z.unknown()])).optional(),
22015
- orderBy: zod.z.object({
22016
- field: zod.z.string(),
22017
- direction: zod.z.enum(["asc", "desc"])
22018
- }).optional(),
22019
- limit: zod.z.number().optional(),
22020
- offset: zod.z.number().optional()
22021
- });
22022
- /** A single stored record: `{ id, data }`. */
22023
- var SettingsRecordSchema = zod.z.object({
22024
- id: zod.z.string(),
22025
- data: zod.z.record(zod.z.string(), zod.z.unknown())
22026
- });
22027
- /**
22028
- * Column declaration for a structured (SQL-backed) collection.
22029
- *
22030
- * Logical types — the backend translates each to the matching SQLite
22031
- * storage class and handles per-type marshaling:
22032
- * - `TEXT` / `INTEGER` / `REAL` — native SQLite types, pass-through
22033
- * - `JSON` — TEXT under the hood; serialised on write, parsed on read
22034
- * - `BOOLEAN` — INTEGER 0/1 under the hood; coerced both directions
22035
- */
22036
- var CollectionColumnSchema = zod.z.object({
22037
- name: zod.z.string(),
22038
- type: zod.z.enum([
22039
- "TEXT",
22040
- "INTEGER",
22041
- "REAL",
22042
- "JSON",
22043
- "BOOLEAN"
22044
- ]),
22045
- primaryKey: zod.z.boolean().optional(),
22046
- notNull: zod.z.boolean().optional(),
22047
- unique: zod.z.boolean().optional()
22048
- });
22049
- var CollectionIndexSchema = zod.z.object({
22050
- name: zod.z.string(),
22051
- columns: zod.z.array(zod.z.string()).readonly(),
22052
- unique: zod.z.boolean().optional()
22053
- });
22054
- /**
22055
- * settings-store — singleton capability for addon-scoped persistence.
22056
- *
22057
- * Every method operates within a `collection`. An optional `namespace`
22058
- * field provides access to additional data spaces beyond the default
22059
- * addon settings — useful for business data (events, tracks, faces, etc.).
22060
- *
22061
- * Scoping: the implementation prefixes every collection with the calling
22062
- * addon's ID automatically. Addons never see each other's data.
22063
- *
22064
- * - No namespace (default): `"addon-settings"` → `"<addonId>:addon-settings"`
22065
- * - With namespace: `{ namespace: 'events', collection: 'detections' }` →
22066
- * `"<addonId>:events:detections"`
22067
- *
22068
- * Implemented by `@camstack/system/builtins/sqlite-settings` (SQLite WAL backend).
22069
- * Addons access it via `ctx.api.settingsStore.*`.
22070
- */
22071
- var settingsStoreCapability = {
22072
- name: "settings-store",
22073
- scope: "system",
22074
- mode: "singleton",
22075
- methods: {
22076
- /** Get a single value by key from a collection. */
22077
- get: require_sleep.method(zod.z.object({
22078
- namespace: zod.z.string().optional(),
22079
- collection: zod.z.string(),
22080
- key: zod.z.string()
22081
- }), zod.z.unknown()),
22082
- /** Set a value by key in a collection (upsert). */
22083
- set: require_sleep.method(zod.z.object({
22084
- namespace: zod.z.string().optional(),
22085
- collection: zod.z.string(),
22086
- key: zod.z.string(),
22087
- value: zod.z.unknown()
22088
- }), zod.z.void(), { kind: "mutation" }),
22089
- /** Get all entries matching an optional filter. */
22090
- query: require_sleep.method(zod.z.object({
22091
- namespace: zod.z.string().optional(),
22092
- collection: zod.z.string(),
22093
- filter: QueryFilterSchema.optional()
22094
- }), zod.z.array(SettingsRecordSchema).readonly()),
22095
- /** Insert a new record. */
22096
- insert: require_sleep.method(zod.z.object({
22097
- namespace: zod.z.string().optional(),
22098
- collection: zod.z.string(),
22099
- record: SettingsRecordSchema
22100
- }), zod.z.void(), { kind: "mutation" }),
22101
- /** Update an existing record by ID. */
22102
- update: require_sleep.method(zod.z.object({
22103
- namespace: zod.z.string().optional(),
22104
- collection: zod.z.string(),
22105
- id: zod.z.string(),
22106
- data: zod.z.record(zod.z.string(), zod.z.unknown())
22107
- }), zod.z.void(), { kind: "mutation" }),
22108
- /** Delete a record by key/ID. */
22109
- delete: require_sleep.method(zod.z.object({
22110
- namespace: zod.z.string().optional(),
22111
- collection: zod.z.string(),
22112
- key: zod.z.string()
22113
- }), zod.z.void(), { kind: "mutation" }),
22114
- /** Count entries in a collection, optionally filtered. */
22115
- count: require_sleep.method(zod.z.object({
22116
- namespace: zod.z.string().optional(),
22117
- collection: zod.z.string(),
22118
- filter: QueryFilterSchema.optional()
22119
- }), zod.z.number()),
22120
- /** Grouped counts per ((field-origin)/bucketSize) bucket, filtered. */
22121
- histogram: require_sleep.method(zod.z.object({
22122
- namespace: zod.z.string().optional(),
22123
- collection: zod.z.string(),
22124
- field: zod.z.string(),
22125
- bucketSize: zod.z.number().int().positive(),
22126
- origin: zod.z.number().int(),
22127
- filter: QueryFilterSchema.optional()
22128
- }), zod.z.array(zod.z.object({
22129
- bucket: zod.z.number().int(),
22130
- count: zod.z.number().int()
22131
- })).readonly()),
22132
- /** Check if a collection is empty. */
22133
- isEmpty: require_sleep.method(zod.z.object({
22134
- namespace: zod.z.string().optional(),
22135
- collection: zod.z.string()
22136
- }), zod.z.boolean()),
22137
- /**
22138
- * Declare a typed (SQL-backed) collection with explicit columns +
22139
- * indexes. Idempotent: re-declaring an existing collection with the
22140
- * same shape is a no-op; shape changes (new columns, new indexes)
22141
- * are applied additively. Subsequent `insert` / `update` / `delete`
22142
- * / `query` calls on this collection use typed columns instead of
22143
- * JSON-blob storage — `record.data` fields are spread across
22144
- * columns, `query.filter.where` matches real columns (no
22145
- * `json_extract` overhead), `orderBy` uses column indexes.
22146
- *
22147
- * Addons call this in `onInitialize` before their first read/write.
22148
- * Collection names should be namespaced by addon (e.g.
22149
- * `pipeline-analytics:object-events`) to avoid cross-addon
22150
- * clashes — no automatic prefix is added.
22151
- */
22152
- declareCollection: require_sleep.method(zod.z.object({
22153
- namespace: zod.z.string().optional(),
22154
- collection: zod.z.string(),
22155
- columns: zod.z.array(CollectionColumnSchema).readonly(),
22156
- indexes: zod.z.array(CollectionIndexSchema).readonly().optional()
22157
- }), zod.z.void(), { kind: "mutation" })
22158
- }
22159
- };
22160
- //#endregion
22161
22400
  //#region src/capabilities/smtp-provider.cap.ts
22162
22401
  /**
22163
22402
  * `smtp-provider` — pluggable email delivery surface.
@@ -25925,7 +26164,7 @@ var recordingCapability = {
25925
26164
  auth: "admin"
25926
26165
  }),
25927
26166
  /**
25928
- * Move footage (segments and/or strips) from one recordings location to
26167
+ * Move footage (recorded segments) from one recordings location to
25929
26168
  * another — the drain workflow's mover (entity-routing spec Phase 4).
25930
26169
  * Throttled copy → size-verify → delete source → index refresh; resumable
25931
26170
  * by construction (copy-if-absent). Single-flight: one job at a time.
@@ -26840,6 +27079,7 @@ var CAPABILITY_NAMES = {
26840
27079
  control: "control",
26841
27080
  cover: "cover",
26842
27081
  customModelRegistry: "custom-model-registry",
27082
+ dataStoreProvider: "data-store-provider",
26843
27083
  dayNight: "day-night",
26844
27084
  decoder: "decoder",
26845
27085
  detectionPipeline: "detection-pipeline",
@@ -27097,6 +27337,10 @@ var CAPABILITY_ROUTER_KEYS = [
27097
27337
  key: "customModelRegistry",
27098
27338
  name: "custom-model-registry"
27099
27339
  },
27340
+ {
27341
+ key: "dataStoreProvider",
27342
+ name: "data-store-provider"
27343
+ },
27100
27344
  {
27101
27345
  key: "dayNight",
27102
27346
  name: "day-night"
@@ -27569,6 +27813,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
27569
27813
  controlCapability,
27570
27814
  coverCapability,
27571
27815
  customModelRegistryCapability,
27816
+ dataStoreProviderCapability,
27572
27817
  dayNightCapability,
27573
27818
  decoderCapability,
27574
27819
  detectionPipelineCapability,
@@ -28578,6 +28823,84 @@ var METHOD_ACCESS_MAP = Object.freeze({
28578
28823
  addonId: null,
28579
28824
  access: "view"
28580
28825
  },
28826
+ "dataStoreProvider.count": {
28827
+ capName: "data-store-provider",
28828
+ capScope: "system",
28829
+ addonId: null,
28830
+ access: "view"
28831
+ },
28832
+ "dataStoreProvider.declareCollection": {
28833
+ capName: "data-store-provider",
28834
+ capScope: "system",
28835
+ addonId: null,
28836
+ access: "create"
28837
+ },
28838
+ "dataStoreProvider.delete": {
28839
+ capName: "data-store-provider",
28840
+ capScope: "system",
28841
+ addonId: null,
28842
+ access: "delete"
28843
+ },
28844
+ "dataStoreProvider.deleteWhere": {
28845
+ capName: "data-store-provider",
28846
+ capScope: "system",
28847
+ addonId: null,
28848
+ access: "delete"
28849
+ },
28850
+ "dataStoreProvider.get": {
28851
+ capName: "data-store-provider",
28852
+ capScope: "system",
28853
+ addonId: null,
28854
+ access: "view"
28855
+ },
28856
+ "dataStoreProvider.getEngineInfo": {
28857
+ capName: "data-store-provider",
28858
+ capScope: "system",
28859
+ addonId: null,
28860
+ access: "view"
28861
+ },
28862
+ "dataStoreProvider.histogram": {
28863
+ capName: "data-store-provider",
28864
+ capScope: "system",
28865
+ addonId: null,
28866
+ access: "view"
28867
+ },
28868
+ "dataStoreProvider.insert": {
28869
+ capName: "data-store-provider",
28870
+ capScope: "system",
28871
+ addonId: null,
28872
+ access: "create"
28873
+ },
28874
+ "dataStoreProvider.isEmpty": {
28875
+ capName: "data-store-provider",
28876
+ capScope: "system",
28877
+ addonId: null,
28878
+ access: "view"
28879
+ },
28880
+ "dataStoreProvider.query": {
28881
+ capName: "data-store-provider",
28882
+ capScope: "system",
28883
+ addonId: null,
28884
+ access: "view"
28885
+ },
28886
+ "dataStoreProvider.set": {
28887
+ capName: "data-store-provider",
28888
+ capScope: "system",
28889
+ addonId: null,
28890
+ access: "create"
28891
+ },
28892
+ "dataStoreProvider.update": {
28893
+ capName: "data-store-provider",
28894
+ capScope: "system",
28895
+ addonId: null,
28896
+ access: "create"
28897
+ },
28898
+ "dataStoreProvider.updateWhere": {
28899
+ capName: "data-store-provider",
28900
+ capScope: "system",
28901
+ addonId: null,
28902
+ access: "create"
28903
+ },
28581
28904
  "dayNight.getOptions": {
28582
28905
  capName: "day-night",
28583
28906
  capScope: "device",
@@ -31656,6 +31979,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
31656
31979
  addonId: null,
31657
31980
  access: "delete"
31658
31981
  },
31982
+ "settingsStore.deleteWhere": {
31983
+ capName: "settings-store",
31984
+ capScope: "system",
31985
+ addonId: null,
31986
+ access: "delete"
31987
+ },
31659
31988
  "settingsStore.get": {
31660
31989
  capName: "settings-store",
31661
31990
  capScope: "system",
@@ -31698,6 +32027,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
31698
32027
  addonId: null,
31699
32028
  access: "create"
31700
32029
  },
32030
+ "settingsStore.updateWhere": {
32031
+ capName: "settings-store",
32032
+ capScope: "system",
32033
+ addonId: null,
32034
+ access: "create"
32035
+ },
31701
32036
  "smtpProvider.getStatus": {
31702
32037
  capName: "smtp-provider",
31703
32038
  capScope: "system",
@@ -32745,6 +33080,7 @@ var KNOWN_CAP_NAMES = [
32745
33080
  "control",
32746
33081
  "cover",
32747
33082
  "custom-model-registry",
33083
+ "data-store-provider",
32748
33084
  "day-night",
32749
33085
  "decoder",
32750
33086
  "device-adoption",
@@ -32907,6 +33243,7 @@ var SYSTEM_CAP_NAMES = [
32907
33243
  "backup",
32908
33244
  "broker",
32909
33245
  "custom-model-registry",
33246
+ "data-store-provider",
32910
33247
  "decoder",
32911
33248
  "device-adoption",
32912
33249
  "device-export",
@@ -33727,6 +34064,7 @@ exports.DEVICE_SETTINGS_CONTRIBUTION_METHODS = require_sleep.DEVICE_SETTINGS_CON
33727
34064
  exports.DEVICE_STATUS_METHOD = require_sleep.DEVICE_STATUS_METHOD;
33728
34065
  exports.DEVICE_TYPE_CONTROL_KIND = DEVICE_TYPE_CONTROL_KIND;
33729
34066
  exports.DEVICE_TYPE_INFO = DEVICE_TYPE_INFO;
34067
+ exports.DataStoreEngineInfoSchema = EngineInfoSchema;
33730
34068
  exports.DayNightModeSchema = DayNightModeSchema;
33731
34069
  exports.DayNightOptionsSchema = DayNightOptionsSchema;
33732
34070
  exports.DayNightSettingsPatchSchema = DayNightSettingsPatchSchema;
@@ -33816,6 +34154,8 @@ exports.HistoryResolutionEnum = HistoryResolutionEnum;
33816
34154
  exports.HumidifierStatusSchema = HumidifierStatusSchema;
33817
34155
  exports.HumiditySensorStatusSchema = HumiditySensorStatusSchema;
33818
34156
  exports.HvacModeSchema = HvacModeSchema;
34157
+ exports.ImageContractSchema = ImageContractSchema;
34158
+ exports.ImageContractStateSchema = ImageContractStateSchema;
33819
34159
  exports.ImageRotateSchema = ImageRotateSchema;
33820
34160
  exports.ImageSettingsOptionsSchema = ImageSettingsOptionsSchema;
33821
34161
  exports.ImageSettingsPatchSchema = ImageSettingsPatchSchema;
@@ -33913,6 +34253,7 @@ exports.MotionZonePatchSchema = MotionZonePatchSchema;
33913
34253
  exports.MotionZoneRegionSchema = MotionZoneRegionSchema;
33914
34254
  exports.MotionZoneStatusSchema = MotionZoneStatusSchema;
33915
34255
  exports.MqttBrokerStatusSchema = StatusSchema;
34256
+ exports.MutationFilterSchema = MutationFilterSchema;
33916
34257
  exports.NC_BASE_CONDITION_KEYS = NC_BASE_CONDITION_KEYS;
33917
34258
  exports.NC_CONDITION_CATALOG = NC_CONDITION_CATALOG;
33918
34259
  exports.NC_HISTORY_LIMIT_DEFAULT = NC_HISTORY_LIMIT_DEFAULT;
@@ -34284,6 +34625,7 @@ exports.createSliceHandle = require_sleep.createSliceHandle;
34284
34625
  exports.createSystemProxy = createSystemProxy;
34285
34626
  exports.customAction = customAction;
34286
34627
  exports.customModelRegistryCapability = customModelRegistryCapability;
34628
+ exports.dataStoreProviderCapability = dataStoreProviderCapability;
34287
34629
  exports.dayNightCapability = dayNightCapability;
34288
34630
  exports.decoderCapability = decoderCapability;
34289
34631
  exports.defaultDeviceFor = defaultDeviceFor;