@camstack/system 1.2.28 → 1.2.30

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.
Files changed (60) hide show
  1. package/dist/addon-runner.js +3 -3
  2. package/dist/addon-runner.mjs +2 -2
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.d.ts +0 -7
  8. package/dist/builtins/alerts/alerts.addon.js +47 -25
  9. package/dist/builtins/alerts/alerts.addon.mjs +47 -25
  10. package/dist/builtins/alerts/prune-policy.d.ts +44 -0
  11. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  13. package/dist/builtins/console-logging/index.js +1 -1
  14. package/dist/builtins/console-logging/index.mjs +1 -1
  15. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  16. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  18. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  19. package/dist/builtins/hub-forwarder/index.js +1 -1
  20. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  21. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  22. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  23. package/dist/builtins/local-network/local-network.addon.js +1 -1
  24. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  25. package/dist/builtins/loki-logging/index.js +1 -1
  26. package/dist/builtins/loki-logging/index.mjs +1 -1
  27. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  28. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  29. package/dist/builtins/platform-probe/index.js +1 -1
  30. package/dist/builtins/platform-probe/index.mjs +1 -1
  31. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  32. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  33. package/dist/builtins/snapshot/index.js +1 -1
  34. package/dist/builtins/snapshot/index.mjs +1 -1
  35. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  36. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  37. package/dist/builtins/sqlite-storage/filter-compiler.d.ts +61 -0
  38. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +50 -1
  39. package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +8 -2
  40. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +136 -64
  41. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +136 -64
  42. package/dist/builtins/storage-orchestrator/data-store-dispatch.d.ts +12 -0
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +18 -7
  44. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +80 -11
  45. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +80 -11
  46. package/dist/builtins/system-config/system-config.addon.js +1 -1
  47. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  48. package/dist/builtins/winston-logging/index.js +1 -1
  49. package/dist/builtins/winston-logging/index.mjs +1 -1
  50. package/dist/{dist-BXmFvn3h.mjs → dist-Dr_P0DOB.mjs} +492 -166
  51. package/dist/{dist-BxtIzexq.js → dist-VIwdvws5.js} +497 -165
  52. package/dist/index.d.ts +2 -0
  53. package/dist/index.js +150 -2
  54. package/dist/index.mjs +146 -3
  55. package/dist/kernel/addon-installer.d.ts +38 -0
  56. package/dist/kernel/heap-watch.d.ts +36 -0
  57. package/dist/kernel/transport/uds-event-bus.d.ts +4 -1
  58. package/dist/{manifest-python-deps-KZ00cbfP.js → manifest-python-deps-BqE5j0-O.js} +25 -4
  59. package/dist/{manifest-python-deps-BNOApgK0.mjs → manifest-python-deps-Ck4-9K9m.mjs} +25 -4
  60. package/package.json +7 -3
@@ -3381,14 +3381,7 @@ var RecordingConfigSchema = z.object({
3381
3381
  * windows only — existing sheets are immutable, and each window's index
3382
3382
  * carries its own tile dims so mixed-preset history renders correctly.
3383
3383
  */
3384
- scrubThumbnails: ScrubThumbnailPresetSchema.optional(),
3385
- /**
3386
- * OPT-IN thumbnail-strip generation for this camera: every keyframe of the
3387
- * low recording saved as a JPEG (the fast-drag scrub depth), a derived
3388
- * cache that eviction reclaims with the footage. Absent/false = no strips
3389
- * are written and scrub reads exact keyframes at every velocity.
3390
- */
3391
- stripsEnabled: z.boolean().optional()
3384
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
3392
3385
  }).strict();
3393
3386
  /**
3394
3387
  * Ops-log — the durable, append-only operations audit shared by the
@@ -3450,7 +3443,7 @@ var OpsLogQueryInputSchema = z.object({
3450
3443
  /**
3451
3444
  * Entity-relocation job state (storage entity-routing spec, Phase 4).
3452
3445
  *
3453
- * One shape shared by the recorder's `relocateFootage` (segments + strips) and
3446
+ * One shape shared by the recorder's `relocateFootage` (segments) and
3454
3447
  * pipeline-analytics' `relocateMedia` (event media blobs) so the admin Data
3455
3448
  * page renders both movers with one component. Jobs are in-RAM (a restart
3456
3449
  * forgets them — re-running is safe by construction: copy-if-absent, delete
@@ -3472,7 +3465,7 @@ var RelocateJobSchema = z.object({
3472
3465
  toLocationId: z.string(),
3473
3466
  /** Scoped device, or null = every device. */
3474
3467
  deviceId: z.number().nullable(),
3475
- /** What the job moves (owner-addon specific: segments/strips or media). */
3468
+ /** What the job moves (owner-addon specific: segments or media). */
3476
3469
  entities: z.array(z.string()),
3477
3470
  filesMoved: z.number().int(),
3478
3471
  bytesMoved: z.number().int(),
