@fleetless/contracts 1.2.0 → 3.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 (68) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/artifacts/constants.json +30 -4
  3. package/artifacts/openapi.json +882 -80
  4. package/artifacts/routes.json +217 -7
  5. package/artifacts/schema/app-deletion-summary.schema.json +51 -0
  6. package/artifacts/schema/apply-error.schema.json +2 -1
  7. package/artifacts/schema/asset-list-response.schema.json +77 -12
  8. package/artifacts/schema/asset-sync-status.schema.json +35 -8
  9. package/artifacts/schema/asset.schema.json +2 -3
  10. package/artifacts/schema/assets-clear-response.schema.json +23 -0
  11. package/artifacts/schema/bridge-asset-progress.schema.json +14 -8
  12. package/artifacts/schema/bridge-config-applied.schema.json +2 -1
  13. package/artifacts/schema/bridge-link-mode.schema.json +36 -0
  14. package/artifacts/schema/bridge-state.schema.json +6 -1
  15. package/artifacts/schema/client-robot-list-item.schema.json +6 -1
  16. package/artifacts/schema/client-robot-list-response.schema.json +6 -1
  17. package/artifacts/schema/cloud-config.schema.json +90 -5
  18. package/artifacts/schema/cloud-hello-ok.schema.json +45 -0
  19. package/artifacts/schema/cloud-ping.schema.json +27 -1
  20. package/artifacts/schema/config-draft-response.schema.json +90 -5
  21. package/artifacts/schema/config-state.schema.json +2 -1
  22. package/artifacts/schema/config-version-response.schema.json +90 -5
  23. package/artifacts/schema/datapoint-config.schema.json +5 -0
  24. package/artifacts/schema/datapoint-frame.schema.json +4 -0
  25. package/artifacts/schema/datapoint-list-response.schema.json +2 -2
  26. package/artifacts/schema/joint-state-put-request.schema.json +23 -0
  27. package/artifacts/schema/joint-state-put-response.schema.json +24 -0
  28. package/artifacts/schema/org-quota-usage-counts.schema.json +0 -5
  29. package/artifacts/schema/org-quota-usage.schema.json +1 -12
  30. package/artifacts/schema/org-quotas.schema.json +1 -7
  31. package/artifacts/schema/put-app-auth-mcp-request.schema.json +27 -0
  32. package/artifacts/schema/put-app-auth-registration-request.schema.json +36 -0
  33. package/artifacts/schema/put-app-auth-urls-request.schema.json +48 -0
  34. package/artifacts/schema/robot-config-doc.schema.json +90 -5
  35. package/artifacts/schema/robot-deletion-summary.schema.json +2 -1
  36. package/artifacts/schema/robot-detail-response.schema.json +63 -2
  37. package/artifacts/schema/robot-list-item.schema.json +15 -1
  38. package/artifacts/schema/robot-list-response.schema.json +15 -1
  39. package/artifacts/schema/robot-token-rotate-response.schema.json +15 -0
  40. package/artifacts/schema-outgoing/bridge-asset-progress.schema.json +14 -8
  41. package/artifacts/schema-outgoing/bridge-config-applied.schema.json +2 -1
  42. package/artifacts/schema-outgoing/bridge-link-mode.schema.json +37 -0
  43. package/artifacts/schema-outgoing/datapoint-frame.schema.json +4 -0
  44. package/dist/app-users.d.ts +25 -7
  45. package/dist/app-users.js +24 -6
  46. package/dist/apps.d.ts +22 -0
  47. package/dist/apps.js +33 -0
  48. package/dist/assets.d.ts +85 -50
  49. package/dist/assets.js +152 -62
  50. package/dist/audit.d.ts +1 -1
  51. package/dist/audit.js +1 -1
  52. package/dist/client-robots.d.ts +2 -0
  53. package/dist/common.d.ts +10 -0
  54. package/dist/common.js +16 -1
  55. package/dist/config.d.ts +69 -1
  56. package/dist/config.js +86 -6
  57. package/dist/errors.d.ts +1 -1
  58. package/dist/errors.js +1 -8
  59. package/dist/index.d.ts +12 -12
  60. package/dist/index.js +6 -6
  61. package/dist/protocol.d.ts +150 -71
  62. package/dist/protocol.js +144 -87
  63. package/dist/rest.d.ts +139 -35
  64. package/dist/rest.js +100 -66
  65. package/dist/routes.js +129 -21
  66. package/package.json +1 -1
  67. package/artifacts/schema/bridge-pressure.schema.json +0 -292
  68. package/artifacts/schema/put-app-auth-config-request.schema.json +0 -93
