@camstack/system 1.2.67 → 1.2.69

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 (55) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  9. package/dist/builtins/console-logging/index.js +1 -1
  10. package/dist/builtins/console-logging/index.mjs +1 -1
  11. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  12. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  13. package/dist/builtins/device-manager/adoption-job-engine.d.ts +89 -0
  14. package/dist/builtins/device-manager/device-manager.addon.d.ts +8 -0
  15. package/dist/builtins/device-manager/device-manager.addon.js +329 -2
  16. package/dist/builtins/device-manager/device-manager.addon.mjs +329 -2
  17. package/dist/builtins/device-manager/device-provider-context.d.ts +3 -0
  18. package/dist/builtins/device-manager/device-queries.d.ts +3 -0
  19. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  20. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  21. package/dist/builtins/hub-forwarder/index.js +1 -1
  22. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  23. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  24. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  25. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  26. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  27. package/dist/builtins/local-network/local-network.addon.js +1 -1
  28. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  29. package/dist/builtins/loki-logging/index.js +1 -1
  30. package/dist/builtins/loki-logging/index.mjs +1 -1
  31. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  32. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  33. package/dist/builtins/platform-probe/index.js +1 -1
  34. package/dist/builtins/platform-probe/index.mjs +1 -1
  35. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  36. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  37. package/dist/builtins/snapshot/index.js +339 -2
  38. package/dist/builtins/snapshot/index.mjs +339 -2
  39. package/dist/builtins/snapshot/snapshot-link-url.d.ts +66 -0
  40. package/dist/builtins/snapshot/snapshot.addon.d.ts +79 -0
  41. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  42. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  43. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
  44. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
  45. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  46. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  47. package/dist/builtins/system-config/system-config.addon.js +1 -1
  48. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  49. package/dist/builtins/winston-logging/index.js +1 -1
  50. package/dist/builtins/winston-logging/index.mjs +1 -1
  51. package/dist/{dist-CbS6uoSb.mjs → dist-C-QzFJ8v.mjs} +313 -1
  52. package/dist/{dist-NRYjb8qK.js → dist-D7wC-S4p.js} +313 -1
  53. package/dist/index.js +1 -1
  54. package/dist/index.mjs +1 -1
  55. package/package.json +1 -1
@@ -3200,6 +3200,104 @@ z.object({
3200
3200
  })
3201
3201
  });
