@fleetless/contracts 5.2.0 → 5.3.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 +38 -0
- package/artifacts/openapi.json +288 -4
- package/artifacts/routes.json +96 -1
- package/artifacts/schema/audit-query.schema.json +6 -0
- package/artifacts/schema/feedback-request.schema.json +34 -0
- package/artifacts/schema/feedback-response.schema.json +27 -0
- package/artifacts/schema/job-actor.schema.json +15 -1
- package/artifacts/schema/job-run-list-response.schema.json +15 -1
- package/artifacts/schema/job-run.schema.json +15 -1
- package/artifacts/schema/role-delete-query.schema.json +13 -0
- package/artifacts/schema/role-in-use-details.schema.json +28 -0
- package/artifacts/schema/role-list-response.schema.json +1 -1
- package/artifacts/schema/role-rename-request.schema.json +16 -0
- package/artifacts/schema/role.schema.json +1 -1
- package/dist/apps.d.ts +38 -2
- package/dist/apps.js +46 -3
- package/dist/audit.d.ts +1 -0
- package/dist/audit.js +13 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +29 -0
- package/dist/feedback.d.ts +42 -0
- package/dist/feedback.js +35 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/jobs.d.ts +3 -0
- package/dist/jobs.js +16 -0
- package/dist/routes.js +40 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,44 @@ version.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [5.3.0] — 2026-10-02
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **`feedbackRequest` and `feedbackResponse`** (`FeedbackRequest`,
|
|
17
|
+
`FeedbackResponse`) for the new `POST /api/feedback`: a developer's
|
|
18
|
+
message to the people who build Fleetless, `{ kind, message, page }` with
|
|
19
|
+
`kind` one of `FEEDBACK_KINDS` (`idea`, `problem`, `question`, `other`), a
|
|
20
|
+
trimmed message of at most `FEEDBACK_MESSAGE_MAX` (5000) characters and
|
|
21
|
+
the console path it was sent from. The route answers `202` with
|
|
22
|
+
`{ id, mail }`: the message is stored before any mail is tried, so `mail`
|
|
23
|
+
(`sent`, `failed`, `not_configured`) never means it was lost. Rate
|
|
24
|
+
limited to 10 per developer per hour. Published as the artifacts
|
|
25
|
+
`feedback-request` and `feedback-response`.
|
|
26
|
+
- **`jobActor.name`**: the person's display name when the job started,
|
|
27
|
+
required and nullable — `null` for a server key, a person without a name
|
|
28
|
+
and runs recorded before this version; show `label` then. `jobActor`
|
|
29
|
+
stays non-strict, so a consumer on 5.2.0 strips the new key rather than
|
|
30
|
+
refusing the run. Changes the artifacts `job-actor`, `job-run` and
|
|
31
|
+
`job-run-list-response`.
|
|
32
|
+
- **`roleRenameRequest`, `roleDeleteQuery` and `roleInUseDetails`**
|
|
33
|
+
(`RoleRenameRequest`, `RoleDeleteQuery`, `RoleInUseDetails`) for the new
|
|
34
|
+
`PATCH` and `DELETE /api/apps/:id/roles/:roleId`: rename a role
|
|
35
|
+
(`409 role_name_taken` on a clash), or delete it, moving its app users,
|
|
36
|
+
pending invitations and default-role status to `move_to`. Without
|
|
37
|
+
`move_to` a held role answers `409 role_in_use` with
|
|
38
|
+
`{ users, invitations, is_default }`; the app's only role answers
|
|
39
|
+
`409 last_role`. The three codes join `ERROR_CODES`. `role.builtin` no
|
|
40
|
+
longer says built-in roles cannot be renamed or deleted, and the create
|
|
41
|
+
route's note now states the 60-character limit `role.name` always had.
|
|
42
|
+
Published as the artifacts `role-rename-request`, `role-delete-query`
|
|
43
|
+
and `role-in-use-details`.
|
|
44
|
+
- **`auditQuery.target_id`**: only the events about one target — for a
|
|
45
|
+
robot also those that name it in `details.robot_id`, so a robot's log
|
|
46
|
+
includes what was started on it. A string, since target ids are not all
|
|
47
|
+
uuids. `GET /api/audit/export` takes it too. Changes the artifact
|
|
48
|
+
`audit-query`.
|
|
49
|
+
|
|
12
50
|
## [5.2.0] — 2026-09-30
|
|
13
51
|
|
|
14
52
|
### Added
|
package/artifacts/openapi.json
CHANGED
|
@@ -525,6 +525,17 @@
|
|
|
525
525
|
"maxLength": 40
|
|
526
526
|
}
|
|
527
527
|
},
|
|
528
|
+
{
|
|
529
|
+
"name": "target_id",
|
|
530
|
+
"in": "query",
|
|
531
|
+
"required": false,
|
|
532
|
+
"schema": {
|
|
533
|
+
"description": "Events whose target is this id; for a robot also the events that name it in `details.robot_id` (`action.invoked`, `service.called`, …), so a robot's events include what was started on it.",
|
|
534
|
+
"type": "string",
|
|
535
|
+
"minLength": 1,
|
|
536
|
+
"maxLength": 200
|
|
537
|
+
}
|
|
538
|
+
},
|
|
528
539
|
{
|
|
529
540
|
"name": "from_ms",
|
|
530
541
|
"in": "query",
|
|
@@ -676,6 +687,17 @@
|
|
|
676
687
|
"maxLength": 40
|
|
677
688
|
}
|
|
678
689
|
},
|
|
690
|
+
{
|
|
691
|
+
"name": "target_id",
|
|
692
|
+
"in": "query",
|
|
693
|
+
"required": false,
|
|
694
|
+
"schema": {
|
|
695
|
+
"description": "Events whose target is this id; for a robot also the events that name it in `details.robot_id` (`action.invoked`, `service.called`, …), so a robot's events include what was started on it.",
|
|
696
|
+
"type": "string",
|
|
697
|
+
"minLength": 1,
|
|
698
|
+
"maxLength": 200
|
|
699
|
+
}
|
|
700
|
+
},
|
|
679
701
|
{
|
|
680
702
|
"name": "from_ms",
|
|
681
703
|
"in": "query",
|
|
@@ -1058,7 +1080,7 @@
|
|
|
1058
1080
|
}
|
|
1059
1081
|
}
|
|
1060
1082
|
},
|
|
1061
|
-
"description": "The body is `{ \"name\": string }` — non-empty, trimmed, at most
|
|
1083
|
+
"description": "The body is `{ \"name\": string }` — non-empty, trimmed, at most 60 characters as on `role.name` — and is deliberately not a contract shape: contracts define the `role` this answers with, not this one trivial request. **The answer is a bare `role`, not an envelope**, unlike the listing beside it."
|
|
1062
1084
|
},
|
|
1063
1085
|
"get": {
|
|
1064
1086
|
"operationId": "get_api_apps_id_roles",
|
|
@@ -1285,6 +1307,132 @@
|
|
|
1285
1307
|
"description": "Built by the same builder the MCP server's own `robot_describe` uses, so the two cannot drift. It answers what the role *would* be offered and consults nothing about any user's actual MCP entitlement. A robot the role grants nothing on still appears, with an empty `exposures` — dropping it would read as \"not attached\", which is a different fact."
|
|
1286
1308
|
}
|
|
1287
1309
|
},
|
|
1310
|
+
"/api/apps/{id}/roles/{roleId}": {
|
|
1311
|
+
"patch": {
|
|
1312
|
+
"operationId": "patch_api_apps_id_roles_roleId",
|
|
1313
|
+
"summary": "Renames a role; its users keep it.",
|
|
1314
|
+
"tags": [
|
|
1315
|
+
"apps"
|
|
1316
|
+
],
|
|
1317
|
+
"security": [
|
|
1318
|
+
{
|
|
1319
|
+
"developerSession": []
|
|
1320
|
+
}
|
|
1321
|
+
],
|
|
1322
|
+
"parameters": [
|
|
1323
|
+
{
|
|
1324
|
+
"name": "id",
|
|
1325
|
+
"in": "path",
|
|
1326
|
+
"required": true,
|
|
1327
|
+
"description": "The app's uuid, as returned by `POST /api/apps` or listed by `GET /api/apps`.",
|
|
1328
|
+
"schema": {
|
|
1329
|
+
"type": "string"
|
|
1330
|
+
}
|
|
1331
|
+
},
|
|
1332
|
+
{
|
|
1333
|
+
"name": "roleId",
|
|
1334
|
+
"in": "path",
|
|
1335
|
+
"required": true,
|
|
1336
|
+
"description": "The role's uuid, from `GET /api/apps/:id/roles`; a role of another app answers `404`.",
|
|
1337
|
+
"schema": {
|
|
1338
|
+
"type": "string"
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
],
|
|
1342
|
+
"responses": {
|
|
1343
|
+
"200": {
|
|
1344
|
+
"description": "Success.",
|
|
1345
|
+
"content": {
|
|
1346
|
+
"application/json": {
|
|
1347
|
+
"schema": {
|
|
1348
|
+
"$ref": "#/components/schemas/role"
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
},
|
|
1353
|
+
"default": {
|
|
1354
|
+
"description": "An error envelope. Codes this route is known to answer: `unauthorized`, `token_expired`, `token_revoked`, `invalid_uuid`, `not_found`, `validation_error`, `role_name_taken`.",
|
|
1355
|
+
"content": {
|
|
1356
|
+
"application/json": {
|
|
1357
|
+
"schema": {
|
|
1358
|
+
"$ref": "#/components/schemas/api-error"
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
},
|
|
1364
|
+
"description": "Names are unique per app, compared exactly as stored after trimming. Built-in roles can be renamed.",
|
|
1365
|
+
"requestBody": {
|
|
1366
|
+
"required": true,
|
|
1367
|
+
"content": {
|
|
1368
|
+
"application/json": {
|
|
1369
|
+
"schema": {
|
|
1370
|
+
"$ref": "#/components/schemas/role-rename-request"
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
},
|
|
1376
|
+
"delete": {
|
|
1377
|
+
"operationId": "delete_api_apps_id_roles_roleId",
|
|
1378
|
+
"summary": "Deletes a role, moving its users, pending invitations and default-role status to another role.",
|
|
1379
|
+
"tags": [
|
|
1380
|
+
"apps"
|
|
1381
|
+
],
|
|
1382
|
+
"security": [
|
|
1383
|
+
{
|
|
1384
|
+
"developerSession": []
|
|
1385
|
+
}
|
|
1386
|
+
],
|
|
1387
|
+
"parameters": [
|
|
1388
|
+
{
|
|
1389
|
+
"name": "id",
|
|
1390
|
+
"in": "path",
|
|
1391
|
+
"required": true,
|
|
1392
|
+
"description": "The app's uuid, as returned by `POST /api/apps` or listed by `GET /api/apps`.",
|
|
1393
|
+
"schema": {
|
|
1394
|
+
"type": "string"
|
|
1395
|
+
}
|
|
1396
|
+
},
|
|
1397
|
+
{
|
|
1398
|
+
"name": "roleId",
|
|
1399
|
+
"in": "path",
|
|
1400
|
+
"required": true,
|
|
1401
|
+
"description": "The role's uuid, from `GET /api/apps/:id/roles`; a role of another app answers `404`.",
|
|
1402
|
+
"schema": {
|
|
1403
|
+
"type": "string"
|
|
1404
|
+
}
|
|
1405
|
+
},
|
|
1406
|
+
{
|
|
1407
|
+
"name": "move_to",
|
|
1408
|
+
"in": "query",
|
|
1409
|
+
"required": false,
|
|
1410
|
+
"schema": {
|
|
1411
|
+
"description": "Another role of the same app that takes over the deleted role's app users, pending invitations and, when it applies, the app's default. The role itself or a role of another app answers `400 validation_error`.",
|
|
1412
|
+
"type": "string",
|
|
1413
|
+
"format": "uuid",
|
|
1414
|
+
"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)$"
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
],
|
|
1418
|
+
"responses": {
|
|
1419
|
+
"204": {
|
|
1420
|
+
"description": "Success."
|
|
1421
|
+
},
|
|
1422
|
+
"default": {
|
|
1423
|
+
"description": "An error envelope. Codes this route is known to answer: `unauthorized`, `token_expired`, `token_revoked`, `invalid_uuid`, `not_found`, `validation_error`, `role_in_use`, `last_role`.",
|
|
1424
|
+
"content": {
|
|
1425
|
+
"application/json": {
|
|
1426
|
+
"schema": {
|
|
1427
|
+
"$ref": "#/components/schemas/api-error"
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
},
|
|
1433
|
+
"description": "Without `move_to`, a role that app users or pending invitations hold, or that is the app's default, answers `409 role_in_use` with `{ users, invitations, is_default }`. With `move_to` — another role of the same app, else `400 validation_error` — one transaction moves `app_users.role_id`, pending invitations and `default_role_id`, then deletes the role and its permissions. The app's only role answers `409 last_role`. Built-in roles can be deleted like any other."
|
|
1434
|
+
}
|
|
1435
|
+
},
|
|
1288
1436
|
"/api/apps/{id}/server-keys": {
|
|
1289
1437
|
"post": {
|
|
1290
1438
|
"operationId": "post_api_apps_id_server_keys",
|
|
@@ -8476,6 +8624,54 @@
|
|
|
8476
8624
|
},
|
|
8477
8625
|
"description": "A window longer than `USAGE_WINDOW_MAX_DAYS` is refused naming the field, not silently capped: a caller who asked for more than the platform will answer is owed a refusal, not a shorter answer they will mistake for the whole picture. `from_day <= to_day` is a cross-field rule no JSON Schema can express and is enforced here. The window is echoed back."
|
|
8478
8626
|
}
|
|
8627
|
+
},
|
|
8628
|
+
"/api/feedback": {
|
|
8629
|
+
"post": {
|
|
8630
|
+
"operationId": "post_api_feedback",
|
|
8631
|
+
"summary": "Sends a message from a developer to the people who build Fleetless.",
|
|
8632
|
+
"tags": [
|
|
8633
|
+
"org"
|
|
8634
|
+
],
|
|
8635
|
+
"security": [
|
|
8636
|
+
{
|
|
8637
|
+
"developerSession": []
|
|
8638
|
+
}
|
|
8639
|
+
],
|
|
8640
|
+
"parameters": [],
|
|
8641
|
+
"responses": {
|
|
8642
|
+
"202": {
|
|
8643
|
+
"description": "Success.",
|
|
8644
|
+
"content": {
|
|
8645
|
+
"application/json": {
|
|
8646
|
+
"schema": {
|
|
8647
|
+
"$ref": "#/components/schemas/feedback-response"
|
|
8648
|
+
}
|
|
8649
|
+
}
|
|
8650
|
+
}
|
|
8651
|
+
},
|
|
8652
|
+
"default": {
|
|
8653
|
+
"description": "An error envelope. Codes this route is known to answer: `unauthorized`, `token_expired`, `token_revoked`, `validation_error`, `rate_limited`.",
|
|
8654
|
+
"content": {
|
|
8655
|
+
"application/json": {
|
|
8656
|
+
"schema": {
|
|
8657
|
+
"$ref": "#/components/schemas/api-error"
|
|
8658
|
+
}
|
|
8659
|
+
}
|
|
8660
|
+
}
|
|
8661
|
+
}
|
|
8662
|
+
},
|
|
8663
|
+
"description": "The message is stored before any mail is tried, so `202` means it is kept whatever `mail` says: `sent`, `failed`, or `not_configured` when this cloud has no feedback address. At most 10 messages per developer per hour; the 11th answers `429 rate_limited` with `retry_after_ms`. Replies come by mail, to the sender's address.",
|
|
8664
|
+
"requestBody": {
|
|
8665
|
+
"required": true,
|
|
8666
|
+
"content": {
|
|
8667
|
+
"application/json": {
|
|
8668
|
+
"schema": {
|
|
8669
|
+
"$ref": "#/components/schemas/feedback-request"
|
|
8670
|
+
}
|
|
8671
|
+
}
|
|
8672
|
+
}
|
|
8673
|
+
}
|
|
8674
|
+
}
|
|
8479
8675
|
}
|
|
8480
8676
|
},
|
|
8481
8677
|
"components": {
|
|
@@ -14004,6 +14200,65 @@
|
|
|
14004
14200
|
],
|
|
14005
14201
|
"additionalProperties": false
|
|
14006
14202
|
},
|
|
14203
|
+
"feedback-request": {
|
|
14204
|
+
"type": "object",
|
|
14205
|
+
"properties": {
|
|
14206
|
+
"kind": {
|
|
14207
|
+
"type": "string",
|
|
14208
|
+
"enum": [
|
|
14209
|
+
"idea",
|
|
14210
|
+
"problem",
|
|
14211
|
+
"question",
|
|
14212
|
+
"other"
|
|
14213
|
+
],
|
|
14214
|
+
"description": "What the message is: an `idea`, a `problem`, a `question` or `other`. It only sorts the inbox; it changes nothing about how the message is handled."
|
|
14215
|
+
},
|
|
14216
|
+
"message": {
|
|
14217
|
+
"type": "string",
|
|
14218
|
+
"minLength": 1,
|
|
14219
|
+
"maxLength": 5000,
|
|
14220
|
+
"description": "What the developer wrote, trimmed. At most 5000 characters; a message that is only whitespace is refused."
|
|
14221
|
+
},
|
|
14222
|
+
"page": {
|
|
14223
|
+
"type": "string",
|
|
14224
|
+
"maxLength": 512,
|
|
14225
|
+
"pattern": "^\\/.*",
|
|
14226
|
+
"description": "The console path the message was sent from, e.g. `/robots/:id/jobs` with its real id. A path, never a full URL, so no host and no query string reach the inbox by accident."
|
|
14227
|
+
}
|
|
14228
|
+
},
|
|
14229
|
+
"required": [
|
|
14230
|
+
"kind",
|
|
14231
|
+
"message",
|
|
14232
|
+
"page"
|
|
14233
|
+
],
|
|
14234
|
+
"additionalProperties": false
|
|
14235
|
+
},
|
|
14236
|
+
"feedback-response": {
|
|
14237
|
+
"type": "object",
|
|
14238
|
+
"properties": {
|
|
14239
|
+
"id": {
|
|
14240
|
+
"type": "string",
|
|
14241
|
+
"format": "uuid",
|
|
14242
|
+
"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)$",
|
|
14243
|
+
"description": "The stored message. It exists whatever `mail` says."
|
|
14244
|
+
},
|
|
14245
|
+
"mail": {
|
|
14246
|
+
"type": "string",
|
|
14247
|
+
"enum": [
|
|
14248
|
+
"sent",
|
|
14249
|
+
"not_requested",
|
|
14250
|
+
"not_configured",
|
|
14251
|
+
"failed"
|
|
14252
|
+
],
|
|
14253
|
+
"description": "What happened to the notification mail: `sent`, `failed`, or `not_configured` when this cloud has no feedback address. The message is stored in every case, so a client shows success for all three."
|
|
14254
|
+
}
|
|
14255
|
+
},
|
|
14256
|
+
"required": [
|
|
14257
|
+
"id",
|
|
14258
|
+
"mail"
|
|
14259
|
+
],
|
|
14260
|
+
"additionalProperties": false
|
|
14261
|
+
},
|
|
14007
14262
|
"fetch-types-request": {
|
|
14008
14263
|
"type": "object",
|
|
14009
14264
|
"properties": {
|
|
@@ -15026,12 +15281,26 @@
|
|
|
15026
15281
|
"minLength": 1,
|
|
15027
15282
|
"maxLength": 200,
|
|
15028
15283
|
"description": "A display name taken at invoke time — the email for a Fleetless user or an app user, the key's own name for a server key. Storing it rather than joining is the point: renaming a key afterwards does not rewrite history."
|
|
15284
|
+
},
|
|
15285
|
+
"name": {
|
|
15286
|
+
"anyOf": [
|
|
15287
|
+
{
|
|
15288
|
+
"type": "string",
|
|
15289
|
+
"minLength": 1,
|
|
15290
|
+
"maxLength": 200
|
|
15291
|
+
},
|
|
15292
|
+
{
|
|
15293
|
+
"type": "null"
|
|
15294
|
+
}
|
|
15295
|
+
],
|
|
15296
|
+
"description": "The person's display name when the job started: the Fleetless user's `display_name` for a developer, the app user's `display_name` for an app user. `null` for a server key, when the person had no name set, and for runs recorded before contracts 5.3.0. Show `label` when it is null."
|
|
15029
15297
|
}
|
|
15030
15298
|
},
|
|
15031
15299
|
"required": [
|
|
15032
15300
|
"kind",
|
|
15033
15301
|
"id",
|
|
15034
|
-
"label"
|
|
15302
|
+
"label",
|
|
15303
|
+
"name"
|
|
15035
15304
|
],
|
|
15036
15305
|
"additionalProperties": false,
|
|
15037
15306
|
"description": "Who invoked the run, and what they were acting as at the time."
|
|
@@ -17637,7 +17906,7 @@
|
|
|
17637
17906
|
},
|
|
17638
17907
|
"builtin": {
|
|
17639
17908
|
"type": "boolean",
|
|
17640
|
-
"description": "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them.
|
|
17909
|
+
"description": "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them. Built-in roles can be renamed and deleted like any other; the flag only records that the cloud seeded them."
|
|
17641
17910
|
}
|
|
17642
17911
|
},
|
|
17643
17912
|
"required": [
|
|
@@ -17676,7 +17945,7 @@
|
|
|
17676
17945
|
},
|
|
17677
17946
|
"builtin": {
|
|
17678
17947
|
"type": "boolean",
|
|
17679
|
-
"description": "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them.
|
|
17948
|
+
"description": "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them. Built-in roles can be renamed and deleted like any other; the flag only records that the cloud seeded them."
|
|
17680
17949
|
}
|
|
17681
17950
|
},
|
|
17682
17951
|
"required": [
|
|
@@ -17755,6 +18024,21 @@
|
|
|
17755
18024
|
"capabilities"
|
|
17756
18025
|
]
|
|
17757
18026
|
},
|
|
18027
|
+
"role-rename-request": {
|
|
18028
|
+
"type": "object",
|
|
18029
|
+
"properties": {
|
|
18030
|
+
"name": {
|
|
18031
|
+
"type": "string",
|
|
18032
|
+
"minLength": 1,
|
|
18033
|
+
"maxLength": 60,
|
|
18034
|
+
"description": "The new name, trimmed, 1 to 60 characters. Unique per app: another role of this app with the same name answers `409 role_name_taken`. The role's users keep it under its new name."
|
|
18035
|
+
}
|
|
18036
|
+
},
|
|
18037
|
+
"required": [
|
|
18038
|
+
"name"
|
|
18039
|
+
],
|
|
18040
|
+
"additionalProperties": false
|
|
18041
|
+
},
|
|
17758
18042
|
"server-key-list-response": {
|
|
17759
18043
|
"type": "object",
|
|
17760
18044
|
"properties": {
|
package/artifacts/routes.json
CHANGED
|
@@ -574,7 +574,7 @@
|
|
|
574
574
|
"validation_error"
|
|
575
575
|
],
|
|
576
576
|
"transport": "http",
|
|
577
|
-
"notes": "The body is `{ \"name\": string }` — non-empty, trimmed, at most
|
|
577
|
+
"notes": "The body is `{ \"name\": string }` — non-empty, trimmed, at most 60 characters as on `role.name` — and is deliberately not a contract shape: contracts define the `role` this answers with, not this one trivial request. **The answer is a bare `role`, not an envelope**, unlike the listing beside it."
|
|
578
578
|
},
|
|
579
579
|
{
|
|
580
580
|
"method": "GET",
|
|
@@ -704,6 +704,77 @@
|
|
|
704
704
|
"transport": "http",
|
|
705
705
|
"notes": "Built by the same builder the MCP server's own `robot_describe` uses, so the two cannot drift. It answers what the role *would* be offered and consults nothing about any user's actual MCP entitlement. A robot the role grants nothing on still appears, with an empty `exposures` — dropping it would read as \"not attached\", which is a different fact."
|
|
706
706
|
},
|
|
707
|
+
{
|
|
708
|
+
"method": "PATCH",
|
|
709
|
+
"path": "/api/apps/:id/roles/:roleId",
|
|
710
|
+
"section": "apps",
|
|
711
|
+
"summary": "Renames a role; its users keep it.",
|
|
712
|
+
"audience": "developer",
|
|
713
|
+
"auth": "developer",
|
|
714
|
+
"rateLimited": false,
|
|
715
|
+
"ownerTier": false,
|
|
716
|
+
"status": 200,
|
|
717
|
+
"params": [
|
|
718
|
+
{
|
|
719
|
+
"name": "id",
|
|
720
|
+
"description": "The app's uuid, as returned by `POST /api/apps` or listed by `GET /api/apps`."
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
"name": "roleId",
|
|
724
|
+
"description": "The role's uuid, from `GET /api/apps/:id/roles`; a role of another app answers `404`."
|
|
725
|
+
}
|
|
726
|
+
],
|
|
727
|
+
"query": null,
|
|
728
|
+
"request": "role-rename-request",
|
|
729
|
+
"response": "role",
|
|
730
|
+
"errors": [
|
|
731
|
+
"unauthorized",
|
|
732
|
+
"token_expired",
|
|
733
|
+
"token_revoked",
|
|
734
|
+
"invalid_uuid",
|
|
735
|
+
"not_found",
|
|
736
|
+
"validation_error",
|
|
737
|
+
"role_name_taken"
|
|
738
|
+
],
|
|
739
|
+
"transport": "http",
|
|
740
|
+
"notes": "Names are unique per app, compared exactly as stored after trimming. Built-in roles can be renamed."
|
|
741
|
+
},
|
|
742
|
+
{
|
|
743
|
+
"method": "DELETE",
|
|
744
|
+
"path": "/api/apps/:id/roles/:roleId",
|
|
745
|
+
"section": "apps",
|
|
746
|
+
"summary": "Deletes a role, moving its users, pending invitations and default-role status to another role.",
|
|
747
|
+
"audience": "developer",
|
|
748
|
+
"auth": "developer",
|
|
749
|
+
"rateLimited": false,
|
|
750
|
+
"ownerTier": false,
|
|
751
|
+
"status": 204,
|
|
752
|
+
"params": [
|
|
753
|
+
{
|
|
754
|
+
"name": "id",
|
|
755
|
+
"description": "The app's uuid, as returned by `POST /api/apps` or listed by `GET /api/apps`."
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
"name": "roleId",
|
|
759
|
+
"description": "The role's uuid, from `GET /api/apps/:id/roles`; a role of another app answers `404`."
|
|
760
|
+
}
|
|
761
|
+
],
|
|
762
|
+
"query": "role-delete-query",
|
|
763
|
+
"request": null,
|
|
764
|
+
"response": null,
|
|
765
|
+
"errors": [
|
|
766
|
+
"unauthorized",
|
|
767
|
+
"token_expired",
|
|
768
|
+
"token_revoked",
|
|
769
|
+
"invalid_uuid",
|
|
770
|
+
"not_found",
|
|
771
|
+
"validation_error",
|
|
772
|
+
"role_in_use",
|
|
773
|
+
"last_role"
|
|
774
|
+
],
|
|
775
|
+
"transport": "http",
|
|
776
|
+
"notes": "Without `move_to`, a role that app users or pending invitations hold, or that is the app's default, answers `409 role_in_use` with `{ users, invitations, is_default }`. With `move_to` — another role of the same app, else `400 validation_error` — one transaction moves `app_users.role_id`, pending invitations and `default_role_id`, then deletes the role and its permissions. The app's only role answers `409 last_role`. Built-in roles can be deleted like any other."
|
|
777
|
+
},
|
|
707
778
|
{
|
|
708
779
|
"method": "POST",
|
|
709
780
|
"path": "/api/apps/:id/server-keys",
|
|
@@ -4821,6 +4892,30 @@
|
|
|
4821
4892
|
"transport": "http",
|
|
4822
4893
|
"notes": "A window longer than `USAGE_WINDOW_MAX_DAYS` is refused naming the field, not silently capped: a caller who asked for more than the platform will answer is owed a refusal, not a shorter answer they will mistake for the whole picture. `from_day <= to_day` is a cross-field rule no JSON Schema can express and is enforced here. The window is echoed back."
|
|
4823
4894
|
},
|
|
4895
|
+
{
|
|
4896
|
+
"method": "POST",
|
|
4897
|
+
"path": "/api/feedback",
|
|
4898
|
+
"section": "org",
|
|
4899
|
+
"summary": "Sends a message from a developer to the people who build Fleetless.",
|
|
4900
|
+
"audience": "developer",
|
|
4901
|
+
"auth": "developer",
|
|
4902
|
+
"rateLimited": true,
|
|
4903
|
+
"ownerTier": false,
|
|
4904
|
+
"status": 202,
|
|
4905
|
+
"params": [],
|
|
4906
|
+
"query": null,
|
|
4907
|
+
"request": "feedback-request",
|
|
4908
|
+
"response": "feedback-response",
|
|
4909
|
+
"errors": [
|
|
4910
|
+
"unauthorized",
|
|
4911
|
+
"token_expired",
|
|
4912
|
+
"token_revoked",
|
|
4913
|
+
"validation_error",
|
|
4914
|
+
"rate_limited"
|
|
4915
|
+
],
|
|
4916
|
+
"transport": "http",
|
|
4917
|
+
"notes": "The message is stored before any mail is tried, so `202` means it is kept whatever `mail` says: `sent`, `failed`, or `not_configured` when this cloud has no feedback address. At most 10 messages per developer per hour; the 11th answers `429 rate_limited` with `retry_after_ms`. Replies come by mail, to the sender's address."
|
|
4918
|
+
},
|
|
4824
4919
|
{
|
|
4825
4920
|
"method": "POST",
|
|
4826
4921
|
"path": "/api/bridge/assets",
|
|
@@ -48,6 +48,12 @@
|
|
|
48
48
|
"minLength": 1,
|
|
49
49
|
"maxLength": 40
|
|
50
50
|
},
|
|
51
|
+
"target_id": {
|
|
52
|
+
"description": "Events whose target is this id; for a robot also the events that name it in `details.robot_id` (`action.invoked`, `service.called`, …), so a robot's events include what was started on it.",
|
|
53
|
+
"type": "string",
|
|
54
|
+
"minLength": 1,
|
|
55
|
+
"maxLength": 200
|
|
56
|
+
},
|
|
51
57
|
"from_ms": {
|
|
52
58
|
"anyOf": [
|
|
53
59
|
{
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"kind": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"enum": [
|
|
8
|
+
"idea",
|
|
9
|
+
"problem",
|
|
10
|
+
"question",
|
|
11
|
+
"other"
|
|
12
|
+
],
|
|
13
|
+
"description": "What the message is: an `idea`, a `problem`, a `question` or `other`. It only sorts the inbox; it changes nothing about how the message is handled."
|
|
14
|
+
},
|
|
15
|
+
"message": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"minLength": 1,
|
|
18
|
+
"maxLength": 5000,
|
|
19
|
+
"description": "What the developer wrote, trimmed. At most 5000 characters; a message that is only whitespace is refused."
|
|
20
|
+
},
|
|
21
|
+
"page": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"maxLength": 512,
|
|
24
|
+
"pattern": "^\\/.*",
|
|
25
|
+
"description": "The console path the message was sent from, e.g. `/robots/:id/jobs` with its real id. A path, never a full URL, so no host and no query string reach the inbox by accident."
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"required": [
|
|
29
|
+
"kind",
|
|
30
|
+
"message",
|
|
31
|
+
"page"
|
|
32
|
+
],
|
|
33
|
+
"additionalProperties": false
|
|
34
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"id": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"format": "uuid",
|
|
8
|
+
"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)$",
|
|
9
|
+
"description": "The stored message. It exists whatever `mail` says."
|
|
10
|
+
},
|
|
11
|
+
"mail": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"enum": [
|
|
14
|
+
"sent",
|
|
15
|
+
"not_requested",
|
|
16
|
+
"not_configured",
|
|
17
|
+
"failed"
|
|
18
|
+
],
|
|
19
|
+
"description": "What happened to the notification mail: `sent`, `failed`, or `not_configured` when this cloud has no feedback address. The message is stored in every case, so a client shows success for all three."
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"required": [
|
|
23
|
+
"id",
|
|
24
|
+
"mail"
|
|
25
|
+
],
|
|
26
|
+
"additionalProperties": false
|
|
27
|
+
}
|
|
@@ -23,12 +23,26 @@
|
|
|
23
23
|
"minLength": 1,
|
|
24
24
|
"maxLength": 200,
|
|
25
25
|
"description": "A display name taken at invoke time — the email for a Fleetless user or an app user, the key's own name for a server key. Storing it rather than joining is the point: renaming a key afterwards does not rewrite history."
|
|
26
|
+
},
|
|
27
|
+
"name": {
|
|
28
|
+
"anyOf": [
|
|
29
|
+
{
|
|
30
|
+
"type": "string",
|
|
31
|
+
"minLength": 1,
|
|
32
|
+
"maxLength": 200
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"type": "null"
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"description": "The person's display name when the job started: the Fleetless user's `display_name` for a developer, the app user's `display_name` for an app user. `null` for a server key, when the person had no name set, and for runs recorded before contracts 5.3.0. Show `label` when it is null."
|
|
26
39
|
}
|
|
27
40
|
},
|
|
28
41
|
"required": [
|
|
29
42
|
"kind",
|
|
30
43
|
"id",
|
|
31
|
-
"label"
|
|
44
|
+
"label",
|
|
45
|
+
"name"
|
|
32
46
|
],
|
|
33
47
|
"additionalProperties": false
|
|
34
48
|
}
|
|
@@ -142,12 +142,26 @@
|
|
|
142
142
|
"minLength": 1,
|
|
143
143
|
"maxLength": 200,
|
|
144
144
|
"description": "A display name taken at invoke time — the email for a Fleetless user or an app user, the key's own name for a server key. Storing it rather than joining is the point: renaming a key afterwards does not rewrite history."
|
|
145
|
+
},
|
|
146
|
+
"name": {
|
|
147
|
+
"anyOf": [
|
|
148
|
+
{
|
|
149
|
+
"type": "string",
|
|
150
|
+
"minLength": 1,
|
|
151
|
+
"maxLength": 200
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"type": "null"
|
|
155
|
+
}
|
|
156
|
+
],
|
|
157
|
+
"description": "The person's display name when the job started: the Fleetless user's `display_name` for a developer, the app user's `display_name` for an app user. `null` for a server key, when the person had no name set, and for runs recorded before contracts 5.3.0. Show `label` when it is null."
|
|
145
158
|
}
|
|
146
159
|
},
|
|
147
160
|
"required": [
|
|
148
161
|
"kind",
|
|
149
162
|
"id",
|
|
150
|
-
"label"
|
|
163
|
+
"label",
|
|
164
|
+
"name"
|
|
151
165
|
],
|
|
152
166
|
"additionalProperties": false,
|
|
153
167
|
"description": "Who invoked the run, and what they were acting as at the time."
|
|
@@ -137,12 +137,26 @@
|
|
|
137
137
|
"minLength": 1,
|
|
138
138
|
"maxLength": 200,
|
|
139
139
|
"description": "A display name taken at invoke time — the email for a Fleetless user or an app user, the key's own name for a server key. Storing it rather than joining is the point: renaming a key afterwards does not rewrite history."
|
|
140
|
+
},
|
|
141
|
+
"name": {
|
|
142
|
+
"anyOf": [
|
|
143
|
+
{
|
|
144
|
+
"type": "string",
|
|
145
|
+
"minLength": 1,
|
|
146
|
+
"maxLength": 200
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"type": "null"
|
|
150
|
+
}
|
|
151
|
+
],
|
|
152
|
+
"description": "The person's display name when the job started: the Fleetless user's `display_name` for a developer, the app user's `display_name` for an app user. `null` for a server key, when the person had no name set, and for runs recorded before contracts 5.3.0. Show `label` when it is null."
|
|
140
153
|
}
|
|
141
154
|
},
|
|
142
155
|
"required": [
|
|
143
156
|
"kind",
|
|
144
157
|
"id",
|
|
145
|
-
"label"
|
|
158
|
+
"label",
|
|
159
|
+
"name"
|
|
146
160
|
],
|
|
147
161
|
"additionalProperties": false,
|
|
148
162
|
"description": "Who invoked the run, and what they were acting as at the time."
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"move_to": {
|
|
6
|
+
"description": "Another role of the same app that takes over the deleted role's app users, pending invitations and, when it applies, the app's default. The role itself or a role of another app answers `400 validation_error`.",
|
|
7
|
+
"type": "string",
|
|
8
|
+
"format": "uuid",
|
|
9
|
+
"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)$"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"additionalProperties": false
|
|
13
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"users": {
|
|
6
|
+
"type": "integer",
|
|
7
|
+
"minimum": 0,
|
|
8
|
+
"maximum": 9007199254740991,
|
|
9
|
+
"description": "App users whose role this is."
|
|
10
|
+
},
|
|
11
|
+
"invitations": {
|
|
12
|
+
"type": "integer",
|
|
13
|
+
"minimum": 0,
|
|
14
|
+
"maximum": 9007199254740991,
|
|
15
|
+
"description": "Pending invitations that would grant this role when accepted."
|
|
16
|
+
},
|
|
17
|
+
"is_default": {
|
|
18
|
+
"type": "boolean",
|
|
19
|
+
"description": "`true` when this is the app's `default_role_id`; the default then moves with the users to `move_to`."
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"required": [
|
|
23
|
+
"users",
|
|
24
|
+
"invitations",
|
|
25
|
+
"is_default"
|
|
26
|
+
],
|
|
27
|
+
"additionalProperties": false
|
|
28
|
+
}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"builtin": {
|
|
29
29
|
"type": "boolean",
|
|
30
|
-
"description": "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them.
|
|
30
|
+
"description": "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them. Built-in roles can be renamed and deleted like any other; the flag only records that the cloud seeded them."
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
"required": [
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"name": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"minLength": 1,
|
|
8
|
+
"maxLength": 60,
|
|
9
|
+
"description": "The new name, trimmed, 1 to 60 characters. Unique per app: another role of this app with the same name answers `409 role_name_taken`. The role's users keep it under its new name."
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"required": [
|
|
13
|
+
"name"
|
|
14
|
+
],
|
|
15
|
+
"additionalProperties": false
|
|
16
|
+
}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"builtin": {
|
|
24
24
|
"type": "boolean",
|
|
25
|
-
"description": "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them.
|
|
25
|
+
"description": "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them. Built-in roles can be renamed and deleted like any other; the flag only records that the cloud seeded them."
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"required": [
|
package/dist/apps.d.ts
CHANGED
|
@@ -151,8 +151,9 @@ export declare const createServerKeyResponse: z.ZodObject<{
|
|
|
151
151
|
export type CreateServerKeyResponse = z.infer<typeof createServerKeyResponse>;
|
|
152
152
|
/**
|
|
153
153
|
* Every app starts with `observe` and `operate`; custom roles are allowed
|
|
154
|
-
* too. `builtin` marks the two starting roles — editable
|
|
155
|
-
* the flag only tells the console where they came
|
|
154
|
+
* too. `builtin` marks the two starting roles — editable, renamable and
|
|
155
|
+
* deletable like any other; the flag only tells the console where they came
|
|
156
|
+
* from.
|
|
156
157
|
*/
|
|
157
158
|
export declare const role: z.ZodObject<{
|
|
158
159
|
id: z.ZodUUID;
|
|
@@ -171,6 +172,41 @@ export declare const roleListResponse: z.ZodObject<{
|
|
|
171
172
|
}, z.core.$strip>>;
|
|
172
173
|
}, z.core.$strip>;
|
|
173
174
|
export type RoleListResponse = z.infer<typeof roleListResponse>;
|
|
175
|
+
/**
|
|
176
|
+
* The body of `PATCH /api/apps/:id/roles/:roleId`: the role's new name.
|
|
177
|
+
*
|
|
178
|
+
* The same bounds as `role.name`, trimmed. Names are unique per app, compared
|
|
179
|
+
* exactly as stored after trimming; a clash answers `409 role_name_taken`.
|
|
180
|
+
*/
|
|
181
|
+
export declare const roleRenameRequest: z.ZodObject<{
|
|
182
|
+
name: z.ZodString;
|
|
183
|
+
}, z.core.$strict>;
|
|
184
|
+
export type RoleRenameRequest = z.infer<typeof roleRenameRequest>;
|
|
185
|
+
/**
|
|
186
|
+
* The query of `DELETE /api/apps/:id/roles/:roleId`.
|
|
187
|
+
*
|
|
188
|
+
* `move_to` is what makes a held role deletable: every app user and pending
|
|
189
|
+
* invitation holding the role moves to it, and so does the app's default when
|
|
190
|
+
* it pointed at the role, in the same transaction as the delete. Without it a
|
|
191
|
+
* held role answers `409 role_in_use` with `roleInUseDetails`, so a client
|
|
192
|
+
* can ask where they should go instead of guessing.
|
|
193
|
+
*/
|
|
194
|
+
export declare const roleDeleteQuery: z.ZodObject<{
|
|
195
|
+
move_to: z.ZodOptional<z.ZodUUID>;
|
|
196
|
+
}, z.core.$strict>;
|
|
197
|
+
export type RoleDeleteQuery = z.infer<typeof roleDeleteQuery>;
|
|
198
|
+
/**
|
|
199
|
+
* `details` of `409 role_in_use`: what still holds the role.
|
|
200
|
+
*
|
|
201
|
+
* All three are reported, zeros included, so a client renders one sentence
|
|
202
|
+
* from one shape rather than inferring a missing key.
|
|
203
|
+
*/
|
|
204
|
+
export declare const roleInUseDetails: z.ZodObject<{
|
|
205
|
+
users: z.ZodNumber;
|
|
206
|
+
invitations: z.ZodNumber;
|
|
207
|
+
is_default: z.ZodBoolean;
|
|
208
|
+
}, z.core.$strict>;
|
|
209
|
+
export type RoleInUseDetails = z.infer<typeof roleInUseDetails>;
|
|
174
210
|
/**
|
|
175
211
|
* The rights matrix of one role: which slugs of which robot it may use, plus
|
|
176
212
|
* the capabilities roles also govern. `capabilities`' own doc comment
|
package/dist/apps.js
CHANGED
|
@@ -206,8 +206,9 @@ export const createServerKeyResponse = z.object({
|
|
|
206
206
|
});
|
|
207
207
|
/**
|
|
208
208
|
* Every app starts with `observe` and `operate`; custom roles are allowed
|
|
209
|
-
* too. `builtin` marks the two starting roles — editable
|
|
210
|
-
* the flag only tells the console where they came
|
|
209
|
+
* too. `builtin` marks the two starting roles — editable, renamable and
|
|
210
|
+
* deletable like any other; the flag only tells the console where they came
|
|
211
|
+
* from.
|
|
211
212
|
*/
|
|
212
213
|
export const role = z.object({
|
|
213
214
|
id: z.uuid().meta({
|
|
@@ -220,7 +221,7 @@ export const role = z.object({
|
|
|
220
221
|
description: 'The role\'s name, shown wherever a user\'s access is chosen. The two roles every app starts with are named `observe` and `operate`.',
|
|
221
222
|
}),
|
|
222
223
|
builtin: z.boolean().meta({
|
|
223
|
-
description: '`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role\'s, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them.
|
|
224
|
+
description: '`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role\'s, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them. Built-in roles can be renamed and deleted like any other; the flag only records that the cloud seeded them.',
|
|
224
225
|
}),
|
|
225
226
|
});
|
|
226
227
|
/** What `GET /api/apps/:id/roles` answers: the app's roles, builtin and custom alike. */
|
|
@@ -229,6 +230,48 @@ export const roleListResponse = z.object({
|
|
|
229
230
|
description: 'The app\'s roles, built-in and custom alike, ordered by `created_at` and then by `name`. The tie-break is not cosmetic — the two built-in roles are inserted in one statement and share a creation time to the microsecond, so never read a role by position.',
|
|
230
231
|
}),
|
|
231
232
|
});
|
|
233
|
+
/**
|
|
234
|
+
* The body of `PATCH /api/apps/:id/roles/:roleId`: the role's new name.
|
|
235
|
+
*
|
|
236
|
+
* The same bounds as `role.name`, trimmed. Names are unique per app, compared
|
|
237
|
+
* exactly as stored after trimming; a clash answers `409 role_name_taken`.
|
|
238
|
+
*/
|
|
239
|
+
export const roleRenameRequest = z.object({
|
|
240
|
+
name: z.string().trim().min(1).max(60).meta({
|
|
241
|
+
description: 'The new name, trimmed, 1 to 60 characters. Unique per app: another role of this app with the same name answers `409 role_name_taken`. The role\'s users keep it under its new name.',
|
|
242
|
+
}),
|
|
243
|
+
}).strict();
|
|
244
|
+
/**
|
|
245
|
+
* The query of `DELETE /api/apps/:id/roles/:roleId`.
|
|
246
|
+
*
|
|
247
|
+
* `move_to` is what makes a held role deletable: every app user and pending
|
|
248
|
+
* invitation holding the role moves to it, and so does the app's default when
|
|
249
|
+
* it pointed at the role, in the same transaction as the delete. Without it a
|
|
250
|
+
* held role answers `409 role_in_use` with `roleInUseDetails`, so a client
|
|
251
|
+
* can ask where they should go instead of guessing.
|
|
252
|
+
*/
|
|
253
|
+
export const roleDeleteQuery = z.object({
|
|
254
|
+
move_to: z.uuid().optional().meta({
|
|
255
|
+
description: 'Another role of the same app that takes over the deleted role\'s app users, pending invitations and, when it applies, the app\'s default. The role itself or a role of another app answers `400 validation_error`.',
|
|
256
|
+
}),
|
|
257
|
+
}).strict();
|
|
258
|
+
/**
|
|
259
|
+
* `details` of `409 role_in_use`: what still holds the role.
|
|
260
|
+
*
|
|
261
|
+
* All three are reported, zeros included, so a client renders one sentence
|
|
262
|
+
* from one shape rather than inferring a missing key.
|
|
263
|
+
*/
|
|
264
|
+
export const roleInUseDetails = z.object({
|
|
265
|
+
users: z.number().int().nonnegative().meta({
|
|
266
|
+
description: 'App users whose role this is.',
|
|
267
|
+
}),
|
|
268
|
+
invitations: z.number().int().nonnegative().meta({
|
|
269
|
+
description: 'Pending invitations that would grant this role when accepted.',
|
|
270
|
+
}),
|
|
271
|
+
is_default: z.boolean().meta({
|
|
272
|
+
description: '`true` when this is the app\'s `default_role_id`; the default then moves with the users to `move_to`.',
|
|
273
|
+
}),
|
|
274
|
+
}).strict();
|
|
232
275
|
/**
|
|
233
276
|
* The rights matrix of one role: which slugs of which robot it may use, plus
|
|
234
277
|
* the capabilities roles also govern. `capabilities`' own doc comment
|
package/dist/audit.d.ts
CHANGED
|
@@ -72,6 +72,7 @@ export declare const auditQuery: z.ZodObject<{
|
|
|
72
72
|
action_prefix: z.ZodOptional<z.ZodString>;
|
|
73
73
|
actor_id: z.ZodOptional<z.ZodUUID>;
|
|
74
74
|
target_kind: z.ZodOptional<z.ZodString>;
|
|
75
|
+
target_id: z.ZodOptional<z.ZodString>;
|
|
75
76
|
from_ms: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodTransform<number, string | number>>, z.ZodNumber>>;
|
|
76
77
|
to_ms: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodTransform<number, string | number>>, z.ZodNumber>>;
|
|
77
78
|
}, z.core.$strict>;
|
package/dist/audit.js
CHANGED
|
@@ -155,6 +155,19 @@ export const auditQuery = z.object({
|
|
|
155
155
|
actor_id: z.uuid().optional(),
|
|
156
156
|
/** Only events about this kind of target, e.g. `robot`. */
|
|
157
157
|
target_kind: z.string().min(1).max(40).optional(),
|
|
158
|
+
/**
|
|
159
|
+
* Only events about this target — and, for a robot, also the events that
|
|
160
|
+
* name it in `details.robot_id`, so a robot's log includes what was
|
|
161
|
+
* started on it.
|
|
162
|
+
*
|
|
163
|
+
* **A string, not `z.uuid()`**, unlike `actor_id` above: `target.id` is a
|
|
164
|
+
* string in this contract and text in the cloud's table, so a uuid rule
|
|
165
|
+
* here would refuse ids the log can hold, and no value can fail a cast.
|
|
166
|
+
*/
|
|
167
|
+
target_id: z.string().min(1).max(200).optional().meta({
|
|
168
|
+
description: 'Events whose target is this id; for a robot also the events that name it in `details.robot_id` (`action.invoked`, ' +
|
|
169
|
+
'`service.called`, …), so a robot\'s events include what was started on it.',
|
|
170
|
+
}),
|
|
158
171
|
/**
|
|
159
172
|
* Absolute bounds in unix milliseconds, **half-open `[from, to)`** — the
|
|
160
173
|
* same rule the history shapes follow.
|
package/dist/errors.d.ts
CHANGED
|
@@ -68,5 +68,5 @@ export type CancelRejectedDetails = z.infer<typeof cancelRejectedDetails>;
|
|
|
68
68
|
* list is the shared vocabulary, not a closed set, so a new refusal never
|
|
69
69
|
* needs a contracts release before it can be reported honestly.
|
|
70
70
|
*/
|
|
71
|
-
export declare const ERROR_CODES: readonly ["not_found", "validation_error", "bad_request", "unknown_datapoint", "invalid_token", "protocol_mismatch", "bridge_too_old", "invalid_frame", "duplicate_slug", "reserved_slug", "unknown_slug", "unknown_field_path", "unknown_type", "unknown_topic", "invalid_rate", "invalid_range", "config_conflict", "no_data", "robot_offline", "bridge_timeout", "unauthorized", "forbidden", "invalid_credentials", "token_expired", "token_revoked", "email_taken", "identifier_taken", "weak_password", "account_blocked", "busy", "parameter_invalid", "cancel_rejected", "job_lost", "job_unknown_to_bridge", "action_server_lost", "action_failed", "goal_rejected", "goal_send_failed", "result_failed", "goal_uncontrollable", "bridge_disconnected", "config_changed", "publisher_busy", "unknown_command", "not_subscribable", "camera_offline", "no_snapshot_yet", "live_unavailable", "wrong_kind", "not_recorded", "not_aggregatable", "quota_exceeded", "credential_in_use", "goal_timeout", "robot_in_use", "robot_deletion_partial", "job_queue_full", "invalid_uuid", "rate_limited", "tier_required", "token_spent", "service_timeout", "asset_missing", "dynamic_registration_disabled", "client_limit_reached", "idp_unavailable", "mcp_disabled", "tool_not_available", "capability_required", "last_owner", "target_state_conflict", "signup_closed", "draft_not_a_document", "internal_error", "not_cancellable", "unsupported_media_type", "wrong_browser", "invalid_yaml", "unstorable_yaml", "registration_closed", "domain_not_allowed", "email_unverified", "origin_not_allowed", "template_invalid", "provider_disabled", "provider_misconfigured", "invalid_redirect_uri", "interaction_expired"];
|
|
71
|
+
export declare const ERROR_CODES: readonly ["not_found", "validation_error", "bad_request", "unknown_datapoint", "invalid_token", "protocol_mismatch", "bridge_too_old", "invalid_frame", "duplicate_slug", "reserved_slug", "unknown_slug", "unknown_field_path", "unknown_type", "unknown_topic", "invalid_rate", "invalid_range", "config_conflict", "no_data", "robot_offline", "bridge_timeout", "unauthorized", "forbidden", "invalid_credentials", "token_expired", "token_revoked", "email_taken", "identifier_taken", "weak_password", "account_blocked", "busy", "parameter_invalid", "cancel_rejected", "job_lost", "job_unknown_to_bridge", "action_server_lost", "action_failed", "goal_rejected", "goal_send_failed", "result_failed", "goal_uncontrollable", "bridge_disconnected", "config_changed", "publisher_busy", "unknown_command", "not_subscribable", "camera_offline", "no_snapshot_yet", "live_unavailable", "wrong_kind", "not_recorded", "not_aggregatable", "quota_exceeded", "credential_in_use", "goal_timeout", "robot_in_use", "robot_deletion_partial", "job_queue_full", "invalid_uuid", "rate_limited", "tier_required", "token_spent", "service_timeout", "asset_missing", "dynamic_registration_disabled", "client_limit_reached", "idp_unavailable", "mcp_disabled", "tool_not_available", "capability_required", "last_owner", "role_name_taken", "role_in_use", "last_role", "target_state_conflict", "signup_closed", "draft_not_a_document", "internal_error", "not_cancellable", "unsupported_media_type", "wrong_browser", "invalid_yaml", "unstorable_yaml", "registration_closed", "domain_not_allowed", "email_unverified", "origin_not_allowed", "template_invalid", "provider_disabled", "provider_misconfigured", "invalid_redirect_uri", "interaction_expired"];
|
|
72
72
|
export type ErrorCode = (typeof ERROR_CODES)[number];
|
package/dist/errors.js
CHANGED
|
@@ -562,6 +562,35 @@ export const ERROR_CODES = [
|
|
|
562
562
|
* caller could infer from a silence about existence.
|
|
563
563
|
*/
|
|
564
564
|
'last_owner',
|
|
565
|
+
// App roles.
|
|
566
|
+
/**
|
|
567
|
+
* **Another role of this app already has that name.** 409, on
|
|
568
|
+
* `PATCH /api/apps/:id/roles/:roleId`. Names are unique per app, compared
|
|
569
|
+
* exactly as stored after trimming.
|
|
570
|
+
*
|
|
571
|
+
* Not `validation_error`: the name is well-formed, and the remedy — pick
|
|
572
|
+
* another — depends on the app's other roles, not on the body.
|
|
573
|
+
*/
|
|
574
|
+
'role_name_taken',
|
|
575
|
+
/**
|
|
576
|
+
* **The role is still held**, by app users, by pending invitations, or as
|
|
577
|
+
* the app's default role. 409, on `DELETE /api/apps/:id/roles/:roleId`
|
|
578
|
+
* without `move_to`. `details` is `roleInUseDetails`:
|
|
579
|
+
* `{ users, invitations, is_default }`.
|
|
580
|
+
*
|
|
581
|
+
* Not `conflict`: the details tell the console what to offer — a role to
|
|
582
|
+
* move them to — and a generic code would leave it guessing.
|
|
583
|
+
*/
|
|
584
|
+
'role_in_use',
|
|
585
|
+
/**
|
|
586
|
+
* **An app must keep at least one role.** 409, on
|
|
587
|
+
* `DELETE /api/apps/:id/roles/:roleId` for the app's only role: every app
|
|
588
|
+
* user holds exactly one role, so an app without roles could hold no users.
|
|
589
|
+
*
|
|
590
|
+
* The same shape of refusal as `last_owner`: the caller may delete roles;
|
|
591
|
+
* the app's remaining state is what refuses this one.
|
|
592
|
+
*/
|
|
593
|
+
'last_role',
|
|
565
594
|
// OIDC federation.
|
|
566
595
|
/**
|
|
567
596
|
* **The target is in a state that refuses the operation** — not the caller's
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
/**
|
|
4
|
+
* **Feedback: a message from a developer to the people who build Fleetless.**
|
|
5
|
+
*
|
|
6
|
+
* The console's feedback modal sends it; the cloud stores it first and mails
|
|
7
|
+
* it afterwards, so a message is never lost to a mail failure. Who sent it and
|
|
8
|
+
* from which org is the caller's session, never the body: the body carries
|
|
9
|
+
* only what the developer chose to say and the page they said it on.
|
|
10
|
+
*/
|
|
11
|
+
/** The four kinds the console's modal offers. */
|
|
12
|
+
export declare const FEEDBACK_KINDS: readonly ["idea", "problem", "question", "other"];
|
|
13
|
+
export declare const feedbackKind: z.ZodEnum<{
|
|
14
|
+
idea: "idea";
|
|
15
|
+
problem: "problem";
|
|
16
|
+
question: "question";
|
|
17
|
+
other: "other";
|
|
18
|
+
}>;
|
|
19
|
+
/** Longest message the cloud stores; the console's counter starts at 4 500. */
|
|
20
|
+
export declare const FEEDBACK_MESSAGE_MAX = 5000;
|
|
21
|
+
export declare const feedbackRequest: z.ZodObject<{
|
|
22
|
+
kind: z.ZodEnum<{
|
|
23
|
+
idea: "idea";
|
|
24
|
+
problem: "problem";
|
|
25
|
+
question: "question";
|
|
26
|
+
other: "other";
|
|
27
|
+
}>;
|
|
28
|
+
message: z.ZodString;
|
|
29
|
+
page: z.ZodString;
|
|
30
|
+
}, z.core.$strict>;
|
|
31
|
+
export declare const feedbackResponse: z.ZodObject<{
|
|
32
|
+
id: z.ZodUUID;
|
|
33
|
+
mail: z.ZodEnum<{
|
|
34
|
+
sent: "sent";
|
|
35
|
+
not_requested: "not_requested";
|
|
36
|
+
not_configured: "not_configured";
|
|
37
|
+
failed: "failed";
|
|
38
|
+
}>;
|
|
39
|
+
}, z.core.$strict>;
|
|
40
|
+
export type FeedbackKind = z.infer<typeof feedbackKind>;
|
|
41
|
+
export type FeedbackRequest = z.infer<typeof feedbackRequest>;
|
|
42
|
+
export type FeedbackResponse = z.infer<typeof feedbackResponse>;
|
package/dist/feedback.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { mailStatus } from './identity.js';
|
|
4
|
+
/**
|
|
5
|
+
* **Feedback: a message from a developer to the people who build Fleetless.**
|
|
6
|
+
*
|
|
7
|
+
* The console's feedback modal sends it; the cloud stores it first and mails
|
|
8
|
+
* it afterwards, so a message is never lost to a mail failure. Who sent it and
|
|
9
|
+
* from which org is the caller's session, never the body: the body carries
|
|
10
|
+
* only what the developer chose to say and the page they said it on.
|
|
11
|
+
*/
|
|
12
|
+
/** The four kinds the console's modal offers. */
|
|
13
|
+
export const FEEDBACK_KINDS = ['idea', 'problem', 'question', 'other'];
|
|
14
|
+
export const feedbackKind = z.enum(FEEDBACK_KINDS);
|
|
15
|
+
/** Longest message the cloud stores; the console's counter starts at 4 500. */
|
|
16
|
+
export const FEEDBACK_MESSAGE_MAX = 5000;
|
|
17
|
+
export const feedbackRequest = z.object({
|
|
18
|
+
kind: feedbackKind.meta({
|
|
19
|
+
description: 'What the message is: an `idea`, a `problem`, a `question` or `other`. It only sorts the inbox; it changes nothing about how the message is handled.',
|
|
20
|
+
}),
|
|
21
|
+
message: z.string().trim().min(1).max(FEEDBACK_MESSAGE_MAX).meta({
|
|
22
|
+
description: 'What the developer wrote, trimmed. At most 5000 characters; a message that is only whitespace is refused.',
|
|
23
|
+
}),
|
|
24
|
+
page: z.string().startsWith('/').max(512).meta({
|
|
25
|
+
description: 'The console path the message was sent from, e.g. `/robots/:id/jobs` with its real id. A path, never a full URL, so no host and no query string reach the inbox by accident.',
|
|
26
|
+
}),
|
|
27
|
+
}).strict();
|
|
28
|
+
export const feedbackResponse = z.object({
|
|
29
|
+
id: z.uuid().meta({
|
|
30
|
+
description: 'The stored message. It exists whatever `mail` says.',
|
|
31
|
+
}),
|
|
32
|
+
mail: mailStatus.meta({
|
|
33
|
+
description: 'What happened to the notification mail: `sent`, `failed`, or `not_configured` when this cloud has no feedback address. The message is stored in every case, so a client shows success for all three.',
|
|
34
|
+
}),
|
|
35
|
+
}).strict();
|
package/dist/index.d.ts
CHANGED
|
@@ -31,8 +31,10 @@ export { clientAuth, authOk, authError, clientInvoke, clientCancel, clientPublis
|
|
|
31
31
|
export type { ClientAuth, AuthOk, AuthError, ClientInvoke, ClientCancel, ClientPublish, CommandResult, ErrorFrame, ClientSubscribe, ClientUnsubscribe, SubscribeError, DatapointEvent, ResourceHealthEvent, ResourceHealthCleared, LiveSessionEndReason, LiveSessionEvent, OrgEventKind, OrgEventSeverity, OrgEvent, OrgEventSubscribe, OrgEventUnsubscribe, OrgEventReplay, OrgEventDropped, } from './realtime.js';
|
|
32
32
|
export { password, org, patchOrgResponse, sessionTokens, refreshRequest, signUpRequest, signUpResponse, waitlistRequest, developerLoginRequest, USER_DISPLAY_NAME_MAX, orgAdminTier, fleetlessUser, fleetlessUserListResponse, createTeamInviteRequest, teamInvite, pendingTeamInvite, pendingTeamInviteListResponse, acceptTeamInviteRequest, patchFleetlessUserRequest, tierChangeRequest, mailStatus, tierRequiredDetails, passwordChangeRequest, passwordResetRequest, passwordResetConfirm, idpIssuer, authMeResponse, patchOrgRequest, patchAuthMeRequest, } from './identity.js';
|
|
33
33
|
export type { Org, PatchOrgResponse, SessionTokens, RefreshRequest, SignUpRequest, SignUpResponse, WaitlistRequest, DeveloperLoginRequest, OrgAdminTier, FleetlessUser, FleetlessUserListResponse, CreateTeamInviteRequest, TeamInvite, PendingTeamInvite, PendingTeamInviteListResponse, AcceptTeamInviteRequest, PatchFleetlessUserRequest, TierChangeRequest, MailStatus, TierRequiredDetails, PasswordChangeRequest, PasswordResetRequest, PasswordResetConfirm, IdpIssuer, AuthMeResponse, PatchOrgRequest, PatchAuthMeRequest, } from './identity.js';
|
|
34
|
-
export {
|
|
35
|
-
export type {
|
|
34
|
+
export { FEEDBACK_KINDS, FEEDBACK_MESSAGE_MAX, feedbackKind, feedbackRequest, feedbackResponse } from './feedback.js';
|
|
35
|
+
export type { FeedbackKind, FeedbackRequest, FeedbackResponse } from './feedback.js';
|
|
36
|
+
export { appIdentifier, app, appListResponse, appDeletionSummary, createAppRequest, updateAppRequest, serverKeyToken, serverKey, serverKeyListResponse, createServerKeyResponse, role, roleListResponse, rolePermissions, roleRenameRequest, roleDeleteQuery, roleInUseDetails, } from './apps.js';
|
|
37
|
+
export type { App, AppListResponse, AppDeletionSummary, CreateAppRequest, UpdateAppRequest, ServerKey, ServerKeyListResponse, CreateServerKeyResponse, Role, RoleListResponse, RolePermissions, RoleRenameRequest, RoleDeleteQuery, RoleInUseDetails, } from './apps.js';
|
|
36
38
|
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
39
|
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
40
|
export { clientRobotListItem, clientRobotListResponse } from './client-robots.js';
|
package/dist/index.js
CHANGED
|
@@ -38,7 +38,9 @@ USER_DISPLAY_NAME_MAX, orgAdminTier, fleetlessUser, fleetlessUserListResponse, c
|
|
|
38
38
|
mailStatus, tierRequiredDetails, passwordChangeRequest, passwordResetRequest, passwordResetConfirm, idpIssuer,
|
|
39
39
|
// auth/me, org and member patches.
|
|
40
40
|
authMeResponse, patchOrgRequest, patchAuthMeRequest, } from './identity.js';
|
|
41
|
-
|
|
41
|
+
// 2026-09-30 — the console's feedback modal.
|
|
42
|
+
export { FEEDBACK_KINDS, FEEDBACK_MESSAGE_MAX, feedbackKind, feedbackRequest, feedbackResponse } from './feedback.js';
|
|
43
|
+
export { appIdentifier, app, appListResponse, appDeletionSummary, createAppRequest, updateAppRequest, serverKeyToken, serverKey, serverKeyListResponse, createServerKeyResponse, role, roleListResponse, rolePermissions, roleRenameRequest, roleDeleteQuery, roleInUseDetails, } from './apps.js';
|
|
42
44
|
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
45
|
export { clientRobotListItem, clientRobotListResponse } from './client-robots.js';
|
|
44
46
|
// The per-app identity space.
|
package/dist/jobs.d.ts
CHANGED
|
@@ -241,6 +241,7 @@ export declare const jobActor: z.ZodObject<{
|
|
|
241
241
|
}>;
|
|
242
242
|
id: z.ZodUUID;
|
|
243
243
|
label: z.ZodString;
|
|
244
|
+
name: z.ZodNullable<z.ZodString>;
|
|
244
245
|
}, z.core.$strip>;
|
|
245
246
|
export type JobActor = z.infer<typeof jobActor>;
|
|
246
247
|
export declare const jobRunKind: z.ZodEnum<{
|
|
@@ -289,6 +290,7 @@ export declare const jobRun: z.ZodObject<{
|
|
|
289
290
|
}>;
|
|
290
291
|
id: z.ZodUUID;
|
|
291
292
|
label: z.ZodString;
|
|
293
|
+
name: z.ZodNullable<z.ZodString>;
|
|
292
294
|
}, z.core.$strip>;
|
|
293
295
|
seq: z.ZodNumber;
|
|
294
296
|
progress: z.ZodNullable<z.ZodNumber>;
|
|
@@ -351,6 +353,7 @@ export declare const jobRunListResponse: z.ZodObject<{
|
|
|
351
353
|
}>;
|
|
352
354
|
id: z.ZodUUID;
|
|
353
355
|
label: z.ZodString;
|
|
356
|
+
name: z.ZodNullable<z.ZodString>;
|
|
354
357
|
}, z.core.$strip>;
|
|
355
358
|
seq: z.ZodNumber;
|
|
356
359
|
progress: z.ZodNullable<z.ZodNumber>;
|
package/dist/jobs.js
CHANGED
|
@@ -226,6 +226,22 @@ export const jobActor = z.object({
|
|
|
226
226
|
label: z.string().min(1).max(200).meta({
|
|
227
227
|
description: 'A display name taken at invoke time — the email for a Fleetless user or an app user, the key\'s own name for a server key. Storing it rather than joining is the point: renaming a key afterwards does not rewrite history.',
|
|
228
228
|
}),
|
|
229
|
+
/**
|
|
230
|
+
* The person's display name, snapshotted beside `label` for the same
|
|
231
|
+
* reason. **Required and nullable**, so a consumer never has to tell
|
|
232
|
+
* "absent" from "null": every run a 5.3.0 cloud answers carries it, and
|
|
233
|
+
* `null` means there is no name to show — a server key, a person without
|
|
234
|
+
* one, or a run recorded before the field existed.
|
|
235
|
+
*
|
|
236
|
+
* `jobActor` stays a plain object, not `.strict()`: a consumer still on an
|
|
237
|
+
* older contracts version then parses a newer cloud's answer by stripping
|
|
238
|
+
* the key instead of refusing the whole run.
|
|
239
|
+
*/
|
|
240
|
+
name: z.string().min(1).max(200).nullable().meta({
|
|
241
|
+
description: 'The person\'s display name when the job started: the Fleetless user\'s `display_name` for a developer, the app user\'s ' +
|
|
242
|
+
'`display_name` for an app user. `null` for a server key, when the person had no name set, and for runs recorded before ' +
|
|
243
|
+
'contracts 5.3.0. Show `label` when it is null.',
|
|
244
|
+
}),
|
|
229
245
|
});
|
|
230
246
|
export const jobRunKind = z.enum(['action', 'service']);
|
|
231
247
|
/**
|
package/dist/routes.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
import { appListResponse, appDeletionSummary, createAppRequest, createServerKeyResponse, app as appSchema, role, roleListResponse, rolePermissions, serverKeyListResponse, updateAppRequest, } from './apps.js';
|
|
2
|
+
import { appListResponse, appDeletionSummary, createAppRequest, createServerKeyResponse, app as appSchema, role, roleListResponse, roleDeleteQuery, rolePermissions, roleRenameRequest, serverKeyListResponse, updateAppRequest, } from './apps.js';
|
|
3
3
|
import { alertListResponse, orgAlertsQuery, orgFiringAlertsResponse } from './alerts.js';
|
|
4
4
|
import { asset, assetListResponse, assetsClearResponse, assetSyncRequest, assetSyncResponse, assetSyncStatus, missingAssetQuery } from './assets.js';
|
|
5
5
|
import { auditListResponse, auditQuery } from './audit.js';
|
|
6
|
+
import { feedbackRequest, feedbackResponse } from './feedback.js';
|
|
6
7
|
import { CLIENT_OIDC_CALLBACK_PATH, clientAcceptInvitationRequest, clientIdentity, clientLoginRequest, clientLogoutRequest, clientMcpInteraction, clientMcpInteractionDecisionResponse, clientOidcCallbackQuery, clientOidcExchangeRequest, clientOidcStartQuery, clientPasswordResetConfirmRequest, clientPasswordResetRequest, clientProviderListQuery, clientProviderListResponse, clientRefreshRequest, clientRegisterRequest, clientResendVerificationRequest, clientVerifyEmailRequest, mcpConsentGrantListResponse, } from './client-auth.js';
|
|
7
8
|
import { clientRobotListResponse } from './client-robots.js';
|
|
8
9
|
import { appAuthConfig, appInvitation, appInvitationListResponse, appMailTemplate, appMailTemplateListResponse, appOidcProvider, appOidcProviderListResponse, appUser, appUserListResponse, createAppInvitationRequest, createAppOidcProviderRequest, createAppUserRequest, mailOutcome, mailTemplatePreviewRequest, mailTemplatePreviewResponse, patchAppOidcProviderRequest, patchAppUserRequest, putAppAuthMcpRequest, putAppAuthRegistrationRequest, putAppAuthUrlsRequest, putAppMailTemplateRequest, } from './app-users.js';
|
|
@@ -328,7 +329,7 @@ export const ROUTES = [
|
|
|
328
329
|
params: [{ name: 'id', description: 'The app\'s uuid, as returned by `POST /api/apps` or listed by `GET /api/apps`.' }],
|
|
329
330
|
query: null, request: null, response: role,
|
|
330
331
|
errors: [...DEVELOPER_GUARD, 'invalid_uuid', 'not_found', 'validation_error'], transport: 'http',
|
|
331
|
-
notes: 'The body is `{ "name": string }` — non-empty, trimmed, at most
|
|
332
|
+
notes: 'The body is `{ "name": string }` — non-empty, trimmed, at most 60 characters as on `role.name` — and is deliberately not a contract shape: contracts ' +
|
|
332
333
|
'define the `role` this answers with, not this one trivial request. **The answer is a bare `role`, not an envelope**, unlike the ' +
|
|
333
334
|
'listing beside it.',
|
|
334
335
|
},
|
|
@@ -381,6 +382,33 @@ export const ROUTES = [
|
|
|
381
382
|
'offered and consults nothing about any user\'s actual MCP entitlement. A robot the role grants nothing on still appears, with an empty ' +
|
|
382
383
|
'`exposures` — dropping it would read as "not attached", which is a different fact.',
|
|
383
384
|
},
|
|
385
|
+
{
|
|
386
|
+
method: 'PATCH', path: '/api/apps/:id/roles/:roleId', section: 'apps',
|
|
387
|
+
summary: 'Renames a role; its users keep it.',
|
|
388
|
+
audience: 'developer', auth: 'developer', rateLimited: false, ownerTier: false, status: 200,
|
|
389
|
+
params: [
|
|
390
|
+
{ name: 'id', description: 'The app\'s uuid, as returned by `POST /api/apps` or listed by `GET /api/apps`.' },
|
|
391
|
+
{ name: 'roleId', description: 'The role\'s uuid, from `GET /api/apps/:id/roles`; a role of another app answers `404`.' },
|
|
392
|
+
],
|
|
393
|
+
query: null, request: roleRenameRequest, response: role,
|
|
394
|
+
errors: [...DEVELOPER_GUARD, 'invalid_uuid', 'not_found', 'validation_error', 'role_name_taken'], transport: 'http',
|
|
395
|
+
notes: 'Names are unique per app, compared exactly as stored after trimming. Built-in roles can be renamed.',
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
method: 'DELETE', path: '/api/apps/:id/roles/:roleId', section: 'apps',
|
|
399
|
+
summary: 'Deletes a role, moving its users, pending invitations and default-role status to another role.',
|
|
400
|
+
audience: 'developer', auth: 'developer', rateLimited: false, ownerTier: false, status: 204,
|
|
401
|
+
params: [
|
|
402
|
+
{ name: 'id', description: 'The app\'s uuid, as returned by `POST /api/apps` or listed by `GET /api/apps`.' },
|
|
403
|
+
{ name: 'roleId', description: 'The role\'s uuid, from `GET /api/apps/:id/roles`; a role of another app answers `404`.' },
|
|
404
|
+
],
|
|
405
|
+
query: roleDeleteQuery, request: null, response: null,
|
|
406
|
+
errors: [...DEVELOPER_GUARD, 'invalid_uuid', 'not_found', 'validation_error', 'role_in_use', 'last_role'], transport: 'http',
|
|
407
|
+
notes: 'Without `move_to`, a role that app users or pending invitations hold, or that is the app\'s default, answers ' +
|
|
408
|
+
'`409 role_in_use` with `{ users, invitations, is_default }`. With `move_to` — another role of the same app, else ' +
|
|
409
|
+
'`400 validation_error` — one transaction moves `app_users.role_id`, pending invitations and `default_role_id`, then deletes ' +
|
|
410
|
+
'the role and its permissions. The app\'s only role answers `409 last_role`. Built-in roles can be deleted like any other.',
|
|
411
|
+
},
|
|
384
412
|
{
|
|
385
413
|
method: 'POST', path: '/api/apps/:id/server-keys', section: 'apps',
|
|
386
414
|
summary: 'Mints a server key for the app and returns the raw secret once.',
|
|
@@ -2417,6 +2445,16 @@ export const ROUTES = [
|
|
|
2417
2445
|
'platform will answer is owed a refusal, not a shorter answer they will mistake for the whole picture. `from_day <= to_day` is a ' +
|
|
2418
2446
|
'cross-field rule no JSON Schema can express and is enforced here. The window is echoed back.',
|
|
2419
2447
|
},
|
|
2448
|
+
{
|
|
2449
|
+
method: 'POST', path: '/api/feedback', section: 'org',
|
|
2450
|
+
summary: 'Sends a message from a developer to the people who build Fleetless.',
|
|
2451
|
+
audience: 'developer', auth: 'developer', rateLimited: true, ownerTier: false, status: 202,
|
|
2452
|
+
params: [], query: null, request: feedbackRequest, response: feedbackResponse,
|
|
2453
|
+
errors: [...DEVELOPER_GUARD, 'validation_error', 'rate_limited'], transport: 'http',
|
|
2454
|
+
notes: 'The message is stored before any mail is tried, so `202` means it is kept whatever `mail` says: `sent`, `failed`, or ' +
|
|
2455
|
+
'`not_configured` when this cloud has no feedback address. At most 10 messages per developer per hour; the 11th answers ' +
|
|
2456
|
+
'`429 rate_limited` with `retry_after_ms`. Replies come by mail, to the sender\'s address.',
|
|
2457
|
+
},
|
|
2420
2458
|
/* ------------------------------------------------- assets (robot upload) */
|
|
2421
2459
|
{
|
|
2422
2460
|
method: 'POST', path: '/api/bridge/assets', section: 'assets',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fleetless/contracts",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"description": "Fleetless wire contracts: the bridge-cloud protocol, the REST API schemas and the error codes, as zod schemas with generated JSON Schema and OpenAPI artifacts.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Dehne Robotik GmbH",
|