@camstack/system 1.2.275 → 1.2.277

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 (63) 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/autotrack/index.js +1 -1
  8. package/dist/builtins/autotrack/index.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  11. package/dist/builtins/camera-grid/grid-last-frame-store.d.ts +39 -0
  12. package/dist/builtins/camera-grid/index.js +67 -12
  13. package/dist/builtins/camera-grid/index.mjs +67 -12
  14. package/dist/builtins/console-logging/index.js +1 -1
  15. package/dist/builtins/console-logging/index.mjs +1 -1
  16. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  17. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  18. package/dist/builtins/device-manager/device-aggregation.d.ts +0 -6
  19. package/dist/builtins/device-manager/device-bindings-store.d.ts +22 -2
  20. package/dist/builtins/device-manager/device-manager.addon.d.ts +4 -0
  21. package/dist/builtins/device-manager/device-manager.addon.js +122 -58
  22. package/dist/builtins/device-manager/device-manager.addon.mjs +122 -59
  23. package/dist/builtins/device-manager/device-meta-types.d.ts +24 -3
  24. package/dist/builtins/device-manager/device-source-activation.d.ts +36 -0
  25. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  26. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  27. package/dist/builtins/hub-forwarder/index.js +1 -1
  28. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  29. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  30. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  31. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  32. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  33. package/dist/builtins/local-network/local-network.addon.js +1 -1
  34. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  35. package/dist/builtins/loki-logging/index.js +1 -1
  36. package/dist/builtins/loki-logging/index.mjs +1 -1
  37. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  38. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  39. package/dist/builtins/platform-probe/index.js +1 -1
  40. package/dist/builtins/platform-probe/index.mjs +1 -1
  41. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  42. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  43. package/dist/builtins/snapshot/index.js +1 -1
  44. package/dist/builtins/snapshot/index.mjs +1 -1
  45. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  46. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  47. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  48. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  49. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  50. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  51. package/dist/builtins/system-config/system-config.addon.js +1 -1
  52. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  53. package/dist/builtins/winston-logging/index.js +1 -1
  54. package/dist/builtins/winston-logging/index.mjs +1 -1
  55. package/dist/{dist-CYXhwfDf.js → dist-Bu8HN-KS.js} +354 -11
  56. package/dist/{dist-DWSJVcZe.mjs → dist-DOF9wnat.mjs} +349 -12
  57. package/dist/index.d.ts +7 -0
  58. package/dist/index.js +61 -48
  59. package/dist/index.mjs +62 -50
  60. package/dist/kernel/capability-registry.d.ts +28 -8
  61. package/dist/{retired-settings-keys-DEFtUH8o.js → retired-settings-keys-BwE6S2eQ.js} +1 -1
  62. package/dist/{retired-settings-keys-BHGnCLW0.mjs → retired-settings-keys-DXMfts8c.mjs} +1 -1
  63. package/package.json +2 -2
@@ -691,7 +691,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
691
691
  return EventCategory;
692
692
  }({});
693
693
  //#endregion
694
- //#region ../types/dist/sleep-CiR4z7r9.mjs
694
+ //#region ../types/dist/sleep-7AN_Txmu.mjs
695
695
  /**
696
696
  * The audio chunk plane's byte format, and the ONE expansion from a coded
697
697
  * window to float samples (D455).
@@ -2724,6 +2724,7 @@ function toNodeId(idOrRunnerId) {
2724
2724
  function resolveCapMount(def) {
2725
2725
  if (def.mount) return def.mount;
2726
2726
  if (def.deviceNative === true) return { kind: "device-native" };
2727
+ if (def.scope === "device" && def.kind === "wrapper") return { kind: "device-scoped" };
2727
2728
  if (def.mode === "collection") return { kind: "collection" };
2728
2729
  return { kind: "singleton" };
2729
2730
  }
@@ -10380,6 +10381,33 @@ function objectInputDeclaresAddonId(schema) {
10380
10381
  const shape = direct !== void 0 && direct !== null ? direct : resolveDefShape(schema._def?.shape);
10381
10382
  return shape !== void 0 && shape !== null && typeof shape === "object" && Object.prototype.hasOwnProperty.call(shape, "addonId");
10382
10383
  }
10384
+ /**
10385
+ * True when an OBJECT input schema declares a top-level, REQUIRED, numeric
10386
+ * `deviceId` — i.e. the method names exactly one device on every call.
10387
+ *
10388
+ * This is the question the per-device dispatcher asks at runtime
10389
+ * (`requireDeviceScoped`: `typeof input.deviceId !== 'number'` → BAD_REQUEST),
10390
+ * asked once at build time instead. A `device-scoped` cap's method that cannot
10391
+ * answer it has no device to resolve for and therefore resolves through the
10392
+ * cap's SYSTEM provider (`cap-router-builder.ts`) — the same destination
10393
+ * `systemOnly` names explicitly. Without this, flipping the mount would 400
10394
+ * every batch (`deviceIds`), fleet-wide (`z.void()`) and job-keyed method on
10395
+ * the eight device-scoped wrapper caps.
10396
+ *
10397
+ * Structural, never a name heuristic over source text: the field must resolve
10398
+ * to `z.number()` and must NOT be wrapped in `optional` / `nullable` /
10399
+ * `default` — an absent value is exactly the input the dispatcher rejects.
10400
+ */
10401
+ function objectInputDeclaresRequiredDeviceId(schema) {
10402
+ if (schema === null || typeof schema !== "object" && typeof schema !== "function") return false;
10403
+ const direct = schema.shape;
10404
+ const shape = direct !== void 0 && direct !== null ? direct : resolveDefShape(schema._def?.shape);
10405
+ if (shape === void 0 || shape === null || typeof shape !== "object") return false;
10406
+ const field = shape["deviceId"];
10407
+ if (field === void 0 || field === null) return false;
10408
+ const def = field._def;
10409
+ return field.constructor?.name === "ZodNumber" || def?.type === "number" || def?.typeName === "ZodNumber";
10410
+ }
10383
10411
  /** `_def.shape` may be a thunk (Zod defers object-shape evaluation) or a plain record. */