@@ -36,11 +36,16 @@
36
36
  "type": "null"
37
37
  }
38
38
  ]
39
+ },
40
+ "low_bandwidth": {
41
+ "type": "boolean",
42
+ "description": "Whether the bridge is in its low-bandwidth mode: datapoints capped, cameras reduced or stopped. Bridge-reported."
39
43
  }
40
44
  },
41
45
  "required": [
42
46
  "online",
43
- "latency_ms"
47
+ "latency_ms",
48
+ "low_bandwidth"
44
49
  ],
45
50
  "additionalProperties": false
46
51
  },
@@ -82,6 +87,15 @@
82
87
  ],
83
88
  "additionalProperties": false
84
89
  },
90
+ "protocol_status": {
91
+ "description": "Where this robot's bridge stands against the protocol window: `current`, `deprecated` (still served, sunset date on the detail), or `refused` (its last hello was refused for its version; offline until upgraded). Absent from a cloud older than 0.21.0; read absence as `current`.",
92
+ "type": "string",
93
+ "enum": [
94
+ "current",
95
+ "deprecated",
96
+ "refused"
97
+ ]
98
+ },
85
99
  "bridge_version": {
86
100
  "anyOf": [
87
101
  {
@@ -93,6 +107,52 @@
93
107
  }
94
108
  ]
95
109
  },
110
+ "protocol_version": {
111
+ "description": "The protocol version the bridge announced in its last accepted hello; `null` before the first. Absent from a cloud older than 0.21.0.",
112
+ "anyOf": [
113
+ {
114
+ "type": "integer",
115
+ "exclusiveMinimum": 0,
116
+ "maximum": 9007199254740991
117
+ },
118
+ {
119
+ "type": "null"
120
+ }
121
+ ]
122
+ },
123
+ "protocol": {
124
+ "description": "The window verdict for `protocol_version`.",
125
+ "type": "object",
126
+ "properties": {
127
+ "status": {
128
+ "type": "string",
129
+ "enum": [
130
+ "current",
131
+ "deprecated",
132
+ "refused"
133
+ ],
134
+ "description": "Same values as `protocol_status`."
135
+ },
136
+ "sunset_at": {
137
+ "anyOf": [
138
+ {
139
+ "type": "string",
140
+ "format": "date",
141
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
142
+ },
143
+ {
144
+ "type": "null"
145
+ }
146
+ ],
147
+ "description": "ISO date the announced version stops being served; `null` when current or unknown."
148
+ }
149
+ },
150
+ "required": [
151
+ "status",
152
+ "sunset_at"
153
+ ],
154
+ "additionalProperties": false
155
+ },
96
156
  "last_hello_error": {
97
157
  "anyOf": [
98
158
  {
@@ -202,7 +262,8 @@
202
262
  "action",
203
263
  "service",
204
264
  "publisher",
205
- "camera"
265
+ "camera",
266
+ "low_bandwidth"
206
267
  ]
207
268
  },
208
269
  "code": {
@@ -36,11 +36,16 @@
36
36
  "type": "null"
37
37
  }
38
38
  ]
39
+ },
40
+ "low_bandwidth": {
41
+ "type": "boolean",
42
+ "description": "Whether the bridge is in its low-bandwidth mode: datapoints capped, cameras reduced or stopped. Bridge-reported."
39
43
  }
40
44
  },
41
45
  "required": [
42
46
  "online",
43
- "latency_ms"
47
+ "latency_ms",
48
+ "low_bandwidth"
44
49
  ],
