@fleetless/contracts 1.0.0

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 (287) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +17 -0
  4. package/README.md +88 -0
  5. package/artifacts/constants.json +24 -0
  6. package/artifacts/openapi.json +17219 -0
  7. package/artifacts/routes.json +4605 -0
  8. package/artifacts/schema/accept-team-invite-request.schema.json +22 -0
  9. package/artifacts/schema/action-config.schema.json +198 -0
  10. package/artifacts/schema/alert-list-response.schema.json +172 -0
  11. package/artifacts/schema/api-error.schema.json +20 -0
  12. package/artifacts/schema/app-auth-config.schema.json +106 -0
  13. package/artifacts/schema/app-invitation-list-response.schema.json +57 -0
  14. package/artifacts/schema/app-invitation.schema.json +69 -0
  15. package/artifacts/schema/app-list-response.schema.json +82 -0
  16. package/artifacts/schema/app-mail-template-list-response.schema.json +68 -0
  17. package/artifacts/schema/app-mail-template.schema.json +54 -0
  18. package/artifacts/schema/app-oidc-provider-list-response.schema.json +93 -0
  19. package/artifacts/schema/app-oidc-provider.schema.json +80 -0
  20. package/artifacts/schema/app-user-list-response.schema.json +111 -0
  21. package/artifacts/schema/app-user.schema.json +98 -0
  22. package/artifacts/schema/app.schema.json +69 -0
  23. package/artifacts/schema/apply-error.schema.json +41 -0
  24. package/artifacts/schema/asset-list-response.schema.json +288 -0
  25. package/artifacts/schema/asset-sync-request.schema.json +17 -0
  26. package/artifacts/schema/asset-sync-response.schema.json +16 -0
  27. package/artifacts/schema/asset-sync-status.schema.json +136 -0
  28. package/artifacts/schema/asset.schema.json +68 -0
  29. package/artifacts/schema/audit-actor.schema.json +32 -0
  30. package/artifacts/schema/audit-event.schema.json +119 -0
  31. package/artifacts/schema/audit-list-response.schema.json +144 -0
  32. package/artifacts/schema/audit-query.schema.json +79 -0
  33. package/artifacts/schema/auth-error.schema.json +23 -0
  34. package/artifacts/schema/auth-me-response.schema.json +99 -0
  35. package/artifacts/schema/auth-ok.schema.json +115 -0
  36. package/artifacts/schema/authorization-server-metadata.schema.json +80 -0
  37. package/artifacts/schema/bridge-asset-progress.schema.json +99 -0
  38. package/artifacts/schema/bridge-assets-available.schema.json +25 -0
  39. package/artifacts/schema/bridge-camera-state.schema.json +78 -0
  40. package/artifacts/schema/bridge-config-applied.schema.json +67 -0
  41. package/artifacts/schema/bridge-hello.schema.json +65 -0
  42. package/artifacts/schema/bridge-introspect.schema.json +114 -0
  43. package/artifacts/schema/bridge-job-lost.schema.json +22 -0
  44. package/artifacts/schema/bridge-job-update.schema.json +100 -0
  45. package/artifacts/schema/bridge-pong.schema.json +19 -0
  46. package/artifacts/schema/bridge-pressure.schema.json +292 -0
  47. package/artifacts/schema/bridge-state.schema.json +24 -0
  48. package/artifacts/schema/bridge-type-definitions.schema.json +169 -0
  49. package/artifacts/schema/busy-details.schema.json +115 -0
  50. package/artifacts/schema/camera-descriptor.schema.json +45 -0
  51. package/artifacts/schema/camera-list-response.schema.json +58 -0
  52. package/artifacts/schema/camera-source.schema.json +240 -0
  53. package/artifacts/schema/cancel-request.schema.json +20 -0
  54. package/artifacts/schema/client-accept-invitation-request.schema.json +35 -0
  55. package/artifacts/schema/client-auth.schema.json +18 -0
  56. package/artifacts/schema/client-cancel.schema.json +45 -0
  57. package/artifacts/schema/client-identity.schema.json +103 -0
  58. package/artifacts/schema/client-invoke.schema.json +45 -0
  59. package/artifacts/schema/client-login-request.schema.json +29 -0
  60. package/artifacts/schema/client-logout-request.schema.json +14 -0
  61. package/artifacts/schema/client-mcp-interaction-decision-response.schema.json +15 -0
  62. package/artifacts/schema/client-mcp-interaction.schema.json +59 -0
  63. package/artifacts/schema/client-oidc-callback-query.schema.json +28 -0
  64. package/artifacts/schema/client-oidc-exchange-request.schema.json +21 -0
  65. package/artifacts/schema/client-oidc-start-query.schema.json +36 -0
  66. package/artifacts/schema/client-password-reset-confirm-request.schema.json +22 -0
  67. package/artifacts/schema/client-password-reset-request.schema.json +24 -0
  68. package/artifacts/schema/client-provider-list-query.schema.json +17 -0
  69. package/artifacts/schema/client-provider-list-response.schema.json +34 -0
  70. package/artifacts/schema/client-publish.schema.json +40 -0
  71. package/artifacts/schema/client-refresh-request.schema.json +14 -0
  72. package/artifacts/schema/client-register-request.schema.json +44 -0
  73. package/artifacts/schema/client-resend-verification-request.schema.json +24 -0
  74. package/artifacts/schema/client-subscribe.schema.json +43 -0
  75. package/artifacts/schema/client-unsubscribe.schema.json +26 -0
  76. package/artifacts/schema/client-verify-email-request.schema.json +15 -0
  77. package/artifacts/schema/cloud-asset-request.schema.json +37 -0
  78. package/artifacts/schema/cloud-camera-start.schema.json +41 -0
  79. package/artifacts/schema/cloud-camera-stop.schema.json +26 -0
  80. package/artifacts/schema/cloud-cancel.schema.json +33 -0
  81. package/artifacts/schema/cloud-config.schema.json +1635 -0
  82. package/artifacts/schema/cloud-hello-error.schema.json +23 -0
  83. package/artifacts/schema/cloud-hello-ok.schema.json +19 -0
  84. package/artifacts/schema/cloud-introspect-request.schema.json +19 -0
  85. package/artifacts/schema/cloud-invoke.schema.json +40 -0
  86. package/artifacts/schema/cloud-ping.schema.json +19 -0
  87. package/artifacts/schema/cloud-publish.schema.json +28 -0
  88. package/artifacts/schema/cloud-type-request.schema.json +30 -0
  89. package/artifacts/schema/command-result.schema.json +175 -0
  90. package/artifacts/schema/config-draft-response.schema.json +1695 -0
  91. package/artifacts/schema/config-state.schema.json +124 -0
  92. package/artifacts/schema/config-version-response.schema.json +1641 -0
  93. package/artifacts/schema/config-versions-response.schema.json +33 -0
  94. package/artifacts/schema/create-app-invitation-request.schema.json +40 -0
  95. package/artifacts/schema/create-app-oidc-provider-request.schema.json +70 -0
  96. package/artifacts/schema/create-app-request.schema.json +30 -0
  97. package/artifacts/schema/create-app-user-request.schema.json +42 -0
  98. package/artifacts/schema/create-robot-request.schema.json +14 -0
  99. package/artifacts/schema/create-robot-response.schema.json +44 -0
  100. package/artifacts/schema/create-server-key-response.schema.json +65 -0
  101. package/artifacts/schema/create-team-invite-request.schema.json +43 -0
  102. package/artifacts/schema/datapoint-alert-row.schema.json +160 -0
  103. package/artifacts/schema/datapoint-config.schema.json +366 -0
  104. package/artifacts/schema/datapoint-display.schema.json +31 -0
  105. package/artifacts/schema/datapoint-event.schema.json +34 -0
  106. package/artifacts/schema/datapoint-frame.schema.json +28 -0
  107. package/artifacts/schema/datapoint-list-response.schema.json +61 -0
  108. package/artifacts/schema/datapoint-value.schema.json +28 -0
  109. package/artifacts/schema/developer-login-request.schema.json +19 -0
  110. package/artifacts/schema/dynamic-client-registration-request.schema.json +60 -0
  111. package/artifacts/schema/dynamic-client-registration-response.schema.json +68 -0
  112. package/artifacts/schema/error-frame.schema.json +23 -0
  113. package/artifacts/schema/exposure-counts.schema.json +39 -0
  114. package/artifacts/schema/exposure-list-response.schema.json +43 -0
  115. package/artifacts/schema/fetch-types-request.schema.json +19 -0
  116. package/artifacts/schema/fetch-types-response.schema.json +163 -0
  117. package/artifacts/schema/fleetless-user-list-response.schema.json +73 -0
  118. package/artifacts/schema/fleetless-user.schema.json +60 -0
  119. package/artifacts/schema/history-buckets-response.schema.json +79 -0
  120. package/artifacts/schema/history-query.schema.json +58 -0
  121. package/artifacts/schema/history-response.schema.json +150 -0
  122. package/artifacts/schema/history-samples-response.schema.json +68 -0
  123. package/artifacts/schema/introspection-response.schema.json +118 -0
  124. package/artifacts/schema/invoke-or-service-response.schema.json +141 -0
  125. package/artifacts/schema/invoke-request.schema.json +23 -0
  126. package/artifacts/schema/invoke-response.schema.json +125 -0
  127. package/artifacts/schema/job-actor.schema.json +34 -0
  128. package/artifacts/schema/job-event.schema.json +158 -0
  129. package/artifacts/schema/job-response.schema.json +123 -0
  130. package/artifacts/schema/job-run-list-response.schema.json +222 -0
  131. package/artifacts/schema/job-run-query.schema.json +95 -0
  132. package/artifacts/schema/job-run-summary-query.schema.json +23 -0
  133. package/artifacts/schema/job-run-summary.schema.json +33 -0
  134. package/artifacts/schema/job-run.schema.json +195 -0
  135. package/artifacts/schema/job-state.schema.json +11 -0
  136. package/artifacts/schema/job.schema.json +106 -0
  137. package/artifacts/schema/latency-bucket.schema.json +63 -0
  138. package/artifacts/schema/live-session-response.schema.json +41 -0
  139. package/artifacts/schema/mail-outcome.schema.json +20 -0
  140. package/artifacts/schema/mail-template-preview-request.schema.json +35 -0
  141. package/artifacts/schema/mail-template-preview-response.schema.json +31 -0
  142. package/artifacts/schema/mail-template-problem-details.schema.json +24 -0
  143. package/artifacts/schema/mcp-consent-grant-list-response.schema.json +52 -0
  144. package/artifacts/schema/mcp-consent-grant.schema.json +39 -0
  145. package/artifacts/schema/mcp-robot-datasheet.schema.json +115 -0
  146. package/artifacts/schema/mcp-role-preview-response.schema.json +134 -0
  147. package/artifacts/schema/missing-asset-query.schema.json +11 -0
  148. package/artifacts/schema/oauth-authorize-query.schema.json +47 -0
  149. package/artifacts/schema/oauth-redirect-response.schema.json +15 -0
  150. package/artifacts/schema/oauth-token-request.schema.json +47 -0
  151. package/artifacts/schema/oauth-token-response.schema.json +38 -0
  152. package/artifacts/schema/org-alerts-query.schema.json +15 -0
  153. package/artifacts/schema/org-event-dropped.schema.json +26 -0
  154. package/artifacts/schema/org-event-replay.schema.json +97 -0
  155. package/artifacts/schema/org-event-subscribe.schema.json +14 -0
  156. package/artifacts/schema/org-event-unsubscribe.schema.json +14 -0
  157. package/artifacts/schema/org-event.schema.json +75 -0
  158. package/artifacts/schema/org-firing-alerts-response.schema.json +178 -0
  159. package/artifacts/schema/org-health-query.schema.json +13 -0
  160. package/artifacts/schema/org-latency-query.schema.json +42 -0
  161. package/artifacts/schema/org-latency-response.schema.json +124 -0
  162. package/artifacts/schema/org-quota-usage-counts.schema.json +42 -0
  163. package/artifacts/schema/org-quota-usage.schema.json +102 -0
  164. package/artifacts/schema/org-quotas.schema.json +51 -0
  165. package/artifacts/schema/org-usage-query.schema.json +19 -0
  166. package/artifacts/schema/org-usage-response.schema.json +77 -0
  167. package/artifacts/schema/org.schema.json +30 -0
  168. package/artifacts/schema/parameter-invalid-details.schema.json +37 -0
  169. package/artifacts/schema/parameter-spec.schema.json +120 -0
  170. package/artifacts/schema/parameter-violation.schema.json +24 -0
  171. package/artifacts/schema/password-change-request.schema.json +21 -0
  172. package/artifacts/schema/password-reset-confirm.schema.json +19 -0
  173. package/artifacts/schema/password-reset-request.schema.json +14 -0
  174. package/artifacts/schema/patch-app-oidc-provider-request.schema.json +50 -0
  175. package/artifacts/schema/patch-app-user-request.schema.json +34 -0
  176. package/artifacts/schema/patch-auth-me-request.schema.json +22 -0
  177. package/artifacts/schema/patch-fleetless-user-request.schema.json +20 -0
  178. package/artifacts/schema/patch-org-request.schema.json +15 -0
  179. package/artifacts/schema/patch-org-response.schema.json +40 -0
  180. package/artifacts/schema/patch-robot-request.schema.json +15 -0
  181. package/artifacts/schema/patch-robot-response.schema.json +40 -0
  182. package/artifacts/schema/pending-team-invite-list-response.schema.json +52 -0
  183. package/artifacts/schema/pending-team-invite.schema.json +39 -0
  184. package/artifacts/schema/protected-resource-metadata.schema.json +41 -0
  185. package/artifacts/schema/publish-config-response.schema.json +21 -0
  186. package/artifacts/schema/publish-request.schema.json +17 -0
  187. package/artifacts/schema/publisher-config.schema.json +285 -0
  188. package/artifacts/schema/put-app-auth-config-request.schema.json +93 -0
  189. package/artifacts/schema/put-app-mail-template-request.schema.json +35 -0
  190. package/artifacts/schema/put-config-draft-request.schema.json +13 -0
  191. package/artifacts/schema/put-datapoint-display-request.schema.json +31 -0
  192. package/artifacts/schema/put-robot-details-request.schema.json +41 -0
  193. package/artifacts/schema/put-robot-details-response.schema.json +43 -0
  194. package/artifacts/schema/rate-limit-details.schema.json +15 -0
  195. package/artifacts/schema/refresh-request.schema.json +13 -0
  196. package/artifacts/schema/release-live-query.schema.json +13 -0
  197. package/artifacts/schema/rename-slug-request.schema.json +23 -0
  198. package/artifacts/schema/rename-slug-response.schema.json +24 -0
  199. package/artifacts/schema/resource-health-event.schema.json +72 -0
  200. package/artifacts/schema/resource-health-list-response.schema.json +80 -0
  201. package/artifacts/schema/resource-health-state.schema.json +68 -0
  202. package/artifacts/schema/robot-config-doc.schema.json +1616 -0
  203. package/artifacts/schema/robot-delete-query.schema.json +12 -0
  204. package/artifacts/schema/robot-deletion-summary.schema.json +63 -0
  205. package/artifacts/schema/robot-detail-response.schema.json +262 -0
  206. package/artifacts/schema/robot-details-doc.schema.json +33 -0
  207. package/artifacts/schema/robot-jobs-response.schema.json +119 -0
  208. package/artifacts/schema/robot-latency-series.schema.json +81 -0
  209. package/artifacts/schema/robot-list-item.schema.json +94 -0
  210. package/artifacts/schema/robot-list-response.schema.json +106 -0
  211. package/artifacts/schema/robot.schema.json +30 -0
  212. package/artifacts/schema/role-list-response.schema.json +48 -0
  213. package/artifacts/schema/role-permissions.schema.json +61 -0
  214. package/artifacts/schema/role.schema.json +35 -0
  215. package/artifacts/schema/ros-graph.schema.json +99 -0
  216. package/artifacts/schema/server-key-list-response.schema.json +64 -0
  217. package/artifacts/schema/server-key.schema.json +51 -0
  218. package/artifacts/schema/service-call-response.schema.json +13 -0
  219. package/artifacts/schema/service-config.schema.json +198 -0
  220. package/artifacts/schema/session-tokens.schema.json +28 -0
  221. package/artifacts/schema/sign-up-request.schema.json +26 -0
  222. package/artifacts/schema/sign-up-response.schema.json +127 -0
  223. package/artifacts/schema/slug-usage-response.schema.json +32 -0
  224. package/artifacts/schema/snapshot-header.schema.json +44 -0
  225. package/artifacts/schema/snapshot-meta-response.schema.json +85 -0
  226. package/artifacts/schema/subscribe-error.schema.json +31 -0
  227. package/artifacts/schema/team-invite.schema.json +57 -0
  228. package/artifacts/schema/tier-change-request.schema.json +17 -0
  229. package/artifacts/schema/type-definition.schema.json +144 -0
  230. package/artifacts/schema/types-response.schema.json +156 -0
  231. package/artifacts/schema/update-app-request.schema.json +32 -0
  232. package/artifacts/schema/urdf-completeness.schema.json +50 -0
  233. package/artifacts/schema/validation-issue.schema.json +43 -0
  234. package/artifacts/schema/waitlist-request.schema.json +15 -0
  235. package/artifacts/schema-outgoing/bridge-asset-progress.schema.json +102 -0
  236. package/artifacts/schema-outgoing/bridge-assets-available.schema.json +26 -0
  237. package/artifacts/schema-outgoing/bridge-camera-state.schema.json +80 -0
  238. package/artifacts/schema-outgoing/bridge-config-applied.schema.json +69 -0
  239. package/artifacts/schema-outgoing/bridge-hello.schema.json +68 -0
  240. package/artifacts/schema-outgoing/bridge-introspect.schema.json +119 -0
  241. package/artifacts/schema-outgoing/bridge-job-lost.schema.json +23 -0
  242. package/artifacts/schema-outgoing/bridge-job-update.schema.json +102 -0
  243. package/artifacts/schema-outgoing/bridge-pong.schema.json +20 -0
  244. package/artifacts/schema-outgoing/bridge-type-definitions.schema.json +174 -0
  245. package/artifacts/schema-outgoing/datapoint-frame.schema.json +29 -0
  246. package/artifacts/schema-outgoing/snapshot-header.schema.json +45 -0
  247. package/dist/alerts.d.ts +255 -0
  248. package/dist/alerts.js +193 -0
  249. package/dist/app-users.d.ts +606 -0
  250. package/dist/app-users.js +696 -0
  251. package/dist/apps.d.ts +175 -0
  252. package/dist/apps.js +267 -0
  253. package/dist/assets.d.ts +434 -0
  254. package/dist/assets.js +546 -0
  255. package/dist/audit.d.ts +129 -0
  256. package/dist/audit.js +238 -0
  257. package/dist/client-auth.d.ts +409 -0
  258. package/dist/client-auth.js +487 -0
  259. package/dist/common.d.ts +186 -0
  260. package/dist/common.js +199 -0
  261. package/dist/config-issues.d.ts +175 -0
  262. package/dist/config-issues.js +339 -0
  263. package/dist/config.d.ts +862 -0
  264. package/dist/config.js +1988 -0
  265. package/dist/errors.d.ts +52 -0
  266. package/dist/errors.js +786 -0
  267. package/dist/identity.d.ts +549 -0
  268. package/dist/identity.js +503 -0
  269. package/dist/index.d.ts +51 -0
  270. package/dist/index.js +51 -0
  271. package/dist/introspection.d.ts +99 -0
  272. package/dist/introspection.js +97 -0
  273. package/dist/jobs.d.ts +334 -0
  274. package/dist/jobs.js +345 -0
  275. package/dist/mcp.d.ts +239 -0
  276. package/dist/mcp.js +153 -0
  277. package/dist/oauth.d.ts +344 -0
  278. package/dist/oauth.js +488 -0
  279. package/dist/protocol.d.ts +781 -0
  280. package/dist/protocol.js +715 -0
  281. package/dist/realtime.d.ts +494 -0
  282. package/dist/realtime.js +512 -0
  283. package/dist/rest.d.ts +1989 -0
  284. package/dist/rest.js +1963 -0
  285. package/dist/routes.d.ts +94 -0
  286. package/dist/routes.js +2298 -0
  287. package/package.json +61 -0