3202
3202
  /**
3203
+ * Adoption job — the background form of `device-adoption.adopt`.
3204
+ *
3205
+ * ## Why this exists
3206
+ *
3207
+ * `adopt({childNativeIds: [...]})` materialises one CamStack device per
3208
+ * candidate PLUS every accessory child, and the whole array shares ONE UDS
3209
+ * request deadline (60s). Measured on the live hub against Home Assistant:
3210
+ * each device the kernel creates costs ~450 ms — `devices.create` pre-seeds
3211
+ * meta with up to eleven SEQUENTIAL round trips (`setName`, `setType`,
3212
+ * `setRole`, … `persistConfig`) before the class is constructed — and an
3213
+ * accessory child costs the same as its parent. So the real unit of work is
3214
+ * the CHILD, not the candidate:
3215
+ *
3216
+ * - 25 candidates averaging 6 children → ~150 devices → **>60s, times out**
3217
+ * - ONE candidate with 217 children → ~217 devices → **>60s, times out**
3218
+ *
3219
+ * That second line is why this is a job and not a smaller batch. No chunking,
3220
+ * no bounded concurrency over candidates and no per-call tuning can fix a
3221
+ * shape where **N=1 already exceeds the deadline** — the count that blows the
3222
+ * budget is the source system's accessory fan-out, which the operator does not
3223
+ * choose and cannot see. A design that only works below some N is the same bug
3224
+ * deferred.
3225
+ *
3226
+ * ## What the timeout did NOT do
3227
+ *
3228
+ * It did not stop the work. The UDS deadline ends the CALLER's wait; the
3229
+ * provider's loop runs to completion. Measured: a 25-candidate adopt that
3230
+ * "failed" at 60s had adopted 17 by 87s and all 25 by ~130s. The operator saw
3231
+ * an error and had no way to learn that. Every field below exists so that
3232
+ * question has an answer.
3233
+ *
3234
+ * ## Idempotency
3235
+ *
3236
+ * Jobs are in-RAM; a restart forgets them. That is safe here because adoption
3237
+ * is keyed by a stable id (`ha:<broker>:dev:<nativeId>` and equivalents), so
3238
+ * re-running a job re-adopts nothing: an already-adopted candidate is SKIPPED
3239
+ * by the engine before any provider call and lands in `alreadyAdopted`. It is
3240
+ * never a duplicate device, and never an error the operator has to interpret.
3241
+ */
3242
+ var AdoptionJobStateSchema = z.enum([
3243
+ "running",
3244
+ "done",
3245
+ "failed",
3246
+ "cancelled"
3247
+ ]);
3248
+ /**
3249
+ * Per-candidate result. Every candidate the job was asked to adopt ends in
3250
+ * exactly one of these buckets — there is no silent drop, and the operator can
3251
+ * always answer "which of my 25 landed?".
3252
+ *
3253
+ * - `adopted` — created now by this job.
3254
+ * - `already-adopted` — a device for this candidate existed before the job
3255
+ * reached it (a re-run, or a retry after a timeout). Not an error.
3256
+ * - `failed` — the provider threw; `error` carries the message.
3257
+ * - `cancelled` — the operator cancelled before this candidate was reached.
3258
+ */
3259
+ var AdoptionOutcomeSchema = z.enum([
3260
+ "adopted",
3261
+ "already-adopted",
3262
+ "failed",
3263
+ "cancelled"
3264
+ ]);
3265
+ var AdoptionCandidateResultSchema = z.object({
3266
+ childNativeId: z.string(),
3267
+ outcome: AdoptionOutcomeSchema,
3268
+ /** The materialised parent device id — null for `failed` / `cancelled`. */
3269
+ parentDeviceId: z.number().int().nonnegative().nullable(),
3270
+ /** Accessory children created for this candidate. */
3271
+ accessoryCount: z.number().int().nonnegative(),
3272
+ /** Failure message; null unless `outcome === 'failed'`. */
3273
+ error: z.string().nullable()
3274
+ });
3275
+ var AdoptionJobSchema = z.object({
3276
+ jobId: z.string(),
3277
+ /** The integration provider this job adopts through (the `addonId` pin). */
3278
+ addonId: z.string(),
3279
+ integrationId: z.string(),
3280
+ state: AdoptionJobStateSchema,
3281
+ /** Candidates the job was asked to adopt. Known up front, so never null. */
3282
+ total: z.number().int().nonnegative(),
3283
+ /** Candidates that have reached a terminal bucket. */
3284
+ processed: z.number().int().nonnegative(),
3285
+ adopted: z.number().int().nonnegative(),
3286
+ alreadyAdopted: z.number().int().nonnegative(),
3287
+ failed: z.number().int().nonnegative(),
3288
+ /** Accessory child devices created across every candidate — the real unit
3289
+ * of work, surfaced so a slow job is legible rather than mysterious. */
3290
+ accessoriesCreated: z.number().int().nonnegative(),
3291
+ /** The candidate currently being adopted; null when idle or finished. */
3292
+ currentChildNativeId: z.string().nullable(),
3293
+ /** One entry per candidate, in the order they were processed. */
3294
+ results: z.array(AdoptionCandidateResultSchema).readonly(),
3295
+ startedAt: z.number(),
3296
+ finishedAt: z.number().nullable(),
3297
+ /** Set only when the job itself broke (not a per-candidate failure). */
3298
+ error: z.string().nullable()
3299
+ });
3300
+ /**
3203
3301
  * Per-camera FUNCTION SWITCHES — the one coherent on/off surface over the
3204
3302
  * pipeline functions an operator thinks in terms of.
3205
3303
  *
@@ -9484,6 +9582,31 @@ var deviceManagerCapability = {
9484
9582
  auth: "admin"
9485
9583
  }),
9486
9584
  /**
9585
+ * Start a background adoption and return its `jobId` immediately. The job
9586
+ * adopts ONE candidate per provider call, so no single request can exceed
9587
+ * the transport deadline, and it skips candidates that are already adopted
9588
+ * — which makes re-submitting a batch after a timeout safe and silent
9589
+ * rather than a wall of duplicate-stableId errors.
9590
+ */
9591
+ adoptionStartJob: method(AdoptInputSchema.extend({ addonId: z.string() }), z.object({ jobId: z.string() }), {
9592
+ kind: "mutation",
9593
+ auth: "admin"
9594
+ }),
9595
+ /**
9596
+ * Adoption jobs for an integration, newest first. This is the answer to
9597
+ * "which of my 25 landed?" — `results` carries one entry per candidate,
9598
+ * every one of them in a named bucket.
9599
+ */
9600
+ adoptionListJobs: method(z.object({
9601
+ addonId: z.string(),
9602
+ integrationId: z.string().optional()
9603
+ }), z.array(AdoptionJobSchema).readonly(), { auth: "admin" }),
9604
+ /** Cooperative cancel: the in-flight candidate finishes, the rest never start. */
9605
+ adoptionCancelJob: method(z.object({ jobId: z.string() }), z.object({ cancelled: z.boolean() }), {
9606
+ kind: "mutation",
9607
+ auth: "admin"
9608
+ }),
9609
+ /**
9487
9610
  * Re-sync a device with its source via the device-adoption provider of the
9488
9611
  * device's OWNING addon (resolved from `camDeviceId`). Unlike the singleton
9489
9612
  * `device-adoption.resync`, this routes to the correct integration so a
@@ -17112,6 +17235,77 @@ var snapshotCapability = {
17112
17235
  lastCapturedAt: z.number().nullable(),
17113
17236
  cacheAgeMs: z.number().nullable(),
17114
17237
  etag: z.string().nullable()
17238
+ }))),
17239
+ /**
17240
+ * Signed, expiring links to a CLIENT-SIZED frame — and the demand signal
17241
+ * that makes those frames current.
17242
+ *
17243
+ * ## The problem it replaces
17244
+ *
17245
+ * `getSnapshotOverview` is cache-only by contract: it answers from whatever
17246
+ * the wrapper happens to hold and never captures. Under D93 the client
17247
+ * versions its image URL on that answer, and an image REQUEST is what enrols
17248
+ * a camera in the keep-warm loop. Both of those are satisfiable by the
17249
+ * client's own image cache — `expo-image` is URL-keyed and never revalidates
17250
+ * — so a URL painted in a previous session comes off disk with no network,
17251
+ * no enrolment, and nothing warming. Measured on the live hub: reopening
17252
+ * after two minutes idle painted 15 of 16 tiles at **168 s old** with zero
17253
+ * HTTP requests, and the fleet only recovered because a later poll happened
17254
+ * to observe a different identity.
17255
+ *
17256
+ * ## The two properties that fix it
17257
+ *
17258
+ * **It is an RPC, so no client cache can answer it.** The demand signal
17259
+ * always reaches the wrapper. This method therefore MAY create keep-warm
17260
+ * subscriptions, where `getSnapshotOverview` must never (D93) — the
17261
+ * distinction is not "one is newer" but that the overview poll is app-wide
17262
+ * (a creating overview would warm every camera on the install) while this is
17263
+ * called by a rendered surface naming the tiles it is actually painting, at
17264
+ * the width it is painting them.
17265
+ *
17266
+ * **It waits, briefly and boundedly, for the capture it triggered.** The
17267
+ * returned `capturedAt` is the frame the link will serve, not the frame the
17268
+ * cache held when the client asked, so a first paint is honest and current
17269
+ * instead of a generation behind. A device that does not settle inside the
17270
+ * bound still gets a link and its real (older) `capturedAt` — the next poll
17271
+ * carries it forward.
17272
+ *
17273
+ * `force` is never set on behalf of a client here. A sleeping battery camera
17274
+ * is reported with `sleeping: true` and the last frame it produced, however
17275
+ * old; the wrapper's existing sleep gate owns that decision and this method
17276
+ * adds no second one.
17277
+ */
17278
+ getSnapshotLinks: systemMethod(z.object({
17279
+ /** The tiles a surface is actually rendering. One entry per (device,
17280
+ * width) the caller will paint — the width is snapped to the server's
17281
+ * ladder and becomes part of the link's SIGNED identity. */
17282
+ targets: z.array(z.object({
17283
+ deviceId: z.number(),
17284
+ /** Target width in px. Omit for the frame as captured — correct
17285
+ * for a full-bleed surface, wrong (and expensive) for a grid. */
17286
+ width: z.number().int().positive().optional()
17287
+ })).min(1).max(200) }), z.array(z.object({
17288
+ deviceId: z.number(),
17289
+ /** Root-relative signed path, or null when the link plane is not
17290
+ * served (no data-plane facility). Present even for a device that has
17291
+ * never captured — the request is what triggers the first one (D94). */
17292
+ url: z.string().nullable(),
17293
+ /** Epoch ms of the frame this link serves. Null = never captured.
17294
+ * THE honest age: the tRPC path carried none before this. */
17295
+ capturedAt: z.number().nullable(),
17296
+ /** Age of that frame at the moment the answer was built. */
17297
+ ageMs: z.number().nullable(),
17298
+ /** Epoch ms after which `url` stops verifying. */
17299
+ expiresAt: z.number().nullable(),
17300
+ /** Ladder rung the bytes are at; null = the frame as captured. */
17301
+ width: z.number().nullable(),
17302
+ /** The device has never produced a frame. An empty state, not a
17303
+ * failure — and never a reason to withhold the link (D94). */
17304
+ neverCaptured: z.boolean(),
17305
+ /** A sleeping battery camera: the frame is deliberately stale and will
17306
+ * NOT refresh in the background. A surface should say so rather than
17307
+ * present it as current. */
17308
+ sleeping: z.boolean()
17115
17309
  })))