45
50
  "additionalProperties": false
46
51
  },
@@ -81,6 +86,15 @@
81
86
  "cameras"
82
87
  ],
83
88
  "additionalProperties": false
89
+ },
90
+ "protocol_status": {
91
+ "description": "Where this robot's bridge stands against the protocol window: `current`, `deprecated` (still served, sunset date on the detail), or `refused` (its last hello was refused for its version; offline until upgraded). Absent from a cloud older than 0.21.0; read absence as `current`.",
92
+ "type": "string",
93
+ "enum": [
94
+ "current",
95
+ "deprecated",
96
+ "refused"
97
+ ]
84
98
  }
85
99
  },
86
100
  "required": [
@@ -41,11 +41,16 @@
41
41
  "type": "null"
42
42
  }
43
43
  ]
44
+ },
45
+ "low_bandwidth": {
46
+ "type": "boolean",
47
+ "description": "Whether the bridge is in its low-bandwidth mode: datapoints capped, cameras reduced or stopped. Bridge-reported."
44
48
  }
45
49
  },
46
50
  "required": [
47
51
  "online",
48
- "latency_ms"
52
+ "latency_ms",
53
+ "low_bandwidth"
49
54
  ],
50
55
  "additionalProperties": false
51
56
  },
@@ -86,6 +91,15 @@
86
91
  "cameras"
87
92
  ],
88
93
  "additionalProperties": false
94
+ },
95
+ "protocol_status": {
96
+ "description": "Where this robot's bridge stands against the protocol window: `current`, `deprecated` (still served, sunset date on the detail), or `refused` (its last hello was refused for its version; offline until upgraded). Absent from a cloud older than 0.21.0; read absence as `current`.",
97
+ "type": "string",
98
+ "enum": [
99
+ "current",
100
+ "deprecated",
101
+ "refused"
102
+ ]
89
103
  }
90
104
  },
91
105
  "required": [
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "token": {
6
+ "type": "string",
7
+ "pattern": "^frt_[0-9a-f]{32}$",
8
+ "description": "The robot's new bridge token. Returned exactly once; the previous token stops working at the bridge's next hello."
9
+ }
10
+ },
11
+ "required": [
12
+ "token"
13
+ ],
14
+ "additionalProperties": false
15
+ }
@@ -38,32 +38,38 @@
38
38
  "enum": [
39
39
  "unresolvable",
40
40
  "upload_failed",
41
- "refused",
42
- "too_large"
41
+ "refused"
43
42
  ],
44
- "description": "Why it failed. `unresolvable` means the reference names nothing the producer can find or may read, and is **permanent** — the only kind reconciliation may treat as gone. `upload_failed` means the bytes exist and the transfer did not succeed, `refused` means it was never attempted because a producer-side ceiling was hit, and `too_large` means it exceeds the upload limit and carries both numbers in `details`."
43
+ "description": "Why it failed. `unresolvable` means the reference names nothing the producer can find or may read, and is **permanent** — the only kind reconciliation may treat as gone. `upload_failed` means the bytes exist and the transfer did not succeed, and `refused` means it was never attempted, either because the robot's asset store had no room — then `details` carries the three numbers — or because a producer-side ceiling was hit."
45
44
  },
