@camstack/types 1.2.443 → 1.2.445

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 (58) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/addon.d.ts +5 -1
  3. package/dist/addon.js +21 -5
  4. package/dist/addon.mjs +17 -6
  5. package/dist/automation/export-entities.d.ts +98 -0
  6. package/dist/capabilities/capability-definition.d.ts +1 -1
  7. package/dist/capabilities/composer.cap.d.ts +34 -0
  8. package/dist/capabilities/consumables.cap.d.ts +4 -5
  9. package/dist/capabilities/core-blocks.cap.d.ts +244 -0
  10. package/dist/capabilities/device-export.cap.d.ts +111 -1
  11. package/dist/capabilities/index.d.ts +6 -4
  12. package/dist/capabilities/metrics-provider.cap.d.ts +16 -16
  13. package/dist/capabilities/nodes.cap.d.ts +2 -2
  14. package/dist/capabilities/pet-fountain.cap.d.ts +138 -0
  15. package/dist/capabilities/pipeline-executor.cap.d.ts +6 -22
  16. package/dist/capabilities/pipeline-orchestrator.cap.d.ts +2 -2
  17. package/dist/capabilities/pipeline-runner.cap.d.ts +2 -2
  18. package/dist/capabilities/pipeline-step-input.d.ts +33 -0
  19. package/dist/capabilities/server-management.cap.d.ts +28 -0
  20. package/dist/client.js +820 -799
  21. package/dist/client.mjs +820 -799
  22. package/dist/composition/composition-items.d.ts +3 -3
  23. package/dist/composition/composition-report.d.ts +17 -0
  24. package/dist/composition/composition.d.ts +68 -1
  25. package/dist/composition/index.d.ts +2 -0
  26. package/dist/composition/presets/pet-fountain.d.ts +36 -0
  27. package/dist/composition/validate-forward.d.ts +31 -0
  28. package/dist/{composition-C0dlyG9U.js → composition-CO6Sak-s.js} +49 -5
  29. package/dist/{composition-bIBW5_13.mjs → composition-kSuOifuG.mjs} +32 -6
  30. package/dist/device/cap-vocabulary/entries/appliances.d.ts +1 -1
  31. package/dist/device/device-control-resolution.d.ts +1 -1
  32. package/dist/device/device-type.d.ts +4 -0
  33. package/dist/{err-msg-BCdlR2Li.mjs → err-msg-DWXWc0Di.mjs} +4 -0
  34. package/dist/{err-msg-COHgq7Mb.js → err-msg-EJhsFHqa.js} +4 -0
  35. package/dist/generated/addon-api.d.ts +148 -32
  36. package/dist/generated/addon-cap-facts.d.ts +1 -1
  37. package/dist/generated/cap-status-types.d.ts +3 -1
  38. package/dist/generated/capability-router-map.d.ts +5 -2
  39. package/dist/generated/collection-array-methods.d.ts +2 -2
  40. package/dist/generated/customize-catalog.d.ts +1 -1
  41. package/dist/generated/device-local-state.d.ts +3 -0
  42. package/dist/generated/device-proxy.d.ts +3 -0
  43. package/dist/generated/device-scoped-caps.d.ts +1 -1
  44. package/dist/generated/history-catalog.d.ts +1 -1
  45. package/dist/generated/method-access-map.d.ts +1 -1
  46. package/dist/generated/method-device-selectors.d.ts +1 -1
  47. package/dist/generated/runtime-state-durability.d.ts +1 -1
  48. package/dist/generated/system-proxy.d.ts +2 -2
  49. package/dist/index.d.ts +1 -0
  50. package/dist/index.js +1465 -603
  51. package/dist/index.mjs +1433 -592
  52. package/dist/interfaces/agent.d.ts +6 -2
  53. package/dist/interfaces/event-bus.d.ts +1 -1
  54. package/dist/node.js +2 -2
  55. package/dist/node.mjs +2 -2
  56. package/dist/{sleep-C_u6cEAJ.mjs → sleep-6eG9C8Xl.mjs} +223 -72
  57. package/dist/{sleep-E7qAQwTE.js → sleep-UXDHn-oJ.js} +246 -71
  58. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-E7qAQwTE.js");
2
+ const require_sleep = require("./sleep-UXDHn-oJ.js");
3
3
  const require_event_category = require("./event-category-E17zaHjN.js");
4
- const require_composition = require("./composition-C0dlyG9U.js");
4
+ const require_composition = require("./composition-CO6Sak-s.js");
5
5
  const require_node_color = require("./node-color.js");
6
- const require_err_msg = require("./err-msg-COHgq7Mb.js");
6
+ const require_err_msg = require("./err-msg-EJhsFHqa.js");
7
7
  const require_enums = require("./enums.js");
8
8
  let zod = require("zod");
9
9
  //#region src/media/media-owner-key.ts
@@ -10705,6 +10705,593 @@ function buildActionSteps(steps, vars, deps) {
10705
10705
  });
10706
10706
  }
10707
10707
  //#endregion