10384
10412
  function resolveDefShape(defShape) {
10385
10413
  return typeof defShape === "function" ? defShape() : defShape;
@@ -13210,14 +13238,30 @@ var deviceManagerCapability = {
13210
13238
  */
13211
13239
  getAllBindings: method(z.object({}), z.array(DeviceBindingsForDeviceSchema)),
13212
13240
  /**
13213
- * Activate (or deactivate) a wrapper addon for a (device, cap) pair.
13214
- * Persists the binding via ctx.settings. active=false clears the wrapper
13215
- * (falls back to native if registered).
13241
+ * Activate (or deactivate) a wrapper addon — or ONE of its sources — for a
13242
+ * (device, cap) pair.
13243
+ *
13244
+ * Persists the binding via ctx.settings; `active: false` on a SINGLETON cap
13245
+ * clears the wrapper (falling back to native if one is registered). On a
13246
+ * COLLECTION cap it adds to the per-device DENY set instead, and the
13247
+ * wrapper binding is left exactly as it was — a cap with several sources
13248
+ * has no "which one" for `getBindings` to answer (D556).
13249
+ *
13250
+ * `sourceId` is what makes the switch match its label. One addon can serve
13251
+ * several of a device's sources (`addon-provider-reolink` answers a hub
13252
+ * child with `native:reolink:onboard` AND `native:reolink:hub`), so a
13253
+ * toggle keyed by addon would turn off both while saying it turned off one
13254
+ * — the D62 defect with the polarity reversed (D557). Naming the source
13255
+ * denies exactly that source. OMITTING it keeps the cap-wide meaning
13256
+ * D556 shipped: every source this addon serves for this device, which is
13257
+ * also how a deny entry persisted before the re-key is still read.
13216
13258
  */
13217
13259
  setWrapperActive: method(z.object({
13218
13260
  deviceId: z.number(),
13219
13261
  capName: z.string(),
13220
13262
  wrapperAddonId: z.string(),
13263
+ /** The `ClipSource.source` id to toggle. Absent = all of the addon's. */
13264
+ sourceId: z.string().optional(),
13221
13265
  active: z.boolean()
13222
13266
  }), z.void(), {
13223
13267
  kind: "mutation",
@@ -25975,41 +26019,323 @@ var ClipSchema = z.object({
25975
26019
  holes: z.array(z.object({
25976
26020
  startMs: z.number(),
25977
26021
  endMs: z.number()
25978
- })).optional()
26022
+ })).optional(),
26023
+ /**
26024
+ * A URL that MAY produce this clip's still, asked only for rows actually on
26025
+ * screen. The opposite of {@link ClipSchema.thumbnail}: that one is VOUCHED
26026
+ * (a JPEG is already on disk), this one is an offer. The route answers 200
26027
+ * with the image, or 204 with `x-camstack-reason` when it could not mint one
26028
+ * — a surface latches that refusal to the instant rather than retrying.
26029
+ *
26030
+ * Never both: a clip with a vouched `thumbnail` needs no mint.
26031
+ */
26032
+ thumbnailMint: z.string().optional(),
26033
+ /**
26034
+ * Why no still will be produced for this clip RIGHT NOW — set when the
26035
+ * provider already knows, so the surface draws the glyph and the reason
26036
+ * instead of firing a mint that cannot succeed.
26037
+ *
26038
+ * `sleeping` — a standalone battery camera; a read would be a wake (D549 4).
26039
+ * `camera-refused` — the camera answered the CoverPreview with a refusal.
26040
+ * `no-keyframe` — the window holds no decodable I-frame (an event that sits
26041
+ * inside no file is the measured case).
26042
+ * `unsupported` — this source cannot mint stills at all.
26043
+ */
26044
+ thumbnailUnavailable: z.object({ reason: z.enum([
26045
+ "sleeping",
26046
+ "camera-refused",
26047
+ "no-keyframe",
26048
+ "unsupported"
26049
+ ]) }).optional(),
26050
+ /**
26051
+ * When the catalog this row came from was last CONFIRMED against the device.
26052
+ * Absent means "this row was read live". A persisted catalog served while a
26053
+ * camera sleeps carries the age it really has — a cached list is never drawn
26054
+ * as current (D549 13).
26055
+ */
26056
+ catalogAsOf: z.number().optional(),
26057
+ /**
26058
+ * The camera's OWN type strings for this clip, all of them, unmapped
26059
+ * (`md`, `people`, `dog_cat`, `sched`, …). Kept beside {@link labels}
26060
+ * because a firmware inventing a type must not vanish: the mapping into our
26061
+ * filter vocabulary is lossy on purpose and this is the lossless copy
26062
+ * (D549 20).
26063
+ */
26064
+ nativeTypes: z.array(z.string()).optional(),
26065
+ /**
26066
+ * What the subject DID — `crossline`, `intrude`, `loitering`. A behaviour
26067
+ * travels BESIDE a class, never instead of one, and the class filter ignores
26068
+ * it: "a person crossed a line" is still a person (D549 20).
26069
+ */
26070
+ behaviours: z.array(z.string()).optional(),
26071
+ /**
26072
+ * The same recording as two files — the sub twin (what the row and its
26073
+ * thumbnail are) and its main twin, matched at listing time so a quality
26074
+ * change never re-searches the camera (D549 15). `getClipPlayback`'s
26075
+ * `profile` picks between them: `low | mid` → sub, `high` → main.
26076
+ */
26077
+ streams: z.object({
26078
+ sub: z.object({
26079
+ id: z.string(),
26080
+ bytes: z.number().optional()
26081
+ }).optional(),
26082
+ main: z.object({
26083
+ id: z.string(),
26084
+ bytes: z.number().optional()
26085
+ }).optional()
26086
+ }).optional(),
26087
+ /** The camera says it holds a sub-stream copy of this recording. */
26088
+ supportSub: z.boolean().optional(),
26089
+ /**
26090
+ * Whether this row has bytes behind it. ABSENT means yes — every clip that
26091
+ * IS a file is playable, and only a source that lists EVENTS can produce a
26092
+ * row with nothing to play (the measured case: 1 of 29 hub events on 3628
26093
+ * fell inside no file). Such a row is shown, never dropped and never offered
26094
+ * as playable-then-failing (D549 19).
26095
+ */
26096
+ playable: z.boolean().optional(),
26097
+ /** Why {@link playable} is false, verbatim (`no-file-for-window`). */
26098
+ unplayableReason: z.string().optional()
25979
26099
  });
25980
26100
  var ClipPlaybackSchema = z.object({
25981
- /** HLS master URL through the hub data-plane (Range + token in path). */
26101
+ /**
26102
+ * A media URL through the hub data-plane. {@link ClipPlaybackSchema.format}
26103
+ * says what kind — an HLS master playlist for a recording-derived clip, a
26104
+ * progressive MP4 for a native file the vendor addon muxed and serves with
26105
+ * `Range`. The URL is NOT a credential: both planes are registered
26106
+ * `access: 'authenticated'` and the hub's `/addon/<id>/<prefix>` proxy takes
26107
+ * the session cookie, so nothing is appended to it (D549 22).
26108
+ */
25982
26109
  playbackUrl: z.string(),
26110
+ /**
26111
+ * How to play {@link playbackUrl}. Absent means `hls` — the shape every
26112
+ * existing consumer already assumes. A player that branches on this is the
26113
+ * one change a native clip needs; a player that ignores it will hand an MP4
26114
+ * to hls.js and fail parsing it as a manifest.
26115
+ */
26116
+ format: z.enum(["hls", "mp4"]).optional(),
26117
+ /**
26118
+ * Which twin was actually served. A clip that holds only one stream answers
26119
+ * with the one it has, and the surface SAYS so — a missing main twin is
26120
+ * never served silently as if it were the asked-for quality (D549 15).
26121
+ */
26122
+ served: CamProfileSchema.optional(),
25983
26123
  /** Optional LAN/remote alternates for the same clip. */
25984
26124
  playbackEndpoints: z.array(z.string()).optional(),
25985
26125
  token: z.string().optional()
25986
26126
  });
26127
+ /**
26128
+ * Why a source cannot answer right now — per SOURCE, never fleet-wide.
26129
+ *
26130
+ * `ok` is the only state whose clip list may be read as complete. The other
26131
+ * three exist because an empty list from a sleeping camera reads as "this
26132
+ * camera has no recordings", which is the defect this whole line of work is
26133
+ * about: a battery camera nobody woke (`sleeping`), a camera that could not be
26134
+ * reached at all (`unreachable`, also what a provider that THREW reports), a
26135
+ * camera whose SD card is not mounted (`no-storage`, measured on 640 —
26136
+ * `HddInfo mount 0`, honestly nothing to list rather than "no clips"), and a
26137
+ * camera whose OWN index disagrees with its OWN calendar (`index-empty`,
26138
+ * measured on 618: the calendar marks 3–20 September, days 6–20 list zero
26139
+ * files on both streams and both filters, with 177 GB free). That last one is
26140
+ * a defect ON THE CAMERA, and the only honest thing a provider can do is say
26141
+ * which days it asked for and got nothing — "no clips" would be a lie about a
26142
+ * card that is full of them.
26143
+ */
26144
+ var ClipSourceAvailabilitySchema = z.object({
26145
+ state: z.enum([
26146
+ "ok",
26147
+ "sleeping",
26148
+ "unreachable",
26149
+ "no-storage",
26150
+ "index-empty"
26151
+ ]),
26152
+ /** Free text, shown verbatim. Names the camera's own refusal when there is one. */
26153
+ reason: z.string().optional(),
26154
+ /** When this source's catalog was last CONFIRMED. A cached list is never
26155
+ * drawn as current: the surface shows the age whenever it is older than the
26156
+ * refresh interval. */
26157
+ catalogAsOf: z.number().optional()
26158
+ });
26159
+ /**
26160
+ * One SOURCE of clips for a camera — a row of the picker, and the namespace
26161
+ * every clip id from it is prefixed with (`analytics`,
26162
+ * `native:reolink:onboard`, `hksv`, …).
26163
+ *
26164
+ * A provider lists the sources IT serves for that device, and answers for each
26165
+ * of them whether it can answer at all. A provider with nothing to offer on a
26166
+ * camera returns `[]` — it is not that camera's business.
26167
+ */
26168
+ var ClipSourceSchema = z.object({
26169
+ /** The value this source stamps on {@link ClipSchema.source}, and the prefix
26170
+ * of every clip id it mints. `getClipPlayback` routes on it. */
26171
+ source: z.string(),
26172
+ /** Operator-facing name of the source ("CamStack recordings", "SD card"). */
26173
+ label: z.string(),
26174
+ /**
26175
+ * The addon that SERVES this row.
26176
+ *
26177
+ * A surface cannot otherwise resolve a source to who answers it, and the
26178
+ * alternative — a `native:reolink:* → provider-reolink` table inside the
26179
+ * widget — is a second authority on provider identity living in the one
26180
+ * package with no business knowing it, wrong the day a third source appears
26181
+ * (D557). One addon may serve SEVERAL sources: `addon-provider-reolink`
26182
+ * answers a hub child with both `native:reolink:onboard` and
26183
+ * `native:reolink:hub`, which is why the per-device switch is keyed by
26184
+ * SOURCE and not by this (D555).
26185
+ *
26186
+ * Optional for version skew only. The collection dispatcher stamps it from
26187
+ * the registry, so a row that travelled through the fan-out carries the
26188
+ * authoritative id whatever the provider filled in.
26189
+ */
26190
+ addonId: z.string().optional(),
26191
+ /**
26192
+ * Which API this source resolved to FOR THIS CAMERA, when it has a choice.
26193
+ *
26194
+ * A source may cover one store through more than one surface — the Reolink
26195
+ * provider reads a hub child through the parent's event log and a standalone
26196
+ * through its own file list, because that is what each camera answers. The
26197
+ * CHOICE is the provider's, made from what the camera is, and is never a row
26198
+ * the operator has to understand; but it is REPORTED, because a source that
26199
+ * silently reads a different API on two cameras and then behaves differently
26200
+ * is the thing nobody can debug later. Absent when the source has only one
26201
+ * way to read its store.
26202
+ */
26203
+ via: z.string().optional(),
26204
+ availability: ClipSourceAvailabilitySchema,
26205
+ /**
26206
+ * The operator switched this source OFF for this camera.
26207
+ *
26208
+ * Deliberately NOT a member of {@link ClipSourceAvailabilitySchema}'s
26209
+ * vocabulary. That enum models what the source CAN do — a sleeping camera, an
26210
+ * unmounted card, an index that disagrees with its own calendar — and a
26211
+ * switched-off source could answer perfectly well; the operator decided it
26212
+ * should not. Folding the choice in is how `disabled` and `broken` stop being
26213
+ * distinguishable, which is the D62 rule this repo has already paid for twice:
26214
+ * an off switch is REPORTED off (`CameraStatus.switchedOff`, the same word),
26215
+ * and disabled must never look like broken. It is also what lets every
26216
+ * exhaustive consumer of the availability enum keep compiling.
26217
+ *
26218
+ * A switched-off source contributes NO clips (`listClips` never calls it) and
26219
+ * its row carries no `catalogAsOf`: nothing confirms a catalog it is not
26220
+ * allowed to serve, and a frozen age that can only grow draws a stalling
26221
+ * source rather than an off switch.
26222
+ *
26223
+ * The row survives BECAUSE it is the control the operator switches the source
26224
+ * back on from — D554 decision 4's rule ("a source that cannot answer
26225
+ * produces a ROW, not an absence") applied to the one case D556 carved out of
26226
+ * it, and D557's own kept property ("a source is never hidden, only its
26227
+ * rows"). Absent means on.
26228
+ *
26229
+ * A provider never sets this — like {@link ClipSourceSchema.addonId} it is
26230
+ * stamped by the collection dispatcher, which holds the registry's projection
26231
+ * of the persisted authority (D556) and is the only place that knows it.
26232
+ */
26233
+ switchedOff: z.boolean().optional()
26234
+ });
25987
26235
  var videoclipsCapability = {
25988
26236
  name: "videoclips",
25989
26237
  scope: "device",
25990
- mode: "singleton",
26238
+ mode: "collection",
25991
26239
  kind: "wrapper",
25992
26240
  defaultActive: true,
25993
26241
  /** A clip is a window over a camera's footage — the cap is meaningless on a
25994
26242
  * sensor, a button or an event emitter, and the `defaultActive` auto-bind
25995
26243
  * reads this to decide which devices it may claim. */
25996
26244
  deviceTypes: [DeviceType.Camera],
26245
+ /**
26246
+ * The Clips section of a camera's device details is FRAMEWORK-DERIVED (D14):
26247
+ * the aggregator turns this declaration into the `type:'widget'` section and
26248
+ * `DeviceDetail.tsx` is never edited. `videoclips` is the first WRAPPER cap
26249
+ * to declare one — every previous `host/` widget cap is `deviceNative` — so
26250
+ * `device-config-widget-wrapped-binding.spec.ts` pins that a `kind:'wrapped'`
26251
+ * binding entry derives the same section a native one does.
26252
+ *
26253
+ * **It is a SECTION of the Recording tab, at the end of it — not a tab of
26254
+ * its own.** It shipped as a `clips` top-tab and the operator rejected the
26255
+ * placement: *"utilizzerei la stessa tab recordings, lì abbiamo già tutto il
26256
+ * necessario, una nuova sezione alla fine per le clips"*. The Recording tab
26257
+ * already holds the recorder's panel, the schedule bands and the unified
26258
+ * retention policy; footage the camera itself holds is the same question,
26259
+ * asked of a different store. `order: 100` puts it after all of them with
26260
+ * room left in front. The `clips` entry in `WELL_KNOWN_TABS` went with it —
26261
+ * a well-known id nobody declares is an invitation to mint the tab again.
26262
+ *
26263
+ * `topTab` stays: the browser owns a pane (a day's tiles, a source list and
26264
+ * a player) and the Config tab's inner bar has no room for one. The widget
26265
+ * itself is `host/clips-browser` in ui-library's `HOST_WIDGETS`, and because
26266
+ * the admin Recordings page renders every `location:'top-tab'` +
26267
+ * `tab:'recording'` section behind its camera picker
26268
+ * (`CameraRecordingSettingsSection`), this declaration lands the section on
26269
+ * BOTH surfaces with no second wiring.
26270
+ */
26271
+ deviceConfig: { ui: {
26272
+ kind: "widget",
26273
+ widgetId: "host/clips-browser",
26274
+ tab: "recording",
26275
+ topTab: true,
26276
+ label: "Clips",
26277
+ order: 100
26278
+ } },
25997
26279
  methods: {
25998
26280
  listClips: method(z.object({
25999
26281
  deviceId: z.number(),
26000
26282
  since: z.number(),
26001
26283
  until: z.number(),
26002
- limit: z.number().int().positive().optional()
26284
+ limit: z.number().int().positive().optional(),
26285
+ /**
26286
+ * WHICH provider to ask — the `addonId` a {@link ClipSourceSchema} row
26287
+ * carries, never a source id and never a list.
26288
+ *
26289
+ * Absent means the device's BOUND provider, which is CamStack on every
26290
+ * camera today because that is what `deviceManager.getBindings`
26291
+ * answers with. A provider the device is not bound to is refused by
26292
+ * name rather than answered by another one (D552's
26293
+ * `rejectUnresolvedAddonPin` rule).
26294
+ *
26295
+ * It replaced `sources?: string[]`, a VIEW filter over source ids that
26296
+ * assumed the answer was a fan-out over everything a camera has. The
26297
+ * operator settled otherwise on 2026-09-20 — *"il get videoclips
26298
+ * prendere un provider opzionale, di default va su camstack"*,
26299
+ * *"l'utilizzatore è uno solo"* — so the list is asked of one provider
26300
+ * at a time and there is nothing to filter out of it.
26301
+ */
26302
+ provider: z.string().optional()
26003
26303
  }), z.array(ClipSchema).readonly(), {
26004
26304
  kind: "query",
26005
- auth: "admin"
26305
+ auth: "protected"
26306
+ }),
26307
+ /**
26308
+ * The sources this camera has, WITH the reason any of them cannot answer.
26309
+ *
26310
+ * Asked separately from `listClips` because an empty clip list is
26311
+ * ambiguous and this is the only place the ambiguity is resolved: every
26312
+ * bound provider contributes its own rows, and a provider that could not
26313
+ * be reached at all still produces one row saying so. A surface that draws
26314
+ * "no clips" without reading this is drawing a guess.
26315
+ */
26316
+ listSources: method(z.object({ deviceId: z.number() }), z.array(ClipSourceSchema).readonly(), {
26317
+ kind: "query",
26318
+ auth: "protected"
26006
26319
  }),
26007
26320
  getClipPlayback: method(z.object({
26008
26321
  deviceId: z.number(),
26009
- clipId: z.string()
26322
+ clipId: z.string(),
26323
+ /**
26324
+ * Which twin to serve, on the ONE quality scale the system already has
26325
+ * (`CamProfileSchema`). `low | mid` → the sub file, `high` → the main
26326
+ * twin; both ids are already on the row so this never re-searches the
26327
+ * camera. Absent means the provider's own default (the sub file, which
26328
+ * every source is measured to hold).
26329
+ *
26330
+ * `auto` is deliberately NOT accepted here: a stored file has no
26331
+ * broker session, so the adaptive tier cannot be resolved for it. The
26332
+ * viewer resolves `auto` to a profile the same way live does, before
26333
+ * it calls (D549 15).
26334
+ */
26335
+ profile: CamProfileSchema.optional()
26010
26336
  }), ClipPlaybackSchema, {
26011
26337
  kind: "query",
26012
- auth: "admin"
26338
+ auth: "protected"
26013
26339
  })
26014
26340
  }
26015
26341
  };
@@ -45989,6 +46315,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
45989
46315
  addonId: null,
45990
46316
  access: "view"
45991
46317
  },