46
45
  "details": {
47
- "description": "The two numbers behind a `too_large` failure, and absent for every other kind — a forced `null` on every `unresolvable` entry buys nothing. The pairing is enforced, not merely described.",
46
+ "description": "The three numbers behind a `refused` entry the robot's store had no room for, and absent for every other kind — a forced `null` on every `unresolvable` entry buys nothing. A `refused` entry may also carry no details: the producer's own ceiling is the other half of that kind, and no store number describes it.",
48
47
  "anyOf": [
49
48
  {
50
49
  "type": "object",
51
50
  "properties": {
52
- "limit_bytes": {
51
+ "store_bytes": {
53
52
  "type": "integer",
54
53
  "exclusiveMinimum": 0,
55
54
  "maximum": 9007199254740991,
56
- "description": "The upload ceiling, in bytes."
55
+ "description": "The robot's store, in bytes."
56
+ },
57
+ "used_bytes": {
58
+ "type": "integer",
59
+ "minimum": 0,
60
+ "maximum": 9007199254740991,
61
+ "description": "Bytes the robot's assets occupy before this upload."
57
62
  },
58
63
  "size_bytes": {
59
64
  "type": "integer",
60
65
  "exclusiveMinimum": 0,
61
66
  "maximum": 9007199254740991,
62
- "description": "How large the refused file is, in bytes. With `limit_bytes` beside it a developer can tell whether to shrink the mesh or raise the limit; \"too large\" alone answers neither."
67
+ "description": "The refused upload, in bytes."
63
68
  }
64
69
  },
65
70
  "required": [
66
- "limit_bytes",
71
+ "store_bytes",
72
+ "used_bytes",
67
73
  "size_bytes"
68
74
  ],
69
75
  "additionalProperties": false
@@ -29,7 +29,8 @@
29
29
  "action",
30
30
  "service",
31
31
  "publisher",
32
- "camera"
32
+ "camera",
33
+ "low_bandwidth"
33
34
  ]
34
35
  },
35
36
  "code": {
@@ -0,0 +1,37 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "type": {
6
+ "type": "string",
7
+ "const": "link_mode"
8
+ },
9
+ "low_bandwidth": {
10
+ "type": "boolean",
11
+ "description": "Whether the mode is active after this transition."
12
+ },
13
+ "reason": {
14
+ "type": "string",
15
+ "enum": [
16
+ "lag",
17
+ "dwell",
18
+ "forced",
19
+ "recovered"
20
+ ],
21
+ "description": "`lag`: the cloud-measured lag crossed the threshold; `dwell`: the bridge-measured queue dwell did; `forced`: `mode: on` or `off`; `recovered`: both measures stayed at or below the exit threshold."
22
+ },
23
+ "at_ms": {
24
+ "type": "integer",
25
+ "minimum": 0,
26
+ "maximum": 9007199254740991,
27
+ "description": "Bridge time of the transition, epoch milliseconds."
28
+ }
29
+ },
30
+ "required": [
31
+ "type",
32
+ "low_bandwidth",
33
+ "reason",
34
+ "at_ms"
35
+ ],
36
+ "additionalProperties": false
37
+ }
@@ -17,6 +17,10 @@
17
17
  "type": "integer",
18
18
  "minimum": 0,
19
19
  "maximum": 9007199254740991
20
+ },
21
+ "backfill": {
22
+ "description": "true when the sample was captured while the bridge was disconnected and is being replayed after the reconnect. The cloud keeps such a sample out of its lag measure; absent means live.",
23
+ "type": "boolean"
20
24
  }
21
25
  },