10708
+ //#region src/capabilities/notification-output.cap.ts
10709
+ /**
10710
+ * notification-output — canonical, capability-gated notification delivery.
10711
+ *
10712
+ * Apprise-derived model (see
10713
+ * `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
10714
+ * callers emit ONE canonical `Notification`; each provider declares a
10715
+ * per-kind capability descriptor (`TargetKind`), and the pure degrade
10716
+ * engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
10717
+ * message to what the kind supports — callers never special-case a service.
10718
+ *
10719
+ * DESIGN DECISIONS (locked):
10720
+ * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
10721
+ * `setTargetEnabled`), each provider persisting via the `settings-store`
10722
+ * cap. Rationale: the admin UI needs one uniform surface across the
10723
+ * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
10724
+ * alternative would fork the UI per addon and cannot host the
10725
+ * discovery→adopt flow.
10726
+ * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
10727
+ * the generated cap-mount auto-`concatCollection`-fans them across every
10728
+ * registered provider (notifiers addon + HA addon) so one catalog is
10729
+ * routable. `send` / `testTarget` / CRUD route to ONE provider by the
10730
+ * `addonId` the generated collection router extracts from the call input.
10731
+ * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
10732
+ * transformer) + UDS MsgPack both round-trip typed arrays — already used by
10733
+ * `storage` / `storage-provider` / `recording` caps over the same path. No
10734
+ * base64 fallback needed.
10735
+ *
10736
+ * TODO (deferred, closed-set change — separate decision): add
10737
+ * `providerKind: 'notify'` so notification providers surface on the unified
10738
+ * admin "Integrations" page.
10739
+ */
10740
+ /**
10741
+ * Zentik-derived typed-media enum — the superset across every kind. Each
10742
+ * adapter picks what it supports and the degrade engine filters the rest.
10743
+ */
10744
+ var AttachmentMediaTypeSchema = zod.z.enum([
10745
+ "image",
10746
+ "video",
10747
+ "gif",
10748
+ "audio",
10749
+ "icon"
10750
+ ]);
10751
+ /**
10752
+ * A single attachment. Exactly one of `url` (remote source, most adapters
10753
+ * prefer this) or `bytes` (inline source; required for Pushover-style
10754
+ * bytes-only kinds) MUST be present — the degrade engine expresses a
10755
+ * url→bytes fetch as a `needsFetch` directive the adapter executes.
10756
+ */
10757
+ var AttachmentSchema = zod.z.object({
10758
+ mediaType: AttachmentMediaTypeSchema,
10759
+ url: zod.z.string().optional(),
10760
+ bytes: zod.z.instanceof(Uint8Array).optional(),
10761
+ mime: zod.z.string().optional(),
10762
+ name: zod.z.string().optional()
10763
+ }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
10764
+ var NotificationFormatSchema = zod.z.enum([
10765
+ "text",
10766
+ "markdown",
10767
+ "html"
10768
+ ]);
10769
+ /**
10770
+ * The CLOSED icon vocabulary an action button may use.
10771
+ *
10772
+ * A closed set, not a free string, and that is the whole point: an arbitrary
10773
+ * icon name is one that ntfy renders, zentik silently drops, and nobody
10774
+ * notices — the same class of gap as a zone vocabulary nothing produced
10775
+ * ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
10776
+ * declares `actionIcons: false` and the degrade engine strips the field.
10777
+ *
10778
+ * Named by INTENT, never by glyph. "check" would tie the vocabulary to one
10779
+ * renderer's icon set; "acknowledge" survives an adapter that draws it
10780
+ * differently.
10781
+ *
10782
+ * ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
10783
+ *
10784
+ * These names are for US. **No adapter may forward one verbatim.** Each maps
10785
+ * the whole set onto its own renderer's vocabulary through a
10786
+ * `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
10787
+ * fallback, so adding a member here fails every adapter's build until someone
10788
+ * decides its glyph, which is the only place that decision can be made
10789
+ * honestly.
10790
+ *
10791
+ * This paragraph is the bug. Zentik declared `actionIcons: true` and passed
10792
+ * `disarm` straight through; iOS feeds that string to
10793
+ * `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
10794
+ * and every snooze and alarm button arrived BLANK. A pass-through is not a
10795
+ * mapping, and "the field is documented" is not "the value renders".
10796
+ *
10797
+ * Adding a member is TRAIN-BOUND. The enum lives in the published
10798
+ * `@camstack/server` closure and the cap seam validates against the HUB's copy,
10799
+ * so an addon that emits a token the running hub does not know does not lose an
10800
+ * icon — its whole `send` fails Zod validation and the notification never
10801
+ * arrives. Never emit a new token from an addon before the train carrying it.
10802
+ */
10803
+ var NotificationActionIconSchema = zod.z.enum([
10804
+ "acknowledge",
10805
+ "dismiss",
10806
+ "silence",
10807
+ "snooze",
10808
+ "view",
10809
+ "play",
10810
+ "open",
10811
+ "close",
10812
+ "lock",
10813
+ "unlock",
10814
+ "arm",
10815
+ "arm-home",
10816
+ "arm-away",
10817
+ "arm-night",
10818
+ "disarm",
10819
+ "light",
10820
+ "alert",
10821
+ "camera"
10822
+ ]);
10823
+ /** A single tap-through action button. */
10824
+ var NotificationActionSchema = zod.z.object({
10825
+ id: zod.z.string(),
10826
+ label: zod.z.string(),
10827
+ url: zod.z.string().optional(),
10828
+ /** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
10829
+ icon: NotificationActionIconSchema.optional(),
10830
+ /**
10831
+ * Renders in a warning style where the notifier supports it.
10832
+ *
10833
+ * A HINT, never a gate. The callback's authority is its token and nothing
10834
+ * else — see `notification-center/action-token.ts` for what that does and
10835
+ * does not buy.
10836
+ */
10837
+ destructive: zod.z.boolean().optional(),
10838
+ /**
10839
+ * How the tap should REACH the url.
10840
+ *
10841
+ * `navigate` (absent, and every button authored before this field) opens it:
10842
+ * the phone leaves the notification and shows whatever the callback returns.
10843
+ * That is right for a button whose answer the operator wants to read.
10844
+ *
10845
+ * `background` fires it as a POST and stays put. It exists for the buttons
10846
+ * whose whole point is not to interrupt — "silence this for 30 minutes" is
10847
+ * an answer to the notification, and being thrown into a browser tab to
10848
+ * confirm it costs more attention than the notification did. A backend that
10849
+ * cannot do a background call renders it as an ordinary link (the adapters
10850
+ * fall back rather than dropping the button), so this is a preference, never
10851
+ * a requirement.
10852
+ */
10853
+ mode: zod.z.enum(["navigate", "background"]).optional()
10854
+ });
10855
+ /** A delivery parameter value: what a notifier declared it takes. */
10856
+ var DeliveryParamValueSchema = zod.z.union([
10857
+ zod.z.string(),
10858
+ zod.z.number(),
10859
+ zod.z.boolean()
10860
+ ]);
10861
+ /**
10862
+ * One extra delivery parameter a notifier KIND declares (D938). A rule that
10863
+ * addresses a target of that kind may set it; a kind that declares nothing is
10864
+ * never handed one. The admin UI derives its control from this descriptor —
10865
+ * no editor knows a service by name.
10866
+ *
10867
+ * Optional fields are `.optional()`, never `.default()` (a Zod default does not
10868
+ * run on the addon cap path).
10869
+ */
10870
+ var DeliveryParamSchema = zod.z.object({
10871
+ key: zod.z.string().min(1),
10872
+ label: zod.z.string(),
10873
+ /** i18n key of the label, resolved by the declaring addon's namespace. */
10874
+ labelKey: zod.z.string().optional(),
10875
+ type: zod.z.enum([
10876
+ "enum",
10877
+ "number",
10878
+ "boolean",
10879
+ "string"
10880
+ ]),
10881
+ /** `enum` only: the closed set of accepted values. */
10882
+ options: zod.z.array(zod.z.object({
10883
+ value: zod.z.string(),
10884
+ label: zod.z.string()
10885
+ })).optional(),
10886
+ /** `number` only. */
10887
+ min: zod.z.number().optional(),
10888
+ max: zod.z.number().optional(),
10889
+ /** What the notifier does when the rule sets nothing; shown as the placeholder. */
10890
+ default: DeliveryParamValueSchema.optional()
10891
+ });
10892
+ /**
10893
+ * The canonical notification. `body` is the only hard field (Apprise model).
10894
+ * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
10895
+ * NOT a fixed severity enum — each kind declares its own `caps.levels` and
10896
+ * the adapter maps this ordinal onto its native level. `level?` is an
10897
+ * optional kind-native level id (`emergency`, `silent`, …) that overrides
10898
+ * `priority` for that one target.
10899
+ */
10900
+ var NotificationSchema = zod.z.object({
10901
+ body: zod.z.string(),
10902
+ title: zod.z.string().optional(),
10903
+ format: NotificationFormatSchema.default("text"),
10904
+ priority: zod.z.number().int().min(1).max(5).default(3),
10905
+ level: zod.z.string().optional(),
10906
+ attachments: zod.z.array(AttachmentSchema).optional(),
10907
+ clickUrl: zod.z.string().optional(),
10908
+ actions: zod.z.array(NotificationActionSchema).optional(),
10909
+ sound: zod.z.string().optional(),
10910
+ ttl: zod.z.number().optional(),
10911
+ tag: zod.z.string().optional(),
10912
+ deviceId: zod.z.number().optional(),
10913
+ eventId: zod.z.string().optional(),
10914
+ metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
10915
+ /**
10916
+ * Values for the parameters the target's kind DECLARES in
10917
+ * {@link TargetKindSchema} `deliveryParams`. Already validated against the
10918
+ * declaration by the sender; a notifier reads only the keys it declared.
10919
+ */
10920
+ delivery: zod.z.record(zod.z.string(), DeliveryParamValueSchema).optional()
10921
+ });
10922
+ /** One declared native severity/priority level for a kind. */
10923
+ var TargetKindLevelSchema = zod.z.object({
10924
+ id: zod.z.string(),
10925
+ label: zod.z.string(),
10926
+ /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
10927
+ ordinal: zod.z.number().int().min(1).max(5).nullable(),
10928
+ flags: zod.z.object({
10929
+ critical: zod.z.boolean().optional(),
10930
+ silent: zod.z.boolean().optional(),
10931
+ noPush: zod.z.boolean().optional()
10932
+ }).optional(),
10933
+ /** e.g. Pushover `emergency` requires `retry` / `expire`. */
10934
+ requires: zod.z.array(zod.z.string()).optional(),
10935
+ description: zod.z.string().optional()
10936
+ });
10937
+ /** Attachment capabilities for a kind (drives the degrade engine + test panel). */
10938
+ var TargetKindAttachmentsCapsSchema = zod.z.object({
10939
+ mediaTypes: zod.z.array(AttachmentMediaTypeSchema),
10940
+ mode: zod.z.enum([
10941
+ "url",
10942
+ "bytes",
10943
+ "both"
10944
+ ]),
10945
+ max: zod.z.number().int().nonnegative(),
10946
+ maxBytes: zod.z.number().int().positive().optional()
10947
+ });
10948
+ /** The full capability block consulted before dispatch. */
10949
+ var TargetKindCapsSchema = zod.z.object({
10950
+ attachments: TargetKindAttachmentsCapsSchema,
10951
+ /** Max action buttons (0 = none). */
10952
+ actions: zod.z.number().int().nonnegative(),
10953
+ /**
10954
+ * Whether this kind renders a per-action ICON.
10955
+ *
10956
+ * `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
10957
+ * run on the addon cap path — three production failures in one day taught
10958
+ * this repo that once. Absent is read as false by the degrade engine, which
10959
+ * is the safe direction: an icon that is not rendered costs nothing, an icon
10960
+ * assumed and dropped costs the operator's trust in the field.
10961
+ */
10962
+ actionIcons: zod.z.boolean().optional(),
10963
+ levels: zod.z.array(TargetKindLevelSchema),
10964
+ format: zod.z.array(NotificationFormatSchema),
10965
+ clickUrl: zod.z.boolean(),
10966
+ sound: zod.z.boolean(),
10967
+ ttl: zod.z.boolean(),
10968
+ bodyMaxLen: zod.z.number().int().positive()
10969
+ });
10970
+ /**
10971
+ * `configSchema` is a `ConfigUISchema` tree passed through to the admin
10972
+ * FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
10973
+ * `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
10974
+ * the union is large and not meant for runtime validation here; the exported
10975
+ * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
10976
+ */
10977
+ var ConfigSchemaPassthrough$3 = zod.z.unknown();
10978
+ var TargetKindSchema = zod.z.object({
10979
+ kind: zod.z.string(),
10980
+ label: zod.z.string(),
10981
+ icon: zod.z.string(),
10982
+ /** Stamped by each provider so the concat-fanned catalog stays routable. */
10983
+ addonId: zod.z.string(),
10984
+ /**
10985
+ * URL of the kind's bundled BRAND icon, served by the providing addon over
10986
+ * its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
10987
+ * when the addon bundles no icon for that kind — the client then falls back
10988
+ * to a neutral glyph rather than rendering the raw `icon` NAME as text.
10989
+ *
10990
+ * Root-relative on purpose: it resolves against whatever origin serves a web
10991
+ * client, and a native client joins it onto its own hub base.
10992
+ *
10993
+ * DECLARED here deliberately. It used to travel as an undeclared passthrough
10994
+ * field that survived only because the runtime cap-router forwards provider
10995
+ * output verbatim — so every consumer had to re-declare it by hand to stop
10996
+ * its own Zod parse from stripping it, and the whole arrangement would have
10997
+ * broken silently the moment output validation was tightened anywhere.
10998
+ */
10999
+ iconUrl: zod.z.string().optional(),
11000
+ /**
11001
+ * Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
11002
+ *
11003
+ * The server knows this and therefore says it, because the client cannot
11004
+ * safely guess: a React-Native client renders SVG and raster through two
11005
+ * DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
11006
+ * not decode SVG on iOS/Android), so without this it silently fell back to a
11007
+ * placeholder glyph for every vector icon while the web build looked fine.
11008
+ *
11009
+ * Absent when {@link iconUrl} is absent, or for a legacy provider that has
11010
+ * not been updated — a client that cannot determine the type should prefer
11011
+ * its raster path, which is the safe default for an unknown image.
11012
+ */
11013
+ iconMediaType: zod.z.string().optional(),
11014
+ configSchema: ConfigSchemaPassthrough$3,
11015
+ /**
11016
+ * The config keys that decide WHERE this kind's secrets are sent (server,
11017
+ * base URL, instance, chat, recipient). A save that changes any of them
11018
+ * while leaving a secret redacted is refused by the provider (D634), so an
11019
+ * editor uses this to ask for the secret on exactly those edits instead of
11020
+ * on every edit.
11021
+ *
11022
+ * Optional: absent from a provider that holds no secret (the Home Assistant
11023
+ * kinds) and from a provider older than this field — an editor that finds
11024
+ * it absent must assume ANY edit may need the secret.
11025
+ */
11026
+ destinationKeys: zod.z.array(zod.z.string()).optional(),
11027
+ supportsDiscovery: zod.z.boolean(),
11028
+ /**
11029
+ * Extra delivery parameters a rule may set for a target of this kind (D938).
11030
+ * Absent = none; the rule editor then offers nothing.
11031
+ */
11032
+ deliveryParams: zod.z.array(DeliveryParamSchema).optional(),
11033
+ caps: TargetKindCapsSchema
11034
+ });
11035
+ /**
11036
+ * A persisted target. `config` holds secrets; providers REDACT secret fields
11037
+ * (return a presence marker only) when serving `listTargets` — never
11038
+ * round-trip a stored secret to the UI.
11039
+ */
11040
+ var TargetSchema = zod.z.object({
11041
+ id: zod.z.string(),
11042
+ name: zod.z.string(),
11043
+ kind: zod.z.string(),
11044
+ addonId: zod.z.string(),
11045
+ enabled: zod.z.boolean(),
11046
+ config: zod.z.record(zod.z.string(), zod.z.unknown())
11047
+ });
11048
+ /** A discovery-surfaced candidate (config is partial + non-secret). */
11049
+ var DiscoveredTargetSchema = zod.z.object({
11050
+ kind: zod.z.string(),
11051
+ suggestedName: zod.z.string(),
11052
+ config: zod.z.record(zod.z.string(), zod.z.unknown())
11053
+ });
11054
+ /** The degrade engine's report — what was resolved / dropped / degraded. */
11055
+ var RenderedAsSchema = zod.z.object({
11056
+ level: zod.z.string(),
11057
+ format: NotificationFormatSchema,
11058
+ attachmentsSent: zod.z.number().int().nonnegative(),
11059
+ actionsSent: zod.z.number().int().nonnegative(),
11060
+ truncated: zod.z.boolean(),
11061
+ dropped: zod.z.array(zod.z.string())
11062
+ });
11063
+ var SendResultSchema = zod.z.object({
11064
+ success: zod.z.boolean(),
11065
+ error: zod.z.string().optional(),
11066
+ renderedAs: RenderedAsSchema.optional()
11067
+ });
11068
+ /** Same shape as SendResult — kept as a distinct name for the test panel. */
11069
+ var TestResultSchema = SendResultSchema;
11070
+ /**
11071
+ * Pre-save credential probe against a partial (unsaved) target config.
11072
+ * `resolvedUrl` never includes secrets (token stripped from path).
11073
+ */
11074
+ var ProbeTargetConfigResultSchema = zod.z.object({
11075
+ ok: zod.z.boolean(),
11076
+ resolvedUrl: zod.z.string(),
11077
+ httpStatus: zod.z.number().int().optional(),
11078
+ stateInstance: zod.z.string().optional(),
11079
+ error: zod.z.string().optional()
11080
+ });
11081
+ var notificationOutputCapability = {
11082
+ name: "notification-output",
11083
+ scope: "system",
11084
+ mode: "collection",
11085
+ methods: {
11086
+ listTargetKinds: require_sleep.method(zod.z.object({}), zod.z.array(TargetKindSchema)),
11087
+ listTargets: require_sleep.method(zod.z.object({}), zod.z.array(TargetSchema), { auth: "admin" }),
11088
+ /**
11089
+ * One target WITH secrets — admin edit / duplicate hydration only.
11090
+ * `listTargets` stays redacted; the editor loads this so password fields
11091
+ * are filled (masked in the input) and a save can round-trip the token
11092
+ * when a destination changes (Duplicate with a new chatId, etc.).
11093
+ */
11094
+ getTarget: require_sleep.method(zod.z.object({ targetId: zod.z.string() }), TargetSchema, { auth: "admin" }),
11095
+ discoverTargets: require_sleep.method(zod.z.object({
11096
+ kind: zod.z.string(),
11097
+ config: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
11098
+ /**
11099
+ * When editing / duplicating, the blank password field never round-trips
11100
+ * the stored token. Pass the source target id so the provider can restore
11101
+ * secrets server-side before dialling (same merge as upsert).
11102
+ */
11103
+ targetId: zod.z.string().optional()
11104
+ }), zod.z.array(DiscoveredTargetSchema), { auth: "admin" }),
11105
+ probeTargetConfig: require_sleep.method(zod.z.object({
11106
+ kind: zod.z.string(),
11107
+ config: zod.z.record(zod.z.string(), zod.z.unknown()),
11108
+ /** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
11109
+ targetId: zod.z.string().optional()
11110
+ }), ProbeTargetConfigResultSchema, {
11111
+ kind: "mutation",
11112
+ auth: "admin"
11113
+ }),
11114
+ send: require_sleep.method(zod.z.object({
11115
+ targetId: zod.z.string(),
11116
+ notification: NotificationSchema
11117
+ }), SendResultSchema, {
11118
+ kind: "mutation",
11119
+ auth: "admin"
11120
+ }),
11121
+ testTarget: require_sleep.method(zod.z.object({
11122
+ targetId: zod.z.string(),
11123
+ sample: NotificationSchema.optional()
11124
+ }), TestResultSchema, {
11125
+ kind: "mutation",
11126
+ auth: "admin"
11127
+ }),
11128
+ upsertTarget: require_sleep.method(zod.z.object({
11129
+ target: TargetSchema,
11130
+ /**
11131
+ * Duplicate flow: create a NEW target id while copying secrets from an
11132
+ * existing row. The UI never holds the stored token; blank / redacted
11133
+ * password fields are restored from this source (D634 still applies if
11134
+ * destinations diverge without a re-typed secret).
11135
+ */
11136
+ cloneSecretsFrom: zod.z.string().optional()
11137
+ }), TargetSchema, {
11138
+ kind: "mutation",
11139
+ auth: "admin"
11140
+ }),
11141
+ deleteTarget: require_sleep.method(zod.z.object({ targetId: zod.z.string() }), zod.z.void(), {
11142
+ kind: "mutation",
11143
+ auth: "admin"
11144
+ }),
11145
+ setTargetEnabled: require_sleep.method(zod.z.object({
11146
+ targetId: zod.z.string(),
11147
+ enabled: zod.z.boolean()
11148
+ }), zod.z.void(), {
11149
+ kind: "mutation",
11150
+ auth: "admin"
11151
+ })
11152
+ }
11153
+ };
11154
+ //#endregion
11155
+ //#region src/automation/export-entities.ts
11156
+ function errorText(err) {
11157
+ return err instanceof Error ? err.message : String(err);
11158
+ }
11159
+ /** `actions.run` flattens a refusal to `[code] text`; the code leads. */
11160
+ function refusalFromThrown(message) {
11161
+ if (message.startsWith("[busy]")) return "already-running";
11162
+ if (message.startsWith("[not-found]")) return "not-found";
11163
+ return null;
11164
+ }
11165
+ async function runExportedAction(api, log, input) {
11166
+ const { exporter, actionId } = input;
11167
+ const tags = { actionId };
11168
+ const refuse = (reason, detail) => {
11169
+ log.warn(`${exporter}: exported action not run — ${reason}`, {
11170
+ tags,
11171
+ meta: {
11172
+ reason,
11173
+ ...detail ? { detail } : {}
11174
+ }
11175
+ });
11176
+ return {
11177
+ status: "refused",
11178
+ reason
11179
+ };
11180
+ };
11181
+ try {
11182
+ const result = await api.actions.run.mutate({ actionId });
11183
+ if (result.outcome === "skipped-overlap") return refuse("already-running", result.message);
11184
+ if (result.outcome === "skipped-cooldown") return refuse("cooldown", result.message);
11185
+ if (result.outcome === "failed" || result.outcome === "aborted") {
11186
+ const message = result.message ?? result.outcome;
11187
+ log.warn(`${exporter}: exported action failed`, {
11188
+ tags,
11189
+ meta: {
11190
+ outcome: result.outcome,
11191
+ message
11192
+ }
11193
+ });
11194
+ return {
11195
+ status: "failed",
11196
+ message
11197
+ };
11198
+ }
11199
+ log.info(`${exporter}: exported action ran`, {
11200
+ tags,
11201
+ meta: { outcome: result.outcome }
11202
+ });
11203
+ return { status: "started" };
11204
+ } catch (err) {
11205
+ const message = errorText(err);
11206
+ const reason = refusalFromThrown(message);
11207
+ if (reason !== null) return refuse(reason, message);
11208
+ log.warn(`${exporter}: exported action failed`, {
11209
+ tags,
11210
+ meta: { message }
11211
+ });
11212
+ return {
11213
+ status: "failed",
11214
+ message
11215
+ };
11216
+ }
11217
+ }
11218
+ /** Send through one CamStack notification target on behalf of an exporter. */
11219
+ async function sendToExportedTarget(api, log, input) {
11220
+ const { exporter, targetId } = input;
11221
+ const tags = { targetId };
11222
+ try {
11223
+ const target = (await api.notificationOutput.listTargets.query({})).find((t) => t.id === targetId);
11224
+ if (target === void 0) {
11225
+ log.warn(`${exporter}: exported notifier not used — target-gone`, {
11226
+ tags,
11227
+ meta: { reason: "target-gone" }
11228
+ });
11229
+ return {
11230
+ status: "refused",
11231
+ reason: "target-gone"
11232
+ };
11233
+ }
11234
+ if (!target.enabled) {
11235
+ log.warn(`${exporter}: exported notifier not used — target-disabled`, {
11236
+ tags,
11237
+ meta: { reason: "target-disabled" }
11238
+ });
11239
+ return {
11240
+ status: "refused",
11241
+ reason: "target-disabled"
11242
+ };
11243
+ }
11244
+ const result = await api.notificationOutput.send.mutate({
11245
+ targetId,
11246
+ addonId: target.addonId,
11247
+ notification: NotificationSchema.parse(input.notification)
11248
+ });
11249
+ if (!result.success) {
11250
+ const message = result.error ?? "send failed";
11251
+ log.warn(`${exporter}: exported notifier send failed`, {
11252
+ tags,
11253
+ meta: { message }
11254
+ });
11255
+ return {
11256
+ status: "failed",
11257
+ message
11258
+ };
11259
+ }
11260
+ return { status: "sent" };
11261
+ } catch (err) {
11262
+ const message = errorText(err);
11263
+ log.warn(`${exporter}: exported notifier send failed`, {
11264
+ tags,
11265
+ meta: { message }
11266
+ });
11267
+ return {
11268
+ status: "failed",
11269
+ message
11270
+ };
11271
+ }
11272
+ }
11273
+ /**
11274
+ * Split an exporter's selection into what still exists and what was deleted.
11275
+ * Devices are never judged here (their lifecycle is the tombstone, D865).
11276
+ * Callers pass ids from a query that ANSWERED — an unanswered list must not
11277
+ * reach this function (D49: a failed read changes nothing).
11278
+ */
11279
+ function pruneDeletedEntities(selection, live, log, exporter) {
11280
+ const kept = [];
11281
+ const dropped = [];
11282
+ for (const ref of selection) if (ref.kind === "action" && !live.actionIds.has(ref.actionId)) {
11283
+ dropped.push(ref);
11284
+ log.warn(`${exporter}: exported action was deleted — dropped from the export`, { tags: { actionId: ref.actionId } });
11285
+ } else if (ref.kind === "notifier" && !live.targetIds.has(ref.targetId)) {
11286
+ dropped.push(ref);
11287
+ log.warn(`${exporter}: exported notifier target was deleted — dropped from the export`, { tags: { targetId: ref.targetId } });
11288
+ } else kept.push(ref);
11289
+ return {
11290
+ kept,
11291
+ dropped
11292
+ };
11293
+ }
11294
+ //#endregion
10708
11295
  //#region src/capabilities/addon-pages.cap.ts
10709
11296
  /**
10710
11297
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
@@ -10744,71 +11331,6 @@ var addonPagesCapability = {
10744
11331
  methods: { listPages: require_sleep.method(zod.z.void(), zod.z.array(AddonPageInfoSchema).readonly()) }
10745
11332
  };
10746
11333
  //#endregion
10747
- //#region src/capabilities/addon-pages-source.cap.ts
10748
- /**
10749
- * `addon-pages-source` — collection cap exposing per-provider raw page
10750
- * declarations. Every addon that contributes a UI page registers a
10751
- * provider here. The hub-side singleton aggregator (`addon-pages` cap,
10752
- * see `addon-pages.cap.ts`) walks this collection, stamps versioned
10753
- * `bundleUrl` values, and returns the enriched `AddonPageInfo[]` list
10754
- * that admin-ui consumes.
10755
- *
10756
- * The split exists because the public listing has a different output
10757
- * shape than the per-provider raw declarations, and we want both ends
10758
- * to flow through codegen instead of relying on a hand-written wrapper.
10759
- */
10760
- var AddonPageDeclarationSchema = zod.z.object({
10761
- id: zod.z.string(),
10762
- label: zod.z.string(),
10763
- icon: zod.z.string(),
10764
- path: zod.z.string(),
10765
- /**
10766
- * Module Federation remote name — must match the `name` field on the
10767
- * page addon's `federation()` plugin config. Used by admin-ui's
10768
- * `<AddonPageLoader>` to call `loadRemote('<remoteName>/page')`.
10769
- * Conventionally `addon_<id>_page` (snake_case; MF names cannot
10770
- * contain hyphens).
10771
- */
10772
- remoteName: zod.z.string(),
10773
- /**
10774
- * Bundle filename inside the addon's `dist/` dir served at
10775
- * `/api/addon-pages/<addonId>/<bundle>`. With Module Federation this
10776
- * is always `'remoteEntry.js'`; the value is kept on the metadata so
10777
- * the static-file route can compute an mtime-based cache-buster URL
10778
- * without a separate filesystem stat.
10779
- */
10780
- bundle: zod.z.string(),
10781
- /**
10782
- * Sidebar section this page docks into. Well-known ids: `'detection'`,
10783
- * `'cluster'`, `'administration'` — the page renders inside that group.
10784
- * Any OTHER string creates (or joins) a custom section rendered after
10785
- * the built-in groups; its label comes from `sectionLabel` (first
10786
- * declaration wins), falling back to the id. Absent → the legacy
10787
- * "Addon Pages" group.
10788
- */
10789
- section: zod.z.string().optional(),
10790
- /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
10791
- sectionLabel: zod.z.string().optional()
10792
- });
10793
- var addonPagesSourceCapability = {
10794
- name: "addon-pages-source",
10795
- scope: "system",
10796
- mode: "collection",
10797
- internal: true,
10798
- methods: {
10799
- /**
10800
- * `internal: true` did not gate the mount (see the 2026-08-26 note on
10801
- * `data-store-provider`) — this was reachable on the AppRouter by ANY
10802
- * authenticated session at the default `auth: 'protected'`. The only
10803
- * consumer, the `addon-pages-aggregator` builtin, reads it via
10804
- * `ctx.capabilities.getCollection('addon-pages-source')` — LOCAL-PROCESS
10805
- * only, never tRPC (per `ctx.capabilities` seeing only the local
10806
- * process). The public, enriched listing admin-ui actually consumes is
10807
- * the separate `addon-pages` cap, unaffected by this change.
10808
- */
10809
- listPages: require_sleep.method(zod.z.void(), zod.z.array(AddonPageDeclarationSchema).readonly(), { auth: "admin" }) }
10810
- };
10811
- //#endregion
10812
11334
  //#region src/capabilities/addon-routes.cap.ts
10813
11335
  var AddonHttpRouteSchema = zod.z.object({
10814
11336
  method: zod.z.enum([
@@ -14915,7 +15437,11 @@ var CompositionProblemCodeSchema = zod.z.enum([
14915
15437
  "mixed-quantity",
14916
15438
  "helper-mismatch",
14917
15439
  "member-constraint",
14918
- "retention"
15440
+ "retention",
15441
+ "command-method-unknown",
15442
+ "command-source-unknown",
15443
+ "command-input-unknown",
15444
+ "command-input-missing"
14919
15445
  ]);
14920
15446
  /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
14921
15447
  var CompositionProblemSchema = zod.z.object({
@@ -14957,7 +15483,9 @@ var CompositionFieldRoleSchema = zod.z.enum([
14957
15483
  var CompositionFieldItemSchema = zod.z.object({
14958
15484
  arrayPath: zod.z.string(),
14959
15485
  key: zod.z.string(),
14960
- label: zod.z.string()
15486
+ label: zod.z.string(),
15487
+ /** True when the item carries a `reset` forward: the composer writes `resettable` from this (D967). */
15488
+ resettable: zod.z.boolean().optional()
14961
15489
  });
14962
15490
  var CompositionFieldPlanSchema = zod.z.object({
14963
15491
  cap: zod.z.string(),
@@ -15478,7 +16006,7 @@ function unavailableFor(tf, role, mode, cap, at) {
15478
16006
  }
15479
16007
  //#endregion
15480
16008
  //#region src/composition/composition-items.ts
15481
- /** Item fields the composer writes itself, with the value it writes. */
16009
+ /** Item fields the composer writes itself, with the value it writes. `resettable` is the default: an item with a `reset` forward writes `true` (D967). */
15482
16010
  var COMPOSED_ITEM_FIXED_FIELDS = { resettable: false };
15483
16011
  function reservedItemFields(itemArray) {
15484
16012
  return new Set([
@@ -15521,7 +16049,8 @@ function planItemField(ctx, leaf) {
15521
16049
  item: {
15522
16050
  arrayPath: itemArray.path,
15523
16051
  key,
15524
- label
16052
+ label,
16053
+ ...leaf.resettable ? { resettable: true } : {}
15525
16054
  },
15526
16055
  featureIndex,
15527
16056
  kind: tf.kind,
@@ -15561,6 +16090,7 @@ function planItems(def, feature, featureIndex, lookupCap, mode = "new") {
15561
16090
  }, ...Object.entries(item.fields).map(([path, source]) => planItemField(ctx, {
15562
16091
  key,
15563
16092
  label: item.label,
16093
+ resettable: item.reset !== void 0,
15564
16094
  path,
15565
16095
  source
15566
16096
  }))]);
@@ -15572,12 +16102,163 @@ function planItems(def, feature, featureIndex, lookupCap, mode = "new") {
15572
16102
  };
15573
16103
  }
15574
16104
  //#endregion
16105
+ //#region src/composition/validate-forward.ts
16106
+ /**
16107
+ * Save-time validation of `forward` commands (D659, amended by D967).
16108
+ *
16109
+ * A BUILT capability serves a method only through a command, so every method
16110
+ * the cap serves needs one. A forward is checked against the two input schemas
16111
+ * it joins: the BUILT method's (what the caller sends) and the SOURCE method's
16112
+ * (what is called). An input field in neither place is refused BY NAME, and so
16113
+ * is a required source input nothing provides. `deviceId` is the source's own
16114
+ * and is never an argument.
16115
+ *
16116
+ * Static: it needs only the capability definitions. That the source DEVICE
16117
+ * exists and binds the cap is `forwardSourceProblems`, with the sources read.
16118
+ */
16119
+ var DEVICE_ID_FIELD = "deviceId";
16120
+ /** Every forward of a feature: the method commands, then each item's `reset`. */
16121
+ function featureForwards(feature, featureIndex) {
16122
+ const at = `features[${featureIndex}]`;
16123
+ const commands = Object.entries(feature.commands ?? {}).flatMap(([method, target]) => target.kind === "forward" ? [{
16124
+ at: `${at}.commands.${method}`,
16125
+ method,
16126
+ command: target
16127
+ }] : []);
16128
+ const resets = Object.entries(feature.items ?? {}).flatMap(([key, item]) => item.reset === void 0 ? [] : [{
16129
+ at: `${at}.items.${key}.reset`,
16130
+ method: "reset",
16131
+ itemKey: key,
16132
+ command: item.reset
16133
+ }]);
16134
+ return [...commands, ...resets];
16135
+ }
16136
+ /** Every source a forward of an enabled feature calls, once. */
16137
+ function forwardSourceRefs(composition) {
16138
+ const refs = composition.features.flatMap((feature, index) => feature.kind === "fields" && require_composition.compositionFeatureEnabled(feature) ? featureForwards(feature, index).map((forward) => forward.command.source) : []);
16139
+ return [...new Map(refs.map((ref) => [require_composition.compositionSourceKey(ref), ref])).values()];
16140
+ }
16141
+ /** The methods a BUILT cap must give a command: served, and not answered by the composer itself. */
16142
+ function commandedMethods(def) {
16143
+ return Object.entries(def.methods).filter(([name, m]) => m.systemOnly !== true && def.composedMethods?.[name] === void 0).map(([name]) => name);
16144
+ }
16145
+ var NO_INPUT = {
16146
+ all: /* @__PURE__ */ new Set(),
16147
+ required: /* @__PURE__ */ new Set()
16148
+ };
16149
+ /** The top-level input fields of a method, `deviceId` removed; null when the input is not an object. */
16150
+ function inputFields(def, method) {
16151
+ const input = def.methods[method]?.input;
16152
+ if (input === void 0) return null;
16153
+ const root = unwrapSchema(input);
16154
+ if (!(root instanceof zod.z.ZodObject)) return null;
16155
+ const entries = Object.entries(root.shape).filter(([name]) => name !== DEVICE_ID_FIELD);
16156
+ return {
16157
+ all: new Set(entries.map(([name]) => name)),
16158
+ required: new Set(entries.flatMap(([name, field]) => field instanceof zod.z.ZodType && !field.safeParse(void 0).success ? [name] : []))
16159
+ };
16160
+ }
16161
+ function inputRefs(arg) {
16162
+ if (typeof arg !== "object") return [];
16163
+ if (require_composition.isForwardInputRef(arg)) return [arg.$input];
16164
+ return Object.values(arg).flatMap(inputRefs);
16165
+ }
16166
+ function names(list) {
16167
+ return [...list].map((name) => `\`${name}\``).join(", ");
16168
+ }
16169
+ function sourceMethodOf(forward, lookupCap) {
16170
+ const { command, at } = forward;
16171
+ const def = lookupCap(command.cap);
16172
+ if (def === null) return {
16173
+ def: null,
16174
+ problems: [problem("command-source-unknown", `${at}.cap`, `no capability is named \`${command.cap}\``)]
16175
+ };
16176
+ const served = def.methods[command.method];
16177
+ if (served === void 0 || served.systemOnly === true) return {
16178
+ def: null,
16179
+ problems: [problem("command-source-unknown", `${at}.method`, `\`${command.cap}\` has no method \`${command.method}\` to forward to`)]
16180
+ };
16181
+ if (served.kind !== "mutation") return {
16182
+ def: null,
16183
+ problems: [problem("command-source-unknown", `${at}.method`, `\`${command.cap}.${command.method}\` is a query — a command calls a mutation of the source`)]
16184
+ };
16185
+ return {
16186
+ def,
16187
+ problems: []
16188
+ };
16189
+ }
16190
+ function mappedInputProblems(forward, built, source, args) {
16191
+ const { at, command } = forward;
16192
+ const where = `${command.cap}.${command.method}`;
16193
+ const problems = [];
16194
+ if ("deviceId" in args) problems.push(problem("command-input-unknown", `${at}.args.${DEVICE_ID_FIELD}`, `\`${DEVICE_ID_FIELD}\` is the source device's own — a forward never takes it as an argument`));
16195
+ if (source !== null) {
16196
+ for (const key of Object.keys(args).filter((k) => k !== "deviceId" && !source.all.has(k))) problems.push(problem("command-input-unknown", `${at}.args.${key}`, `\`${where}\` has no input field \`${key}\` (it takes ${names(source.all)})`));
16197
+ const missing = [...source.required].filter((k) => !(k in args));
16198
+ if (missing.length > 0) problems.push(problem("command-input-missing", `${at}.args`, `\`${where}\` needs ${names(missing)}, which \`args\` does not provide`));
16199
+ }
16200
+ const readable = forward.itemKey === void 0 ? built : NO_INPUT;
16201
+ if (readable !== null) for (const ref of Object.values(args).flatMap(inputRefs)) {
16202
+ if (readable.all.has(ref)) continue;
16203
+ problems.push(problem("command-input-unknown", `${at}.args`, readable === NO_INPUT ? `an item's \`reset\` takes no input — \`$input: ${ref}\` has nothing to read` : `\`$input: ${ref}\` is not a field of the \`${forward.method}\` input (it takes ${names(readable.all)})`));
16204
+ }
16205
+ return problems;
16206
+ }
16207
+ function passthroughInputProblems(forward, built, source) {
16208
+ const { at, command } = forward;
16209
+ const where = `${command.cap}.${command.method}`;
16210
+ const sent = forward.itemKey === void 0 ? built : NO_INPUT;
16211
+ if (sent === null || source === null) return [];
16212
+ const problems = [];
16213
+ for (const field of [...sent.all].filter((f) => !source.all.has(f))) problems.push(problem("command-input-unknown", `${at}.args`, `\`${forward.method}\` takes \`${field}\`, which \`${where}\` does not (it takes ${names(source.all)}) — add \`args\` to map it`));
16214
+ const missing = [...source.required].filter((k) => !sent.all.has(k));
16215
+ if (missing.length > 0) problems.push(problem("command-input-missing", `${at}.args`, `\`${where}\` needs ${names(missing)}, which \`${forward.method}\` does not take — add \`args\` to provide it`));
16216
+ return problems;
16217
+ }
16218
+ /** One forward against the built cap's method and the source cap's method. */
16219
+ function forwardProblems(built, forward, lookupCap) {
16220
+ const source = sourceMethodOf(forward, lookupCap);
16221
+ if (source.def === null) return source.problems;
16222
+ const builtFields = inputFields(built, forward.method);
16223
+ const sourceFields = inputFields(source.def, forward.command.method);
16224
+ const mapping = forward.command.args;
16225
+ return mapping === void 0 ? passthroughInputProblems(forward, builtFields, sourceFields) : mappedInputProblems(forward, builtFields, sourceFields, mapping);
16226
+ }
16227
+ /** A command must name a method the built cap serves and the composer does not answer itself. */
16228
+ function commandMethodProblems(built, feature, at) {
16229
+ const commanded = new Set(commandedMethods(built));
16230
+ return Object.keys(feature.commands ?? {}).flatMap((method) => {
16231
+ if (commanded.has(method)) return [];
16232
+ const answered = built.composedMethods?.[method] !== void 0;
16233
+ return [problem("command-method-unknown", `${at}.commands.${method}`, answered ? `\`${built.name}.${method}\` is answered by the composer itself — an item's \`reset\` serves it` : `\`${built.name}\` has no method \`${method}\` to serve`)];
16234
+ });
16235
+ }
16236
+ /**
16237
+ * The source DEVICES of the forwards, read: each must exist and bind the cap it
16238
+ * is called on. A forward onto an unread device is refused rather than stored
16239
+ * unchecked, like every other source (D391).
16240
+ */
16241
+ function forwardSourceProblems(composition, sources) {
16242
+ return composition.features.flatMap((feature, index) => {
16243
+ if (feature.kind !== "fields" || !require_composition.compositionFeatureEnabled(feature)) return [];
16244
+ return featureForwards(feature, index).flatMap((forward) => {
16245
+ const key = require_composition.compositionSourceKey(forward.command.source);
16246
+ const desc = sources.get(key);
16247
+ const at = `${forward.at}.source`;
16248
+ if (desc === void 0 || desc.kind === "unreadable") return [problem("source-unreadable", at, `could not read source \`${key}\` (${desc === void 0 ? "it was not read" : desc.error}) — the save is refused rather than stored unchecked`)];
16249
+ if (desc.kind === "absent") return [problem("source-missing", at, `source \`${key}\` does not exist`)];
16250
+ if (!desc.caps.has(forward.command.cap)) return [problem("source-capability-missing", at, `source \`${key}\` (device ${desc.deviceId}) has no \`${forward.command.cap}\``)];
16251
+ return [];
16252
+ });
16253
+ });
16254
+ }
16255
+ //#endregion
15575
16256
  //#region src/composition/helpers/catalog.ts
15576
16257
  /**
15577
16258
  * One catalog row per base reading a group helper can build (D944 §3).
15578
16259
  * Data, not a form default written into the block.
15579
16260
  */
15580
- var NOW$2 = {
16261
+ var NOW$3 = {
15581
16262
  fieldPath: "lastFetchedAt",
15582
16263
  expr: "now"
15583
16264
  };
@@ -15629,7 +16310,7 @@ var GROUP_TEMPLATES = [
15629
16310
  quantity: "temperature",
15630
16311
  defaultAggregate: "mean",
15631
16312
  empty: "null",
15632
- companions: [NOW$2],
16313
+ companions: [NOW$3],
15633
16314
  alsoFrom: {
15634
16315
  deviceTypes: CLIMATE_DEVICES,
15635
16316
  accept: {
@@ -15647,7 +16328,7 @@ var GROUP_TEMPLATES = [
15647
16328
  quantity: "humidity",
15648
16329
  defaultAggregate: "mean",
15649
16330
  empty: "null",
15650
- companions: [NOW$2],
16331
+ companions: [NOW$3],
15651
16332
  alsoFrom: {
15652
16333
  deviceTypes: CLIMATE_DEVICES,
15653
16334
  accept: {
@@ -15665,7 +16346,7 @@ var GROUP_TEMPLATES = [
15665
16346
  quantity: "pressure",
15666
16347
  defaultAggregate: "mean",
15667
16348
  empty: "null",
15668
- companions: [NOW$2]
16349
+ companions: [NOW$3]
15669
16350
  }),
15670
16351
  numeric({
15671
16352
  id: "illuminance",
@@ -15676,7 +16357,7 @@ var GROUP_TEMPLATES = [
15676
16357
  quantity: "illuminance",
15677
16358
  defaultAggregate: "mean",
15678
16359
  empty: "null",
15679
- companions: [NOW$2]
16360
+ companions: [NOW$3]
15680
16361
  }),
15681
16362
  numeric({
15682
16363
  id: "power",
@@ -15687,7 +16368,7 @@ var GROUP_TEMPLATES = [
15687
16368
  quantity: "power",
15688
16369
  defaultAggregate: "sum",
15689
16370
  empty: "omit",
15690
- companions: [NOW$2]
16371
+ companions: [NOW$3]
15691
16372
  }),
15692
16373
  numeric({
15693
16374
  id: "energy",
@@ -15698,7 +16379,7 @@ var GROUP_TEMPLATES = [
15698
16379
  quantity: "energy",
15699
16380
  defaultAggregate: "sum",
15700
16381
  empty: "omit",
15701
- companions: [NOW$2]
16382
+ companions: [NOW$3]
15702
16383
  }),
15703
16384
  numeric({
15704
16385
  id: "battery",
@@ -15733,7 +16414,7 @@ var GROUP_TEMPLATES = [
15733
16414
  quantity: "device",
15734
16415
  defaultAggregate: "mean",
15735
16416
  empty: "null",
15736
- companions: [NOW$2]
16417
+ companions: [NOW$3]
15737
16418
  }),
15738
16419
  binary({
15739
16420
  id: "motion",
@@ -16159,7 +16840,7 @@ async function runFanOut(calls, invoke) {
16159
16840
  //#region src/generated/history-catalog.ts
16160
16841
  /**
16161
16842
  * Cap history catalog, projected from every `*.cap.ts` `history` block.
16162
- * 41 caps with series (76 series), 34 caps answered `'none'`.
16843
+ * 42 caps with series (80 series), 34 caps answered `'none'`.
16163
16844
  * Field keys are `runtimeState` paths (`a.b` into fixed objects only).
16164
16845
  */
16165
16846
  var HISTORY_CATALOG = {
@@ -16399,6 +17080,25 @@ var HISTORY_CATALOG = {
16399
17080
  unit: "s"
16400
17081
  }
16401
17082
  },
17083
+ "pet-fountain": {
17084
+ "waterLevel": {
17085
+ kind: "gauge",
17086
+ unit: "%",
17087
+ deadband: 1
17088
+ },
17089
+ "waterVolume": {
17090
+ kind: "gauge",
17091
+ unit: "L"
17092
+ },
17093
+ "todayDrink": {
17094
+ kind: "gauge",
17095
+ unit: "L"
17096
+ },
17097
+ "last24hDrink": {
17098
+ kind: "gauge",
17099
+ unit: "L"
17100
+ }
17101
+ },
16402
17102
  "power-meter": {
16403
17103
  "power": {
16404
17104
  kind: "gauge",
@@ -16596,7 +17296,7 @@ function historyFieldsFor(capName) {
16596
17296
  * Project a statistics or integral helper onto a new device (D944 §3).
16597
17297
  * The value field is `series`: the composer fills it from history.
16598
17298
  */
16599
- var NOW$1 = {
17299
+ var NOW$2 = {
16600
17300
  kind: "expression",
16601
17301
  expr: "now",
16602
17302
  bindings: {}
@@ -16656,7 +17356,7 @@ function draft(input) {
16656
17356
  cap: input.cap,
16657
17357
  fields: {
16658
17358
  [input.field]: series(input.member, "value", input.empty),
16659
- lastFetchedAt: NOW$1
17359
+ lastFetchedAt: NOW$2
16660
17360
  }
16661
17361
  }, {
16662
17362
  kind: "fields",
@@ -16738,12 +17438,12 @@ function integralDraft(spec, retention = require_composition.DEFAULT_HISTORY_RET
16738
17438
  * a free expression is the expression engine unchanged — it still includes a
16739
17439
  * stale input, and the template says so.
16740
17440
  */
16741
- var NOW = {
17441
+ var NOW$1 = {
16742
17442
  kind: "expression",
16743
17443
  expr: "now",
16744
17444
  bindings: {}
16745
17445
  };
16746
- function from$1(member) {
17446
+ function from$2(member) {
16747
17447
  return {
16748
17448
  kind: "from",
16749
17449
  source: member.source,
@@ -16774,7 +17474,7 @@ function numericSensor(helper, value, extra = []) {
16774
17474
  cap: "numeric-sensor",
16775
17475
  fields: {
16776
17476
  value,
16777
- lastFetchedAt: NOW
17477
+ lastFetchedAt: NOW$1
16778
17478
  }
16779
17479
  }, ...extra]
16780
17480
  };
@@ -16792,7 +17492,7 @@ function binarySensor(helper, on) {
16792
17492
  cap: "binary",
16793
17493
  fields: {
16794
17494
  on,
16795
- lastChangedAt: NOW
17495
+ lastChangedAt: NOW$1
16796
17496
  }
16797
17497
  }]
16798
17498
  };
@@ -16810,7 +17510,7 @@ function thresholdDraft(spec) {
16810
17510
  const delays = onFor !== null && offFor !== null && (spec.onForMs > 0 || spec.offForMs > 0) ? `, ${onFor}, ${offFor}` : "";
16811
17511
  return {
16812
17512
  problems,
16813
- draft: binarySensor(spec, expression$1(`coalesce(schmitt(x, ${high ?? "0"}, ${low ?? "0"}${delays}), false)`, { x: from$1(spec.source) }))
17513
+ draft: binarySensor(spec, expression$1(`coalesce(schmitt(x, ${high ?? "0"}, ${low ?? "0"}${delays}), false)`, { x: from$2(spec.source) }))
16814
17514
  };
16815
17515
  }
16816
17516
  var NAMES = ["a", "b"];
@@ -16854,7 +17554,7 @@ function arithmeticDraft(spec) {
16854
17554
  }])
16855
17555
  };
16856
17556
  }
16857
- const bindings = Object.fromEntries(spec.inputs.slice(0, 2).map((input, index) => [NAMES[index] ?? "a", from$1(input)]));
17557
+ const bindings = Object.fromEntries(spec.inputs.slice(0, 2).map((input, index) => [NAMES[index] ?? "a", from$2(input)]));
16858
17558
  return {
16859
17559
  problems,
16860
17560
  draft: numericSensor(spec, expression$1(spec.op === "difference" ? "a - b" : "b == 0 ? null : a / b", bindings))
@@ -16870,7 +17570,7 @@ function expressionProblems(spec) {
16870
17570
  seen.add(input.name);
16871
17571
  });
16872
17572
  if (problems.length > 0) return problems;
16873
- const bindings = Object.fromEntries(spec.inputs.map((input) => [input.name, from$1(input.member)]));
17573
+ const bindings = Object.fromEntries(spec.inputs.map((input) => [input.name, from$2(input.member)]));
16874
17574
  const message = require_composition.validateExpressionSource({
16875
17575
  expr: spec.expr,
16876
17576
  bindings
@@ -16879,7 +17579,7 @@ function expressionProblems(spec) {
16879
17579
  }
16880
17580
  function expressionDraft(spec) {
16881
17581
  const problems = expressionProblems(spec);
16882
- const bindings = Object.fromEntries(spec.inputs.map((input) => [input.name, from$1(input.member)]));
17582
+ const bindings = Object.fromEntries(spec.inputs.map((input) => [input.name, from$2(input.member)]));
16883
17583
  const source = expression$1(spec.expr, bindings);
16884
17584
  return {
16885
17585
  problems,
@@ -17396,8 +18096,9 @@ function actuatorCommandCaps(composition) {
17396
18096
  function builtCapProblems({ def, state, fields: targetFields }, feature, at, deviceType, servedCommands) {
17397
18097
  const problems = [];
17398
18098
  if (def.deviceTypes !== void 0 && !def.deviceTypes.includes(deviceType)) problems.push(problem("wrong-device-type", `${at}.cap`, `\`${def.name}\` is not offered on a \`${deviceType}\` device (it allows ${def.deviceTypes.join(", ")})`));
17399
- const served = Object.entries(def.methods).filter(([name, m]) => m.systemOnly !== true && def.composedMethods?.[name] === void 0).map(([name]) => `\`${name}\``);
17400
- if (served.length > 0 && !servedCommands.has(def.name)) problems.push(problem("has-commands", `${at}.cap`, `\`${def.name}\` has ${served.join(", ")}, which a composed device cannot serve until commands arrive (slice 3)`));
18099
+ const commanded = new Set(Object.entries(feature.commands ?? {}).flatMap(([name, target]) => target.kind === "forward" ? [name] : []));
18100
+ const unserved = commandedMethods(def).filter((name) => !commanded.has(name)).map((name) => `\`${name}\``);
18101
+ if (unserved.length > 0 && !servedCommands.has(def.name)) problems.push(problem("has-commands", `${at}.cap`, `\`${def.name}\` has ${unserved.join(", ")} with no command — a composed device serves a method only through a \`forward\` (D967)`));
17401
18102
  const itemArrayPath = feature.items === void 0 ? null : def.status?.itemArray?.path ?? null;
17402
18103
  for (const key of requiredTopLevelKeys(state)) {
17403
18104
  if (key === itemArrayPath) continue;
@@ -17438,12 +18139,28 @@ function planTopLevelField({ def, fields }, field, mode, lookupCap) {
17438
18139
  events: []
17439
18140
  };
17440
18141
  }
18142
+ /**
18143
+ * The commands of a feature (D967). Only a BUILT capability on a NEW device
18144
+ * serves a method through a `forward`; on an existing device the native keeps
18145
+ * its methods, and a `code` command is slice 4.
18146
+ */
18147
+ function commandProblems(def, feature, featureIndex, mode, lookupCap) {
18148
+ const at = `features[${featureIndex}]`;
18149
+ const forwards = featureForwards(feature, featureIndex);
18150
+ const problems = Object.entries(feature.commands ?? {}).filter(([, t]) => t.kind === "code").map(([method]) => problem("reserved-variant", `${at}.commands.${method}`, "a `code` command arrives in slice 4"));
18151
+ if (forwards.length > 0 && mode !== "new") return [...problems, problem("reserved-variant", `${at}.commands`, "commands (`forward`) are served only by a capability a NEW device builds — not on an existing device")];
18152
+ return [
18153
+ ...problems,
18154
+ ...commandMethodProblems(def, feature, at),
18155
+ ...forwards.flatMap((forward) => forwardProblems(def, forward, lookupCap))
18156
+ ];
18157
+ }
17441
18158
  function planFeature(feature, featureIndex, target, lookupCap, servedCommands) {
17442
18159
  const at = `features[${featureIndex}]`;
17443
18160
  const cap = composableCap(feature, at, lookupCap);
17444
18161
  if ("code" in cap) return refused(cap);
17445
18162
  const capProblems = target.mode === "replace" ? [] : [...builtCapProblems(cap, feature, at, target.deviceType, servedCommands)];
17446
- if (feature.commands !== void 0 && Object.keys(feature.commands).length > 0) capProblems.push(problem("reserved-variant", `${at}.commands`, "commands (`forward`) arrive in slice 3"));
18163
+ capProblems.push(...commandProblems(cap.def, feature, featureIndex, target.mode, lookupCap));
17447
18164
  const perField = Object.entries(feature.fields).map(([path, source]) => planTopLevelField(cap, {
17448
18165
  featureIndex,
17449
18166
  path,
@@ -17562,7 +18279,7 @@ function planComposition(composition, lookupCap, target) {
17562
18279
  }
17563
18280
  function compositionSourceRefs(composition) {
17564
18281
  const refs = composition.features.flatMap((f) => require_composition.compositionFeatureEnabled(f) && f.kind === "fields" ? require_composition.featureSources(f).flatMap(require_composition.fieldReads).map((r) => r.source) : []);
17565
- return [...new Map(refs.map((r) => [require_composition.compositionSourceKey(r), r])).values()];
18282
+ return [...new Map([...refs, ...forwardSourceRefs(composition)].map((r) => [require_composition.compositionSourceKey(r), r])).values()];
17566
18283
  }
17567
18284
  /**
17568
18285
  * The candidate's device: the existing target itself, else `self`, else a
@@ -17698,6 +18415,7 @@ function validateComposition(composition, ctx) {
17698
18415
  const problems = [
17699
18416
  ...plan.problems,
17700
18417
  ...sourceProblems(plan.fields, ctx, self),
18418
+ ...forwardSourceProblems(composition, ctx.sources),
17701
18419
  ...cycles,
17702
18420
  ...helperSaveProblems(composition, facts)
17703
18421
  ];
@@ -21058,6 +21776,36 @@ var ExposeInputSchema = zod.z.object({
21058
21776
  capabilities: zod.z.array(zod.z.string()).optional()
21059
21777
  });
21060
21778
  var UnexposeInputSchema = zod.z.object({ deviceId: zod.z.string() });
21779
+ /**
21780
+ * What an exporter may expose besides devices (D968). An action and a
21781
+ * notification target are LIST ENTRIES, never devices (D954): they enter an
21782
+ * exporter's own selection, not the device registry.
21783
+ */
21784
+ var ExportEntityKindSchema = zod.z.enum([
21785
+ "device",
21786
+ "action",
21787
+ "notifier"
21788
+ ]);
21789
+ var ExportEntityRefSchema = zod.z.discriminatedUnion("kind", [
21790
+ zod.z.object({
21791
+ kind: zod.z.literal("device"),
21792
+ deviceId: zod.z.string()
21793
+ }),
21794
+ zod.z.object({
21795
+ kind: zod.z.literal("action"),
21796
+ actionId: zod.z.string().min(1)
21797
+ }),
21798
+ zod.z.object({
21799
+ kind: zod.z.literal("notifier"),
21800
+ targetId: zod.z.string().min(1)
21801
+ })
21802
+ ]);
21803
+ var ExposedEntitySchema = zod.z.object({
21804
+ entity: ExportEntityRefSchema,
21805
+ exposedAs: zod.z.string().optional()
21806
+ });
21807
+ var ExposeEntityInputSchema = zod.z.object({ entity: ExportEntityRefSchema });
21808
+ var UnexposeEntityInputSchema = zod.z.object({ entity: ExportEntityRefSchema });
21061
21809
  var deviceExportCapability = {
21062
21810
  name: "device-export",
21063
21811
  scope: "system",
@@ -21087,6 +21835,16 @@ var deviceExportCapability = {
21087
21835
  unexposeDevice: require_sleep.method(UnexposeInputSchema, zod.z.void(), {
21088
21836
  kind: "mutation",
21089
21837
  auth: "admin"
21838
+ }),
21839
+ listSupportedEntityKinds: require_sleep.optionalMethod(zod.z.void(), zod.z.array(ExportEntityKindSchema)),
21840
+ listExposedEntities: require_sleep.optionalMethod(zod.z.void(), zod.z.array(ExposedEntitySchema)),
21841
+ exposeEntity: require_sleep.optionalMethod(ExposeEntityInputSchema, zod.z.void(), {
21842
+ kind: "mutation",
21843
+ auth: "admin"
21844
+ }),
21845
+ unexposeEntity: require_sleep.optionalMethod(UnexposeEntityInputSchema, zod.z.void(), {
21846
+ kind: "mutation",
21847
+ auth: "admin"
21090
21848
  })
21091
21849
  }
21092
21850
  };
@@ -44795,7 +45553,7 @@ var LlmProviderWidgetSchema = zod.z.object({
44795
45553
  /** ConfigUISchema tree passed through untyped on the wire (the
44796
45554
  * notification-output `ConfigSchemaPassthrough` precedent at
44797
45555
  * notification-output.cap.ts:151); the exported TS type re-tightens it. */
44798
- var ConfigSchemaPassthrough$3 = zod.z.unknown();
45556
+ var ConfigSchemaPassthrough$2 = zod.z.unknown();
44799
45557
  var LlmProfileKindDescriptorSchema = zod.z.object({
44800
45558
  kind: LlmProfileKindSchema,
44801
45559
  label: zod.z.string(),
@@ -44804,9 +45562,9 @@ var LlmProfileKindDescriptorSchema = zod.z.object({
44804
45562
  /** Stamped by each provider so the concat-fanned catalog stays routable. */
44805
45563
  addonId: zod.z.string(),
44806
45564
  /** The provider's own fields (`providerConfig`); `password` fields are keychain-held. */
44807
- connectionSchema: ConfigSchemaPassthrough$3,
45565
+ connectionSchema: ConfigSchemaPassthrough$2,
44808
45566
  /** Provider fields that belong under "Advanced" (extra headers, runtime tuning). */
44809
- advancedSchema: ConfigSchemaPassthrough$3.optional(),
45567
+ advancedSchema: ConfigSchemaPassthrough$2.optional(),
44810
45568
  /** The kind's defaults; a model from `listModels` may override any of them. */
44811
45569
  features: LlmModelFeaturesSchema,
44812
45570
  /** `false` = the provider stamps `supportsVision` itself (from what a node reports). */
@@ -45563,10 +46321,10 @@ var CpuBreakdownSchema = zod.z.object({
45563
46321
  cores: zod.z.number()
45564
46322
  });
45565
46323
  var MemoryInfoSchema = zod.z.object({
45566
- percent: zod.z.number(),
46324
+ percent: zod.z.number().nullable(),
45567
46325
  totalBytes: zod.z.number(),
45568
- usedBytes: zod.z.number(),
45569
- availableBytes: zod.z.number(),
46326
+ usedBytes: zod.z.number().nullable(),
46327
+ availableBytes: zod.z.number().nullable(),
45570
46328
  swapUsedBytes: zod.z.number(),
45571
46329
  swapTotalBytes: zod.z.number()
45572
46330
  });
@@ -45870,8 +46628,9 @@ var GetLoadSeriesInputSchema = zod.z.object({
45870
46628
  });
45871
46629
  var SystemMetricsSchema = zod.z.object({
45872
46630
  cpuPercent: zod.z.number(),
45873
- memoryPercent: zod.z.number(),
45874
- memoryUsedMB: zod.z.number(),
46631
+ /** `null` when the memory sample failed or was not taken yet (D393). */
46632
+ memoryPercent: zod.z.number().nullable(),
46633
+ memoryUsedMB: zod.z.number().nullable(),
45875
46634
  memoryTotalMB: zod.z.number(),
45876
46635
  diskPercent: zod.z.number().optional(),
45877
46636
  temperature: zod.z.number().optional(),
@@ -46273,453 +47032,6 @@ var networkAccessCapability = {
46273
47032
  }
46274
47033
  };
46275
47034
  //#endregion
46276
- //#region src/capabilities/notification-output.cap.ts
46277
- /**
46278
- * notification-output — canonical, capability-gated notification delivery.
46279
- *
46280
- * Apprise-derived model (see
46281
- * `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
46282
- * callers emit ONE canonical `Notification`; each provider declares a
46283
- * per-kind capability descriptor (`TargetKind`), and the pure degrade
46284
- * engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
46285
- * message to what the kind supports — callers never special-case a service.
46286
- *
46287
- * DESIGN DECISIONS (locked):
46288
- * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
46289
- * `setTargetEnabled`), each provider persisting via the `settings-store`
46290
- * cap. Rationale: the admin UI needs one uniform surface across the
46291
- * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
46292
- * alternative would fork the UI per addon and cannot host the
46293
- * discovery→adopt flow.
46294
- * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
46295
- * the generated cap-mount auto-`concatCollection`-fans them across every
46296
- * registered provider (notifiers addon + HA addon) so one catalog is
46297
- * routable. `send` / `testTarget` / CRUD route to ONE provider by the
46298
- * `addonId` the generated collection router extracts from the call input.
46299
- * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
46300
- * transformer) + UDS MsgPack both round-trip typed arrays — already used by
46301
- * `storage` / `storage-provider` / `recording` caps over the same path. No
46302
- * base64 fallback needed.
46303
- *
46304
- * TODO (deferred, closed-set change — separate decision): add
46305
- * `providerKind: 'notify'` so notification providers surface on the unified
46306
- * admin "Integrations" page.
46307
- */
46308
- /**
46309
- * Zentik-derived typed-media enum — the superset across every kind. Each
46310
- * adapter picks what it supports and the degrade engine filters the rest.
46311
- */
46312
- var AttachmentMediaTypeSchema = zod.z.enum([
46313
- "image",
46314
- "video",
46315
- "gif",
46316
- "audio",
46317
- "icon"
46318
- ]);
46319
- /**
46320
- * A single attachment. Exactly one of `url` (remote source, most adapters
46321
- * prefer this) or `bytes` (inline source; required for Pushover-style
46322
- * bytes-only kinds) MUST be present — the degrade engine expresses a
46323
- * url→bytes fetch as a `needsFetch` directive the adapter executes.
46324
- */
46325
- var AttachmentSchema = zod.z.object({
46326
- mediaType: AttachmentMediaTypeSchema,
46327
- url: zod.z.string().optional(),
46328
- bytes: zod.z.instanceof(Uint8Array).optional(),
46329
- mime: zod.z.string().optional(),
46330
- name: zod.z.string().optional()
46331
- }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
46332
- var NotificationFormatSchema = zod.z.enum([
46333
- "text",
46334
- "markdown",
46335
- "html"
46336
- ]);
46337
- /**
46338
- * The CLOSED icon vocabulary an action button may use.
46339
- *
46340
- * A closed set, not a free string, and that is the whole point: an arbitrary
46341
- * icon name is one that ntfy renders, zentik silently drops, and nobody
46342
- * notices — the same class of gap as a zone vocabulary nothing produced
46343
- * ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
46344
- * declares `actionIcons: false` and the degrade engine strips the field.
46345
- *
46346
- * Named by INTENT, never by glyph. "check" would tie the vocabulary to one
46347
- * renderer's icon set; "acknowledge" survives an adapter that draws it
46348
- * differently.
46349
- *
46350
- * ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
46351
- *
46352
- * These names are for US. **No adapter may forward one verbatim.** Each maps
46353
- * the whole set onto its own renderer's vocabulary through a
46354
- * `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
46355
- * fallback, so adding a member here fails every adapter's build until someone
46356
- * decides its glyph, which is the only place that decision can be made
46357
- * honestly.
46358
- *
46359
- * This paragraph is the bug. Zentik declared `actionIcons: true` and passed
46360
- * `disarm` straight through; iOS feeds that string to
46361
- * `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
46362
- * and every snooze and alarm button arrived BLANK. A pass-through is not a
46363
- * mapping, and "the field is documented" is not "the value renders".
46364
- *
46365
- * Adding a member is TRAIN-BOUND. The enum lives in the published
46366
- * `@camstack/server` closure and the cap seam validates against the HUB's copy,
46367
- * so an addon that emits a token the running hub does not know does not lose an
46368
- * icon — its whole `send` fails Zod validation and the notification never
46369
- * arrives. Never emit a new token from an addon before the train carrying it.
46370
- */
46371
- var NotificationActionIconSchema = zod.z.enum([
46372
- "acknowledge",
46373
- "dismiss",
46374
- "silence",
46375
- "snooze",
46376
- "view",
46377
- "play",
46378
- "open",
46379
- "close",
46380
- "lock",
46381
- "unlock",
46382
- "arm",
46383
- "arm-home",
46384
- "arm-away",
46385
- "arm-night",
46386
- "disarm",
46387
- "light",
46388
- "alert",
46389
- "camera"
46390
- ]);
46391
- /** A single tap-through action button. */
46392
- var NotificationActionSchema = zod.z.object({
46393
- id: zod.z.string(),
46394
- label: zod.z.string(),
46395
- url: zod.z.string().optional(),
46396
- /** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
46397
- icon: NotificationActionIconSchema.optional(),
46398
- /**
46399
- * Renders in a warning style where the notifier supports it.
46400
- *
46401
- * A HINT, never a gate. The callback's authority is its token and nothing
46402
- * else — see `notification-center/action-token.ts` for what that does and
46403
- * does not buy.
46404
- */
46405
- destructive: zod.z.boolean().optional(),
46406
- /**
46407
- * How the tap should REACH the url.
46408
- *
46409
- * `navigate` (absent, and every button authored before this field) opens it:
46410
- * the phone leaves the notification and shows whatever the callback returns.
46411
- * That is right for a button whose answer the operator wants to read.
46412
- *
46413
- * `background` fires it as a POST and stays put. It exists for the buttons
46414
- * whose whole point is not to interrupt — "silence this for 30 minutes" is
46415
- * an answer to the notification, and being thrown into a browser tab to
46416
- * confirm it costs more attention than the notification did. A backend that
46417
- * cannot do a background call renders it as an ordinary link (the adapters
46418
- * fall back rather than dropping the button), so this is a preference, never
46419
- * a requirement.
46420
- */
46421
- mode: zod.z.enum(["navigate", "background"]).optional()
46422
- });
46423
- /** A delivery parameter value: what a notifier declared it takes. */
46424
- var DeliveryParamValueSchema = zod.z.union([
46425
- zod.z.string(),
46426
- zod.z.number(),
46427
- zod.z.boolean()
46428
- ]);
46429
- /**
46430
- * One extra delivery parameter a notifier KIND declares (D938). A rule that
46431
- * addresses a target of that kind may set it; a kind that declares nothing is
46432
- * never handed one. The admin UI derives its control from this descriptor —
46433
- * no editor knows a service by name.
46434
- *
46435
- * Optional fields are `.optional()`, never `.default()` (a Zod default does not
46436
- * run on the addon cap path).
46437
- */
46438
- var DeliveryParamSchema = zod.z.object({
46439
- key: zod.z.string().min(1),
46440
- label: zod.z.string(),
46441
- /** i18n key of the label, resolved by the declaring addon's namespace. */
46442
- labelKey: zod.z.string().optional(),
46443
- type: zod.z.enum([
46444
- "enum",
46445
- "number",
46446
- "boolean",
46447
- "string"
46448
- ]),
46449
- /** `enum` only: the closed set of accepted values. */
46450
- options: zod.z.array(zod.z.object({
46451
- value: zod.z.string(),
46452
- label: zod.z.string()
46453
- })).optional(),
46454
- /** `number` only. */
46455
- min: zod.z.number().optional(),
46456
- max: zod.z.number().optional(),
46457
- /** What the notifier does when the rule sets nothing; shown as the placeholder. */
46458
- default: DeliveryParamValueSchema.optional()
46459
- });
46460
- /**
46461
- * The canonical notification. `body` is the only hard field (Apprise model).
46462
- * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
46463
- * NOT a fixed severity enum — each kind declares its own `caps.levels` and
46464
- * the adapter maps this ordinal onto its native level. `level?` is an
46465
- * optional kind-native level id (`emergency`, `silent`, …) that overrides
46466
- * `priority` for that one target.
46467
- */
46468
- var NotificationSchema = zod.z.object({
46469
- body: zod.z.string(),
46470
- title: zod.z.string().optional(),
46471
- format: NotificationFormatSchema.default("text"),
46472
- priority: zod.z.number().int().min(1).max(5).default(3),
46473
- level: zod.z.string().optional(),
46474
- attachments: zod.z.array(AttachmentSchema).optional(),
46475
- clickUrl: zod.z.string().optional(),
46476
- actions: zod.z.array(NotificationActionSchema).optional(),
46477
- sound: zod.z.string().optional(),
46478
- ttl: zod.z.number().optional(),
46479
- tag: zod.z.string().optional(),
46480
- deviceId: zod.z.number().optional(),
46481
- eventId: zod.z.string().optional(),
46482
- metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
46483
- /**
46484
- * Values for the parameters the target's kind DECLARES in
46485
- * {@link TargetKindSchema} `deliveryParams`. Already validated against the
46486
- * declaration by the sender; a notifier reads only the keys it declared.
46487
- */
46488
- delivery: zod.z.record(zod.z.string(), DeliveryParamValueSchema).optional()
46489
- });
46490
- /** One declared native severity/priority level for a kind. */
46491
- var TargetKindLevelSchema = zod.z.object({
46492
- id: zod.z.string(),
46493
- label: zod.z.string(),
46494
- /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
46495
- ordinal: zod.z.number().int().min(1).max(5).nullable(),
46496
- flags: zod.z.object({
46497
- critical: zod.z.boolean().optional(),
46498
- silent: zod.z.boolean().optional(),
46499
- noPush: zod.z.boolean().optional()
46500
- }).optional(),
46501
- /** e.g. Pushover `emergency` requires `retry` / `expire`. */
46502
- requires: zod.z.array(zod.z.string()).optional(),
46503
- description: zod.z.string().optional()
46504
- });
46505
- /** Attachment capabilities for a kind (drives the degrade engine + test panel). */
46506
- var TargetKindAttachmentsCapsSchema = zod.z.object({
46507
- mediaTypes: zod.z.array(AttachmentMediaTypeSchema),
46508
- mode: zod.z.enum([
46509
- "url",
46510
- "bytes",
46511
- "both"
46512
- ]),
46513
- max: zod.z.number().int().nonnegative(),
46514
- maxBytes: zod.z.number().int().positive().optional()
46515
- });
46516
- /** The full capability block consulted before dispatch. */
46517
- var TargetKindCapsSchema = zod.z.object({
46518
- attachments: TargetKindAttachmentsCapsSchema,
46519
- /** Max action buttons (0 = none). */
46520
- actions: zod.z.number().int().nonnegative(),
46521
- /**
46522
- * Whether this kind renders a per-action ICON.
46523
- *
46524
- * `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
46525
- * run on the addon cap path — three production failures in one day taught
46526
- * this repo that once. Absent is read as false by the degrade engine, which
46527
- * is the safe direction: an icon that is not rendered costs nothing, an icon
46528
- * assumed and dropped costs the operator's trust in the field.
46529
- */
46530
- actionIcons: zod.z.boolean().optional(),
46531
- levels: zod.z.array(TargetKindLevelSchema),
46532
- format: zod.z.array(NotificationFormatSchema),
46533
- clickUrl: zod.z.boolean(),
46534
- sound: zod.z.boolean(),
46535
- ttl: zod.z.boolean(),
46536
- bodyMaxLen: zod.z.number().int().positive()
46537
- });
46538
- /**
46539
- * `configSchema` is a `ConfigUISchema` tree passed through to the admin
46540
- * FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
46541
- * `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
46542
- * the union is large and not meant for runtime validation here; the exported
46543
- * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
46544
- */
46545
- var ConfigSchemaPassthrough$2 = zod.z.unknown();
46546
- var TargetKindSchema = zod.z.object({
46547
- kind: zod.z.string(),
46548
- label: zod.z.string(),
46549
- icon: zod.z.string(),
46550
- /** Stamped by each provider so the concat-fanned catalog stays routable. */
46551
- addonId: zod.z.string(),
46552
- /**
46553
- * URL of the kind's bundled BRAND icon, served by the providing addon over
46554
- * its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
46555
- * when the addon bundles no icon for that kind — the client then falls back
46556
- * to a neutral glyph rather than rendering the raw `icon` NAME as text.
46557
- *
46558
- * Root-relative on purpose: it resolves against whatever origin serves a web
46559
- * client, and a native client joins it onto its own hub base.
46560
- *
46561
- * DECLARED here deliberately. It used to travel as an undeclared passthrough
46562
- * field that survived only because the runtime cap-router forwards provider
46563
- * output verbatim — so every consumer had to re-declare it by hand to stop
46564
- * its own Zod parse from stripping it, and the whole arrangement would have
46565
- * broken silently the moment output validation was tightened anywhere.
46566
- */
46567
- iconUrl: zod.z.string().optional(),
46568
- /**
46569
- * Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
46570
- *
46571
- * The server knows this and therefore says it, because the client cannot
46572
- * safely guess: a React-Native client renders SVG and raster through two
46573
- * DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
46574
- * not decode SVG on iOS/Android), so without this it silently fell back to a
46575
- * placeholder glyph for every vector icon while the web build looked fine.
46576
- *
46577
- * Absent when {@link iconUrl} is absent, or for a legacy provider that has
46578
- * not been updated — a client that cannot determine the type should prefer
46579
- * its raster path, which is the safe default for an unknown image.
46580
- */
46581
- iconMediaType: zod.z.string().optional(),
46582
- configSchema: ConfigSchemaPassthrough$2,
46583
- /**
46584
- * The config keys that decide WHERE this kind's secrets are sent (server,
46585
- * base URL, instance, chat, recipient). A save that changes any of them
46586
- * while leaving a secret redacted is refused by the provider (D634), so an
46587
- * editor uses this to ask for the secret on exactly those edits instead of
46588
- * on every edit.
46589
- *
46590
- * Optional: absent from a provider that holds no secret (the Home Assistant
46591
- * kinds) and from a provider older than this field — an editor that finds
46592
- * it absent must assume ANY edit may need the secret.
46593
- */
46594
- destinationKeys: zod.z.array(zod.z.string()).optional(),
46595
- supportsDiscovery: zod.z.boolean(),
46596
- /**
46597
- * Extra delivery parameters a rule may set for a target of this kind (D938).
46598
- * Absent = none; the rule editor then offers nothing.
46599
- */
46600
- deliveryParams: zod.z.array(DeliveryParamSchema).optional(),
46601
- caps: TargetKindCapsSchema
46602
- });
46603
- /**
46604
- * A persisted target. `config` holds secrets; providers REDACT secret fields
46605
- * (return a presence marker only) when serving `listTargets` — never
46606
- * round-trip a stored secret to the UI.
46607
- */
46608
- var TargetSchema = zod.z.object({
46609
- id: zod.z.string(),
46610
- name: zod.z.string(),
46611
- kind: zod.z.string(),
46612
- addonId: zod.z.string(),
46613
- enabled: zod.z.boolean(),
46614
- config: zod.z.record(zod.z.string(), zod.z.unknown())
46615
- });
46616
- /** A discovery-surfaced candidate (config is partial + non-secret). */
46617
- var DiscoveredTargetSchema = zod.z.object({
46618
- kind: zod.z.string(),
46619
- suggestedName: zod.z.string(),
46620
- config: zod.z.record(zod.z.string(), zod.z.unknown())
46621
- });
46622
- /** The degrade engine's report — what was resolved / dropped / degraded. */
46623
- var RenderedAsSchema = zod.z.object({
46624
- level: zod.z.string(),
46625
- format: NotificationFormatSchema,
46626
- attachmentsSent: zod.z.number().int().nonnegative(),
46627
- actionsSent: zod.z.number().int().nonnegative(),
46628
- truncated: zod.z.boolean(),
46629
- dropped: zod.z.array(zod.z.string())
46630
- });
46631
- var SendResultSchema = zod.z.object({
46632
- success: zod.z.boolean(),
46633
- error: zod.z.string().optional(),
46634
- renderedAs: RenderedAsSchema.optional()
46635
- });
46636
- /** Same shape as SendResult — kept as a distinct name for the test panel. */
46637
- var TestResultSchema = SendResultSchema;
46638
- /**
46639
- * Pre-save credential probe against a partial (unsaved) target config.
46640
- * `resolvedUrl` never includes secrets (token stripped from path).
46641
- */
46642
- var ProbeTargetConfigResultSchema = zod.z.object({
46643
- ok: zod.z.boolean(),
46644
- resolvedUrl: zod.z.string(),
46645
- httpStatus: zod.z.number().int().optional(),
46646
- stateInstance: zod.z.string().optional(),
46647
- error: zod.z.string().optional()
46648
- });
46649
- var notificationOutputCapability = {
46650
- name: "notification-output",
46651
- scope: "system",
46652
- mode: "collection",
46653
- methods: {
46654
- listTargetKinds: require_sleep.method(zod.z.object({}), zod.z.array(TargetKindSchema)),
46655
- listTargets: require_sleep.method(zod.z.object({}), zod.z.array(TargetSchema), { auth: "admin" }),
46656
- /**
46657
- * One target WITH secrets — admin edit / duplicate hydration only.
46658
- * `listTargets` stays redacted; the editor loads this so password fields
46659
- * are filled (masked in the input) and a save can round-trip the token
46660
- * when a destination changes (Duplicate with a new chatId, etc.).
46661
- */
46662
- getTarget: require_sleep.method(zod.z.object({ targetId: zod.z.string() }), TargetSchema, { auth: "admin" }),
46663
- discoverTargets: require_sleep.method(zod.z.object({
46664
- kind: zod.z.string(),
46665
- config: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
46666
- /**
46667
- * When editing / duplicating, the blank password field never round-trips
46668
- * the stored token. Pass the source target id so the provider can restore
46669
- * secrets server-side before dialling (same merge as upsert).
46670
- */
46671
- targetId: zod.z.string().optional()
46672
- }), zod.z.array(DiscoveredTargetSchema), { auth: "admin" }),
46673
- probeTargetConfig: require_sleep.method(zod.z.object({
46674
- kind: zod.z.string(),
46675
- config: zod.z.record(zod.z.string(), zod.z.unknown()),
46676
- /** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
46677
- targetId: zod.z.string().optional()
46678
- }), ProbeTargetConfigResultSchema, {
46679
- kind: "mutation",
46680
- auth: "admin"
46681
- }),
46682
- send: require_sleep.method(zod.z.object({
46683
- targetId: zod.z.string(),
46684
- notification: NotificationSchema
46685
- }), SendResultSchema, {
46686
- kind: "mutation",
46687
- auth: "admin"
46688
- }),
46689
- testTarget: require_sleep.method(zod.z.object({
46690
- targetId: zod.z.string(),
46691
- sample: NotificationSchema.optional()
46692
- }), TestResultSchema, {
46693
- kind: "mutation",
46694
- auth: "admin"
46695
- }),
46696
- upsertTarget: require_sleep.method(zod.z.object({
46697
- target: TargetSchema,
46698
- /**
46699
- * Duplicate flow: create a NEW target id while copying secrets from an
46700
- * existing row. The UI never holds the stored token; blank / redacted
46701
- * password fields are restored from this source (D634 still applies if
46702
- * destinations diverge without a re-typed secret).
46703
- */
46704
- cloneSecretsFrom: zod.z.string().optional()
46705
- }), TargetSchema, {
46706
- kind: "mutation",
46707
- auth: "admin"
46708
- }),
46709
- deleteTarget: require_sleep.method(zod.z.object({ targetId: zod.z.string() }), zod.z.void(), {
46710
- kind: "mutation",
46711
- auth: "admin"
46712
- }),
46713
- setTargetEnabled: require_sleep.method(zod.z.object({
46714
- targetId: zod.z.string(),
46715
- enabled: zod.z.boolean()
46716
- }), zod.z.void(), {
46717
- kind: "mutation",
46718
- auth: "admin"
46719
- })
46720
- }
46721
- };
46722
- //#endregion
46723
47035
  //#region src/catalogs/audio-classmap.ts
46724
47036
  var AUDIO_MACRO_LABELS = [
46725
47037
  {
@@ -55594,14 +55906,6 @@ var EngineProvisioningSchema = zod.z.object({
55594
55906
  */
55595
55907
  configIssues: zod.z.array(zod.z.string()).optional()
55596
55908
  });
55597
- var PipelineStepInputSchema = zod.z.lazy(() => zod.z.object({
55598
- addonId: zod.z.string(),
55599
- modelId: zod.z.string().optional(),
55600
- enabled: zod.z.boolean().default(true),
55601
- children: zod.z.array(PipelineStepInputSchema).optional(),
55602
- settings: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
55603
- jumpDeviceKey: zod.z.string().optional()
55604
- }));
55605
55909
  var ModelSubstitutionSchema = zod.z.object({
55606
55910
  addonId: zod.z.string(),
55607
55911
  chosen: zod.z.string(),
@@ -55680,7 +55984,6 @@ var ConfigUISchemaBridge = zod.z.custom();
55680
55984
  var ConfigUISchemaNullableBridge = zod.z.custom();
55681
55985
  var InferenceCapabilitiesBridge = zod.z.custom();
55682
55986
  var ModelAvailabilityListBridge = zod.z.custom();
55683
- var PipelineRunResultBridge = zod.z.custom();
55684
55987
  /**
55685
55988
  * Pipeline executor — detection engine + configuration + inference API.
55686
55989
  *
@@ -55793,7 +56096,7 @@ var pipelineExecutorCapability = {
55793
56096
  * reads any media: can this (node, deviceKey) run exactly this model?
55794
56097
  */
55795
56098
  validatePipeline: require_sleep.method(zod.z.object({
55796
- steps: zod.z.array(PipelineStepInputSchema),
56099
+ steps: zod.z.array(require_sleep.PipelineStepInputSchema),
55797
56100
  deviceKey: zod.z.string().optional()
55798
56101
  }), PipelineValidationResultSchema),
55799
56102
  getCapabilities: require_sleep.method(zod.z.void(), InferenceCapabilitiesBridge),
@@ -55837,7 +56140,7 @@ var pipelineExecutorCapability = {
55837
56140
  * - `referenceImage`: named file from the reference-image store.
55838
56141
  */
55839
56142
  runPipeline: require_sleep.method(zod.z.object({
55840
- steps: zod.z.array(PipelineStepInputSchema).min(1),
56143
+ steps: zod.z.array(require_sleep.PipelineStepInputSchema).min(1),
55841
56144
  frame: FrameInputSchema.optional(),
55842
56145
  /**
55843
56146
  * Process-local lazy frame. Valid only when caller and provider resolve
@@ -55911,7 +56214,7 @@ var pipelineExecutorCapability = {
55911
56214
  * PIXELS of the image this call carries. Absent ⇒ the root step runs.
55912
56215
  */
55913
56216
  suppliedRoot: SuppliedRootOutputSchema.optional()
55914
- }), PipelineRunResultBridge, { kind: "mutation" }),
56217
+ }), require_sleep.PipelineRunResultBridge, { kind: "mutation" }),
55915
56218
  /**
55916
56219
  * Batched run — N raw frames packed into one cap call. The provider
55917
56220
  * routes the batch through `SharedInferencePool.inferBatch`
@@ -55924,7 +56227,7 @@ var pipelineExecutorCapability = {
55924
56227
  * N frames in one call to amortise per-call IPC overhead.
55925
56228
  */
55926
56229
  runPipelineBatch: require_sleep.method(zod.z.object({
55927
- steps: zod.z.array(PipelineStepInputSchema).min(1),
56230
+ steps: zod.z.array(require_sleep.PipelineStepInputSchema).min(1),
55928
56231
  frames: zod.z.array(FrameInputSchema).min(1).max(255),
55929
56232
  deviceId: zod.z.number().optional(),
55930
56233
  sessionId: zod.z.string().optional(),
@@ -55941,7 +56244,7 @@ var pipelineExecutorCapability = {
55941
56244
  frameId: zod.z.number().int().nonnegative().optional(),
55942
56245
  /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
55943
56246
  deviceKey: zod.z.string().optional()
55944
- }), zod.z.object({ results: zod.z.array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }),
56247
+ }), zod.z.object({ results: zod.z.array(require_sleep.PipelineRunResultBridge).readonly() }), { kind: "mutation" }),
55945
56248
  /**
55946
56249
  * Cache a raw frame inside the Python inference pool's memory.
55947
56250
  * Returns a numeric `frameId` that `inferCached` references —
@@ -57644,7 +57947,7 @@ var RunnerCameraConfigSchema = zod.z.object({
57644
57947
  detectionSources: DetectionSourcesSchema.default(["pipeline"]),
57645
57948
  pipelineEnabled: zod.z.boolean().default(true),
57646
57949
  /** Ordered tree of video steps. Absent → runner skips video detection. */
57647
- steps: zod.z.array(PipelineStepInputSchema).readonly().optional(),
57950
+ steps: zod.z.array(require_sleep.PipelineStepInputSchema).readonly().optional(),
57648
57951
  /** Audio classification branch. `enabled:false` disables, null skips. */
57649
57952
  audio: zod.z.object({
57650
57953
  modelId: zod.z.string().optional(),
@@ -58379,7 +58682,7 @@ var pipelineRunnerCapability = {
58379
58682
  //#region src/capabilities/pipeline-orchestrator.cap.ts
58380
58683
  var CameraPipelineConfigSchema = zod.z.object({
58381
58684
  engine: PipelineEngineChoiceSchema.optional(),
58382
- steps: zod.z.array(PipelineStepInputSchema).readonly(),
58685
+ steps: zod.z.array(require_sleep.PipelineStepInputSchema).readonly(),
58383
58686
  audio: zod.z.object({
58384
58687
  engine: PipelineEngineChoiceSchema.optional(),
58385
58688
  modelId: zod.z.string().optional(),
@@ -60095,6 +60398,12 @@ var ServerUpdateActionResultSchema = zod.z.object({
60095
60398
  */
60096
60399
  blockers: zod.z.array(UpdateBlockerSchema).optional()
60097
60400
  });
60401
+ var LaunchOverridesSchema = zod.z.object({
60402
+ /** The persisted heap ceiling in MB, or `null` when none is set. */
60403
+ heapMb: zod.z.number().int().nullable() });
60404
+ var SetLaunchOverridesResultSchema = LaunchOverridesSchema.extend({
60405
+ /** Always true: the override is read at launch, not by the running process. */
60406
+ restartRequired: zod.z.boolean() });
60098
60407
  var serverManagementCapability = {
60099
60408
  name: "server-management",
60100
60409
  scope: "system",
@@ -60133,6 +60442,21 @@ var serverManagementCapability = {
60133
60442
  restartServer: require_sleep.method(zod.z.void(), ServerUpdateActionResultSchema, {
60134
60443
  kind: "mutation",
60135
60444
  auth: "admin"
60445
+ }),
60446
+ /**
60447
+ * The node's persisted launch override (ADR 0970): the heap ceiling the node's
60448
+ * `node` process is started with, or `null` when none is set (the closure's
60449
+ * computed default applies). Read from the node's own data dir.
60450
+ */
60451
+ getLaunchOverrides: require_sleep.method(zod.z.void(), LaunchOverridesSchema, { auth: "admin" }),
60452
+ /**
60453
+ * Set (`heapMb`) or clear (`null`) the node's heap-ceiling override. It is
60454
+ * written on the node itself and applies at the NEXT launch — restart the
60455
+ * node (`restartServer`) for it to take effect.
60456
+ */
60457
+ setLaunchOverrides: require_sleep.method(zod.z.object({ heapMb: zod.z.number().int().min(512).max(65536).nullable() }), SetLaunchOverridesResultSchema, {
60458
+ kind: "mutation",
60459
+ auth: "admin"
60136
60460
  })
60137
60461
  },
60138
60462
  /** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
@@ -66984,14 +67308,13 @@ reset: require_sleep.method(zod.z.object({
66984
67308
  auth: "admin"
66985
67309
  }) },
66986
67310
  /**
66987
- * A composed consumable (item added to a device with no native consumables
66988
- * provider) refuses `reset` by name: "Only meaningful when the item's
66989
- * `resettable` is true" — a composed item is never resettable until
66990
- * commands (`forward`) arrive in slice 3 (D663).
67311
+ * A consumable ADDED to an existing device (a graft) refuses `reset` by
67312
+ * name: only a composed device of a NEW type forwards an item's reset to a
67313
+ * source button, and marks that item `resettable` (D967).
66991
67314
  */
66992
67315
  composedMethods: { reset: {
66993
67316
  kind: "refuse",
66994
- reason: "a composed consumable is not resettable until commands (forward) arrive — slice 3"
67317
+ reason: "a consumable added to an existing device is not resettable — only a new composed device forwards a reset"
66995
67318
  } },
66996
67319
  status: {
66997
67320
  schema: ConsumablesStatusSchema,
@@ -67654,7 +67977,7 @@ var DishwasherStatusSchema = zod.z.object({
67654
67977
  /** Ms epoch of the last value received from the appliance; 0 = never. */
67655
67978
  lastFetchedAt: zod.z.number()
67656
67979
  });
67657
- var DeviceIdSchema = zod.z.number().int().nonnegative();
67980
+ var DeviceIdSchema$1 = zod.z.number().int().nonnegative();
67658
67981
  /** A value a program option or setting can take. */
67659
67982
  var DishwasherValueSchema = zod.z.union([
67660
67983
  zod.z.boolean(),
@@ -67729,77 +68052,77 @@ var dishwasherCapability = {
67729
68052
  * option feature keys; each is checked against the program's constraints.
67730
68053
  */
67731
68054
  start: require_sleep.method(zod.z.object({
67732
- deviceId: DeviceIdSchema,
68055
+ deviceId: DeviceIdSchema$1,
67733
68056
  program: zod.z.string().min(1).optional(),
67734
68057
  options: zod.z.record(zod.z.string(), DishwasherValueSchema).optional()
67735
68058
  }), zod.z.void(), {
67736
68059
  kind: "mutation",
67737
68060
  auth: "protected"
67738
68061
  }),
67739
- pause: require_sleep.method(zod.z.object({ deviceId: DeviceIdSchema }), zod.z.void(), {
68062
+ pause: require_sleep.method(zod.z.object({ deviceId: DeviceIdSchema$1 }), zod.z.void(), {
67740
68063
  kind: "mutation",
67741
68064
  auth: "protected"
67742
68065
  }),
67743
- resume: require_sleep.method(zod.z.object({ deviceId: DeviceIdSchema }), zod.z.void(), {
68066
+ resume: require_sleep.method(zod.z.object({ deviceId: DeviceIdSchema$1 }), zod.z.void(), {
67744
68067
  kind: "mutation",
67745
68068
  auth: "protected"
67746
68069
  }),
67747
- stop: require_sleep.method(zod.z.object({ deviceId: DeviceIdSchema }), zod.z.void(), {
68070
+ stop: require_sleep.method(zod.z.object({ deviceId: DeviceIdSchema$1 }), zod.z.void(), {
67748
68071
  kind: "mutation",
67749
68072
  auth: "protected"
67750
68073
  }),
67751
68074
  /** Select (not start) a program; refused when the appliance does not offer it. */
67752
68075
  setProgram: require_sleep.method(zod.z.object({
67753
- deviceId: DeviceIdSchema,
68076
+ deviceId: DeviceIdSchema$1,
67754
68077
  program: zod.z.string().min(1)
67755
68078
  }), zod.z.void(), {
67756
68079
  kind: "mutation",
67757
68080
  auth: "protected"
67758
68081
  }),
67759
68082
  setPower: require_sleep.method(zod.z.object({
67760
- deviceId: DeviceIdSchema,
68083
+ deviceId: DeviceIdSchema$1,
67761
68084
  on: zod.z.boolean()
67762
68085
  }), zod.z.void(), {
67763
68086
  kind: "mutation",
67764
68087
  auth: "protected"
67765
68088
  }),
67766
68089
  setHalfLoad: require_sleep.method(zod.z.object({
67767
- deviceId: DeviceIdSchema,
68090
+ deviceId: DeviceIdSchema$1,
67768
68091
  on: zod.z.boolean()
67769
68092
  }), zod.z.void(), {
67770
68093
  kind: "mutation",
67771
68094
  auth: "protected"
67772
68095
  }),
67773
68096
  setHygienePlus: require_sleep.method(zod.z.object({
67774
- deviceId: DeviceIdSchema,
68097
+ deviceId: DeviceIdSchema$1,
67775
68098
  on: zod.z.boolean()
67776
68099
  }), zod.z.void(), {
67777
68100
  kind: "mutation",
67778
68101
  auth: "protected"
67779
68102
  }),
67780
68103
  setExtraDry: require_sleep.method(zod.z.object({
67781
- deviceId: DeviceIdSchema,
68104
+ deviceId: DeviceIdSchema$1,
67782
68105
  on: zod.z.boolean()
67783
68106
  }), zod.z.void(), {
67784
68107
  kind: "mutation",
67785
68108
  auth: "protected"
67786
68109
  }),
67787
68110
  setIntensiveZone: require_sleep.method(zod.z.object({
67788
- deviceId: DeviceIdSchema,
68111
+ deviceId: DeviceIdSchema$1,
67789
68112
  on: zod.z.boolean()
67790
68113
  }), zod.z.void(), {
67791
68114
  kind: "mutation",
67792
68115
  auth: "protected"
67793
68116
  }),
67794
68117
  setVarioSpeedPlus: require_sleep.method(zod.z.object({
67795
- deviceId: DeviceIdSchema,
68118
+ deviceId: DeviceIdSchema$1,
67796
68119
  on: zod.z.boolean()
67797
68120
  }), zod.z.void(), {
67798
68121
  kind: "mutation",
67799
68122
  auth: "protected"
67800
68123
  }),
67801
68124
  setChildLock: require_sleep.method(zod.z.object({
67802
- deviceId: DeviceIdSchema,
68125
+ deviceId: DeviceIdSchema$1,
67803
68126
  on: zod.z.boolean()
67804
68127
  }), zod.z.void(), {
67805
68128
  kind: "mutation",
@@ -67811,7 +68134,7 @@ var dishwasherCapability = {
67811
68134
  * its declared range / members.
67812
68135
  */
67813
68136
  setOption: require_sleep.method(zod.z.object({
67814
- deviceId: DeviceIdSchema,
68137
+ deviceId: DeviceIdSchema$1,
67815
68138
  key: zod.z.string().min(1),
67816
68139
  value: DishwasherValueSchema
67817
68140
  }), zod.z.void(), {
@@ -67819,7 +68142,7 @@ var dishwasherCapability = {
67819
68142
  auth: "protected"
67820
68143
  }),
67821
68144
  /** What this appliance can do, from its own description — the UI is derived from it. */
67822
- getOptions: require_sleep.method(zod.z.object({ deviceId: DeviceIdSchema }), DishwasherOptionsSchema, { auth: "protected" })
68145
+ getOptions: require_sleep.method(zod.z.object({ deviceId: DeviceIdSchema$1 }), DishwasherOptionsSchema, { auth: "protected" })
67823
68146
  },
67824
68147
  status: {
67825
68148
  schema: DishwasherStatusSchema,
@@ -72122,7 +72445,8 @@ var TopologyNodeSchema = zod.z.object({
72122
72445
  isHub: zod.z.boolean(),
72123
72446
  isOnline: zod.z.boolean(),
72124
72447
  cpuPercent: zod.z.number(),
72125
- memoryPercent: zod.z.number(),
72448
+ /** `null` when the node's memory read failed or it has not reported one (D393). */
72449
+ memoryPercent: zod.z.number().nullable(),
72126
72450
  uptime: zod.z.number(),
72127
72451
  lastSeen: zod.z.string(),
72128
72452
  localIps: zod.z.array(zod.z.string()).readonly(),
@@ -72963,9 +73287,10 @@ var PetFeederStatusSchema = zod.z.object({
72963
73287
  * combined reading; per-hopper levels live in `food1`/`food2`. Null when
72964
73288
  * the device has not reported yet. */
72965
73289
  foodLevel: zod.z.number().nullable().meta(require_sleep.readingMeta("none")),
72966
- /** Food in the BOWL as the feeder reports it (`state.bowl`), in the
72967
- * feeder's own units. Null when the model reports none. */
72968
- bowlFood: zod.z.number().nullable().meta(require_sleep.readingMeta("none")),
73290
+ /** Food in the BOWL, a mass (stored in kilograms, D944). Reported only by a
73291
+ * feeder with a bowl scale; null when the model has none or its reading is
73292
+ * of an unproven unit. */
73293
+ bowlFood: zod.z.number().nullable().meta(require_sleep.readingMeta("mass")),
72969
73294
  /** True while the pet is eating (`state.eating`). */
72970
73295
  eating: zod.z.boolean(),
72971
73296
  /** Whether `markFoodReplenished` does anything on this model. When false
@@ -73150,6 +73475,139 @@ var petFeederCapability = {
73150
73475
  } }
73151
73476
  };
73152
73477
  //#endregion
73478
+ //#region src/capabilities/pet-fountain.cap.ts
73479
+ /**
73480
+ * Pet water-fountain cap. Models the telemetry and the control surface of a
73481
+ * circulating drinking fountain: how much water is in the tank, how much the
73482
+ * pet drank, whether the pump runs and on what duty cycle.
73483
+ *
73484
+ * It has no native vendor provider of its own: a fountain reaches CamStack as
73485
+ * a container of loose entities (a pump switch, two number boxes, a "filter
73486
+ * changed" button), and an operator BUILDS the device from them — a composed
73487
+ * device of this type (D659, D967). Every method is therefore a forward to ONE
73488
+ * method of ONE source entity, and every reading is a field derived from a
73489
+ * source entity; the provider-shaped interface below exists so the composed
73490
+ * device and any future native provider answer the same contract.
73491
+ *
73492
+ * Quantities are stored in the base unit of their kind (D944): volumes in
73493
+ * LITRES, durations in SECONDS, the tank level in percent. A source that
73494
+ * reports millilitres or minutes is converted in the composition's expression,
73495
+ * never here. Every reading may be `null`: a source that is offline or does
73496
+ * not report is unknown, never zero.
73497
+ *
73498
+ * The filter is not a field of this cap: it is an item of the `consumables`
73499
+ * cap on the same device (kind `wear`), reset by the same forward mechanism.
73500
+ */
73501
+ var PetFountainStatusSchema = zod.z.object({
73502
+ /** Water in the tank, 0..100 (%). Null while the source has not reported. */
73503
+ waterLevel: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
73504
+ /** Water in the tank, in litres. Null while the source has not reported. */
73505
+ waterVolume: zod.z.number().min(0).nullable().meta(require_sleep.readingMeta("volume")),
73506
+ /** True when the tank is empty. Null while unknown. */
73507
+ empty: zod.z.boolean().nullable(),
73508
+ /** True while the pump is running. Null while unknown. */
73509
+ pumpRunning: zod.z.boolean().nullable(),
73510
+ /** True when the pump works on an on/off duty cycle rather than continuously. */
73511
+ pumpIntervalEnabled: zod.z.boolean().nullable(),
73512
+ /** How long the pump runs per cycle, in seconds. */
73513
+ pumpOnTime: zod.z.number().min(0).nullable().meta(require_sleep.readingMeta("duration")),
73514
+ /** How long the pump rests per cycle, in seconds. */
73515
+ pumpOffTime: zod.z.number().min(0).nullable().meta(require_sleep.readingMeta("duration")),
73516
+ /** Water drunk in the last drink, in litres. */
73517
+ lastDrink: zod.z.number().min(0).nullable().meta(require_sleep.readingMeta("volume")),
73518
+ /** Water drunk in the current hour, in litres. */
73519
+ currentHourDrink: zod.z.number().min(0).nullable().meta(require_sleep.readingMeta("volume")),
73520
+ /** Water drunk over the last 24 hours (rolling), in litres. */
73521
+ last24hDrink: zod.z.number().min(0).nullable().meta(require_sleep.readingMeta("volume")),
73522
+ /** Water drunk since local midnight, in litres. */
73523
+ todayDrink: zod.z.number().min(0).nullable().meta(require_sleep.readingMeta("volume")),
73524
+ /** Decoded fault message. Null means healthy; a non-null string is an active fault. */
73525
+ error: zod.z.string().nullable(),
73526
+ /** Ms epoch when the slice was last refreshed. */
73527
+ lastFetchedAt: zod.z.number()
73528
+ });
73529
+ var DeviceIdSchema = zod.z.number().int().nonnegative();
73530
+ var MinutesSchema = zod.z.number().int().min(0);
73531
+ var petFountainCapability = {
73532
+ name: "pet-fountain",
73533
+ scope: "device",
73534
+ deviceNative: true,
73535
+ mode: "singleton",
73536
+ deviceTypes: [require_err_msg.DeviceType.PetFountain],
73537
+ methods: {
73538
+ /** Start or stop the pump. */
73539
+ setPump: require_sleep.method(zod.z.object({
73540
+ deviceId: DeviceIdSchema,
73541
+ on: zod.z.boolean()
73542
+ }), zod.z.void(), {
73543
+ kind: "mutation",
73544
+ auth: "protected"
73545
+ }),
73546
+ /** Switch the on/off duty cycle on or off (off = the pump runs continuously). */
73547
+ setPumpInterval: require_sleep.method(zod.z.object({
73548
+ deviceId: DeviceIdSchema,
73549
+ on: zod.z.boolean()
73550
+ }), zod.z.void(), {
73551
+ kind: "mutation",
73552
+ auth: "protected"
73553
+ }),
73554
+ /** Set how many minutes the pump runs per cycle. */
73555
+ setPumpOnMinutes: require_sleep.method(zod.z.object({
73556
+ deviceId: DeviceIdSchema,
73557
+ minutes: MinutesSchema
73558
+ }), zod.z.void(), {
73559
+ kind: "mutation",
73560
+ auth: "protected"
73561
+ }),
73562
+ /** Set how many minutes the pump rests per cycle. */
73563
+ setPumpOffMinutes: require_sleep.method(zod.z.object({
73564
+ deviceId: DeviceIdSchema,
73565
+ minutes: MinutesSchema
73566
+ }), zod.z.void(), {
73567
+ kind: "mutation",
73568
+ auth: "protected"
73569
+ }),
73570
+ /** Clear the rolling daily consumption counters on the device. */
73571
+ resetDailyConsumption: require_sleep.method(zod.z.object({ deviceId: DeviceIdSchema }), zod.z.void(), {
73572
+ kind: "mutation",
73573
+ auth: "protected"
73574
+ })
73575
+ },
73576
+ status: {
73577
+ schema: PetFountainStatusSchema,
73578
+ kind: "push"
73579
+ },
73580
+ /** Runtime-state slice — mirrored by the kernel; the hero card reads it whole. */
73581
+ runtimeState: PetFountainStatusSchema,
73582
+ /**
73583
+ * Runtime-state durability: **session** — live appliance state re-published on connect.
73584
+ *
73585
+ * See `RuntimeStateDurability`. Enforced by
73586
+ * `scripts/check-runtime-state-durability.ts`.
73587
+ */
73588
+ durability: "session",
73589
+ /** History (D850): recorded series. */
73590
+ history: { fields: {
73591
+ waterLevel: {
73592
+ kind: "gauge",
73593
+ unit: "%",
73594
+ deadband: 1
73595
+ },
73596
+ waterVolume: {
73597
+ kind: "gauge",
73598
+ unit: "L"
73599
+ },
73600
+ todayDrink: {
73601
+ kind: "gauge",
73602
+ unit: "L"
73603
+ },
73604
+ last24hDrink: {
73605
+ kind: "gauge",
73606
+ unit: "L"
73607
+ }
73608
+ } }
73609
+ };
73610
+ //#endregion
73153
73611
  //#region src/capabilities/plate-gallery.cap.ts
73154
73612
  var VehicleSchema = zod.z.object({
73155
73613
  id: zod.z.string(),
@@ -83042,7 +83500,7 @@ var ROOM_EXAMPLE_MOTION_SOURCES = [{
83042
83500
  addonId: "provider-homeassistant",
83043
83501
  stableId: "binary_sensor.living_room_presence"
83044
83502
  }];
83045
- function from(source, cap, fieldPath) {
83503
+ function from$1(source, cap, fieldPath) {
83046
83504
  return {
83047
83505
  kind: "from",
83048
83506
  source,
@@ -83051,7 +83509,7 @@ function from(source, cap, fieldPath) {
83051
83509
  };
83052
83510
  }
83053
83511
  function named(prefix, sources, cap, fieldPath) {
83054
- return Object.fromEntries(sources.map((s, i) => [`${prefix}${i + 1}`, from(s, cap, fieldPath)]));
83512
+ return Object.fromEntries(sources.map((s, i) => [`${prefix}${i + 1}`, from$1(s, cap, fieldPath)]));
83055
83513
  }
83056
83514
  function roomCompositionExample(input) {
83057
83515
  const t = named("t", input.temperatureSources, "temperature-sensor", "temperature");
@@ -83139,7 +83597,7 @@ function feederExampleSources(brokerId) {
83139
83597
  };
83140
83598
  }
83141
83599
  function feederCustomizationExample(sources) {
83142
- const days = { d: from(sources.desiccantDays, "enum-sensor", "value") };
83600
+ const days = { d: from$1(sources.desiccantDays, "enum-sensor", "value") };
83143
83601
  return require_composition.CompositionSchema.parse({
83144
83602
  target: {
83145
83603
  kind: "existing",
@@ -83187,7 +83645,7 @@ function feederCustomizationExample(sources) {
83187
83645
  level: {
83188
83646
  kind: "expression",
83189
83647
  expr: "s == \"low\" ? 15 : (s == \"normal\" ? 100 : null)",
83190
- bindings: { s: from(sources.batteryStatus, "enum-sensor", "value") }
83648
+ bindings: { s: from$1(sources.batteryStatus, "enum-sensor", "value") }
83191
83649
  },
83192
83650
  binary: {
83193
83651
  kind: "expression",
@@ -83197,7 +83655,7 @@ function feederCustomizationExample(sources) {
83197
83655
  charging: {
83198
83656
  kind: "expression",
83199
83657
  expr: "c ? \"dc\" : \"unknown\"",
83200
- bindings: { c: from(sources.batteryCharging, "binary", "on") }
83658
+ bindings: { c: from$1(sources.batteryCharging, "binary", "on") }
83201
83659
  },
83202
83660
  sleeping: {
83203
83661
  kind: "expression",
@@ -83283,6 +83741,158 @@ function convertPresetFeature(input) {
83283
83741
  };
83284
83742
  }
83285
83743
  //#endregion
83744
+ //#region src/composition/presets/pet-fountain.ts
83745
+ /**
83746
+ * The preset for a pet water fountain built from loose entities (D659, D967).
83747
+ *
83748
+ * A fountain reaches CamStack as a container of single-purpose entities: a
83749
+ * pump switch, a cycle switch, two number boxes, a handful of sensors that
83750
+ * report millilitres as text, and a few buttons. This preset names the
83751
+ * `pet-fountain` cap's fields over those entities, forwards each of its five
83752
+ * methods to ONE entity method, and adds the filter as a resettable
83753
+ * `consumables` item.
83754
+ *
83755
+ * Stored quantities are base units (D944): litres for volume, seconds for
83756
+ * duration. The cycle times cannot be read back (see `pumpOnTime` below). The sensors are `enum-sensor`s, so their value is a STRING:
83757
+ * `number(x)` reads it and anything that is not a number stays `null`, never
83758
+ * 0 (D393). The caller supplies the sources — it knows which entity is which;
83759
+ * the preset guesses no entity id.
83760
+ */
83761
+ var UNKNOWN = {
83762
+ kind: "expression",
83763
+ expr: "null",
83764
+ bindings: {}
83765
+ };
83766
+ var NOW = {
83767
+ kind: "expression",
83768
+ expr: "now",
83769
+ bindings: {}
83770
+ };
83771
+ function from(source, cap, fieldPath) {
83772
+ return {
83773
+ kind: "from",
83774
+ source,
83775
+ cap,
83776
+ fieldPath
83777
+ };
83778
+ }
83779
+ function over(name, binding, expr) {
83780
+ return {
83781
+ kind: "expression",
83782
+ expr,
83783
+ bindings: { [name]: binding }
83784
+ };
83785
+ }
83786
+ /** A text reading in millilitres, as litres; unreadable stays unknown. */
83787
+ function millilitresAsLitres(source) {
83788
+ return over("v", from(source, "enum-sensor", "value"), "number(v) == null ? null : number(v) / 1000");
83789
+ }
83790
+ function percentOf(source) {
83791
+ return over("l", from(source, "enum-sensor", "value"), "number(l)");
83792
+ }
83793
+ function filterFeature(filter) {
83794
+ const days = { d: from(filter.daysRemaining, "enum-sensor", "value") };
83795
+ return {
83796
+ kind: "fields",
83797
+ cap: "consumables",
83798
+ fields: {
83799
+ lastChangedAt: NOW,
83800
+ lastFetchedAt: NOW
83801
+ },
83802
+ items: { filter: {
83803
+ label: "Filter",
83804
+ fields: {
83805
+ "remaining.value": {
83806
+ kind: "expression",
83807
+ expr: "number(d)",
83808
+ bindings: days
83809
+ },
83810
+ "remaining.kind": {
83811
+ kind: "expression",
83812
+ expr: "\"duration\"",
83813
+ bindings: {}
83814
+ },
83815
+ status: {
83816
+ kind: "expression",
83817
+ expr: "number(d) == null ? null : (number(d) <= 0 ? \"replace\" : \"ok\")",
83818
+ bindings: days
83819
+ }
83820
+ },
83821
+ reset: {
83822
+ kind: "forward",
83823
+ source: filter.changed,
83824
+ cap: "button",
83825
+ method: "press"
83826
+ }
83827
+ } }
83828
+ };
83829
+ }
83830
+ function petFountainFromEntitiesComposition(sources) {
83831
+ const minutes = {
83832
+ kind: "numeric",
83833
+ value: { $input: "minutes" }
83834
+ };
83835
+ return require_composition.CompositionSchema.parse({
83836
+ target: {
83837
+ kind: "new",
83838
+ type: require_err_msg.DeviceType.PetFountain
83839
+ },
83840
+ features: [{
83841
+ kind: "fields",
83842
+ cap: "pet-fountain",
83843
+ fields: {
83844
+ waterLevel: percentOf(sources.waterLevel),
83845
+ waterVolume: millilitresAsLitres(sources.waterVolume),
83846
+ empty: over("l", from(sources.waterLevel, "enum-sensor", "value"), "number(l) == null ? null : number(l) <= 0"),
83847
+ pumpRunning: from(sources.pump, "switch", "on"),
83848
+ pumpIntervalEnabled: from(sources.pumpInterval, "switch", "on"),
83849
+ pumpOnTime: UNKNOWN,
83850
+ pumpOffTime: UNKNOWN,
83851
+ lastDrink: millilitresAsLitres(sources.lastDrink),
83852
+ currentHourDrink: millilitresAsLitres(sources.currentHourDrink),
83853
+ last24hDrink: millilitresAsLitres(sources.last24hDrink),
83854
+ todayDrink: millilitresAsLitres(sources.todayDrink),
83855
+ error: UNKNOWN,
83856
+ lastFetchedAt: NOW
83857
+ },
83858
+ commands: {
83859
+ setPump: {
83860
+ kind: "forward",
83861
+ source: sources.pump,
83862
+ cap: "switch",
83863
+ method: "setState"
83864
+ },
83865
+ setPumpInterval: {
83866
+ kind: "forward",
83867
+ source: sources.pumpInterval,
83868
+ cap: "switch",
83869
+ method: "setState"
83870
+ },
83871
+ setPumpOnMinutes: {
83872
+ kind: "forward",
83873
+ source: sources.pumpOnMinutes,
83874
+ cap: "control",
83875
+ method: "setValue",
83876
+ args: { control: minutes }
83877
+ },
83878
+ setPumpOffMinutes: {
83879
+ kind: "forward",
83880
+ source: sources.pumpOffMinutes,
83881
+ cap: "control",
83882
+ method: "setValue",
83883
+ args: { control: minutes }
83884
+ },
83885
+ resetDailyConsumption: {
83886
+ kind: "forward",
83887
+ source: sources.clearDaily,
83888
+ cap: "button",
83889
+ method: "press"
83890
+ }
83891
+ }
83892
+ }, ...sources.filter === void 0 ? [] : [filterFeature(sources.filter)]]
83893
+ });
83894
+ }
83895
+ //#endregion
83286
83896
  //#region src/composition/cards/classify-field.ts
83287
83897
  function fromSources(binding) {
83288
83898
  return {
@@ -83532,6 +84142,7 @@ var CAP_CUSTOMIZE_CATALOG = {
83532
84142
  require_err_msg.DeviceType.Container,
83533
84143
  require_err_msg.DeviceType.Image,
83534
84144
  require_err_msg.DeviceType.PetFeeder,
84145
+ require_err_msg.DeviceType.PetFountain,
83535
84146
  require_err_msg.DeviceType.Dishwasher,
83536
84147
  require_err_msg.DeviceType.Washer,
83537
84148
  require_err_msg.DeviceType.Dryer
@@ -86044,6 +86655,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
86044
86655
  notifier: notifierCapability,
86045
86656
  numericSensor: numericSensorCapability,
86046
86657
  petFeeder: petFeederCapability,
86658
+ petFountain: petFountainCapability,
86047
86659
  powerMeter: powerMeterCapability,
86048
86660
  presence: presenceCapability,
86049
86661
  pressureSensor: pressureSensorCapability,
@@ -88032,6 +88644,7 @@ var DEVICE_TYPE_CONTROL_KIND = {
88032
88644
  [require_err_msg.DeviceType.Container]: "dummy",
88033
88645
  [require_err_msg.DeviceType.Image]: "image",
88034
88646
  [require_err_msg.DeviceType.PetFeeder]: "pet-feeder",
88647
+ [require_err_msg.DeviceType.PetFountain]: "pet-fountain",
88035
88648
  [require_err_msg.DeviceType.Dishwasher]: "dishwasher",
88036
88649
  [require_err_msg.DeviceType.Washer]: "dummy",
88037
88650
  [require_err_msg.DeviceType.Dryer]: "dummy"
@@ -90074,6 +90687,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
90074
90687
  access: "create",
90075
90688
  role: "admin"
90076
90689
  },
90690
+ "deviceExport.exposeEntity": {
90691
+ capName: "device-export",
90692
+ capScope: "system",
90693
+ addonId: null,
90694
+ access: "create",
90695
+ role: "admin"
90696
+ },
90077
90697
  "deviceExport.getDeviceLiveContribution": {
90078
90698
  capName: "device-export",
90079
90699
  capScope: "system",
@@ -90102,6 +90722,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
90102
90722
  access: "view",
90103
90723
  role: "admin"
90104
90724
  },
90725
+ "deviceExport.listExposedEntities": {
90726
+ capName: "device-export",
90727
+ capScope: "system",
90728
+ addonId: null,
90729
+ access: "view",
90730
+ role: "admin"
90731
+ },
90105
90732
  "deviceExport.listSupportedDeviceKinds": {
90106
90733
  capName: "device-export",
90107
90734
  capScope: "system",
@@ -90109,6 +90736,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
90109
90736
  access: "view",
90110
90737
  role: "admin"
90111
90738
  },
90739
+ "deviceExport.listSupportedEntityKinds": {
90740
+ capName: "device-export",
90741
+ capScope: "system",
90742
+ addonId: null,
90743
+ access: "view",
90744
+ role: "admin"
90745
+ },
90112
90746
  "deviceExport.unexposeDevice": {
90113
90747
  capName: "device-export",
90114
90748
  capScope: "system",
@@ -90116,6 +90750,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
90116
90750
  access: "create",
90117
90751
  role: "admin"
90118
90752
  },
90753
+ "deviceExport.unexposeEntity": {
90754
+ capName: "device-export",
90755
+ capScope: "system",
90756
+ addonId: null,
90757
+ access: "create",
90758
+ role: "admin"
90759
+ },
90119
90760
  "deviceExtension.listCandidates": {
90120
90761
  capName: "device-extension",
90121
90762
  capScope: "device",
@@ -93581,6 +94222,48 @@ var METHOD_ACCESS_MAP = Object.freeze({
93581
94222
  access: "create",
93582
94223
  role: "viewer"
93583
94224
  },
94225
+ "petFountain.getStatus": {
94226
+ capName: "pet-fountain",
94227
+ capScope: "device",
94228
+ addonId: null,
94229
+ access: "view",
94230
+ role: "viewer"
94231
+ },
94232
+ "petFountain.resetDailyConsumption": {
94233
+ capName: "pet-fountain",
94234
+ capScope: "device",
94235
+ addonId: null,
94236
+ access: "delete",
94237
+ role: "viewer"
94238
+ },
94239
+ "petFountain.setPump": {
94240
+ capName: "pet-fountain",
94241
+ capScope: "device",
94242
+ addonId: null,
94243
+ access: "create",
94244
+ role: "viewer"
94245
+ },
94246
+ "petFountain.setPumpInterval": {
94247
+ capName: "pet-fountain",
94248
+ capScope: "device",
94249
+ addonId: null,
94250
+ access: "create",
94251
+ role: "viewer"
94252
+ },
94253
+ "petFountain.setPumpOffMinutes": {
94254
+ capName: "pet-fountain",
94255
+ capScope: "device",
94256
+ addonId: null,
94257
+ access: "create",
94258
+ role: "viewer"
94259
+ },
94260
+ "petFountain.setPumpOnMinutes": {
94261
+ capName: "pet-fountain",
94262
+ capScope: "device",
94263
+ addonId: null,
94264
+ access: "create",
94265
+ role: "viewer"
94266
+ },
93584
94267
  "pipelineAnalytics.addRetrainFrameFromRecording": {
93585
94268
  capName: "pipeline-analytics",
93586
94269
  capScope: "device",
@@ -95982,6 +96665,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
95982
96665
  access: "create",
95983
96666
  role: "admin"
95984
96667
  },
96668
+ "serverManagement.getLaunchOverrides": {
96669
+ capName: "server-management",
96670
+ capScope: "system",
96671
+ addonId: null,
96672
+ access: "view",
96673
+ role: "admin"
96674
+ },
95985
96675
  "serverManagement.getServerPackageStatus": {
95986
96676
  capName: "server-management",
95987
96677
  capScope: "system",
@@ -96003,6 +96693,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
96003
96693
  access: "create",
96004
96694
  role: "admin"
96005
96695
  },
96696
+ "serverManagement.setLaunchOverrides": {
96697
+ capName: "server-management",
96698
+ capScope: "system",
96699
+ addonId: null,
96700
+ access: "create",
96701
+ role: "admin"
96702
+ },
96006
96703
  "settingsStore.aggregate": {
96007
96704
  capName: "settings-store",
96008
96705
  capScope: "system",
@@ -98190,6 +98887,7 @@ var KNOWN_CAP_NAMES = [
98190
98887
  "osd",
98191
98888
  "osd-manager",
98192
98889
  "pet-feeder",
98890
+ "pet-fountain",
98193
98891
  "pipeline-analytics",
98194
98892
  "pipeline-executor",
98195
98893
  "pipeline-orchestrator",
@@ -98321,6 +99019,7 @@ var DEVICE_CAP_NAMES = [
98321
99019
  "numeric-sensor",
98322
99020
  "osd",
98323
99021
  "pet-feeder",
99022
+ "pet-fountain",
98324
99023
  "pipeline-analytics",
98325
99024
  "power-meter",
98326
99025
  "presence",
@@ -100460,6 +101159,100 @@ var APPLIANCE_CAP_VOCABULARY = {
100460
101159
  }
100461
101160
  ]
100462
101161
  },
101162
+ "pet-fountain": {
101163
+ scope: "device",
101164
+ noun: t("Pet fountain", "Fontana per animali"),
101165
+ actions: [
101166
+ {
101167
+ id: "petFountain.pumpOn",
101168
+ method: "setPump",
101169
+ label: t("Pump on", "Pompa accesa"),
101170
+ phrase: t("Start the pump of {device}", "Avvia la pompa di {device}"),
101171
+ icon: "play",
101172
+ args: { on: true }
101173
+ },
101174
+ {
101175
+ id: "petFountain.pumpOff",
101176
+ method: "setPump",
101177
+ label: t("Pump off", "Pompa spenta"),
101178
+ phrase: t("Stop the pump of {device}", "Ferma la pompa di {device}"),
101179
+ icon: "square",
101180
+ args: { on: false }
101181
+ },
101182
+ {
101183
+ id: "petFountain.intervalOn",
101184
+ method: "setPumpInterval",
101185
+ label: t("Pump cycle on", "Ciclo pompa attivo"),
101186
+ phrase: t("Run the pump of {device} on its on/off cycle", "Fai lavorare la pompa di {device} a ciclo acceso/spento"),
101187
+ icon: "timer",
101188
+ args: { on: true }
101189
+ },
101190
+ {
101191
+ id: "petFountain.intervalOff",
101192
+ method: "setPumpInterval",
101193
+ label: t("Pump cycle off", "Ciclo pompa spento"),
101194
+ phrase: t("Run the pump of {device} continuously", "Fai lavorare la pompa di {device} in continuo"),
101195
+ icon: "timer-off",
101196
+ args: { on: false }
101197
+ },
101198
+ {
101199
+ id: "petFountain.pumpOnMinutes",
101200
+ method: "setPumpOnMinutes",
101201
+ label: t("Set pump run time", "Imposta il tempo di lavoro della pompa"),
101202
+ phrase: t("Set the pump of {device} to run {minutes} minutes per cycle", "Imposta la pompa di {device} a lavorare {minutes} minuti per ciclo"),
101203
+ icon: "timer",
101204
+ openArgs: [{
101205
+ key: "minutes",
101206
+ label: t("Minutes", "Minuti"),
101207
+ offer: {
101208
+ kind: "number",
101209
+ min: 0,
101210
+ step: 1,
101211
+ unit: "min"
101212
+ }
101213
+ }]
101214
+ },
101215
+ {
101216
+ id: "petFountain.pumpOffMinutes",
101217
+ method: "setPumpOffMinutes",
101218
+ label: t("Set pump rest time", "Imposta il tempo di riposo della pompa"),
101219
+ phrase: t("Set the pump of {device} to rest {minutes} minutes per cycle", "Imposta la pompa di {device} a riposare {minutes} minuti per ciclo"),
101220
+ icon: "timer-off",
101221
+ openArgs: [{
101222
+ key: "minutes",
101223
+ label: t("Minutes", "Minuti"),
101224
+ offer: {
101225
+ kind: "number",
101226
+ min: 0,
101227
+ step: 1,
101228
+ unit: "min"
101229
+ }
101230
+ }]
101231
+ },
101232
+ {
101233
+ id: "petFountain.resetDailyConsumption",
101234
+ method: "resetDailyConsumption",
101235
+ label: t("Clear daily consumption", "Azzera il consumo giornaliero"),
101236
+ phrase: t("Clear the daily water consumption of {device}", "Azzera il consumo giornaliero di acqua di {device}"),
101237
+ icon: "rotate-ccw"
101238
+ }
101239
+ ],
101240
+ state: [{
101241
+ kind: "boolean",
101242
+ field: "empty",
101243
+ label: t("Tank empty", "Serbatoio vuoto"),
101244
+ whenTrue: t("When the tank of {device} is empty", "Quando il serbatoio di {device} è vuoto"),
101245
+ whenFalse: t("When the tank of {device} is refilled", "Quando il serbatoio di {device} viene riempito"),
101246
+ icon: "droplet"
101247
+ }, {
101248
+ kind: "numeric",
101249
+ field: "waterLevel",
101250
+ quantity: t("the water level", "il livello dell’acqua"),
101251
+ label: t("Water level", "Livello dell’acqua"),
101252
+ unit: "%",
101253
+ icon: "droplets"
101254
+ }]
101255
+ },
100463
101256
  "media-player": {
100464
101257
  scope: "device",
100465
101258
  noun: t("Media player", "Lettore multimediale"),
@@ -102522,6 +103315,7 @@ var DEVICE_TYPE_DO_CAPS = {
102522
103315
  [require_err_msg.DeviceType.Container]: [],
102523
103316
  [require_err_msg.DeviceType.Image]: [],
102524
103317
  [require_err_msg.DeviceType.PetFeeder]: ["pet-feeder"],
103318
+ [require_err_msg.DeviceType.PetFountain]: ["pet-fountain"],
102525
103319
  [require_err_msg.DeviceType.Dishwasher]: ["dishwasher"],
102526
103320
  [require_err_msg.DeviceType.Washer]: [],
102527
103321
  [require_err_msg.DeviceType.Dryer]: []
@@ -104140,6 +104934,7 @@ var CAP_NAMES_WITH_STATUS = [
104140
104934
  "numeric-sensor",
104141
104935
  "osd",
104142
104936
  "pet-feeder",
104937
+ "pet-fountain",
104143
104938
  "power-meter",
104144
104939
  "presence",
104145
104940
  "pressure-sensor",
@@ -104294,6 +105089,7 @@ var CAPABILITY_NAMES = {
104294
105089
  osd: "osd",
104295
105090
  osdManager: "osd-manager",
104296
105091
  petFeeder: "pet-feeder",
105092
+ petFountain: "pet-fountain",
104297
105093
  pipelineAnalytics: "pipeline-analytics",
104298
105094
  pipelineExecutor: "pipeline-executor",
104299
105095
  pipelineOrchestrator: "pipeline-orchestrator",
@@ -104866,6 +105662,10 @@ var CAPABILITY_ROUTER_KEYS = [
104866
105662
  key: "petFeeder",
104867
105663
  name: "pet-feeder"
104868
105664
  },
105665
+ {
105666
+ key: "petFountain",
105667
+ name: "pet-fountain"
105668
+ },
104869
105669
  {
104870
105670
  key: "pipelineAnalytics",
104871
105671
  name: "pipeline-analytics"
@@ -105141,7 +105941,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
105141
105941
  actionSourceCapability,
105142
105942
  actionsCapability,
105143
105943
  addonPagesCapability,
105144
- addonPagesSourceCapability,
105944
+ require_sleep.addonPagesSourceCapability,
105145
105945
  addonRoutesCapability,
105146
105946
  addonSettingsCapability,
105147
105947
  addonWidgetsCapability,
@@ -105263,6 +106063,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
105263
106063
  osdCapability,
105264
106064
  osdManagerCapability,
105265
106065
  petFeederCapability,
106066
+ petFountainCapability,
105266
106067
  pipelineAnalyticsCapability,
105267
106068
  pipelineExecutorCapability,
105268
106069
  pipelineOrchestratorCapability,
@@ -106841,6 +107642,36 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
106841
107642
  form: "single",
106842
107643
  optional: false
106843
107644
  }],
107645
+ "petFountain.getStatus": [{
107646
+ name: "deviceId",
107647
+ form: "single",
107648
+ optional: false
107649
+ }],
107650
+ "petFountain.resetDailyConsumption": [{
107651
+ name: "deviceId",
107652
+ form: "single",
107653
+ optional: false
107654
+ }],
107655
+ "petFountain.setPump": [{
107656
+ name: "deviceId",
107657
+ form: "single",
107658
+ optional: false
107659
+ }],
107660
+ "petFountain.setPumpInterval": [{
107661
+ name: "deviceId",
107662
+ form: "single",
107663
+ optional: false
107664
+ }],
107665
+ "petFountain.setPumpOffMinutes": [{
107666
+ name: "deviceId",
107667
+ form: "single",
107668
+ optional: false
107669
+ }],
107670
+ "petFountain.setPumpOnMinutes": [{
107671
+ name: "deviceId",
107672
+ form: "single",
107673
+ optional: false
107674
+ }],
106844
107675
  "pipelineAnalytics.addRetrainFrameFromRecording": [{
106845
107676
  name: "deviceId",
106846
107677
  form: "single",
@@ -108398,7 +109229,7 @@ function getCapsByProviderKind(kind) {
108398
109229
  //#region src/generated/runtime-state-durability.ts
108399
109230
  /**
108400
109231
  * Per-cap runtime-state policy, projected from every `*.cap.ts` that declares
108401
- * `runtimeState`. 76 caps: 39 `restored`, 37 `session`.
109232
+ * `runtimeState`. 77 caps: 39 `restored`, 38 `session`.
108402
109233
  *
108403
109234
  * A cap absent from this map has no runtime-state slice at all. A cap PRESENT
108404
109235
  * with `durability: 'session'` has one and has decided not to keep it.
@@ -108604,6 +109435,10 @@ var RUNTIME_STATE_POLICY = {
108604
109435
  durability: "session",
108605
109436
  volatileFields: []
108606
109437
  },
109438
+ "pet-fountain": {
109439
+ durability: "session",
109440
+ volatileFields: []
109441
+ },
108607
109442
  "power-meter": {
108608
109443
  durability: "restored",
108609
109444
  volatileFields: ["lastFetchedAt"]
@@ -109591,7 +110426,11 @@ function createSystemProxy(api) {
109591
110426
  listSupportedDeviceKinds: (input) => dispatch("deviceExport", "listSupportedDeviceKinds", "query", input),
109592
110427
  listExposedDevices: (input) => dispatch("deviceExport", "listExposedDevices", "query", input),
109593
110428
  exposeDevice: (input) => dispatch("deviceExport", "exposeDevice", "mutation", input),
109594
- unexposeDevice: (input) => dispatch("deviceExport", "unexposeDevice", "mutation", input)
110429
+ unexposeDevice: (input) => dispatch("deviceExport", "unexposeDevice", "mutation", input),
110430
+ listSupportedEntityKinds: (input) => dispatch("deviceExport", "listSupportedEntityKinds", "query", input),
110431
+ listExposedEntities: (input) => dispatch("deviceExport", "listExposedEntities", "query", input),
110432
+ exposeEntity: (input) => dispatch("deviceExport", "exposeEntity", "mutation", input),
110433
+ unexposeEntity: (input) => dispatch("deviceExport", "unexposeEntity", "mutation", input)
109595
110434
  },
109596
110435
  deviceHistory: {
109597
110436
  listNodeSeries: (input) => dispatch("deviceHistory", "listNodeSeries", "query", input),
@@ -110041,7 +110880,9 @@ function createSystemProxy(api) {
110041
110880
  checkServerUpdate: (input) => dispatch("serverManagement", "checkServerUpdate", "mutation", input),
110042
110881
  applyServerUpdate: (input) => dispatch("serverManagement", "applyServerUpdate", "mutation", input),
110043
110882
  rollbackServerUpdate: (input) => dispatch("serverManagement", "rollbackServerUpdate", "mutation", input),
110044
- restartServer: (input) => dispatch("serverManagement", "restartServer", "mutation", input)
110883
+ restartServer: (input) => dispatch("serverManagement", "restartServer", "mutation", input),
110884
+ getLaunchOverrides: (input) => dispatch("serverManagement", "getLaunchOverrides", "query", input),
110885
+ setLaunchOverrides: (input) => dispatch("serverManagement", "setLaunchOverrides", "mutation", input)
110045
110886
  },
110046
110887
  settingsStore: {
110047
110888
  get: (input) => dispatch("settingsStore", "get", "query", input),
@@ -121014,7 +121855,7 @@ exports.AddBrokerInputSchema = AddBrokerInputSchema;
121014
121855
  exports.AddRetrainFrameFromRecordingResultSchema = AddRetrainFrameFromRecordingResultSchema;
121015
121856
  exports.AddonAutoUpdateSchema = AddonAutoUpdateSchema;
121016
121857
  exports.AddonListItemSchema = AddonListItemSchema;
121017
- exports.AddonPageDeclarationSchema = AddonPageDeclarationSchema;
121858
+ exports.AddonPageDeclarationSchema = require_sleep.AddonPageDeclarationSchema;
121018
121859
  exports.AddonPageInfoSchema = AddonPageInfoSchema;
121019
121860
  exports.AddonRoutesScopeSchema = AddonRoutesScopeSchema;
121020
121861
  exports.AdminLinkTargetEnum = AdminLinkTargetEnum;
@@ -121345,6 +122186,7 @@ exports.CompositionFieldSourceSchema = require_composition.CompositionFieldSourc
121345
122186
  exports.CompositionFieldStateKindSchema = CompositionFieldStateKindSchema;
121346
122187
  exports.CompositionFieldStateSchema = CompositionFieldStateSchema;
121347
122188
  exports.CompositionFieldsFeatureSchema = require_composition.CompositionFieldsFeatureSchema;
122189
+ exports.CompositionForwardArgSchema = require_composition.CompositionForwardArgSchema;
121348
122190
  exports.CompositionForwardCommandSchema = require_composition.CompositionForwardCommandSchema;
121349
122191
  exports.CompositionFromSourceSchema = require_composition.CompositionFromSourceSchema;
121350
122192
  exports.CompositionGetReadSchema = CompositionGetReadSchema;
@@ -121471,6 +122313,7 @@ exports.DEVICE_HISTORY_ENERGY_KEY = DEVICE_HISTORY_ENERGY_KEY;
121471
122313
  exports.DEVICE_HISTORY_ORPHAN_DELETE_MAX = DEVICE_HISTORY_ORPHAN_DELETE_MAX;
121472
122314
  exports.DEVICE_HISTORY_ORPHAN_LIST_MAX = DEVICE_HISTORY_ORPHAN_LIST_MAX;
121473
122315
  exports.DEVICE_HISTORY_QUERY_MANY_MAX_ITEMS = DEVICE_HISTORY_QUERY_MANY_MAX_ITEMS;
122316
+ exports.DEVICE_ID_FIELD = DEVICE_ID_FIELD;
121474
122317
  exports.DEVICE_NONCE_TTL_MS = DEVICE_NONCE_TTL_MS;
121475
122318
  exports.DEVICE_PROFILES = DEVICE_PROFILES;
121476
122319
  exports.DEVICE_RAMP = DEVICE_RAMP;
@@ -121653,6 +122496,8 @@ exports.ExportClipSubjectSchema = ExportClipSubjectSchema;
121653
122496
  exports.ExportDenseRangeSchema = ExportDenseRangeSchema;
121654
122497
  exports.ExportDenseSchema = ExportDenseSchema;
121655
122498
  exports.ExportDownloadSchema = ExportDownloadSchema;
122499
+ exports.ExportEntityKindSchema = ExportEntityKindSchema;
122500
+ exports.ExportEntityRefSchema = ExportEntityRefSchema;
121656
122501
  exports.ExportFootageSubjectSchema = ExportFootageSubjectSchema;
121657
122502
  exports.ExportOptionsSchema = ExportOptionsSchema;
121658
122503
  exports.ExportRecordSchema = ExportRecordSchema;
@@ -121662,7 +122507,9 @@ exports.ExportSpeedSchema = ExportSpeedSchema;
121662
122507
  exports.ExportStateSchema = ExportStateSchema;
121663
122508
  exports.ExportSubjectSchema = ExportSubjectSchema;
121664
122509
  exports.ExportTimelapseSchema = ExportTimelapseSchema;
122510
+ exports.ExposeEntityInputSchema = ExposeEntityInputSchema;
121665
122511
  exports.ExposedDeviceSchema = ExposedDeviceSchema;
122512
+ exports.ExposedEntitySchema = ExposedEntitySchema;
121666
122513
  exports.ExposureModeSchema = ExposureModeSchema;
121667
122514
  exports.ExpressionBindingSourceSchema = ExpressionBindingSourceSchema;
121668
122515
  exports.ExpressionEvalError = require_composition.ExpressionEvalError;
@@ -121698,6 +122545,7 @@ exports.FEEDER_EXAMPLE_ADDON_ID = FEEDER_EXAMPLE_ADDON_ID;
121698
122545
  exports.FEEDER_EXAMPLE_HA_DEVICE_ID = FEEDER_EXAMPLE_HA_DEVICE_ID;
121699
122546
  exports.FFMPEG_PATH_ENV = require_composition.FFMPEG_PATH_ENV;
121700
122547
  exports.FIRST_LEVEL_MACRO_CLASSES = FIRST_LEVEL_MACRO_CLASSES;
122548
+ exports.FORWARD_INPUT_KEY = require_composition.FORWARD_INPUT_KEY;
121701
122549
  exports.FULL_IMAGE_BBOX = FULL_IMAGE_BBOX;
121702
122550
  exports.FailureContributionSchema = FailureContributionSchema;
121703
122551
  exports.FailureCounters = FailureCounters;
@@ -122410,6 +123258,7 @@ exports.PcmSampleFormatSchema = PcmSampleFormatSchema;
122410
123258
  exports.PeerBytesTicketSchema = require_sleep.PeerBytesTicketSchema;
122411
123259
  exports.PerScopeBreakdownSchema = PerScopeBreakdownSchema;
122412
123260
  exports.PetFeederStatusSchema = PetFeederStatusSchema;
123261
+ exports.PetFountainStatusSchema = PetFountainStatusSchema;
122413
123262
  exports.PickStreamPreferencesSchema = PickStreamPreferencesSchema;
122414
123263
  exports.PickStreamRequirementsSchema = PickStreamRequirementsSchema;
122415
123264
  exports.PickedCamStreamSchema = PickedCamStreamSchema;
@@ -122417,8 +123266,8 @@ exports.PipelineAssignmentSchema = PipelineAssignmentSchema;
122417
123266
  exports.PipelineDefaultStepSchema = PipelineDefaultStepSchema;
122418
123267
  exports.PipelineEngineChoiceSchema = PipelineEngineChoiceSchema;
122419
123268
  exports.PipelineModelFormatStatusSchema = PipelineModelFormatStatusSchema;
122420
- exports.PipelineRunResultBridge = PipelineRunResultBridge;
122421
- exports.PipelineStepInputSchema = PipelineStepInputSchema;
123269
+ exports.PipelineRunResultBridge = require_sleep.PipelineRunResultBridge;
123270
+ exports.PipelineStepInputSchema = require_sleep.PipelineStepInputSchema;
122422
123271
  exports.PipelineValidationIssueSchema = PipelineValidationIssueSchema;
122423
123272
  exports.PipelineValidationResultSchema = PipelineValidationResultSchema;
122424
123273
  exports.PlaceholderReasonSchema = PlaceholderReasonSchema;
@@ -123007,6 +123856,7 @@ exports.UNIT_TABLE = UNIT_TABLE;
123007
123856
  exports.UNLOCK_CONFIRMATION = UNLOCK_CONFIRMATION;
123008
123857
  exports.USE_ROOT_MODEL_DEFAULTS = USE_ROOT_MODEL_DEFAULTS;
123009
123858
  exports.USE_ROOT_MODEL_SETTING_KEY = USE_ROOT_MODEL_SETTING_KEY;
123859
+ exports.UnexposeEntityInputSchema = UnexposeEntityInputSchema;
123010
123860
  exports.UnifiedBrokerInfoSchema = BrokerInfoSchema;
123011
123861
  exports.UnindexedFilterError = require_sleep.UnindexedFilterError;
123012
123862
  exports.UninstallNodeOutcomeSchema = UninstallNodeOutcomeSchema;
@@ -123111,7 +123961,7 @@ exports.actuatorMethodRow = actuatorMethodRow;
123111
123961
  exports.addonCatalogHref = addonCatalogHref;
123112
123962
  exports.addonIdOfActionId = addonIdOfActionId;
123113
123963
  exports.addonPagesCapability = addonPagesCapability;
123114
- exports.addonPagesSourceCapability = addonPagesSourceCapability;
123964
+ exports.addonPagesSourceCapability = require_sleep.addonPagesSourceCapability;
123115
123965
  exports.addonRoutesCapability = addonRoutesCapability;
123116
123966
  exports.addonSettingsCapability = addonSettingsCapability;
123117
123967
  exports.addonWidgetsCapability = addonWidgetsCapability;
@@ -123267,6 +124117,8 @@ exports.collectSecretMarkerRefs = require_sleep.collectSecretMarkerRefs;
123267
124117
  exports.collectZodSecretPaths = require_sleep.collectZodSecretPaths;
123268
124118
  exports.colorCapability = colorCapability;
123269
124119
  exports.colorForKind = colorForKind;
124120
+ exports.commandMethodProblems = commandMethodProblems;
124121
+ exports.commandedMethods = commandedMethods;
123270
124122
  exports.commitWatchdogRestart = commitWatchdogRestart;
123271
124123
  exports.compareToTarget = compareToTarget;
123272
124124
  exports.compileExpression = require_composition.compileExpression;
@@ -123480,6 +124332,7 @@ exports.failureRate = failureRate;
123480
124332
  exports.fanControlCapability = fanControlCapability;
123481
124333
  exports.fanOutTargets = fanOutTargets;
123482
124334
  exports.featureFieldEntries = require_composition.featureFieldEntries;
124335
+ exports.featureForwards = featureForwards;
123483
124336
  exports.featureOfRole = featureOfRole;
123484
124337
  exports.featureProbeCapability = featureProbeCapability;
123485
124338
  exports.featureSources = require_composition.featureSources;
@@ -123507,6 +124360,9 @@ exports.formatParkedFor = formatParkedFor;
123507
124360
  exports.formatReading = formatReading;
123508
124361
  exports.formatSecretScope = require_sleep.formatSecretScope;
123509
124362
  exports.formulaProblem = formulaProblem;
124363
+ exports.forwardProblems = forwardProblems;
124364
+ exports.forwardSourceProblems = forwardSourceProblems;
124365
+ exports.forwardSourceRefs = forwardSourceRefs;
123510
124366
  exports.freshResults = freshResults;
123511
124367
  exports.fuseRecoveredDetections = fuseRecoveredDetections;
123512
124368
  exports.gasCapability = gasCapability;
@@ -123597,6 +124453,7 @@ exports.isEvent = require_sleep.isEvent;
123597
124453
  exports.isEventKindLabelKey = isEventKindLabelKey;
123598
124454
  exports.isEventOwnerType = isEventOwnerType;
123599
124455
  exports.isFirstLevelMacroClass = isFirstLevelMacroClass;
124456
+ exports.isForwardInputRef = require_composition.isForwardInputRef;
123600
124457
  exports.isHeldBackTemplateVar = isHeldBackTemplateVar;
123601
124458
  exports.isHistoryHelper = require_composition.isHistoryHelper;
123602
124459
  exports.isIconKey = isIconKey;
@@ -123812,6 +124669,8 @@ exports.parseStreamParamsFormPatch = parseStreamParamsFormPatch;
123812
124669
  exports.parseTrainingEventLine = parseTrainingEventLine;
123813
124670
  exports.patchAudio = patchAudio;
123814
124671
  exports.petFeederCapability = petFeederCapability;
124672
+ exports.petFountainCapability = petFountainCapability;
124673
+ exports.petFountainFromEntitiesComposition = petFountainFromEntitiesComposition;
123815
124674
  exports.pickAccessoryControl = pickAccessoryControl;
123816
124675
  exports.pickAppLinkHub = pickAppLinkHub;
123817
124676
  exports.pickClusterStepModels = pickClusterStepModels;
@@ -123860,6 +124719,7 @@ exports.projectScalarAssignment = projectScalarAssignment;
123860
124719
  exports.projectSpanAssignment = projectSpanAssignment;
123861
124720
  exports.projectSubjectPath = projectSubjectPath;
123862
124721
  exports.providerMotionEvent = providerMotionEvent;
124722
+ exports.pruneDeletedEntities = pruneDeletedEntities;
123863
124723
  exports.ptzAutotrackCapability = ptzAutotrackCapability;
123864
124724
  exports.ptzCapability = ptzCapability;
123865
124725
  exports.publicEventCategories = require_event_category.publicEventCategories;
@@ -123989,6 +124849,7 @@ exports.ruleSection = ruleSection;
123989
124849
  exports.ruleSectionOf = ruleSectionOf;
123990
124850
  exports.ruleSeedForSection = ruleSeedForSection;
123991
124851
  exports.ruleTriggerZoneIds = ruleTriggerZoneIds;
124852
+ exports.runExportedAction = runExportedAction;
123992
124853
  exports.runFanOut = runFanOut;
123993
124854
  exports.runInferenceStep = runInferenceStep;
123994
124855
  exports.runSteps = runSteps;
@@ -124027,6 +124888,7 @@ exports.selectAssignedProfileSlots = require_sleep.selectAssignedProfileSlots;
124027
124888
  exports.selectRecoveryStep = selectRecoveryStep;
124028
124889
  exports.sendTimesForEditor = sendTimesForEditor;
124029
124890
  exports.sendTimesOfSchedule = sendTimesOfSchedule;
124891
+ exports.sendToExportedTarget = sendToExportedTarget;
124030
124892
  exports.sensorRaisedOf = sensorRaisedOf;
124031
124893
  exports.sensorTransitionNeverFires = sensorTransitionNeverFires;
124032
124894
  exports.sensorTransitionOffered = sensorTransitionOffered;