@@ -3486,7 +3479,7 @@ var RelocateFootageInputSchema = z.object({
3486
3479
  deviceId: z.number().optional(),
3487
3480
  fromLocationId: z.string(),
3488
3481
  toLocationId: z.string(),
3489
- entities: z.array(z.enum(["segments", "strips"])).optional(),
3482
+ entities: z.array(z.enum(["segments"])).optional(),
3490
3483
  /** Copy throttle in MB/s (default 40) — the drain is a background chore,
3491
3484
  * never allowed to starve live writers. */
3492
3485
  throttleMbps: z.number().min(1).max(1e3).optional()
@@ -14538,6 +14531,349 @@ var decoderCapability = {
14538
14531
  }
14539
14532
  };
14540
14533
  /**
14534
+ * Query filter for settings-store collections.
14535
+ */
14536
+ var QueryFilterSchema = z.object({
14537
+ where: z.record(z.string(), z.unknown()).optional(),
14538
+ whereIn: z.record(z.string(), z.array(z.unknown())).optional(),
14539
+ whereBetween: z.record(z.string(), z.tuple([z.unknown(), z.unknown()])).optional(),
14540
+ orderBy: z.object({
14541
+ field: z.string(),
14542
+ direction: z.enum(["asc", "desc"])
14543
+ }).optional(),
14544
+ limit: z.number().optional(),
14545
+ offset: z.number().optional()
14546
+ });
14547
+ /**
14548
+ * The predicate half of a filter, for BULK MUTATIONS.
14549
+ *
14550
+ * Deliberately not `QueryFilterSchema`: `orderBy` / `limit` / `offset` have no
14551
+ * meaning for a statement that rewrites a set, and accepting them would invite
14552
+ * a caller to believe `limit` bounds the damage. Every field is optional here
14553
+ * only so the shape stays composable — the implementation REJECTS a filter
14554
+ * that compiles to no predicate, because that is the whole collection.
14555
+ */
14556
+ var MutationFilterSchema = z.object({
14557
+ where: z.record(z.string(), z.unknown()).optional(),
14558
+ whereIn: z.record(z.string(), z.array(z.unknown())).optional(),
14559
+ whereBetween: z.record(z.string(), z.tuple([z.unknown(), z.unknown()])).optional()
14560
+ });
14561
+ /** A single stored record: `{ id, data }`. */
14562
+ var SettingsRecordSchema = z.object({
14563
+ id: z.string(),
14564
+ data: z.record(z.string(), z.unknown())
14565
+ });
14566
+ /**
14567
+ * Column declaration for a structured (SQL-backed) collection.
14568
+ *
14569
+ * Logical types — the backend translates each to the matching SQLite
14570
+ * storage class and handles per-type marshaling:
14571
+ * - `TEXT` / `INTEGER` / `REAL` — native SQLite types, pass-through
14572
+ * - `JSON` — TEXT under the hood; serialised on write, parsed on read
14573
+ * - `BOOLEAN` — INTEGER 0/1 under the hood; coerced both directions
14574
+ */
14575
+ var CollectionColumnSchema = z.object({
14576
+ name: z.string(),
14577
+ type: z.enum([
14578
+ "TEXT",
14579
+ "INTEGER",
14580
+ "REAL",
14581
+ "JSON",
14582
+ "BOOLEAN"
14583
+ ]),
14584
+ primaryKey: z.boolean().optional(),
14585
+ notNull: z.boolean().optional(),
14586
+ unique: z.boolean().optional()
14587
+ });
14588
+ var CollectionIndexSchema = z.object({
14589
+ name: z.string(),
14590
+ columns: z.array(z.string()).readonly(),
14591
+ unique: z.boolean().optional()
14592
+ });
14593
+ /**
14594
+ * settings-store — singleton capability for addon-scoped persistence.
14595
+ *
14596
+ * Every method operates within a `collection`. An optional `namespace`
14597
+ * field provides access to additional data spaces beyond the default
14598
+ * addon settings — useful for business data (events, tracks, faces, etc.).
14599
+ *
14600
+ * **Scoping is the CALLER's, and it is opt-in.** The table is
14601
+ * `namespace ? `${namespace}:${collection}` : collection` — nothing
14602
+ * consults the identity of the caller. An earlier version of this comment
14603
+ * claimed the implementation prefixes every collection with the calling
14604
+ * addon's ID and that "addons never see each other's data"; that was never
14605
+ * true, and this same file contradicted it under `declareCollection`. What
14606
+ * exists is `addon-context-factory`, which passes `namespace: addonId` ON
14607
+ * THE ADDON'S BEHALF for the `addon-settings` / `addon-devices` paths only.
14608
+ * Business collections use bare names, and any caller may name any
14609
+ * namespace, or none. Making the door enforce it is tracked separately —
14610
+ * it renames tables, so it needs a migration.
14611
+ *
14612
+ * - `{ collection: 'addon-settings' }` → table `addon-settings`
14613
+ * - `{ namespace: 'my-addon', collection: 'addon-settings' }` →
14614
+ * table `my-addon:addon-settings`
14615
+ *
14616
+ * Served by the **storage-orchestrator** builtin, which dispatches to the
14617
+ * `data-store-provider` engine registered for the collection
14618
+ * (`sqlite-settings`, a SQLite WAL backend, today). One addon owns the
14619
+ * data door; engines sit behind it
14620
+ * ([D44](../../../../docs/decisions/adr-0044.md)).
14621
+ * Addons access it via `ctx.api.settingsStore.*`.
14622
+ */
14623
+ var settingsStoreCapability = {
14624
+ name: "settings-store",
14625
+ scope: "system",
14626
+ mode: "singleton",
14627
+ methods: {
14628
+ /** Get a single value by key from a collection. */
14629
+ get: method(z.object({
14630
+ namespace: z.string().optional(),
14631
+ collection: z.string(),
14632
+ key: z.string()
14633
+ }), z.unknown()),
14634
+ /** Set a value by key in a collection (upsert). */
14635
+ set: method(z.object({
14636
+ namespace: z.string().optional(),
14637
+ collection: z.string(),
14638
+ key: z.string(),
14639
+ value: z.unknown()
14640
+ }), z.void(), { kind: "mutation" }),
14641
+ /** Get all entries matching an optional filter. */
14642
+ query: method(z.object({
14643
+ namespace: z.string().optional(),
14644
+ collection: z.string(),
14645
+ filter: QueryFilterSchema.optional()
14646
+ }), z.array(SettingsRecordSchema).readonly()),
14647
+ /** Insert a new record. */
14648
+ insert: method(z.object({
14649
+ namespace: z.string().optional(),
14650
+ collection: z.string(),
14651
+ record: SettingsRecordSchema
14652
+ }), z.void(), { kind: "mutation" }),
14653
+ /** Update an existing record by ID. */
14654
+ update: method(z.object({
14655
+ namespace: z.string().optional(),
14656
+ collection: z.string(),
14657
+ id: z.string(),
14658
+ data: z.record(z.string(), z.unknown())
14659
+ }), z.void(), { kind: "mutation" }),
14660
+ /** Delete a record by key/ID. */
14661
+ delete: method(z.object({
14662
+ namespace: z.string().optional(),
14663
+ collection: z.string(),
14664
+ key: z.string()
14665
+ }), z.void(), { kind: "mutation" }),
14666
+ /**
14667
+ * Delete every record matching `filter`, in ONE statement, returning how
14668
+ * many rows went. This exists because its absence made every retention
14669
+ * path in the system an N+1 drain loop: `delete` takes a key, so a sweep
14670
+ * had to SELECT a page of full rows — every column, including the fat
14671
+ * ones — purely to learn their ids, then issue one call per row.
14672
+ *
14673
+ * **The filter is required and must resolve.** A predicate naming
14674
+ * something the collection cannot express is an ERROR here, not a
14675
+ * widening as it is on `query`, and a filter with no predicates is an
14676
+ * error rather than "every row". Deleting a whole collection is a
14677
+ * legitimate intent, but it must be asked for by name — not reached by
14678
+ * an empty object.
14679
+ */
14680
+ deleteWhere: method(z.object({
14681
+ namespace: z.string().optional(),
14682
+ collection: z.string(),
14683
+ filter: MutationFilterSchema
14684
+ }), z.object({ deleted: z.number().int() }), { kind: "mutation" }),
14685
+ /**
14686
+ * Apply `data` to every record matching `filter`, in one statement,
14687
+ * returning how many rows changed. Same filter contract as
14688
+ * {@link deleteWhere} — an unresolvable predicate is an error.
14689
+ */
14690
+ updateWhere: method(z.object({
14691
+ namespace: z.string().optional(),
14692
+ collection: z.string(),
14693
+ filter: MutationFilterSchema,
14694
+ data: z.record(z.string(), z.unknown())
14695
+ }), z.object({ updated: z.number().int() }), { kind: "mutation" }),
14696
+ /** Count entries in a collection, optionally filtered. */
14697
+ count: method(z.object({
14698
+ namespace: z.string().optional(),
14699
+ collection: z.string(),
14700
+ filter: QueryFilterSchema.optional()
14701
+ }), z.number()),
14702
+ /** Grouped counts per ((field-origin)/bucketSize) bucket, filtered. */
14703
+ histogram: method(z.object({
14704
+ namespace: z.string().optional(),
14705
+ collection: z.string(),
14706
+ field: z.string(),
14707
+ bucketSize: z.number().int().positive(),
14708
+ origin: z.number().int(),
14709
+ filter: QueryFilterSchema.optional()
14710
+ }), z.array(z.object({
14711
+ bucket: z.number().int(),
14712
+ count: z.number().int()
14713
+ })).readonly()),
14714
+ /** Check if a collection is empty. */
14715
+ isEmpty: method(z.object({
14716
+ namespace: z.string().optional(),
14717
+ collection: z.string()
14718
+ }), z.boolean()),
14719
+ /**
14720
+ * Declare a typed (SQL-backed) collection with explicit columns +
14721
+ * indexes. Idempotent: re-declaring an existing collection with the
14722
+ * same shape is a no-op; shape changes (new columns, new indexes)
14723
+ * are applied additively. Subsequent `insert` / `update` / `delete`
14724
+ * / `query` calls on this collection use typed columns instead of
14725
+ * JSON-blob storage — `record.data` fields are spread across
14726
+ * columns, `query.filter.where` matches real columns (no
14727
+ * `json_extract` overhead), `orderBy` uses column indexes.
14728
+ *
14729
+ * Addons call this in `onInitialize` before their first read/write.
14730
+ * Collection names should be namespaced by addon (e.g.
14731
+ * `pipeline-analytics:object-events`) to avoid cross-addon
14732
+ * clashes — no automatic prefix is added.
14733
+ */
14734
+ declareCollection: method(z.object({
14735
+ namespace: z.string().optional(),
14736
+ collection: z.string(),
14737
+ columns: z.array(CollectionColumnSchema).readonly(),
14738
+ indexes: z.array(CollectionIndexSchema).readonly().optional()
14739
+ }), z.void(), { kind: "mutation" })
14740
+ }
14741
+ };
14742
+ /**
14743
+ * What one engine says about itself. The orchestrator uses `kind` to pick
14744
+ * a registrant for a collection; `engineId` is what a log line names when
14745
+ * a call is routed or refused.
14746
+ */
14747
+ var EngineInfoSchema = z.object({
14748
+ engineId: z.string(),
14749
+ /**
14750
+ * `relational` — rows, columns, indexes, the surface `settings-store`
14751
+ * has always described. `vector` — an embedding store answering
14752
+ * similarity queries. A registrant declares exactly one; an engine that
14753
+ * does both registers twice, because "both" would make the routing
14754
+ * decision ambiguous at exactly the point it must not be.
14755
+ */
14756
+ kind: z.enum(["relational", "vector"]),
14757
+ displayName: z.string()
14758
+ });
14759
+ /**
14760
+ * data-store-provider — the engine contract behind the data door.
14761
+ *
14762
+ * The sibling of `storage-provider`, for rows instead of bytes. The
14763
+ * orchestrator (singleton `settings-store` cap, owned by the
14764
+ * storage-orchestrator builtin) dispatches every call to the registrant
14765
+ * that serves the collection.
14766
+ *
14767
+ * `internal: true` — consumed only by the orchestrator. Public consumers
14768
+ * go through `settings-store`; they never see this cap, and an engine
14769
+ * never sees a caller.
14770
+ *
14771
+ * Design notes:
14772
+ * - **Stateless dispatch.** Every method carries its own
14773
+ * `namespace` + `collection`, so an engine keeps no per-caller state
14774
+ * and the orchestrator forwards the payload verbatim. Scoping is a
14775
+ * property of the input, not of the connection — see the note on
14776
+ * `settings-store` about what that does and does not guarantee.
14777
+ * - **One registrant today** (`sqlite-settings`). The collection shape
14778
+ * exists so a second engine is a registration rather than a second
14779
+ * door ([D44](../../../../docs/decisions/adr-0044.md)).
14780
+ *
14781
+ * The method set is deliberately identical to `settings-store`'s: the
14782
+ * orchestrator is a router, not a translator. A capability the door
14783
+ * gains, an engine must be able to answer.
14784
+ */
14785
+ var dataStoreProviderCapability = {
14786
+ name: "data-store-provider",
14787
+ scope: "system",
14788
+ mode: "collection",
14789
+ internal: true,
14790
+ methods: {
14791
+ /** Self-description — how the orchestrator picks a registrant. */
14792
+ getEngineInfo: method(z.void(), EngineInfoSchema),
14793
+ /** Get a single value by key from a collection. */
14794
+ get: method(z.object({
14795
+ namespace: z.string().optional(),
14796
+ collection: z.string(),
14797
+ key: z.string()
14798
+ }), z.unknown()),
14799
+ /** Set a value by key in a collection (upsert). */
14800
+ set: method(z.object({
14801
+ namespace: z.string().optional(),
14802
+ collection: z.string(),
14803
+ key: z.string(),
14804
+ value: z.unknown()
14805
+ }), z.void(), { kind: "mutation" }),
14806
+ /** Get all entries matching an optional filter. */
14807
+ query: method(z.object({
14808
+ namespace: z.string().optional(),
14809
+ collection: z.string(),
14810
+ filter: QueryFilterSchema.optional()
14811
+ }), z.array(SettingsRecordSchema).readonly()),
14812
+ /** Insert a new record. */
14813
+ insert: method(z.object({
14814
+ namespace: z.string().optional(),
14815
+ collection: z.string(),
14816
+ record: SettingsRecordSchema
14817
+ }), z.void(), { kind: "mutation" }),
14818
+ /** Update an existing record by ID. */
14819
+ update: method(z.object({
14820
+ namespace: z.string().optional(),
14821
+ collection: z.string(),
14822
+ id: z.string(),
14823
+ data: z.record(z.string(), z.unknown())
14824
+ }), z.void(), { kind: "mutation" }),
14825
+ /** Delete a record by key/ID. */
14826
+ delete: method(z.object({
14827
+ namespace: z.string().optional(),
14828
+ collection: z.string(),
14829
+ key: z.string()
14830
+ }), z.void(), { kind: "mutation" }),
14831
+ /** Delete every record matching `filter`, in one statement. */
14832
+ deleteWhere: method(z.object({
14833
+ namespace: z.string().optional(),
14834
+ collection: z.string(),
14835
+ filter: MutationFilterSchema
14836
+ }), z.object({ deleted: z.number().int() }), { kind: "mutation" }),
14837
+ /** Apply `data` to every record matching `filter`, in one statement. */
14838
+ updateWhere: method(z.object({
14839
+ namespace: z.string().optional(),
14840
+ collection: z.string(),
14841
+ filter: MutationFilterSchema,
14842
+ data: z.record(z.string(), z.unknown())
14843
+ }), z.object({ updated: z.number().int() }), { kind: "mutation" }),
14844
+ /** Count entries in a collection, optionally filtered. */
14845
+ count: method(z.object({
14846
+ namespace: z.string().optional(),
14847
+ collection: z.string(),
14848
+ filter: QueryFilterSchema.optional()
14849
+ }), z.number()),
14850
+ /** Grouped counts per ((field-origin)/bucketSize) bucket, filtered. */
14851
+ histogram: method(z.object({
14852
+ namespace: z.string().optional(),
14853
+ collection: z.string(),
14854
+ field: z.string(),
14855
+ bucketSize: z.number().int().positive(),
14856
+ origin: z.number().int(),
14857
+ filter: QueryFilterSchema.optional()
14858
+ }), z.array(z.object({
14859
+ bucket: z.number().int(),
14860
+ count: z.number().int()
14861
+ })).readonly()),
14862
+ /** Check if a collection is empty. */
14863
+ isEmpty: method(z.object({
14864
+ namespace: z.string().optional(),
14865
+ collection: z.string()
14866
+ }), z.boolean()),
14867
+ /** Declare a typed (SQL-backed) collection with columns + indexes. */
14868
+ declareCollection: method(z.object({
14869
+ namespace: z.string().optional(),
14870
+ collection: z.string(),
14871
+ columns: z.array(CollectionColumnSchema).readonly(),
14872
+ indexes: z.array(CollectionIndexSchema).readonly().optional()
14873
+ }), z.void(), { kind: "mutation" })
14874
+ }
14875
+ };
14876
+ /**
14541
14877
  * detection-pipeline — device-scoped facade over the system
14542
14878
  * `pipeline-executor`.
14543
14879
  *
@@ -18926,6 +19262,52 @@ var ServerUpdateStateSchema = z.enum([
18926
19262
  "pending-restart",
18927
19263
  "awaiting-confirmation"
18928
19264
  ]);
19265
+ /**
19266
+ * Verdict of comparing the node's IMMUTABLE baked seed (the Docker image / the
19267
+ * desktop app bundle) against the release the deployment contract would
19268
+ * deliver today. `applyServerUpdate` swaps the data-root closure and NEVER
19269
+ * touches the image, so `runningVersion` can be perfectly current while the
19270
+ * container is weeks old, from a renamed repository nothing rebuilds — which
19271
+ * is exactly what happened on 2026-08-02 (hub on `camstack-server:intel-1.1.74`
19272
+ * while every existing surface said "up to date"). The seed version is the one
19273
+ * image fingerprint a container can see from inside (no docker socket).
19274
+ *
19275
+ * - `in-sync` — seed equals the current release; the node runs the
19276
+ * contract image.
19277
+ * - `behind-patch` — same release series, older patch: the image predates
19278
+ * the current release. Normal between deliberate image
19279
+ * refreshes, but the starter/entrypoint are still old.
19280
+ * - `behind-series` — the seed's major/minor predates the current release
19281
+ * series. The shape of a pinned tag or a dead image
19282
+ * repository; a `docker pull` may fix nothing.
19283
+ * - `ahead` — seed newer than the best-known release (stale registry
19284
+ * check).
19285
+ * - `unknown` — no seed (dev workspace) or nothing to compare against
19286
+ * yet.
19287
+ */
19288
+ var ImageContractStateSchema = z.enum([
19289
+ "in-sync",
19290
+ "behind-patch",
19291
+ "behind-series",
19292
+ "ahead",
19293
+ "unknown"
19294
+ ]);
19295
+ var ImageContractSchema = z.object({
19296
+ state: ImageContractStateSchema,
19297
+ /** The baked seed closure version — the image/app-bundle fingerprint. */
19298
+ seedVersion: z.string().nullable(),
19299
+ /** Best-known version the deployment contract delivers today. */
19300
+ contractVersion: z.string().nullable(),
19301
+ /**
19302
+ * Where `contractVersion` came from: a real registry check (`registry`), or
19303
+ * the node's own running version (`running` — a node can never run code
19304
+ * newer than the newest release, so `seed < running` proves image staleness
19305
+ * even before any registry check has run).
19306
+ */
19307
+ contractSource: z.enum(["registry", "running"]).nullable(),
19308
+ /** One operator-grade sentence: this node runs image X; the contract says Y. */
19309
+ message: z.string()
19310
+ });
18929
19311
  var ServerRollbackInfoSchema = z.object({
18930
19312
  /** The version that failed (or was manually rolled back). */
18931
19313
  fromVersion: z.string(),
@@ -18962,7 +19344,12 @@ var ServerPackageStatusSchema = z.object({
18962
19344
  * versions are being IGNORED. Surfaced as a warning in the UI.
18963
19345
  */
18964
19346
  stateFileCorrupt: z.boolean(),
18965
- lastCheckedAtMs: z.number().nullable()
19347
+ lastCheckedAtMs: z.number().nullable(),
19348
+ /**
19349
+ * Seed-vs-contract verdict (see {@link ImageContractSchema}). Optional for
19350
+ * version skew: an older provider's payload simply omits it.
19351
+ */
19352
+ imageContract: ImageContractSchema.optional()
18966
19353
  });
18967
19354
  var ServerUpdateCheckResultSchema = z.object({
18968
19355
  packageName: z.string(),
@@ -19017,158 +19404,6 @@ version: z.string().optional() }), ServerUpdateActionResultSchema, {
19017
19404
  mount: { kind: "server-provided" }
19018
19405
  };
19019
19406
  /**
19020
- * Query filter for settings-store collections.
19021
- */
19022
- var QueryFilterSchema = z.object({
19023
- where: z.record(z.string(), z.unknown()).optional(),
19024
- whereIn: z.record(z.string(), z.array(z.unknown())).optional(),
19025
- whereBetween: z.record(z.string(), z.tuple([z.unknown(), z.unknown()])).optional(),
19026
- orderBy: z.object({
19027
- field: z.string(),
19028
- direction: z.enum(["asc", "desc"])
19029
- }).optional(),
19030
- limit: z.number().optional(),
19031
- offset: z.number().optional()
19032
- });
19033
- /** A single stored record: `{ id, data }`. */
19034
- var SettingsRecordSchema = z.object({
19035
- id: z.string(),
19036
- data: z.record(z.string(), z.unknown())
19037
- });
19038
- /**
19039
- * Column declaration for a structured (SQL-backed) collection.
19040
- *
19041
- * Logical types — the backend translates each to the matching SQLite
19042
- * storage class and handles per-type marshaling:
19043
- * - `TEXT` / `INTEGER` / `REAL` — native SQLite types, pass-through
19044
- * - `JSON` — TEXT under the hood; serialised on write, parsed on read
19045
- * - `BOOLEAN` — INTEGER 0/1 under the hood; coerced both directions
19046
- */
19047
- var CollectionColumnSchema = z.object({
19048
- name: z.string(),
19049
- type: z.enum([
19050
- "TEXT",
19051
- "INTEGER",
19052
- "REAL",
19053
- "JSON",
19054
- "BOOLEAN"
19055
- ]),
19056
- primaryKey: z.boolean().optional(),
19057
- notNull: z.boolean().optional(),
19058
- unique: z.boolean().optional()
19059
- });
19060
- var CollectionIndexSchema = z.object({
19061
- name: z.string(),
19062
- columns: z.array(z.string()).readonly(),
19063
- unique: z.boolean().optional()
19064
- });
19065
- /**
19066
- * settings-store — singleton capability for addon-scoped persistence.
19067
- *
19068
- * Every method operates within a `collection`. An optional `namespace`
19069
- * field provides access to additional data spaces beyond the default
19070
- * addon settings — useful for business data (events, tracks, faces, etc.).
19071
- *
19072
- * Scoping: the implementation prefixes every collection with the calling
19073
- * addon's ID automatically. Addons never see each other's data.
19074
- *
19075
- * - No namespace (default): `"addon-settings"` → `"<addonId>:addon-settings"`
19076
- * - With namespace: `{ namespace: 'events', collection: 'detections' }` →
19077
- * `"<addonId>:events:detections"`
19078
- *
19079
- * Implemented by `@camstack/system/builtins/sqlite-settings` (SQLite WAL backend).
19080
- * Addons access it via `ctx.api.settingsStore.*`.
19081
- */
19082
- var settingsStoreCapability = {
19083
- name: "settings-store",
19084
- scope: "system",
19085
- mode: "singleton",
19086
- methods: {
19087
- /** Get a single value by key from a collection. */
19088
- get: method(z.object({
19089
- namespace: z.string().optional(),
19090
- collection: z.string(),
19091
- key: z.string()
19092
- }), z.unknown()),
19093
- /** Set a value by key in a collection (upsert). */
19094
- set: method(z.object({
19095
- namespace: z.string().optional(),
19096
- collection: z.string(),
19097
- key: z.string(),
19098
- value: z.unknown()
19099
- }), z.void(), { kind: "mutation" }),
19100
- /** Get all entries matching an optional filter. */
19101
- query: method(z.object({
19102
- namespace: z.string().optional(),
19103
- collection: z.string(),
19104
- filter: QueryFilterSchema.optional()
19105
- }), z.array(SettingsRecordSchema).readonly()),
19106
- /** Insert a new record. */
19107
- insert: method(z.object({
19108
- namespace: z.string().optional(),
19109
- collection: z.string(),
19110
- record: SettingsRecordSchema
19111
- }), z.void(), { kind: "mutation" }),
19112
- /** Update an existing record by ID. */
19113
- update: method(z.object({
19114
- namespace: z.string().optional(),
19115
- collection: z.string(),
19116
- id: z.string(),
19117
- data: z.record(z.string(), z.unknown())
19118
- }), z.void(), { kind: "mutation" }),
19119
- /** Delete a record by key/ID. */
19120
- delete: method(z.object({
19121
- namespace: z.string().optional(),
19122
- collection: z.string(),
19123
- key: z.string()
19124
- }), z.void(), { kind: "mutation" }),
19125
- /** Count entries in a collection, optionally filtered. */
19126
- count: method(z.object({
19127
- namespace: z.string().optional(),
19128
- collection: z.string(),
19129
- filter: QueryFilterSchema.optional()
19130
- }), z.number()),
19131
- /** Grouped counts per ((field-origin)/bucketSize) bucket, filtered. */
19132
- histogram: method(z.object({
19133
- namespace: z.string().optional(),
19134
- collection: z.string(),
19135
- field: z.string(),
19136
- bucketSize: z.number().int().positive(),
19137
- origin: z.number().int(),
19138
- filter: QueryFilterSchema.optional()
19139
- }), z.array(z.object({
19140
- bucket: z.number().int(),
19141
- count: z.number().int()
19142
- })).readonly()),
19143
- /** Check if a collection is empty. */
19144
- isEmpty: method(z.object({
19145
- namespace: z.string().optional(),
19146
- collection: z.string()
19147
- }), z.boolean()),
19148
- /**
19149
- * Declare a typed (SQL-backed) collection with explicit columns +
19150
- * indexes. Idempotent: re-declaring an existing collection with the
19151
- * same shape is a no-op; shape changes (new columns, new indexes)
19152
- * are applied additively. Subsequent `insert` / `update` / `delete`
19153
- * / `query` calls on this collection use typed columns instead of
19154
- * JSON-blob storage — `record.data` fields are spread across
19155
- * columns, `query.filter.where` matches real columns (no
19156
- * `json_extract` overhead), `orderBy` uses column indexes.
19157
- *
19158
- * Addons call this in `onInitialize` before their first read/write.
19159
- * Collection names should be namespaced by addon (e.g.
19160
- * `pipeline-analytics:object-events`) to avoid cross-addon
19161
- * clashes — no automatic prefix is added.
19162
- */
19163
- declareCollection: method(z.object({
19164
- namespace: z.string().optional(),
19165
- collection: z.string(),
19166
- columns: z.array(CollectionColumnSchema).readonly(),
19167
- indexes: z.array(CollectionIndexSchema).readonly().optional()
19168
- }), z.void(), { kind: "mutation" })
19169
- }
19170
- };
19171
- /**
19172
19407
  * `smtp-provider` — pluggable email delivery surface.
19173
19408
  *
19174
19409
  * Collection cap: a deployment may install multiple SMTP relays (e.g.
@@ -22878,7 +23113,7 @@ var recordingCapability = {
22878
23113
  auth: "admin"
22879
23114
  }),
22880
23115
  /**
22881
- * Move footage (segments and/or strips) from one recordings location to
23116
+ * Move footage (recorded segments) from one recordings location to
22882
23117
  * another — the drain workflow's mover (entity-routing spec Phase 4).
22883
23118
  * Throttled copy → size-verify → delete source → index refresh; resumable
22884
23119
  * by construction (copy-if-absent). Single-flight: one job at a time.
@@ -23761,6 +23996,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
23761
23996
  controlCapability,
23762
23997
  coverCapability,
23763
23998
  customModelRegistryCapability,
23999
+ dataStoreProviderCapability,
23764
24000
  dayNightCapability,
23765
24001
  decoderCapability,
23766
24002
  detectionPipelineCapability,
@@ -24766,6 +25002,84 @@ var METHOD_ACCESS_MAP = Object.freeze({
24766
25002
  addonId: null,
24767
25003
  access: "view"
24768
25004
  },
25005
+ "dataStoreProvider.count": {
25006
+ capName: "data-store-provider",
25007
+ capScope: "system",
25008
+ addonId: null,
25009
+ access: "view"
25010
+ },
25011
+ "dataStoreProvider.declareCollection": {
25012
+ capName: "data-store-provider",
25013
+ capScope: "system",
25014
+ addonId: null,
25015
+ access: "create"
25016
+ },
25017
+ "dataStoreProvider.delete": {
25018
+ capName: "data-store-provider",
25019
+ capScope: "system",
25020
+ addonId: null,
25021
+ access: "delete"
25022
+ },
25023
+ "dataStoreProvider.deleteWhere": {
25024
+ capName: "data-store-provider",
25025
+ capScope: "system",
25026
+ addonId: null,
25027
+ access: "delete"
25028
+ },
25029
+ "dataStoreProvider.get": {
25030
+ capName: "data-store-provider",
25031
+ capScope: "system",
25032
+ addonId: null,
25033
+ access: "view"
25034
+ },
25035
+ "dataStoreProvider.getEngineInfo": {
25036
+ capName: "data-store-provider",
25037
+ capScope: "system",
25038
+ addonId: null,
25039
+ access: "view"
25040
+ },
25041
+ "dataStoreProvider.histogram": {
25042
+ capName: "data-store-provider",
25043
+ capScope: "system",
25044
+ addonId: null,
25045
+ access: "view"
25046
+ },
25047
+ "dataStoreProvider.insert": {
25048
+ capName: "data-store-provider",
25049
+ capScope: "system",
25050
+ addonId: null,
25051
+ access: "create"
25052
+ },
25053
+ "dataStoreProvider.isEmpty": {
25054
+ capName: "data-store-provider",
25055
+ capScope: "system",
25056
+ addonId: null,
25057
+ access: "view"
25058
+ },
25059
+ "dataStoreProvider.query": {
25060
+ capName: "data-store-provider",
25061
+ capScope: "system",
25062
+ addonId: null,
25063
+ access: "view"
25064
+ },
25065
+ "dataStoreProvider.set": {
25066
+ capName: "data-store-provider",
25067
+ capScope: "system",
25068
+ addonId: null,
25069
+ access: "create"
25070
+ },
25071
+ "dataStoreProvider.update": {
25072
+ capName: "data-store-provider",
25073
+ capScope: "system",
25074
+ addonId: null,
25075
+ access: "create"
25076
+ },
25077
+ "dataStoreProvider.updateWhere": {
25078
+ capName: "data-store-provider",
25079
+ capScope: "system",
25080
+ addonId: null,
25081
+ access: "create"
25082
+ },
24769
25083
  "dayNight.getOptions": {
24770
25084
  capName: "day-night",
24771
25085
  capScope: "device",
@@ -27844,6 +28158,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
27844
28158
  addonId: null,
27845
28159
  access: "delete"
27846
28160
  },
28161
+ "settingsStore.deleteWhere": {
28162
+ capName: "settings-store",
28163
+ capScope: "system",
28164
+ addonId: null,
28165
+ access: "delete"
28166
+ },
27847
28167
  "settingsStore.get": {
27848
28168
  capName: "settings-store",
27849
28169
  capScope: "system",
@@ -27886,6 +28206,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
27886
28206
  addonId: null,
27887
28207
  access: "create"
27888
28208
  },
28209
+ "settingsStore.updateWhere": {
28210
+ capName: "settings-store",
28211
+ capScope: "system",
28212
+ addonId: null,
28213
+ access: "create"
28214
+ },
27889
28215
  "smtpProvider.getStatus": {
27890
28216
  capName: "smtp-provider",
27891
28217
  capScope: "system",
@@ -29024,4 +29350,4 @@ function enumerateInferenceDevices(hw) {
29024
29350
  return out;
29025
29351
  }
29026
29352
  //#endregion
29027
- export { storageProviderCapability as $, filesystemBrowseCapability as A, sleep as At, logLevelAtMost as B, deviceStatusCapability as C, isDeviceConfigCap as Ct, enumerateSchemaFields as D, readinessKey as Dt, enumerateItemArrayFields as E, parseJsonUnknown as Et, isVoidInput as F, parseStreamParamsFormPatch as G, metricsProviderCapability as H, kebabToCamel as I, scoreRuntimes as J, platformProbeCapability as K, lifecycleJobSchema as L, isArrayOutputSchema as M, isCollectionArrayMethod as N, evaluateLinkExpression as O, resolveCapMount as Ot, isObjectInput as P, storageCapability as Q, localNetworkCapability as R, deviceStateCapability as S, hydrateSchema as St, enumerateInferenceDevices as T, parseJsonObject as Tt, normalizeUnit as U, looseSchema as V, objectInputDeclaresAddonId as W, settingsStoreCapability as X, setByPath as Y, snapshotCapability as Z, applyTransform as _, asString as _t, CAP_NAMES_WITH_STATUS as a, BaseAddon as at, buildStreamParamsConfigSchema as b, emitReadiness as bt, RUNTIME_DEFAULTS as c, DEVICE_STATUS_METHOD as ct, StorageLocationTypeSchema as d, DeviceType as dt, streamQualityLabel as et, UserRecordSchema as f, ReadinessRegistry as ft, alertsCapability as g, asNumber as gt, addonWidgetsCapability as h, asJsonObject as ht, BatteryStatusSchema as i, errMsg as it, getByPath as j, EventCategory as jt, extractNestedAddonId as k, scopeKey as kt, STREAM_PROFILE_META as l, DeviceFeature as lt, addonSettingsCapability as m, WELL_KNOWN_TAB_MAP as mt, AlertSchema as n, userManagementCapability as nt, DeviceStatusSchema as o, DATAPLANE_SECRET_HEADER as ot, addonPagesCapability as p, ReadinessTimeoutError as pt, procedureAuthKey as q, ApiKeyRecordSchema as r, validateExpressionSource as rt, METHOD_ACCESS_MAP as s, DEVICE_SETTINGS_CONTRIBUTION_METHODS as st, ALL_CAPABILITY_DEFINITIONS as t, toExpressionValue as tt, ScopedTokenSchema as u, DeviceRole as ut, authProviderCapability as v, createEvent as vt, doorbellCapability as w, nodePin as wt, deviceManagerCapability as x, expandCapMethods as xt, backupCapability as y, emitDownForOwnedCaps as yt, logDestinationCapability as z };
29353
+ export { storageCapability as $, extractNestedAddonId as A, scopeKey as At, logDestinationCapability as B, deviceStateCapability as C, hydrateSchema as Ct, enumerateItemArrayFields as D, parseJsonUnknown as Dt, enumerateInferenceDevices as E, parseJsonObject as Et, isObjectInput as F, objectInputDeclaresAddonId as G, looseSchema as H, isVoidInput as I, procedureAuthKey as J, parseStreamParamsFormPatch as K, kebabToCamel as L, getByPath as M, EventCategory as Mt, isArrayOutputSchema as N, enumerateSchemaFields as O, readinessKey as Ot, isCollectionArrayMethod as P, snapshotCapability as Q, lifecycleJobSchema as R, deviceManagerCapability as S, expandCapMethods as St, doorbellCapability as T, nodePin as Tt, metricsProviderCapability as U, logLevelAtMost as V, normalizeUnit as W, setByPath as X, scoreRuntimes as Y, settingsStoreCapability as Z, applyTransform as _, asNumber as _t, CAP_NAMES_WITH_STATUS as a, errMsg as at, buildStreamParamsConfigSchema as b, emitDownForOwnedCaps as bt, RUNTIME_DEFAULTS as c, DEVICE_SETTINGS_CONTRIBUTION_METHODS as ct, StorageLocationTypeSchema as d, DeviceRole as dt, storageProviderCapability as et, UserRecordSchema as f, DeviceType as ft, alertsCapability as g, asJsonObject as gt, addonWidgetsCapability as h, WELL_KNOWN_TAB_MAP as ht, BatteryStatusSchema as i, validateExpressionSource as it, filesystemBrowseCapability as j, sleep as jt, evaluateLinkExpression as k, resolveCapMount as kt, STREAM_PROFILE_META as l, DEVICE_STATUS_METHOD as lt, addonSettingsCapability as m, ReadinessTimeoutError as mt, AlertSchema as n, toExpressionValue as nt, DeviceStatusSchema as o, BaseAddon as ot, addonPagesCapability as p, ReadinessRegistry as pt, platformProbeCapability as q, ApiKeyRecordSchema as r, userManagementCapability as rt, METHOD_ACCESS_MAP as s, DATAPLANE_SECRET_HEADER as st, ALL_CAPABILITY_DEFINITIONS as t, streamQualityLabel as tt, ScopedTokenSchema as u, DeviceFeature as ut, authProviderCapability as v, asString as vt, deviceStatusCapability as w, isDeviceConfigCap as wt, dataStoreProviderCapability as x, emitReadiness as xt, backupCapability as y, createEvent as yt, localNetworkCapability as z };