@fleetless/contracts 5.0.0-next.1 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -1
- package/artifacts/openapi.json +2 -2
- package/artifacts/routes.json +7 -2
- package/artifacts/schema/bridge-cancel-result.schema.json +86 -0
- package/artifacts/schema/bridge-hello.schema.json +0 -1
- package/artifacts/schema/bridge-job-status.schema.json +0 -1
- package/artifacts/schema/bridge-job-update.schema.json +0 -1
- package/artifacts/schema/cloud-cancel.schema.json +6 -0
- package/artifacts/schema-outgoing/bridge-cancel-result.schema.json +89 -0
- package/artifacts/schema-outgoing/bridge-hello.schema.json +0 -1
- package/artifacts/schema-outgoing/bridge-job-status.schema.json +0 -1
- package/artifacts/schema-outgoing/bridge-job-update.schema.json +0 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +8 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/jobs.d.ts +15 -0
- package/dist/jobs.js +8 -0
- package/dist/protocol.d.ts +79 -12
- package/dist/protocol.js +72 -11
- package/dist/routes.js +9 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,8 @@ version.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [5.0.0] — 2026-09-30
|
|
13
|
+
|
|
12
14
|
### Added
|
|
13
15
|
|
|
14
16
|
- **Protocol 5: `unknown` jobs, external goals, and a hard cut of protocols 3
|
|
@@ -29,13 +31,27 @@ version.
|
|
|
29
31
|
`hello_error` for a bridge below protocol 5, naming bridge `6.0.0`; and
|
|
30
32
|
`job_unknown_to_bridge`, the final `lost` reason when the bridge does not
|
|
31
33
|
know a job and nothing it cannot attribute runs on the job's action.
|
|
34
|
+
- **A cancel is answered with each goal's `CancelGoal` return code.**
|
|
35
|
+
`cancel` gains a required `request_id`; the bridge answers with the new
|
|
36
|
+
frame `cancel_result` (`bridgeCancelResult`, `bridgeCancelResultEntry`):
|
|
37
|
+
every goal the cancel reached, with its `job_id`, `goal_id` and
|
|
38
|
+
`return_code` (`CANCEL_RETURN_CODES`, `null` when the server did not
|
|
39
|
+
answer). A cancel naming a `job_id` the bridge does not hold cancels every
|
|
40
|
+
external goal on the action, never one of the bridge's own jobs. New error
|
|
41
|
+
code `cancel_rejected`: the server answered `ERROR_REJECTED`, and the
|
|
42
|
+
caller is refused, not told the cancel succeeded. `reportedJobState`, every
|
|
43
|
+
state but `unknown`, is what `job_update`, `job_status` entries and
|
|
44
|
+
`hello.active_jobs` accept, so a bridge claiming `unknown` fails validation.
|
|
45
|
+
`POST /api/robots/:id/jobs/:slug/cancel` lists the answers this adds:
|
|
46
|
+
`409 cancel_rejected`, `504 bridge_timeout`, and `502` with the bridge's
|
|
47
|
+
own code (`unknown_slug`, `action_server_lost`, `internal_error`).
|
|
32
48
|
|
|
33
49
|
### Removed
|
|
34
50
|
|
|
35
51
|
- **Protocols 2, 3 and 4, and an origin-less `job`; this is why the release
|
|
36
52
|
is a major.** A bridge below `6.0.0` is refused at `hello`, and a `job` or
|
|
37
53
|
`job_update` literal without `origin` (or a `job_update` without
|
|
38
|
-
`goal_id`) no longer parses
|
|
54
|
+
`goal_id`) no longer parses, nor does a `cancel` without `request_id`.
|
|
39
55
|
|
|
40
56
|
## [4.0.0] — 2026-09-29
|
|
41
57
|
|
package/artifacts/openapi.json
CHANGED
|
@@ -7003,7 +7003,7 @@
|
|
|
7003
7003
|
}
|
|
7004
7004
|
},
|
|
7005
7005
|
"default": {
|
|
7006
|
-
"description": "An error envelope. Codes this route is known to answer: `unauthorized`, `token_expired`, `token_revoked`, `forbidden`, `invalid_uuid`, `not_found`, `validation_error`, `not_cancellable`, `robot_offline`.",
|
|
7006
|
+
"description": "An error envelope. Codes this route is known to answer: `unauthorized`, `token_expired`, `token_revoked`, `forbidden`, `invalid_uuid`, `not_found`, `validation_error`, `not_cancellable`, `robot_offline`, `cancel_rejected`, `bridge_timeout`, `unknown_slug`, `action_server_lost`, `internal_error`.",
|
|
7007
7007
|
"content": {
|
|
7008
7008
|
"application/json": {
|
|
7009
7009
|
"schema": {
|
|
@@ -7013,7 +7013,7 @@
|
|
|
7013
7013
|
}
|
|
7014
7014
|
}
|
|
7015
7015
|
},
|
|
7016
|
-
"description": "The body is optional: a bodyless `POST` was every caller's shape before `job_id` existed, and absent or `job_id: null` both mean \"cancel whatever is running\". A named `job_id` that is **not** what is running cancels nothing and answers `404` — the caller named an id and thereby ruled the other one out. An `external` job is cancelled the same way, through its goal id. Cancelling an `unknown` job also cancels every external goal on its action, since one of them may be that job. A service is `422 not_cancellable`: a service call has no goal to cancel. Nothing running is a `200` with `job: null`.",
|
|
7016
|
+
"description": "The body is optional: a bodyless `POST` was every caller's shape before `job_id` existed, and absent or `job_id: null` both mean \"cancel whatever is running\". A named `job_id` that is **not** what is running cancels nothing and answers `404` — the caller named an id and thereby ruled the other one out. An `external` job is cancelled the same way, through its goal id. Cancelling an `unknown` job also cancels every external goal on its action, since one of them may be that job. A service is `422 not_cancellable`: a service call has no goal to cancel. Nothing running is a `200` with `job: null`. The answer waits for the bridge's `cancel_result`: a `200` means the action server accepted the cancel request, not that the goal has ended — the job's end arrives as its own update. Any goal answered `ERROR_REJECTED` makes it `409 cancel_rejected`, with every goal and its `return_code` in `details.goals`; no answer within `JOB_HEARTBEAT_TIMEOUT_MS` is `504 bridge_timeout`; a cancel the bridge could not send at all is `502` carrying the bridge's own code (`unknown_slug`, `action_server_lost`, `internal_error`).",
|
|
7017
7017
|
"requestBody": {
|
|
7018
7018
|
"required": false,
|
|
7019
7019
|
"content": {
|
package/artifacts/routes.json
CHANGED
|
@@ -4147,10 +4147,15 @@
|
|
|
4147
4147
|
"not_found",
|
|
4148
4148
|
"validation_error",
|
|
4149
4149
|
"not_cancellable",
|
|
4150
|
-
"robot_offline"
|
|
4150
|
+
"robot_offline",
|
|
4151
|
+
"cancel_rejected",
|
|
4152
|
+
"bridge_timeout",
|
|
4153
|
+
"unknown_slug",
|
|
4154
|
+
"action_server_lost",
|
|
4155
|
+
"internal_error"
|
|
4151
4156
|
],
|
|
4152
4157
|
"transport": "http",
|
|
4153
|
-
"notes": "The body is optional: a bodyless `POST` was every caller's shape before `job_id` existed, and absent or `job_id: null` both mean \"cancel whatever is running\". A named `job_id` that is **not** what is running cancels nothing and answers `404` — the caller named an id and thereby ruled the other one out. An `external` job is cancelled the same way, through its goal id. Cancelling an `unknown` job also cancels every external goal on its action, since one of them may be that job. A service is `422 not_cancellable`: a service call has no goal to cancel. Nothing running is a `200` with `job: null`."
|
|
4158
|
+
"notes": "The body is optional: a bodyless `POST` was every caller's shape before `job_id` existed, and absent or `job_id: null` both mean \"cancel whatever is running\". A named `job_id` that is **not** what is running cancels nothing and answers `404` — the caller named an id and thereby ruled the other one out. An `external` job is cancelled the same way, through its goal id. Cancelling an `unknown` job also cancels every external goal on its action, since one of them may be that job. A service is `422 not_cancellable`: a service call has no goal to cancel. Nothing running is a `200` with `job: null`. The answer waits for the bridge's `cancel_result`: a `200` means the action server accepted the cancel request, not that the goal has ended — the job's end arrives as its own update. Any goal answered `ERROR_REJECTED` makes it `409 cancel_rejected`, with every goal and its `return_code` in `details.goals`; no answer within `JOB_HEARTBEAT_TIMEOUT_MS` is `504 bridge_timeout`; a cancel the bridge could not send at all is `502` carrying the bridge's own code (`unknown_slug`, `action_server_lost`, `internal_error`)."
|
|
4154
4159
|
},
|
|
4155
4160
|
{
|
|
4156
4161
|
"method": "POST",
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"type": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"const": "cancel_result"
|
|
8
|
+
},
|
|
9
|
+
"request_id": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"minLength": 1,
|
|
12
|
+
"maxLength": 64
|
|
13
|
+
},
|
|
14
|
+
"slug": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"minLength": 2,
|
|
17
|
+
"maxLength": 63,
|
|
18
|
+
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$"
|
|
19
|
+
},
|
|
20
|
+
"goals": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"items": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"properties": {
|
|
25
|
+
"job_id": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"format": "uuid",
|
|
28
|
+
"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)$"
|
|
29
|
+
},
|
|
30
|
+
"goal_id": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"minLength": 1
|
|
33
|
+
},
|
|
34
|
+
"return_code": {
|
|
35
|
+
"anyOf": [
|
|
36
|
+
{
|
|
37
|
+
"type": "integer",
|
|
38
|
+
"minimum": 0,
|
|
39
|
+
"maximum": 3
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"type": "null"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"required": [
|
|
48
|
+
"job_id",
|
|
49
|
+
"goal_id",
|
|
50
|
+
"return_code"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"error": {
|
|
55
|
+
"anyOf": [
|
|
56
|
+
{
|
|
57
|
+
"type": "object",
|
|
58
|
+
"properties": {
|
|
59
|
+
"code": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"minLength": 1
|
|
62
|
+
},
|
|
63
|
+
"message": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"minLength": 1
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"required": [
|
|
69
|
+
"code",
|
|
70
|
+
"message"
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"type": "null"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"required": [
|
|
80
|
+
"type",
|
|
81
|
+
"request_id",
|
|
82
|
+
"slug",
|
|
83
|
+
"goals",
|
|
84
|
+
"error"
|
|
85
|
+
]
|
|
86
|
+
}
|
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
"type": "string",
|
|
7
7
|
"const": "cancel"
|
|
8
8
|
},
|
|
9
|
+
"request_id": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"minLength": 1,
|
|
12
|
+
"maxLength": 64
|
|
13
|
+
},
|
|
9
14
|
"slug": {
|
|
10
15
|
"type": "string",
|
|
11
16
|
"minLength": 2,
|
|
@@ -27,6 +32,7 @@
|
|
|
27
32
|
},
|
|
28
33
|
"required": [
|
|
29
34
|
"type",
|
|
35
|
+
"request_id",
|
|
30
36
|
"slug",
|
|
31
37
|
"job_id"
|
|
32
38
|
]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"type": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"const": "cancel_result"
|
|
8
|
+
},
|
|
9
|
+
"request_id": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"minLength": 1,
|
|
12
|
+
"maxLength": 64
|
|
13
|
+
},
|
|
14
|
+
"slug": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"minLength": 2,
|
|
17
|
+
"maxLength": 63,
|
|
18
|
+
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$"
|
|
19
|
+
},
|
|
20
|
+
"goals": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"items": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"properties": {
|
|
25
|
+
"job_id": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"format": "uuid",
|
|
28
|
+
"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)$"
|
|
29
|
+
},
|
|
30
|
+
"goal_id": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"minLength": 1
|
|
33
|
+
},
|
|
34
|
+
"return_code": {
|
|
35
|
+
"anyOf": [
|
|
36
|
+
{
|
|
37
|
+
"type": "integer",
|
|
38
|
+
"minimum": 0,
|
|
39
|
+
"maximum": 3
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"type": "null"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"required": [
|
|
48
|
+
"job_id",
|
|
49
|
+
"goal_id",
|
|
50
|
+
"return_code"
|
|
51
|
+
],
|
|
52
|
+
"additionalProperties": false
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"error": {
|
|
56
|
+
"anyOf": [
|
|
57
|
+
{
|
|
58
|
+
"type": "object",
|
|
59
|
+
"properties": {
|
|
60
|
+
"code": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"minLength": 1
|
|
63
|
+
},
|
|
64
|
+
"message": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"minLength": 1
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"required": [
|
|
70
|
+
"code",
|
|
71
|
+
"message"
|
|
72
|
+
],
|
|
73
|
+
"additionalProperties": false
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"type": "null"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"required": [
|
|
82
|
+
"type",
|
|
83
|
+
"request_id",
|
|
84
|
+
"slug",
|
|
85
|
+
"goals",
|
|
86
|
+
"error"
|
|
87
|
+
],
|
|
88
|
+
"additionalProperties": false
|
|
89
|
+
}
|
package/dist/errors.d.ts
CHANGED
|
@@ -49,5 +49,5 @@ export type ParameterInvalidDetails = z.infer<typeof parameterInvalidDetails>;
|
|
|
49
49
|
* list is the shared vocabulary, not a closed set, so a new refusal never
|
|
50
50
|
* needs a contracts release before it can be reported honestly.
|
|
51
51
|
*/
|
|
52
|
-
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", "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"];
|
|
52
|
+
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"];
|
|
53
53
|
export type ErrorCode = (typeof ERROR_CODES)[number];
|
package/dist/errors.js
CHANGED
|
@@ -156,6 +156,14 @@ export const ERROR_CODES = [
|
|
|
156
156
|
'busy',
|
|
157
157
|
/** A parameter failed its declared rule; details name the field and the rule. */
|
|
158
158
|
'parameter_invalid',
|
|
159
|
+
/**
|
|
160
|
+
* The action server refused a cancel request — `CancelGoal` answered
|
|
161
|
+
* `ERROR_REJECTED` (the bridge's `cancel_result`). The caller's cancel is
|
|
162
|
+
* refused with this code, never reported as success; `details.goals` carry
|
|
163
|
+
* each goal's `job_id`, `goal_id` and `return_code`. Whether the goal ends
|
|
164
|
+
* anyway is what its `job_update` says afterwards.
|
|
165
|
+
*/
|
|
166
|
+
'cancel_rejected',
|
|
159
167
|
/**
|
|
160
168
|
* The bridge's own statement, while connected, that it lost track of a job
|
|
161
169
|
* it still names — the vocabulary behind its `job_lost` frame. Distinct
|
package/dist/index.d.ts
CHANGED
|
@@ -3,10 +3,10 @@ export { SLUG_RULE, ROS_NAME_RULE, ROS_TYPE_NAME_RULE, FIELD_PATH_RULE, slug, ro
|
|
|
3
3
|
export type { ApplyErrorKind, ApplyError } from './common.js';
|
|
4
4
|
export { MCP_PROTOCOL_VERSION, MCP_ENDPOINT_PATH, mcpAppEndpointPath, MCP_APP_PATHS, MCP_TOOL_NAME_MAX, MCP_ASSET_LINK_PATH, MCP_ASSET_LINK_TTL_MS, mcpToolNamePattern, mcpToolKind, mcpExposure, mcpCapabilities, mcpRobotDatasheet, mcpRolePreviewResponse, } from './mcp.js';
|
|
5
5
|
export type { McpAppPaths, McpToolKind, McpExposure, McpCapabilities, McpRobotDatasheet, McpRolePreviewResponse, } from './mcp.js';
|
|
6
|
-
export { PROTOCOL_VERSION, PROTOCOL_VERSIONS, PROTOCOL_SUNSET_DAYS, LATEST_BRIDGE_VERSION, protocolStatus, minimumProtocolVersion, sunsetOf, statusFromTable, bridgeHello, cloudHelloOk, cloudHelloError, cloudPing, bridgePong, bridgeLinkMode, datapointFrame, bridgeState, cloudConfig, bridgeConfigApplied, cloudIntrospectRequest, bridgeIntrospect, cloudTypeRequest, bridgeTypeDefinitions, cloudInvoke, cloudCancel, cloudPublish, bridgeJobUpdate, bridgeJobLost, cloudJobQuery, bridgeJobStatusEntry, bridgeJobStatus, snapshotHeader, cloudCameraStart, cloudCameraStop, bridgeCameraState, SNAPSHOT_MAX_BYTES, CLOSE_ROBOT_DELETED, CLOSE_TOKEN_ROTATED, bridgeAssetsAvailable, cloudAssetRequest, bridgeAssetProgress, activeJob, DEFAULT_PATIENCE_MS, MAX_PATIENCE_MS, MIN_PATIENCE_MS, JOB_HEARTBEAT_INTERVAL_MS, JOB_HEARTBEAT_TIMEOUT_MS, JOB_OFFLINE_GRACE_MS, } from './protocol.js';
|
|
7
|
-
export type { ProtocolVersionEntry, ProtocolStatus, BridgeHello, CloudHelloOk, CloudHelloError, CloudPing, BridgePong, BridgeLinkMode, DatapointFrame, BridgeState, CloudConfig, BridgeConfigApplied, CloudIntrospectRequest, BridgeIntrospect, CloudTypeRequest, BridgeTypeDefinitions, CloudInvoke, CloudCancel, CloudPublish, BridgeJobUpdate, BridgeJobLost, CloudJobQuery, BridgeJobStatusEntry, BridgeJobStatus, SnapshotHeader, CloudCameraStart, CloudCameraStop, BridgeCameraState, ActiveJob, BridgeAssetsAvailable, CloudAssetRequest, BridgeAssetProgress, } from './protocol.js';
|
|
8
|
-
export { jobState, jobOrigin, job, jobEvent, busyDetails, publisherBusyDetails, jobQueueFullDetails } from './jobs.js';
|
|
9
|
-
export type { JobState, JobOrigin, Job, JobEvent, BusyDetails, PublisherBusyDetails, JobQueueFullDetails, } from './jobs.js';
|
|
6
|
+
export { PROTOCOL_VERSION, PROTOCOL_VERSIONS, PROTOCOL_SUNSET_DAYS, LATEST_BRIDGE_VERSION, protocolStatus, minimumProtocolVersion, sunsetOf, statusFromTable, bridgeHello, cloudHelloOk, cloudHelloError, cloudPing, bridgePong, bridgeLinkMode, datapointFrame, bridgeState, cloudConfig, bridgeConfigApplied, cloudIntrospectRequest, bridgeIntrospect, cloudTypeRequest, bridgeTypeDefinitions, cloudInvoke, cloudCancel, CANCEL_RETURN_CODES, cancelReturnCode, bridgeCancelResultEntry, bridgeCancelResult, cloudPublish, bridgeJobUpdate, bridgeJobLost, cloudJobQuery, bridgeJobStatusEntry, bridgeJobStatus, snapshotHeader, cloudCameraStart, cloudCameraStop, bridgeCameraState, SNAPSHOT_MAX_BYTES, CLOSE_ROBOT_DELETED, CLOSE_TOKEN_ROTATED, bridgeAssetsAvailable, cloudAssetRequest, bridgeAssetProgress, activeJob, DEFAULT_PATIENCE_MS, MAX_PATIENCE_MS, MIN_PATIENCE_MS, JOB_HEARTBEAT_INTERVAL_MS, JOB_HEARTBEAT_TIMEOUT_MS, JOB_OFFLINE_GRACE_MS, } from './protocol.js';
|
|
7
|
+
export type { ProtocolVersionEntry, ProtocolStatus, BridgeHello, CloudHelloOk, CloudHelloError, CloudPing, BridgePong, BridgeLinkMode, DatapointFrame, BridgeState, CloudConfig, BridgeConfigApplied, CloudIntrospectRequest, BridgeIntrospect, CloudTypeRequest, BridgeTypeDefinitions, CloudInvoke, CloudCancel, CancelReturnCode, BridgeCancelResultEntry, BridgeCancelResult, CloudPublish, BridgeJobUpdate, BridgeJobLost, CloudJobQuery, BridgeJobStatusEntry, BridgeJobStatus, SnapshotHeader, CloudCameraStart, CloudCameraStop, BridgeCameraState, ActiveJob, BridgeAssetsAvailable, CloudAssetRequest, BridgeAssetProgress, } from './protocol.js';
|
|
8
|
+
export { jobState, reportedJobState, jobOrigin, job, jobEvent, busyDetails, publisherBusyDetails, jobQueueFullDetails } from './jobs.js';
|
|
9
|
+
export type { JobState, ReportedJobState, JobOrigin, Job, JobEvent, BusyDetails, PublisherBusyDetails, JobQueueFullDetails, } from './jobs.js';
|
|
10
10
|
export { JOB_RUN_PAGE_MAX, JOB_RUN_RETENTION_DAYS, jobActor, jobRunKind, jobRun, jobRunQuery, jobRunListResponse, jobRunSummaryQuery, jobRunSummary, } from './jobs.js';
|
|
11
11
|
export type { JobActor, JobRunKind, JobRun, JobRunQuery, JobRunListResponse, JobRunSummary } from './jobs.js';
|
|
12
12
|
export { FLEETLESS_FORMAT_VERSION, RESERVED_SLUGS, parameterType, parameterSpec, parameterMap, serviceDescription, parameterDescription, messageTemplate, messageRef, messageBody, messageMap, PLACEHOLDER_RE, placeholderNames, actionConfig, serviceConfig, publisherConfig, cameraConfig, alertCondition, datapointAlert, rateThrottleHz, datapointNumeric, datapointRetention, datapointChart, datapointConfig, lowBandwidthSection, LOW_BANDWIDTH_DEFAULTS, robotConfigDoc, validationIssue, configState, snapshotIntervalSeconds, ALERT_SEVERITY_DEFAULT, ALERT_ENABLED_DEFAULT, RETENTION_INTERVAL_SECONDS_DEFAULT, CHART_WINDOW_MINUTES_DEFAULT, cameraSource, cameraCredentials, } from './config.js';
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
export { SLUG_RULE, ROS_NAME_RULE, ROS_TYPE_NAME_RULE, FIELD_PATH_RULE, slug, rosName, rosTypeName, fieldPath, wireSeqCursor, wireTimestampMs, applyErrorKind, applyError, } from './common.js';
|
|
3
3
|
export { MCP_PROTOCOL_VERSION, MCP_ENDPOINT_PATH, mcpAppEndpointPath, MCP_APP_PATHS, MCP_TOOL_NAME_MAX, MCP_ASSET_LINK_PATH, MCP_ASSET_LINK_TTL_MS, mcpToolNamePattern, mcpToolKind, mcpExposure, mcpCapabilities, mcpRobotDatasheet, mcpRolePreviewResponse, } from './mcp.js';
|
|
4
|
-
export { PROTOCOL_VERSION, PROTOCOL_VERSIONS, PROTOCOL_SUNSET_DAYS, LATEST_BRIDGE_VERSION, protocolStatus, minimumProtocolVersion, sunsetOf, statusFromTable, bridgeHello, cloudHelloOk, cloudHelloError, cloudPing, bridgePong, bridgeLinkMode, datapointFrame, bridgeState, cloudConfig, bridgeConfigApplied, cloudIntrospectRequest, bridgeIntrospect, cloudTypeRequest, bridgeTypeDefinitions, cloudInvoke, cloudCancel, cloudPublish, bridgeJobUpdate, bridgeJobLost, cloudJobQuery, bridgeJobStatusEntry, bridgeJobStatus, snapshotHeader, cloudCameraStart, cloudCameraStop, bridgeCameraState, SNAPSHOT_MAX_BYTES, CLOSE_ROBOT_DELETED, CLOSE_TOKEN_ROTATED,
|
|
4
|
+
export { PROTOCOL_VERSION, PROTOCOL_VERSIONS, PROTOCOL_SUNSET_DAYS, LATEST_BRIDGE_VERSION, protocolStatus, minimumProtocolVersion, sunsetOf, statusFromTable, bridgeHello, cloudHelloOk, cloudHelloError, cloudPing, bridgePong, bridgeLinkMode, datapointFrame, bridgeState, cloudConfig, bridgeConfigApplied, cloudIntrospectRequest, bridgeIntrospect, cloudTypeRequest, bridgeTypeDefinitions, cloudInvoke, cloudCancel, CANCEL_RETURN_CODES, cancelReturnCode, bridgeCancelResultEntry, bridgeCancelResult, cloudPublish, bridgeJobUpdate, bridgeJobLost, cloudJobQuery, bridgeJobStatusEntry, bridgeJobStatus, snapshotHeader, cloudCameraStart, cloudCameraStop, bridgeCameraState, SNAPSHOT_MAX_BYTES, CLOSE_ROBOT_DELETED, CLOSE_TOKEN_ROTATED,
|
|
5
5
|
// Assets.
|
|
6
6
|
bridgeAssetsAvailable, cloudAssetRequest, bridgeAssetProgress,
|
|
7
7
|
// Addressing.
|
|
8
8
|
activeJob, DEFAULT_PATIENCE_MS, MAX_PATIENCE_MS, MIN_PATIENCE_MS, JOB_HEARTBEAT_INTERVAL_MS, JOB_HEARTBEAT_TIMEOUT_MS, JOB_OFFLINE_GRACE_MS, } from './protocol.js';
|
|
9
|
-
export { jobState, jobOrigin, job, jobEvent, busyDetails, publisherBusyDetails, jobQueueFullDetails } from './jobs.js';
|
|
9
|
+
export { jobState, reportedJobState, jobOrigin, job, jobEvent, busyDetails, publisherBusyDetails, jobQueueFullDetails } from './jobs.js';
|
|
10
10
|
export { JOB_RUN_PAGE_MAX, JOB_RUN_RETENTION_DAYS, jobActor, jobRunKind, jobRun, jobRunQuery, jobRunListResponse, jobRunSummaryQuery, jobRunSummary, } from './jobs.js';
|
|
11
11
|
export { FLEETLESS_FORMAT_VERSION, RESERVED_SLUGS, parameterType, parameterSpec, parameterMap, serviceDescription, parameterDescription, messageTemplate, messageRef, messageBody, messageMap, PLACEHOLDER_RE, placeholderNames, actionConfig, serviceConfig, publisherConfig, cameraConfig, alertCondition, datapointAlert, rateThrottleHz, datapointNumeric, datapointRetention, datapointChart, datapointConfig, lowBandwidthSection, LOW_BANDWIDTH_DEFAULTS, robotConfigDoc, validationIssue, configState, snapshotIntervalSeconds,
|
|
12
12
|
// The defaults the format names, so nobody invents them twice.
|
package/dist/jobs.d.ts
CHANGED
|
@@ -31,6 +31,21 @@ export declare const jobState: z.ZodEnum<{
|
|
|
31
31
|
lost: "lost";
|
|
32
32
|
}>;
|
|
33
33
|
export type JobState = z.infer<typeof jobState>;
|
|
34
|
+
/**
|
|
35
|
+
* The states a bridge may state about a job: every `jobState` but `unknown`,
|
|
36
|
+
* which is the cloud's own word for not having heard. The bridge's
|
|
37
|
+
* `job_update`, `job_status` entries and `hello.active_jobs` use this, so a
|
|
38
|
+
* bridge claiming `unknown` fails validation instead of parking a job
|
|
39
|
+
* nobody will ever ask about.
|
|
40
|
+
*/
|
|
41
|
+
export declare const reportedJobState: z.ZodEnum<{
|
|
42
|
+
failed: "failed";
|
|
43
|
+
running: "running";
|
|
44
|
+
succeeded: "succeeded";
|
|
45
|
+
cancelled: "cancelled";
|
|
46
|
+
lost: "lost";
|
|
47
|
+
}>;
|
|
48
|
+
export type ReportedJobState = z.infer<typeof reportedJobState>;
|
|
34
49
|
/**
|
|
35
50
|
* Who started a job.
|
|
36
51
|
*
|
package/dist/jobs.js
CHANGED
|
@@ -24,6 +24,14 @@ import { slug, wireSeqCursor, wireTimestampMs } from './common.js';
|
|
|
24
24
|
* back and says it finished.
|
|
25
25
|
*/
|
|
26
26
|
export const jobState = z.enum(['running', 'unknown', 'succeeded', 'failed', 'cancelled', 'lost']);
|
|
27
|
+
/**
|
|
28
|
+
* The states a bridge may state about a job: every `jobState` but `unknown`,
|
|
29
|
+
* which is the cloud's own word for not having heard. The bridge's
|
|
30
|
+
* `job_update`, `job_status` entries and `hello.active_jobs` use this, so a
|
|
31
|
+
* bridge claiming `unknown` fails validation instead of parking a job
|
|
32
|
+
* nobody will ever ask about.
|
|
33
|
+
*/
|
|
34
|
+
export const reportedJobState = jobState.exclude(['unknown']);
|
|
27
35
|
/**
|
|
28
36
|
* Who started a job.
|
|
29
37
|
*
|
package/dist/protocol.d.ts
CHANGED
|
@@ -221,13 +221,18 @@ export { slug } from './common.js';
|
|
|
221
221
|
* has a terminal result still in hand reports it here and the cloud writes it
|
|
222
222
|
* down, instead of publishing `lost` over a job that in fact succeeded.
|
|
223
223
|
* Never `unknown`: that is the cloud's word for not having heard, and a
|
|
224
|
-
* bridge listing a job has, by definition, something to say about it
|
|
224
|
+
* bridge listing a job has, by definition, something to say about it — the
|
|
225
|
+
* schema refuses it.
|
|
226
|
+
*
|
|
227
|
+
* Only the bridge's own jobs, never an external goal: this entry carries no
|
|
228
|
+
* `origin`, and a cloud adopting an external goal from it would take it for
|
|
229
|
+
* its own. An active external goal is reported by the next `job_update`
|
|
230
|
+
* tick, with its origin.
|
|
225
231
|
*/
|
|
226
232
|
export declare const activeJob: z.ZodObject<{
|
|
227
233
|
job_id: z.ZodUUID;
|
|
228
234
|
slug: z.ZodString;
|
|
229
235
|
state: z.ZodEnum<{
|
|
230
|
-
unknown: "unknown";
|
|
231
236
|
failed: "failed";
|
|
232
237
|
running: "running";
|
|
233
238
|
succeeded: "succeeded";
|
|
@@ -246,7 +251,6 @@ export declare const bridgeHello: z.ZodObject<{
|
|
|
246
251
|
job_id: z.ZodUUID;
|
|
247
252
|
slug: z.ZodString;
|
|
248
253
|
state: z.ZodEnum<{
|
|
249
|
-
unknown: "unknown";
|
|
250
254
|
failed: "failed";
|
|
251
255
|
running: "running";
|
|
252
256
|
succeeded: "succeeded";
|
|
@@ -625,18 +629,79 @@ export type CloudInvoke = z.infer<typeof cloudInvoke>;
|
|
|
625
629
|
* wire had nowhere to put it.
|
|
626
630
|
*
|
|
627
631
|
* `null` keeps today's meaning and must be read as exactly that: *cancel
|
|
628
|
-
*
|
|
629
|
-
* hitting stop wants the robot stopped, not a lecture about job
|
|
630
|
-
* and it stays available for that.
|
|
631
|
-
*
|
|
632
|
-
* the
|
|
632
|
+
* every goal active on this slug's action*. It is a real request — an
|
|
633
|
+
* operator hitting stop wants the robot stopped, not a lecture about job
|
|
634
|
+
* identity — and it stays available for that.
|
|
635
|
+
*
|
|
636
|
+
* A `job_id` the bridge holds (its own job, or an external goal's derived
|
|
637
|
+
* id) cancels that goal alone. A `job_id` it does not hold is how the cloud
|
|
638
|
+
* cancels an `unknown` job: the bridge cancels every **external** goal
|
|
639
|
+
* active on the slug's action, since one of them may be that job, and never
|
|
640
|
+
* one of its own jobs, which the caller did not name.
|
|
641
|
+
*
|
|
642
|
+
* `request_id` correlates the bridge's `cancel_result`, which carries each
|
|
643
|
+
* goal's `CancelGoal` return code; the cloud answers its caller only from
|
|
644
|
+
* that.
|
|
633
645
|
*/
|
|
634
646
|
export declare const cloudCancel: z.ZodObject<{
|
|
635
647
|
type: z.ZodLiteral<"cancel">;
|
|
648
|
+
request_id: z.ZodString;
|
|
636
649
|
slug: z.ZodString;
|
|
637
650
|
job_id: z.ZodNullable<z.ZodUUID>;
|
|
638
651
|
}, z.core.$strip>;
|
|
639
652
|
export type CloudCancel = z.infer<typeof cloudCancel>;
|
|
653
|
+
/**
|
|
654
|
+
* The ROS 2 `action_msgs/srv/CancelGoal` return codes: `0` `ERROR_NONE` (the
|
|
655
|
+
* server accepted the cancel request), `1` `ERROR_REJECTED` (it refused),
|
|
656
|
+
* `2` `ERROR_UNKNOWN_GOAL_ID`, `3` `ERROR_GOAL_TERMINATED` (the goal had
|
|
657
|
+
* already ended). An accepted request is not an ended goal: whether the goal
|
|
658
|
+
* ends, and how, is what the action's status reports afterwards, and reaches
|
|
659
|
+
* the cloud as the goal's `job_update`.
|
|
660
|
+
*/
|
|
661
|
+
export declare const CANCEL_RETURN_CODES: {
|
|
662
|
+
readonly none: 0;
|
|
663
|
+
readonly rejected: 1;
|
|
664
|
+
readonly unknown_goal_id: 2;
|
|
665
|
+
readonly goal_terminated: 3;
|
|
666
|
+
};
|
|
667
|
+
export declare const cancelReturnCode: z.ZodNumber;
|
|
668
|
+
export type CancelReturnCode = z.infer<typeof cancelReturnCode>;
|
|
669
|
+
/** One goal a `cancel` reached, and what its action server answered. */
|
|
670
|
+
export declare const bridgeCancelResultEntry: z.ZodObject<{
|
|
671
|
+
job_id: z.ZodUUID;
|
|
672
|
+
goal_id: z.ZodString;
|
|
673
|
+
return_code: z.ZodNullable<z.ZodNumber>;
|
|
674
|
+
}, z.core.$strip>;
|
|
675
|
+
export type BridgeCancelResultEntry = z.infer<typeof bridgeCancelResultEntry>;
|
|
676
|
+
/**
|
|
677
|
+
* The bridge's answer to a `cancel`, once every goal it sent a cancel request
|
|
678
|
+
* for has answered — or `error` when it could not ask. `goals` is empty when
|
|
679
|
+
* nothing matched (a `job_id` the bridge does not hold, with no external goal
|
|
680
|
+
* on the action; or nothing active on the slug): the cancel reached nothing,
|
|
681
|
+
* which is an answer, not a failure.
|
|
682
|
+
*
|
|
683
|
+
* A goal whose server did not answer the cancel request within the bridge's
|
|
684
|
+
* own bound is listed with `return_code: null`, not left out.
|
|
685
|
+
*
|
|
686
|
+
* The cloud reports `return_code` `1` (`ERROR_REJECTED`) to the caller as a
|
|
687
|
+
* refusal (`cancel_rejected`), never as success, and writes the audit event
|
|
688
|
+
* of a cancel of an external goal from this frame, return code included.
|
|
689
|
+
*/
|
|
690
|
+
export declare const bridgeCancelResult: z.ZodObject<{
|
|
691
|
+
type: z.ZodLiteral<"cancel_result">;
|
|
692
|
+
request_id: z.ZodString;
|
|
693
|
+
slug: z.ZodString;
|
|
694
|
+
goals: z.ZodArray<z.ZodObject<{
|
|
695
|
+
job_id: z.ZodUUID;
|
|
696
|
+
goal_id: z.ZodString;
|
|
697
|
+
return_code: z.ZodNullable<z.ZodNumber>;
|
|
698
|
+
}, z.core.$strip>>;
|
|
699
|
+
error: z.ZodNullable<z.ZodObject<{
|
|
700
|
+
code: z.ZodString;
|
|
701
|
+
message: z.ZodString;
|
|
702
|
+
}, z.core.$strip>>;
|
|
703
|
+
}, z.core.$strip>;
|
|
704
|
+
export type BridgeCancelResult = z.infer<typeof bridgeCancelResult>;
|
|
640
705
|
export declare const cloudPublish: z.ZodObject<{
|
|
641
706
|
type: z.ZodLiteral<"publish">;
|
|
642
707
|
slug: z.ZodString;
|
|
@@ -666,7 +731,6 @@ export declare const bridgeJobUpdate: z.ZodObject<{
|
|
|
666
731
|
job_id: z.ZodUUID;
|
|
667
732
|
slug: z.ZodString;
|
|
668
733
|
state: z.ZodEnum<{
|
|
669
|
-
unknown: "unknown";
|
|
670
734
|
failed: "failed";
|
|
671
735
|
running: "running";
|
|
672
736
|
succeeded: "succeeded";
|
|
@@ -729,11 +793,15 @@ export declare const cloudJobQuery: z.ZodObject<{
|
|
|
729
793
|
job_ids: z.ZodArray<z.ZodUUID>;
|
|
730
794
|
}, z.core.$strip>;
|
|
731
795
|
export type CloudJobQuery = z.infer<typeof cloudJobQuery>;
|
|
732
|
-
/**
|
|
796
|
+
/**
|
|
797
|
+
* One job the bridge recognises, in a `job_status` answer: its state, feedback,
|
|
798
|
+
* progress, result and error, as a `job_update` would carry them. No `slug`,
|
|
799
|
+
* `origin`, `goal_id` or `timestamp_ms` — the cloud asked about a job it
|
|
800
|
+
* already holds, so it knows the first three, and the answer is current.
|
|
801
|
+
*/
|
|
733
802
|
export declare const bridgeJobStatusEntry: z.ZodObject<{
|
|
734
803
|
job_id: z.ZodUUID;
|
|
735
804
|
state: z.ZodEnum<{
|
|
736
|
-
unknown: "unknown";
|
|
737
805
|
failed: "failed";
|
|
738
806
|
running: "running";
|
|
739
807
|
succeeded: "succeeded";
|
|
@@ -767,7 +835,6 @@ export declare const bridgeJobStatus: z.ZodObject<{
|
|
|
767
835
|
jobs: z.ZodArray<z.ZodObject<{
|
|
768
836
|
job_id: z.ZodUUID;
|
|
769
837
|
state: z.ZodEnum<{
|
|
770
|
-
unknown: "unknown";
|
|
771
838
|
failed: "failed";
|
|
772
839
|
running: "running";
|
|
773
840
|
succeeded: "succeeded";
|
package/dist/protocol.js
CHANGED
|
@@ -4,7 +4,7 @@ import { assetFailure } from './assets.js';
|
|
|
4
4
|
import { applyError, slug } from './common.js';
|
|
5
5
|
import { robotConfigDoc } from './config.js';
|
|
6
6
|
import { rosGraph, typeDefinition } from './introspection.js';
|
|
7
|
-
import { jobOrigin,
|
|
7
|
+
import { jobOrigin, reportedJobState } from './jobs.js';
|
|
8
8
|
import { rosTypeName } from './common.js';
|
|
9
9
|
/**
|
|
10
10
|
* Bridge <-> cloud protocol, version 5.
|
|
@@ -238,12 +238,18 @@ export { slug } from './common.js';
|
|
|
238
238
|
* has a terminal result still in hand reports it here and the cloud writes it
|
|
239
239
|
* down, instead of publishing `lost` over a job that in fact succeeded.
|
|
240
240
|
* Never `unknown`: that is the cloud's word for not having heard, and a
|
|
241
|
-
* bridge listing a job has, by definition, something to say about it
|
|
241
|
+
* bridge listing a job has, by definition, something to say about it — the
|
|
242
|
+
* schema refuses it.
|
|
243
|
+
*
|
|
244
|
+
* Only the bridge's own jobs, never an external goal: this entry carries no
|
|
245
|
+
* `origin`, and a cloud adopting an external goal from it would take it for
|
|
246
|
+
* its own. An active external goal is reported by the next `job_update`
|
|
247
|
+
* tick, with its origin.
|
|
242
248
|
*/
|
|
243
249
|
export const activeJob = z.object({
|
|
244
250
|
job_id: z.uuid(),
|
|
245
251
|
slug,
|
|
246
|
-
state:
|
|
252
|
+
state: reportedJobState,
|
|
247
253
|
});
|
|
248
254
|
/** First frame a bridge sends after the socket opens. */
|
|
249
255
|
export const bridgeHello = z.object({
|
|
@@ -462,17 +468,67 @@ export const cloudInvoke = z.object({
|
|
|
462
468
|
* wire had nowhere to put it.
|
|
463
469
|
*
|
|
464
470
|
* `null` keeps today's meaning and must be read as exactly that: *cancel
|
|
465
|
-
*
|
|
466
|
-
* hitting stop wants the robot stopped, not a lecture about job
|
|
467
|
-
* and it stays available for that.
|
|
468
|
-
*
|
|
469
|
-
* the
|
|
471
|
+
* every goal active on this slug's action*. It is a real request — an
|
|
472
|
+
* operator hitting stop wants the robot stopped, not a lecture about job
|
|
473
|
+
* identity — and it stays available for that.
|
|
474
|
+
*
|
|
475
|
+
* A `job_id` the bridge holds (its own job, or an external goal's derived
|
|
476
|
+
* id) cancels that goal alone. A `job_id` it does not hold is how the cloud
|
|
477
|
+
* cancels an `unknown` job: the bridge cancels every **external** goal
|
|
478
|
+
* active on the slug's action, since one of them may be that job, and never
|
|
479
|
+
* one of its own jobs, which the caller did not name.
|
|
480
|
+
*
|
|
481
|
+
* `request_id` correlates the bridge's `cancel_result`, which carries each
|
|
482
|
+
* goal's `CancelGoal` return code; the cloud answers its caller only from
|
|
483
|
+
* that.
|
|
470
484
|
*/
|
|
471
485
|
export const cloudCancel = z.object({
|
|
472
486
|
type: z.literal('cancel'),
|
|
487
|
+
request_id: z.string().min(1).max(64),
|
|
473
488
|
slug,
|
|
474
489
|
job_id: z.uuid().nullable(),
|
|
475
490
|
});
|
|
491
|
+
/**
|
|
492
|
+
* The ROS 2 `action_msgs/srv/CancelGoal` return codes: `0` `ERROR_NONE` (the
|
|
493
|
+
* server accepted the cancel request), `1` `ERROR_REJECTED` (it refused),
|
|
494
|
+
* `2` `ERROR_UNKNOWN_GOAL_ID`, `3` `ERROR_GOAL_TERMINATED` (the goal had
|
|
495
|
+
* already ended). An accepted request is not an ended goal: whether the goal
|
|
496
|
+
* ends, and how, is what the action's status reports afterwards, and reaches
|
|
497
|
+
* the cloud as the goal's `job_update`.
|
|
498
|
+
*/
|
|
499
|
+
export const CANCEL_RETURN_CODES = { none: 0, rejected: 1, unknown_goal_id: 2, goal_terminated: 3 };
|
|
500
|
+
export const cancelReturnCode = z.number().int().min(0).max(3);
|
|
501
|
+
/** One goal a `cancel` reached, and what its action server answered. */
|
|
502
|
+
export const bridgeCancelResultEntry = z.object({
|
|
503
|
+
/** The job the goal belongs to — the bridge's own, or an external goal's derived id. */
|
|
504
|
+
job_id: z.uuid(),
|
|
505
|
+
/** The ROS 2 goal id the cancel was sent for. */
|
|
506
|
+
goal_id: z.string().min(1),
|
|
507
|
+
/** `null` when the action server did not answer the cancel request within the bridge's own bound. */
|
|
508
|
+
return_code: cancelReturnCode.nullable(),
|
|
509
|
+
});
|
|
510
|
+
/**
|
|
511
|
+
* The bridge's answer to a `cancel`, once every goal it sent a cancel request
|
|
512
|
+
* for has answered — or `error` when it could not ask. `goals` is empty when
|
|
513
|
+
* nothing matched (a `job_id` the bridge does not hold, with no external goal
|
|
514
|
+
* on the action; or nothing active on the slug): the cancel reached nothing,
|
|
515
|
+
* which is an answer, not a failure.
|
|
516
|
+
*
|
|
517
|
+
* A goal whose server did not answer the cancel request within the bridge's
|
|
518
|
+
* own bound is listed with `return_code: null`, not left out.
|
|
519
|
+
*
|
|
520
|
+
* The cloud reports `return_code` `1` (`ERROR_REJECTED`) to the caller as a
|
|
521
|
+
* refusal (`cancel_rejected`), never as success, and writes the audit event
|
|
522
|
+
* of a cancel of an external goal from this frame, return code included.
|
|
523
|
+
*/
|
|
524
|
+
export const bridgeCancelResult = z.object({
|
|
525
|
+
type: z.literal('cancel_result'),
|
|
526
|
+
request_id: z.string().min(1).max(64),
|
|
527
|
+
slug,
|
|
528
|
+
goals: z.array(bridgeCancelResultEntry),
|
|
529
|
+
/** Set when the bridge could not send the cancel at all (no such slug, a service, the server gone). */
|
|
530
|
+
error: z.object({ code: z.string().min(1), message: z.string().min(1) }).nullable(),
|
|
531
|
+
});
|
|
476
532
|
export const cloudPublish = z.object({
|
|
477
533
|
type: z.literal('publish'),
|
|
478
534
|
slug,
|
|
@@ -511,7 +567,7 @@ export const bridgeJobUpdate = z.object({
|
|
|
511
567
|
job_id: z.uuid(),
|
|
512
568
|
slug,
|
|
513
569
|
/** Never `unknown`: that is the cloud's word for not having heard. */
|
|
514
|
-
state:
|
|
570
|
+
state: reportedJobState,
|
|
515
571
|
origin: jobOrigin,
|
|
516
572
|
/** The ROS 2 goal id; `null` for a service job, which has no goal. */
|
|
517
573
|
goal_id: z.string().min(1).nullable(),
|
|
@@ -559,11 +615,16 @@ export const cloudJobQuery = z.object({
|
|
|
559
615
|
request_id: z.string().min(1).max(64),
|
|
560
616
|
job_ids: z.array(z.uuid()).min(1),
|
|
561
617
|
});
|
|
562
|
-
/**
|
|
618
|
+
/**
|
|
619
|
+
* One job the bridge recognises, in a `job_status` answer: its state, feedback,
|
|
620
|
+
* progress, result and error, as a `job_update` would carry them. No `slug`,
|
|
621
|
+
* `origin`, `goal_id` or `timestamp_ms` — the cloud asked about a job it
|
|
622
|
+
* already holds, so it knows the first three, and the answer is current.
|
|
623
|
+
*/
|
|
563
624
|
export const bridgeJobStatusEntry = z.object({
|
|
564
625
|
job_id: z.uuid(),
|
|
565
626
|
/** Never `unknown` — the bridge only ever states a definite fact about a job it recognises. */
|
|
566
|
-
state:
|
|
627
|
+
state: reportedJobState,
|
|
567
628
|
feedback: z.unknown().nullable(),
|
|
568
629
|
progress: z.number().min(0).max(1).nullable(),
|
|
569
630
|
result: z.unknown().nullable(),
|
package/dist/routes.js
CHANGED
|
@@ -2112,12 +2112,19 @@ export const ROUTES = [
|
|
|
2112
2112
|
{ name: 'slug', description: 'The action slug from the published configuration; a service slug is refused.' },
|
|
2113
2113
|
],
|
|
2114
2114
|
query: null, request: cancelRequest, requestOptional: true, response: jobResponse,
|
|
2115
|
-
errors: [
|
|
2115
|
+
errors: [
|
|
2116
|
+
...CLIENT_GUARD, 'invalid_uuid', 'not_found', 'validation_error', 'not_cancellable', 'robot_offline',
|
|
2117
|
+
'cancel_rejected', 'bridge_timeout', 'unknown_slug', 'action_server_lost', 'internal_error',
|
|
2118
|
+
], transport: 'http',
|
|
2116
2119
|
notes: 'The body is optional: a bodyless `POST` was every caller\'s shape before `job_id` existed, and absent or `job_id: null` both mean ' +
|
|
2117
2120
|
'"cancel whatever is running". A named `job_id` that is **not** what is running cancels nothing and answers `404` — the caller named an ' +
|
|
2118
2121
|
'id and thereby ruled the other one out. An `external` job is cancelled the same way, through its goal id. Cancelling an `unknown` job ' +
|
|
2119
2122
|
'also cancels every external goal on its action, since one of them may be that job. A service is `422 not_cancellable`: a service call ' +
|
|
2120
|
-
'has no goal to cancel. Nothing running is a `200` with `job: null`.'
|
|
2123
|
+
'has no goal to cancel. Nothing running is a `200` with `job: null`. The answer waits for the bridge\'s `cancel_result`: a `200` means ' +
|
|
2124
|
+
'the action server accepted the cancel request, not that the goal has ended — the job\'s end arrives as its own update. Any goal ' +
|
|
2125
|
+
'answered `ERROR_REJECTED` makes it `409 cancel_rejected`, with every goal and its `return_code` in `details.goals`; no answer within ' +
|
|
2126
|
+
'`JOB_HEARTBEAT_TIMEOUT_MS` is `504 bridge_timeout`; a cancel the bridge could not send at all is `502` carrying the bridge\'s own ' +
|
|
2127
|
+
'code (`unknown_slug`, `action_server_lost`, `internal_error`).',
|
|
2121
2128
|
},
|
|
2122
2129
|
{
|
|
2123
2130
|
method: 'POST', path: '/api/robots/:id/publishers/:slug', section: 'commands',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fleetless/contracts",
|
|
3
|
-
"version": "5.0.0
|
|
3
|
+
"version": "5.0.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",
|