@fleetless/contracts 1.0.5 → 1.1.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 +17 -2
- package/CONTRIBUTING.md +100 -75
- package/README.md +69 -83
- package/SECURITY.md +24 -24
- package/artifacts/openapi.json +359 -65
- package/artifacts/routes.json +74 -3
- 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/client-robot-list-item.schema.json +70 -0
- package/artifacts/schema/client-robot-list-response.schema.json +83 -0
- 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 +15 -17
- package/dist/alerts.js +15 -17
- 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/client-robots.d.ts +37 -0
- package/dist/client-robots.js +30 -0
- package/dist/config-issues.d.ts +3 -3
- package/dist/config-issues.js +3 -3
- package/dist/config.d.ts +5 -6
- package/dist/config.js +7 -8
- package/dist/errors.d.ts +4 -4
- package/dist/errors.js +8 -9
- package/dist/identity.d.ts +4 -5
- package/dist/identity.js +7 -8
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/jobs.js +5 -5
- package/dist/mcp.d.ts +11 -9
- package/dist/mcp.js +8 -3
- 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.d.ts +2 -2
- package/dist/realtime.js +4 -4
- package/dist/rest.js +4 -4
- package/dist/routes.js +40 -4
- package/package.json +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
/**
|
|
4
|
+
* One robot as `GET /api/client/robots` lists it — the REST twin of the MCP
|
|
5
|
+
* tool `robots_list`, and the one robot question no robot-scoped route can
|
|
6
|
+
* answer: which robots may I name at all.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately not `robotListItem`: that one carries `exposes`, the per-kind
|
|
9
|
+
* counts a developer's list shows, which are a configuration fact rather than
|
|
10
|
+
* something an app user's role grants. What an app user is entitled to is the
|
|
11
|
+
* robot, its bridge state, and whether anything is published on it yet.
|
|
12
|
+
*/
|
|
13
|
+
export declare const clientRobotListItem: z.ZodObject<{
|
|
14
|
+
bridge_state: z.ZodObject<{
|
|
15
|
+
online: z.ZodBoolean;
|
|
16
|
+
latency_ms: z.ZodNullable<z.ZodNumber>;
|
|
17
|
+
}, z.core.$strip>;
|
|
18
|
+
published_version: z.ZodNullable<z.ZodNumber>;
|
|
19
|
+
id: z.ZodUUID;
|
|
20
|
+
name: z.ZodString;
|
|
21
|
+
created_at: z.ZodISODateTime;
|
|
22
|
+
}, z.core.$strip>;
|
|
23
|
+
export type ClientRobotListItem = z.infer<typeof clientRobotListItem>;
|
|
24
|
+
/** What `GET /api/client/robots` answers. Never null: a caller who reaches nothing gets an empty array, and an absent key would make "nothing" and "not answered" the same reading. */
|
|
25
|
+
export declare const clientRobotListResponse: z.ZodObject<{
|
|
26
|
+
robots: z.ZodArray<z.ZodObject<{
|
|
27
|
+
bridge_state: z.ZodObject<{
|
|
28
|
+
online: z.ZodBoolean;
|
|
29
|
+
latency_ms: z.ZodNullable<z.ZodNumber>;
|
|
30
|
+
}, z.core.$strip>;
|
|
31
|
+
published_version: z.ZodNullable<z.ZodNumber>;
|
|
32
|
+
id: z.ZodUUID;
|
|
33
|
+
name: z.ZodString;
|
|
34
|
+
created_at: z.ZodISODateTime;
|
|
35
|
+
}, z.core.$strip>>;
|
|
36
|
+
}, z.core.$strip>;
|
|
37
|
+
export type ClientRobotListResponse = z.infer<typeof clientRobotListResponse>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { bridgeState } from './protocol.js';
|
|
4
|
+
import { robot } from './rest.js';
|
|
5
|
+
/* ------------------------------------------ the robots an app user reaches */
|
|
6
|
+
/**
|
|
7
|
+
* One robot as `GET /api/client/robots` lists it — the REST twin of the MCP
|
|
8
|
+
* tool `robots_list`, and the one robot question no robot-scoped route can
|
|
9
|
+
* answer: which robots may I name at all.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately not `robotListItem`: that one carries `exposes`, the per-kind
|
|
12
|
+
* counts a developer's list shows, which are a configuration fact rather than
|
|
13
|
+
* something an app user's role grants. What an app user is entitled to is the
|
|
14
|
+
* robot, its bridge state, and whether anything is published on it yet.
|
|
15
|
+
*/
|
|
16
|
+
export const clientRobotListItem = z.object({
|
|
17
|
+
...robot.shape,
|
|
18
|
+
bridge_state: bridgeState.meta({
|
|
19
|
+
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.',
|
|
20
|
+
}),
|
|
21
|
+
published_version: z.number().int().positive().nullable().meta({
|
|
22
|
+
description: 'The published configuration version, or `null` when nothing has been published yet. A robot with nothing published is still listed — "not configured yet" is a real state, and the caller is entitled to it — and its datasheet answers an empty exposure list.',
|
|
23
|
+
}),
|
|
24
|
+
});
|
|
25
|
+
/** What `GET /api/client/robots` answers. Never null: a caller who reaches nothing gets an empty array, and an absent key would make "nothing" and "not answered" the same reading. */
|
|
26
|
+
export const clientRobotListResponse = z.object({
|
|
27
|
+
robots: z.array(clientRobotListItem).meta({
|
|
28
|
+
description: 'Every robot the caller reaches, in name order with the id as the tiebreak. An app user reaches the robots their app attaches on which their role grants at least one slug or capability; a server key reaches every robot its app attaches; a developer reaches every robot of the organisation.',
|
|
29
|
+
}),
|
|
30
|
+
});
|
package/dist/config-issues.d.ts
CHANGED
|
@@ -92,9 +92,9 @@ export declare function schemaIssues(value: unknown, issues: readonly SchemaIssu
|
|
|
92
92
|
* `configDraftResponse`, not one issue: a client's `safeParse` drops the
|
|
93
93
|
* response and hands the editor nothing, for two characters typed.
|
|
94
94
|
*
|
|
95
|
-
* The quoted spelling is the segment's JSON string literal
|
|
96
|
-
*
|
|
97
|
-
*
|
|
95
|
+
* The quoted spelling is the segment's JSON string literal — the whole reason
|
|
96
|
+
* for choosing it: JSON's string syntax is a subset of YAML's double-quoted
|
|
97
|
+
* scalar syntax, so `""`, `" "` and `"\t"` are each a
|
|
98
98
|
* valid YAML spelling of exactly the key being complained about. The path is
|
|
99
99
|
* therefore text the developer can search their own file for — which is the
|
|
100
100
|
* bar this has to clear. It is also the same move `DOCUMENT_ROOT_PATH` makes
|
package/dist/config-issues.js
CHANGED
|
@@ -111,9 +111,9 @@ function slugOf(path) {
|
|
|
111
111
|
* `configDraftResponse`, not one issue: a client's `safeParse` drops the
|
|
112
112
|
* response and hands the editor nothing, for two characters typed.
|
|
113
113
|
*
|
|
114
|
-
* The quoted spelling is the segment's JSON string literal
|
|
115
|
-
*
|
|
116
|
-
*
|
|
114
|
+
* The quoted spelling is the segment's JSON string literal — the whole reason
|
|
115
|
+
* for choosing it: JSON's string syntax is a subset of YAML's double-quoted
|
|
116
|
+
* scalar syntax, so `""`, `" "` and `"\t"` are each a
|
|
117
117
|
* valid YAML spelling of exactly the key being complained about. The path is
|
|
118
118
|
* therefore text the developer can search their own file for — which is the
|
|
119
119
|
* bar this has to clear. It is also the same move `DOCUMENT_ROOT_PATH` makes
|
package/dist/config.d.ts
CHANGED
|
@@ -20,8 +20,8 @@ import { z } from 'zod';
|
|
|
20
20
|
* field as **required** in the generated JSON Schema, because after parsing
|
|
21
21
|
* it is always present. That is recorded in `scripts/export-schemas.ts`, whose
|
|
22
22
|
* remedy (`io: 'input'`, applied per schema) is a judgement call across dozens
|
|
23
|
-
* of schemas and every consumer that vendors them. So this field
|
|
24
|
-
*
|
|
23
|
+
* of schemas and every consumer that vendors them. So this field does not add
|
|
24
|
+
* another instance: optional is optional in both modes, and *absent* is
|
|
25
25
|
* the single spelling of "not described". `.min(1)` keeps the empty string from
|
|
26
26
|
* becoming a second.
|
|
27
27
|
*/
|
|
@@ -144,10 +144,9 @@ export declare const parameterMap: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
|
144
144
|
*
|
|
145
145
|
* **This constant is the only list.** The cloud builds its set from it and
|
|
146
146
|
* emits `reserved_slug`; the rename path reads it for the target; an editor
|
|
147
|
-
* reads it for slug suggestion and repairs. Nothing copies the members.
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* publishes.
|
|
147
|
+
* reads it for slug suggestion and repairs. Nothing copies the members. It is
|
|
148
|
+
* NOT the enumeration of built-in datapoints — those are kept separately,
|
|
149
|
+
* and must be, because a reserved name exists that nothing publishes.
|
|
151
150
|
*
|
|
152
151
|
* **What it does not do: `robotConfigDoc` does not enforce it.** Reservation is
|
|
153
152
|
* a semantic check that belongs with the ones that need the robot's context,
|
package/dist/config.js
CHANGED
|
@@ -379,8 +379,8 @@ const underSlug = (slugKey, snippet) => ({ ...snippet, body: { [slugKey]: snippe
|
|
|
379
379
|
* field as **required** in the generated JSON Schema, because after parsing
|
|
380
380
|
* it is always present. That is recorded in `scripts/export-schemas.ts`, whose
|
|
381
381
|
* remedy (`io: 'input'`, applied per schema) is a judgement call across dozens
|
|
382
|
-
* of schemas and every consumer that vendors them. So this field
|
|
383
|
-
*
|
|
382
|
+
* of schemas and every consumer that vendors them. So this field does not add
|
|
383
|
+
* another instance: optional is optional in both modes, and *absent* is
|
|
384
384
|
* the single spelling of "not described". `.min(1)` keeps the empty string from
|
|
385
385
|
* becoming a second.
|
|
386
386
|
*/
|
|
@@ -582,7 +582,7 @@ export const parameterSpec = strictObject({
|
|
|
582
582
|
* Without this, a default is the one way past bounds that are otherwise
|
|
583
583
|
* the enforcement point. `min_value`/`max_value` are the speed limit that
|
|
584
584
|
* actually holds, enforced in the cloud before anything reaches the robot.
|
|
585
|
-
* But a caller who
|
|
585
|
+
* But a caller who omits the parameter gets the
|
|
586
586
|
* default, and the bridge fills it at the template walk without
|
|
587
587
|
* re-checking bounds, deliberately: a second enforcement point there
|
|
588
588
|
* would be the weaker of two policies. So `{min_value: -1, max_value: 1,
|
|
@@ -658,10 +658,9 @@ export const parameterMap = slugKeyed(parameterSpec)
|
|
|
658
658
|
*
|
|
659
659
|
* **This constant is the only list.** The cloud builds its set from it and
|
|
660
660
|
* emits `reserved_slug`; the rename path reads it for the target; an editor
|
|
661
|
-
* reads it for slug suggestion and repairs. Nothing copies the members.
|
|
662
|
-
*
|
|
663
|
-
*
|
|
664
|
-
* publishes.
|
|
661
|
+
* reads it for slug suggestion and repairs. Nothing copies the members. It is
|
|
662
|
+
* NOT the enumeration of built-in datapoints — those are kept separately,
|
|
663
|
+
* and must be, because a reserved name exists that nothing publishes.
|
|
665
664
|
*
|
|
666
665
|
* **What it does not do: `robotConfigDoc` does not enforce it.** Reservation is
|
|
667
666
|
* a semantic check that belongs with the ones that need the robot's context,
|
|
@@ -993,7 +992,7 @@ export const datapointConfig = strictObject({
|
|
|
993
992
|
examples: [2, 0.5],
|
|
994
993
|
}).optional(),
|
|
995
994
|
description: serviceDescription.meta({
|
|
996
|
-
description: 'Prose about what this value is, for whoever meets it in the console
|
|
995
|
+
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.',
|
|
997
996
|
/**
|
|
998
997
|
* The sentence both datapoint snippets already place here, verbatim. Its
|
|
999
998
|
* four siblings — an action's, a service's, a publisher's, a camera's —
|
package/dist/errors.d.ts
CHANGED
|
@@ -19,10 +19,10 @@ export type ApiError = z.infer<typeof apiError>;
|
|
|
19
19
|
* console renders a third, and each is right in its own tests.
|
|
20
20
|
*
|
|
21
21
|
* `field` is the **flat key exactly as the caller sent it** — the same string
|
|
22
|
-
* as the `parameterSpec.name` it violated. That is the
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
22
|
+
* as the `parameterSpec.name` it violated. That is why the parameter form
|
|
23
|
+
* stays flat: a refusal has to name something the caller can find in what
|
|
24
|
+
* they typed, and a console can attach the error to that one input rather
|
|
25
|
+
* than to the form.
|
|
26
26
|
*/
|
|
27
27
|
export declare const parameterViolation: z.ZodObject<{
|
|
28
28
|
field: z.ZodString;
|
package/dist/errors.js
CHANGED
|
@@ -18,10 +18,10 @@ export const apiError = z.object({
|
|
|
18
18
|
* console renders a third, and each is right in its own tests.
|
|
19
19
|
*
|
|
20
20
|
* `field` is the **flat key exactly as the caller sent it** — the same string
|
|
21
|
-
* as the `parameterSpec.name` it violated. That is the
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
21
|
+
* as the `parameterSpec.name` it violated. That is why the parameter form
|
|
22
|
+
* stays flat: a refusal has to name something the caller can find in what
|
|
23
|
+
* they typed, and a console can attach the error to that one input rather
|
|
24
|
+
* than to the form.
|
|
25
25
|
*/
|
|
26
26
|
export const parameterViolation = z.object({
|
|
27
27
|
field: z.string().min(1),
|
|
@@ -81,11 +81,10 @@ export const ERROR_CODES = [
|
|
|
81
81
|
'invalid_credentials',
|
|
82
82
|
'token_expired',
|
|
83
83
|
'token_revoked',
|
|
84
|
-
/* `invite_expired` and `invite_used` were removed on 2026-09-05
|
|
85
|
-
* reasoning
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* either.
|
|
84
|
+
/* `invite_expired` and `invite_used` were removed on 2026-09-05 — same
|
|
85
|
+
* reasoning and evidence as `not_a_member`: a `grep` across contracts, cloud,
|
|
86
|
+
* sdk, console and bridge found only their own entries here and one test
|
|
87
|
+
* asserting those entries existed. Nothing has ever emitted either.
|
|
89
88
|
*
|
|
90
89
|
* They were written for `POST /api/client/invitations/accept`, to tell an
|
|
91
90
|
* expired invitation from an already-accepted one. That route answers `410
|
package/dist/identity.d.ts
CHANGED
|
@@ -19,11 +19,10 @@ import { z } from 'zod';
|
|
|
19
19
|
*
|
|
20
20
|
* **What that deleted, with no successor**: groups and the Org Admins
|
|
21
21
|
* group, app assignments, impersonation, the per-user MCP override and the
|
|
22
|
-
* org-level federation policy. The 2026-08-29 model
|
|
23
|
-
* users
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* reason.
|
|
22
|
+
* org-level federation policy. The 2026-08-29 model pooled developers and end
|
|
23
|
+
* users per org and joined them with all of the above. In use, wrong model:
|
|
24
|
+
* the two populations have different lifecycles, and every joining mechanism
|
|
25
|
+
* was cost without a product reason.
|
|
27
26
|
*
|
|
28
27
|
* What did **not** change: the password rules, the session and token shapes,
|
|
29
28
|
* and the enumeration-oracle reasoning on password reset. None of those was
|
package/dist/identity.js
CHANGED
|
@@ -19,11 +19,10 @@ import { z } from 'zod';
|
|
|
19
19
|
*
|
|
20
20
|
* **What that deleted, with no successor**: groups and the Org Admins
|
|
21
21
|
* group, app assignments, impersonation, the per-user MCP override and the
|
|
22
|
-
* org-level federation policy. The 2026-08-29 model
|
|
23
|
-
* users
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* reason.
|
|
22
|
+
* org-level federation policy. The 2026-08-29 model pooled developers and end
|
|
23
|
+
* users per org and joined them with all of the above. In use, wrong model:
|
|
24
|
+
* the two populations have different lifecycles, and every joining mechanism
|
|
25
|
+
* was cost without a product reason.
|
|
27
26
|
*
|
|
28
27
|
* What did **not** change: the password rules, the session and token shapes,
|
|
29
28
|
* and the enumeration-oracle reasoning on password reset. None of those was
|
|
@@ -78,7 +77,7 @@ export const org = z.object({
|
|
|
78
77
|
/** What `PATCH /api/org` answers: the org as it now stands. */
|
|
79
78
|
export const patchOrgResponse = z.object({
|
|
80
79
|
org: org.meta({
|
|
81
|
-
description: 'The organisation as it now stands, after the patch
|
|
80
|
+
description: 'The organisation as it now stands, after the patch. The whole resource comes back, not only the changed fields.',
|
|
82
81
|
}),
|
|
83
82
|
});
|
|
84
83
|
/**
|
|
@@ -104,7 +103,7 @@ export const fleetlessUser = z.object({
|
|
|
104
103
|
description: 'The Fleetless user in the API, assigned by the cloud and stable for the life of the account.',
|
|
105
104
|
}),
|
|
106
105
|
org_id: z.uuid().meta({
|
|
107
|
-
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
|
|
106
|
+
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.',
|
|
108
107
|
}),
|
|
109
108
|
email: z.email().meta({
|
|
110
109
|
description: 'The address the account is identified by, **globally unique** across every organisation. Immutable after creation: it is what every invitation, reset link and audit line names.',
|
|
@@ -113,7 +112,7 @@ export const fleetlessUser = z.object({
|
|
|
113
112
|
description: 'Optional human name, shown by the console instead of the address where present. Self-service through `PATCH /api/auth/me`; never used for authentication. `null` when the person never supplied one.',
|
|
114
113
|
}),
|
|
115
114
|
tier: orgAdminTier.meta({
|
|
116
|
-
description: 'The console powers this person holds. **Required** — every Fleetless user
|
|
115
|
+
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.',
|
|
117
116
|
}),
|
|
118
117
|
created_at: z.iso.datetime().meta({
|
|
119
118
|
description: 'When the account was created, as an ISO 8601 timestamp.',
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export type { ParameterType, ParameterSpec, ActionConfig, ServiceConfig, Publish
|
|
|
15
15
|
/**
|
|
16
16
|
* The one account of what is wrong with a configuration document, shared by
|
|
17
17
|
* every layer that reports on one. See `config-issues.ts`'s header for why a
|
|
18
|
-
* second copy of this vocabulary is a defect
|
|
18
|
+
* second copy of this vocabulary is a defect, not a convenience.
|
|
19
19
|
*/
|
|
20
20
|
export { DOCUMENT_ROOT_PATH, EXPOSURE_SECTIONS, schemaIssues, formatPath, splitFormatPath, configSchemaHash, } from './config-issues.js';
|
|
21
21
|
export type { SchemaIssue, ExposureSection } from './config-issues.js';
|
|
@@ -35,6 +35,8 @@ export { appIdentifier, app, appListResponse, createAppRequest, updateAppRequest
|
|
|
35
35
|
export type { App, AppListResponse, CreateAppRequest, UpdateAppRequest, ServerKey, ServerKeyListResponse, CreateServerKeyResponse, Role, RoleListResponse, RolePermissions, } from './apps.js';
|
|
36
36
|
export { clientLoginRequest, clientRefreshRequest, clientLogoutRequest, clientRegisterRequest, clientVerifyEmailRequest, clientResendVerificationRequest, clientPasswordResetRequest, clientPasswordResetConfirmRequest, clientAcceptInvitationRequest, CLIENT_OIDC_CALLBACK_PATH, clientProviderListQuery, clientProviderListResponse, clientOidcStartQuery, clientOidcCallbackQuery, clientOidcExchangeRequest, clientOidcErrorCode, clientMcpInteraction, clientMcpInteractionDecisionResponse, mcpConsentGrant, mcpConsentGrantListResponse, clientIdentity, } from './client-auth.js';
|
|
37
37
|
export type { ClientLoginRequest, ClientRefreshRequest, ClientLogoutRequest, ClientRegisterRequest, ClientVerifyEmailRequest, ClientResendVerificationRequest, ClientPasswordResetRequest, ClientPasswordResetConfirmRequest, ClientAcceptInvitationRequest, ClientProviderListQuery, ClientProviderListResponse, ClientOidcStartQuery, ClientOidcCallbackQuery, ClientOidcExchangeRequest, ClientOidcErrorCode, ClientMcpInteraction, ClientMcpInteractionDecisionResponse, McpConsentGrant, McpConsentGrantListResponse, ClientIdentity, } from './client-auth.js';
|
|
38
|
+
export { clientRobotListItem, clientRobotListResponse } from './client-robots.js';
|
|
39
|
+
export type { ClientRobotListItem, ClientRobotListResponse } from './client-robots.js';
|
|
38
40
|
export { APP_USER_DISPLAY_NAME_MAX, APP_URL_PLACEHOLDERS, MAIL_TEMPLATE_VARIABLES, DEFAULT_MAIL_TEMPLATES, providerSlug, appUserStatus, appUser, appUserListResponse, createAppUserRequest, patchAppUserRequest, createAppInvitationRequest, appInvitation, pendingAppInvitation, appInvitationListResponse, appOidcProvider, appOidcProviderListResponse, createAppOidcProviderRequest, patchAppOidcProviderRequest, appUrlTemplate, allowedOrigin, emailDomain, appAuthConfig, putAppAuthConfigRequest, mailTemplateKind, appMailTemplate, appMailTemplateListResponse, putAppMailTemplateRequest, mailTemplatePreviewRequest, mailTemplatePreviewResponse, mailTemplateProblemDetails, mailOutcome, } from './app-users.js';
|
|
39
41
|
export type { AppUserStatus, AppUser, AppUserListResponse, CreateAppUserRequest, PatchAppUserRequest, CreateAppInvitationRequest, AppInvitation, PendingAppInvitation, AppInvitationListResponse, AppOidcProvider, AppOidcProviderListResponse, CreateAppOidcProviderRequest, PatchAppOidcProviderRequest, AppAuthConfig, PutAppAuthConfigRequest, MailTemplateKind, AppMailTemplate, AppMailTemplateListResponse, PutAppMailTemplateRequest, MailTemplatePreviewRequest, MailTemplatePreviewResponse, MailTemplateProblemDetails, MailOutcome, } from './app-users.js';
|
|
40
42
|
export { assetKind, URDF_ASSET_NAME, asset, urdfCompleteness, assetListResponse, missingAssetQuery, assetSyncRequest, assetSyncResponse, assetSyncState, assetSyncStatus, assetFailure, assetFailureKind, assetTooLargeDetails, assetSyncBusyDetails, ASSET_UPLOAD_MAX_BYTES, } from './assets.js';
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ cameraSource, cameraCredentials, } from './config.js';
|
|
|
16
16
|
/**
|
|
17
17
|
* The one account of what is wrong with a configuration document, shared by
|
|
18
18
|
* every layer that reports on one. See `config-issues.ts`'s header for why a
|
|
19
|
-
* second copy of this vocabulary is a defect
|
|
19
|
+
* second copy of this vocabulary is a defect, not a convenience.
|
|
20
20
|
*/
|
|
21
21
|
export { DOCUMENT_ROOT_PATH, EXPOSURE_SECTIONS, schemaIssues, formatPath, splitFormatPath, configSchemaHash, } from './config-issues.js';
|
|
22
22
|
export { rosGraphEntry, rosGraph, typeField, typeDefinition, parameterFieldsOf } from './introspection.js';
|
|
@@ -40,6 +40,7 @@ mailStatus, tierRequiredDetails, passwordChangeRequest, passwordResetRequest, pa
|
|
|
40
40
|
authMeResponse, patchOrgRequest, patchAuthMeRequest, } from './identity.js';
|
|
41
41
|
export { appIdentifier, app, appListResponse, createAppRequest, updateAppRequest, serverKeyToken, serverKey, serverKeyListResponse, createServerKeyResponse, role, roleListResponse, rolePermissions, } from './apps.js';
|
|
42
42
|
export { clientLoginRequest, clientRefreshRequest, clientLogoutRequest, clientRegisterRequest, clientVerifyEmailRequest, clientResendVerificationRequest, clientPasswordResetRequest, clientPasswordResetConfirmRequest, clientAcceptInvitationRequest, CLIENT_OIDC_CALLBACK_PATH, clientProviderListQuery, clientProviderListResponse, clientOidcStartQuery, clientOidcCallbackQuery, clientOidcExchangeRequest, clientOidcErrorCode, clientMcpInteraction, clientMcpInteractionDecisionResponse, mcpConsentGrant, mcpConsentGrantListResponse, clientIdentity, } from './client-auth.js';
|
|
43
|
+
export { clientRobotListItem, clientRobotListResponse } from './client-robots.js';
|
|
43
44
|
// The per-app identity space.
|
|
44
45
|
export { APP_USER_DISPLAY_NAME_MAX, APP_URL_PLACEHOLDERS, MAIL_TEMPLATE_VARIABLES, DEFAULT_MAIL_TEMPLATES, providerSlug, appUserStatus, appUser, appUserListResponse, createAppUserRequest, patchAppUserRequest, createAppInvitationRequest, appInvitation, pendingAppInvitation, appInvitationListResponse, appOidcProvider, appOidcProviderListResponse, createAppOidcProviderRequest, patchAppOidcProviderRequest, appUrlTemplate, allowedOrigin, emailDomain, appAuthConfig, putAppAuthConfigRequest, mailTemplateKind, appMailTemplate, appMailTemplateListResponse, putAppMailTemplateRequest, mailTemplatePreviewRequest, mailTemplatePreviewResponse, mailTemplateProblemDetails, mailOutcome, } from './app-users.js';
|
|
45
46
|
export { assetKind, URDF_ASSET_NAME, asset, urdfCompleteness, assetListResponse, missingAssetQuery, assetSyncRequest, assetSyncResponse, assetSyncState, assetSyncStatus, assetFailure, assetFailureKind, assetTooLargeDetails, assetSyncBusyDetails, ASSET_UPLOAD_MAX_BYTES, } from './assets.js';
|
package/dist/jobs.js
CHANGED
|
@@ -21,17 +21,17 @@ import { slug, wireSeqCursor, wireTimestampMs } from './common.js';
|
|
|
21
21
|
export const jobState = z.enum(['running', 'succeeded', 'failed', 'cancelled', 'lost']);
|
|
22
22
|
export const job = z.object({
|
|
23
23
|
id: z.uuid().meta({
|
|
24
|
-
description: 'The job\'s id, minted by the cloud when the invocation is accepted. Informative
|
|
24
|
+
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.',
|
|
25
25
|
}),
|
|
26
26
|
robot_id: z.uuid().meta({ description: 'The robot this job is running on.' }),
|
|
27
27
|
slug: slug.meta({
|
|
28
28
|
description: 'The action or service this job is running, as the published configuration exposes it. One slug carries one job at a time, so every observer of that slug sees the same one.',
|
|
29
29
|
}),
|
|
30
30
|
state: jobState.meta({
|
|
31
|
-
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 —
|
|
31
|
+
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.',
|
|
32
32
|
}),
|
|
33
33
|
started_at: z.iso.datetime().meta({
|
|
34
|
-
description: 'When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge
|
|
34
|
+
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.',
|
|
35
35
|
}),
|
|
36
36
|
updated_at: z.iso.datetime().meta({
|
|
37
37
|
description: 'When this job last changed, as an ISO 8601 timestamp.',
|
|
@@ -176,7 +176,7 @@ export const JOB_RUN_RETENTION_DAYS = 90;
|
|
|
176
176
|
*/
|
|
177
177
|
export const jobActor = z.object({
|
|
178
178
|
kind: z.enum(['developer', 'end_user', 'app_user', 'server_key']).meta({
|
|
179
|
-
description: 'What the caller was acting as: a `developer` in the console, an `app_user` of one app, or a `server_key` used by server-side code. A bridge invokes nothing, so it is deliberately not a case here. `end_user` appears only on runs recorded before app users replaced the organisation-wide user pool —
|
|
179
|
+
description: 'What the caller was acting as: a `developer` in the console, an `app_user` of one app, or a `server_key` used by server-side code. A bridge invokes nothing, so it is deliberately not a case here. `end_user` appears only on runs recorded before app users replaced the organisation-wide user pool — kept so old runs still render; nothing writes it now.',
|
|
180
180
|
}),
|
|
181
181
|
id: z.uuid().meta({
|
|
182
182
|
description: 'The id of the Fleetless user, app user or server key that invoked the run.',
|
|
@@ -200,7 +200,7 @@ export const jobRunKind = z.enum(['action', 'service']);
|
|
|
200
200
|
*/
|
|
201
201
|
export const jobRun = z.object({
|
|
202
202
|
id: z.uuid().meta({
|
|
203
|
-
description: 'The run\'s id
|
|
203
|
+
description: 'The run\'s id — the same id the invocation was answered with, so a caller that kept a job id can find its durable record here later.',
|
|
204
204
|
}),
|
|
205
205
|
robot_id: z.uuid().meta({ description: 'The robot the run happened on.' }),
|
|
206
206
|
slug: slug.meta({
|
package/dist/mcp.d.ts
CHANGED
|
@@ -8,8 +8,8 @@ import { z } from 'zod';
|
|
|
8
8
|
* **This file describes the seam, not the protocol.** The MCP messages
|
|
9
9
|
* themselves (`initialize`, `tools/list`, `tools/call`) are defined by the
|
|
10
10
|
* Model Context Protocol and implemented with its official SDK — writing our
|
|
11
|
-
* own zod copies of them would
|
|
12
|
-
*
|
|
11
|
+
* own zod copies of them would duplicate somebody else's specification, the
|
|
12
|
+
* one thing this package exists to avoid.
|
|
13
13
|
* What lives here is what *Fleetless* decides: which revision we speak, where
|
|
14
14
|
* the endpoint is, how a tool is named, and what the console is shown before
|
|
15
15
|
* an end user ever connects.
|
|
@@ -94,12 +94,9 @@ export declare function mcpAppEndpointPath(appIdentifier: string): string;
|
|
|
94
94
|
* ever fetch. Writing them here is what keeps that reading from being made
|
|
95
95
|
* twice.
|
|
96
96
|
*
|
|
97
|
-
* **These are paths, not URLs.** Append them to `PUBLIC_API_BASE_URL`,
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* string, never against the request's `Host`. The friendly alias is a proxy in
|
|
101
|
-
* front of the same cloud, and a URL built on it hands a client an audience
|
|
102
|
-
* the token endpoint will refuse.
|
|
97
|
+
* **These are paths, not URLs.** Append them to `PUBLIC_API_BASE_URL`, not the
|
|
98
|
+
* friendly `mcp.fleetless.dev` alias — see `mcpAppEndpointPath` for why: a URL
|
|
99
|
+
* built on the alias carries an audience the token endpoint refuses.
|
|
103
100
|
*
|
|
104
101
|
* Named in the shape `OAUTH_PATHS` had, and deliberately a **function** rather
|
|
105
102
|
* than the object that constant was: there is one set of these per app, and a
|
|
@@ -172,7 +169,12 @@ export declare const mcpCapabilities: z.ZodObject<{
|
|
|
172
169
|
assets: z.ZodBoolean;
|
|
173
170
|
}, z.core.$strip>;
|
|
174
171
|
export type McpCapabilities = z.infer<typeof mcpCapabilities>;
|
|
175
|
-
/**
|
|
172
|
+
/**
|
|
173
|
+
* What one caller may do on one robot — the answer to `robot_describe`, and
|
|
174
|
+
* since 1.1.0 to `GET /api/robots/:id/datasheet` as well. One schema for both
|
|
175
|
+
* surfaces on purpose: an app and an AI tool read the same description of the
|
|
176
|
+
* same grant. The `mcp` prefix is history, not scope.
|
|
177
|
+
*/
|
|
176
178
|
export declare const mcpRobotDatasheet: z.ZodObject<{
|
|
177
179
|
robot_id: z.ZodUUID;
|
|
178
180
|
robot_name: z.ZodString;
|
package/dist/mcp.js
CHANGED
|
@@ -9,8 +9,8 @@ import { slug } from './common.js';
|
|
|
9
9
|
* **This file describes the seam, not the protocol.** The MCP messages
|
|
10
10
|
* themselves (`initialize`, `tools/list`, `tools/call`) are defined by the
|
|
11
11
|
* Model Context Protocol and implemented with its official SDK — writing our
|
|
12
|
-
* own zod copies of them would
|
|
13
|
-
*
|
|
12
|
+
* own zod copies of them would duplicate somebody else's specification, the
|
|
13
|
+
* one thing this package exists to avoid.
|
|
14
14
|
* What lives here is what *Fleetless* decides: which revision we speak, where
|
|
15
15
|
* the endpoint is, how a tool is named, and what the console is shown before
|
|
16
16
|
* an end user ever connects.
|
|
@@ -124,7 +124,12 @@ export const mcpCapabilities = z.object({
|
|
|
124
124
|
action_history: z.boolean(),
|
|
125
125
|
assets: z.boolean(),
|
|
126
126
|
});
|
|
127
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* What one caller may do on one robot — the answer to `robot_describe`, and
|
|
129
|
+
* since 1.1.0 to `GET /api/robots/:id/datasheet` as well. One schema for both
|
|
130
|
+
* surfaces on purpose: an app and an AI tool read the same description of the
|
|
131
|
+
* same grant. The `mcp` prefix is history, not scope.
|
|
132
|
+
*/
|
|
128
133
|
export const mcpRobotDatasheet = z.object({
|
|
129
134
|
robot_id: z.uuid(),
|
|
130
135
|
robot_name: z.string().min(1).max(200),
|
package/dist/oauth.d.ts
CHANGED
|
@@ -14,8 +14,8 @@ import { z } from 'zod';
|
|
|
14
14
|
* and per app for an app's users — and the console's own OAuth portal, which
|
|
15
15
|
* answers `oauthRedirectResponse` at its login and sign-up steps.
|
|
16
16
|
*
|
|
17
|
-
* The client model this file was written to get right
|
|
18
|
-
*
|
|
17
|
+
* The client model this file was written to get right survived the cut
|
|
18
|
+
* intact: an MCP client **registers itself**
|
|
19
19
|
* (RFC 7591) because the person only ever pastes a URL into an AI tool.
|
|
20
20
|
* Nobody vetted it, its redirect URIs arrive from the client itself, and the
|
|
21
21
|
* tools it will call move a physical robot. That is why consent names the
|
|
@@ -37,10 +37,9 @@ import { z } from 'zod';
|
|
|
37
37
|
* app's auth settings — are ordinary console API and use `apiError` with
|
|
38
38
|
* `ERROR_CODES` like everything else.
|
|
39
39
|
*
|
|
40
|
-
* So: **two shapes, split by audience, not by accident.** Written down
|
|
41
|
-
* because
|
|
42
|
-
*
|
|
43
|
-
* there.
|
|
40
|
+
* So: **two shapes, split by audience, not by accident.** Written down
|
|
41
|
+
* because two error formats in one server invite unifying them — which would
|
|
42
|
+
* silently erase the reason the standard one exists.
|
|
44
43
|
*
|
|
45
44
|
* **The split is by audience and the path prefix will mislead you.**
|
|
46
45
|
* `/mcp/oauth/consent` sits under an `/oauth/` segment and is nevertheless an
|
|
@@ -198,10 +197,9 @@ export type DynamicClientRegistrationResponse = z.infer<typeof dynamicClientRegi
|
|
|
198
197
|
* from a client we are about to trust, where an unknown key is a caller
|
|
199
198
|
* assuming a feature into existence, while a token request comes from any
|
|
200
199
|
* RFC-compliant client, which may legitimately send parameters this server
|
|
201
|
-
* does not read. Refusing those would be a conformance bug.
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* an absent one. Assert on the parsed value.
|
|
200
|
+
* does not read. Refusing those would be a conformance bug. Unknown keys are
|
|
201
|
+
* **stripped**, which bit the test for this schema: `safeParse().success`
|
|
202
|
+
* cannot tell a present field from an absent one. Assert on the parsed value.
|
|
205
203
|
*/
|
|
206
204
|
export declare const oauthTokenRequest: z.ZodObject<{
|
|
207
205
|
grant_type: z.ZodLiteral<"authorization_code">;
|
package/dist/oauth.js
CHANGED
|
@@ -14,8 +14,8 @@ import { z } from 'zod';
|
|
|
14
14
|
* and per app for an app's users — and the console's own OAuth portal, which
|
|
15
15
|
* answers `oauthRedirectResponse` at its login and sign-up steps.
|
|
16
16
|
*
|
|
17
|
-
* The client model this file was written to get right
|
|
18
|
-
*
|
|
17
|
+
* The client model this file was written to get right survived the cut
|
|
18
|
+
* intact: an MCP client **registers itself**
|
|
19
19
|
* (RFC 7591) because the person only ever pastes a URL into an AI tool.
|
|
20
20
|
* Nobody vetted it, its redirect URIs arrive from the client itself, and the
|
|
21
21
|
* tools it will call move a physical robot. That is why consent names the
|
|
@@ -37,10 +37,9 @@ import { z } from 'zod';
|
|
|
37
37
|
* app's auth settings — are ordinary console API and use `apiError` with
|
|
38
38
|
* `ERROR_CODES` like everything else.
|
|
39
39
|
*
|
|
40
|
-
* So: **two shapes, split by audience, not by accident.** Written down
|
|
41
|
-
* because
|
|
42
|
-
*
|
|
43
|
-
* there.
|
|
40
|
+
* So: **two shapes, split by audience, not by accident.** Written down
|
|
41
|
+
* because two error formats in one server invite unifying them — which would
|
|
42
|
+
* silently erase the reason the standard one exists.
|
|
44
43
|
*
|
|
45
44
|
* **The split is by audience and the path prefix will mislead you.**
|
|
46
45
|
* `/mcp/oauth/consent` sits under an `/oauth/` segment and is nevertheless an
|
|
@@ -180,10 +179,10 @@ export const MCP_DCR_MAX_REDIRECT_URIS = 5;
|
|
|
180
179
|
export const dynamicClientRegistrationRequest = z
|
|
181
180
|
.object({
|
|
182
181
|
redirect_uris: z.array(redirectUri).min(1).max(MCP_DCR_MAX_REDIRECT_URIS).meta({
|
|
183
|
-
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.
|
|
182
|
+
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 \`${MCP_DCR_MAX_REDIRECT_URIS}\` of them; duplicates are collapsed rather than counted twice. Matched **exactly** at the authorize step against what was registered here.`,
|
|
184
183
|
}),
|
|
185
184
|
client_name: z.string().min(1).max(200).optional().meta({
|
|
186
|
-
description: 'The name the client calls itself. Optional
|
|
185
|
+
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"*.',
|
|
187
186
|
}),
|
|
188
187
|
token_endpoint_auth_method: z.enum(['none']).optional().meta({
|
|
189
188
|
description: '`none`, RFC 7591\'s value for a public client, and the only value either server registers. Any other value is **refused rather than silently downgraded**: a client that believes it holds a secret and does not has a wrong mental model of its own security. There is no client secret to hold — mandatory PKCE (`S256`) is the defence.',
|
|
@@ -257,10 +256,9 @@ export const dynamicClientRegistrationResponse = z.object({
|
|
|
257
256
|
* from a client we are about to trust, where an unknown key is a caller
|
|
258
257
|
* assuming a feature into existence, while a token request comes from any
|
|
259
258
|
* RFC-compliant client, which may legitimately send parameters this server
|
|
260
|
-
* does not read. Refusing those would be a conformance bug.
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
* an absent one. Assert on the parsed value.
|
|
259
|
+
* does not read. Refusing those would be a conformance bug. Unknown keys are
|
|
260
|
+
* **stripped**, which bit the test for this schema: `safeParse().success`
|
|
261
|
+
* cannot tell a present field from an absent one. Assert on the parsed value.
|
|
264
262
|
*/
|
|
265
263
|
export const oauthTokenRequest = z
|
|
266
264
|
.object({
|
|
@@ -280,7 +278,7 @@ export const oauthTokenRequest = z
|
|
|
280
278
|
description: 'The PKCE verifier whose `S256` hash was sent as the challenge at the authorize step. Between `43` and `128` unreserved characters, per RFC 7636 §4.1 — it is compared rather than parsed, so a length nobody checks is a length an attacker chooses. PKCE is mandatory for every client under OAuth 2.1.',
|
|
281
279
|
}),
|
|
282
280
|
resource: z.url().optional().meta({
|
|
283
|
-
description: 'The resource the token is
|
|
281
|
+
description: 'The resource the token is requested for, per RFC 8707. It must match the audience the code was authorized for, or the answer is `invalid_target`; omitted, the code\'s own audience stands. It becomes the token\'s `aud`, and a resource refuses a token whose audience names something else — which is what keeps a token minted for one app out of another app\'s endpoint.',
|
|
284
282
|
}),
|
|
285
283
|
})
|
|
286
284
|
.meta({
|
|
@@ -325,7 +323,7 @@ export const authorizationServerMetadata = z.object({
|
|
|
325
323
|
description: 'The issuer identifier of this authorization server, per RFC 8414 §2. It is what a client checks a token\'s `iss` against.',
|
|
326
324
|
}),
|
|
327
325
|
authorization_endpoint: z.url().meta({
|
|
328
|
-
description: '
|
|
326
|
+
description: 'Where a client sends the user to authorize.',
|
|
329
327
|
}),
|
|
330
328
|
token_endpoint: z.url().meta({
|
|
331
329
|
description: 'The URL where a client exchanges an authorization code, or a refresh token, for tokens.',
|
|
@@ -438,7 +436,7 @@ export const oauthAuthorizeQuery = z
|
|
|
438
436
|
description: 'One of the client\'s registered redirect URIs, compared **exactly** — string equality against the registered list, never a prefix or a host match. Both the shape (`redirectUri`) and the registration are checked, and a failure of either is a `400 invalid_request` with no redirect.',
|
|
439
437
|
}),
|
|
440
438
|
code_challenge: z.string().min(1).meta({
|
|
441
|
-
description: 'The PKCE challenge; the verifier is presented at the token endpoint. Only non-emptiness is checked here — length and alphabet are not — since the verifier is what
|
|
439
|
+
description: 'The PKCE challenge; the verifier is presented at the token endpoint. Only non-emptiness is checked here — length and alphabet are not — since the verifier is what has to match.',
|
|
442
440
|
}),
|
|
443
441
|
code_challenge_method: z.literal('S256').meta({
|
|
444
442
|
description: 'Only `S256`. `plain` is refused: a challenge equal to its verifier defends against nothing.',
|
package/dist/protocol.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ import { z } from 'zod';
|
|
|
4
4
|
* Bridge <-> cloud protocol, version 2.
|
|
5
5
|
*
|
|
6
6
|
* The version is exchanged in the hello handshake; the cloud refuses an
|
|
7
|
-
* incompatible bridge
|
|
8
|
-
*
|
|
7
|
+
* incompatible bridge: `protocol_mismatch`, which names both versions and
|
|
8
|
+
* reaches the robot's detail view as `last_hello_error`.
|
|
9
9
|
*
|
|
10
10
|
* **2 (2026-08-21):** `config_applied.errors` entries gained `kind` and `code`
|
|
11
11
|
* beside `message`. The check is `!==`, not a floor, so a bridge that is not
|
|
@@ -72,12 +72,11 @@ export { slug } from './common.js';
|
|
|
72
72
|
*
|
|
73
73
|
* It carries the **slug and the state**, not only the id, because the cloud's
|
|
74
74
|
* reconciliation needs both and had neither. Reading `active_job_ids` as bare
|
|
75
|
-
* uuids, a restarted cloud could
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* the row.
|
|
75
|
+
* uuids, a restarted cloud could only ask "is this job still alive?" for jobs
|
|
76
|
+
* it already knew about — not what the robot is doing, not whether a
|
|
77
|
+
* `running` job had actually finished while the cloud was down, and nothing
|
|
78
|
+
* at all about a job it never recorded because it crashed between minting the
|
|
79
|
+
* id and writing the row.
|
|
81
80
|
*
|
|
82
81
|
* `state` is the bridge's own current answer, not a history. A bridge that
|
|
83
82
|
* has a terminal result still in hand reports it here and the cloud writes it
|