46318
+ "videoclips.listSources": {
46319
+ capName: "videoclips",
46320
+ capScope: "device",
46321
+ addonId: null,
46322
+ access: "view"
46323
+ },
45992
46324
  "viewerUi.getStaticDir": {
45993
46325
  capName: "viewer-ui",
45994
46326
  capScope: "system",
@@ -47997,6 +48329,11 @@ Object.freeze({
47997
48329
  form: "single",
47998
48330
  optional: false
47999
48331
  }],
48332
+ "videoclips.listSources": [{
48333
+ name: "deviceId",
48334
+ form: "single",
48335
+ optional: false
48336
+ }],
48000
48337
  "waterHeater.setAway": [{
48001
48338
  name: "deviceId",
48002
48339
  form: "single",
@@ -49119,4 +49456,4 @@ function enumerateInferenceDevices(hw) {
49119
49456
  return out;
49120
49457
  }
49121
49458
  //#endregion
49122
- export { deviceStateCapability as $, streamCatalogCapability as $t, StorageLocationModeSchema as A, readinessKey as An, motionCapability as At, bareAddonId as B, resolveLocationMode as Bt, RecordingStatusSchema as C, expandCapMethods as Cn, logDestinationCapability as Ct, STREAM_PROFILE_META as D, nodePin as Dn, mayWriteToLocation as Dt, STORAGE_ACCESS_FALLBACK as E, isEvent as En, mayReadLocation as Et, addonSettingsCapability as F, procedureAuthKey as Ft, coreBlockAddonId as G, scoreRuntimes as Gt, buildStreamParamsConfigSchema as H, runtimeStatePolicyFor as Ht, addonWidgetsCapability as I, ptzAutotrackCapability as It, dataStoreProviderCapability as J, snapshotCapability as Jt, coreBlockIdFromAddonId as K, settingsStoreCapability as Kt, alertsCapability as L, reducePoints as Lt, StorageMigrationJobSchema as M, scopeKey as Mn, objectInputDeclaresAddonId as Mt, UserRecordSchema as N, sleep as Nn, parseStreamParamsFormPatch as Nt, ScopedTokenSchema as O, parseJsonObject as On, metricsProviderCapability as Ot, addonPagesCapability as P, EventCategory as Pn, platformProbeCapability as Pt, deviceManagerCapability as Q, storageProviderCapability as Qt, authProviderCapability as R, resolveBucketMs as Rt, RUNTIME_DEFAULTS as S, emitReadiness as Sn, locationIconKey as St, SOURCE_DEVICE_TYPES as T, isDeviceConfigCap as Tn, looseSchema as Tt, cameraGridLayoutCapability as U, scopesAllowAddon as Ut, batteryCapability as V, resolveMethodAuth as Vt, collectSecretConfigKeys as W, scopesAllowDeviceCap as Wt, decodeVectorBase64 as X, storageMigrationCapability as Xt, declarationOwnerNodeId as Y, storageCapability as Yt, deriveBatteryPresence as Z, storageOccupancyCapability as Zt, LegacyStorageLocationDefaultSchema as _, asJsonObject as _n, isVoidInput as _t, BatteryStatusSchema as a, errMsg as an, evaluateSensorEdge as at, MOTION_CLOSE_AFTER_MS as b, createEvent as bn, lifecycleJobSchema as bt, CORE_BLOCKS_ADDON_ID as c, DATAPLANE_SECRET_HEADER as cn, filesystemBrowseCapability as ct, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as d, DeviceFeature as dn, isArrayOutputSchema as dt, streamQualityLabel as en, deviceStatusCapability as et, DERIVED_CAMERA_SILENCED_CAP_NAMES as f, DeviceRole as fn, isCollectionArrayMethod as ft, LOG_LEVEL_RANK as g, WELL_KNOWN_TAB_MAP as gn, isSourceCap as gt, FIRST_LEVEL_MACRO_CLASSES as h, ReadinessTimeoutError as hn, isSameAddonId as ht, BaseDevice as i, withLocationMode as in, enumerateSchemaFields as it, StorageLocationTypeSchema as j, resolveCapMount as jn, normalizeUnit as jt, StorageCleanupJobSchema as k, parseJsonUnknown as kn, modeMayWrite as kt, CORE_BLOCK_ADDON_PREFIX as l, DEVICE_SETTINGS_CONTRIBUTION_METHODS as ln, foldSnapshotByFunction as lt, DeviceStatusSchema as m, ReadinessRegistry as mn, isObjectInput as mt, AlertSchema as n, vectorDimFromBase64 as nn, enumerateInferenceDevices as nt, CAMERA_SWITCH_ORDER as o, buildFfmpegArgs as on, evictionPolicyOfLocation as ot, DeclaredDevices as p, DeviceType as pn, isLocationEnabled as pt, coreBlocksCapability as q, siteZoneKey as qt, ApiKeyRecordSchema as r, vectorStoreCapability as rn, enumerateItemArrayFields as rt, CAP_NAMES_WITH_STATUS as s, BaseAddon as sn, extractNestedAddonId as st, ALL_CAPABILITY_DEFINITIONS as t, userManagementCapability as tn, doorbellCapability as tt, CoreBlockSchema as u, DEVICE_STATUS_METHOD as un, gbToBytes as ut, LocationIconIdSchema as v, asNumber as vn, kebabToCamel as vt, SOURCE_CAPS as w, hydrateSchema as wn, logLevelAtMost as wt, REDACTED_SECRET as x, emitDownForOwnedCaps as xn, localNetworkCapability as xt, METHOD_ACCESS_MAP as y, asString as yn, legacyModeOf as yt, backupCapability as z, resolveContainerPrimaryChild as zt };
49459
+ export { deviceStateCapability as $, storageProviderCapability as $t, StorageLocationModeSchema as A, parseJsonUnknown as An, motionCapability as At, bareAddonId as B, resolveContainerPrimaryChild as Bt, RecordingStatusSchema as C, emitReadiness as Cn, logDestinationCapability as Ct, STREAM_PROFILE_META as D, isEvent as Dn, mayWriteToLocation as Dt, STORAGE_ACCESS_FALLBACK as E, isDeviceConfigCap as En, mayReadLocation as Et, addonSettingsCapability as F, EventCategory as Fn, platformProbeCapability as Ft, coreBlockAddonId as G, scopesAllowDeviceCap as Gt, buildStreamParamsConfigSchema as H, resolveMethodAuth as Ht, addonWidgetsCapability as I, procedureAuthKey as It, dataStoreProviderCapability as J, siteZoneKey as Jt, coreBlockIdFromAddonId as K, scoreRuntimes as Kt, alertsCapability as L, ptzAutotrackCapability as Lt, StorageMigrationJobSchema as M, resolveCapMount as Mn, objectInputDeclaresAddonId as Mt, UserRecordSchema as N, scopeKey as Nn, objectInputDeclaresRequiredDeviceId as Nt, ScopedTokenSchema as O, nodePin as On, metricsProviderCapability as Ot, addonPagesCapability as P, sleep as Pn, parseStreamParamsFormPatch as Pt, deviceManagerCapability as Q, storageOccupancyCapability as Qt, authProviderCapability as R, reducePoints as Rt, RUNTIME_DEFAULTS as S, emitDownForOwnedCaps as Sn, locationIconKey as St, SOURCE_DEVICE_TYPES as T, hydrateSchema as Tn, looseSchema as Tt, cameraGridLayoutCapability as U, runtimeStatePolicyFor as Ut, batteryCapability as V, resolveLocationMode as Vt, collectSecretConfigKeys as W, scopesAllowAddon as Wt, decodeVectorBase64 as X, storageCapability as Xt, declarationOwnerNodeId as Y, snapshotCapability as Yt, deriveBatteryPresence as Z, storageMigrationCapability as Zt, LegacyStorageLocationDefaultSchema as _, WELL_KNOWN_TAB_MAP as _n, isVoidInput as _t, BatteryStatusSchema as a, withLocationMode as an, evaluateSensorEdge as at, MOTION_CLOSE_AFTER_MS as b, asString as bn, lifecycleJobSchema as bt, CORE_BLOCKS_ADDON_ID as c, BaseAddon as cn, filesystemBrowseCapability as ct, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as d, DEVICE_STATUS_METHOD as dn, isArrayOutputSchema as dt, streamCatalogCapability as en, deviceStatusCapability as et, DERIVED_CAMERA_SILENCED_CAP_NAMES as f, DeviceFeature as fn, isCollectionArrayMethod as ft, LOG_LEVEL_RANK as g, ReadinessTimeoutError as gn, isSourceCap as gt, FIRST_LEVEL_MACRO_CLASSES as h, ReadinessRegistry as hn, isSameAddonId as ht, BaseDevice as i, vectorStoreCapability as in, enumerateSchemaFields as it, StorageLocationTypeSchema as j, readinessKey as jn, normalizeUnit as jt, StorageCleanupJobSchema as k, parseJsonObject as kn, modeMayWrite as kt, CORE_BLOCK_ADDON_PREFIX as l, DATAPLANE_SECRET_HEADER as ln, foldSnapshotByFunction as lt, DeviceStatusSchema as m, DeviceType as mn, isObjectInput as mt, AlertSchema as n, userManagementCapability as nn, enumerateInferenceDevices as nt, CAMERA_SWITCH_ORDER as o, errMsg as on, evictionPolicyOfLocation as ot, DeclaredDevices as p, DeviceRole as pn, isLocationEnabled as pt, coreBlocksCapability as q, settingsStoreCapability as qt, ApiKeyRecordSchema as r, vectorDimFromBase64 as rn, enumerateItemArrayFields as rt, CAP_NAMES_WITH_STATUS as s, buildFfmpegArgs as sn, extractNestedAddonId as st, ALL_CAPABILITY_DEFINITIONS as t, streamQualityLabel as tn, doorbellCapability as tt, CoreBlockSchema as u, DEVICE_SETTINGS_CONTRIBUTION_METHODS as un, gbToBytes as ut, LocationIconIdSchema as v, asJsonObject as vn, kebabToCamel as vt, SOURCE_CAPS as w, expandCapMethods as wn, logLevelAtMost as wt, REDACTED_SECRET as x, createEvent as xn, localNetworkCapability as xt, METHOD_ACCESS_MAP as y, asNumber as yn, legacyModeOf as yt, backupCapability as z, resolveBucketMs as zt };
package/dist/index.d.ts CHANGED
@@ -16,6 +16,13 @@ export { AlertCenterAddon } from './builtins/alerts/index.js';
16
16
  export { ConsoleDestination, ConsoleLoggingAddon } from './builtins/console-logging/index.js';
17
17
  export { CoreBlocksAddon } from './builtins/core-blocks/index.js';
18
18
  export { DeviceManagerAddon } from './builtins/device-manager/index.js';
19
+ /**
20
+ * The per-device wrapper activation (D556). Exported because it is the
21
+ * OPERATOR-FACING verb and the only writer of the registry's per-device source
22
+ * projection — a spec that proves "activate, then list" must be able to drive
23
+ * the real one, not a re-implementation of it.
24
+ */
25
+ export { setWrapperActive, type WrapperActivationDeps, type WrapperActivationInput, } from './builtins/device-manager/device-source-activation.js';
19
26
  export { HubForwarderAddon, HubForwarderDestination } from './builtins/hub-forwarder/index.js';
20
27
  export { LivenessMonitorAddon } from './builtins/liveness-monitor/index.js';
21
28
  export { LocalAuthAddon } from './builtins/local-auth/index.js';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-Cek0wNdY.js");
3
- const require_dist = require("./dist-CYXhwfDf.js");
3
+ const require_dist = require("./dist-Bu8HN-KS.js");
4
4
  const require_builtins_alerts_alerts_addon = require("./builtins/alerts/alerts.addon.js");
5
5
  require("./builtins/alerts/index.js");
6
6
  const require_formatter = require("./formatter-DqAKDlvN.js");
@@ -5892,8 +5892,22 @@ var CapabilityRegistry = class CapabilityRegistry {
5892
5892
  singletonNodeOverrides = /* @__PURE__ */ new Map();
5893
5893
  /** Optional reader for persisted per-node overrides: (cap, nodeId) → addonId. */
5894
5894
  nodeConfigReader = null;
5895
- /** Per-device collection filters: deviceId → (capability → addonIds[]) */
5896
- deviceCollectionFilters = /* @__PURE__ */ new Map();
5895
+ /**
5896
+ * Per-device SOURCE deactivations: deviceId → (capability → disabled addon ids).
5897
+ *
5898
+ * The in-memory PROJECTION of the persisted wrapper activation
5899
+ * (`deviceBindings[deviceId][capName].disabledSourceAddonIds`, written by
5900
+ * `setWrapperActive`), and never an authority of its own —
5901
+ * [D556](../../../../docs/decisions/adr-0556-a-per-device-source-switch-is-the-wrapper-activation-and-the-registry-holds-only-its-projection.md).
5902
+ * Nothing but `device-source-activation.ts` writes it: the operator's verb
5903
+ * is `deviceManager.setWrapperActive`, and a second writable surface here is
5904
+ * exactly the two-authorities defect D62 exists for.
5905
+ *
5906
+ * A DENY list, not an allow list, and that is the deliberate answer to what
5907
+ * "unset" means: a source nobody has ruled out is a source. An untouched
5908
+ * camera keeps every provider it has today, and a provider that registers
5909
+ * after the operator's last toggle needs no second write to be seen.
5910
+ */
5897
5911
  /** autoActivate flags per (capability, addonId) — from addon manifests */
5898
5912
  autoActivateFlags = /* @__PURE__ */ new Map();
5899
5913
  /**
@@ -6789,54 +6803,24 @@ var CapabilityRegistry = class CapabilityRegistry {
6789
6803
  for (const [addonId, provider] of state.providers) if (this.isActiveForSystem(capability, addonId)) return asProvider(provider);
6790
6804
  return null;
6791
6805
  }
6792
- /** Set a per-device collection filter. */
6793
- setDeviceCollectionFilter(deviceId, capability, addonIds) {
6794
- if (!this.capabilities.get(capability)) {
6795
- this.logger.warn("Cannot set device collection filter for undeclared capability", { meta: { capability } });
6796
- return;
6797
- }
6798
- let deviceMap = this.deviceCollectionFilters.get(deviceId);
6799
- if (!deviceMap) {
6800
- deviceMap = /* @__PURE__ */ new Map();
6801
- this.deviceCollectionFilters.set(deviceId, deviceMap);
6802
- }
6803
- deviceMap.set(capability, [...addonIds]);
6804
- this.logger.info("Device collection filter set", {
6805
- tags: { deviceId: Number(deviceId) },
6806
- meta: {
6807
- capability,
6808
- addonIds
6809
- }
6810
- });
6811
- }
6812
- /** Clear a per-device collection filter. */
6813
- clearDeviceCollectionFilter(deviceId, capability) {
6814
- const deviceMap = this.deviceCollectionFilters.get(deviceId);
6815
- if (!deviceMap) return;
6816
- deviceMap.delete(capability);
6817
- if (deviceMap.size === 0) this.deviceCollectionFilters.delete(deviceId);
6818
- this.logger.info("Device collection filter cleared", {
6819
- tags: { deviceId: Number(deviceId) },
6820
- meta: { capability }
6821
- });
6822
- }
6823
6806
  /**
6824
6807
  * Resolve collection providers for a specific device.
6825
- * Filters by activation (the manifest's autoActivate) and optional collection filter.
6808
+ *
6809
+ * Filters by activation (the manifest's autoActivate) and by nothing else.
6810
+ * The per-device DENY set that used to filter here is gone with the switch
6811
+ * it projected (2026-09-20): *"tutti i providers funzionano sempre"*. WHICH
6812
+ * providers a device has is now answered by its BINDINGS
6813
+ * (`device-bindings-store.ts` step 0), which is a different question from
6814
+ * which of them are registered and running — this one.
6815
+ *
6816
+ * `deviceId` is kept in the signature and deliberately unused: the resolution
6817
+ * is per-device by CONTRACT, and a caller that stops passing one is a caller
6818
+ * that has stopped asking about a device.
6826
6819
  */
6827
- resolveCollectionForDevice(capability, deviceId) {
6820
+ resolveCollectionForDevice(capability, _deviceId) {
6828
6821
  const state = this.capabilities.get(capability);
6829
6822
  if (!state || state.definition.mode !== "collection") return [];
6830
- let entries = [...state.providers.entries()].filter(([addonId]) => !state.disabledProviders.has(addonId)).filter(([addonId]) => this.isActiveForSystem(capability, addonId));
6831
- const deviceMap = this.deviceCollectionFilters.get(deviceId);
6832
- if (deviceMap) {
6833
- const filterAddonIds = deviceMap.get(capability);
6834
- if (filterAddonIds) {
6835
- const filterSet = new Set(filterAddonIds);
6836
- entries = entries.filter(([addonId]) => filterSet.has(addonId));
6837
- }
6838
- }
6839
- return entries.map(([, provider]) => asProvider(provider));
6823
+ return [...state.providers.entries()].filter(([addonId]) => !state.disabledProviders.has(addonId)).filter(([addonId]) => this.isActiveForSystem(capability, addonId)).map(([, provider]) => asProvider(provider));
6840
6824
  }
6841
6825
  /** Check if all dependencies for a capability are satisfied. */
6842
6826
  areDependenciesMet(capabilityName) {
@@ -7000,6 +6984,35 @@ var CapabilityRegistry = class CapabilityRegistry {
7000
6984
  function systemMountKindFor(def) {
7001
6985
  return def.mode === "collection" ? "collection" : "singleton";
7002
6986
  }
6987
+ /**
6988
+ * The mount kind ONE method resolves through — the cap's mount, except where
6989
+ * the method cannot be dispatched per device.
6990
+ *
6991
+ * Two ways a method leaves a per-device mount for the cap's SYSTEM provider:
6992
+ *
6993
+ * - `systemOnly: true` — declared via the `systemMethod()` factory. The
6994
+ * author said so; it wins everywhere, including on a `device-native` cap
6995
+ * (`snapshot.getSnapshotOverview`).
6996
+ * - a `device-scoped` cap's method whose input declares no top-level REQUIRED
6997
+ * numeric `deviceId`. The per-device dispatcher demands one on every call
6998
+ * (`requireDeviceScoped`), so such a method would answer BAD_REQUEST
6999
+ * "input must carry numeric deviceId" for every caller — unreachable. It
7000
+ * names no device, so there is no per-device provider to resolve: the cap's
7001
+ * hub-resident system/wrapper provider is the only honest destination, and
7002
+ * it is where that method already resolved before the mount was fixed.
7003
+ *
7004
+ * NOT applied to `device-native`, deliberately: there the native IS the device
7005
+ * and a deviceId-less method is a definition error the author must resolve
7006
+ * with `systemMethod()` — which is what `snapshot` does. This rule exists for
7007
+ * the mount kind that was DERIVED under caps that never opted in, where a
7008
+ * silent 400 would be a regression introduced by the derivation itself.
7009
+ */
7010
+ function methodMountKindFor(def, mountKind, method) {
7011
+ if (method.schema.systemOnly === true) return systemMountKindFor(def);
7012
+ if (mountKind !== "device-scoped") return mountKind;
7013
+ if (require_dist.objectInputDeclaresRequiredDeviceId(method.schema.input)) return mountKind;
7014
+ return systemMountKindFor(def);
7015
+ }
7003
7016
  function methodsFor(def) {
7004
7017
  const effective = require_dist.expandCapMethods(def);
7005
7018
  return Object.entries(effective).map(([name, schema]) => {
@@ -7126,12 +7139,11 @@ function requireLocal(capName, getLocal, services) {
7126
7139
  function buildOneCapRouter(def, primitives, services) {
7127
7140
  const capName = def.name;
7128
7141
  const mount = require_dist.resolveCapMount(def);
7129
- const systemMountKind = systemMountKindFor(def);
7130
7142
  const nodeIdData = def.nodeIdMode === "data";
7131
7143
  const procedures = {};
7132
7144
  for (const method of methodsFor(def)) {
7133
7145
  if (method.callerRequired && (method.kind === "subscription" || !method.isObject)) throw new Error(`Cap "${capName}" method "${method.name}": caller: 'required' is only supported on query/mutation methods with an object input schema`);
7134
- const effectiveMountKind = method.schema.systemOnly === true ? systemMountKind : mount.kind;
7146
+ const effectiveMountKind = methodMountKindFor(def, mount.kind, method);
7135
7147
  const isCollection = effectiveMountKind === "collection";
7136
7148
  const getLocal = (ctx, addonId) => services.getLocalProvider(capName, effectiveMountKind, ctx, addonId);
7137
7149
  const base = primitives.procedures[require_dist.procedureAuthKey(method.auth)];
@@ -96220,6 +96232,7 @@ exports.selectReportedSpaces = require_manifest_system_deps.selectReportedSpaces
96220
96232
  exports.serializeTypedArrays = require_manifest_system_deps.serializeTypedArrays;
96221
96233
  exports.setHubConnected = require_manifest_system_deps.setHubConnected;
96222
96234
  exports.setNodeEventInterest = require_manifest_system_deps.setNodeEventInterest;
96235
+ exports.setWrapperActive = require_builtins_device_manager_device_manager_addon.setWrapperActive;
96223
96236
  exports.shouldReclaim = require_manifest_system_deps.shouldReclaim;
96224
96237
  exports.socketDirectionBytes = require_manifest_system_deps.socketDirectionBytes;
96225
96238
  exports.socketDirectionMessages = require_manifest_system_deps.socketDirectionMessages;