22
26
  "required": [
@@ -400,23 +400,41 @@ export declare const appAuthConfig: z.ZodObject<{
400
400
  }, z.core.$strip>;
401
401
  export type AppAuthConfig = z.infer<typeof appAuthConfig>;
402
402
  /**
403
- * `PUT /api/apps/:id/auth-config` — a replace, not a merge, and `.strict()`.
403
+ * `PUT /api/apps/:id/auth-config/registration` — who may get in, and from
404
+ * where.
404
405
  *
405
- * `oidc_callback_url` and `updated_at` are omitted because both are the
406
- * server's: see the callback URL's own note for why a writable one would be a
407
- * redirect-target hole rather than a convenience.
406
+ * Three slices rather than one document, and each still a **replace** with
407
+ * every field of its slice required: three screens carving up one
408
+ * all-required request is how a field nobody's screen shows becomes a field
409
+ * somebody's save clears. The slice states its own ownership, so a new field
410
+ * lands in one schema and one screen.
411
+ *
412
+ * The merge is the server's, against the stored row — never the caller's,
413
+ * whose copy may be older than the row it would overwrite.
408
414
  */
409
- export declare const putAppAuthConfigRequest: z.ZodObject<{
415
+ export declare const putAppAuthRegistrationRequest: z.ZodObject<{
410
416
  self_registration: z.ZodBoolean;
411
417
  allowed_domains: z.ZodArray<z.ZodString>;
412
418
  allowed_origins: z.ZodArray<z.ZodString>;
413
- mcp_enabled: z.ZodBoolean;
419
+ }, z.core.$strict>;
420
+ export type PutAppAuthRegistrationRequest = z.infer<typeof putAppAuthRegistrationRequest>;
421
+ /** `PUT /api/apps/:id/auth-config/urls` — the three pages Fleetless's mails point at. */
422
+ export declare const putAppAuthUrlsRequest: z.ZodObject<{
414
423
  invite_url: z.ZodNullable<z.ZodString>;
415
424
  verify_url: z.ZodNullable<z.ZodString>;
416
425
  reset_url: z.ZodNullable<z.ZodString>;
426
+ }, z.core.$strict>;
427
+ export type PutAppAuthUrlsRequest = z.infer<typeof putAppAuthUrlsRequest>;
428
+ /**
429
+ * `PUT /api/apps/:id/auth-config/mcp` — the switch and the login URL, which
430
+ * belong together: on without a URL refuses every sign-in, in the MCP
431
+ * client's browser mid-OAuth, where no console screen ever sees it.
432
+ */
433
+ export declare const putAppAuthMcpRequest: z.ZodObject<{
434
+ mcp_enabled: z.ZodBoolean;
417
435
  mcp_login_url: z.ZodNullable<z.ZodString>;
418
436
  }, z.core.$strict>;
419
- export type PutAppAuthConfigRequest = z.infer<typeof putAppAuthConfigRequest>;
437
+ export type PutAppAuthMcpRequest = z.infer<typeof putAppAuthMcpRequest>;
420
438
  /**
421
439
  * The three mails a developer may replace with their own template.
422
440
  * Mails to *Fleetless* users — a team invitation, a console password reset —
package/dist/app-users.js CHANGED
@@ -481,14 +481,32 @@ export const appAuthConfig = z.object({
481
481
  updated_at: z.iso.datetime().meta({ description: 'When the configuration was last written, as an ISO 8601 timestamp.' }),
482
482
  });
483
483
  /**
484
- * `PUT /api/apps/:id/auth-config` — a replace, not a merge, and `.strict()`.
484
+ * `PUT /api/apps/:id/auth-config/registration` — who may get in, and from
485
+ * where.
485
486
  *
486
- * `oidc_callback_url` and `updated_at` are omitted because both are the
487
- * server's: see the callback URL's own note for why a writable one would be a
488
- * redirect-target hole rather than a convenience.
487
+ * Three slices rather than one document, and each still a **replace** with
488
+ * every field of its slice required: three screens carving up one
489
+ * all-required request is how a field nobody's screen shows becomes a field
490
+ * somebody's save clears. The slice states its own ownership, so a new field
491
+ * lands in one schema and one screen.
492
+ *
493
+ * The merge is the server's, against the stored row — never the caller's,
494
+ * whose copy may be older than the row it would overwrite.
495
+ */
496
+ export const putAppAuthRegistrationRequest = appAuthConfig
497
+ .pick({ self_registration: true, allowed_domains: true, allowed_origins: true })
498
+ .strict();
499
+ /** `PUT /api/apps/:id/auth-config/urls` — the three pages Fleetless's mails point at. */
500
+ export const putAppAuthUrlsRequest = appAuthConfig
501
+ .pick({ invite_url: true, verify_url: true, reset_url: true })
502
+ .strict();
503
+ /**
504
+ * `PUT /api/apps/:id/auth-config/mcp` — the switch and the login URL, which
505
+ * belong together: on without a URL refuses every sign-in, in the MCP
506
+ * client's browser mid-OAuth, where no console screen ever sees it.
489
507
  */
490
- export const putAppAuthConfigRequest = appAuthConfig
491
- .omit({ oidc_callback_url: true, updated_at: true })
508
+ export const putAppAuthMcpRequest = appAuthConfig
509
+ .pick({ mcp_enabled: true, mcp_login_url: true })
492
510
  .strict();
493
511
  /**
494
512
  * The three mails a developer may replace with their own template.
package/dist/apps.d.ts CHANGED
@@ -42,6 +42,28 @@ export declare const appListResponse: z.ZodObject<{
42
42
  }, z.core.$strip>>;
43
43
  }, z.core.$strip>;
44
44
  export type AppListResponse = z.infer<typeof appListResponse>;
45
+ /**
46
+ * What deleting an app would destroy — read before the irreversible click,
47
+ * and carried again by the `app.deleted` audit event.
48
+ *
49
+ * **Six numbers, never a sum**, for the reason `robotDeletionSummary` states
50
+ * at length: the console reads this aloud as one sentence, and a total would
51
+ * describe six unrelated magnitudes with one figure on the one screen whose
52
+ * entire justification is naming what cannot be undone.
53
+ *
54
+ * Robots are not here because they do not go: they belong to the
55
+ * organization, not to the app. The audit trail is not here either — a record
56
+ * of what happened outlives the thing it happened to.
57
+ */
58
+ export declare const appDeletionSummary: z.ZodObject<{
59
+ user_count: z.ZodNumber;
60
+ role_count: z.ZodNumber;
61
+ server_key_count: z.ZodNumber;
62
+ invitation_count: z.ZodNumber;
63
+ oidc_provider_count: z.ZodNumber;
64
+ mail_template_count: z.ZodNumber;
65
+ }, z.core.$strip>;
66
+ export type AppDeletionSummary = z.infer<typeof appDeletionSummary>;
45
67
  /**
46
68
  * **`robot_ids` is accepted here, and `.strict()` catches everything else.**
47
69
  * A create shape carrying `name` and `identifier` only would let zod strip an
package/dist/apps.js CHANGED
@@ -78,6 +78,39 @@ export const appListResponse = z.object({
78
78
  description: 'Every app of the caller\'s organisation, oldest first by `created_at`. The org scope is the whole filter — there is no id to narrow by and nothing to refuse.',
79
79
  }),
80
80
  });
81
+ /**
82
+ * What deleting an app would destroy — read before the irreversible click,
83
+ * and carried again by the `app.deleted` audit event.
84
+ *
85
+ * **Six numbers, never a sum**, for the reason `robotDeletionSummary` states
86
+ * at length: the console reads this aloud as one sentence, and a total would
87
+ * describe six unrelated magnitudes with one figure on the one screen whose
88
+ * entire justification is naming what cannot be undone.
89
+ *
90
+ * Robots are not here because they do not go: they belong to the
91
+ * organization, not to the app. The audit trail is not here either — a record
92
+ * of what happened outlives the thing it happened to.
93
+ */
94
+ export const appDeletionSummary = z.object({
95
+ user_count: z.number().int().nonnegative().meta({
96
+ description: 'App users deleted with the app. They are the developer\'s own customers, not Fleetless users, and exist in no other app.',
97
+ }),
98
+ role_count: z.number().int().nonnegative().meta({
99
+ description: 'Roles deleted with the app, each with its per-robot slug grants.',
100
+ }),
101
+ server_key_count: z.number().int().nonnegative().meta({
102
+ description: 'Server keys deleted with the app. A client still holding one is refused at its next request.',
103
+ }),
104
+ invitation_count: z.number().int().nonnegative().meta({
105
+ description: 'Outstanding invitations — unspent and unexpired — that will never be accepted.',
106
+ }),
107
+ oidc_provider_count: z.number().int().nonnegative().meta({
108
+ description: 'Identity providers configured for this app. The providers themselves are somebody else\'s; only this app\'s configuration of them goes.',
109
+ }),
110
+ mail_template_count: z.number().int().nonnegative().meta({
111
+ description: 'Custom mail templates, of at most three. A kind using the Fleetless default text is not counted — there is no row to lose.',
112
+ }),
113
+ });
81
114
  /**
82
115
  * **`robot_ids` is accepted here, and `.strict()` catches everything else.**
83
116
  * A create shape carrying `name` and `identifier` only would let zod strip an