@camstack/addon-pipeline 1.2.370 → 1.2.372

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 (37) hide show
  1. package/dist/audio-analyzer/index.js +3 -3
  2. package/dist/audio-analyzer/index.mjs +3 -3
  3. package/dist/{custom-model-catalog-CVbK0xKc.mjs → custom-model-catalog-C6C2cSwd.mjs} +2 -2
  4. package/dist/{custom-model-catalog-BFosahFu.js → custom-model-catalog-n9v8MUL6.js} +2 -2
  5. package/dist/detection-pipeline/index.js +4 -4
  6. package/dist/detection-pipeline/index.mjs +4 -4
  7. package/dist/{dist-CbFPOFAD.mjs → dist-8AEiKOFO.mjs} +291 -21
  8. package/dist/{dist-jkwxbcbK.js → dist-DmuX7IFJ.js} +308 -20
  9. package/dist/{model-catalogs-DJmwfLfC.mjs → model-catalogs-BSWAHZJh.mjs} +1 -1
  10. package/dist/{model-catalogs-Ds2hC2Br.js → model-catalogs-DO4aA1qo.js} +1 -1
  11. package/dist/motion-wasm/index.js +1 -1
  12. package/dist/motion-wasm/index.mjs +1 -1
  13. package/dist/{node-C-2fB9kH.mjs → node-Bd4va7c-.mjs} +1 -1
  14. package/dist/{node-C_1U1uzn.js → node-DqA6nD1z.js} +1 -1
  15. package/dist/pipeline-runner/index.js +3 -3
  16. package/dist/pipeline-runner/index.mjs +3 -3
  17. package/dist/{process-memory-Cfh1BQYg.js → process-memory-2OQ2oxrz.js} +1 -1
  18. package/dist/{process-memory-B3I-NvnX.mjs → process-memory-BvS2Z88w.mjs} +1 -1
  19. package/dist/recorder/index.js +189 -54
  20. package/dist/recorder/index.mjs +189 -54
  21. package/dist/{segment-demux-js-uxBcJy15.js → segment-demux-js-BkGK1rhu.js} +1 -1
  22. package/dist/{segment-demux-js-D3JBJeN6.mjs → segment-demux-js-By9MLn_g.mjs} +1 -1
  23. package/dist/stream-broker/_stub.js +1188 -861
  24. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DwwDKaZJ.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DjoAY42S.mjs} +3 -3
  25. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-C3ssSNKh.mjs +26 -0
  26. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-nua-KuDg.mjs +26 -0
  27. package/dist/stream-broker/demux-worker-child.js +1 -1
  28. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  29. package/dist/stream-broker/{hostInit-DVwYY7oo.mjs → hostInit-DPiGNc5u.mjs} +3 -3
  30. package/dist/stream-broker/index.js +3 -3
  31. package/dist/stream-broker/index.mjs +3 -3
  32. package/dist/stream-broker/remoteEntry.js +1 -1
  33. package/i18n/en.json +8 -0
  34. package/i18n/it.json +8 -0
  35. package/package.json +6 -1
  36. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Bna0L8uC.mjs +0 -26
  37. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BAvXcDKy.mjs +0 -26
