@camstack/system 1.2.32 → 1.2.34

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 (50) hide show
  1. package/dist/auth/scoped-token-manager.d.ts +25 -0
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  6. package/dist/builtins/alerts/alerts.addon.js +1 -1
  7. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  10. package/dist/builtins/console-logging/index.js +1 -1
  11. package/dist/builtins/console-logging/index.mjs +1 -1
  12. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  13. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  14. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  15. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  16. package/dist/builtins/hub-forwarder/index.js +1 -1
  17. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  18. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  19. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  20. package/dist/builtins/local-auth/local-auth.addon.d.ts +12 -0
  21. package/dist/builtins/local-auth/local-auth.addon.js +100 -1
  22. package/dist/builtins/local-auth/local-auth.addon.mjs +100 -1
  23. package/dist/builtins/local-auth/oauth-session-manager.d.ts +19 -0
  24. package/dist/builtins/local-network/local-network.addon.js +1 -1
  25. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  26. package/dist/builtins/loki-logging/index.js +1 -1
  27. package/dist/builtins/loki-logging/index.mjs +1 -1
  28. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  29. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  30. package/dist/builtins/platform-probe/index.js +1 -1
  31. package/dist/builtins/platform-probe/index.mjs +1 -1
  32. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  34. package/dist/builtins/snapshot/index.js +1 -1
  35. package/dist/builtins/snapshot/index.mjs +1 -1
  36. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  38. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
  39. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
  40. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  41. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  42. package/dist/builtins/system-config/system-config.addon.js +1 -1
  43. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  44. package/dist/builtins/winston-logging/index.js +1 -1
  45. package/dist/builtins/winston-logging/index.mjs +1 -1
  46. package/dist/{dist-BL_FHWPP.mjs → dist-BjUYsgkj.mjs} +189 -5
  47. package/dist/{dist-CSs1A90r.js → dist-Bqyu3d1X.js} +189 -5
  48. package/dist/index.js +1 -1
  49. package/dist/index.mjs +1 -1
  50. package/package.json +1 -1
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CSs1A90r.js");
6
+ const require_dist = require("../../dist-Bqyu3d1X.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { et as storageProviderCapability, j as filesystemBrowseCapability, ot as BaseAddon } from "../../dist-BL_FHWPP.mjs";
1
+ import { et as storageProviderCapability, j as filesystemBrowseCapability, ot as BaseAddon } from "../../dist-BjUYsgkj.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import * as path$1 from "node:path";
4
4
  import { basename, dirname, join, resolve, sep } from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CSs1A90r.js");
6
+ const require_dist = require("../../dist-Bqyu3d1X.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let better_sqlite3 = require("better-sqlite3");
9
9
  better_sqlite3 = require_chunk.__toESM(better_sqlite3);
@@ -1,4 +1,4 @@
1
- import { Dt as parseJsonUnknown, at as errMsg, c as RUNTIME_DEFAULTS, gt as asJsonObject, ot as BaseAddon, x as dataStoreProviderCapability } from "../../dist-BL_FHWPP.mjs";
1
+ import { Dt as parseJsonUnknown, at as errMsg, c as RUNTIME_DEFAULTS, gt as asJsonObject, ot as BaseAddon, x as dataStoreProviderCapability } from "../../dist-BjUYsgkj.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import Database from "better-sqlite3";
4
4
  //#region src/builtins/sqlite-storage/filter-compiler.ts
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CSs1A90r.js");
6
+ const require_dist = require("../../dist-Bqyu3d1X.js");
7
7
  let node_path = require("node:path");
8
8
  node_path = require_chunk.__toESM(node_path);
9
9
  let node_fs_promises = require("node:fs/promises");
@@ -1,4 +1,4 @@
1
- import { $ as storageCapability, Et as parseJsonObject, Z as settingsStoreCapability, d as StorageLocationTypeSchema, ot as BaseAddon } from "../../dist-BL_FHWPP.mjs";
1
+ import { $ as storageCapability, Et as parseJsonObject, Z as settingsStoreCapability, d as StorageLocationTypeSchema, ot as BaseAddon } from "../../dist-BjUYsgkj.mjs";
2
2
  import * as path$1 from "node:path";
3
3
  import * as fs from "node:fs/promises";
4
4
  import { buildStorageLocationRegistry } from "@camstack/system";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CSs1A90r.js");
6
+ const require_dist = require("../../dist-Bqyu3d1X.js");
7
7
  //#region src/builtins/system-config/system-config.addon.ts
8
8
  /**
9
9
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -1,4 +1,4 @@
1
- import { Ct as hydrateSchema, at as errMsg, ot as BaseAddon } from "../../dist-BL_FHWPP.mjs";
1
+ import { Ct as hydrateSchema, at as errMsg, ot as BaseAddon } from "../../dist-BjUYsgkj.mjs";
2
2
  //#region src/builtins/system-config/system-config.addon.ts
3
3
  /**
4
4
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CSs1A90r.js");
6
+ const require_dist = require("../../dist-Bqyu3d1X.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  let node_path = require("node:path");
9
9
  node_path = require_chunk.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { B as logDestinationCapability, ot as BaseAddon } from "../../dist-BL_FHWPP.mjs";
1
+ import { B as logDestinationCapability, ot as BaseAddon } from "../../dist-BjUYsgkj.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  import * as path$1 from "node:path";
4
4
  import path from "node:path";
@@ -5412,7 +5412,24 @@ var NcZoneConditionSchema = z.object({
5412
5412
  * The P1 condition set — a flat AND of groups; absent group = pass;
5413
5413
  * membership lists are OR within the list (spec §2.3).
5414
5414
  */
5415
+ /**
5416
+ * "This rule applies only while `deviceId` is in one of `states`."
5417
+ *
5418
+ * The states are the DEVICE's own vocabulary — `AlarmState` for a panel,
5419
+ * `on`/`off` for a switch — not a normalised set, because normalising would
5420
+ * make the condition lie about devices whose states have no equivalent.
5421
+ *
5422
+ * An unreadable state does NOT match: see the engine's fail-closed gate. A
5423
+ * condition that fired on "I could not read it" would be worse than no gate.
5424
+ */
5425
+ var NcDeviceStateConditionSchema = z.object({
5426
+ deviceId: z.number().int(),
5427
+ /** Any of these matches. */
5428
+ states: z.array(z.string().min(1)).min(1)
5429
+ });
5415
5430
  var NcConditionsSchema = z.object({
5431
+ /** Gate on ANOTHER device's current state (the alarm armed, a switch on). */
5432
+ deviceState: NcDeviceStateConditionSchema.optional(),
5416
5433
  /** Device scope — absent = all devices. */
5417
5434
  devices: z.array(z.number()).optional(),
5418
5435
  /** Detector class names (any overlap with the record's class set). */
@@ -5613,6 +5630,14 @@ var NcMediaPolicySchema = z.object({
5613
5630
  clipPreRollSec: z.number().int().min(0).max(30).optional(),
5614
5631
  clipPostRollSec: z.number().int().min(0).max(30).optional(),
5615
5632
  /**
5633
+ * Playback rate of the attached gif / clip. Absent = 2x.
5634
+ *
5635
+ * A notification clip is GLANCED at on a lock screen, not watched: at real
5636
+ * time an eight-second passage is eight seconds of the recipient's attention
5637
+ * and twice the bytes. 1 is real time for the operator who wants it.
5638
+ */
5639
+ clipSpeed: z.number().min(1).max(8).optional(),
5640
+ /**
5616
5641
  * Which stream profile the footage is cut from. Absent = the CHEAPEST
5617
5642
  * assigned profile: a notification is watched on a phone, so the 4K
5618
5643
  * rendition would burn CPU to produce a file the client downscales anyway.
@@ -5684,7 +5709,21 @@ var NcRuleInputSchema = z.object({
5684
5709
  * behaviour, visible to all, read-only in the viewer). Present = personal
5685
5710
  * rule owned by this userId. Server-stamped; never trusted from a client.
5686
5711
  */
5687
- ownerUserId: z.string().optional()
5712
+ ownerUserId: z.string().optional(),
5713
+ /**
5714
+ * May a non-admin snooze this rule for EVERYONE, not just themselves?
5715
+ *
5716
+ * A snooze is personal by default — it silences the person who set it. This
5717
+ * opts THIS rule into the "the gardener is here all afternoon" case, where
5718
+ * silencing the camera for the whole household is legitimate. It silences
5719
+ * other people, so it is off unless a rule deliberately allows it.
5720
+ *
5721
+ * `.optional()`, deliberately NOT `.default()`: a Zod default does not run on
5722
+ * the addon cap path (three production failures in one day), so absent is
5723
+ * read as `false` by {@link canSetGlobal} in the engine. Admins are not bound
5724
+ * by this flag — see the scope rules on that function.
5725
+ */
5726
+ snoozeAllowGlobal: z.boolean().optional()
5688
5727
  });
5689
5728
  /**
5690
5729
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -5755,7 +5794,8 @@ var NcConditionDescriptorSchema = z.object({
5755
5794
  "packagePhase",
5756
5795
  "crossingSelect",
5757
5796
  "polygonDraw",
5758
- "occupancy"
5797
+ "occupancy",
5798
+ "deviceState"
5759
5799
  ]),
5760
5800
  operator: z.enum([
5761
5801
  "in",
@@ -5769,7 +5809,28 @@ var NcConditionDescriptorSchema = z.object({
5769
5809
  /** Which delivery kinds the condition applies to. */
5770
5810
  appliesTo: z.array(NcDeliverySchema),
5771
5811
  phase: z.string(),
5772
- description: z.string().optional()
5812
+ description: z.string().optional(),
5813
+ /**
5814
+ * The CHOICES for a single-choice widget (`sourceSelect`, `crossingSelect`,
5815
+ * `packagePhase`, …), served with the descriptor.
5816
+ *
5817
+ * Before this the descriptor said which widget to render and not what to put
5818
+ * in it, so every option list lived in three places: this file's enums, the
5819
+ * admin's `NC_*_OPTIONS` and the viewer's `NC_*_VALUES`. That triple mirror
5820
+ * is the drift that emptied the viewer's rule editor on 2026-08-04 — the app
5821
+ * mirrors the cap by hand, so it is only ever as current as its last build.
5822
+ *
5823
+ * With the options on the wire, a condition of an EXISTING `valueType` costs
5824
+ * zero client changes. Clients keep a local fallback for an older hub that
5825
+ * does not send them; absent here is "use your own list", not "no choices".
5826
+ */
5827
+ options: z.array(z.object({
5828
+ /** Written to the rule verbatim. `''` means the ABSENT state. */
5829
+ value: z.string(),
5830
+ label: z.string(),
5831
+ /** What THIS choice matches — shown one at a time, under the control. */
5832
+ hint: z.string().optional()
5833
+ })).readonly().optional()
5773
5834
  });
5774
5835
  /**
5775
5836
  * The delivery lifecycle status of a history row — a straight read of the
@@ -5854,6 +5915,74 @@ var NcHistoryFilterSchema = z.object({
5854
5915
  until: z.number().optional(),
5855
5916
  limit: z.number().int().min(1).max(500).default(100)
5856
5917
  });
5918
+ /**
5919
+ * What a snooze covers. Broader scopes win when several overlap, so one window
5920
+ * leaves ONE digest rather than a rule snooze and a whole-feed snooze both
5921
+ * summarising the same silence.
5922
+ */
5923
+ var NcSnoozeScopeSchema = z.enum([
5924
+ "rule",
5925
+ "device",
5926
+ "all"
5927
+ ]);
5928
+ /**
5929
+ * Client-authored snooze. The server stamps `userId`, `startedAt` and
5930
+ * `expiresAt` — a DURATION is sent rather than an instant so a client with a
5931
+ * skewed clock cannot author a window that is already over, or never ends.
5932
+ */
5933
+ var NcSnoozeInputSchema = z.object({
5934
+ scope: NcSnoozeScopeSchema,
5935
+ /** Required when `scope: 'rule'` — a scoped snooze with no id matches
5936
+ * NOTHING rather than degrading to "everything". */
5937
+ ruleId: z.string().optional(),
5938
+ /** Required when `scope: 'device'`. */
5939
+ deviceId: z.number().int().optional(),
5940
+ durationMinutes: z.number().int().min(1).max(1440),
5941
+ /**
5942
+ * Silence this for EVERY recipient, not just the caller. Permission is
5943
+ * checked server-side (the rule's `snoozeAllowGlobal`, or admin for the
5944
+ * broader scopes). Absent = personal.
5945
+ */
5946
+ global: z.boolean().optional(),
5947
+ /**
5948
+ * Deliver a summary of what was suppressed when the window ends. Absent =
5949
+ * ON: someone silencing a nuisance camera wants it off, someone silencing a
5950
+ * SECURITY camera wants to know what they missed, and choosing "off" for
5951
+ * everybody is how a snooze becomes an outage. Resolved to a concrete
5952
+ * boolean by the server at create time — never left to a Zod default, which
5953
+ * does not run on the addon cap path.
5954
+ */
5955
+ summary: z.boolean().optional()
5956
+ });
5957
+ /** A persisted snooze window. */
5958
+ var NcSnoozeSchema = z.object({
5959
+ id: z.string(),
5960
+ /** Who set it. Also who it silences, unless `global`. */
5961
+ userId: z.string(),
5962
+ scope: NcSnoozeScopeSchema,
5963
+ ruleId: z.string().optional(),
5964
+ deviceId: z.number().int().optional(),
5965
+ startedAt: z.number(),
5966
+ /** Exclusive: at exactly this instant the snooze is over. Expiry is a
5967
+ * COMPARISON, not a job — no sweeper can leave the operator silenced. */
5968
+ expiresAt: z.number(),
5969
+ global: z.boolean(),
5970
+ summary: z.boolean(),
5971
+ /** When the end-of-window digest went out. Absent = not sent (yet, or the
5972
+ * window has not closed, or `summary` is false). */
5973
+ digestSentAt: z.number().optional()
5974
+ });
5975
+ z.object({
5976
+ snoozeId: z.string(),
5977
+ targetId: z.string(),
5978
+ ruleId: z.string(),
5979
+ ruleName: z.string(),
5980
+ deviceId: z.number().int(),
5981
+ /** How many notifications this snooze hid for that pair. */
5982
+ count: z.number().int(),
5983
+ firstAt: z.number(),
5984
+ lastAt: z.number()
5985
+ });
5857
5986
  var notificationRulesCapability = {
5858
5987
  name: "notification-rules",
5859
5988
  scope: "system",
@@ -5920,7 +6049,36 @@ var notificationRulesCapability = {
5920
6049
  * P1 (no user dimension); the P2 viewer History screen adds per-caller
5921
6050
  * scoping on the same method.
5922
6051
  */
5923
- getHistory: method(z.object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), z.object({ entries: z.array(NcHistoryEntrySchema) }), { auth: "admin" })
6052
+ getHistory: method(z.object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), z.object({ entries: z.array(NcHistoryEntrySchema) }), { auth: "admin" }),
6053
+ /**
6054
+ * Snooze windows currently in effect, plus any whose digest has not yet
6055
+ * gone out. `caller: 'required'`: a user sees their OWN windows and the
6056
+ * global ones that silence them, never another person's private silence.
6057
+ * An admin sees every window — a household-wide silence is exactly the
6058
+ * thing an operator has to be able to find and cancel.
6059
+ */
6060
+ listSnoozes: method(z.object({}), z.object({ snoozes: z.array(NcSnoozeSchema) }), { caller: "required" }),
6061
+ /**
6062
+ * Open a window. `userId` is stamped from the caller; a GLOBAL window is
6063
+ * permission-checked server-side (the rule's `snoozeAllowGlobal` for a
6064
+ * rule-scoped one, admin for a camera or the whole feed) — a client asking
6065
+ * for one it may not have is rejected, never silently downgraded, because
6066
+ * a snooze the operator believes is in effect and is not is the same
6067
+ * failure as one they cannot end.
6068
+ */
6069
+ createSnooze: method(z.object({ snooze: NcSnoozeInputSchema }), z.object({ snooze: NcSnoozeSchema }), {
6070
+ kind: "mutation",
6071
+ caller: "required"
6072
+ }),
6073
+ /**
6074
+ * End a window NOW. The digest still goes out (what was hidden while it
6075
+ * was open is still what the operator missed); only the silence stops.
6076
+ * A user may cancel their own; an admin may cancel any.
6077
+ */
6078
+ cancelSnooze: method(z.object({ snoozeId: z.string() }), z.object({ success: z.literal(true) }), {
6079
+ kind: "mutation",
6080
+ caller: "required"
6081
+ })
5924
6082
  }
5925
6083
  };