@@ -0,0 +1,255 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Datapoint alerts and per-datapoint chart display config (spec
4
+ * `2026-08-28-alerts-and-datapoint-modal-design`, D1/D2/D5).
5
+ *
6
+ * An alert is a **state machine** (`ok ⇄ firing`), not a fire-once event —
7
+ * the definition (this file's request/entity shapes) and the runtime state
8
+ * (`state`, `state_since`, `last_value`) share one row, evaluated by the
9
+ * cloud at ingest.
10
+ *
11
+ * **Both tables moved into `robotConfigDoc` in FL-002.** The alert definition
12
+ * is `config.ts`'s `datapointAlert`, nested under the datapoint it watches;
13
+ * the chart bounds are `datapointChart`. They therefore take effect on
14
+ * publish rather than immediately, and in exchange every change to them is
15
+ * versioned, comparable and revertible. The runtime state stays wherever the
16
+ * definition goes: it belongs in the database and has no business in a
17
+ * versioned document.
18
+ *
19
+ * **What is left here is the read surface**, which FL-002 wave 4 kept rather
20
+ * than deleted: `GET /api/robots/:id/alerts` and `GET /api/org/alerts` still
21
+ * answer with the definition joined to its state, and the shapes below are
22
+ * what they answer with. What wave 4 did remove is the mail path — the fields
23
+ * `cooldown_minutes`, `recipients` and `notify_on_resolve`, and the two
24
+ * bounds that guarded them. No alert can send mail, so nothing here describes
25
+ * one.
26
+ */
27
+ /**
28
+ * `above`/`below` compare the numeric sample value (already scale/offset
29
+ * applied by the bridge) against `threshold`. `resolve_hysteresis` (≥ 0,
30
+ * **defaulted to 0** — plain re-cross) moves the resolve point off the
31
+ * threshold itself: `above` resolves at `value ≤ threshold −
32
+ * resolve_hysteresis`, mirrored for `below`. Defaulted rather than left
33
+ * `optional` so a parsed entity never makes a consumer re-derive "absent
34
+ * means 0" — the cloud always sends an explicit resolve point, and every
35
+ * reader gets the same number whether it was sent or not. `equals` compares
36
+ * the raw value for equality — the shape for boolean/string datapoints a
37
+ * threshold cannot describe ("Hindernis erkannt" = `equals true`) — and
38
+ * carries no hysteresis, because equality has no direction to relax.
39
+ *
40
+ * A discriminated union on `kind` rather than one object with optional
41
+ * fields: an `equals` alert carrying a stray `threshold` would otherwise
42
+ * parse silently and mean nothing, and a consumer's `switch (kind)` fails
43
+ * `tsc` on an unhandled member instead of failing at runtime on a `never`.
44
+ *
45
+ * **Each member is `.strict()`, not the union's default `z.object`.** A
46
+ * plain `z.object` strips unknown keys silently rather than refusing them —
47
+ * so without this, `{kind: 'equals', value: true, threshold: 5}` would
48
+ * parse successfully with `threshold` dropped on the floor, which is exactly
49
+ * the "parse silently and mean nothing" failure the paragraph above already
50
+ * argued against, just one layer further in.
51
+ *
52
+ * **`Row` distinguishes this from the document's own condition.**
53
+ * `config.ts`'s `alertCondition` is what an author writes inside a datapoint —
54
+ * a different shape for a different question (`fire_at`/`resolve_at` rather
55
+ * than `kind`/`threshold`/`resolve_hysteresis`). This one is what the
56
+ * evaluator switches on and what the read routes answer with; the cloud
57
+ * derives it from the document (`alert-definitions.ts`'s `toRowCondition`) and
58
+ * derives it nowhere else.
59
+ *
60
+ * **It was renamed for a wave 4 deletion that did not happen**, and the name
61
+ * is kept because the distinction it draws is still needed: two condition
62
+ * shapes coexist, and only one of them is authored. Nothing is stored under
63
+ * this shape any more — the database keeps runtime state only — so read `Row`
64
+ * as "the derived one", not as "the persisted one".
65
+ */
66
+ export declare const alertRowCondition: z.ZodDiscriminatedUnion<[z.ZodObject<{
67
+ kind: z.ZodLiteral<"above">;
68
+ threshold: z.ZodNumber;
69
+ resolve_hysteresis: z.ZodDefault<z.ZodNumber>;
70
+ }, z.core.$strict>, z.ZodObject<{
71
+ kind: z.ZodLiteral<"below">;
72
+ threshold: z.ZodNumber;
73
+ resolve_hysteresis: z.ZodDefault<z.ZodNumber>;
74
+ }, z.core.$strict>, z.ZodObject<{
75
+ kind: z.ZodLiteral<"equals">;
76
+ value: z.ZodUnion<readonly [z.ZodNumber, z.ZodString, z.ZodBoolean]>;
77
+ }, z.core.$strict>], "kind">;
78
+ export type AlertRowCondition = z.infer<typeof alertRowCondition>;
79
+ /**
80
+ * Assigned at creation, carried onto every `orgEventKind: 'alert'` firing
81
+ * event verbatim (resolved events are always `info` — see
82
+ * `orgEventKind`'s doc comment in `realtime.ts`).
83
+ */
84
+ export declare const alertSeverity: z.ZodEnum<{
85
+ error: "error";
86
+ warning: "warning";
87
+ }>;
88
+ export type AlertSeverity = z.infer<typeof alertSeverity>;
89
+ /** The two states of the alert state machine. There is no third state — an alert is never "unknown" or "pending"; it holds its last state across non-comparable samples (D2). */
90
+ export declare const alertState: z.ZodEnum<{
91
+ ok: "ok";
92
+ firing: "firing";
93
+ }>;
94
+ export type AlertState = z.infer<typeof alertState>;
95
+ /**
96
+ * One alert row, definition and runtime state together — the runtime fields
97
+ * (`state`, `state_since`, `last_value`) are DB-held so they survive a cloud
98
+ * restart, and are read-only from every client's point of view: nothing
99
+ * writes them from outside the cloud's own evaluator.
100
+ *
101
+ * **`Row` is a name the shape outgrew**, kept only to keep it apart from
102
+ * `config.ts`'s `datapointAlert`, which is the definition an author writes.
103
+ * Nothing is stored in this shape: the definition comes out of the published
104
+ * document and the runtime state out of `datapoint_alert_state`, and the
105
+ * cloud joins the two per request (`routes/alerts.ts`'s `toWire`).
106
+ *
107
+ * **It carried three mail settings — `cooldown_minutes`, `recipients` and
108
+ * `notify_on_resolve` — and FL-002 wave 4 removed them with the mail path.**
109
+ * The format has no mail fields, so no alert could be configured to send one;
110
+ * the three had nothing behind them well before they were deleted.
111
+ */
112
+ export declare const datapointAlertRow: z.ZodObject<{
113
+ id: z.ZodUUID;
114
+ robot_id: z.ZodUUID;
115
+ slug: z.ZodString;
116
+ name: z.ZodString;
117
+ enabled: z.ZodBoolean;
118
+ severity: z.ZodEnum<{
119
+ error: "error";
120
+ warning: "warning";
121
+ }>;
122
+ condition: z.ZodDiscriminatedUnion<[z.ZodObject<{
123
+ kind: z.ZodLiteral<"above">;
124
+ threshold: z.ZodNumber;
125
+ resolve_hysteresis: z.ZodDefault<z.ZodNumber>;
126
+ }, z.core.$strict>, z.ZodObject<{
127
+ kind: z.ZodLiteral<"below">;
128
+ threshold: z.ZodNumber;
129
+ resolve_hysteresis: z.ZodDefault<z.ZodNumber>;
130
+ }, z.core.$strict>, z.ZodObject<{
131
+ kind: z.ZodLiteral<"equals">;
132
+ value: z.ZodUnion<readonly [z.ZodNumber, z.ZodString, z.ZodBoolean]>;
133
+ }, z.core.$strict>], "kind">;
134
+ state: z.ZodEnum<{
135
+ ok: "ok";
136
+ firing: "firing";
137
+ }>;
138
+ state_since: z.ZodNullable<z.ZodISODateTime>;
139
+ last_value: z.ZodNullable<z.ZodUnknown>;
140
+ created_at: z.ZodISODateTime;
141
+ }, z.core.$strip>;
142
+ export type DatapointAlertRow = z.infer<typeof datapointAlertRow>;
143
+ /** `GET /api/robots/:id/alerts`. */
144
+ export declare const alertListResponse: z.ZodObject<{
145
+ alerts: z.ZodArray<z.ZodObject<{
146
+ id: z.ZodUUID;
147
+ robot_id: z.ZodUUID;
148
+ slug: z.ZodString;
149
+ name: z.ZodString;
150
+ enabled: z.ZodBoolean;
151
+ severity: z.ZodEnum<{
152
+ error: "error";
153
+ warning: "warning";
154
+ }>;
155
+ condition: z.ZodDiscriminatedUnion<[z.ZodObject<{
156
+ kind: z.ZodLiteral<"above">;
157
+ threshold: z.ZodNumber;
158
+ resolve_hysteresis: z.ZodDefault<z.ZodNumber>;
159
+ }, z.core.$strict>, z.ZodObject<{
160
+ kind: z.ZodLiteral<"below">;
161
+ threshold: z.ZodNumber;
162
+ resolve_hysteresis: z.ZodDefault<z.ZodNumber>;
163
+ }, z.core.$strict>, z.ZodObject<{
164
+ kind: z.ZodLiteral<"equals">;
165
+ value: z.ZodUnion<readonly [z.ZodNumber, z.ZodString, z.ZodBoolean]>;
166
+ }, z.core.$strict>], "kind">;
167
+ state: z.ZodEnum<{
168
+ ok: "ok";
169
+ firing: "firing";
170
+ }>;
171
+ state_since: z.ZodNullable<z.ZodISODateTime>;
172
+ last_value: z.ZodNullable<z.ZodUnknown>;
173
+ created_at: z.ZodISODateTime;
174
+ }, z.core.$strip>>;
175
+ }, z.core.$strip>;
176
+ export type AlertListResponse = z.infer<typeof alertListResponse>;
177
+ /**
178
+ * `GET /api/org/alerts?state=firing` — feeds the overview's "open issues"
179
+ * tile and the fleet grid's per-robot badge (D3). Org-scoped and
180
+ * cross-robot, so each entry carries `robot_name` alongside the alert: the
181
+ * overview has no robot context of its own to join against.
182
+ */
183
+ export declare const orgFiringAlertsResponse: z.ZodObject<{
184
+ alerts: z.ZodArray<z.ZodObject<{
185
+ id: z.ZodUUID;
186
+ robot_id: z.ZodUUID;
187
+ slug: z.ZodString;
188
+ name: z.ZodString;
189
+ enabled: z.ZodBoolean;
190
+ severity: z.ZodEnum<{
191
+ error: "error";
192
+ warning: "warning";
193
+ }>;
194
+ condition: z.ZodDiscriminatedUnion<[z.ZodObject<{
195
+ kind: z.ZodLiteral<"above">;
196
+ threshold: z.ZodNumber;
197
+ resolve_hysteresis: z.ZodDefault<z.ZodNumber>;
198
+ }, z.core.$strict>, z.ZodObject<{
199
+ kind: z.ZodLiteral<"below">;
200
+ threshold: z.ZodNumber;
201
+ resolve_hysteresis: z.ZodDefault<z.ZodNumber>;
202
+ }, z.core.$strict>, z.ZodObject<{
203
+ kind: z.ZodLiteral<"equals">;
204
+ value: z.ZodUnion<readonly [z.ZodNumber, z.ZodString, z.ZodBoolean]>;
205
+ }, z.core.$strict>], "kind">;
206
+ state: z.ZodEnum<{
207
+ ok: "ok";
208
+ firing: "firing";
209
+ }>;
210
+ state_since: z.ZodNullable<z.ZodISODateTime>;
211
+ last_value: z.ZodNullable<z.ZodUnknown>;
212
+ created_at: z.ZodISODateTime;
213
+ robot_name: z.ZodString;
214
+ }, z.core.$strip>>;
215
+ }, z.core.$strip>;
216
+ export type OrgFiringAlertsResponse = z.infer<typeof orgFiringAlertsResponse>;
217
+ /**
218
+ * The query of `GET /api/org/alerts`: only the firing set is served.
219
+ *
220
+ * A schema for a one-value parameter looks like ceremony, and it is not: the
221
+ * route **refuses** anything else rather than ignoring it, so the single
222
+ * accepted value is a contract a caller can read off the parameter table
223
+ * instead of discovering as a `400`.
224
+ */
225
+ export declare const orgAlertsQuery: z.ZodObject<{
226
+ state: z.ZodLiteral<"firing">;
227
+ }, z.core.$strip>;
228
+ export type OrgAlertsQuery = z.infer<typeof orgAlertsQuery>;
229
+ /**
230
+ * `GET /api/robots/:id/datapoints/:slug/display` — chart display config from
231
+ * the modal's Chart tab (D1, D4). `robot_id`/`slug` live in the path, not
232
+ * the body; there is exactly one row per `(robot_id, slug)`, so there is
233
+ * nothing to list or identify beyond the path itself.
234
+ *
235
+ * `null` means auto-scale — the uPlot chart's default — not "unset versus
236
+ * zero": a bound of literal `0` is a real, common y-axis floor and must
237
+ * round-trip as `0`, not fall back to auto because it was falsy.
238
+ */
239
+ export declare const datapointDisplay: z.ZodObject<{
240
+ y_min: z.ZodNullable<z.ZodNumber>;
241
+ y_max: z.ZodNullable<z.ZodNumber>;
242
+ }, z.core.$strip>;
243
+ export type DatapointDisplay = z.infer<typeof datapointDisplay>;
244
+ /**
245
+ * `PUT /api/robots/:id/datapoints/:slug/display` — applies immediately,
246
+ * never published, never sent to the bridge (D1). Same shape as
247
+ * `datapointDisplay`, kept as its own type per house convention (`put*Request`
248
+ * beside the entity it writes) so the two can diverge if the read side ever
249
+ * grows a field the write side should not accept.
250
+ */
251
+ export declare const putDatapointDisplayRequest: z.ZodObject<{
252
+ y_min: z.ZodNullable<z.ZodNumber>;
253
+ y_max: z.ZodNullable<z.ZodNumber>;
254
+ }, z.core.$strict>;
255
+ export type PutDatapointDisplayRequest = z.infer<typeof putDatapointDisplayRequest>;
package/dist/alerts.js ADDED
@@ -0,0 +1,193 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { z } from 'zod';
3
+ import { slug } from './common.js';
4
+ /**
5
+ * Datapoint alerts and per-datapoint chart display config (spec
6
+ * `2026-08-28-alerts-and-datapoint-modal-design`, D1/D2/D5).
7
+ *
8
+ * An alert is a **state machine** (`ok ⇄ firing`), not a fire-once event —
9
+ * the definition (this file's request/entity shapes) and the runtime state
10
+ * (`state`, `state_since`, `last_value`) share one row, evaluated by the
11
+ * cloud at ingest.
12
+ *
13
+ * **Both tables moved into `robotConfigDoc` in FL-002.** The alert definition
14
+ * is `config.ts`'s `datapointAlert`, nested under the datapoint it watches;
15
+ * the chart bounds are `datapointChart`. They therefore take effect on
16
+ * publish rather than immediately, and in exchange every change to them is
17
+ * versioned, comparable and revertible. The runtime state stays wherever the
18
+ * definition goes: it belongs in the database and has no business in a
19
+ * versioned document.
20
+ *
21
+ * **What is left here is the read surface**, which FL-002 wave 4 kept rather
22
+ * than deleted: `GET /api/robots/:id/alerts` and `GET /api/org/alerts` still
23
+ * answer with the definition joined to its state, and the shapes below are
24
+ * what they answer with. What wave 4 did remove is the mail path — the fields
25
+ * `cooldown_minutes`, `recipients` and `notify_on_resolve`, and the two
26
+ * bounds that guarded them. No alert can send mail, so nothing here describes
27
+ * one.
28
+ */
29
+ /**
30
+ * `above`/`below` compare the numeric sample value (already scale/offset
31
+ * applied by the bridge) against `threshold`. `resolve_hysteresis` (≥ 0,
32
+ * **defaulted to 0** — plain re-cross) moves the resolve point off the
33
+ * threshold itself: `above` resolves at `value ≤ threshold −
34
+ * resolve_hysteresis`, mirrored for `below`. Defaulted rather than left
35
+ * `optional` so a parsed entity never makes a consumer re-derive "absent
36
+ * means 0" — the cloud always sends an explicit resolve point, and every
37
+ * reader gets the same number whether it was sent or not. `equals` compares
38
+ * the raw value for equality — the shape for boolean/string datapoints a
39
+ * threshold cannot describe ("Hindernis erkannt" = `equals true`) — and
40
+ * carries no hysteresis, because equality has no direction to relax.
41
+ *
42
+ * A discriminated union on `kind` rather than one object with optional
43
+ * fields: an `equals` alert carrying a stray `threshold` would otherwise
44
+ * parse silently and mean nothing, and a consumer's `switch (kind)` fails
45
+ * `tsc` on an unhandled member instead of failing at runtime on a `never`.
46
+ *
47
+ * **Each member is `.strict()`, not the union's default `z.object`.** A
48
+ * plain `z.object` strips unknown keys silently rather than refusing them —
49
+ * so without this, `{kind: 'equals', value: true, threshold: 5}` would
50
+ * parse successfully with `threshold` dropped on the floor, which is exactly
51
+ * the "parse silently and mean nothing" failure the paragraph above already
52
+ * argued against, just one layer further in.
53
+ *
54
+ * **`Row` distinguishes this from the document's own condition.**
55
+ * `config.ts`'s `alertCondition` is what an author writes inside a datapoint —
56
+ * a different shape for a different question (`fire_at`/`resolve_at` rather
57
+ * than `kind`/`threshold`/`resolve_hysteresis`). This one is what the
58
+ * evaluator switches on and what the read routes answer with; the cloud
59
+ * derives it from the document (`alert-definitions.ts`'s `toRowCondition`) and
60
+ * derives it nowhere else.
61
+ *
62
+ * **It was renamed for a wave 4 deletion that did not happen**, and the name
63
+ * is kept because the distinction it draws is still needed: two condition
64
+ * shapes coexist, and only one of them is authored. Nothing is stored under
65
+ * this shape any more — the database keeps runtime state only — so read `Row`
66
+ * as "the derived one", not as "the persisted one".
67
+ */
68
+ export const alertRowCondition = z.discriminatedUnion('kind', [
69
+ z.strictObject({
70
+ kind: z.literal('above'),
71
+ threshold: z.number().finite(),
72
+ resolve_hysteresis: z.number().nonnegative().default(0),
73
+ }),
74
+ z.strictObject({
75
+ kind: z.literal('below'),
76
+ threshold: z.number().finite(),
77
+ resolve_hysteresis: z.number().nonnegative().default(0),
78
+ }),
79
+ z.strictObject({
80
+ kind: z.literal('equals'),
81
+ /** A JSON scalar, matching what a datapoint value actually is on the wire — never an object or array. */
82
+ value: z.union([z.number(), z.string(), z.boolean()]),
83
+ }),
84
+ ]);
85
+ /**
86
+ * Assigned at creation, carried onto every `orgEventKind: 'alert'` firing
87
+ * event verbatim (resolved events are always `info` — see
88
+ * `orgEventKind`'s doc comment in `realtime.ts`).
89
+ */
90
+ export const alertSeverity = z.enum(['warning', 'error']);
91
+ /** The two states of the alert state machine. There is no third state — an alert is never "unknown" or "pending"; it holds its last state across non-comparable samples (D2). */
92
+ export const alertState = z.enum(['ok', 'firing']);
93
+ /**
94
+ * One alert row, definition and runtime state together — the runtime fields
95
+ * (`state`, `state_since`, `last_value`) are DB-held so they survive a cloud
96
+ * restart, and are read-only from every client's point of view: nothing
97
+ * writes them from outside the cloud's own evaluator.
98
+ *
99
+ * **`Row` is a name the shape outgrew**, kept only to keep it apart from
100
+ * `config.ts`'s `datapointAlert`, which is the definition an author writes.
101
+ * Nothing is stored in this shape: the definition comes out of the published
102
+ * document and the runtime state out of `datapoint_alert_state`, and the
103
+ * cloud joins the two per request (`routes/alerts.ts`'s `toWire`).
104
+ *
105
+ * **It carried three mail settings — `cooldown_minutes`, `recipients` and
106
+ * `notify_on_resolve` — and FL-002 wave 4 removed them with the mail path.**
107
+ * The format has no mail fields, so no alert could be configured to send one;
108
+ * the three had nothing behind them well before they were deleted.
109
+ */
110
+ export const datapointAlertRow = z.object({
111
+ id: z.uuid(),
112
+ robot_id: z.uuid(),
113
+ slug,
114
+ name: z.string().min(1).max(120),
115
+ enabled: z.boolean(),
116
+ severity: alertSeverity,
117
+ condition: alertRowCondition,
118
+ state: alertState,
119
+ /** `null` only until the first evaluation writes a state; every alert is created `ok` (D2), so in practice this is set from creation onward. */
120
+ state_since: z.iso.datetime().nullable(),
121
+ /**
122
+ * The value at the alert's last state transition — written only when the
123
+ * alert fires or resolves, never on a per-sample basis. This is a
124
+ * deliberate cost trade, not an oversight: a per-sample write would turn
125
+ * every accepted sample into a DB write regardless of whether anything
126
+ * changed, which is exactly the hot-path cost the evaluator avoids
127
+ * everywhere else. It follows that this is NOT "the datapoint's current
128
+ * value" — for that, read the live snapshot (`datapointValue`, or the
129
+ * realtime datapoint stream), never this field. `null` before the
130
+ * alert's first transition, and returns to `null` when a `PATCH`
131
+ * replaces `condition` wholesale — the old value was judged against the
132
+ * old condition, and the store resets runtime state in that same write
133
+ * rather than let it survive a condition change it no longer means
134
+ * anything against.
135
+ */
136
+ last_value: z.unknown().nullable(),
137
+ created_at: z.iso.datetime(),
138
+ });
139
+ /** `GET /api/robots/:id/alerts`. */
140
+ export const alertListResponse = z.object({
141
+ alerts: z.array(datapointAlertRow),
142
+ });
143
+ /**
144
+ * `GET /api/org/alerts?state=firing` — feeds the overview's "open issues"
145
+ * tile and the fleet grid's per-robot badge (D3). Org-scoped and
146
+ * cross-robot, so each entry carries `robot_name` alongside the alert: the
147
+ * overview has no robot context of its own to join against.
148
+ */
149
+ export const orgFiringAlertsResponse = z.object({
150
+ alerts: z.array(datapointAlertRow.extend({ robot_name: z.string().min(1).max(63) })),
151
+ });
152
+ /**
153
+ * The query of `GET /api/org/alerts`: only the firing set is served.
154
+ *
155
+ * A schema for a one-value parameter looks like ceremony, and it is not: the
156
+ * route **refuses** anything else rather than ignoring it, so the single
157
+ * accepted value is a contract a caller can read off the parameter table
158
+ * instead of discovering as a `400`.
159
+ */
160
+ export const orgAlertsQuery = z
161
+ .object({
162
+ state: z.literal('firing').meta({
163
+ description: 'Required, and the only accepted value — this endpoint lists the alerts that are firing now. Anything else, the parameter\'s absence included, is `400 validation_error`: a door with one answer must not advertise a dial.',
164
+ }),
165
+ })
166
+ .meta({ description: 'The query of `GET /api/org/alerts`. One required parameter with one accepted value.' });
167
+ /**
168
+ * `GET /api/robots/:id/datapoints/:slug/display` — chart display config from
169
+ * the modal's Chart tab (D1, D4). `robot_id`/`slug` live in the path, not
170
+ * the body; there is exactly one row per `(robot_id, slug)`, so there is
171
+ * nothing to list or identify beyond the path itself.
172
+ *
173
+ * `null` means auto-scale — the uPlot chart's default — not "unset versus
174
+ * zero": a bound of literal `0` is a real, common y-axis floor and must
175
+ * round-trip as `0`, not fall back to auto because it was falsy.
176
+ */
177
+ export const datapointDisplay = z.object({
178
+ y_min: z.number().finite().nullable(),
179
+ y_max: z.number().finite().nullable(),
180
+ });
181
+ /**
182
+ * `PUT /api/robots/:id/datapoints/:slug/display` — applies immediately,
183
+ * never published, never sent to the bridge (D1). Same shape as
184
+ * `datapointDisplay`, kept as its own type per house convention (`put*Request`
185
+ * beside the entity it writes) so the two can diverge if the read side ever
186
+ * grows a field the write side should not accept.
187
+ */
188
+ export const putDatapointDisplayRequest = z
189
+ .object({
190
+ y_min: z.number().finite().nullable(),
191
+ y_max: z.number().finite().nullable(),
192
+ })
193
+ .strict();