@fleetless/contracts 1.0.4 → 1.0.6
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 +68 -3
- package/CONTRIBUTING.md +100 -75
- package/README.md +69 -83
- package/SECURITY.md +24 -24
- package/artifacts/openapi.json +67 -67
- package/artifacts/routes.json +5 -5
- package/artifacts/schema/app-list-response.schema.json +2 -2
- package/artifacts/schema/app-oidc-provider-list-response.schema.json +1 -1
- package/artifacts/schema/app-oidc-provider.schema.json +1 -1
- package/artifacts/schema/app-user-list-response.schema.json +2 -2
- package/artifacts/schema/app-user.schema.json +2 -2
- package/artifacts/schema/app.schema.json +2 -2
- package/artifacts/schema/asset-list-response.schema.json +7 -7
- package/artifacts/schema/asset-sync-request.schema.json +1 -1
- package/artifacts/schema/asset-sync-status.schema.json +1 -1
- package/artifacts/schema/asset.schema.json +3 -3
- package/artifacts/schema/auth-me-response.schema.json +2 -2
- package/artifacts/schema/auth-ok.schema.json +1 -1
- package/artifacts/schema/authorization-server-metadata.schema.json +1 -1
- package/artifacts/schema/bridge-asset-progress.schema.json +1 -1
- package/artifacts/schema/busy-details.schema.json +3 -3
- package/artifacts/schema/client-identity.schema.json +1 -1
- package/artifacts/schema/client-login-request.schema.json +1 -1
- package/artifacts/schema/client-logout-request.schema.json +1 -1
- package/artifacts/schema/client-mcp-interaction.schema.json +1 -1
- package/artifacts/schema/cloud-config.schema.json +1 -1
- package/artifacts/schema/command-result.schema.json +3 -3
- package/artifacts/schema/config-draft-response.schema.json +1 -1
- package/artifacts/schema/config-version-response.schema.json +1 -1
- package/artifacts/schema/create-server-key-response.schema.json +1 -1
- package/artifacts/schema/datapoint-config.schema.json +1 -1
- package/artifacts/schema/datapoint-value.schema.json +2 -2
- package/artifacts/schema/dynamic-client-registration-request.schema.json +2 -2
- package/artifacts/schema/fleetless-user-list-response.schema.json +2 -2
- package/artifacts/schema/fleetless-user.schema.json +2 -2
- package/artifacts/schema/invoke-or-service-response.schema.json +4 -4
- package/artifacts/schema/invoke-response.schema.json +3 -3
- package/artifacts/schema/job-actor.schema.json +1 -1
- package/artifacts/schema/job-event.schema.json +3 -3
- package/artifacts/schema/job-response.schema.json +3 -3
- package/artifacts/schema/job-run-list-response.schema.json +2 -2
- package/artifacts/schema/job-run.schema.json +2 -2
- package/artifacts/schema/job.schema.json +3 -3
- package/artifacts/schema/mcp-consent-grant-list-response.schema.json +2 -2
- package/artifacts/schema/mcp-consent-grant.schema.json +2 -2
- package/artifacts/schema/oauth-authorize-query.schema.json +1 -1
- package/artifacts/schema/oauth-token-request.schema.json +1 -1
- package/artifacts/schema/patch-org-response.schema.json +1 -1
- package/artifacts/schema/patch-robot-response.schema.json +1 -1
- package/artifacts/schema/robot-config-doc.schema.json +1 -1
- package/artifacts/schema/robot-jobs-response.schema.json +3 -3
- package/artifacts/schema/role-list-response.schema.json +1 -1
- package/artifacts/schema/role.schema.json +1 -1
- package/artifacts/schema/server-key-list-response.schema.json +2 -2
- package/artifacts/schema/server-key.schema.json +1 -1
- package/artifacts/schema/service-call-response.schema.json +1 -1
- package/artifacts/schema/sign-up-response.schema.json +2 -2
- package/artifacts/schema/urdf-completeness.schema.json +2 -2
- package/artifacts/schema-outgoing/bridge-asset-progress.schema.json +1 -1
- package/dist/alerts.d.ts +16 -18
- package/dist/alerts.js +16 -18
- package/dist/app-users.d.ts +5 -6
- package/dist/app-users.js +9 -10
- package/dist/apps.d.ts +5 -6
- package/dist/apps.js +11 -12
- package/dist/assets.js +10 -13
- package/dist/audit.d.ts +10 -12
- package/dist/audit.js +14 -17
- package/dist/client-auth.d.ts +8 -9
- package/dist/client-auth.js +14 -15
- package/dist/config-issues.d.ts +5 -5
- package/dist/config-issues.js +5 -5
- package/dist/config.d.ts +6 -7
- package/dist/config.js +12 -13
- package/dist/errors.d.ts +4 -4
- package/dist/errors.js +15 -16
- package/dist/identity.d.ts +6 -7
- package/dist/identity.js +9 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/jobs.js +5 -5
- package/dist/mcp.d.ts +5 -8
- package/dist/mcp.js +2 -2
- package/dist/oauth.d.ts +8 -10
- package/dist/oauth.js +13 -15
- package/dist/protocol.d.ts +7 -8
- package/dist/protocol.js +20 -22
- package/dist/realtime.js +4 -4
- package/dist/rest.js +4 -4
- package/dist/routes.js +7 -7
- package/package.json +2 -1
package/artifacts/routes.json
CHANGED
|
@@ -1006,7 +1006,7 @@
|
|
|
1006
1006
|
},
|
|
1007
1007
|
{
|
|
1008
1008
|
"name": "clientId",
|
|
1009
|
-
"description": "The MCP client, as `GET /api/apps/:id/users/:userId/mcp-grants` reports its `client_id`. Not a uuid —
|
|
1009
|
+
"description": "The MCP client, as `GET /api/apps/:id/users/:userId/mcp-grants` reports its `client_id`. Not a uuid — the identifier dynamic registration issued."
|
|
1010
1010
|
}
|
|
1011
1011
|
],
|
|
1012
1012
|
"query": null,
|
|
@@ -2370,7 +2370,7 @@
|
|
|
2370
2370
|
"forbidden"
|
|
2371
2371
|
],
|
|
2372
2372
|
"transport": "http",
|
|
2373
|
-
"notes": "MCP's Streamable HTTP gives this path three verbs: `POST` carries JSON-RPC, `GET` opens the server-initiated SSE stream, and `DELETE` ends a session. This server has no sessions — the argument is in `MCP_PROTOCOL_VERSION`'s own note, and a per-process session map is what breaks at the second cloud instance — so `GET` and `DELETE` answer `405`, which is what a client is built to fall back from. \n\n**The `405` is this cloud's own answer, not the SDK's**, and the two differ: MCP SDK 1.30.0 opens an SSE stream on `GET` (`handleGetRequest`) and answers `200` on `DELETE` (`handleDeleteRequest`), neither of which a stateless server has any business doing, so the cloud writes the `405` itself in the transport's own JSON-RPC error shape with `Allow: POST`. \n\n**The row exists so that the `405` is not a `404`.** An unregistered verb answers `404`, and at a path whose last segment is an app identifier a `404` already means *no such app* —
|
|
2373
|
+
"notes": "MCP's Streamable HTTP gives this path three verbs: `POST` carries JSON-RPC, `GET` opens the server-initiated SSE stream, and `DELETE` ends a session. This server has no sessions — the argument is in `MCP_PROTOCOL_VERSION`'s own note, and a per-process session map is what breaks at the second cloud instance — so `GET` and `DELETE` answer `405`, which is what a client is built to fall back from. \n\n**The `405` is this cloud's own answer, not the SDK's**, and the two differ: MCP SDK 1.30.0 opens an SSE stream on `GET` (`handleGetRequest`) and answers `200` on `DELETE` (`handleDeleteRequest`), neither of which a stateless server has any business doing, so the cloud writes the `405` itself in the transport's own JSON-RPC error shape with `Allow: POST`. \n\n**The row exists so that the `405` is not a `404`.** An unregistered verb answers `404`, and at a path whose last segment is an app identifier a `404` already means *no such app* — so an unregistered verb and an unknown app would answer identically. Registering the verb lets the endpoint say \"this app's server is here; this verb is not part of it\". The central `/mcp` registers neither verb and does not need to: its path takes no parameter, so nothing can misread its `404`. \n\n**The `405` body is the transport's JSON-RPC error object, not the `apiError` envelope.** The three codes above are the refusals that come *first* — the app, its switch, then the bearer, in the order `POST` describes — and they are `apiError` because they are answered before the transport is reached at all. If this server ever becomes stateful, this row and the `DELETE` beside it are where that lands, and the cloud's route-manifest test is what would make both repositories notice."
|
|
2374
2374
|
},
|
|
2375
2375
|
{
|
|
2376
2376
|
"method": "DELETE",
|
|
@@ -2995,7 +2995,7 @@
|
|
|
2995
2995
|
"params": [
|
|
2996
2996
|
{
|
|
2997
2997
|
"name": "clientId",
|
|
2998
|
-
"description": "The MCP client, as `GET /api/client/mcp/grants` reports its `client_id`. Not a uuid —
|
|
2998
|
+
"description": "The MCP client, as `GET /api/client/mcp/grants` reports its `client_id`. Not a uuid — the identifier dynamic registration issued."
|
|
2999
2999
|
}
|
|
3000
3000
|
],
|
|
3001
3001
|
"query": null,
|
|
@@ -3788,7 +3788,7 @@
|
|
|
3788
3788
|
"validation_error"
|
|
3789
3789
|
],
|
|
3790
3790
|
"transport": "http",
|
|
3791
|
-
"notes": "Needs the `action_history` capability, and **this route is what makes that switch mean something** — it was unkeepable while nothing durable recorded what had run. Two residuals worth stating rather than implying away. `history` is a syntactically valid slug and
|
|
3791
|
+
"notes": "Needs the `action_history` capability, and **this route is what makes that switch mean something** — it was unkeepable while nothing durable recorded what had run. Two residuals worth stating rather than implying away. `history` is a syntactically valid slug, and a static segment matches before a parameter, so a robot with a service literally slugged `history` can no longer be **read** through `GET /api/robots/:id/jobs/:slug`; invoking, cancelling and the listing are unaffected. And a run row names its actor by email address, so an end user holding this capability learns which other people have been driving the machine. `robot_id` in the query is shared with the org-wide read; a *different* one here is refused rather than quietly answered about the robot in the path."
|
|
3792
3792
|
},
|
|
3793
3793
|
{
|
|
3794
3794
|
"method": "POST",
|
|
@@ -3807,7 +3807,7 @@
|
|
|
3807
3807
|
},
|
|
3808
3808
|
{
|
|
3809
3809
|
"name": "slug",
|
|
3810
|
-
"description": "The action or service slug from the published configuration
|
|
3810
|
+
"description": "The action or service slug from the published configuration — the cloud already knows which kind."
|
|
3811
3811
|
}
|
|
3812
3812
|
],
|
|
3813
3813
|
"query": null,
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"minLength": 2,
|
|
31
31
|
"maxLength": 63,
|
|
32
32
|
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
33
|
-
"description": "The stable handle a client sends at login, lowercase and underscore-separated. **Globally unique, not per organisation** — `clientLoginRequest` carries no org context
|
|
33
|
+
"description": "The stable handle a client sends at login, lowercase and underscore-separated. **Globally unique, not per organisation** — `clientLoginRequest` carries no org context, so a collision is refused with `identifier_taken`."
|
|
34
34
|
},
|
|
35
35
|
"robot_ids": {
|
|
36
36
|
"type": "array",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"type": "null"
|
|
53
53
|
}
|
|
54
54
|
],
|
|
55
|
-
"description": "The role an app user gets when
|
|
55
|
+
"description": "The role an app user gets when created or invited without an explicit one. `null` means this app has not chosen a default, the normal state of an app created before its roles were configured — and then a create or invite that omits `role_id` gets `validation_error`, not a user with no role. An invitation resolves the role when issued, so changing this never re-aims an outstanding one. The role must belong to this app, which `PATCH /api/apps/:id` checks and the schema cannot."
|
|
56
56
|
},
|
|
57
57
|
"created_at": {
|
|
58
58
|
"type": "string",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
},
|
|
61
61
|
"enabled": {
|
|
62
62
|
"type": "boolean",
|
|
63
|
-
"description": "Whether this provider is offered
|
|
63
|
+
"description": "Whether this provider is offered. A disabled provider disappears from `GET /api/client/providers` and refuses a start with `provider_disabled`, without the row and its linked identities being deleted."
|
|
64
64
|
},
|
|
65
65
|
"created_at": {
|
|
66
66
|
"type": "string",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"enabled": {
|
|
57
57
|
"type": "boolean",
|
|
58
|
-
"description": "Whether this provider is offered
|
|
58
|
+
"description": "Whether this provider is offered. A disabled provider disappears from `GET /api/client/providers` and refuses a start with `provider_disabled`, without the row and its linked identities being deleted."
|
|
59
59
|
},
|
|
60
60
|
"created_at": {
|
|
61
61
|
"type": "string",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"has_password": {
|
|
57
57
|
"type": "boolean",
|
|
58
|
-
"description": "Whether this account has a Fleetless-held password
|
|
58
|
+
"description": "Whether this account has a Fleetless-held password. `false` is an identity-provider-only account, or an invitation not yet accepted — it does not mean blocked and it does not mean without access. No hash, no algorithm and no \"last changed\" travels here, and nothing on the wire can say whether a password is strong or already known to somebody else."
|
|
59
59
|
},
|
|
60
60
|
"providers": {
|
|
61
61
|
"maxItems": 20,
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"maxLength": 40,
|
|
66
66
|
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
|
|
67
67
|
},
|
|
68
|
-
"description": "The slugs of the identity providers this account is linked to, empty for a password-only user.
|
|
68
|
+
"description": "The slugs of the identity providers this account is linked to, empty for a password-only user. Lets a developer's user list say where an account came from without a second request."
|
|
69
69
|
},
|
|
70
70
|
"last_login_at": {
|
|
71
71
|
"anyOf": [
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"has_password": {
|
|
52
52
|
"type": "boolean",
|
|
53
|
-
"description": "Whether this account has a Fleetless-held password
|
|
53
|
+
"description": "Whether this account has a Fleetless-held password. `false` is an identity-provider-only account, or an invitation not yet accepted — it does not mean blocked and it does not mean without access. No hash, no algorithm and no \"last changed\" travels here, and nothing on the wire can say whether a password is strong or already known to somebody else."
|
|
54
54
|
},
|
|
55
55
|
"providers": {
|
|
56
56
|
"maxItems": 20,
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"maxLength": 40,
|
|
61
61
|
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
|
|
62
62
|
},
|
|
63
|
-
"description": "The slugs of the identity providers this account is linked to, empty for a password-only user.
|
|
63
|
+
"description": "The slugs of the identity providers this account is linked to, empty for a password-only user. Lets a developer's user list say where an account came from without a second request."
|
|
64
64
|
},
|
|
65
65
|
"last_login_at": {
|
|
66
66
|
"anyOf": [
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"minLength": 2,
|
|
26
26
|
"maxLength": 63,
|
|
27
27
|
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
28
|
-
"description": "The stable handle a client sends at login, lowercase and underscore-separated. **Globally unique, not per organisation** — `clientLoginRequest` carries no org context
|
|
28
|
+
"description": "The stable handle a client sends at login, lowercase and underscore-separated. **Globally unique, not per organisation** — `clientLoginRequest` carries no org context, so a collision is refused with `identifier_taken`."
|
|
29
29
|
},
|
|
30
30
|
"robot_ids": {
|
|
31
31
|
"type": "array",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"type": "null"
|
|
48
48
|
}
|
|
49
49
|
],
|
|
50
|
-
"description": "The role an app user gets when
|
|
50
|
+
"description": "The role an app user gets when created or invited without an explicit one. `null` means this app has not chosen a default, the normal state of an app created before its roles were configured — and then a create or invite that omits `role_id` gets `validation_error`, not a user with no role. An invitation resolves the role when issued, so changing this never re-aims an outstanding one. The role must belong to this app, which `PATCH /api/apps/:id` checks and the schema cannot."
|
|
51
51
|
},
|
|
52
52
|
"created_at": {
|
|
53
53
|
"type": "string",
|
|
@@ -27,13 +27,13 @@
|
|
|
27
27
|
"texture",
|
|
28
28
|
"other"
|
|
29
29
|
],
|
|
30
|
-
"description": "What the file is: the `urdf` itself, a `mesh` it references, a `texture` a mesh or the URDF paints with, or `other`. A renderer decides from this alone, before fetching anything, what
|
|
30
|
+
"description": "What the file is: the `urdf` itself, a `mesh` it references, a `texture` a mesh or the URDF paints with, or `other`. A renderer decides from this alone, before fetching anything, what to pre-fetch."
|
|
31
31
|
},
|
|
32
32
|
"name": {
|
|
33
33
|
"type": "string",
|
|
34
34
|
"minLength": 1,
|
|
35
35
|
"maxLength": 500,
|
|
36
|
-
"description": "What the robot called it — for a mesh, the `package://` URI the URDF references, verbatim,
|
|
36
|
+
"description": "What the robot called it — for a mesh, the `package://` URI the URDF references, verbatim, the only string a developer can match against their own workspace. A file the URDF never names (an image a `.dae` loads for itself) is named by joining the mesh's own directory with that internal reference."
|
|
37
37
|
},
|
|
38
38
|
"media_type": {
|
|
39
39
|
"type": "string",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"sha256": {
|
|
51
51
|
"type": "string",
|
|
52
52
|
"pattern": "^[a-f0-9]{64}$",
|
|
53
|
-
"description": "The content hash, lowercase hex
|
|
53
|
+
"description": "The content hash, lowercase hex. Exposed because it is the only way a client can tell \"this is the same mesh I already have\" across robots — the reason two robots sharing a mesh cost one copy."
|
|
54
54
|
},
|
|
55
55
|
"created_at": {
|
|
56
56
|
"type": "string",
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
"type": "integer",
|
|
150
150
|
"exclusiveMinimum": 0,
|
|
151
151
|
"maximum": 9007199254740991,
|
|
152
|
-
"description": "How large the refused file
|
|
152
|
+
"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."
|
|
153
153
|
}
|
|
154
154
|
},
|
|
155
155
|
"required": [
|
|
@@ -238,7 +238,7 @@
|
|
|
238
238
|
"type": "string",
|
|
239
239
|
"minLength": 1,
|
|
240
240
|
"maxLength": 500,
|
|
241
|
-
"description": "The reference, verbatim, that no stored asset answers — a `package://` URI the workspace does not hold, or an absolute or bare relative path nothing will ever fetch.
|
|
241
|
+
"description": "The reference, verbatim, that no stored asset answers — a `package://` URI the workspace does not hold, or an absolute or bare relative path nothing will ever fetch."
|
|
242
242
|
},
|
|
243
243
|
"element": {
|
|
244
244
|
"type": "string",
|
|
@@ -255,7 +255,7 @@
|
|
|
255
255
|
],
|
|
256
256
|
"additionalProperties": false
|
|
257
257
|
},
|
|
258
|
-
"description": "The references nothing in the store answers, each with the element that asked for it. A bare count
|
|
258
|
+
"description": "The references nothing in the store answers, each with the element that asked for it. A bare count would send a developer hunting through the workspace by hand; the references are what they can act on."
|
|
259
259
|
}
|
|
260
260
|
},
|
|
261
261
|
"required": [
|
|
@@ -275,7 +275,7 @@
|
|
|
275
275
|
"type": "null"
|
|
276
276
|
}
|
|
277
277
|
],
|
|
278
|
-
"description": "What the connected bridge says it *could* transfer
|
|
278
|
+
"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."
|
|
279
279
|
}
|
|
280
280
|
},
|
|
281
281
|
"required": [
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"enum": [
|
|
8
8
|
"bridge"
|
|
9
9
|
],
|
|
10
|
-
"description": "Where the bytes come from. `bridge` is the only value today: the connected bridge reads them from the robot's own workspace.
|
|
10
|
+
"description": "Where the bytes come from. `bridge` is the only value today: the connected bridge reads them from the robot's own workspace. Validated rather than ignored, so a caller naming an unknown source is told so instead of silently getting a bridge sync."
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
"required": [
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"type": "integer",
|
|
74
74
|
"exclusiveMinimum": 0,
|
|
75
75
|
"maximum": 9007199254740991,
|
|
76
|
-
"description": "How large the refused file
|
|
76
|
+
"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."
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
79
|
"required": [
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"texture",
|
|
23
23
|
"other"
|
|
24
24
|
],
|
|
25
|
-
"description": "What the file is: the `urdf` itself, a `mesh` it references, a `texture` a mesh or the URDF paints with, or `other`. A renderer decides from this alone, before fetching anything, what
|
|
25
|
+
"description": "What the file is: the `urdf` itself, a `mesh` it references, a `texture` a mesh or the URDF paints with, or `other`. A renderer decides from this alone, before fetching anything, what to pre-fetch."
|
|
26
26
|
},
|
|
27
27
|
"name": {
|
|
28
28
|
"type": "string",
|
|
29
29
|
"minLength": 1,
|
|
30
30
|
"maxLength": 500,
|
|
31
|
-
"description": "What the robot called it — for a mesh, the `package://` URI the URDF references, verbatim,
|
|
31
|
+
"description": "What the robot called it — for a mesh, the `package://` URI the URDF references, verbatim, the only string a developer can match against their own workspace. A file the URDF never names (an image a `.dae` loads for itself) is named by joining the mesh's own directory with that internal reference."
|
|
32
32
|
},
|
|
33
33
|
"media_type": {
|
|
34
34
|
"type": "string",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"sha256": {
|
|
46
46
|
"type": "string",
|
|
47
47
|
"pattern": "^[a-f0-9]{64}$",
|
|
48
|
-
"description": "The content hash, lowercase hex
|
|
48
|
+
"description": "The content hash, lowercase hex. Exposed because it is the only way a client can tell \"this is the same mesh I already have\" across robots — the reason two robots sharing a mesh cost one copy."
|
|
49
49
|
},
|
|
50
50
|
"created_at": {
|
|
51
51
|
"type": "string",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"type": "string",
|
|
45
45
|
"format": "uuid",
|
|
46
46
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
|
|
47
|
-
"description": "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at
|
|
47
|
+
"description": "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at, not a filter it applies."
|
|
48
48
|
},
|
|
49
49
|
"email": {
|
|
50
50
|
"type": "string",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"owner",
|
|
72
72
|
"developer"
|
|
73
73
|
],
|
|
74
|
-
"description": "The console powers this person holds. **Required** — every Fleetless user
|
|
74
|
+
"description": "The console powers this person holds. **Required** — every Fleetless user has a tier; it was optional only while the org also held people with no console powers to grade, and that pool is gone."
|
|
75
75
|
},
|
|
76
76
|
"created_at": {
|
|
77
77
|
"type": "string",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"app_user",
|
|
17
17
|
"server_key"
|
|
18
18
|
],
|
|
19
|
-
"description": "Which of the three kinds of caller this is: a `developer` working through the console, an `app_user` holding a token from a client login, or a `server_key` used by server-side code. Stated outright
|
|
19
|
+
"description": "Which of the three kinds of caller this is: a `developer` working through the console, an `app_user` holding a token from a client login, or a `server_key` used by server-side code. Stated outright, not inferred from which id is set."
|
|
20
20
|
},
|
|
21
21
|
"developer_id": {
|
|
22
22
|
"anyOf": [
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"authorization_endpoint": {
|
|
11
11
|
"type": "string",
|
|
12
12
|
"format": "uri",
|
|
13
|
-
"description": "
|
|
13
|
+
"description": "Where a client sends the user to authorize."
|
|
14
14
|
},
|
|
15
15
|
"token_endpoint": {
|
|
16
16
|
"type": "string",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"type": "integer",
|
|
60
60
|
"exclusiveMinimum": 0,
|
|
61
61
|
"maximum": 9007199254740991,
|
|
62
|
-
"description": "How large the refused file
|
|
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."
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
65
|
"required": [
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"type": "string",
|
|
10
10
|
"format": "uuid",
|
|
11
11
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
|
|
12
|
-
"description": "The job's id, minted by the cloud when the invocation is accepted. Informative
|
|
12
|
+
"description": "The job's id, minted by the cloud when the invocation is accepted. Informative — state is observed by slug; a cancel names this id to stop one specific job rather than whatever is running."
|
|
13
13
|
},
|
|
14
14
|
"robot_id": {
|
|
15
15
|
"type": "string",
|
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
"cancelled",
|
|
34
34
|
"lost"
|
|
35
35
|
],
|
|
36
|
-
"description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone —
|
|
36
|
+
"description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — stated rather than left reading `running` by default."
|
|
37
37
|
},
|
|
38
38
|
"started_at": {
|
|
39
39
|
"type": "string",
|
|
40
40
|
"format": "date-time",
|
|
41
41
|
"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])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
|
42
|
-
"description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge
|
|
42
|
+
"description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge, this is **adoption time**, not the real start — the cloud never minted it."
|
|
43
43
|
},
|
|
44
44
|
"updated_at": {
|
|
45
45
|
"type": "string",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"app_user",
|
|
10
10
|
"server_key"
|
|
11
11
|
],
|
|
12
|
-
"description": "Which of the three kinds of caller this is: a `developer` working through the console, an `app_user` holding a token from a client login, or a `server_key` used by server-side code. Stated outright
|
|
12
|
+
"description": "Which of the three kinds of caller this is: a `developer` working through the console, an `app_user` holding a token from a client login, or a `server_key` used by server-side code. Stated outright, not inferred from which id is set."
|
|
13
13
|
},
|
|
14
14
|
"developer_id": {
|
|
15
15
|
"anyOf": [
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"minLength": 2,
|
|
8
8
|
"maxLength": 63,
|
|
9
9
|
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
10
|
-
"description": "The app being logged in to
|
|
10
|
+
"description": "The app being logged in to: its globally unique, lowercase, underscore-separated identifier, chosen by the developer at creation. There is no organisation context at login, so this is what decides which app the credentials are checked for."
|
|
11
11
|
},
|
|
12
12
|
"email": {
|
|
13
13
|
"type": "string",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"refresh_token": {
|
|
6
6
|
"type": "string",
|
|
7
7
|
"minLength": 1,
|
|
8
|
-
"description": "Any refresh token of the session to end. The whole token family is revoked server-side, so a token stolen before this call stops working too — clearing a client-side store is a gesture, not a revocation. The answer is `204`: a token the server does not recognise gets it too, since the end state
|
|
8
|
+
"description": "Any refresh token of the session to end. The whole token family is revoked server-side, so a token stolen before this call stops working too — clearing a client-side store is a gesture, not a revocation. The answer is `204`: a token the server does not recognise gets it too, since that is the end state being asked for."
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
11
|
"required": [
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"client_name_verified": {
|
|
27
27
|
"type": "boolean",
|
|
28
28
|
"const": false,
|
|
29
|
-
"description": "Always `false`. The client registered itself without authentication and
|
|
29
|
+
"description": "Always `false`. The client registered itself without authentication and named itself, so it must be rendered as a claim and never as an identity. There is no verified case, which is why this is a literal and not a boolean: a `true` branch would be dead code that looked like a safeguard."
|
|
30
30
|
},
|
|
31
31
|
"scopes": {
|
|
32
32
|
"type": "array",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
]
|
|
119
119
|
},
|
|
120
120
|
"description": {
|
|
121
|
-
"description": "Prose about what this value is, for whoever meets it in the console
|
|
121
|
+
"description": "Prose about what this value is, for whoever meets it in the console. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all — but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
|
|
122
122
|
"examples": [
|
|
123
123
|
"What this value is, for whoever meets it in the console."
|
|
124
124
|
],
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"type": "string",
|
|
24
24
|
"format": "uuid",
|
|
25
25
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
|
|
26
|
-
"description": "The job's id, minted by the cloud when the invocation is accepted. Informative
|
|
26
|
+
"description": "The job's id, minted by the cloud when the invocation is accepted. Informative — state is observed by slug; a cancel names this id to stop one specific job rather than whatever is running."
|
|
27
27
|
},
|
|
28
28
|
"robot_id": {
|
|
29
29
|
"type": "string",
|
|
@@ -47,13 +47,13 @@
|
|
|
47
47
|
"cancelled",
|
|
48
48
|
"lost"
|
|
49
49
|
],
|
|
50
|
-
"description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone —
|
|
50
|
+
"description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — stated rather than left reading `running` by default."
|
|
51
51
|
},
|
|
52
52
|
"started_at": {
|
|
53
53
|
"type": "string",
|
|
54
54
|
"format": "date-time",
|
|
55
55
|
"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])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
|
56
|
-
"description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge
|
|
56
|
+
"description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge, this is **adoption time**, not the real start — the cloud never minted it."
|
|
57
57
|
},
|
|
58
58
|
"updated_at": {
|
|
59
59
|
"type": "string",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
]
|
|
112
112
|
},
|
|
113
113
|
"description": {
|
|
114
|
-
"description": "Prose about what this value is, for whoever meets it in the console
|
|
114
|
+
"description": "Prose about what this value is, for whoever meets it in the console. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all — but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
|
|
115
115
|
"examples": [
|
|
116
116
|
"What this value is, for whoever meets it in the console."
|
|
117
117
|
],
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
]
|
|
120
120
|
},
|
|
121
121
|
"description": {
|
|
122
|
-
"description": "Prose about what this value is, for whoever meets it in the console
|
|
122
|
+
"description": "Prose about what this value is, for whoever meets it in the console. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all — but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
|
|
123
123
|
"examples": [
|
|
124
124
|
"What this value is, for whoever meets it in the console."
|
|
125
125
|
],
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"type": "string",
|
|
10
10
|
"format": "uuid",
|
|
11
11
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
|
|
12
|
-
"description": "The key row, and what the rotate and delete routes address. It is not the key:
|
|
12
|
+
"description": "The key row, and what the rotate and delete routes address. It is not the key: this shape never carries the secret."
|
|
13
13
|
},
|
|
14
14
|
"app_id": {
|
|
15
15
|
"type": "string",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
]
|
|
46
46
|
},
|
|
47
47
|
"description": {
|
|
48
|
-
"description": "Prose about what this value is, for whoever meets it in the console
|
|
48
|
+
"description": "Prose about what this value is, for whoever meets it in the console. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all — but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
|
|
49
49
|
"examples": [
|
|
50
50
|
"What this value is, for whoever meets it in the console."
|
|
51
51
|
],
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
"description": "The datapoint this value belongs to."
|
|
11
11
|
},
|
|
12
12
|
"value": {
|
|
13
|
-
"description": "The value
|
|
13
|
+
"description": "The value, shaped by the datapoint: a number, a boolean, a string, or the whole ROS message where the configuration names no field inside it. Any `scale` and `offset` the configuration declares have already been applied, at the robot."
|
|
14
14
|
},
|
|
15
15
|
"timestamp_ms": {
|
|
16
16
|
"type": "integer",
|
|
17
17
|
"minimum": 0,
|
|
18
18
|
"maximum": 9007199254740991,
|
|
19
|
-
"description": "When the value was captured, as a unix timestamp in milliseconds.
|
|
19
|
+
"description": "When the value was captured, as a unix timestamp in milliseconds. The **bridge's capture time**, never the time the cloud received it — the one exception is the built-in `bridge_state`, which the cloud observes by construction."
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"required": [
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
"minLength": 1,
|
|
12
12
|
"maxLength": 2000
|
|
13
13
|
},
|
|
14
|
-
"description": "Where the authorization code may be returned, and the one field a registration cannot omit. Each must be an `https` URL, or `http` on an explicit loopback address for a native app that cannot hold a certificate, and none may carry a fragment.
|
|
14
|
+
"description": "Where the authorization code may be returned, and the one field a registration cannot omit. Each must be an `https` URL, or `http` on an explicit loopback address for a native app that cannot hold a certificate, and none may carry a fragment. Between `1` and `5` of them; duplicates are collapsed rather than counted twice. Matched **exactly** at the authorize step against what was registered here."
|
|
15
15
|
},
|
|
16
16
|
"client_name": {
|
|
17
|
-
"description": "The name the client calls itself. Optional
|
|
17
|
+
"description": "The name the client calls itself. Optional: RFC 7591 makes every metadata field optional, so a registration without one is recorded under a default name. It is **not** vouched for by Fleetless and must never be rendered as if it were: a self-registered client chooses this string, and one has called itself *\"Fleetless Official Helper\"*.",
|
|
18
18
|
"type": "string",
|
|
19
19
|
"minLength": 1,
|
|
20
20
|
"maxLength": 200
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"type": "string",
|
|
18
18
|
"format": "uuid",
|
|
19
19
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
|
|
20
|
-
"description": "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at
|
|
20
|
+
"description": "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at, not a filter it applies."
|
|
21
21
|
},
|
|
22
22
|
"email": {
|
|
23
23
|
"type": "string",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"owner",
|
|
45
45
|
"developer"
|
|
46
46
|
],
|
|
47
|
-
"description": "The console powers this person holds. **Required** — every Fleetless user
|
|
47
|
+
"description": "The console powers this person holds. **Required** — every Fleetless user has a tier; it was optional only while the org also held people with no console powers to grade, and that pool is gone."
|
|
48
48
|
},
|
|
49
49
|
"created_at": {
|
|
50
50
|
"type": "string",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"type": "string",
|
|
13
13
|
"format": "uuid",
|
|
14
14
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
|
|
15
|
-
"description": "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at
|
|
15
|
+
"description": "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at, not a filter it applies."
|
|
16
16
|
},
|
|
17
17
|
"email": {
|
|
18
18
|
"type": "string",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"owner",
|
|
40
40
|
"developer"
|
|
41
41
|
],
|
|
42
|
-
"description": "The console powers this person holds. **Required** — every Fleetless user
|
|
42
|
+
"description": "The console powers this person holds. **Required** — every Fleetless user has a tier; it was optional only while the org also held people with no console powers to grade, and that pool is gone."
|
|
43
43
|
},
|
|
44
44
|
"created_at": {
|
|
45
45
|
"type": "string",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"type": "string",
|
|
12
12
|
"format": "uuid",
|
|
13
13
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
|
|
14
|
-
"description": "The job's id, minted by the cloud when the invocation is accepted. Informative
|
|
14
|
+
"description": "The job's id, minted by the cloud when the invocation is accepted. Informative — state is observed by slug; a cancel names this id to stop one specific job rather than whatever is running."
|
|
15
15
|
},
|
|
16
16
|
"robot_id": {
|
|
17
17
|
"type": "string",
|
|
@@ -35,13 +35,13 @@
|
|
|
35
35
|
"cancelled",
|
|
36
36
|
"lost"
|
|
37
37
|
],
|
|
38
|
-
"description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone —
|
|
38
|
+
"description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — stated rather than left reading `running` by default."
|
|
39
39
|
},
|
|
40
40
|
"started_at": {
|
|
41
41
|
"type": "string",
|
|
42
42
|
"format": "date-time",
|
|
43
43
|
"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])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
|
44
|
-
"description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge
|
|
44
|
+
"description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge, this is **adoption time**, not the real start — the cloud never minted it."
|
|
45
45
|
},
|
|
46
46
|
"updated_at": {
|
|
47
47
|
"type": "string",
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
"type": "object",
|
|
130
130
|
"properties": {
|
|
131
131
|
"result": {
|
|
132
|
-
"description": "What the service returned, shaped by the ROS service
|
|
132
|
+
"description": "What the service returned, shaped by the ROS service. A service call is awaited to completion, so there is no job to observe afterwards and no id to hold on to."
|
|
133
133
|
}
|
|
134
134
|
},
|
|
135
135
|
"required": [
|