5926
6084
  /**
@@ -6988,7 +7146,15 @@ var streamBrokerCapability = {
6988
7146
  format: z.enum(["gif", "mp4"]).default("gif"),
6989
7147
  maxWidth: z.number().int().min(120).max(1920).default(480),
6990
7148
  /** GIF only — MP4 keeps the source cadence. */
6991
- fps: z.number().int().min(1).max(15).default(5)
7149
+ fps: z.number().int().min(1).max(15).default(5),
7150
+ /**
7151
+ * Playback rate. A notification clip is GLANCED at on a lock screen,
7152
+ * not watched, so 2x is the default: the recipient sees the whole
7153
+ * passage in half the time and the GIF is half the bytes. `1` is real
7154
+ * time. Applies to MP4 as well — the operator set a speed, not a GIF
7155
+ * speed.
7156
+ */
7157
+ speed: z.number().min(1).max(8).default(2)
6992
7158
  }), z.object({
6993
7159
  base64: z.string(),
6994
7160
  mime: z.string(),
@@ -26905,12 +27071,24 @@ var METHOD_ACCESS_MAP = Object.freeze({
26905
27071
  addonId: null,
26906
27072
  access: "create"
26907
27073
  },
27074
+ "notificationRules.cancelSnooze": {
27075
+ capName: "notification-rules",
27076
+ capScope: "system",
27077
+ addonId: null,
27078
+ access: "create"
27079
+ },
26908
27080
  "notificationRules.createRule": {
26909
27081
  capName: "notification-rules",
26910
27082
  capScope: "system",
26911
27083
  addonId: null,
26912
27084
  access: "create"
26913
27085
  },
27086
+ "notificationRules.createSnooze": {
27087
+ capName: "notification-rules",
27088
+ capScope: "system",
27089
+ addonId: null,
27090
+ access: "create"
27091
+ },
26914
27092
  "notificationRules.deleteRule": {
26915
27093
  capName: "notification-rules",
26916
27094
  capScope: "system",
@@ -26941,6 +27119,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
26941
27119
  addonId: null,
26942
27120
  access: "view"
26943
27121
  },
27122
+ "notificationRules.listSnoozes": {
27123
+ capName: "notification-rules",
27124
+ capScope: "system",
27125
+ addonId: null,
27126
+ access: "view"
27127
+ },
26944
27128
  "notificationRules.setRuleEnabled": {
26945
27129
  capName: "notification-rules",
26946
27130
  capScope: "system",
@@ -5412,7 +5412,24 @@ var NcZoneConditionSchema = zod.z.object({
5412
5412
  * The P1 condition set — a flat AND of groups; absent group = pass;
5413
5413
  * membership lists are OR within the list (spec §2.3).
5414
5414
  */
5415
+ /**
5416
+ * "This rule applies only while `deviceId` is in one of `states`."
5417
+ *
5418
+ * The states are the DEVICE's own vocabulary — `AlarmState` for a panel,
5419
+ * `on`/`off` for a switch — not a normalised set, because normalising would
5420
+ * make the condition lie about devices whose states have no equivalent.
5421
+ *
5422
+ * An unreadable state does NOT match: see the engine's fail-closed gate. A
5423
+ * condition that fired on "I could not read it" would be worse than no gate.
5424
+ */
5425
+ var NcDeviceStateConditionSchema = zod.z.object({
5426
+ deviceId: zod.z.number().int(),
5427
+ /** Any of these matches. */
5428
+ states: zod.z.array(zod.z.string().min(1)).min(1)
5429
+ });
5415
5430
  var NcConditionsSchema = zod.z.object({
5431
+ /** Gate on ANOTHER device's current state (the alarm armed, a switch on). */
5432
+ deviceState: NcDeviceStateConditionSchema.optional(),
5416
5433
  /** Device scope — absent = all devices. */
5417
5434
  devices: zod.z.array(zod.z.number()).optional(),
5418
5435
  /** Detector class names (any overlap with the record's class set). */
@@ -5613,6 +5630,14 @@ var NcMediaPolicySchema = zod.z.object({
5613
5630
  clipPreRollSec: zod.z.number().int().min(0).max(30).optional(),
5614
5631
  clipPostRollSec: zod.z.number().int().min(0).max(30).optional(),
5615
5632
  /**
5633
+ * Playback rate of the attached gif / clip. Absent = 2x.
5634
+ *
5635
+ * A notification clip is GLANCED at on a lock screen, not watched: at real
5636
+ * time an eight-second passage is eight seconds of the recipient's attention
5637
+ * and twice the bytes. 1 is real time for the operator who wants it.
5638
+ */
5639
+ clipSpeed: zod.z.number().min(1).max(8).optional(),
5640
+ /**
5616
5641
  * Which stream profile the footage is cut from. Absent = the CHEAPEST
5617
5642
  * assigned profile: a notification is watched on a phone, so the 4K
5618
5643
  * rendition would burn CPU to produce a file the client downscales anyway.
@@ -5684,7 +5709,21 @@ var NcRuleInputSchema = zod.z.object({
5684
5709
  * behaviour, visible to all, read-only in the viewer). Present = personal
5685
5710
  * rule owned by this userId. Server-stamped; never trusted from a client.
5686
5711
  */
5687
- ownerUserId: zod.z.string().optional()
5712
+ ownerUserId: zod.z.string().optional(),
5713
+ /**
5714
+ * May a non-admin snooze this rule for EVERYONE, not just themselves?
5715
+ *
5716
+ * A snooze is personal by default — it silences the person who set it. This
5717
+ * opts THIS rule into the "the gardener is here all afternoon" case, where
5718
+ * silencing the camera for the whole household is legitimate. It silences
5719
+ * other people, so it is off unless a rule deliberately allows it.
5720
+ *
5721
+ * `.optional()`, deliberately NOT `.default()`: a Zod default does not run on
5722
+ * the addon cap path (three production failures in one day), so absent is
5723
+ * read as `false` by {@link canSetGlobal} in the engine. Admins are not bound
5724
+ * by this flag — see the scope rules on that function.
5725
+ */
5726
+ snoozeAllowGlobal: zod.z.boolean().optional()
5688
5727
  });
5689
5728
  /**
5690
5729
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -5755,7 +5794,8 @@ var NcConditionDescriptorSchema = zod.z.object({
5755
5794
  "packagePhase",
5756
5795
  "crossingSelect",
5757
5796
  "polygonDraw",
5758
- "occupancy"
5797
+ "occupancy",
5798
+ "deviceState"
5759
5799
  ]),
5760
5800
  operator: zod.z.enum([
5761
5801
  "in",
@@ -5769,7 +5809,28 @@ var NcConditionDescriptorSchema = zod.z.object({
5769
5809
  /** Which delivery kinds the condition applies to. */
5770
5810
  appliesTo: zod.z.array(NcDeliverySchema),
5771
5811
  phase: zod.z.string(),
5772
- description: zod.z.string().optional()
5812
+ description: zod.z.string().optional(),
5813
+ /**
5814
+ * The CHOICES for a single-choice widget (`sourceSelect`, `crossingSelect`,
5815
+ * `packagePhase`, …), served with the descriptor.
5816
+ *
5817
+ * Before this the descriptor said which widget to render and not what to put
5818
+ * in it, so every option list lived in three places: this file's enums, the
5819
+ * admin's `NC_*_OPTIONS` and the viewer's `NC_*_VALUES`. That triple mirror
5820
+ * is the drift that emptied the viewer's rule editor on 2026-08-04 — the app
5821
+ * mirrors the cap by hand, so it is only ever as current as its last build.
5822
+ *
5823
+ * With the options on the wire, a condition of an EXISTING `valueType` costs
5824
+ * zero client changes. Clients keep a local fallback for an older hub that
5825
+ * does not send them; absent here is "use your own list", not "no choices".
5826
+ */
5827
+ options: zod.z.array(zod.z.object({
5828
+ /** Written to the rule verbatim. `''` means the ABSENT state. */
5829
+ value: zod.z.string(),
5830
+ label: zod.z.string(),
5831
+ /** What THIS choice matches — shown one at a time, under the control. */
5832
+ hint: zod.z.string().optional()
5833
+ })).readonly().optional()
5773
5834
  });
5774
5835
  /**
5775
5836
  * The delivery lifecycle status of a history row — a straight read of the
@@ -5854,6 +5915,74 @@ var NcHistoryFilterSchema = zod.z.object({
5854
5915
  until: zod.z.number().optional(),
5855
5916
  limit: zod.z.number().int().min(1).max(500).default(100)
5856
5917
  });
5918
+ /**
5919
+ * What a snooze covers. Broader scopes win when several overlap, so one window
5920
+ * leaves ONE digest rather than a rule snooze and a whole-feed snooze both
5921
+ * summarising the same silence.
5922
+ */
5923
+ var NcSnoozeScopeSchema = zod.z.enum([
5924
+ "rule",
5925
+ "device",
5926
+ "all"
5927
+ ]);
5928
+ /**
5929
+ * Client-authored snooze. The server stamps `userId`, `startedAt` and
5930
+ * `expiresAt` — a DURATION is sent rather than an instant so a client with a
5931
+ * skewed clock cannot author a window that is already over, or never ends.
5932
+ */
5933
+ var NcSnoozeInputSchema = zod.z.object({
5934
+ scope: NcSnoozeScopeSchema,
5935
+ /** Required when `scope: 'rule'` — a scoped snooze with no id matches
5936
+ * NOTHING rather than degrading to "everything". */
5937
+ ruleId: zod.z.string().optional(),
5938
+ /** Required when `scope: 'device'`. */
5939
+ deviceId: zod.z.number().int().optional(),
5940
+ durationMinutes: zod.z.number().int().min(1).max(1440),
5941
+ /**
5942
+ * Silence this for EVERY recipient, not just the caller. Permission is
5943
+ * checked server-side (the rule's `snoozeAllowGlobal`, or admin for the
5944
+ * broader scopes). Absent = personal.
5945
+ */
5946
+ global: zod.z.boolean().optional(),
5947
+ /**
5948
+ * Deliver a summary of what was suppressed when the window ends. Absent =
5949
+ * ON: someone silencing a nuisance camera wants it off, someone silencing a
5950
+ * SECURITY camera wants to know what they missed, and choosing "off" for
5951
+ * everybody is how a snooze becomes an outage. Resolved to a concrete
5952
+ * boolean by the server at create time — never left to a Zod default, which
5953
+ * does not run on the addon cap path.
5954
+ */
5955
+ summary: zod.z.boolean().optional()
5956
+ });
5957
+ /** A persisted snooze window. */
5958
+ var NcSnoozeSchema = zod.z.object({
5959
+ id: zod.z.string(),
5960
+ /** Who set it. Also who it silences, unless `global`. */
5961
+ userId: zod.z.string(),
5962
+ scope: NcSnoozeScopeSchema,
5963
+ ruleId: zod.z.string().optional(),
5964
+ deviceId: zod.z.number().int().optional(),
5965
+ startedAt: zod.z.number(),
5966
+ /** Exclusive: at exactly this instant the snooze is over. Expiry is a
5967
+ * COMPARISON, not a job — no sweeper can leave the operator silenced. */
5968
+ expiresAt: zod.z.number(),
5969
+ global: zod.z.boolean(),
5970
+ summary: zod.z.boolean(),
5971
+ /** When the end-of-window digest went out. Absent = not sent (yet, or the
5972
+ * window has not closed, or `summary` is false). */
5973
+ digestSentAt: zod.z.number().optional()
5974
+ });
5975
+ zod.z.object({
5976
+ snoozeId: zod.z.string(),
5977
+ targetId: zod.z.string(),
5978
+ ruleId: zod.z.string(),
5979
+ ruleName: zod.z.string(),
5980
+ deviceId: zod.z.number().int(),
5981
+ /** How many notifications this snooze hid for that pair. */
5982
+ count: zod.z.number().int(),
5983
+ firstAt: zod.z.number(),
5984
+ lastAt: zod.z.number()
5985
+ });
5857
5986
  var notificationRulesCapability = {
5858
5987
  name: "notification-rules",
5859
5988
  scope: "system",
@@ -5920,7 +6049,36 @@ var notificationRulesCapability = {
5920
6049
  * P1 (no user dimension); the P2 viewer History screen adds per-caller
5921
6050
  * scoping on the same method.
5922
6051
  */
5923
- getHistory: method(zod.z.object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), zod.z.object({ entries: zod.z.array(NcHistoryEntrySchema) }), { auth: "admin" })
6052
+ getHistory: method(zod.z.object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), zod.z.object({ entries: zod.z.array(NcHistoryEntrySchema) }), { auth: "admin" }),
6053
+ /**
6054
+ * Snooze windows currently in effect, plus any whose digest has not yet
6055
+ * gone out. `caller: 'required'`: a user sees their OWN windows and the
6056
+ * global ones that silence them, never another person's private silence.
6057
+ * An admin sees every window — a household-wide silence is exactly the
6058
+ * thing an operator has to be able to find and cancel.
6059
+ */
6060
+ listSnoozes: method(zod.z.object({}), zod.z.object({ snoozes: zod.z.array(NcSnoozeSchema) }), { caller: "required" }),
6061
+ /**
6062
+ * Open a window. `userId` is stamped from the caller; a GLOBAL window is
6063
+ * permission-checked server-side (the rule's `snoozeAllowGlobal` for a
6064
+ * rule-scoped one, admin for a camera or the whole feed) — a client asking
6065
+ * for one it may not have is rejected, never silently downgraded, because
6066
+ * a snooze the operator believes is in effect and is not is the same
6067
+ * failure as one they cannot end.
6068
+ */
6069
+ createSnooze: method(zod.z.object({ snooze: NcSnoozeInputSchema }), zod.z.object({ snooze: NcSnoozeSchema }), {
6070
+ kind: "mutation",
6071
+ caller: "required"
6072
+ }),
6073
+ /**
6074
+ * End a window NOW. The digest still goes out (what was hidden while it
6075
+ * was open is still what the operator missed); only the silence stops.
6076
+ * A user may cancel their own; an admin may cancel any.
6077
+ */
6078
+ cancelSnooze: method(zod.z.object({ snoozeId: zod.z.string() }), zod.z.object({ success: zod.z.literal(true) }), {
6079
+ kind: "mutation",
6080
+ caller: "required"
6081
+ })
5924
6082
  }
5925
6083
  };
5926
6084
  /**
@@ -6988,7 +7146,15 @@ var streamBrokerCapability = {
6988
7146
  format: zod.z.enum(["gif", "mp4"]).default("gif"),
6989
7147
  maxWidth: zod.z.number().int().min(120).max(1920).default(480),
6990
7148
  /** GIF only — MP4 keeps the source cadence. */
6991
- fps: zod.z.number().int().min(1).max(15).default(5)
7149
+ fps: zod.z.number().int().min(1).max(15).default(5),
7150
+ /**
7151
+ * Playback rate. A notification clip is GLANCED at on a lock screen,
7152
+ * not watched, so 2x is the default: the recipient sees the whole
7153
+ * passage in half the time and the GIF is half the bytes. `1` is real
7154
+ * time. Applies to MP4 as well — the operator set a speed, not a GIF
7155
+ * speed.
7156
+ */
7157
+ speed: zod.z.number().min(1).max(8).default(2)
6992
7158
  }), zod.z.object({
6993
7159
  base64: zod.z.string(),
6994
7160
  mime: zod.z.string(),
@@ -26905,12 +27071,24 @@ var METHOD_ACCESS_MAP = Object.freeze({
26905
27071
  addonId: null,
26906
27072
  access: "create"
26907
27073
  },
27074
+ "notificationRules.cancelSnooze": {
27075
+ capName: "notification-rules",
27076
+ capScope: "system",
27077
+ addonId: null,
27078
+ access: "create"
27079
+ },
26908
27080
  "notificationRules.createRule": {
26909
27081
  capName: "notification-rules",
26910
27082
  capScope: "system",
26911
27083
  addonId: null,
26912
27084
  access: "create"
26913
27085
  },
27086
+ "notificationRules.createSnooze": {
27087
+ capName: "notification-rules",
27088
+ capScope: "system",
27089
+ addonId: null,
27090
+ access: "create"
27091
+ },
26914
27092
  "notificationRules.deleteRule": {
26915
27093
  capName: "notification-rules",
26916
27094
  capScope: "system",
@@ -26941,6 +27119,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
26941
27119
  addonId: null,
26942
27120
  access: "view"
26943
27121
  },
27122
+ "notificationRules.listSnoozes": {
27123
+ capName: "notification-rules",
27124
+ capScope: "system",
27125
+ addonId: null,
27126
+ access: "view"
27127
+ },
26944
27128
  "notificationRules.setRuleEnabled": {
26945
27129
  capName: "notification-rules",
26946
27130
  capScope: "system",
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-CSs1A90r.js");
3
+ const require_dist = require("./dist-Bqyu3d1X.js");
4
4
  const require_model_download_service = require("./model-download-service-hf0ookyy.js");
5
5
  const require_manifest_python_deps = require("./manifest-python-deps-BqE5j0-O.js");
6
6
  const require_resource_monitor = require("./resource-monitor-DNNomR-i.js");
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM$1, r as __exportAll, t as __commonJSMin$1 } from "./chunk-CNf5ZN-e.mjs";
2
- import { A as extractNestedAddonId, At as scopeKey, Dt as parseJsonUnknown$1, Et as parseJsonObject, F as isObjectInput, G as objectInputDeclaresAddonId, H as looseSchema, I as isVoidInput, J as procedureAuthKey, L as kebabToCamel, Mt as EventCategory$1, N as isArrayOutputSchema, Ot as readinessKey, P as isCollectionArrayMethod, R as lifecycleJobSchema, St as expandCapMethods, V as logLevelAtMost, _t as asNumber, at as errMsg$1, bt as emitDownForOwnedCaps, c as RUNTIME_DEFAULTS, ct as DEVICE_SETTINGS_CONTRIBUTION_METHODS, gt as asJsonObject$1, kt as resolveCapMount, lt as DEVICE_STATUS_METHOD, m as addonSettingsCapability, mt as ReadinessTimeoutError, pt as ReadinessRegistry, s as METHOD_ACCESS_MAP, st as DATAPLANE_SECRET_HEADER$1, t as ALL_CAPABILITY_DEFINITIONS, vt as asString$1, yt as createEvent } from "./dist-BL_FHWPP.mjs";
2
+ import { A as extractNestedAddonId, At as scopeKey, Dt as parseJsonUnknown$1, Et as parseJsonObject, F as isObjectInput, G as objectInputDeclaresAddonId, H as looseSchema, I as isVoidInput, J as procedureAuthKey, L as kebabToCamel, Mt as EventCategory$1, N as isArrayOutputSchema, Ot as readinessKey, P as isCollectionArrayMethod, R as lifecycleJobSchema, St as expandCapMethods, V as logLevelAtMost, _t as asNumber, at as errMsg$1, bt as emitDownForOwnedCaps, c as RUNTIME_DEFAULTS, ct as DEVICE_SETTINGS_CONTRIBUTION_METHODS, gt as asJsonObject$1, kt as resolveCapMount, lt as DEVICE_STATUS_METHOD, m as addonSettingsCapability, mt as ReadinessTimeoutError, pt as ReadinessRegistry, s as METHOD_ACCESS_MAP, st as DATAPLANE_SECRET_HEADER$1, t as ALL_CAPABILITY_DEFINITIONS, vt as asString$1, yt as createEvent } from "./dist-BjUYsgkj.mjs";
3
3
  import { a as downloadModel, c as getModelFilePath, d as contentTypeFor, f as createAuthenticatedFileServer, h as resolveFilePath, i as downloadFile, l as isModelDownloaded, m as parseTokenizedUrl, n as collectModelFiles, o as ensureModel, p as parseRangeHeader, r as deleteModelFromDisk, s as fetchJson, t as ModelDownloadService, u as createFileDataPlaneHandler } from "./model-download-service-Cp9f4dk6.mjs";
4
4
  import { $ as buildNativeCapProxy, A as createHubCapForwardService, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as resolveAddonClass, D as localProviderLink, E as ipcParentLink, F as createUdsLogger, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, N as createUdsEventBus, O as HUB_CAP_FWD_ACTION, P as udsChildLogToWorkerEntry, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as runNpm, T as ipcChildLink, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, k as HUB_CAP_FWD_SERVICE, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-Ck4-9K9m.mjs";
5
5
  import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-BkP504Vq.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.32",
3
+ "version": "1.2.34",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",