@fleetless/contracts 4.0.0 → 5.0.0-next.2
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 +39 -0
- package/artifacts/constants.json +4 -14
- package/artifacts/openapi.json +46 -13
- package/artifacts/routes.json +2 -2
- package/artifacts/schema/bridge-cancel-result.schema.json +86 -0
- package/artifacts/schema/bridge-job-status.schema.json +113 -0
- package/artifacts/schema/bridge-job-update.schema.json +20 -0
- package/artifacts/schema/busy-details.schema.json +12 -2
- package/artifacts/schema/cloud-cancel.schema.json +6 -0
- package/artifacts/schema/cloud-job-query.schema.json +29 -0
- package/artifacts/schema/command-result.schema.json +12 -2
- package/artifacts/schema/invoke-or-service-response.schema.json +12 -2
- package/artifacts/schema/invoke-response.schema.json +12 -2
- package/artifacts/schema/job-event.schema.json +12 -2
- package/artifacts/schema/job-response.schema.json +12 -2
- package/artifacts/schema/job-run-list-response.schema.json +4 -3
- package/artifacts/schema/job-run-query.schema.json +2 -1
- package/artifacts/schema/job-run.schema.json +4 -3
- package/artifacts/schema/job-state.schema.json +1 -0
- package/artifacts/schema/job.schema.json +12 -2
- package/artifacts/schema/robot-jobs-response.schema.json +12 -2
- package/artifacts/schema-outgoing/bridge-cancel-result.schema.json +89 -0
- package/artifacts/schema-outgoing/bridge-job-status.schema.json +116 -0
- package/artifacts/schema-outgoing/bridge-job-update.schema.json +20 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +46 -7
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/jobs.d.ts +70 -6
- package/dist/jobs.js +51 -14
- package/dist/protocol.d.ts +233 -46
- package/dist/protocol.js +207 -62
- package/dist/realtime.d.ts +5 -0
- package/dist/rest.d.ts +20 -0
- package/dist/routes.js +6 -3
- package/package.json +1 -1
package/dist/errors.js
CHANGED
|
@@ -51,6 +51,16 @@ export const ERROR_CODES = [
|
|
|
51
51
|
'unknown_datapoint',
|
|
52
52
|
'invalid_token',
|
|
53
53
|
'protocol_mismatch',
|
|
54
|
+
/**
|
|
55
|
+
* The cloud's `hello_error` for a bridge whose `protocol_version` is below
|
|
56
|
+
* every version it serves — since protocol 5, anything below 5. Its message
|
|
57
|
+
* names the bridge release to install (`LATEST_BRIDGE_VERSION`, 6.0.0 at
|
|
58
|
+
* the cut), because "too old" alone leaves an operator guessing how far to
|
|
59
|
+
* upgrade. Distinct from `protocol_mismatch`, the generic refusal for a
|
|
60
|
+
* version the cloud cannot place: this one says which way the gap runs and
|
|
61
|
+
* what closes it. Reaches the robot's detail view as `last_hello_error`.
|
|
62
|
+
*/
|
|
63
|
+
'bridge_too_old',
|
|
54
64
|
'invalid_frame',
|
|
55
65
|
// Configuration.
|
|
56
66
|
'duplicate_slug',
|
|
@@ -74,9 +84,10 @@ export const ERROR_CODES = [
|
|
|
74
84
|
/**
|
|
75
85
|
* The cloud has heard nothing — heartbeat or real progress — from a
|
|
76
86
|
* running job for longer than it tolerates while the bridge is connected:
|
|
77
|
-
* `patience_ms`
|
|
78
|
-
*
|
|
79
|
-
*
|
|
87
|
+
* `patience_ms` until the first report, `JOB_HEARTBEAT_TIMEOUT_MS` after
|
|
88
|
+
* it. `job.error.code` on `unknown`, not `lost`: silence is the cloud's
|
|
89
|
+
* guess, so it asks the bridge with `job_query` and the answer resolves the
|
|
90
|
+
* job — running again clears this code, an end replaces it.
|
|
80
91
|
*/
|
|
81
92
|
'bridge_timeout',
|
|
82
93
|
// Identity and rights. `forbidden` is deliberately the answer both
|
|
@@ -145,8 +156,34 @@ export const ERROR_CODES = [
|
|
|
145
156
|
'busy',
|
|
146
157
|
/** A parameter failed its declared rule; details name the field and the rule. */
|
|
147
158
|
'parameter_invalid',
|
|
148
|
-
/**
|
|
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',
|
|
167
|
+
/**
|
|
168
|
+
* The bridge's own statement, while connected, that it lost track of a job
|
|
169
|
+
* it still names — the vocabulary behind its `job_lost` frame. Distinct
|
|
170
|
+
* from `job_unknown_to_bridge`, the cloud's conclusion about a job the
|
|
171
|
+
* bridge does not name at all.
|
|
172
|
+
*/
|
|
149
173
|
'job_lost',
|
|
174
|
+
/**
|
|
175
|
+
* The bridge does not know this job — its `hello.active_jobs` or a
|
|
176
|
+
* `job_status` answer leaves it out — and, for an action, no goal the
|
|
177
|
+
* bridge cannot attribute is active on the job's action any more, so none
|
|
178
|
+
* of them can be it. A `job.error.code` on `lost`, final: how an `unknown`
|
|
179
|
+
* job the bridge has no word about ends. A service job, which has no goals to look
|
|
180
|
+
* at, gets it as soon as the bridge does not know it; so does a job whose
|
|
181
|
+
* persisted goal the action server no longer knows (its result expired).
|
|
182
|
+
* Distinct from `job_lost`, the bridge's own statement about a job it
|
|
183
|
+
* still names, and from `bridge_disconnected`/`bridge_timeout`, the
|
|
184
|
+
* cloud's guesses that make a job `unknown` in the first place.
|
|
185
|
+
*/
|
|
186
|
+
'job_unknown_to_bridge',
|
|
150
187
|
/**
|
|
151
188
|
* The robot's action server vanished mid-goal — the bridge's own liveness
|
|
152
189
|
* check found `server_is_ready()` false for three seconds straight and
|
|
@@ -167,9 +204,11 @@ export const ERROR_CODES = [
|
|
|
167
204
|
'goal_uncontrollable',
|
|
168
205
|
/**
|
|
169
206
|
* The robot stayed offline for longer than `JOB_OFFLINE_GRACE_MS` while a
|
|
170
|
-
* job was running.
|
|
171
|
-
*
|
|
172
|
-
*
|
|
207
|
+
* job was running. `job.error.code` on `unknown`, not `lost`: the cloud
|
|
208
|
+
* does not know how the job stands, and the reconnecting bridge's
|
|
209
|
+
* `hello.active_jobs` resolves it — running again clears this code, an end
|
|
210
|
+
* replaces it, and a job the bridge does not know becomes `lost` with
|
|
211
|
+
* `job_unknown_to_bridge`.
|
|
173
212
|
*/
|
|
174
213
|
'bridge_disconnected',
|
|
175
214
|
/**
|
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, 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, SnapshotHeader, CloudCameraStart, CloudCameraStop, BridgeCameraState, ActiveJob, BridgeAssetsAvailable, CloudAssetRequest, BridgeAssetProgress, } from './protocol.js';
|
|
8
|
-
export { jobState, job, jobEvent, busyDetails, publisherBusyDetails, jobQueueFullDetails } from './jobs.js';
|
|
9
|
-
export type { JobState, 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, 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, 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
|
@@ -10,14 +10,20 @@ import { z } from 'zod';
|
|
|
10
10
|
* 1. **State is observed by slug, not by id.** The id is informative; a client
|
|
11
11
|
* watches `robot × slug` and sees whatever job is running there, which is
|
|
12
12
|
* also why every observer of a slug sees the same job.
|
|
13
|
-
* 2.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* 2. **What the cloud does not know it calls `unknown`, and `lost` is final.**
|
|
14
|
+
* A job whose robot went quiet — offline past `JOB_OFFLINE_GRACE_MS`, or
|
|
15
|
+
* connected but silent past `JOB_HEARTBEAT_TIMEOUT_MS` — is `unknown`: not
|
|
16
|
+
* terminal, the slug stays occupied, and only a statement of the bridge
|
|
17
|
+
* resolves it (it is running, it ended, or the bridge does not know it and
|
|
18
|
+
* nothing else runs on its action). `lost` is what that last statement
|
|
19
|
+
* produces, and nothing ever leaves it. Neither is left reading "running"
|
|
20
|
+
* because nobody contradicted it: a system that reports a machine is still
|
|
21
|
+
* working when it does not know is worse than one that says so — and one
|
|
22
|
+
* that declares work lost on a guess is wrong the moment the robot comes
|
|
23
|
+
* back and says it finished.
|
|
19
24
|
*/
|
|
20
25
|
export declare const jobState: z.ZodEnum<{
|
|
26
|
+
unknown: "unknown";
|
|
21
27
|
failed: "failed";
|
|
22
28
|
running: "running";
|
|
23
29
|
succeeded: "succeeded";
|
|
@@ -25,17 +31,57 @@ export declare const jobState: z.ZodEnum<{
|
|
|
25
31
|
lost: "lost";
|
|
26
32
|
}>;
|
|
27
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>;
|
|
49
|
+
/**
|
|
50
|
+
* Who started a job.
|
|
51
|
+
*
|
|
52
|
+
* `fleetless` for every job the cloud minted from an invocation. `external`
|
|
53
|
+
* for a goal the bridge found active on a published action without having
|
|
54
|
+
* sent it — started by anyone else on the robot's ROS graph, or the bridge's
|
|
55
|
+
* own goal after its mapping was lost. An external job has the same shape,
|
|
56
|
+
* states, live stream and cancel as any other, but no parameters (ROS 2
|
|
57
|
+
* publishes a goal's request nowhere), no starter, and it lives in memory
|
|
58
|
+
* only: it is never written to `job_runs` and never counts towards quotas.
|
|
59
|
+
*/
|
|
60
|
+
export declare const jobOrigin: z.ZodEnum<{
|
|
61
|
+
fleetless: "fleetless";
|
|
62
|
+
external: "external";
|
|
63
|
+
}>;
|
|
64
|
+
export type JobOrigin = z.infer<typeof jobOrigin>;
|
|
65
|
+
/**
|
|
66
|
+
* One job, as the cloud tells every client about it — a Fleetless job or an
|
|
67
|
+
* external goal alike, told apart only by `origin`.
|
|
68
|
+
*/
|
|
28
69
|
export declare const job: z.ZodObject<{
|
|
29
70
|
id: z.ZodUUID;
|
|
30
71
|
robot_id: z.ZodUUID;
|
|
31
72
|
slug: z.ZodString;
|
|
32
73
|
state: z.ZodEnum<{
|
|
74
|
+
unknown: "unknown";
|
|
33
75
|
failed: "failed";
|
|
34
76
|
running: "running";
|
|
35
77
|
succeeded: "succeeded";
|
|
36
78
|
cancelled: "cancelled";
|
|
37
79
|
lost: "lost";
|
|
38
80
|
}>;
|
|
81
|
+
origin: z.ZodEnum<{
|
|
82
|
+
fleetless: "fleetless";
|
|
83
|
+
external: "external";
|
|
84
|
+
}>;
|
|
39
85
|
started_at: z.ZodISODateTime;
|
|
40
86
|
updated_at: z.ZodISODateTime;
|
|
41
87
|
seq: z.ZodNumber;
|
|
@@ -65,12 +111,17 @@ export declare const jobEvent: z.ZodObject<{
|
|
|
65
111
|
robot_id: z.ZodUUID;
|
|
66
112
|
slug: z.ZodString;
|
|
67
113
|
state: z.ZodEnum<{
|
|
114
|
+
unknown: "unknown";
|
|
68
115
|
failed: "failed";
|
|
69
116
|
running: "running";
|
|
70
117
|
succeeded: "succeeded";
|
|
71
118
|
cancelled: "cancelled";
|
|
72
119
|
lost: "lost";
|
|
73
120
|
}>;
|
|
121
|
+
origin: z.ZodEnum<{
|
|
122
|
+
fleetless: "fleetless";
|
|
123
|
+
external: "external";
|
|
124
|
+
}>;
|
|
74
125
|
started_at: z.ZodISODateTime;
|
|
75
126
|
updated_at: z.ZodISODateTime;
|
|
76
127
|
seq: z.ZodNumber;
|
|
@@ -89,6 +140,11 @@ export type JobEvent = z.infer<typeof jobEvent>;
|
|
|
89
140
|
/**
|
|
90
141
|
* What a busy refusal tells the caller: what is already running. A refusal that
|
|
91
142
|
* only says "busy" forces the caller to guess whether to wait or to give up.
|
|
143
|
+
*
|
|
144
|
+
* `running` is whatever occupies the slug — a `running` job, an `unknown` one
|
|
145
|
+
* the robot has not accounted for yet, or an `external` goal someone else
|
|
146
|
+
* started — and its `state` and `origin` say which, so a caller can tell
|
|
147
|
+
* "wait for it" from "cancel what someone else started".
|
|
92
148
|
*/
|
|
93
149
|
export declare const busyDetails: z.ZodObject<{
|
|
94
150
|
running: z.ZodObject<{
|
|
@@ -96,12 +152,17 @@ export declare const busyDetails: z.ZodObject<{
|
|
|
96
152
|
robot_id: z.ZodUUID;
|
|
97
153
|
slug: z.ZodString;
|
|
98
154
|
state: z.ZodEnum<{
|
|
155
|
+
unknown: "unknown";
|
|
99
156
|
failed: "failed";
|
|
100
157
|
running: "running";
|
|
101
158
|
succeeded: "succeeded";
|
|
102
159
|
cancelled: "cancelled";
|
|
103
160
|
lost: "lost";
|
|
104
161
|
}>;
|
|
162
|
+
origin: z.ZodEnum<{
|
|
163
|
+
fleetless: "fleetless";
|
|
164
|
+
external: "external";
|
|
165
|
+
}>;
|
|
105
166
|
started_at: z.ZodISODateTime;
|
|
106
167
|
updated_at: z.ZodISODateTime;
|
|
107
168
|
seq: z.ZodNumber;
|
|
@@ -203,6 +264,7 @@ export declare const jobRun: z.ZodObject<{
|
|
|
203
264
|
service: "service";
|
|
204
265
|
}>;
|
|
205
266
|
state: z.ZodEnum<{
|
|
267
|
+
unknown: "unknown";
|
|
206
268
|
failed: "failed";
|
|
207
269
|
running: "running";
|
|
208
270
|
succeeded: "succeeded";
|
|
@@ -239,6 +301,7 @@ export declare const jobRunQuery: z.ZodObject<{
|
|
|
239
301
|
robot_id: z.ZodOptional<z.ZodUUID>;
|
|
240
302
|
slug: z.ZodOptional<z.ZodString>;
|
|
241
303
|
state: z.ZodOptional<z.ZodEnum<{
|
|
304
|
+
unknown: "unknown";
|
|
242
305
|
failed: "failed";
|
|
243
306
|
running: "running";
|
|
244
307
|
succeeded: "succeeded";
|
|
@@ -263,6 +326,7 @@ export declare const jobRunListResponse: z.ZodObject<{
|
|
|
263
326
|
service: "service";
|
|
264
327
|
}>;
|
|
265
328
|
state: z.ZodEnum<{
|
|
329
|
+
unknown: "unknown";
|
|
266
330
|
failed: "failed";
|
|
267
331
|
running: "running";
|
|
268
332
|
succeeded: "succeeded";
|
package/dist/jobs.js
CHANGED
|
@@ -11,14 +11,43 @@ import { slug, wireSeqCursor, wireTimestampMs } from './common.js';
|
|
|
11
11
|
* 1. **State is observed by slug, not by id.** The id is informative; a client
|
|
12
12
|
* watches `robot × slug` and sees whatever job is running there, which is
|
|
13
13
|
* also why every observer of a slug sees the same job.
|
|
14
|
-
* 2.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* 2. **What the cloud does not know it calls `unknown`, and `lost` is final.**
|
|
15
|
+
* A job whose robot went quiet — offline past `JOB_OFFLINE_GRACE_MS`, or
|
|
16
|
+
* connected but silent past `JOB_HEARTBEAT_TIMEOUT_MS` — is `unknown`: not
|
|
17
|
+
* terminal, the slug stays occupied, and only a statement of the bridge
|
|
18
|
+
* resolves it (it is running, it ended, or the bridge does not know it and
|
|
19
|
+
* nothing else runs on its action). `lost` is what that last statement
|
|
20
|
+
* produces, and nothing ever leaves it. Neither is left reading "running"
|
|
21
|
+
* because nobody contradicted it: a system that reports a machine is still
|
|
22
|
+
* working when it does not know is worse than one that says so — and one
|
|
23
|
+
* that declares work lost on a guess is wrong the moment the robot comes
|
|
24
|
+
* back and says it finished.
|
|
25
|
+
*/
|
|
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']);
|
|
35
|
+
/**
|
|
36
|
+
* Who started a job.
|
|
37
|
+
*
|
|
38
|
+
* `fleetless` for every job the cloud minted from an invocation. `external`
|
|
39
|
+
* for a goal the bridge found active on a published action without having
|
|
40
|
+
* sent it — started by anyone else on the robot's ROS graph, or the bridge's
|
|
41
|
+
* own goal after its mapping was lost. An external job has the same shape,
|
|
42
|
+
* states, live stream and cancel as any other, but no parameters (ROS 2
|
|
43
|
+
* publishes a goal's request nowhere), no starter, and it lives in memory
|
|
44
|
+
* only: it is never written to `job_runs` and never counts towards quotas.
|
|
45
|
+
*/
|
|
46
|
+
export const jobOrigin = z.enum(['fleetless', 'external']);
|
|
47
|
+
/**
|
|
48
|
+
* One job, as the cloud tells every client about it — a Fleetless job or an
|
|
49
|
+
* external goal alike, told apart only by `origin`.
|
|
20
50
|
*/
|
|
21
|
-
export const jobState = z.enum(['running', 'succeeded', 'failed', 'cancelled', 'lost']);
|
|
22
51
|
export const job = z.object({
|
|
23
52
|
id: z.uuid().meta({
|
|
24
53
|
description: 'The job\'s id, minted by the cloud when the invocation is accepted. Informative — state is observed by slug; a cancel names this id to stop one specific job rather than whatever is running.',
|
|
@@ -28,7 +57,10 @@ export const job = z.object({
|
|
|
28
57
|
description: 'The action or service this job is running, as the published configuration exposes it. One slug carries one job at a time, so every observer of that slug sees the same one.',
|
|
29
58
|
}),
|
|
30
59
|
state: jobState.meta({
|
|
31
|
-
description: 'Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `
|
|
60
|
+
description: 'Where the job stands: `running`, `unknown`, `succeeded`, `failed`, `cancelled` or `lost`. `unknown` is not an outcome — the robot went offline or silent and the cloud does not know yet; the slug stays occupied and the bridge\'s next statement resolves it, `error` naming why the cloud lost sight of it. `lost` is final: the bridge stated it does not know the job and nothing else runs on its action, or the action server vanished mid-goal.',
|
|
61
|
+
}),
|
|
62
|
+
origin: jobOrigin.meta({
|
|
63
|
+
description: "Who started this job. `fleetless` for everything minted by the cloud; `external` for a goal the bridge found active on a published action without having sent it — no parameters, no starter, never written to `job_runs`.",
|
|
32
64
|
}),
|
|
33
65
|
started_at: z.iso.datetime().meta({
|
|
34
66
|
description: 'When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge, this is **adoption time**, not the real start — the cloud never minted it.',
|
|
@@ -47,7 +79,7 @@ export const job = z.object({
|
|
|
47
79
|
* exists for the same reason on the audit log.
|
|
48
80
|
*
|
|
49
81
|
* **Scoped honestly: per cloud process, per run.** Job state lives in memory
|
|
50
|
-
* — that is why `lost`
|
|
82
|
+
* — that is why `unknown` and `lost` exist at all — so this counter restarts when
|
|
51
83
|
* the cloud does, alongside the jobs it orders. Sound, because it only ever
|
|
52
84
|
* orders jobs that coexist in one registry — and stated, because a reader
|
|
53
85
|
* who assumed `auditEvent.seq`'s durable semantics would be wrong.
|
|
@@ -85,7 +117,7 @@ export const job = z.object({
|
|
|
85
117
|
})
|
|
86
118
|
.nullable()
|
|
87
119
|
.meta({
|
|
88
|
-
description: 'Why the job failed: a human `message`, a `code` where one exists, and `details` for the codes that carry a documented payload. `
|
|
120
|
+
description: 'Why the job failed, or why the cloud does not know how it stands: a human `message`, a `code` where one exists, and `details` for the codes that carry a documented payload. Set on `failed` and `lost`, and on `unknown` — where `code` is `bridge_disconnected` or `bridge_timeout`, the cloud\'s own reason for not knowing, cleared when the bridge reports the job running again.',
|
|
89
121
|
}),
|
|
90
122
|
});
|
|
91
123
|
/**
|
|
@@ -111,6 +143,11 @@ export const jobEvent = z.object({
|
|
|
111
143
|
/**
|
|
112
144
|
* What a busy refusal tells the caller: what is already running. A refusal that
|
|
113
145
|
* only says "busy" forces the caller to guess whether to wait or to give up.
|
|
146
|
+
*
|
|
147
|
+
* `running` is whatever occupies the slug — a `running` job, an `unknown` one
|
|
148
|
+
* the robot has not accounted for yet, or an `external` goal someone else
|
|
149
|
+
* started — and its `state` and `origin` say which, so a caller can tell
|
|
150
|
+
* "wait for it" from "cancel what someone else started".
|
|
114
151
|
*/
|
|
115
152
|
export const busyDetails = z.object({
|
|
116
153
|
running: job,
|
|
@@ -210,16 +247,16 @@ export const jobRun = z.object({
|
|
|
210
247
|
description: 'Whether the slug was an `action` or a `service`.',
|
|
211
248
|
}),
|
|
212
249
|
state: jobState.meta({
|
|
213
|
-
description: 'How the run ended, or `running` while it is still going. `lost`
|
|
250
|
+
description: 'How the run ended, or `running` while it is still going. `unknown` while the robot has not accounted for it — offline or silent — and updated once the bridge says how it stands. `lost` is final: the bridge did not know the run and nothing else ran on its action, so the outcome is unknowable rather than unknown.',
|
|
214
251
|
}),
|
|
215
252
|
started_at: z.iso.datetime().meta({
|
|
216
253
|
description: 'When the run started, as an ISO 8601 timestamp. Runs are listed and filtered by this instant.',
|
|
217
254
|
}),
|
|
218
255
|
ended_at: z.iso.datetime().nullable().meta({
|
|
219
|
-
description: 'When the run finished, as an ISO 8601 timestamp. `null` while it is still `running` — a run has an end only once it has one.',
|
|
256
|
+
description: 'When the run finished, as an ISO 8601 timestamp. `null` while it is still `running` or `unknown` — a run has an end only once it has one.',
|
|
220
257
|
}),
|
|
221
258
|
duration_ms: z.number().int().nonnegative().nullable().meta({
|
|
222
|
-
description: 'How long the run took, in milliseconds. `null` while it is still `running`, never `0` standing in for "nothing so far".',
|
|
259
|
+
description: 'How long the run took, in milliseconds. `null` while it is still `running` or `unknown`, never `0` standing in for "nothing so far".',
|
|
223
260
|
}),
|
|
224
261
|
result: z.unknown().nullable().meta({
|
|
225
262
|
description: 'What the action or service returned once it succeeded, shaped by ROS itself. `null` otherwise.',
|
|
@@ -278,7 +315,7 @@ export const jobRunQuery = z
|
|
|
278
315
|
description: 'Only runs of this action or service.',
|
|
279
316
|
}),
|
|
280
317
|
state: jobState.optional().meta({
|
|
281
|
-
description: 'Only runs in this state — `running`, `succeeded`, `failed`, `cancelled` or `lost`.',
|
|
318
|
+
description: 'Only runs in this state — `running`, `unknown`, `succeeded`, `failed`, `cancelled` or `lost`.',
|
|
282
319
|
}),
|
|
283
320
|
kind: jobRunKind.optional().meta({
|
|
284
321
|
description: 'Only `action` runs, or only `service` runs.',
|