@fleetless/contracts 1.1.0 → 2.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.
- package/CHANGELOG.md +32 -1
- package/artifacts/constants.json +30 -4
- package/artifacts/openapi.json +667 -98
- package/artifacts/routes.json +97 -6
- package/artifacts/schema/apply-error.schema.json +2 -1
- package/artifacts/schema/asset-list-response.schema.json +77 -12
- package/artifacts/schema/asset-sync-status.schema.json +35 -8
- package/artifacts/schema/asset.schema.json +2 -3
- package/artifacts/schema/assets-clear-response.schema.json +23 -0
- package/artifacts/schema/authorization-server-metadata.schema.json +1 -1
- package/artifacts/schema/bridge-asset-progress.schema.json +14 -8
- package/artifacts/schema/bridge-config-applied.schema.json +2 -1
- package/artifacts/schema/bridge-link-mode.schema.json +36 -0
- package/artifacts/schema/bridge-state.schema.json +6 -1
- package/artifacts/schema/client-robot-list-item.schema.json +6 -1
- package/artifacts/schema/client-robot-list-response.schema.json +6 -1
- package/artifacts/schema/cloud-config.schema.json +90 -5
- package/artifacts/schema/cloud-hello-ok.schema.json +45 -0
- package/artifacts/schema/cloud-ping.schema.json +27 -1
- package/artifacts/schema/config-draft-response.schema.json +90 -5
- package/artifacts/schema/config-state.schema.json +2 -1
- package/artifacts/schema/config-version-response.schema.json +90 -5
- package/artifacts/schema/datapoint-config.schema.json +5 -0
- package/artifacts/schema/datapoint-frame.schema.json +4 -0
- package/artifacts/schema/datapoint-list-response.schema.json +2 -2
- package/artifacts/schema/dynamic-client-registration-request.schema.json +1 -1
- package/artifacts/schema/dynamic-client-registration-response.schema.json +1 -1
- package/artifacts/schema/joint-state-put-request.schema.json +23 -0
- package/artifacts/schema/joint-state-put-response.schema.json +24 -0
- package/artifacts/schema/oauth-token-request.schema.json +79 -41
- package/artifacts/schema/oauth-token-response.schema.json +1 -1
- package/artifacts/schema/org-quota-usage-counts.schema.json +0 -5
- package/artifacts/schema/org-quota-usage.schema.json +1 -12
- package/artifacts/schema/org-quotas.schema.json +1 -7
- package/artifacts/schema/robot-config-doc.schema.json +90 -5
- package/artifacts/schema/robot-deletion-summary.schema.json +2 -1
- package/artifacts/schema/robot-detail-response.schema.json +63 -2
- package/artifacts/schema/robot-list-item.schema.json +15 -1
- package/artifacts/schema/robot-list-response.schema.json +15 -1
- package/artifacts/schema/robot-token-rotate-response.schema.json +15 -0
- package/artifacts/schema-outgoing/bridge-asset-progress.schema.json +14 -8
- package/artifacts/schema-outgoing/bridge-config-applied.schema.json +2 -1
- package/artifacts/schema-outgoing/bridge-link-mode.schema.json +37 -0
- package/artifacts/schema-outgoing/datapoint-frame.schema.json +4 -0
- package/dist/assets.d.ts +85 -50
- package/dist/assets.js +152 -62
- package/dist/audit.d.ts +1 -1
- package/dist/audit.js +1 -1
- package/dist/client-robots.d.ts +2 -0
- package/dist/common.d.ts +10 -0
- package/dist/common.js +16 -1
- package/dist/config.d.ts +69 -1
- package/dist/config.js +86 -6
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -8
- package/dist/index.d.ts +10 -10
- package/dist/index.js +5 -5
- package/dist/oauth.d.ts +34 -19
- package/dist/oauth.js +39 -24
- package/dist/protocol.d.ts +150 -71
- package/dist/protocol.js +144 -87
- package/dist/rest.d.ts +137 -35
- package/dist/rest.js +98 -66
- package/dist/routes.js +68 -19
- package/package.json +1 -1
- package/artifacts/schema/bridge-pressure.schema.json +0 -292
package/artifacts/routes.json
CHANGED
|
@@ -1972,7 +1972,7 @@
|
|
|
1972
1972
|
"rate_limited"
|
|
1973
1973
|
],
|
|
1974
1974
|
"transport": "http",
|
|
1975
|
-
"notes": "RFC 7591. **The request schema is what this endpoint accepts, not what it parses**: the handler reads the body field by field, because §3.2.2 distinguishes `invalid_redirect_uri` from `invalid_client_metadata` and one `safeParse` failure cannot say which of the two a caller earned. The shape is deliberately **not** strict, which is the schema agreeing with §3.1 rather than a gap in it — a conforming client sends `client_uri`, `logo_uri` and `software_id`, and both the schema and the server ignore them. `client_name` and `redirect_uris` are the two fields read; `grant_types`, `response_types` and `scope` are accepted and ignored. What comes back is what was actually granted, which §3.2.1 allows a server to substitute — this authorization server
|
|
1975
|
+
"notes": "RFC 7591. **The request schema is what this endpoint accepts, not what it parses**: the handler reads the body field by field, because §3.2.2 distinguishes `invalid_redirect_uri` from `invalid_client_metadata` and one `safeParse` failure cannot say which of the two a caller earned. The shape is deliberately **not** strict, which is the schema agreeing with §3.1 rather than a gap in it — a conforming client sends `client_uri`, `logo_uri` and `software_id`, and both the schema and the server ignore them. `client_name` and `redirect_uris` are the two fields read; `grant_types`, `response_types` and `scope` are accepted and ignored. What comes back is what was actually granted, which §3.2.1 allows a server to substitute — this authorization server grants `authorization_code` and `refresh_token` to every registration. The registration carries a TTL. Refusals are `oauthError`; the rate limiter answers `apiError`."
|
|
1976
1976
|
},
|
|
1977
1977
|
{
|
|
1978
1978
|
"method": "GET",
|
|
@@ -2121,7 +2121,7 @@
|
|
|
2121
2121
|
"response": "oauth-token-response",
|
|
2122
2122
|
"errors": [],
|
|
2123
2123
|
"transport": "http",
|
|
2124
|
-
"notes": "
|
|
2124
|
+
"notes": "`authorization_code` mints an `mcp_session` access token bound to the central resource and a refresh token; `refresh_token` rotates that pair, and the presented refresh token is consumed — a second presentation revokes the session, as on `/api/auth/refresh`. The refresh token lives ninety days from its last use and is bound to the `client_id` it was issued to. A refresh re-reads the Fleetless user, so a removed account cannot refresh. Refusals are RFC 6749 §5.2's `oauthError`, so this route emits none of the codes in this reference. The code is single-use, PKCE-verified, and its `resource` must match the audience it was authorized for; a `resource` on a refresh must match the session's audience, and is checked before the token is consumed."
|
|
2125
2125
|
},
|
|
2126
2126
|
{
|
|
2127
2127
|
"method": "GET",
|
|
@@ -2491,7 +2491,7 @@
|
|
|
2491
2491
|
"not_found"
|
|
2492
2492
|
],
|
|
2493
2493
|
"transport": "http",
|
|
2494
|
-
"notes": "RFC 7591, the same wire and the same handler as `POST /mcp/oauth/register` — one implementation, because a second answer to \"is this redirect URI acceptable\" would agree with the first only by luck. The request schema is what the endpoint accepts rather than what it parses, for the reason that row gives: §3.2.2 needs two distinguishable refusals and one `safeParse` failure offers one. `client_name` and `redirect_uris` are read; `grant_types`, `response_types` and `scope` are accepted and ignored, and what comes back is what was actually granted, which §3.2.1 allows —
|
|
2494
|
+
"notes": "RFC 7591, the same wire and the same handler as `POST /mcp/oauth/register` — one implementation, because a second answer to \"is this redirect URI acceptable\" would agree with the first only by luck. The request schema is what the endpoint accepts rather than what it parses, for the reason that row gives: §3.2.2 needs two distinguishable refusals and one `safeParse` failure offers one. `client_name` and `redirect_uris` are read; `grant_types`, `response_types` and `scope` are accepted and ignored, and what comes back is what was actually granted, which §3.2.1 allows — this authorization server grants `authorization_code` and `refresh_token` to every registration. The registration carries a TTL. \n\n**The registration is scoped to this app.** A `client_id` minted here authorizes at this app's endpoint and nowhere else, so a client registered against one app cannot walk into another's authorize with it, and a developer who switches MCP off is not left with strangers' registrations valid somewhere adjacent. \n\nRefusals are `oauthError`; the rate limiter and `404 not_found` answer `apiError`. That `404` covers an unknown identifier **and** an app with the switch off, mirroring the two metadata documents this endpoint is discovered from — a client that could not read those has no business registering here, and giving it a third distinct answer would only tell it something the documents deliberately do not."
|
|
2495
2495
|
},
|
|
2496
2496
|
{
|
|
2497
2497
|
"method": "GET",
|
|
@@ -2540,7 +2540,7 @@
|
|
|
2540
2540
|
"response": "oauth-token-response",
|
|
2541
2541
|
"errors": [],
|
|
2542
2542
|
"transport": "http",
|
|
2543
|
-
"notes": "
|
|
2543
|
+
"notes": "`authorization_code`, PKCE-verified and single-use, and `refresh_token`, which rotates the pair the exchange minted; the refresh token lives ninety days from its last use, is bound to its client and to this app, and a refresh re-reads the app user's status and their standing consent to the client, so a block or a withdrawn consent ends the session at its next refresh at the latest. **The `aud` is this app's endpoint URL on the canonical public base**, and the code's `resource` must match it — that is the whole of what stops a token minted for one app being spent at another's endpoint. \n\n**Every refusal is RFC 6749 §5.2's `oauthError`, so this route emits none of the codes in this reference — including the ones about the app.** An unknown identifier and a switched-off app are `invalid_client` here, not the `404` and `403` the authorize route beside it answers. The difference is who reads the answer: authorize is walked by a browser and its refusal is read by a person, while this endpoint is called by a client's own code in the middle of a flow, and handing that code an envelope its OAuth library cannot parse turns a clean refusal into an unexplained crash."
|
|
2544
2544
|
},
|
|
2545
2545
|
{
|
|
2546
2546
|
"method": "POST",
|
|
@@ -3052,6 +3052,66 @@
|
|
|
3052
3052
|
"transport": "http",
|
|
3053
3053
|
"notes": "`token` is the only moment the raw bridge token exists outside the caller's hands — the cloud stores a hash, so nothing can read it back and a caller who loses it rotates rather than recovers. Audited: this mints a credential that can speak for the org from anywhere, and the event carries no `details`, because the one interesting value here is the token. `max_robots` is checked before anything is created, which is only safe because robot deletion exists."
|
|
3054
3054
|
},
|
|
3055
|
+
{
|
|
3056
|
+
"method": "POST",
|
|
3057
|
+
"path": "/api/robots/:id/token/rotate",
|
|
3058
|
+
"section": "robots",
|
|
3059
|
+
"summary": "Mints a new bridge token for the robot and invalidates the old one.",
|
|
3060
|
+
"audience": "developer",
|
|
3061
|
+
"auth": "developer",
|
|
3062
|
+
"rateLimited": false,
|
|
3063
|
+
"ownerTier": true,
|
|
3064
|
+
"status": 201,
|
|
3065
|
+
"params": [
|
|
3066
|
+
{
|
|
3067
|
+
"name": "id",
|
|
3068
|
+
"description": "The robot's uuid, as returned by `POST /api/robots` or listed by `GET /api/robots`."
|
|
3069
|
+
}
|
|
3070
|
+
],
|
|
3071
|
+
"query": null,
|
|
3072
|
+
"request": null,
|
|
3073
|
+
"response": "robot-token-rotate-response",
|
|
3074
|
+
"errors": [
|
|
3075
|
+
"unauthorized",
|
|
3076
|
+
"token_expired",
|
|
3077
|
+
"token_revoked",
|
|
3078
|
+
"tier_required",
|
|
3079
|
+
"invalid_uuid",
|
|
3080
|
+
"not_found"
|
|
3081
|
+
],
|
|
3082
|
+
"transport": "http",
|
|
3083
|
+
"notes": "Owner tier, behind the org-scoped lookup, so a developer-tier admin sees the `404` a stranger would for a robot outside their org rather than a tier refusal that confirms the id exists. `token` is the only moment the new secret exists outside the caller's hands — the cloud stores a hash — so a caller who loses it rotates again. Audited as `robot.token_rotated`, with no `details`: the one interesting value here is the token. \n\n**It stops the bridge that is connected right now.** The old secret is gone the instant the hash is replaced, so the cloud closes that socket with `CLOSE_TOKEN_ROTATED` rather than leaving a bridge speaking on a credential nothing would accept again. A bridge that does not know the code reconnects and is refused at hello as `invalid_token`, which is the honest answer and ends the same way. **The robot is offline until somebody puts the new token on it** — this is a deliberate interruption, not a background rekey, and a fleet cannot be rotated without a visit to each robot."
|
|
3084
|
+
},
|
|
3085
|
+
{
|
|
3086
|
+
"method": "PUT",
|
|
3087
|
+
"path": "/api/robots/:id/urdf/joint-state",
|
|
3088
|
+
"section": "robots",
|
|
3089
|
+
"summary": "Chooses the datapoint whose joint positions move the robot's URDF, or clears it.",
|
|
3090
|
+
"audience": "developer",
|
|
3091
|
+
"auth": "developer",
|
|
3092
|
+
"rateLimited": false,
|
|
3093
|
+
"ownerTier": false,
|
|
3094
|
+
"status": 200,
|
|
3095
|
+
"params": [
|
|
3096
|
+
{
|
|
3097
|
+
"name": "id",
|
|
3098
|
+
"description": "The robot's uuid, as returned by `POST /api/robots` or listed by `GET /api/robots`."
|
|
3099
|
+
}
|
|
3100
|
+
],
|
|
3101
|
+
"query": null,
|
|
3102
|
+
"request": "joint-state-put-request",
|
|
3103
|
+
"response": "joint-state-put-response",
|
|
3104
|
+
"errors": [
|
|
3105
|
+
"unauthorized",
|
|
3106
|
+
"token_expired",
|
|
3107
|
+
"token_revoked",
|
|
3108
|
+
"invalid_uuid",
|
|
3109
|
+
"not_found",
|
|
3110
|
+
"validation_error"
|
|
3111
|
+
],
|
|
3112
|
+
"transport": "http",
|
|
3113
|
+
"notes": "**What qualifies**: a datapoint of the **published** configuration whose ROS type is `sensor_msgs/msg/JointState` and which carries no `field` — the whole message, because positions and names arrive together and a single extracted field is half of a pose. Anything else is a `validation_error` naming that rule rather than a stored mapping that renders a battery reading as a robot. `{ \"slug\": null }` clears it, which is why the field is required and nullable rather than optional. \n\n**The mapping cannot outlive what it points at.** Every successful publish re-checks it against the new document and clears it when it no longer qualifies, recording `robot.joint_state_cleared` with the version that did it; a slug rename rewrites it like every other reference the editor already rewrites; deleting the robot takes it along. Every write through this route — a slug or `null` — is on the record too, as `robot.joint_state_set` with the actor and the slug, so a clear a person made is never mistaken for one a publish made. The stored value reads back on `GET /api/robots/:id/assets` as `joint_state_slug`, so a renderer fetches the URDF, the meshes and the mapping from one place."
|
|
3114
|
+
},
|
|
3055
3115
|
{
|
|
3056
3116
|
"method": "GET",
|
|
3057
3117
|
"path": "/api/robots",
|
|
@@ -4467,6 +4527,38 @@
|
|
|
4467
4527
|
"transport": "http",
|
|
4468
4528
|
"notes": "Developer sessions only, like starting a sync: the guard admits three caller kinds and the handler answers `401 unauthorized` to the other two. A sync belonging to another robot reads exactly like one that never existed, which is why the robot is resolved first."
|
|
4469
4529
|
},
|
|
4530
|
+
{
|
|
4531
|
+
"method": "DELETE",
|
|
4532
|
+
"path": "/api/robots/:id/assets",
|
|
4533
|
+
"section": "assets",
|
|
4534
|
+
"summary": "Empties a robot's asset store: every URDF, mesh and texture, gone at once.",
|
|
4535
|
+
"audience": "client",
|
|
4536
|
+
"auth": "developer_or_client",
|
|
4537
|
+
"rateLimited": false,
|
|
4538
|
+
"ownerTier": true,
|
|
4539
|
+
"status": 200,
|
|
4540
|
+
"params": [
|
|
4541
|
+
{
|
|
4542
|
+
"name": "id",
|
|
4543
|
+
"description": "The robot's uuid, as returned by `POST /api/robots` or listed by `GET /api/robots`."
|
|
4544
|
+
}
|
|
4545
|
+
],
|
|
4546
|
+
"query": null,
|
|
4547
|
+
"request": null,
|
|
4548
|
+
"response": "assets-clear-response",
|
|
4549
|
+
"errors": [
|
|
4550
|
+
"unauthorized",
|
|
4551
|
+
"token_expired",
|
|
4552
|
+
"token_revoked",
|
|
4553
|
+
"forbidden",
|
|
4554
|
+
"tier_required",
|
|
4555
|
+
"invalid_uuid",
|
|
4556
|
+
"not_found",
|
|
4557
|
+
"busy"
|
|
4558
|
+
],
|
|
4559
|
+
"transport": "http",
|
|
4560
|
+
"notes": "The store's escape hatch: a full store is never a dead end, and this is the blunt third of the three answers to it — the URDF upload is exempt from the gate, reconcile after a sync already frees what the new URDF stopped referencing, and this route lets an Owner clear the robot outright. Owner tier, unconditionally, like starting a sync. Removes every asset of the robot and resets its store to `0`; the next sync fills it again. It does not touch the bridge's availability report — `urdf_available` still answers from the connected robot, unrelated to what this cloud happens to have stored. A clear while a sync is running is `409 busy` naming that sync's details, the same refusal starting a second sync gets, because deleting under a running upload would leave the store counter wrong."
|
|
4561
|
+
},
|
|
4470
4562
|
{
|
|
4471
4563
|
"method": "GET",
|
|
4472
4564
|
"path": "/api/org/quotas",
|
|
@@ -4622,14 +4714,13 @@
|
|
|
4622
4714
|
"errors": [
|
|
4623
4715
|
"unauthorized",
|
|
4624
4716
|
"rate_limited",
|
|
4625
|
-
"asset_too_large",
|
|
4626
4717
|
"validation_error",
|
|
4627
4718
|
"not_found",
|
|
4628
4719
|
"quota_exceeded",
|
|
4629
4720
|
"bad_request"
|
|
4630
4721
|
],
|
|
4631
4722
|
"transport": "http",
|
|
4632
|
-
"notes": "The body is the **raw file bytes**, not JSON, so it has no request schema; everything about the file — its kind, its name, its sync id and its announced size — rides in the `x-fleetless-asset-*` headers `ASSET_UPLOAD_HEADERS` names. The credential is a short-lived upload token minted by `POST /api/robots/:id/assets/sync`, verified in a `preParsing` hook so a refusal precedes the work rather than following it: a `preHandler` would already have buffered the whole file.
|
|
4723
|
+
"notes": "The body is the **raw file bytes**, not JSON, so it has no request schema; everything about the file — its kind, its name, its sync id and its announced size — rides in the `x-fleetless-asset-*` headers `ASSET_UPLOAD_HEADERS` names. The credential is a short-lived upload token minted by `POST /api/robots/:id/assets/sync`, verified in a `preParsing` hook so a refusal precedes the work rather than following it: a `preHandler` would already have buffered the whole file. **Nothing is refused for its own size** — the robot's asset store is the only limit, so the announced size is checked there against `ROBOT_ASSET_STORE_BYTES` and a file with no room left answers `409 quota_exceeded` carrying `store_bytes`, `used_bytes` and `size_bytes`, while the sync carries on with the next file. Past that, the server's own body limit answers a bare `413 bad_request` with none of those numbers in it. Rate limited per robot inside that same hook, which is why `rateLimited` is `false`: there is no rate-limiting preHandler registered on this route. The URDF itself is never refused for the store; only meshes and textures are charged against it."
|
|
4633
4724
|
},
|
|
4634
4725
|
{
|
|
4635
4726
|
"method": "GET",
|
|
@@ -24,10 +24,9 @@
|
|
|
24
24
|
"enum": [
|
|
25
25
|
"urdf",
|
|
26
26
|
"mesh",
|
|
27
|
-
"texture"
|
|
28
|
-
"other"
|
|
27
|
+
"texture"
|
|
29
28
|
],
|
|
30
|
-
"description": "What the file is: the `urdf` itself, a `mesh` it references, a `texture` a mesh or the URDF paints with
|
|
29
|
+
"description": "What the file is: the `urdf` itself, a `mesh` it references, or a `texture` a mesh or the URDF paints with. A renderer decides from this alone, before fetching anything, what to pre-fetch."
|
|
31
30
|
},
|
|
32
31
|
"name": {
|
|
33
32
|
"type": "string",
|
|
@@ -128,32 +127,38 @@
|
|
|
128
127
|
"enum": [
|
|
129
128
|
"unresolvable",
|
|
130
129
|
"upload_failed",
|
|
131
|
-
"refused"
|
|
132
|
-
"too_large"
|
|
130
|
+
"refused"
|
|
133
131
|
],
|
|
134
|
-
"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
|
|
132
|
+
"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."
|
|
135
133
|
},
|
|
136
134
|
"details": {
|
|
137
|
-
"description": "The
|
|
135
|
+
"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.",
|
|
138
136
|
"anyOf": [
|
|
139
137
|
{
|
|
140
138
|
"type": "object",
|
|
141
139
|
"properties": {
|
|
142
|
-
"
|
|
140
|
+
"store_bytes": {
|
|
143
141
|
"type": "integer",
|
|
144
142
|
"exclusiveMinimum": 0,
|
|
145
143
|
"maximum": 9007199254740991,
|
|
146
|
-
"description": "The
|
|
144
|
+
"description": "The robot's store, in bytes."
|
|
145
|
+
},
|
|
146
|
+
"used_bytes": {
|
|
147
|
+
"type": "integer",
|
|
148
|
+
"minimum": 0,
|
|
149
|
+
"maximum": 9007199254740991,
|
|
150
|
+
"description": "Bytes the robot's assets occupy before this upload."
|
|
147
151
|
},
|
|
148
152
|
"size_bytes": {
|
|
149
153
|
"type": "integer",
|
|
150
154
|
"exclusiveMinimum": 0,
|
|
151
155
|
"maximum": 9007199254740991,
|
|
152
|
-
"description": "
|
|
156
|
+
"description": "The refused upload, in bytes."
|
|
153
157
|
}
|
|
154
158
|
},
|
|
155
159
|
"required": [
|
|
156
|
-
"
|
|
160
|
+
"store_bytes",
|
|
161
|
+
"used_bytes",
|
|
157
162
|
"size_bytes"
|
|
158
163
|
],
|
|
159
164
|
"additionalProperties": false
|
|
@@ -184,6 +189,25 @@
|
|
|
184
189
|
],
|
|
185
190
|
"description": "Why the sync ended as it did, when that is not a per-reference fact. `null` when `failed` already says everything there is to say."
|
|
186
191
|
},
|
|
192
|
+
"stored": {
|
|
193
|
+
"anyOf": [
|
|
194
|
+
{
|
|
195
|
+
"type": "integer",
|
|
196
|
+
"minimum": 0,
|
|
197
|
+
"maximum": 9007199254740991
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"type": "null"
|
|
201
|
+
}
|
|
202
|
+
],
|
|
203
|
+
"description": "How many of the announced files the cloud's store actually holds. Counted once, after the robot reports the sync done, and `null` until then — nobody has looked yet. Read it against `announced`: `state` is what the robot reported, this is what arrived."
|
|
204
|
+
},
|
|
205
|
+
"announced": {
|
|
206
|
+
"type": "integer",
|
|
207
|
+
"minimum": 0,
|
|
208
|
+
"maximum": 9007199254740991,
|
|
209
|
+
"description": "How many files the robot announced for this sync — the URDF, if it has one, plus every mesh URI its description references. `0` when the robot announced nothing, and also `0` until it has answered at all: read it beside `stored`, which stays `null` until the terminal frame."
|
|
210
|
+
},
|
|
187
211
|
"started_at": {
|
|
188
212
|
"type": "string",
|
|
189
213
|
"format": "date-time",
|
|
@@ -205,6 +229,8 @@
|
|
|
205
229
|
"total",
|
|
206
230
|
"failed",
|
|
207
231
|
"reason",
|
|
232
|
+
"stored",
|
|
233
|
+
"announced",
|
|
208
234
|
"started_at",
|
|
209
235
|
"updated_at"
|
|
210
236
|
],
|
|
@@ -276,13 +302,52 @@
|
|
|
276
302
|
}
|
|
277
303
|
],
|
|
278
304
|
"description": "What the connected bridge says it *could* transfer — deliberately separate from what has been transferred. `null` when no bridge is connected, distinct from `false`: \"no robot is online to ask\" and \"the robot has no URDF\" send a developer to different places. After a publisher is killed rather than shut down this can read `true` for some seconds, on the underlying DDS liveliness timeout rather than on any check made here."
|
|
305
|
+
},
|
|
306
|
+
"store": {
|
|
307
|
+
"type": "object",
|
|
308
|
+
"properties": {
|
|
309
|
+
"bytes": {
|
|
310
|
+
"type": "integer",
|
|
311
|
+
"exclusiveMinimum": 0,
|
|
312
|
+
"maximum": 9007199254740991,
|
|
313
|
+
"description": "The robot's asset store, `ROBOT_ASSET_STORE_BYTES`."
|
|
314
|
+
},
|
|
315
|
+
"used_bytes": {
|
|
316
|
+
"type": "integer",
|
|
317
|
+
"minimum": 0,
|
|
318
|
+
"maximum": 9007199254740991,
|
|
319
|
+
"description": "Bytes its assets occupy."
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
"required": [
|
|
323
|
+
"bytes",
|
|
324
|
+
"used_bytes"
|
|
325
|
+
],
|
|
326
|
+
"additionalProperties": false,
|
|
327
|
+
"description": "How full this robot's store is."
|
|
328
|
+
},
|
|
329
|
+
"joint_state_slug": {
|
|
330
|
+
"anyOf": [
|
|
331
|
+
{
|
|
332
|
+
"type": "string",
|
|
333
|
+
"minLength": 2,
|
|
334
|
+
"maxLength": 63,
|
|
335
|
+
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$"
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"type": "null"
|
|
339
|
+
}
|
|
340
|
+
],
|
|
341
|
+
"description": "The whole-message `sensor_msgs/msg/JointState` datapoint that drives the console's URDF viewer; null when none is chosen or a publish removed it. Set through `PUT /api/robots/:id/urdf/joint-state`."
|
|
279
342
|
}
|
|
280
343
|
},
|
|
281
344
|
"required": [
|
|
282
345
|
"assets",
|
|
283
346
|
"active_sync",
|
|
284
347
|
"urdf",
|
|
285
|
-
"urdf_available"
|
|
348
|
+
"urdf_available",
|
|
349
|
+
"store",
|
|
350
|
+
"joint_state_slug"
|
|
286
351
|
],
|
|
287
352
|
"additionalProperties": false
|
|
288
353
|
}
|
|
@@ -52,32 +52,38 @@
|
|
|
52
52
|
"enum": [
|
|
53
53
|
"unresolvable",
|
|
54
54
|
"upload_failed",
|
|
55
|
-
"refused"
|
|
56
|
-
"too_large"
|
|
55
|
+
"refused"
|
|
57
56
|
],
|
|
58
|
-
"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
|
|
57
|
+
"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."
|
|
59
58
|
},
|
|
60
59
|
"details": {
|
|
61
|
-
"description": "The
|
|
60
|
+
"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.",
|
|
62
61
|
"anyOf": [
|
|
63
62
|
{
|
|
64
63
|
"type": "object",
|
|
65
64
|
"properties": {
|
|
66
|
-
"
|
|
65
|
+
"store_bytes": {
|
|
67
66
|
"type": "integer",
|
|
68
67
|
"exclusiveMinimum": 0,
|
|
69
68
|
"maximum": 9007199254740991,
|
|
70
|
-
"description": "The
|
|
69
|
+
"description": "The robot's store, in bytes."
|
|
70
|
+
},
|
|
71
|
+
"used_bytes": {
|
|
72
|
+
"type": "integer",
|
|
73
|
+
"minimum": 0,
|
|
74
|
+
"maximum": 9007199254740991,
|
|
75
|
+
"description": "Bytes the robot's assets occupy before this upload."
|
|
71
76
|
},
|
|
72
77
|
"size_bytes": {
|
|
73
78
|
"type": "integer",
|
|
74
79
|
"exclusiveMinimum": 0,
|
|
75
80
|
"maximum": 9007199254740991,
|
|
76
|
-
"description": "
|
|
81
|
+
"description": "The refused upload, in bytes."
|
|
77
82
|
}
|
|
78
83
|
},
|
|
79
84
|
"required": [
|
|
80
|
-
"
|
|
85
|
+
"store_bytes",
|
|
86
|
+
"used_bytes",
|
|
81
87
|
"size_bytes"
|
|
82
88
|
],
|
|
83
89
|
"additionalProperties": false
|
|
@@ -108,6 +114,25 @@
|
|
|
108
114
|
],
|
|
109
115
|
"description": "Why the sync ended as it did, when that is not a per-reference fact. `null` when `failed` already says everything there is to say."
|
|
110
116
|
},
|
|
117
|
+
"stored": {
|
|
118
|
+
"anyOf": [
|
|
119
|
+
{
|
|
120
|
+
"type": "integer",
|
|
121
|
+
"minimum": 0,
|
|
122
|
+
"maximum": 9007199254740991
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"type": "null"
|
|
126
|
+
}
|
|
127
|
+
],
|
|
128
|
+
"description": "How many of the announced files the cloud's store actually holds. Counted once, after the robot reports the sync done, and `null` until then — nobody has looked yet. Read it against `announced`: `state` is what the robot reported, this is what arrived."
|
|
129
|
+
},
|
|
130
|
+
"announced": {
|
|
131
|
+
"type": "integer",
|
|
132
|
+
"minimum": 0,
|
|
133
|
+
"maximum": 9007199254740991,
|
|
134
|
+
"description": "How many files the robot announced for this sync — the URDF, if it has one, plus every mesh URI its description references. `0` when the robot announced nothing, and also `0` until it has answered at all: read it beside `stored`, which stays `null` until the terminal frame."
|
|
135
|
+
},
|
|
111
136
|
"started_at": {
|
|
112
137
|
"type": "string",
|
|
113
138
|
"format": "date-time",
|
|
@@ -129,6 +154,8 @@
|
|
|
129
154
|
"total",
|
|
130
155
|
"failed",
|
|
131
156
|
"reason",
|
|
157
|
+
"stored",
|
|
158
|
+
"announced",
|
|
132
159
|
"started_at",
|
|
133
160
|
"updated_at"
|
|
134
161
|
],
|
|
@@ -19,10 +19,9 @@
|
|
|
19
19
|
"enum": [
|
|
20
20
|
"urdf",
|
|
21
21
|
"mesh",
|
|
22
|
-
"texture"
|
|
23
|
-
"other"
|
|
22
|
+
"texture"
|
|
24
23
|
],
|
|
25
|
-
"description": "What the file is: the `urdf` itself, a `mesh` it references, a `texture` a mesh or the URDF paints with
|
|
24
|
+
"description": "What the file is: the `urdf` itself, a `mesh` it references, or a `texture` a mesh or the URDF paints with. A renderer decides from this alone, before fetching anything, what to pre-fetch."
|
|
26
25
|
},
|
|
27
26
|
"name": {
|
|
28
27
|
"type": "string",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"deleted": {
|
|
6
|
+
"type": "integer",
|
|
7
|
+
"minimum": 0,
|
|
8
|
+
"maximum": 9007199254740991,
|
|
9
|
+
"description": "How many assets — URDF, meshes and textures together — were removed."
|
|
10
|
+
},
|
|
11
|
+
"bytes_freed": {
|
|
12
|
+
"type": "integer",
|
|
13
|
+
"minimum": 0,
|
|
14
|
+
"maximum": 9007199254740991,
|
|
15
|
+
"description": "The bytes the robot's store got back."
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"required": [
|
|
19
|
+
"deleted",
|
|
20
|
+
"bytes_freed"
|
|
21
|
+
],
|
|
22
|
+
"additionalProperties": false
|
|
23
|
+
}
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"refresh_token"
|
|
40
40
|
]
|
|
41
41
|
},
|
|
42
|
-
"description": "The grants this server offers
|
|
42
|
+
"description": "The grants this server offers: `authorization_code` and `refresh_token`. OAuth 2.1 removes the implicit and password grants, so neither appears here."
|
|
43
43
|
},
|
|
44
44
|
"code_challenge_methods_supported": {
|
|
45
45
|
"type": "array",
|
|
@@ -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
|
|
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
|
|
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
|
-
"
|
|
51
|
+
"store_bytes": {
|
|
53
52
|
"type": "integer",
|
|
54
53
|
"exclusiveMinimum": 0,
|
|
55
54
|
"maximum": 9007199254740991,
|
|
56
|
-
"description": "The
|
|
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": "
|
|
67
|
+
"description": "The refused upload, in bytes."
|
|
63
68
|
}
|
|
64
69
|
},
|
|
65
70
|
"required": [
|
|
66
|
-
"
|
|
71
|
+
"store_bytes",
|
|
72
|
+
"used_bytes",
|
|
67
73
|
"size_bytes"
|
|
68
74
|
]
|
|
69
75
|
},
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
}
|
|
@@ -15,10 +15,15 @@
|
|
|
15
15
|
"type": "null"
|
|
16
16
|
}
|
|
17
17
|
]
|
|
18
|
+
},
|
|
19
|
+
"low_bandwidth": {
|
|
20
|
+
"type": "boolean",
|
|
21
|
+
"description": "Whether the bridge is in its low-bandwidth mode: datapoints capped, cameras reduced or stopped. Bridge-reported."
|
|
18
22
|
}
|
|
19
23
|
},
|
|
20
24
|
"required": [
|
|
21
25
|
"online",
|
|
22
|
-
"latency_ms"
|
|
26
|
+
"latency_ms",
|
|
27
|
+
"low_bandwidth"
|
|
23
28
|
]
|
|
24
29
|
}
|
|
@@ -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
|
"description": "The built-in `bridge_state` datapoint as the cloud observes it right now: whether the bridge is connected, and its latency when it is."
|
|
@@ -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
|
"description": "The built-in `bridge_state` datapoint as the cloud observes it right now: whether the bridge is connected, and its latency when it is."
|