17116
17310
  },
17117
17311
  status: {
@@ -19726,6 +19920,45 @@ onColorChanged: { data: z.object({
19726
19920
  */
19727
19921
  runtimeState: ColorStatusSchema
19728
19922
  };
19923
+ var ConnectionTestOutcomeSchema = z.discriminatedUnion("outcome", [
19924
+ z.object({
19925
+ outcome: z.literal("validated"),
19926
+ /** Round-trip of the sign-in, when the provider measured it. */
19927
+ latencyMs: z.number().nonnegative().optional(),
19928
+ /** Optional human detail worth showing next to the tick
19929
+ * ("3 devices visible on this account"). */
19930
+ detail: z.string().optional()
19931
+ }).strict(),
19932
+ z.object({
19933
+ outcome: z.literal("rejected"),
19934
+ error: z.string()
19935
+ }).strict(),
19936
+ z.object({
19937
+ outcome: z.literal("inconclusive"),
19938
+ error: z.string()
19939
+ }).strict()
19940
+ ]);
19941
+ var ConnectionTestInputSchema = z.object({
19942
+ /** Candidate integration settings, exactly as the create form collected them. */
19943
+ settings: z.record(z.string(), z.unknown()) });
19944
+ /**
19945
+ * What the provider's test actually DOES, so the UI can say it in words before
19946
+ * the operator presses the button ("Signs in to the Dreo cloud"). Purely
19947
+ * descriptive — it never changes routing.
19948
+ */
19949
+ var ConnectionTestDescriptorSchema = z.object({ label: z.string() });
19950
+ var connectionTestCapability = {
19951
+ name: "connection-test",
19952
+ scope: "system",
19953
+ mode: "collection",
19954
+ methods: {
19955
+ testSettings: method(ConnectionTestInputSchema, ConnectionTestOutcomeSchema, {
19956
+ kind: "mutation",
19957
+ auth: "admin"
19958
+ }),
19959
+ describeTest: method(z.void(), ConnectionTestDescriptorSchema, { auth: "admin" })
19960
+ }
19961
+ };
19729
19962
  /**
19730
19963
  * Upstream-system connectivity sensor — distinct from `device-status`,
19731
19964
  * which is the kernel-managed online/offline flag for the device's
@@ -21169,15 +21402,57 @@ var AvailableIntegrationTypeSchema = z.object({
21169
21402
  * flow can import (e.g. HA areas). Drives the adopt modal's "import
21170
21403
  * locations" checkbox. Provider-declared in the addon manifest. */
21171
21404
  supportsLocationImport: z.boolean(),
21405
+ /**
21406
+ * True when this integration DECLARES a pre-creation test (the
21407
+ * `connection-test` cap, or a broker whose settings it stores). Drives the
21408
+ * Test button: an integration that cannot be tested must say so up front
21409
+ * rather than offering a button that always answers the same nonsense.
21410
+ */
21411
+ canTest: z.boolean(),
21172
21412
  existingInstances: z.array(z.object({
21173
21413
  id: z.string(),
21174
21414
  name: z.string()
21175
21415
  })),
21176
21416
  canAdd: z.boolean()
21177
21417
  });
21418
+ /**
21419
+ * Why a test could not be answered as a plain boolean.
21420
+ *
21421
+ * `success` alone collapsed four different situations into one red box, and the
21422
+ * one that mattered most — "nobody ever asked the remote anything" — looked
21423
+ * exactly like "the remote said no". The status is the discriminator:
21424
+ *
21425
+ * - `validated` — a provider-declared test ran and the remote ACCEPTED.
21426
+ * - `rejected` — a provider-declared test ran and the remote REFUSED.
21427
+ * The only status that blocks `integrations.create`.
21428
+ * - `inconclusive` — a test IS declared but could not complete (timeout,
21429
+ * DNS, 5xx). Nothing was observed; not a failure.
21430
+ * - `unsupported` — this integration declares NO test. Nothing was
21431
+ * observed either; not a failure, and not a pass.
21432
+ *
21433
+ * `unsupported` and `inconclusive` both carry `success: false` so an older
21434
+ * client can never read them as a green tick, and both carry an `error` string
21435
+ * that SAYS the test did not run rather than inventing a failure.
21436
+ */
21437
+ var TestConnectionStatusEnum = z.enum([
21438
+ "validated",
21439
+ "rejected",
21440
+ "inconclusive",
21441
+ "unsupported"
21442
+ ]);
21178
21443
  var TestConnectionResultSchema$1 = z.object({
21444
+ /** True ONLY for `validated`. Never true for a test that did not run. */
21179
21445
  success: z.boolean(),
21180
- error: z.string().optional()
21446
+ error: z.string().optional(),
21447
+ /** Optional for wire back-compat with clients built before the tri-state;
21448
+ * the server always sets it. */
21449
+ status: TestConnectionStatusEnum.optional(),
21450
+ /** Addon id whose declared test answered — `null` when none did. Lets the UI
21451
+ * attribute a result instead of blaming "the integration". */
21452
+ testedBy: z.string().nullable().optional(),
21453
+ latencyMs: z.number().nonnegative().optional(),
21454
+ /** Human detail from a `validated` result ("3 devices on this account"). */
21455
+ detail: z.string().optional()
21181
21456
  });
21182
21457
  var CreateIntegrationInputSchema = z.object({
21183
21458
  addonId: z.string(),
@@ -26975,6 +27250,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
26975
27250
  carbonMonoxideCapability,
26976
27251
  climateControlCapability,
26977
27252
  colorCapability,
27253
+ connectionTestCapability,
26978
27254
  connectivityCapability,
26979
27255
  consumablesCapability,
26980
27256
  contactCapability,
@@ -27867,6 +28143,18 @@ var METHOD_ACCESS_MAP = Object.freeze({
27867
28143
  addonId: null,
27868
28144
  access: "create"
27869
28145
  },
28146
+ "connectionTest.describeTest": {
28147
+ capName: "connection-test",
28148
+ capScope: "system",
28149
+ addonId: null,
28150
+ access: "view"
28151
+ },
28152
+ "connectionTest.testSettings": {
28153
+ capName: "connection-test",
28154
+ capScope: "system",
28155
+ addonId: null,
28156
+ access: "create"
28157
+ },
27870
28158
  "consumables.reset": {
27871
28159
  capName: "consumables",
27872
28160
  capScope: "device",
@@ -28257,6 +28545,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
28257
28545
  addonId: null,
28258
28546
  access: "create"
28259
28547
  },
28548
+ "deviceManager.adoptionCancelJob": {
28549
+ capName: "device-manager",
28550
+ capScope: "system",
28551
+ addonId: null,
28552
+ access: "create"
28553
+ },
28260
28554
  "deviceManager.adoptionListCandidateFilters": {
28261
28555
  capName: "device-manager",
28262
28556
  capScope: "system",
@@ -28269,6 +28563,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
28269
28563
  addonId: null,
28270
28564
  access: "view"
28271
28565
  },
28566
+ "deviceManager.adoptionListJobs": {
28567
+ capName: "device-manager",
28568
+ capScope: "system",
28569
+ addonId: null,
28570
+ access: "view"
28571
+ },
28272
28572
  "deviceManager.adoptionRefresh": {
28273
28573
  capName: "device-manager",
28274
28574
  capScope: "system",
@@ -28287,6 +28587,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
28287
28587
  addonId: null,
28288
28588
  access: "create"
28289
28589
  },
28590
+ "deviceManager.adoptionStartJob": {
28591
+ capName: "device-manager",
28592
+ capScope: "system",
28593
+ addonId: null,
28594
+ access: "create"
28595
+ },
28290
28596
  "deviceManager.allocateDeviceId": {
28291
28597
  capName: "device-manager",
28292
28598
  capScope: "system",
@@ -31425,6 +31731,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
31425
31731
  addonId: null,
31426
31732
  access: "view"
31427
31733
  },
31734
+ "snapshot.getSnapshotLinks": {
31735
+ capName: "snapshot",
31736
+ capScope: "device",
31737
+ addonId: null,
31738
+ access: "view"
31739
+ },
31428
31740
  "snapshot.getSnapshotOverview": {
31429
31741
  capName: "snapshot",
31430
31742
  capScope: "device",