camstack 1.2.111 → 1.2.113

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.
@@ -14522,7 +14522,7 @@ function date4(params) {
14522
14522
  // ../../node_modules/zod/v4/classic/external.js
14523
14523
  config(en_default());
14524
14524
 
14525
- // ../types/dist/sleep-Cfij6Jj9.mjs
14525
+ // ../types/dist/sleep-Bd-Y4RUt.mjs
14526
14526
  var WELL_KNOWN_TABS = [
14527
14527
  {
14528
14528
  id: "overview",
@@ -15582,46 +15582,6 @@ var CameraSwitchGroupSchema = external_exports.object({
15582
15582
  /** Unix ms when the group was composed server-side. */
15583
15583
  fetchedAt: external_exports.number()
15584
15584
  });
15585
- var LogChannelLevelSchema = external_exports.enum([
15586
- "info",
15587
- "warn",
15588
- "error"
15589
- ]);
15590
- var LogChannelDescriptorSchema = external_exports.object({
15591
- /**
15592
- * Dotted `area.thing`, unique across the workspace. `area` is conventionally
15593
- * the addon's short name so an operator reading a channel list can tell who
15594
- * owns it without a second lookup.
15595
- */
15596
- name: external_exports.string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
15597
- /** One sentence: what the operator will SEE after arming it. */
15598
- description: external_exports.string().min(1),
15599
- /** The level its lines are emitted at. Never below `info`. */
15600
- defaultLevel: LogChannelLevelSchema,
15601
- /**
15602
- * Whether this channel can be narrowed to a camera.
15603
- *
15604
- * `true` is a PROMISE with two halves, and both must hold: the gate is
15605
- * consulted with the numeric device id, AND every line the channel admits
15606
- * carries `tags: { deviceId }` with that same numeric id. The second half is
15607
- * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
15608
- * keeps `deviceId` out of the stream labels for cardinality, so the tag in
15609
- * the body is the only way to filter.
15610
- *
15611
- * A channel whose lines carry the device only in `meta` (or not at all) is
15612
- * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
15613
- * the operator narrows to one camera, sees nothing, and concludes the code
15614
- * path was never taken.
15615
- */
15616
- perDevice: external_exports.boolean()
15617
- });
15618
- var LogChannelWindowSchema = external_exports.object({
15619
- channel: external_exports.string().min(1),
15620
- /** Epoch ms the window closes at. */
15621
- armedUntilMs: external_exports.number(),
15622
- /** `null` = every camera. A non-empty list narrows to those numeric ids. */
15623
- deviceIds: external_exports.array(external_exports.number().int()).readonly().nullable()
15624
- });
15625
15585
  var OpsLogDomainSchema = external_exports.enum(["recording", "events"]);
15626
15586
  var OpsLogOpSchema = external_exports.enum([
15627
15587
  "prune",
@@ -16198,6 +16158,18 @@ var StorageCleanupJobSchema = external_exports.object({
16198
16158
  error: external_exports.string().nullable()
16199
16159
  });
16200
16160
  var StorageCleanupStatusInputSchema = external_exports.object({ jobId: external_exports.string().optional() });
16161
+ var STORAGE_LOCATION_MODES = [
16162
+ "active",
16163
+ "readonly",
16164
+ "drain",
16165
+ "disabled"
16166
+ ];
16167
+ var StorageLocationModeSchema = external_exports.enum(STORAGE_LOCATION_MODES);
16168
+ var StorageEvictionPolicySchema = external_exports.enum([
16169
+ "normal",
16170
+ "never",
16171
+ "drain"
16172
+ ]);
16201
16173
  var StorageLocationTypeSchema = external_exports.string().regex(/^[a-z][a-zA-Z0-9-]*$/);
16202
16174
  var StorageLocationSchema = external_exports.object({
16203
16175
  id: external_exports.string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -16213,23 +16185,37 @@ var StorageLocationSchema = external_exports.object({
16213
16185
  * flag at upsert time, not here (the schema is provider-agnostic).
16214
16186
  */
16215
16187
  nodeId: external_exports.string().optional(),
16216
- isDefault: external_exports.boolean().default(false),
16217
16188
  isSystem: external_exports.boolean().default(false),
16218
16189
  /**
16219
- * Operator opt-in: whether consumers that BALANCE across several locations
16220
- * of a type may write here. Recordings reads it today; event media and
16221
- * backups are the next consumers, which is why the flag lives on the
16222
- * location rather than in any one addon's store — nothing has to be
16223
- * extended to add the next consumer.
16190
+ * THE write switch, and the only one (D383). `enabled: true` means every
16191
+ * consumer that chooses a write target for this type may write here, and all
16192
+ * enabled locations of a type are used TOGETHER; `false` means read-only —
16193
+ * still read, still played back, still age-swept, still drained, never
16194
+ * written.
16224
16195
  *
16225
- * OPTIONAL, and ABSENT MEANS ACTIVE. Every location persisted before the
16226
- * flag existed reads back with no flag and keeps working exactly as before;
16227
- * that is the whole compat story, and it is why no migration ships with it.
16228
- * A newly CREATED sibling is stamped `false` by the orchestrator (creating a
16229
- * disk must not silently start writing to it); the default of a type is
16230
- * always stamped `true`.
16196
+ * OPTIONAL only for the wire: an upsert that omits it means "leave what is
16197
+ * stored" on an update and "born inert unless it is the first location of its
16198
+ * type" on a create. On a PERSISTED row absence is legacy and it means
16199
+ * enabled — {@link isLocationEnabled} is the one place that says so, and the
16200
+ * orchestrator stamps every flagless row `true` once at hydrate so absence
16201
+ * stops existing rather than being re-derived on every read.
16231
16202
  */
16232
16203
  enabled: external_exports.boolean().optional(),
16204
+ /**
16205
+ * THE state of this location (D385), and the only authority on what may be
16206
+ * written, read or evicted here. Interpreted in exactly one place —
16207
+ * `storage-location-mode.ts` — which also folds the legacy
16208
+ * `enabled` / `config.readOnly` pair into a mode so an old row is never
16209
+ * ambiguous.
16210
+ *
16211
+ * OPTIONAL only for the wire and for rows written before D385: absence is
16212
+ * resolved by `resolveLocationMode`, and the orchestrator stamps every
16213
+ * unstamped row ONCE at hydrate so absence stops existing rather than being
16214
+ * re-derived on every read. `enabled` survives one release as a DERIVED
16215
+ * mirror (`mode === 'active'`); `withLocationMode` is the only writer of
16216
+ * either, so the two cannot disagree.
16217
+ */
16218
+ mode: StorageLocationModeSchema.optional(),
16233
16219
  /** COMPUTED at read time by the orchestrator (statfs of the backing volume
16234
16220
  * for node-local locations it can reach) — never persisted, absent when the
16235
16221
  * volume is remote/unreachable. The single capacity truth every UI reads. */
@@ -16237,9 +16223,38 @@ var StorageLocationSchema = external_exports.object({
16237
16223
  totalBytes: external_exports.number(),
16238
16224
  availableBytes: external_exports.number()
16239
16225
  }).nullable().optional(),
16226
+ /**
16227
+ * How much of that volume CamStack ITSELF holds on this location (D388) —
16228
+ * COMPUTED at read time from the `storage-occupancy` providers' own figures,
16229
+ * never persisted, never a filesystem walk.
16230
+ *
16231
+ * **ABSENT MEANS UNKNOWN, never zero.** No provider has reported for this
16232
+ * location yet — nobody stores here, the owning addon is down, or the first
16233
+ * refresh has not completed. A UI must omit the segment rather than draw it
16234
+ * at zero, which would claim we occupy nothing (D315). It is an OBJECT and
16235
+ * not a bare number precisely so that a `?? 0` on the consuming side has to
16236
+ * be spelled out loud instead of appearing by accident.
16237
+ *
16238
+ * `measuredAtMs` is the OLDEST contributing measurement, so it is honest
16239
+ * about the whole figure rather than about its freshest part.
16240
+ */
16241
+ owned: external_exports.object({
16242
+ bytes: external_exports.number().int().nonnegative(),
16243
+ measuredAtMs: external_exports.number().int().nonnegative()
16244
+ }).optional(),
16240
16245
  createdAt: external_exports.number(),
16241
16246
  updatedAt: external_exports.number()
16242
16247
  });
16248
+ var LegacyStorageLocationDefaultSchema = external_exports.object({ isDefault: external_exports.boolean().optional() });
16249
+ var STORAGE_BYTES_PER_GB = 1024 ** 3;
16250
+ var StorageDrainProgressSchema = external_exports.object({
16251
+ locationId: external_exports.string(),
16252
+ startedAtMs: external_exports.number(),
16253
+ startBytes: external_exports.number(),
16254
+ bytesRemaining: external_exports.number(),
16255
+ drained: external_exports.boolean(),
16256
+ estimatedEmptyAtMs: external_exports.number().nullable()
16257
+ });
16243
16258
  var StorageLocationRefSchema = external_exports.union([StorageLocationTypeSchema, external_exports.string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/)]);
16244
16259
  var StorageAccessSchema = external_exports.enum(["local-path", "cap-mediated"]);
16245
16260
  var StorageLocationDeclarationSchema = external_exports.object({
@@ -16366,6 +16381,46 @@ var DecoderSessionConfigSchema = external_exports.object({
16366
16381
  */
16367
16382
  debug: external_exports.boolean().optional()
16368
16383
  });
16384
+ var LogChannelLevelSchema = external_exports.enum([
16385
+ "info",
16386
+ "warn",
16387
+ "error"
16388
+ ]);
16389
+ var LogChannelDescriptorSchema = external_exports.object({
16390
+ /**
16391
+ * Dotted `area.thing`, unique across the workspace. `area` is conventionally
16392
+ * the addon's short name so an operator reading a channel list can tell who
16393
+ * owns it without a second lookup.
16394
+ */
16395
+ name: external_exports.string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
16396
+ /** One sentence: what the operator will SEE after arming it. */
16397
+ description: external_exports.string().min(1),
16398
+ /** The level its lines are emitted at. Never below `info`. */
16399
+ defaultLevel: LogChannelLevelSchema,
16400
+ /**
16401
+ * Whether this channel can be narrowed to a camera.
16402
+ *
16403
+ * `true` is a PROMISE with two halves, and both must hold: the gate is
16404
+ * consulted with the numeric device id, AND every line the channel admits
16405
+ * carries `tags: { deviceId }` with that same numeric id. The second half is
16406
+ * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
16407
+ * keeps `deviceId` out of the stream labels for cardinality, so the tag in
16408
+ * the body is the only way to filter.
16409
+ *
16410
+ * A channel whose lines carry the device only in `meta` (or not at all) is
16411
+ * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
16412
+ * the operator narrows to one camera, sees nothing, and concludes the code
16413
+ * path was never taken.
16414
+ */
16415
+ perDevice: external_exports.boolean()
16416
+ });
16417
+ var LogChannelWindowSchema = external_exports.object({
16418
+ channel: external_exports.string().min(1),
16419
+ /** Epoch ms the window closes at. */
16420
+ armedUntilMs: external_exports.number(),
16421
+ /** `null` = every camera. A non-empty list narrows to those numeric ids. */
16422
+ deviceIds: external_exports.array(external_exports.number().int()).readonly().nullable()
16423
+ });
16369
16424
  var MODEL_FORMATS = [
16370
16425
  "onnx",
16371
16426
  "coreml",
@@ -30109,7 +30164,6 @@ var storageCapability = {
30109
30164
  }), external_exports.instanceof(Uint8Array)),
30110
30165
  endDownload: method(external_exports.object({ downloadId: external_exports.string() }), external_exports.void(), { kind: "mutation" }),
30111
30166
  listLocations: method(external_exports.object({ type: StorageLocationTypeSchema.optional() }), external_exports.array(StorageLocationSchema).readonly()),
30112
- getDefaultLocation: method(external_exports.object({ type: StorageLocationTypeSchema }), StorageLocationSchema.nullable()),
30113
30167
  listLocationDeclarations: method(external_exports.void(), external_exports.array(StorageLocationDeclarationSchema).readonly()),
30114
30168
  upsertLocation: method(StorageLocationSchema.omit({
30115
30169
  createdAt: true,
@@ -30139,6 +30193,12 @@ var storageCapability = {
30139
30193
  kind: "mutation",
30140
30194
  auth: "admin"
30141
30195
  }),
30196
+ /**
30197
+ * How far each draining location has got (D386). A pure READ of what the
30198
+ * last pressure sweep computed — it starts no work, and a location that is
30199
+ * not draining simply does not appear.
30200
+ */
30201
+ listDrainProgress: method(external_exports.void(), external_exports.array(StorageDrainProgressSchema).readonly()),
30142
30202
  testLocation: method(external_exports.object({ id: external_exports.string() }), external_exports.object({
30143
30203
  ok: external_exports.boolean(),
30144
30204
  error: external_exports.string().optional()
@@ -30268,6 +30328,37 @@ var storageMigrationCapability = {
30268
30328
  history: method(external_exports.object({}), external_exports.array(StorageMigrationJobSchema).readonly(), { auth: "admin" })
30269
30329
  }
30270
30330
  };
30331
+ var StorageOccupancyReportSchema = external_exports.object({
30332
+ locationId: external_exports.string(),
30333
+ /** Bytes this provider holds on THAT location — not its eviction domain, and
30334
+ * not net of what it is willing to delete. */
30335
+ ownedBytes: external_exports.number().int().nonnegative(),
30336
+ /** When the provider last actually measured this. The orchestrator carries it
30337
+ * through so a UI can say how old the figure is instead of implying "now". */
30338
+ measuredAtMs: external_exports.number().int().nonnegative()
30339
+ });
30340
+ var storageOccupancyCapability = {
30341
+ name: "storage-occupancy",
30342
+ scope: "system",
30343
+ mode: "collection",
30344
+ internal: true,
30345
+ methods: {
30346
+ /**
30347
+ * Occupancy for the given locations, in ONE round trip.
30348
+ *
30349
+ * A provider answers only for the locations it actually holds bytes on and
30350
+ * OMITS the rest — an omitted location is "I hold nothing measurable here",
30351
+ * which the orchestrator merges as a contribution of nothing rather than as
30352
+ * a claim that the location is empty. Only a location no provider reports
30353
+ * at all stays unknown.
30354
+ *
30355
+ * This must be CHEAP and must never walk a filesystem: it is on the admin
30356
+ * UI's `listLocations` path. The owner keeps its own figure fresh (D224) and
30357
+ * answers from what it already has.
30358
+ */
30359
+ getOccupancy: method(external_exports.object({ locationIds: external_exports.array(external_exports.string()).readonly() }), external_exports.array(StorageOccupancyReportSchema).readonly(), { auth: "admin" })
30360
+ }
30361
+ };
30271
30362
  var ProviderInfoSchema = external_exports.discriminatedUnion("shouldSaveDiskSpace", [external_exports.object({
30272
30363
  providerId: external_exports.string().min(1),
30273
30364
  displayName: external_exports.string().min(1),
@@ -32225,85 +32316,6 @@ var batteryCapability = {
32225
32316
  * whether persisting is worth a SQLite commit. */
32226
32317
  volatileStateFields: ["lastUpdated"]
32227
32318
  };
32228
- var NETWORK_LINK_TYPES = [
32229
- "wifi",
32230
- "ethernet",
32231
- "cellular",
32232
- "unknown"
32233
- ];
32234
- var NetworkLinkStatusSchema = external_exports.object({
32235
- /** The link the device is on. `'unknown'` = not read yet, not "no link". */
32236
- type: external_exports.enum(NETWORK_LINK_TYPES),
32237
- /**
32238
- * Link quality, 0..100 inclusive, normalised by the provider from whatever
32239
- * the firmware reports (bars, RSSI, a vendor scale). **`null` means NOT
32240
- * KNOWN or NOT APPLICABLE** — a wired link has no signal, and a wireless
32241
- * one whose reading has not landed must not be drawn at 0 %. Consumers
32242
- * SKIP a null rather than coerce it.
32243
- */
32244
- signalPercent: external_exports.number().min(0).max(100).nullable(),
32245
- /** Raw received signal strength in dBm, when the firmware reports one. */
32246
- rssiDbm: external_exports.number().optional(),
32247
- /** Network name of a wireless link, when the firmware reports it. */
32248
- ssid: external_exports.string().optional(),
32249
- /** Ms epoch of the last observation. Lets consumers reason about freshness. */
32250
- lastUpdated: external_exports.number()
32251
- });
32252
- var NETWORK_LINK_UNKNOWN = {
32253
- type: "unknown",
32254
- signalPercent: null,
32255
- lastUpdated: 0
32256
- };
32257
- var networkLinkCapability = {
32258
- name: "network-link",
32259
- scope: "device",
32260
- deviceNative: true,
32261
- mode: "singleton",
32262
- deviceTypes: [
32263
- DeviceType.Camera,
32264
- DeviceType.Sensor,
32265
- DeviceType.Button,
32266
- DeviceType.Switch,
32267
- DeviceType.Light,
32268
- DeviceType.Lock,
32269
- DeviceType.Siren
32270
- ],
32271
- methods: {},
32272
- events: {
32273
- /**
32274
- * Emitted whenever the cached status changes (a link switch, a signal
32275
- * reading that moved). Mirrored on the parent chain by the
32276
- * DeviceEventPropagator like `battery.onStatusChanged`.
32277
- */
32278
- onStatusChanged: { data: external_exports.object({
32279
- deviceId: external_exports.number(),
32280
- status: NetworkLinkStatusSchema
32281
- }) }
32282
- },
32283
- status: {
32284
- schema: NetworkLinkStatusSchema,
32285
- kind: "push",
32286
- empty: NETWORK_LINK_UNKNOWN
32287
- },
32288
- /**
32289
- * Runtime-state slice — every provider stores the same shape under
32290
- * `device.runtimeState['network-link']`, read once by the badge and the
32291
- * Home Assistant projector regardless of the driver.
32292
- */
32293
- runtimeState: NetworkLinkStatusSchema,
32294
- /**
32295
- * Runtime-state durability: **restored** — a link reading is slow to
32296
- * change and a sleeping battery camera may not report for hours; the
32297
- * restored slice is what the badge shows until the next read.
32298
- *
32299
- * See `RuntimeStateDurability`. Enforced by
32300
- * `scripts/check-runtime-state-durability.ts`.
32301
- */
32302
- durability: "restored",
32303
- /** Clock fields: written, but excluded from the compare that decides
32304
- * whether persisting is worth a SQLite commit. */
32305
- volatileStateFields: ["lastUpdated"]
32306
- };
32307
32319
  var BinaryStatusSchema = external_exports.object({
32308
32320
  on: external_exports.boolean(),
32309
32321
  /** Ms epoch of the last transition. 0 if never observed. */
@@ -35444,47 +35456,289 @@ var nativeObjectDetectionCapability = {
35444
35456
  * whether persisting is worth a SQLite commit. */
35445
35457
  volatileStateFields: ["lastFetchedAt"]
35446
35458
  };
35447
- var StreamNetworkStatsSchema = external_exports.object({
35448
- nominalBitrateKbps: external_exports.number(),
35449
- observedBitrateKbps: external_exports.number(),
35450
- peakBitrateKbps: external_exports.number(),
35451
- packetLossPercent: external_exports.number(),
35452
- lastUpdated: external_exports.number()
35459
+ var NavigationMoveCommandSchema = external_exports.object({
35460
+ pan: external_exports.number().min(-1).max(1).optional(),
35461
+ tilt: external_exports.number().min(-1).max(1).optional(),
35462
+ speed: external_exports.number().min(0).max(1).optional()
35453
35463
  });
35454
- var ClientNetworkStatsSchema = external_exports.object({
35455
- rttMs: external_exports.number(),
35456
- jitterMs: external_exports.number(),
35457
- estimatedBandwidthKbps: external_exports.number(),
35458
- packetLossPercent: external_exports.number().min(0).max(100),
35459
- lastUpdated: external_exports.number()
35464
+ var NavigationActionIdSchema = external_exports.enum([
35465
+ "goHome",
35466
+ "locate",
35467
+ "spotClean",
35468
+ "findPet",
35469
+ "personFollow",
35470
+ "stop",
35471
+ "startClean",
35472
+ "pauseClean",
35473
+ "dockWash",
35474
+ "autoEmpty",
35475
+ "flashOn",
35476
+ "flashOff"
35477
+ ]);
35478
+ var NavigationEntryKindSchema = external_exports.enum(["action", "sound"]);
35479
+ var NavigationActionEntrySchema = external_exports.object({
35480
+ id: external_exports.string(),
35481
+ kind: NavigationEntryKindSchema,
35482
+ label: external_exports.string(),
35483
+ icon: external_exports.string(),
35484
+ /** Present only on `kind:'sound'` entries — the id to pass to `playSound`. */
35485
+ soundId: external_exports.number().int().optional(),
35486
+ /** Per-device feature flag — render this entry only when true. */
35487
+ enabled: external_exports.boolean()
35460
35488
  });
35461
- var DeviceNetworkStatsSchema = external_exports.object({
35462
- deviceId: external_exports.number(),
35463
- streams: external_exports.record(external_exports.string(), StreamNetworkStatsSchema),
35464
- client: ClientNetworkStatsSchema.optional()
35489
+ var NavigationPointSchema = external_exports.object({
35490
+ x: external_exports.number(),
35491
+ y: external_exports.number()
35465
35492
  });
35466
- var networkQualityCapability = {
35467
- name: "network-quality",
35468
- scope: "system",
35469
- mode: "singleton",
35470
- methods: {
35471
- getDeviceStats: method(external_exports.object({ deviceId: external_exports.number() }), DeviceNetworkStatsSchema.nullable()),
35472
- getAllStats: method(external_exports.void(), external_exports.array(DeviceNetworkStatsSchema).readonly()),
35473
- reportClientStats: method(external_exports.object({
35474
- deviceId: external_exports.number(),
35475
- rttMs: external_exports.number().min(0).max(6e4),
35476
- jitterMs: external_exports.number().min(0).max(1e4),
35477
- estimatedBandwidthKbps: external_exports.number().min(0).max(1e6),
35478
- packetLossPercent: external_exports.number().min(0).max(100)
35479
- }), external_exports.void(), { kind: "mutation" })
35480
- },
35481
- /** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
35482
- mount: { kind: "server-provided" }
35483
- };
35484
- var TopologyServiceSchema = external_exports.object({
35485
- addonId: external_exports.string(),
35486
- capabilities: external_exports.array(external_exports.string()).readonly(),
35487
- status: external_exports.string()
35493
+ var NavigationFeaturesSchema = external_exports.object({
35494
+ move: external_exports.boolean(),
35495
+ stop: external_exports.boolean(),
35496
+ goToPoint: external_exports.boolean(),
35497
+ runAction: external_exports.boolean(),
35498
+ playSound: external_exports.boolean(),
35499
+ light: external_exports.boolean(),
35500
+ lightMode: external_exports.boolean()
35501
+ });
35502
+ var NavigationLightModeSchema = external_exports.enum(["auto", "manual"]);
35503
+ var NavigationModeSchema = external_exports.enum([
35504
+ "idle",
35505
+ "cleaning",
35506
+ "spot",
35507
+ "following",
35508
+ "goto",
35509
+ "returning",
35510
+ "paused",
35511
+ "unknown"
35512
+ ]);
35513
+ var NavigationStatusSchema = external_exports.object({
35514
+ mode: NavigationModeSchema,
35515
+ following: external_exports.boolean(),
35516
+ flash: external_exports.boolean(),
35517
+ lightMode: NavigationLightModeSchema,
35518
+ lightLevel: external_exports.number().min(40).max(100),
35519
+ /** Ms epoch when the slice was last updated. */
35520
+ lastChangedAt: external_exports.number()
35521
+ });
35522
+ var NavigationRuntimeStateSchema = NavigationStatusSchema.extend({ lastFetchedAt: external_exports.number() });
35523
+ var navigationCapability = {
35524
+ name: "navigation",
35525
+ scope: "device",
35526
+ deviceNative: true,
35527
+ mode: "singleton",
35528
+ deviceTypes: [DeviceType.Camera],
35529
+ deviceConfig: { ui: {
35530
+ kind: "widget",
35531
+ widgetId: "host/navigation-panel",
35532
+ tab: "navigation",
35533
+ topTab: true,
35534
+ label: "Navigation",
35535
+ order: 0
35536
+ } },
35537
+ methods: {
35538
+ /**
35539
+ * Momentary drive nudge (the robot moves). `protected` — mirrors
35540
+ * `ptz.continuousMove` so the Viewer navigation panel (and the PTZ-mimic
35541
+ * path) works for any authenticated user, not admin-only. The UI sends
35542
+ * these at ~1 Hz while a control is held; the provider forwards each one to
35543
+ * a single drive write WITHOUT debouncing.
35544
+ */
35545
+ move: method(NavigationMoveCommandSchema.extend({ deviceId: external_exports.number() }), external_exports.void(), { kind: "mutation" }),
35546
+ /** Halt all motion immediately (zero drive vector). */
35547
+ stop: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.void(), { kind: "mutation" }),
35548
+ /** Send the robot to a point on its live map. */
35549
+ goToPoint: method(NavigationPointSchema.extend({ deviceId: external_exports.number() }), external_exports.void(), { kind: "mutation" }),
35550
+ /**
35551
+ * Enumerate the discrete controls THIS device supports (data-driven UI +
35552
+ * PTZ mimic). Camera-probed subset of {@link NAVIGATION_ACTION_CATALOG}.
35553
+ */
35554
+ listActions: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.array(NavigationActionEntrySchema)),
35555
+ /**
35556
+ * Run one discrete action (a `kind:'action'` dictionary entry). Invalid /
35557
+ * unsupported action ids are rejected by the provider.
35558
+ */
35559
+ runAction: method(external_exports.object({
35560
+ deviceId: external_exports.number(),
35561
+ actionId: NavigationActionIdSchema
35562
+ }), external_exports.void(), { kind: "mutation" }),
35563
+ /** Play a sound by its wire id (the `soundId` of a `kind:'sound'` entry). */
35564
+ playSound: method(external_exports.object({
35565
+ deviceId: external_exports.number(),
35566
+ soundId: external_exports.number().int()
35567
+ }), external_exports.void(), { kind: "mutation" }),
35568
+ /**
35569
+ * Turn the on-camera fill light on / off (the `OpenFullLight` control —
35570
+ * works anytime, no active stream required).
35571
+ */
35572
+ setLightOn: method(external_exports.object({
35573
+ deviceId: external_exports.number(),
35574
+ on: external_exports.boolean()
35575
+ }), external_exports.void(), { kind: "mutation" }),
35576
+ /**
35577
+ * Set the fill-light mode (auto vs manual). `manual` optionally carries the
35578
+ * initial `level`. The auto/manual + level control is a CAMERA-service
35579
+ * action that generally needs an active camera stream/monitor session — the
35580
+ * UI shows the manual level slider ONLY when `mode === 'manual'`.
35581
+ */
35582
+ setLightMode: method(external_exports.object({
35583
+ deviceId: external_exports.number(),
35584
+ mode: NavigationLightModeSchema,
35585
+ level: external_exports.number().min(40).max(100).optional()
35586
+ }), external_exports.void(), { kind: "mutation" }),
35587
+ /** Set the MANUAL fill-light level (40..100). Implies `manual` mode. */
35588
+ setLightLevel: method(external_exports.object({
35589
+ deviceId: external_exports.number(),
35590
+ level: external_exports.number().min(40).max(100)
35591
+ }), external_exports.void(), { kind: "mutation" }),
35592
+ /**
35593
+ * Per-device FEATURE-FLAG report for the general primitives — drives which
35594
+ * controls the UI shows (the per-entry flags for the dictionary come back on
35595
+ * `listActions`).
35596
+ */
35597
+ getFeatures: method(external_exports.object({ deviceId: external_exports.number() }), NavigationFeaturesSchema)
35598
+ },
35599
+ events: { onStatusChanged: { data: external_exports.object({
35600
+ deviceId: external_exports.number(),
35601
+ status: NavigationStatusSchema
35602
+ }) } },
35603
+ status: {
35604
+ schema: NavigationStatusSchema,
35605
+ kind: "push"
35606
+ },
35607
+ /**
35608
+ * Runtime-state slice mirrored by the kernel. The navigation panel watches it
35609
+ * for live mode / follow / flash changes.
35610
+ */
35611
+ runtimeState: NavigationRuntimeStateSchema,
35612
+ /**
35613
+ * Runtime-state durability: **session** — like `vacuum-control`, a restored
35614
+ * `mode: cleaning` / `following: true` is a robot that is not actually doing
35615
+ * that. The live handle re-publishes on connect.
35616
+ *
35617
+ * See `RuntimeStateDurability`. Enforced by
35618
+ * `scripts/check-runtime-state-durability.ts`.
35619
+ */
35620
+ durability: "session"
35621
+ };
35622
+ var NETWORK_LINK_TYPES = [
35623
+ "wifi",
35624
+ "ethernet",
35625
+ "cellular",
35626
+ "unknown"
35627
+ ];
35628
+ var NetworkLinkStatusSchema = external_exports.object({
35629
+ /** The link the device is on. `'unknown'` = not read yet, not "no link". */
35630
+ type: external_exports.enum(NETWORK_LINK_TYPES),
35631
+ /**
35632
+ * Link quality, 0..100 inclusive, normalised by the provider from whatever
35633
+ * the firmware reports (bars, RSSI, a vendor scale). **`null` means NOT
35634
+ * KNOWN or NOT APPLICABLE** — a wired link has no signal, and a wireless
35635
+ * one whose reading has not landed must not be drawn at 0 %. Consumers
35636
+ * SKIP a null rather than coerce it.
35637
+ */
35638
+ signalPercent: external_exports.number().min(0).max(100).nullable(),
35639
+ /** Raw received signal strength in dBm, when the firmware reports one. */
35640
+ rssiDbm: external_exports.number().optional(),
35641
+ /** Network name of a wireless link, when the firmware reports it. */
35642
+ ssid: external_exports.string().optional(),
35643
+ /** Ms epoch of the last observation. Lets consumers reason about freshness. */
35644
+ lastUpdated: external_exports.number()
35645
+ });
35646
+ var NETWORK_LINK_UNKNOWN = {
35647
+ type: "unknown",
35648
+ signalPercent: null,
35649
+ lastUpdated: 0
35650
+ };
35651
+ var networkLinkCapability = {
35652
+ name: "network-link",
35653
+ scope: "device",
35654
+ deviceNative: true,
35655
+ mode: "singleton",
35656
+ deviceTypes: [
35657
+ DeviceType.Camera,
35658
+ DeviceType.Sensor,
35659
+ DeviceType.Button,
35660
+ DeviceType.Switch,
35661
+ DeviceType.Light,
35662
+ DeviceType.Lock,
35663
+ DeviceType.Siren
35664
+ ],
35665
+ methods: {},
35666
+ events: {
35667
+ /**
35668
+ * Emitted whenever the cached status changes (a link switch, a signal
35669
+ * reading that moved). Mirrored on the parent chain by the
35670
+ * DeviceEventPropagator like `battery.onStatusChanged`.
35671
+ */
35672
+ onStatusChanged: { data: external_exports.object({
35673
+ deviceId: external_exports.number(),
35674
+ status: NetworkLinkStatusSchema
35675
+ }) }
35676
+ },
35677
+ status: {
35678
+ schema: NetworkLinkStatusSchema,
35679
+ kind: "push",
35680
+ empty: NETWORK_LINK_UNKNOWN
35681
+ },
35682
+ /**
35683
+ * Runtime-state slice — every provider stores the same shape under
35684
+ * `device.runtimeState['network-link']`, read once by the badge and the
35685
+ * Home Assistant projector regardless of the driver.
35686
+ */
35687
+ runtimeState: NetworkLinkStatusSchema,
35688
+ /**
35689
+ * Runtime-state durability: **restored** — a link reading is slow to
35690
+ * change and a sleeping battery camera may not report for hours; the
35691
+ * restored slice is what the badge shows until the next read.
35692
+ *
35693
+ * See `RuntimeStateDurability`. Enforced by
35694
+ * `scripts/check-runtime-state-durability.ts`.
35695
+ */
35696
+ durability: "restored",
35697
+ /** Clock fields: written, but excluded from the compare that decides
35698
+ * whether persisting is worth a SQLite commit. */
35699
+ volatileStateFields: ["lastUpdated"]
35700
+ };
35701
+ var StreamNetworkStatsSchema = external_exports.object({
35702
+ nominalBitrateKbps: external_exports.number(),
35703
+ observedBitrateKbps: external_exports.number(),
35704
+ peakBitrateKbps: external_exports.number(),
35705
+ packetLossPercent: external_exports.number(),
35706
+ lastUpdated: external_exports.number()
35707
+ });
35708
+ var ClientNetworkStatsSchema = external_exports.object({
35709
+ rttMs: external_exports.number(),
35710
+ jitterMs: external_exports.number(),
35711
+ estimatedBandwidthKbps: external_exports.number(),
35712
+ packetLossPercent: external_exports.number().min(0).max(100),
35713
+ lastUpdated: external_exports.number()
35714
+ });
35715
+ var DeviceNetworkStatsSchema = external_exports.object({
35716
+ deviceId: external_exports.number(),
35717
+ streams: external_exports.record(external_exports.string(), StreamNetworkStatsSchema),
35718
+ client: ClientNetworkStatsSchema.optional()
35719
+ });
35720
+ var networkQualityCapability = {
35721
+ name: "network-quality",
35722
+ scope: "system",
35723
+ mode: "singleton",
35724
+ methods: {
35725
+ getDeviceStats: method(external_exports.object({ deviceId: external_exports.number() }), DeviceNetworkStatsSchema.nullable()),
35726
+ getAllStats: method(external_exports.void(), external_exports.array(DeviceNetworkStatsSchema).readonly()),
35727
+ reportClientStats: method(external_exports.object({
35728
+ deviceId: external_exports.number(),
35729
+ rttMs: external_exports.number().min(0).max(6e4),
35730
+ jitterMs: external_exports.number().min(0).max(1e4),
35731
+ estimatedBandwidthKbps: external_exports.number().min(0).max(1e6),
35732
+ packetLossPercent: external_exports.number().min(0).max(100)
35733
+ }), external_exports.void(), { kind: "mutation" })
35734
+ },
35735
+ /** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
35736
+ mount: { kind: "server-provided" }
35737
+ };
35738
+ var TopologyServiceSchema = external_exports.object({
35739
+ addonId: external_exports.string(),
35740
+ capabilities: external_exports.array(external_exports.string()).readonly(),
35741
+ status: external_exports.string()
35488
35742
  });
35489
35743
  var TopologyProcessSchema = external_exports.object({
35490
35744
  pid: external_exports.number(),
@@ -37009,169 +37263,6 @@ var ptzAutotrackCapability = {
37009
37263
  */
37010
37264
  durability: "session"
37011
37265
  };
37012
- var NavigationMoveCommandSchema = external_exports.object({
37013
- pan: external_exports.number().min(-1).max(1).optional(),
37014
- tilt: external_exports.number().min(-1).max(1).optional(),
37015
- speed: external_exports.number().min(0).max(1).optional()
37016
- });
37017
- var NavigationActionIdSchema = external_exports.enum([
37018
- "goHome",
37019
- "locate",
37020
- "spotClean",
37021
- "findPet",
37022
- "personFollow",
37023
- "stop",
37024
- "startClean",
37025
- "pauseClean",
37026
- "dockWash",
37027
- "autoEmpty",
37028
- "flashOn",
37029
- "flashOff"
37030
- ]);
37031
- var NavigationEntryKindSchema = external_exports.enum(["action", "sound"]);
37032
- var NavigationActionEntrySchema = external_exports.object({
37033
- id: external_exports.string(),
37034
- kind: NavigationEntryKindSchema,
37035
- label: external_exports.string(),
37036
- icon: external_exports.string(),
37037
- /** Present only on `kind:'sound'` entries — the id to pass to `playSound`. */
37038
- soundId: external_exports.number().int().optional(),
37039
- /** Per-device feature flag — render this entry only when true. */
37040
- enabled: external_exports.boolean()
37041
- });
37042
- var NavigationPointSchema = external_exports.object({
37043
- x: external_exports.number(),
37044
- y: external_exports.number()
37045
- });
37046
- var NavigationFeaturesSchema = external_exports.object({
37047
- move: external_exports.boolean(),
37048
- stop: external_exports.boolean(),
37049
- goToPoint: external_exports.boolean(),
37050
- runAction: external_exports.boolean(),
37051
- playSound: external_exports.boolean(),
37052
- light: external_exports.boolean(),
37053
- lightMode: external_exports.boolean()
37054
- });
37055
- var NavigationLightModeSchema = external_exports.enum(["auto", "manual"]);
37056
- var NavigationModeSchema = external_exports.enum([
37057
- "idle",
37058
- "cleaning",
37059
- "spot",
37060
- "following",
37061
- "goto",
37062
- "returning",
37063
- "paused",
37064
- "unknown"
37065
- ]);
37066
- var NavigationStatusSchema = external_exports.object({
37067
- mode: NavigationModeSchema,
37068
- following: external_exports.boolean(),
37069
- flash: external_exports.boolean(),
37070
- lightMode: NavigationLightModeSchema,
37071
- lightLevel: external_exports.number().min(40).max(100),
37072
- /** Ms epoch when the slice was last updated. */
37073
- lastChangedAt: external_exports.number()
37074
- });
37075
- var NavigationRuntimeStateSchema = NavigationStatusSchema.extend({ lastFetchedAt: external_exports.number() });
37076
- var navigationCapability = {
37077
- name: "navigation",
37078
- scope: "device",
37079
- deviceNative: true,
37080
- mode: "singleton",
37081
- deviceTypes: [DeviceType.Camera],
37082
- deviceConfig: { ui: {
37083
- kind: "widget",
37084
- widgetId: "host/navigation-panel",
37085
- tab: "navigation",
37086
- topTab: true,
37087
- label: "Navigation",
37088
- order: 0
37089
- } },
37090
- methods: {
37091
- /**
37092
- * Momentary drive nudge (the robot moves). `protected` — mirrors
37093
- * `ptz.continuousMove` so the Viewer navigation panel (and the PTZ-mimic
37094
- * path) works for any authenticated user, not admin-only. The UI sends
37095
- * these at ~1 Hz while a control is held; the provider forwards each one to
37096
- * a single drive write WITHOUT debouncing.
37097
- */
37098
- move: method(NavigationMoveCommandSchema.extend({ deviceId: external_exports.number() }), external_exports.void(), { kind: "mutation" }),
37099
- /** Halt all motion immediately (zero drive vector). */
37100
- stop: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.void(), { kind: "mutation" }),
37101
- /** Send the robot to a point on its live map. */
37102
- goToPoint: method(NavigationPointSchema.extend({ deviceId: external_exports.number() }), external_exports.void(), { kind: "mutation" }),
37103
- /**
37104
- * Enumerate the discrete controls THIS device supports (data-driven UI +
37105
- * PTZ mimic). Camera-probed subset of {@link NAVIGATION_ACTION_CATALOG}.
37106
- */
37107
- listActions: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.array(NavigationActionEntrySchema)),
37108
- /**
37109
- * Run one discrete action (a `kind:'action'` dictionary entry). Invalid /
37110
- * unsupported action ids are rejected by the provider.
37111
- */
37112
- runAction: method(external_exports.object({
37113
- deviceId: external_exports.number(),
37114
- actionId: NavigationActionIdSchema
37115
- }), external_exports.void(), { kind: "mutation" }),
37116
- /** Play a sound by its wire id (the `soundId` of a `kind:'sound'` entry). */
37117
- playSound: method(external_exports.object({
37118
- deviceId: external_exports.number(),
37119
- soundId: external_exports.number().int()
37120
- }), external_exports.void(), { kind: "mutation" }),
37121
- /**
37122
- * Turn the on-camera fill light on / off (the `OpenFullLight` control —
37123
- * works anytime, no active stream required).
37124
- */
37125
- setLightOn: method(external_exports.object({
37126
- deviceId: external_exports.number(),
37127
- on: external_exports.boolean()
37128
- }), external_exports.void(), { kind: "mutation" }),
37129
- /**
37130
- * Set the fill-light mode (auto vs manual). `manual` optionally carries the
37131
- * initial `level`. The auto/manual + level control is a CAMERA-service
37132
- * action that generally needs an active camera stream/monitor session — the
37133
- * UI shows the manual level slider ONLY when `mode === 'manual'`.
37134
- */
37135
- setLightMode: method(external_exports.object({
37136
- deviceId: external_exports.number(),
37137
- mode: NavigationLightModeSchema,
37138
- level: external_exports.number().min(40).max(100).optional()
37139
- }), external_exports.void(), { kind: "mutation" }),
37140
- /** Set the MANUAL fill-light level (40..100). Implies `manual` mode. */
37141
- setLightLevel: method(external_exports.object({
37142
- deviceId: external_exports.number(),
37143
- level: external_exports.number().min(40).max(100)
37144
- }), external_exports.void(), { kind: "mutation" }),
37145
- /**
37146
- * Per-device FEATURE-FLAG report for the general primitives — drives which
37147
- * controls the UI shows (the per-entry flags for the dictionary come back on
37148
- * `listActions`).
37149
- */
37150
- getFeatures: method(external_exports.object({ deviceId: external_exports.number() }), NavigationFeaturesSchema)
37151
- },
37152
- events: { onStatusChanged: { data: external_exports.object({
37153
- deviceId: external_exports.number(),
37154
- status: NavigationStatusSchema
37155
- }) } },
37156
- status: {
37157
- schema: NavigationStatusSchema,
37158
- kind: "push"
37159
- },
37160
- /**
37161
- * Runtime-state slice mirrored by the kernel. The navigation panel watches it
37162
- * for live mode / follow / flash changes.
37163
- */
37164
- runtimeState: NavigationRuntimeStateSchema,
37165
- /**
37166
- * Runtime-state durability: **session** — like `vacuum-control`, a restored
37167
- * `mode: cleaning` / `following: true` is a robot that is not actually doing
37168
- * that. The live handle re-publishes on connect.
37169
- *
37170
- * See `RuntimeStateDurability`. Enforced by
37171
- * `scripts/check-runtime-state-durability.ts`.
37172
- */
37173
- durability: "session"
37174
- };
37175
37266
  var rebootCapability = {
37176
37267
  name: "reboot",
37177
37268
  scope: "device",
@@ -44834,13 +44925,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
44834
44925
  addonId: null,
44835
44926
  access: "view"
44836
44927
  },
44837
- "storage.getDefaultLocation": {
44928
+ "storage.list": {
44838
44929
  capName: "storage",
44839
44930
  capScope: "system",
44840
44931
  addonId: null,
44841
44932
  access: "view"
44842
44933
  },
44843
- "storage.list": {
44934
+ "storage.listDrainProgress": {
44844
44935
  capName: "storage",
44845
44936
  capScope: "system",
44846
44937
  addonId: null,
@@ -44990,6 +45081,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
44990
45081
  addonId: null,
44991
45082
  access: "view"
44992
45083
  },
45084
+ "storageOccupancy.getOccupancy": {
45085
+ capName: "storage-occupancy",
45086
+ capScope: "system",
45087
+ addonId: null,
45088
+ access: "view"
45089
+ },
44993
45090
  "storageProvider.abortUpload": {
44994
45091
  capName: "storage-provider",
44995
45092
  capScope: "system",