@@ -5387,7 +5387,7 @@ var ZodIssueCode = {
5387
5387
  var ZodFirstPartyTypeKind;
5388
5388
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5389
5389
  //#endregion
5390
- //#region ../types/dist/sleep-DAX0-2v9.mjs
5390
+ //#region ../types/dist/sleep-DRqxtc0C.mjs
5391
5391
  /**
5392
5392
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5393
5393
  * window to float samples (D455).
@@ -5536,7 +5536,7 @@ function isKeychainStoredField(field) {
5536
5536
  var SECRET_CLEAR = { $secretClear: true };
5537
5537
  /** The `'***'` token D634 redaction writes for non-admins. */
5538
5538
  var LEGACY_DEVICE_REDACTION = "***";
5539
- function isRecord$2(value) {
5539
+ function isRecord$2$1(value) {
5540
5540
  return typeof value === "object" && value !== null && !Array.isArray(value);
5541
5541
  }
5542
5542
  function makeSecretMarker(ref) {
@@ -5547,12 +5547,12 @@ function makeSecretMarker(ref) {
5547
5547
  }
5548
5548
  /** A stored marker: an object whose `$secret` is a non-empty string. */
5549
5549
  function isSecretMarker(value) {
5550
- if (!isRecord$2(value)) return false;
5550
+ if (!isRecord$2$1(value)) return false;
5551
5551
  const ref = value["$secret"];
5552
5552
  return typeof ref === "string" && ref.length > 0;
5553
5553
  }
5554
5554
  function isSecretClear(value) {
5555
- return isRecord$2(value) && value["$secretClear"] === true;
5555
+ return isRecord$2$1(value) && value["$secretClear"] === true;
5556
5556
  }
5557
5557
  /**
5558
5558
  * The UI view of a stored secret field value: a marker becomes "set", inline
@@ -6178,7 +6178,7 @@ var KeychainWriteRefusedError = class extends Error {
6178
6178
  }
6179
6179
  };
6180
6180
  var KEEP = { kind: "keep" };
6181
- function isRecord$1$1(value) {
6181
+ function isRecord$1(value) {
6182
6182
  return typeof value === "object" && value !== null && !Array.isArray(value);
6183
6183
  }
6184
6184
  function defaultMintId() {
@@ -6191,7 +6191,7 @@ function collectSecretMarkerRefs(value, out = []) {
6191
6191
  return out;
6192
6192
  }
6193
6193
  if (Array.isArray(value)) for (const item of value) collectSecretMarkerRefs(item, out);
6194
- else if (isRecord$1$1(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
6194
+ else if (isRecord$1(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
6195
6195
  return out;
6196
6196
  }
6197
6197
  async function storedValueEquals(secrets, marker, candidate) {
@@ -6246,7 +6246,7 @@ function rowId(row, idKey) {
6246
6246
  return null;
6247
6247
  }
6248
6248
  async function writeArray(ctx, node, incoming, stored, fieldPath) {
6249
- const storedRows = Array.isArray(stored) ? stored.filter(isRecord$1$1) : [];
6249
+ const storedRows = Array.isArray(stored) ? stored.filter(isRecord$1) : [];
6250
6250
  if (!Array.isArray(incoming)) {
6251
6251
  ctx.deletes.push(...collectSecretMarkerRefs(storedRows));
6252
6252
  return {
@@ -6262,7 +6262,7 @@ async function writeArray(ctx, node, incoming, stored, fieldPath) {
6262
6262
  const kept = /* @__PURE__ */ new Set();
6263
6263
  const rows = [];
6264
6264
  for (const row of incoming) {
6265
- if (!isRecord$1$1(row)) {
6265
+ if (!isRecord$1(row)) {
6266
6266
  rows.push(row);
6267
6267
  continue;
6268
6268
  }
@@ -6287,7 +6287,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
6287
6287
  case "leaf": return writeLeaf(ctx, incoming, stored, fieldPath);
6288
6288
  case "array": return writeArray(ctx, node, incoming, stored, fieldPath);
6289
6289
  case "object": {
6290
- if (!isRecord$1$1(incoming)) {
6290
+ if (!isRecord$1(incoming)) {
6291
6291
  if (incoming === void 0) return KEEP;
6292
6292
  ctx.deletes.push(...collectSecretMarkerRefs(stored));
6293
6293
  return {
@@ -6295,7 +6295,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
6295
6295
  value: incoming
6296
6296
  };
6297
6297
  }
6298
- const storedObj = isRecord$1$1(stored) ? stored : {};
6298
+ const storedObj = isRecord$1(stored) ? stored : {};
6299
6299
  return {
6300
6300
  kind: "value",
6301
6301
  value: await writeMembers(ctx, node.children, incoming, storedObj, (m) => `${fieldPath}.${m}`)
@@ -6418,7 +6418,7 @@ async function resolveSecretMarkers(value, secrets, options = {}) {
6418
6418
  value: items
6419
6419
  };
6420
6420
  }
6421
- if (isRecord$1$1(v)) {
6421
+ if (isRecord$1(v)) {
6422
6422
  const out = {};
6423
6423
  for (const [k, child] of Object.entries(v)) {
6424
6424
  const s = substitute(child);
@@ -8354,6 +8354,24 @@ method(object({ deviceId: number() }), array(StreamSourceEntrySchema)), method(o
8354
8354
  }), method(object({ deviceId: number() }), unknown().nullable()), method(object({ deviceId: number() }), RawStateResultSchema.nullable(), { auth: "protected" });
8355
8355
  var HF_REPO = "camstack/camstack-models";
8356
8356
  `${HF_REPO}`;
8357
+ object({
8358
+ code: string().regex(/^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/),
8359
+ params: record(string(), union([string(), number()])).optional()
8360
+ });
8361
+ var CamstackError = class extends Error {
8362
+ i18nCode;
8363
+ i18nParams;
8364
+ constructor(code, params, englishMessage) {
8365
+ super(englishMessage);
8366
+ this.name = "CamstackError";
8367
+ this.i18nCode = code;
8368
+ this.i18nParams = params;
8369
+ }
8370
+ };
8371
+ /** `throw userError('device.not-found', { id }, 'Device 12 not found')` */
8372
+ function userError(code, params, englishFallback) {
8373
+ return new CamstackError(code, params, englishFallback);
8374
+ }
8357
8375
  /**
8358
8376
  * The ONE retry schedule for work that can fail simply because the node was
8359
8377
  * still booting.
@@ -8491,7 +8509,7 @@ function sleep(ms) {
8491
8509
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
8492
8510
  }
8493
8511
  //#endregion
8494
- //#region ../types/dist/err-msg-DX6i_MY4.mjs
8512
+ //#region ../types/dist/err-msg-DLerKiVO.mjs
8495
8513
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
8496
8514
  DeviceType["Camera"] = "camera";
8497
8515
  DeviceType["Hub"] = "hub";
@@ -8741,6 +8759,22 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8741
8759
  DeviceRole["Nightvision"] = "nightvision";
8742
8760
  DeviceRole["PrivacyMask"] = "privacy-mask";
8743
8761
  DeviceRole["Doorbell"] = "doorbell";
8762
+ /** Interior blind / shade / roller blind. HA `device_class: blind|shade`. */
8763
+ DeviceRole["CoverBlind"] = "cover-blind";
8764
+ /** Exterior roller shutter. HA `device_class: shutter`; Homematic BROLL. */
8765
+ DeviceRole["CoverShutter"] = "cover-shutter";
8766
+ /** Exterior awning. HA `device_class: awning`. */
8767
+ DeviceRole["CoverAwning"] = "cover-awning";
8768
+ /** Curtain / drape. HA `device_class: curtain`. */
8769
+ DeviceRole["CoverCurtain"] = "cover-curtain";
8770
+ /** Garage door. HA `device_class: garage`. A security actuator: exports gate it. */
8771
+ DeviceRole["CoverGarage"] = "cover-garage";
8772
+ /** Gate. HA `device_class: gate`. A security actuator: exports gate it. */
8773
+ DeviceRole["CoverGate"] = "cover-gate";
8774
+ /** Door. HA `device_class: door`. A security actuator: exports gate it. */
8775
+ DeviceRole["CoverDoor"] = "cover-door";
8776
+ /** Window. HA `device_class: window`. */
8777
+ DeviceRole["CoverWindow"] = "cover-window";
8744
8778
  /** Virtual HA toggle (input_boolean.*) — distinguishable from a
8745
8779
  * real Switch device for UI rendering / export adapters. */
8746
8780
  DeviceRole["BinaryHelper"] = "binary-helper";
@@ -8821,7 +8855,7 @@ function errMsg(err) {
8821
8855
  return String(err);
8822
8856
  }
8823
8857
  //#endregion
8824
- //#region ../types/dist/composition-C-WpVYTX.mjs
8858
+ //#region ../types/dist/composition-EQ9Ok0Ee.mjs
8825
8859
  var AUDIO_ENCODER_BY_CODEC = {
8826
8860
  opus: "libopus",
8827
8861
  aac: "aac",
@@ -12379,20 +12413,20 @@ var SURVIVING_TRIGGER_KEYS = [
12379
12413
  "objectClasses",
12380
12414
  "sensorDeviceIds"
12381
12415
  ];
12382
- function isRecord$1(value) {
12416
+ function isRecord$2(value) {
12383
12417
  return typeof value === "object" && value !== null && !Array.isArray(value);
12384
12418
  }
12385
12419
  function listensToLegacySignal(band) {
12386
- return isRecord$1(band) && isRecord$1(band.triggers) && band.triggers.deviceSignal === true;
12420
+ return isRecord$2(band) && isRecord$2(band.triggers) && band.triggers.deviceSignal === true;
12387
12421
  }
12388
12422
  function hasSurvivingTrigger(band) {
12389
12423
  const triggers = band.triggers;
12390
- if (!isRecord$1(triggers)) return false;
12424
+ if (!isRecord$2(triggers)) return false;
12391
12425
  return SURVIVING_TRIGGER_KEYS.some((key) => triggers[key] !== void 0);
12392
12426
  }
12393
12427
  function withoutLegacySignal(band) {
12394
12428
  const triggers = band.triggers;
12395
- if (!isRecord$1(triggers)) return band;
12429
+ if (!isRecord$2(triggers)) return band;
12396
12430
  const { deviceSignal: _retired, ...rest } = triggers;
12397
12431
  return {
12398
12432
  ...band,
@@ -12422,11 +12456,11 @@ function migrateLegacyDeviceSignalConfig(raw) {
12422
12456
  migrated: false,
12423
12457
  strippedOnly: false
12424
12458
  };
12425
- if (!isRecord$1(raw) || !Array.isArray(raw.bands)) return unchanged;
12459
+ if (!isRecord$2(raw) || !Array.isArray(raw.bands)) return unchanged;
12426
12460
  if (!raw.bands.some(listensToLegacySignal)) return unchanged;
12427
12461
  const kept = [];
12428
12462
  for (const band of raw.bands) {
12429
- if (!listensToLegacySignal(band) || !isRecord$1(band)) {
12463
+ if (!listensToLegacySignal(band) || !isRecord$2(band)) {
12430
12464
  kept.push(band);
12431
12465
  continue;
12432
12466
  }
@@ -14655,7 +14689,8 @@ var ConvertResultSchema = object({
14655
14689
  })).readonly()
14656
14690
  });
14657
14691
  _enum(["new-session", "step-up"]);
14658
- _enum(["en", "it"]);
14692
+ /** The languages the hub's app landing pages are written in (D858). */
14693
+ var AppPageLangSchema = _enum(["en", "it"]);
14659
14694
  /** How a native app starts one login method (D858), as `auth.listLoginMethods` reports it. */
14660
14695
  var AppLoginPathSchema = discriminatedUnion("kind", [object({ kind: literal("native") }), object({
14661
14696
  kind: literal("browser"),
@@ -15152,7 +15187,9 @@ var ConfigTabDeclarationSchema = object({
15152
15187
  var ConfigSectionWithValuesSchema = object({
15153
15188
  id: string(),
15154
15189
  title: string(),
15190
+ titleKey: string().optional(),
15155
15191
  description: string().optional(),
15192
+ descriptionKey: string().optional(),
15156
15193
  style: _enum(["card", "accordion"]).optional(),
15157
15194
  defaultCollapsed: boolean().optional(),
15158
15195
  columns: union([
@@ -18850,6 +18887,9 @@ method(object({
18850
18887
  }), method(object({ refs: RefListSchema }), KeychainResolveResultSchema, { auth: "admin" }), method(object({ refs: RefListSchema }), object({ deleted: number().int() }), {
18851
18888
  kind: "mutation",
18852
18889
  auth: "admin"
18890
+ }), method(object({ deviceId: number().int() }), object({ refs: array(string()).readonly() }), {
18891
+ kind: "mutation",
18892
+ auth: "admin"
18853
18893
  }), method(object({}), array(KeychainSecretMetaSchema).readonly(), { auth: "admin" }), method(object({}), KeychainStateSchema, { auth: "admin" });
18854
18894
  /**
18855
18895
  * device-history — the ONE mechanism for numeric history (D850).
@@ -40284,6 +40324,42 @@ var DeviceRegisterPayloadSchema = object({
40284
40324
  * omits it and the keyword backstop applies. */
40285
40325
  secretConfigPaths: array(string()).optional()
40286
40326
  });
40327
+ /** One deleted device, kept by device-manager so an addon that missed the
40328
+ * deletion can still learn of it (D865). Written by the delete path ONLY. */
40329
+ var DeviceTombstoneSchema = object({
40330
+ deviceId: number().int().positive(),
40331
+ /** Epoch ms the device was deleted. */
40332
+ deletedAt: number(),
40333
+ /** The addon that owned the device when it was deleted. */
40334
+ providerAddonId: string()
40335
+ });
40336
+ /** How long a tombstone stays readable. Past it, a device that was deleted
40337
+ * and never cleaned is only reachable by the admin "clean data of deleted
40338
+ * devices" action. */
40339
+ var DEVICE_TOMBSTONE_RETENTION_MS = 2160 * 60 * 60 * 1e3;
40340
+ /** Data held for one device that device-manager no longer knows. */
40341
+ var OrphanDeviceDataSchema = object({
40342
+ deviceId: number().int().positive(),
40343
+ /** Rows/files per store (`<addonId>:<store>`). */
40344
+ stores: record(string(), number().int().nonnegative())
40345
+ });
40346
+ var CleanupDeletedDevicesResultSchema = object({
40347
+ /**
40348
+ * `skipped` — the absence check could not be trusted (a device-list read
40349
+ * failed or the two reads disagreed); nothing was listed or deleted.
40350
+ * `dry-run` — `devices` is what a confirmed run would delete.
40351
+ * `cleaned` — `devices` is what was deleted.
40352
+ */
40353
+ status: _enum([
40354
+ "skipped",
40355
+ "dry-run",
40356
+ "cleaned"
40357
+ ]),
40358
+ reason: string().nullable(),
40359
+ devices: array(OrphanDeviceDataSchema),
40360
+ /** Implementers that could not be asked (their stores are not in `devices`). */
40361
+ unreachable: array(string())
40362
+ });
40287
40363
  var DeviceRemovePayloadSchema = object({ deviceId: number() });
40288
40364
  var DevicePersistConfigPayloadSchema = object({
40289
40365
  deviceId: number(),
@@ -40522,6 +40598,9 @@ method(object({
40522
40598
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
40523
40599
  kind: "mutation",
40524
40600
  auth: "admin"
40601
+ }), method(object({ sinceMs: number() }), array(DeviceTombstoneSchema).readonly(), { auth: "admin" }), method(object({ confirm: boolean() }), CleanupDeletedDevicesResultSchema, {
40602
+ kind: "mutation",
40603
+ auth: "admin"
40525
40604
  }), method(object({ integrationId: string() }), object({ removed: number() }), {
40526
40605
  kind: "mutation",
40527
40606
  auth: "admin"
@@ -40984,6 +41063,85 @@ method(object({
40984
41063
  auth: "admin"
40985
41064
  });
40986
41065
  /**
41066
+ * `device-removal-hook` — the reliable way an addon learns a device was DELETED,
41067
+ * so it can delete what it keeps for that device (D865).
41068
+ *
41069
+ * ## Why an event was not enough
41070
+ *
41071
+ * `DeviceUnregistered` is telemetry and may be dropped (D8, D11). An addon that
41072
+ * hung its per-device clean-up on it left durable garbage whenever the event was
41073
+ * lost, the addon was down, or it was restarting at that moment — footage,
41074
+ * analytics rows and settings of device 4483 outlived the device. Loss of this
41075
+ * signal corrupts durable state, so it travels by RPC with a recorded fact
41076
+ * behind it (D8: "if loss would corrupt durable state, use RPC plus a reconcile").
41077
+ *
41078
+ * ## The three pieces
41079
+ *
41080
+ * 1. device-manager records a TOMBSTONE per deleted device
41081
+ * (`deviceManager.listDeletedSince`). The tombstone is written by the delete
41082
+ * path only — a device that is unregistered, offline or whose provider is
41083
+ * restarting is NOT deleted and gets none.
41084
+ * 2. After a delete, device-manager calls `onDeviceRemoved` on EVERY provider of
41085
+ * this collection, one by one, with bounded retries, logged.
41086
+ * 3. An implementer that was down asks `deviceManager.listDeletedSince` at start
41087
+ * (and periodically) and runs its own `onDeviceRemoved` for each tombstone it
41088
+ * may have missed. Reconcile is driven by tombstones, never by "the device is
41089
+ * not in the current list": a failed or partial list read must not destroy data.
41090
+ *
41091
+ * ## Contract for an implementer
41092
+ *
41093
+ * - **Idempotent.** It is called again by a retry, by a reconcile and by the
41094
+ * admin cleanup of data that predates tombstones. A device with nothing left
41095
+ * answers zero counts.
41096
+ * - **One key per store** in `counts`, deleted rows/files per store, so the log
41097
+ * line and the admin dry run can say where the data was.
41098
+ * - **Training datasets are NOT device data** and are never deleted by a hook.
41099
+ * - **Per-device SETTINGS are not hooked.** Configuration lives in the standard
41100
+ * per-device store (`ctx.settings.*DeviceStore`), which device-manager clears
41101
+ * for every addon centrally at delete time. This hook is for device DATA
41102
+ * that is not settings: footage, event media, analytics rows, history.
41103
+ * - A provider that cannot tell is allowed to throw; the caller retries it.
41104
+ *
41105
+ * `listDeviceData` is the read side of the admin "Clean data of deleted devices"
41106
+ * action: which device ids this addon holds data for, and how much per store. It
41107
+ * never deletes. device-manager decides which of those ids are absent from the
41108
+ * fleet, from TWO consistent reads of the device list (D49).
41109
+ */
41110
+ /** What a hook is told about the deletion. */
41111
+ var DeviceRemovedInputSchema = object({
41112
+ deviceId: number().int().positive(),
41113
+ /** Epoch ms the device was deleted (the tombstone's `deletedAt`). */
41114
+ deletedAt: number(),
41115
+ /** The addon that owned the device, as device-manager last knew it. */
41116
+ providerAddonId: string()
41117
+ });
41118
+ /** Rows/files/refs deleted, per store this implementer owns. */
41119
+ var DeviceRemovedResultSchema = object({ counts: record(string(), number().int().nonnegative()) });
41120
+ /** One (store, device) pair an implementer holds data for. */
41121
+ var DeviceDataEntrySchema = object({
41122
+ store: string(),
41123
+ deviceId: number().int().positive(),
41124
+ /** Rows (or files) held for the device in the store. */
41125
+ count: number().int().nonnegative()
41126
+ });
41127
+ var deviceRemovalHookCapability = {
41128
+ name: "device-removal-hook",
41129
+ scope: "system",
41130
+ mode: "collection",
41131
+ internal: true,
41132
+ methods: {
41133
+ /** Delete everything this addon keeps for the deleted device. Idempotent. */
41134
+ onDeviceRemoved: method(DeviceRemovedInputSchema, DeviceRemovedResultSchema, {
41135
+ kind: "mutation",
41136
+ auth: "admin"
41137
+ }),
41138
+ /** Which devices this addon holds data for, per store. Reads only. */
41139
+ listDeviceData: method(_void(), array(DeviceDataEntrySchema).readonly(), { auth: "admin" })
41140
+ },
41141
+ /** In-process only — enumerated through `addons.listCapabilityProviders`. */
41142
+ mount: { kind: "skip" }
41143
+ };
41144
+ /**
40987
41145
  * `failure-contribution` — the capability an addon reports its OWN losses
40988
41146
  * through, per camera, with the denominator attached. It stores nothing.
40989
41147
  *
@@ -67380,6 +67538,12 @@ Object.freeze({
67380
67538
  addonId: null,
67381
67539
  access: "create"
67382
67540
  },
67541
+ "deviceManager.cleanupDeletedDevicesData": {
67542
+ capName: "device-manager",
67543
+ capScope: "system",
67544
+ addonId: null,
67545
+ access: "create"
67546
+ },
67383
67547
  "deviceManager.createDevice": {
67384
67548
  capName: "device-manager",
67385
67549
  capScope: "system",
@@ -67560,6 +67724,12 @@ Object.freeze({
67560
67724
  addonId: null,
67561
67725
  access: "view"
67562
67726
  },
67727
+ "deviceManager.listDeletedSince": {
67728
+ capName: "device-manager",
67729
+ capScope: "system",
67730
+ addonId: null,
67731
+ access: "view"
67732
+ },
67563
67733
  "deviceManager.listDeviceIdsByCaps": {
67564
67734
  capName: "device-manager",
67565
67735
  capScope: "system",
@@ -67968,6 +68138,18 @@ Object.freeze({
67968
68138
  addonId: null,
67969
68139
  access: "create"
67970
68140
  },
68141
+ "deviceRemovalHook.listDeviceData": {
68142
+ capName: "device-removal-hook",
68143
+ capScope: "system",
68144
+ addonId: null,
68145
+ access: "view"
68146
+ },
68147
+ "deviceRemovalHook.onDeviceRemoved": {
68148
+ capName: "device-removal-hook",
68149
+ capScope: "system",
68150
+ addonId: null,
68151
+ access: "create"
68152
+ },
67971
68153
  "deviceState.claimFields": {
67972
68154
  capName: "device-state",
67973
68155
  capScope: "system",
@@ -68400,6 +68582,12 @@ Object.freeze({
68400
68582
  addonId: null,
68401
68583
  access: "delete"
68402
68584
  },
68585
+ "keychain.deleteDeviceScope": {
68586
+ capName: "keychain",
68587
+ capScope: "system",
68588
+ addonId: null,
68589
+ access: "delete"
68590
+ },
68403
68591
  "keychain.getState": {
68404
68592
  capName: "keychain",
68405
68593
  capScope: "system",
@@ -74412,6 +74600,11 @@ Object.freeze({
74412
74600
  form: "single",
74413
74601
  optional: false
74414
74602
  }],
74603
+ "deviceRemovalHook.onDeviceRemoved": [{
74604
+ name: "deviceId",
74605
+ form: "single",
74606
+ optional: false
74607
+ }],
74415
74608
  "deviceState.claimFields": [{
74416
74609
  name: "deviceId",
74417
74610
  form: "single",
@@ -74601,6 +74794,11 @@ Object.freeze({
74601
74794
  form: "single",
74602
74795
  optional: false
74603
74796
  }],
74797
+ "keychain.deleteDeviceScope": [{
74798
+ name: "deviceId",
74799
+ form: "single",
74800
+ optional: false
74801
+ }],
74604
74802
  "lawnMowerControl.dock": [{
74605
74803
  name: "deviceId",
74606
74804
  form: "single",
@@ -76999,6 +77197,8 @@ new Set([
76999
77197
  "audio-tools",
77000
77198
  "audio-silence"
77001
77199
  ]);
77200
+ AppPageLangSchema.options;
77201
+ record(string().min(1), string());
77002
77202
  /**
77003
77203
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
77004
77204
  *
@@ -78564,6 +78764,76 @@ function enumerateInferenceDevices(hw) {
78564
78764
  }
78565
78765
  return out;
78566
78766
  }
78767
+ /** How often an implementer re-asks for tombstones after its start-up pass. */
78768
+ var REMOVAL_RECONCILE_INTERVAL_MS = 360 * 60 * 1e3;
78769
+ /** One reconcile pass over every tombstone inside the retention window. */
78770
+ async function reconcileRemovedDevices(opts) {
78771
+ const now = opts.now ?? (() => Date.now());
78772
+ const api = opts.api;
78773
+ if (api === void 0) {
78774
+ opts.logger.warn("device removal reconcile skipped — no device-manager api");
78775
+ return {
78776
+ read: false,
78777
+ applied: 0,
78778
+ failed: 0
78779
+ };
78780
+ }
78781
+ let tombstones;
78782
+ try {
78783
+ tombstones = await api.deviceManager.listDeletedSince.query({ sinceMs: now() - DEVICE_TOMBSTONE_RETENTION_MS });
78784
+ } catch (err) {
78785
+ opts.logger.warn("device removal reconcile skipped — tombstones unreadable, nothing deleted", { meta: { error: errMsg(err) } });
78786
+ return {
78787
+ read: false,
78788
+ applied: 0,
78789
+ failed: 0
78790
+ };
78791
+ }
78792
+ let applied = 0;
78793
+ let failed = 0;
78794
+ for (const tombstone of tombstones) try {
78795
+ const result = await opts.remove(tombstone);
78796
+ applied += 1;
78797
+ if (Object.values(result.counts).reduce((sum, n) => sum + n, 0) > 0) opts.logger.info("device removal reconcile cleaned a missed deletion", {
78798
+ tags: { deviceId: tombstone.deviceId },
78799
+ meta: {
78800
+ counts: result.counts,
78801
+ deletedAt: tombstone.deletedAt
78802
+ }
78803
+ });
78804
+ } catch (err) {
78805
+ failed += 1;
78806
+ opts.logger.warn("device removal reconcile failed for a device — retried next pass", {
78807
+ tags: { deviceId: tombstone.deviceId },
78808
+ meta: { error: errMsg(err) }
78809
+ });
78810
+ }
78811
+ return {
78812
+ read: true,
78813
+ applied,
78814
+ failed
78815
+ };
78816
+ }
78817
+ /**
78818
+ * Run the reconcile now and then periodically. Returns the disposer. The timer
78819
+ * is unref'd and a pass never overlaps the previous one.
78820
+ */
78821
+ function armRemovalReconcile(opts) {
78822
+ let running = false;
78823
+ const pass = () => {
78824
+ if (running) return;
78825
+ running = true;
78826
+ reconcileRemovedDevices(opts).catch((err) => {
78827
+ opts.logger.warn("device removal reconcile pass crashed", { meta: { error: errMsg(err) } });
78828
+ }).finally(() => {
78829
+ running = false;
78830
+ });
78831
+ };
78832
+ pass();
78833
+ const timer = setInterval(pass, REMOVAL_RECONCILE_INTERVAL_MS);
78834
+ timer.unref?.();
78835
+ return () => clearInterval(timer);
78836
+ }
78567
78837
  //#endregion
78568
78838
  Object.defineProperty(exports, "APPEARANCE_BLOCKS", {
78569
78839
  enumerable: true,
@@ -78943,6 +79213,12 @@ Object.defineProperty(exports, "applyModelBoundSettings", {
78943
79213
  return applyModelBoundSettings;
78944
79214
  }
78945
79215
  });
79216
+ Object.defineProperty(exports, "armRemovalReconcile", {
79217
+ enumerable: true,
79218
+ get: function() {
79219
+ return armRemovalReconcile;
79220
+ }
79221
+ });
78946
79222
  Object.defineProperty(exports, "array", {
78947
79223
  enumerable: true,
78948
79224
  get: function() {
@@ -79105,6 +79381,12 @@ Object.defineProperty(exports, "detectionPipelineCapability", {
79105
79381
  return detectionPipelineCapability;
79106
79382
  }
79107
79383
  });
79384
+ Object.defineProperty(exports, "deviceRemovalHookCapability", {
79385
+ enumerable: true,
79386
+ get: function() {
79387
+ return deviceRemovalHookCapability;
79388
+ }
79389
+ });
79108
79390
  Object.defineProperty(exports, "discriminatedUnion", {
79109
79391
  enumerable: true,
79110
79392
  get: function() {
@@ -79573,6 +79855,12 @@ Object.defineProperty(exports, "union", {
79573
79855
  return union;
79574
79856
  }
79575
79857
  });
79858
+ Object.defineProperty(exports, "userError", {
79859
+ enumerable: true,
79860
+ get: function() {
79861
+ return userError;
79862
+ }
79863
+ });
79576
79864
  Object.defineProperty(exports, "videoclipsCapability", {
79577
79865
  enumerable: true,
79578
79866
  get: function() {
@@ -1,4 +1,4 @@
1
- import { Bn as hfModelUrl } from "./dist-CbFPOFAD.mjs";
1
+ import { Hn as hfModelUrl } from "./dist-8AEiKOFO.mjs";
2
2
  //#region src/detection-pipeline/registry/model-licenses.ts
3
3
  var COCO_DATA = {
4
4
  name: "COCO 2017",
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-jkwxbcbK.js");
1
+ const require_dist = require("./dist-DmuX7IFJ.js");
2
2
  //#region src/detection-pipeline/registry/model-licenses.ts
3
3
  var COCO_DATA = {
4
4
  name: "COCO 2017",
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-jkwxbcbK.js");
5
+ const require_dist = require("../dist-DmuX7IFJ.js");
6
6
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
7
7
  let node_fs = require("node:fs");
8
8
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { An as BaseAddon, Dn as DeviceType, Ht as motionDetectionCapability, Vn as hydrateSchema, bt as evaluateZoneRules } from "../dist-CbFPOFAD.mjs";
1
+ import { Mn as BaseAddon, St as evaluateZoneRules, Un as hydrateSchema, Wt as motionDetectionCapability, kn as DeviceType } from "../dist-8AEiKOFO.mjs";
2
2
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
@@ -1,4 +1,4 @@
1
- import { On as errMsg, Sn as buildFfmpegArgs, Tn as isSoftwareDecode, xn as Fmp4BoxSplitter } from "./dist-CbFPOFAD.mjs";
1
+ import { An as errMsg, Cn as Fmp4BoxSplitter, Dn as isSoftwareDecode, wn as buildFfmpegArgs } from "./dist-8AEiKOFO.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { realpathSync } from "node:fs";
4
4
  import path from "node:path";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-jkwxbcbK.js");
2
+ const require_dist = require("./dist-DmuX7IFJ.js");
3
3
  require("node:os");
4
4
  let node_crypto = require("node:crypto");
5
5
  let node_fs = require("node:fs");
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-jkwxbcbK.js");
7
- const require_node = require("../node-C_1U1uzn.js");
8
- const require_custom_model_catalog = require("../custom-model-catalog-BFosahFu.js");
6
+ const require_dist = require("../dist-DmuX7IFJ.js");
7
+ const require_node = require("../node-DqA6nD1z.js");
8
+ const require_custom_model_catalog = require("../custom-model-catalog-n9v8MUL6.js");
9
9
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
10
10
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
11
11
  const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
@@ -1,6 +1,6 @@
1
- import { A as NativeLeaseAdmissionSchema, An as BaseAddon, E as FailureCounters, In as createEvent, Kn as nodePin, Kt as pickDetailCropConvention, Ln as customAction, M as NodeBenchRunnerInputSchema, Mt as linkedStepOf, Nt as loadContributionCapability, On as errMsg, P as PIPELINE_RAMP, Pn as MODEL_FORMATS, Qn as boolean, Rn as defineCustomActions, V as ROOT_SUPPLIABLE_STEP_IDS, Xn as _enum, Y as allowedSuppliedEffect, Yt as pipelineRunnerCapability, Zn as array, _ as DEFAULT_DETAIL_CROP_CONVENTION, a as BackgroundRunRegistry, an as resolveModelBoundSetting, cn as resolveRootSupply, cr as EventCategory, er as lazy, ht as deriveRootFaceRefineRect, j as NativeLeaseSettingsSchema, ln as rootSuppliesOf, m as CROP_SUPPLY_SOURCES, nr as number, nt as cameraOwnsMotionViaSensor, or as string, pt as deriveDetailCropRect, qt as pickNativeLeaseOverride, rr as object, tr as literal, tt as buildBenchStepTree, wt as failureContributionCapability, x as DEVICE_RAMP, y as DEFAULT_NATIVE_LEASE_SETTINGS } from "../dist-CbFPOFAD.mjs";
2
- import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-C-2fB9kH.mjs";
3
- import { a as stepsForElectedPool, b as getStepDefinition, d as rootObjectGate, f as localFrameRegistry, h as resolveFrameViewGeometry, i as placeDetailStep, l as resolveDefaultDetectionModel, m as createSessionDecodeCoordinator, p as isSessionDecodeReadGone, s as resolveDeviceEngine, t as CustomModelCatalog, u as macroGateVerdict, y as getStep } from "../custom-model-catalog-CVbK0xKc.mjs";
1
+ import { $n as _enum, A as NativeLeaseAdmissionSchema, An as errMsg, Bn as defineCustomActions, E as FailureCounters, Et as failureContributionCapability, Ft as loadContributionCapability, In as MODEL_FORMATS, Jn as nodePin, Jt as pickDetailCropConvention, M as NodeBenchRunnerInputSchema, Mn as BaseAddon, P as PIPELINE_RAMP, Pt as linkedStepOf, Rn as createEvent, V as ROOT_SUPPLIABLE_STEP_IDS, Y as allowedSuppliedEffect, Yt as pickNativeLeaseOverride, Zt as pipelineRunnerCapability, _ as DEFAULT_DETAIL_CROP_CONVENTION, a as BackgroundRunRegistry, ar as number, dn as rootSuppliesOf, dr as EventCategory, er as array, gt as deriveRootFaceRefineRect, ir as literal, j as NativeLeaseSettingsSchema, lr as string, m as CROP_SUPPLY_SOURCES, mt as deriveDetailCropRect, nt as buildBenchStepTree, or as object, rr as lazy, rt as cameraOwnsMotionViaSensor, sn as resolveModelBoundSetting, tr as boolean, un as resolveRootSupply, x as DEVICE_RAMP, y as DEFAULT_NATIVE_LEASE_SETTINGS, zn as customAction } from "../dist-8AEiKOFO.mjs";
2
+ import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-Bd4va7c-.mjs";
3
+ import { a as stepsForElectedPool, b as getStepDefinition, d as rootObjectGate, f as localFrameRegistry, h as resolveFrameViewGeometry, i as placeDetailStep, l as resolveDefaultDetectionModel, m as createSessionDecodeCoordinator, p as isSessionDecodeReadGone, s as resolveDeviceEngine, t as CustomModelCatalog, u as macroGateVerdict, y as getStep } from "../custom-model-catalog-C6C2cSwd.mjs";
4
4
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
5
5
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
6
6
  import { c as withDetectionIntent, o as preRollRequestOf, t as DETECTION_PRE_ROLL_REQUEST_MS } from "../restream-intent-DyGmLAU9.mjs";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-jkwxbcbK.js");
2
+ const require_dist = require("./dist-DmuX7IFJ.js");
3
3
  let node_fs = require("node:fs");
4
4
  node_fs = require_chunk.__toESM(node_fs);
5
5
  let node_child_process = require("node:child_process");
@@ -1,4 +1,4 @@
1
- import { Wt as parseProcStatus } from "./dist-CbFPOFAD.mjs";
1
+ import { Kt as parseProcStatus } from "./dist-8AEiKOFO.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile } from "node:child_process";
4
4
  //#region src/shared/node-topology-platform.ts