@fleetless/contracts 1.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 +26 -0
- package/LICENSE +202 -0
- package/NOTICE +17 -0
- package/README.md +88 -0
- package/artifacts/constants.json +24 -0
- package/artifacts/openapi.json +17219 -0
- package/artifacts/routes.json +4605 -0
- package/artifacts/schema/accept-team-invite-request.schema.json +22 -0
- package/artifacts/schema/action-config.schema.json +198 -0
- package/artifacts/schema/alert-list-response.schema.json +172 -0
- package/artifacts/schema/api-error.schema.json +20 -0
- package/artifacts/schema/app-auth-config.schema.json +106 -0
- package/artifacts/schema/app-invitation-list-response.schema.json +57 -0
- package/artifacts/schema/app-invitation.schema.json +69 -0
- package/artifacts/schema/app-list-response.schema.json +82 -0
- package/artifacts/schema/app-mail-template-list-response.schema.json +68 -0
- package/artifacts/schema/app-mail-template.schema.json +54 -0
- package/artifacts/schema/app-oidc-provider-list-response.schema.json +93 -0
- package/artifacts/schema/app-oidc-provider.schema.json +80 -0
- package/artifacts/schema/app-user-list-response.schema.json +111 -0
- package/artifacts/schema/app-user.schema.json +98 -0
- package/artifacts/schema/app.schema.json +69 -0
- package/artifacts/schema/apply-error.schema.json +41 -0
- package/artifacts/schema/asset-list-response.schema.json +288 -0
- package/artifacts/schema/asset-sync-request.schema.json +17 -0
- package/artifacts/schema/asset-sync-response.schema.json +16 -0
- package/artifacts/schema/asset-sync-status.schema.json +136 -0
- package/artifacts/schema/asset.schema.json +68 -0
- package/artifacts/schema/audit-actor.schema.json +32 -0
- package/artifacts/schema/audit-event.schema.json +119 -0
- package/artifacts/schema/audit-list-response.schema.json +144 -0
- package/artifacts/schema/audit-query.schema.json +79 -0
- package/artifacts/schema/auth-error.schema.json +23 -0
- package/artifacts/schema/auth-me-response.schema.json +99 -0
- package/artifacts/schema/auth-ok.schema.json +115 -0
- package/artifacts/schema/authorization-server-metadata.schema.json +80 -0
- package/artifacts/schema/bridge-asset-progress.schema.json +99 -0
- package/artifacts/schema/bridge-assets-available.schema.json +25 -0
- package/artifacts/schema/bridge-camera-state.schema.json +78 -0
- package/artifacts/schema/bridge-config-applied.schema.json +67 -0
- package/artifacts/schema/bridge-hello.schema.json +65 -0
- package/artifacts/schema/bridge-introspect.schema.json +114 -0
- package/artifacts/schema/bridge-job-lost.schema.json +22 -0
- package/artifacts/schema/bridge-job-update.schema.json +100 -0
- package/artifacts/schema/bridge-pong.schema.json +19 -0
- package/artifacts/schema/bridge-pressure.schema.json +292 -0
- package/artifacts/schema/bridge-state.schema.json +24 -0
- package/artifacts/schema/bridge-type-definitions.schema.json +169 -0
- package/artifacts/schema/busy-details.schema.json +115 -0
- package/artifacts/schema/camera-descriptor.schema.json +45 -0
- package/artifacts/schema/camera-list-response.schema.json +58 -0
- package/artifacts/schema/camera-source.schema.json +240 -0
- package/artifacts/schema/cancel-request.schema.json +20 -0
- package/artifacts/schema/client-accept-invitation-request.schema.json +35 -0
- package/artifacts/schema/client-auth.schema.json +18 -0
- package/artifacts/schema/client-cancel.schema.json +45 -0
- package/artifacts/schema/client-identity.schema.json +103 -0
- package/artifacts/schema/client-invoke.schema.json +45 -0
- package/artifacts/schema/client-login-request.schema.json +29 -0
- package/artifacts/schema/client-logout-request.schema.json +14 -0
- package/artifacts/schema/client-mcp-interaction-decision-response.schema.json +15 -0
- package/artifacts/schema/client-mcp-interaction.schema.json +59 -0
- package/artifacts/schema/client-oidc-callback-query.schema.json +28 -0
- package/artifacts/schema/client-oidc-exchange-request.schema.json +21 -0
- package/artifacts/schema/client-oidc-start-query.schema.json +36 -0
- package/artifacts/schema/client-password-reset-confirm-request.schema.json +22 -0
- package/artifacts/schema/client-password-reset-request.schema.json +24 -0
- package/artifacts/schema/client-provider-list-query.schema.json +17 -0
- package/artifacts/schema/client-provider-list-response.schema.json +34 -0
- package/artifacts/schema/client-publish.schema.json +40 -0
- package/artifacts/schema/client-refresh-request.schema.json +14 -0
- package/artifacts/schema/client-register-request.schema.json +44 -0
- package/artifacts/schema/client-resend-verification-request.schema.json +24 -0
- package/artifacts/schema/client-subscribe.schema.json +43 -0
- package/artifacts/schema/client-unsubscribe.schema.json +26 -0
- package/artifacts/schema/client-verify-email-request.schema.json +15 -0
- package/artifacts/schema/cloud-asset-request.schema.json +37 -0
- package/artifacts/schema/cloud-camera-start.schema.json +41 -0
- package/artifacts/schema/cloud-camera-stop.schema.json +26 -0
- package/artifacts/schema/cloud-cancel.schema.json +33 -0
- package/artifacts/schema/cloud-config.schema.json +1635 -0
- package/artifacts/schema/cloud-hello-error.schema.json +23 -0
- package/artifacts/schema/cloud-hello-ok.schema.json +19 -0
- package/artifacts/schema/cloud-introspect-request.schema.json +19 -0
- package/artifacts/schema/cloud-invoke.schema.json +40 -0
- package/artifacts/schema/cloud-ping.schema.json +19 -0
- package/artifacts/schema/cloud-publish.schema.json +28 -0
- package/artifacts/schema/cloud-type-request.schema.json +30 -0
- package/artifacts/schema/command-result.schema.json +175 -0
- package/artifacts/schema/config-draft-response.schema.json +1695 -0
- package/artifacts/schema/config-state.schema.json +124 -0
- package/artifacts/schema/config-version-response.schema.json +1641 -0
- package/artifacts/schema/config-versions-response.schema.json +33 -0
- package/artifacts/schema/create-app-invitation-request.schema.json +40 -0
- package/artifacts/schema/create-app-oidc-provider-request.schema.json +70 -0
- package/artifacts/schema/create-app-request.schema.json +30 -0
- package/artifacts/schema/create-app-user-request.schema.json +42 -0
- package/artifacts/schema/create-robot-request.schema.json +14 -0
- package/artifacts/schema/create-robot-response.schema.json +44 -0
- package/artifacts/schema/create-server-key-response.schema.json +65 -0
- package/artifacts/schema/create-team-invite-request.schema.json +43 -0
- package/artifacts/schema/datapoint-alert-row.schema.json +160 -0
- package/artifacts/schema/datapoint-config.schema.json +366 -0
- package/artifacts/schema/datapoint-display.schema.json +31 -0
- package/artifacts/schema/datapoint-event.schema.json +34 -0
- package/artifacts/schema/datapoint-frame.schema.json +28 -0
- package/artifacts/schema/datapoint-list-response.schema.json +61 -0
- package/artifacts/schema/datapoint-value.schema.json +28 -0
- package/artifacts/schema/developer-login-request.schema.json +19 -0
- package/artifacts/schema/dynamic-client-registration-request.schema.json +60 -0
- package/artifacts/schema/dynamic-client-registration-response.schema.json +68 -0
- package/artifacts/schema/error-frame.schema.json +23 -0
- package/artifacts/schema/exposure-counts.schema.json +39 -0
- package/artifacts/schema/exposure-list-response.schema.json +43 -0
- package/artifacts/schema/fetch-types-request.schema.json +19 -0
- package/artifacts/schema/fetch-types-response.schema.json +163 -0
- package/artifacts/schema/fleetless-user-list-response.schema.json +73 -0
- package/artifacts/schema/fleetless-user.schema.json +60 -0
- package/artifacts/schema/history-buckets-response.schema.json +79 -0
- package/artifacts/schema/history-query.schema.json +58 -0
- package/artifacts/schema/history-response.schema.json +150 -0
- package/artifacts/schema/history-samples-response.schema.json +68 -0
- package/artifacts/schema/introspection-response.schema.json +118 -0
- package/artifacts/schema/invoke-or-service-response.schema.json +141 -0
- package/artifacts/schema/invoke-request.schema.json +23 -0
- package/artifacts/schema/invoke-response.schema.json +125 -0
- package/artifacts/schema/job-actor.schema.json +34 -0
- package/artifacts/schema/job-event.schema.json +158 -0
- package/artifacts/schema/job-response.schema.json +123 -0
- package/artifacts/schema/job-run-list-response.schema.json +222 -0
- package/artifacts/schema/job-run-query.schema.json +95 -0
- package/artifacts/schema/job-run-summary-query.schema.json +23 -0
- package/artifacts/schema/job-run-summary.schema.json +33 -0
- package/artifacts/schema/job-run.schema.json +195 -0
- package/artifacts/schema/job-state.schema.json +11 -0
- package/artifacts/schema/job.schema.json +106 -0
- package/artifacts/schema/latency-bucket.schema.json +63 -0
- package/artifacts/schema/live-session-response.schema.json +41 -0
- package/artifacts/schema/mail-outcome.schema.json +20 -0
- package/artifacts/schema/mail-template-preview-request.schema.json +35 -0
- package/artifacts/schema/mail-template-preview-response.schema.json +31 -0
- package/artifacts/schema/mail-template-problem-details.schema.json +24 -0
- package/artifacts/schema/mcp-consent-grant-list-response.schema.json +52 -0
- package/artifacts/schema/mcp-consent-grant.schema.json +39 -0
- package/artifacts/schema/mcp-robot-datasheet.schema.json +115 -0
- package/artifacts/schema/mcp-role-preview-response.schema.json +134 -0
- package/artifacts/schema/missing-asset-query.schema.json +11 -0
- package/artifacts/schema/oauth-authorize-query.schema.json +47 -0
- package/artifacts/schema/oauth-redirect-response.schema.json +15 -0
- package/artifacts/schema/oauth-token-request.schema.json +47 -0
- package/artifacts/schema/oauth-token-response.schema.json +38 -0
- package/artifacts/schema/org-alerts-query.schema.json +15 -0
- package/artifacts/schema/org-event-dropped.schema.json +26 -0
- package/artifacts/schema/org-event-replay.schema.json +97 -0
- package/artifacts/schema/org-event-subscribe.schema.json +14 -0
- package/artifacts/schema/org-event-unsubscribe.schema.json +14 -0
- package/artifacts/schema/org-event.schema.json +75 -0
- package/artifacts/schema/org-firing-alerts-response.schema.json +178 -0
- package/artifacts/schema/org-health-query.schema.json +13 -0
- package/artifacts/schema/org-latency-query.schema.json +42 -0
- package/artifacts/schema/org-latency-response.schema.json +124 -0
- package/artifacts/schema/org-quota-usage-counts.schema.json +42 -0
- package/artifacts/schema/org-quota-usage.schema.json +102 -0
- package/artifacts/schema/org-quotas.schema.json +51 -0
- package/artifacts/schema/org-usage-query.schema.json +19 -0
- package/artifacts/schema/org-usage-response.schema.json +77 -0
- package/artifacts/schema/org.schema.json +30 -0
- package/artifacts/schema/parameter-invalid-details.schema.json +37 -0
- package/artifacts/schema/parameter-spec.schema.json +120 -0
- package/artifacts/schema/parameter-violation.schema.json +24 -0
- package/artifacts/schema/password-change-request.schema.json +21 -0
- package/artifacts/schema/password-reset-confirm.schema.json +19 -0
- package/artifacts/schema/password-reset-request.schema.json +14 -0
- package/artifacts/schema/patch-app-oidc-provider-request.schema.json +50 -0
- package/artifacts/schema/patch-app-user-request.schema.json +34 -0
- package/artifacts/schema/patch-auth-me-request.schema.json +22 -0
- package/artifacts/schema/patch-fleetless-user-request.schema.json +20 -0
- package/artifacts/schema/patch-org-request.schema.json +15 -0
- package/artifacts/schema/patch-org-response.schema.json +40 -0
- package/artifacts/schema/patch-robot-request.schema.json +15 -0
- package/artifacts/schema/patch-robot-response.schema.json +40 -0
- package/artifacts/schema/pending-team-invite-list-response.schema.json +52 -0
- package/artifacts/schema/pending-team-invite.schema.json +39 -0
- package/artifacts/schema/protected-resource-metadata.schema.json +41 -0
- package/artifacts/schema/publish-config-response.schema.json +21 -0
- package/artifacts/schema/publish-request.schema.json +17 -0
- package/artifacts/schema/publisher-config.schema.json +285 -0
- package/artifacts/schema/put-app-auth-config-request.schema.json +93 -0
- package/artifacts/schema/put-app-mail-template-request.schema.json +35 -0
- package/artifacts/schema/put-config-draft-request.schema.json +13 -0
- package/artifacts/schema/put-datapoint-display-request.schema.json +31 -0
- package/artifacts/schema/put-robot-details-request.schema.json +41 -0
- package/artifacts/schema/put-robot-details-response.schema.json +43 -0
- package/artifacts/schema/rate-limit-details.schema.json +15 -0
- package/artifacts/schema/refresh-request.schema.json +13 -0
- package/artifacts/schema/release-live-query.schema.json +13 -0
- package/artifacts/schema/rename-slug-request.schema.json +23 -0
- package/artifacts/schema/rename-slug-response.schema.json +24 -0
- package/artifacts/schema/resource-health-event.schema.json +72 -0
- package/artifacts/schema/resource-health-list-response.schema.json +80 -0
- package/artifacts/schema/resource-health-state.schema.json +68 -0
- package/artifacts/schema/robot-config-doc.schema.json +1616 -0
- package/artifacts/schema/robot-delete-query.schema.json +12 -0
- package/artifacts/schema/robot-deletion-summary.schema.json +63 -0
- package/artifacts/schema/robot-detail-response.schema.json +262 -0
- package/artifacts/schema/robot-details-doc.schema.json +33 -0
- package/artifacts/schema/robot-jobs-response.schema.json +119 -0
- package/artifacts/schema/robot-latency-series.schema.json +81 -0
- package/artifacts/schema/robot-list-item.schema.json +94 -0
- package/artifacts/schema/robot-list-response.schema.json +106 -0
- package/artifacts/schema/robot.schema.json +30 -0
- package/artifacts/schema/role-list-response.schema.json +48 -0
- package/artifacts/schema/role-permissions.schema.json +61 -0
- package/artifacts/schema/role.schema.json +35 -0
- package/artifacts/schema/ros-graph.schema.json +99 -0
- package/artifacts/schema/server-key-list-response.schema.json +64 -0
- package/artifacts/schema/server-key.schema.json +51 -0
- package/artifacts/schema/service-call-response.schema.json +13 -0
- package/artifacts/schema/service-config.schema.json +198 -0
- package/artifacts/schema/session-tokens.schema.json +28 -0
- package/artifacts/schema/sign-up-request.schema.json +26 -0
- package/artifacts/schema/sign-up-response.schema.json +127 -0
- package/artifacts/schema/slug-usage-response.schema.json +32 -0
- package/artifacts/schema/snapshot-header.schema.json +44 -0
- package/artifacts/schema/snapshot-meta-response.schema.json +85 -0
- package/artifacts/schema/subscribe-error.schema.json +31 -0
- package/artifacts/schema/team-invite.schema.json +57 -0
- package/artifacts/schema/tier-change-request.schema.json +17 -0
- package/artifacts/schema/type-definition.schema.json +144 -0
- package/artifacts/schema/types-response.schema.json +156 -0
- package/artifacts/schema/update-app-request.schema.json +32 -0
- package/artifacts/schema/urdf-completeness.schema.json +50 -0
- package/artifacts/schema/validation-issue.schema.json +43 -0
- package/artifacts/schema/waitlist-request.schema.json +15 -0
- package/artifacts/schema-outgoing/bridge-asset-progress.schema.json +102 -0
- package/artifacts/schema-outgoing/bridge-assets-available.schema.json +26 -0
- package/artifacts/schema-outgoing/bridge-camera-state.schema.json +80 -0
- package/artifacts/schema-outgoing/bridge-config-applied.schema.json +69 -0
- package/artifacts/schema-outgoing/bridge-hello.schema.json +68 -0
- package/artifacts/schema-outgoing/bridge-introspect.schema.json +119 -0
- package/artifacts/schema-outgoing/bridge-job-lost.schema.json +23 -0
- package/artifacts/schema-outgoing/bridge-job-update.schema.json +102 -0
- package/artifacts/schema-outgoing/bridge-pong.schema.json +20 -0
- package/artifacts/schema-outgoing/bridge-type-definitions.schema.json +174 -0
- package/artifacts/schema-outgoing/datapoint-frame.schema.json +29 -0
- package/artifacts/schema-outgoing/snapshot-header.schema.json +45 -0
- package/dist/alerts.d.ts +255 -0
- package/dist/alerts.js +193 -0
- package/dist/app-users.d.ts +606 -0
- package/dist/app-users.js +696 -0
- package/dist/apps.d.ts +175 -0
- package/dist/apps.js +267 -0
- package/dist/assets.d.ts +434 -0
- package/dist/assets.js +546 -0
- package/dist/audit.d.ts +129 -0
- package/dist/audit.js +238 -0
- package/dist/client-auth.d.ts +409 -0
- package/dist/client-auth.js +487 -0
- package/dist/common.d.ts +186 -0
- package/dist/common.js +199 -0
- package/dist/config-issues.d.ts +175 -0
- package/dist/config-issues.js +339 -0
- package/dist/config.d.ts +862 -0
- package/dist/config.js +1988 -0
- package/dist/errors.d.ts +52 -0
- package/dist/errors.js +786 -0
- package/dist/identity.d.ts +549 -0
- package/dist/identity.js +503 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +51 -0
- package/dist/introspection.d.ts +99 -0
- package/dist/introspection.js +97 -0
- package/dist/jobs.d.ts +334 -0
- package/dist/jobs.js +345 -0
- package/dist/mcp.d.ts +239 -0
- package/dist/mcp.js +153 -0
- package/dist/oauth.d.ts +344 -0
- package/dist/oauth.js +488 -0
- package/dist/protocol.d.ts +781 -0
- package/dist/protocol.js +715 -0
- package/dist/realtime.d.ts +494 -0
- package/dist/realtime.js +512 -0
- package/dist/rest.d.ts +1989 -0
- package/dist/rest.js +1963 -0
- package/dist/routes.d.ts +94 -0
- package/dist/routes.js +2298 -0
- package/package.json +61 -0
|
@@ -0,0 +1,1695 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"doc": {
|
|
6
|
+
"anyOf": [
|
|
7
|
+
{
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"fleetless": {
|
|
11
|
+
"type": "number",
|
|
12
|
+
"const": 1,
|
|
13
|
+
"description": "The format version, and the first line of the file. It decides how everything below is read, so a file that omits it — or names a version this cloud does not know — is **refused rather than half understood**."
|
|
14
|
+
},
|
|
15
|
+
"messages": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"propertyNames": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"minLength": 2,
|
|
20
|
+
"maxLength": 63,
|
|
21
|
+
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
22
|
+
"patternErrorMessage": "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore — `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused."
|
|
23
|
+
},
|
|
24
|
+
"additionalProperties": {
|
|
25
|
+
"description": "The message as it will be sent, written out in full: literals are fixed, `${name}` is a hole a caller fills, and a field written `0.0` is one no client can change. Directly after `message:` a `${name}` standing alone names a shared message instead; anywhere inside a body it is a parameter. `null` is refused **at every depth** — omitting a key is the only spelling of \"not set\".",
|
|
26
|
+
"defaultSnippets": [
|
|
27
|
+
{
|
|
28
|
+
"label": "a shared message",
|
|
29
|
+
"description": "One reusable body, with one parameter hole in it.",
|
|
30
|
+
"body": {
|
|
31
|
+
"linear": {
|
|
32
|
+
"x": "\\${speed}"
|
|
33
|
+
},
|
|
34
|
+
"angular": {
|
|
35
|
+
"z": 0
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"description": "Reusable message bodies, keyed by name, inserted elsewhere by writing `${name}` directly after `message:`. A shared body may hold placeholders and whoever inserts it declares the parameters, so two publishers can send the same message under different bounds. **A shared message may not insert another**, so a `${name}` inside a body is always a parameter and never a second message.",
|
|
42
|
+
"defaultSnippets": [
|
|
43
|
+
{
|
|
44
|
+
"label": "a shared message",
|
|
45
|
+
"description": "One reusable body, with one parameter hole in it.",
|
|
46
|
+
"body": {
|
|
47
|
+
"${1:drive}": {
|
|
48
|
+
"linear": {
|
|
49
|
+
"x": "\\${speed}"
|
|
50
|
+
},
|
|
51
|
+
"angular": {
|
|
52
|
+
"z": 0
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"datapoints": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"propertyNames": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"minLength": 2,
|
|
64
|
+
"maxLength": 63,
|
|
65
|
+
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
66
|
+
"patternErrorMessage": "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore — `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused."
|
|
67
|
+
},
|
|
68
|
+
"additionalProperties": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"properties": {
|
|
71
|
+
"topic": {
|
|
72
|
+
"type": "string",
|
|
73
|
+
"maxLength": 255,
|
|
74
|
+
"pattern": "^\\/[A-Za-z_][A-Za-z0-9_]*(?:\\/[A-Za-z_][A-Za-z0-9_]*)*$",
|
|
75
|
+
"description": "The ROS topic this datapoint reads, as an absolute graph name. One datapoint reads **one** topic: a value assembled from two topics is not expressible here.",
|
|
76
|
+
"patternErrorMessage": "A ROS graph name is absolute: it begins with a slash, and each segment after a slash starts with a letter or an underscore and continues with letters, digits and underscores — `/camera/image_raw`. A relative name, a trailing slash, a dash or a dot is refused.",
|
|
77
|
+
"examples": [
|
|
78
|
+
"/battery"
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
"type": {
|
|
82
|
+
"type": "string",
|
|
83
|
+
"maxLength": 255,
|
|
84
|
+
"pattern": "^[a-z][a-z0-9_]*\\/(?:msg|srv|action)\\/[A-Za-z][A-Za-z0-9]*$",
|
|
85
|
+
"description": "The message type carried by `topic`, spelled the way ROS 2 spells it, with the `msg` segment in the middle — `sensor_msgs/msg/BatteryState`, never `sensor_msgs/BatteryState`. It is declared here rather than discovered, so a configuration can be written for a robot that has never been connected; the cloud checks it against the robot's own message definitions only once one is there.",
|
|
86
|
+
"patternErrorMessage": "A ROS 2 type name has three segments: the package, then `msg`, `srv` or `action`, then the type — `sensor_msgs/msg/BatteryState`, `std_srvs/srv/Trigger`, `nav2_msgs/action/NavigateToPose`. The middle segment is the one usually left out. The package is lower-case with underscores; the type itself is letters and digits, conventionally CamelCase.",
|
|
87
|
+
"examples": [
|
|
88
|
+
"sensor_msgs/msg/BatteryState"
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
"field": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"maxLength": 255,
|
|
94
|
+
"pattern": "^[a-z_][a-z0-9_]*(?:\\[\\d+\\])?(?:\\.[a-z_][a-z0-9_]*(?:\\[\\d+\\])?)*$",
|
|
95
|
+
"description": "A dotted path into the message naming the single value this datapoint carries, each segment indexing at most one array level — `ranges[0]`, never `ranges[0][1]`, because ROS 2 has no nested arrays. Without it the datapoint is the whole message, and `numeric`, `chart` and `alerts` are then refused.",
|
|
96
|
+
"patternErrorMessage": "A field path is dotted and lower-case, and each segment may index at most one array level — `voltage`, `pose.position.x`, `ranges[0]`. ROS 2 has no nested arrays, so a second index on one segment could name nothing that exists.",
|
|
97
|
+
"examples": [
|
|
98
|
+
"voltage",
|
|
99
|
+
"pose.position.x",
|
|
100
|
+
"ranges[0]"
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"rate_throttle_hz": {
|
|
104
|
+
"type": "number",
|
|
105
|
+
"minimum": 0,
|
|
106
|
+
"maximum": 20,
|
|
107
|
+
"description": "A ceiling on how often this datapoint is sent, in hertz. Omitted or `0` means no throttling. It is **a ceiling, not a clock**: a slow topic stays slow, a value is never repeated to manufacture a rate, and within a window the newest value wins. The bridge enforces it, so the robot's bandwidth is genuinely saved.",
|
|
108
|
+
"examples": [
|
|
109
|
+
2,
|
|
110
|
+
0.5
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
"description": {
|
|
114
|
+
"description": "Prose about what this value is, for whoever meets it in the console later. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all — but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
|
|
115
|
+
"examples": [
|
|
116
|
+
"What this value is, for whoever meets it in the console."
|
|
117
|
+
],
|
|
118
|
+
"type": "string",
|
|
119
|
+
"minLength": 1,
|
|
120
|
+
"maxLength": 2000
|
|
121
|
+
},
|
|
122
|
+
"numeric": {
|
|
123
|
+
"type": "object",
|
|
124
|
+
"properties": {
|
|
125
|
+
"scale": {
|
|
126
|
+
"type": "number",
|
|
127
|
+
"description": "A factor the robot multiplies the raw value by before sending it (`value * scale + offset`). The arithmetic happens once, at the source, so REST, realtime and history can never disagree about a number.",
|
|
128
|
+
"examples": [
|
|
129
|
+
100
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
"offset": {
|
|
133
|
+
"type": "number",
|
|
134
|
+
"description": "A constant the robot adds after `scale` (`value * scale + offset`), for a value whose zero sits in the wrong place. Like `scale` it is applied before sending, so history stores the converted value and a later correction cannot reach what is already stored.",
|
|
135
|
+
"examples": [
|
|
136
|
+
-273.15
|
|
137
|
+
]
|
|
138
|
+
},
|
|
139
|
+
"unit": {
|
|
140
|
+
"type": "string",
|
|
141
|
+
"maxLength": 32,
|
|
142
|
+
"description": "The unit of the value **after** `scale` and `offset`, not the robot's own. It is shown beside the value and carried by `robot_describe` as its own field, so a model does not have to guess whether 15 means percent, volts or minutes.",
|
|
143
|
+
"examples": [
|
|
144
|
+
"%"
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
"decimals": {
|
|
148
|
+
"type": "integer",
|
|
149
|
+
"minimum": 0,
|
|
150
|
+
"maximum": 6,
|
|
151
|
+
"description": "How many fraction digits the console shows the value with — value tile, chart axis and tooltip, and the datapoint detail page — and the number `robot_describe` reports as its own field, so a model formats the value the way the console does. Presentation only: the stored value keeps the precision it arrived with, and absent means the console's own default rather than zero.",
|
|
152
|
+
"examples": [
|
|
153
|
+
1
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"additionalProperties": false,
|
|
158
|
+
"description": "Arithmetic and formatting for a numeric value. `scale` and `offset` are applied **on the robot**, before sending, which is why REST, realtime and history all carry identical numbers. `unit` and `decimals` change nothing the robot does, so a publish that touches only those pushes no configuration.",
|
|
159
|
+
"defaultSnippets": [
|
|
160
|
+
{
|
|
161
|
+
"label": "a unit, and the arithmetic that produces it",
|
|
162
|
+
"description": "A 0..1 fraction sent as a percentage to one decimal. `scale` is applied on the robot before sending, so history stores the converted value and a later correction cannot reach what is already stored.",
|
|
163
|
+
"body": {
|
|
164
|
+
"scale": 100,
|
|
165
|
+
"unit": "\"%\"",
|
|
166
|
+
"decimals": 1
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
},
|
|
171
|
+
"retention": {
|
|
172
|
+
"type": "object",
|
|
173
|
+
"properties": {
|
|
174
|
+
"enabled": {
|
|
175
|
+
"type": "boolean",
|
|
176
|
+
"description": "Whether values are written to the time series and become queryable. Off by default: without it the value is live only, and nobody who was not watching will ever see it."
|
|
177
|
+
},
|
|
178
|
+
"interval_seconds": {
|
|
179
|
+
"type": "integer",
|
|
180
|
+
"minimum": 1,
|
|
181
|
+
"maximum": 3600,
|
|
182
|
+
"description": "How often a value is written to history, in seconds; absent means `300`. **Not** how often it is sent — that is `rate_throttle_hz`. Stored points are billed, so this is the direct lever on what a robot costs, and a bumper that is true for 200 ms does not appear unless a write falls inside it.",
|
|
183
|
+
"examples": [
|
|
184
|
+
300,
|
|
185
|
+
60
|
|
186
|
+
]
|
|
187
|
+
},
|
|
188
|
+
"max_buffer_values": {
|
|
189
|
+
"type": "integer",
|
|
190
|
+
"minimum": 1,
|
|
191
|
+
"maximum": 100000,
|
|
192
|
+
"description": "How many values the robot holds while the bridge is disconnected, to be pushed once it reconnects. The catch-up runs behind live telemetry and job results at a limited rate, so closing a gap never delays the present; without it the series simply has a gap, which is an honest answer.",
|
|
193
|
+
"examples": [
|
|
194
|
+
5000
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
"additionalProperties": false,
|
|
199
|
+
"description": "What outlives the moment: whether this value is written to the time series, how often, and how many points the robot buffers while the bridge is away. Absent means no history at all — the value is live only.",
|
|
200
|
+
"defaultSnippets": [
|
|
201
|
+
{
|
|
202
|
+
"label": "history, on, with its interval and buffer",
|
|
203
|
+
"description": "Writes this value to the time series every 300 seconds and holds 5000 points on the robot while the bridge is away. Stored points are billed, so both numbers are worth choosing rather than inheriting.",
|
|
204
|
+
"body": {
|
|
205
|
+
"enabled": true,
|
|
206
|
+
"interval_seconds": 300,
|
|
207
|
+
"max_buffer_values": 5000
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
},
|
|
212
|
+
"chart": {
|
|
213
|
+
"type": "object",
|
|
214
|
+
"properties": {
|
|
215
|
+
"y_min": {
|
|
216
|
+
"type": "number",
|
|
217
|
+
"description": "A fixed floor for the chart's y axis; omitted, the axis scales to the data. `0` is a real floor and is read as `0`, never as unset.",
|
|
218
|
+
"examples": [
|
|
219
|
+
0
|
|
220
|
+
]
|
|
221
|
+
},
|
|
222
|
+
"y_max": {
|
|
223
|
+
"type": "number",
|
|
224
|
+
"description": "A fixed ceiling for the chart's y axis; omitted, the axis scales to the data. It may not sit below `y_min`: a reversed pair is refused here because nothing downstream catches it, and the chart would render empty.",
|
|
225
|
+
"examples": [
|
|
226
|
+
100
|
|
227
|
+
]
|
|
228
|
+
},
|
|
229
|
+
"style": {
|
|
230
|
+
"type": "string",
|
|
231
|
+
"enum": [
|
|
232
|
+
"line",
|
|
233
|
+
"step"
|
|
234
|
+
],
|
|
235
|
+
"description": "How the drawing joins two samples, which is not a matter of taste. `line` claims the value moved evenly between them, roughly true of a temperature or a charge; `step` holds and then jumps, the only honest drawing for a mode, a switch or a counter, where a straight line would show values that never existed.",
|
|
236
|
+
"enumDescriptions": [
|
|
237
|
+
"Straight lines between samples, so the drawing claims the value moved evenly from one to the next. Right for a quantity that really is continuous — a temperature, a charge level — where a reading taken between two samples would have landed somewhere on that line.",
|
|
238
|
+
"Each value is held until the next one arrives, then jumps to it. Right for anything that does not slide between its values — a mode, a state, a switch, a counter — where a sloped line would draw readings the robot never reported."
|
|
239
|
+
]
|
|
240
|
+
},
|
|
241
|
+
"default_window_minutes": {
|
|
242
|
+
"type": "integer",
|
|
243
|
+
"minimum": 1,
|
|
244
|
+
"maximum": 43200,
|
|
245
|
+
"description": "How far back the chart reaches when it is first opened, in minutes; absent means `60`. Only the starting zoom: a viewer may look further, and nothing about what is stored follows from it.",
|
|
246
|
+
"examples": [
|
|
247
|
+
1440
|
|
248
|
+
]
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
"additionalProperties": false,
|
|
252
|
+
"description": "How the console draws this value over time: axis bounds, whether the line interpolates or steps, and the window a chart opens on. **Display only** — it changes no stored value, no alert and nothing the robot does, so a publish that touches only it pushes no configuration.",
|
|
253
|
+
"defaultSnippets": [
|
|
254
|
+
{
|
|
255
|
+
"label": "axis bounds, and how two samples are joined",
|
|
256
|
+
"description": "A fixed 0..100 axis rather than one that scales to the data, and a choice between interpolating and stepping between samples — which is not a matter of taste.",
|
|
257
|
+
"body": {
|
|
258
|
+
"y_min": 0,
|
|
259
|
+
"y_max": 100,
|
|
260
|
+
"style": "${1|line,step|}"
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
]
|
|
264
|
+
},
|
|
265
|
+
"alerts": {
|
|
266
|
+
"type": "object",
|
|
267
|
+
"propertyNames": {
|
|
268
|
+
"type": "string",
|
|
269
|
+
"minLength": 2,
|
|
270
|
+
"maxLength": 63,
|
|
271
|
+
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
272
|
+
"patternErrorMessage": "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore — `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused."
|
|
273
|
+
},
|
|
274
|
+
"additionalProperties": {
|
|
275
|
+
"type": "object",
|
|
276
|
+
"properties": {
|
|
277
|
+
"condition": {
|
|
278
|
+
"type": "object",
|
|
279
|
+
"properties": {
|
|
280
|
+
"fire_at": {
|
|
281
|
+
"anyOf": [
|
|
282
|
+
{
|
|
283
|
+
"type": "number"
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"type": "string"
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"type": "boolean"
|
|
290
|
+
}
|
|
291
|
+
],
|
|
292
|
+
"description": "The value at which the alert starts firing. Alone it is an **equality**: it fires while the value equals `fire_at` and is ok again as soon as it differs, which is what makes a boolean or a string condition meaningful. Adding `resolve_at` turns it into a threshold instead.",
|
|
293
|
+
"examples": [
|
|
294
|
+
15,
|
|
295
|
+
true
|
|
296
|
+
]
|
|
297
|
+
},
|
|
298
|
+
"resolve_at": {
|
|
299
|
+
"type": "number",
|
|
300
|
+
"description": "The value at which a firing alert becomes ok again — allowed only when `fire_at` is a number, and it **must differ from it**. That gap is the hysteresis, and it makes the condition a threshold whose direction follows from which of the two values is higher. Without a gap a value sitting on the line flips on every sample.",
|
|
301
|
+
"examples": [
|
|
302
|
+
18
|
|
303
|
+
]
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
"required": [
|
|
307
|
+
"fire_at"
|
|
308
|
+
],
|
|
309
|
+
"additionalProperties": false,
|
|
310
|
+
"description": "When this alert fires and when it is ok again. It carries **no discriminator**: upper threshold, lower threshold or equality all follow from the two values in it. Editing it resets the alert to `ok` on the next publish, while a publish that leaves it untouched keeps the running state.",
|
|
311
|
+
"defaultSnippets": [
|
|
312
|
+
{
|
|
313
|
+
"label": "a threshold, with its hysteresis",
|
|
314
|
+
"description": "Fires below 15 and is ok again above 18. The gap is what keeps a value sitting on the line from flipping on every sample; the direction follows from which of the two is higher, and nothing else declares it.",
|
|
315
|
+
"body": {
|
|
316
|
+
"fire_at": 15,
|
|
317
|
+
"resolve_at": 18
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"label": "an equality",
|
|
322
|
+
"description": "Fires while the value equals `fire_at` and is ok as soon as it differs — the only form a boolean or a string condition can take. No `resolve_at`: adding one would turn this into a threshold, and is refused unless `fire_at` is a number.",
|
|
323
|
+
"body": {
|
|
324
|
+
"fire_at": true
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
]
|
|
328
|
+
},
|
|
329
|
+
"severity": {
|
|
330
|
+
"type": "string",
|
|
331
|
+
"enum": [
|
|
332
|
+
"warning",
|
|
333
|
+
"error"
|
|
334
|
+
],
|
|
335
|
+
"description": "How bad it is when this alert fires; absent means `warning`. It changes no behaviour — nothing is escalated, retried or delivered differently — it travels with the org event and colours the alert wherever it is shown.",
|
|
336
|
+
"enumDescriptions": [
|
|
337
|
+
"Worth seeing. This is what an alert that names no severity gets.",
|
|
338
|
+
"Worth acting on. The only difference from `warning` is how the alert is shown: the same event is written, at the same moment, to the same places."
|
|
339
|
+
]
|
|
340
|
+
},
|
|
341
|
+
"name": {
|
|
342
|
+
"type": "string",
|
|
343
|
+
"minLength": 1,
|
|
344
|
+
"maxLength": 120,
|
|
345
|
+
"description": "A human-readable label shown wherever this alert appears, in place of its bare key. It is not the alert's identity — the key is — so the label can be reworded freely, while changing the key deletes one alert and creates another.",
|
|
346
|
+
"examples": [
|
|
347
|
+
"Battery low"
|
|
348
|
+
]
|
|
349
|
+
},
|
|
350
|
+
"enabled": {
|
|
351
|
+
"type": "boolean",
|
|
352
|
+
"description": "Whether this alert is evaluated at all. Absent means on, the opposite of `retention.enabled`: an alert that is written down watches unless it is explicitly switched off, which is how one is silenced without losing the key that identifies it."
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
"required": [
|
|
356
|
+
"condition"
|
|
357
|
+
],
|
|
358
|
+
"additionalProperties": false,
|
|
359
|
+
"defaultSnippets": [
|
|
360
|
+
{
|
|
361
|
+
"label": "an alert, with its condition",
|
|
362
|
+
"description": "One whole alert: the label shown in place of its key, and the threshold with the gap that keeps it from flipping on every sample.",
|
|
363
|
+
"body": {
|
|
364
|
+
"condition": {
|
|
365
|
+
"fire_at": 15,
|
|
366
|
+
"resolve_at": 18
|
|
367
|
+
},
|
|
368
|
+
"name": "${2:Battery low}"
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
]
|
|
372
|
+
},
|
|
373
|
+
"description": "Alerts watching this value, keyed by slug; each moves between `ok` and `firing` and writes an org event on every transition. No mail is sent. **The key is the identity**, so renaming an alert is a delete plus a create: its runtime state is lost, and an alert that is still true fires again.",
|
|
374
|
+
"defaultSnippets": [
|
|
375
|
+
{
|
|
376
|
+
"label": "an alert, with its condition",
|
|
377
|
+
"description": "One whole alert: the label shown in place of its key, and the threshold with the gap that keeps it from flipping on every sample.",
|
|
378
|
+
"body": {
|
|
379
|
+
"${1:battery_low}": {
|
|
380
|
+
"condition": {
|
|
381
|
+
"fire_at": 15,
|
|
382
|
+
"resolve_at": 18
|
|
383
|
+
},
|
|
384
|
+
"name": "${2:Battery low}"
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
]
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
"required": [
|
|
392
|
+
"topic",
|
|
393
|
+
"type"
|
|
394
|
+
],
|
|
395
|
+
"additionalProperties": false,
|
|
396
|
+
"defaultSnippets": [
|
|
397
|
+
{
|
|
398
|
+
"label": "a datapoint",
|
|
399
|
+
"description": "One value the robot publishes: one field of one topic.",
|
|
400
|
+
"body": {
|
|
401
|
+
"topic": "${2:/battery}",
|
|
402
|
+
"type": "${3:sensor_msgs/msg/BatteryState}",
|
|
403
|
+
"field": "${4:voltage}",
|
|
404
|
+
"description": "${5:What this value is, for whoever meets it in the console.}"
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
"label": "a numeric datapoint, with history and a chart",
|
|
409
|
+
"description": "A number with its unit, what is kept of it and how it is drawn — the blocks a plain datapoint leaves out.",
|
|
410
|
+
"body": {
|
|
411
|
+
"topic": "${2:/battery}",
|
|
412
|
+
"type": "${3:sensor_msgs/msg/BatteryState}",
|
|
413
|
+
"field": "${4:percentage}",
|
|
414
|
+
"description": "${5:What this value is, for whoever meets it in the console.}",
|
|
415
|
+
"numeric": {
|
|
416
|
+
"scale": 100,
|
|
417
|
+
"unit": "\"%\"",
|
|
418
|
+
"decimals": 1
|
|
419
|
+
},
|
|
420
|
+
"retention": {
|
|
421
|
+
"enabled": true,
|
|
422
|
+
"interval_seconds": 300
|
|
423
|
+
},
|
|
424
|
+
"chart": {
|
|
425
|
+
"y_min": 0,
|
|
426
|
+
"y_max": 100,
|
|
427
|
+
"style": "line"
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
]
|
|
432
|
+
},
|
|
433
|
+
"description": "Values the robot publishes, each one field of one topic or a whole topic, and **never several topics**. Keys are slugs, one namespace across all five exposure sections, which is what lets a role grant say `{robot, slug}` without naming a kind; `bridge_state`, `robot_details` and `bridge_pressure` are built-in, and `history` is reserved because `GET …/jobs/history` would shadow an action of that name; all four are refused when the document is validated.",
|
|
434
|
+
"defaultSnippets": [
|
|
435
|
+
{
|
|
436
|
+
"label": "a datapoint",
|
|
437
|
+
"description": "One value the robot publishes: one field of one topic.",
|
|
438
|
+
"body": {
|
|
439
|
+
"${1:battery_voltage}": {
|
|
440
|
+
"topic": "${2:/battery}",
|
|
441
|
+
"type": "${3:sensor_msgs/msg/BatteryState}",
|
|
442
|
+
"field": "${4:voltage}",
|
|
443
|
+
"description": "${5:What this value is, for whoever meets it in the console.}"
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
"label": "a numeric datapoint, with history and a chart",
|
|
449
|
+
"description": "A number with its unit, what is kept of it and how it is drawn — the blocks a plain datapoint leaves out.",
|
|
450
|
+
"body": {
|
|
451
|
+
"${1:battery}": {
|
|
452
|
+
"topic": "${2:/battery}",
|
|
453
|
+
"type": "${3:sensor_msgs/msg/BatteryState}",
|
|
454
|
+
"field": "${4:percentage}",
|
|
455
|
+
"description": "${5:What this value is, for whoever meets it in the console.}",
|
|
456
|
+
"numeric": {
|
|
457
|
+
"scale": 100,
|
|
458
|
+
"unit": "\"%\"",
|
|
459
|
+
"decimals": 1
|
|
460
|
+
},
|
|
461
|
+
"retention": {
|
|
462
|
+
"enabled": true,
|
|
463
|
+
"interval_seconds": 300
|
|
464
|
+
},
|
|
465
|
+
"chart": {
|
|
466
|
+
"y_min": 0,
|
|
467
|
+
"y_max": 100,
|
|
468
|
+
"style": "line"
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
]
|
|
474
|
+
},
|
|
475
|
+
"actions": {
|
|
476
|
+
"type": "object",
|
|
477
|
+
"propertyNames": {
|
|
478
|
+
"type": "string",
|
|
479
|
+
"minLength": 2,
|
|
480
|
+
"maxLength": 63,
|
|
481
|
+
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
482
|
+
"patternErrorMessage": "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore — `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused."
|
|
483
|
+
},
|
|
484
|
+
"additionalProperties": {
|
|
485
|
+
"type": "object",
|
|
486
|
+
"properties": {
|
|
487
|
+
"ros_name": {
|
|
488
|
+
"type": "string",
|
|
489
|
+
"maxLength": 255,
|
|
490
|
+
"pattern": "^\\/[A-Za-z_][A-Za-z0-9_]*(?:\\/[A-Za-z_][A-Za-z0-9_]*)*$",
|
|
491
|
+
"description": "The action server on the robot, as an absolute graph name — this is what the bridge sends the goal to. Clients never see it: they address this entry by its slug, so a server can be renamed on the robot without a single app changing.",
|
|
492
|
+
"patternErrorMessage": "A ROS graph name is absolute: it begins with a slash, and each segment after a slash starts with a letter or an underscore and continues with letters, digits and underscores — `/camera/image_raw`. A relative name, a trailing slash, a dash or a dot is refused.",
|
|
493
|
+
"examples": [
|
|
494
|
+
"/navigate_to_pose"
|
|
495
|
+
]
|
|
496
|
+
},
|
|
497
|
+
"type": {
|
|
498
|
+
"type": "string",
|
|
499
|
+
"maxLength": 255,
|
|
500
|
+
"pattern": "^[a-z][a-z0-9_]*\\/(?:msg|srv|action)\\/[A-Za-z][A-Za-z0-9]*$",
|
|
501
|
+
"description": "The action type `ros_name` implements, with the `action` segment in the middle — `nav2_msgs/action/NavigateToPose`, never `nav2_msgs/NavigateToPose`. Declared rather than introspected, so an action can be configured for a robot that has never connected; the cloud checks it against the robot's own definitions only once one is there.",
|
|
502
|
+
"patternErrorMessage": "A ROS 2 type name has three segments: the package, then `msg`, `srv` or `action`, then the type — `sensor_msgs/msg/BatteryState`, `std_srvs/srv/Trigger`, `nav2_msgs/action/NavigateToPose`. The middle segment is the one usually left out. The package is lower-case with underscores; the type itself is letters and digits, conventionally CamelCase.",
|
|
503
|
+
"examples": [
|
|
504
|
+
"nav2_msgs/action/NavigateToPose"
|
|
505
|
+
]
|
|
506
|
+
},
|
|
507
|
+
"message": {
|
|
508
|
+
"description": "The message as it will be sent, written out in full: literals are fixed, `${name}` is a hole a caller fills, and a field written `0.0` is one no client can change. Directly after `message:` a `${name}` standing alone names a shared message instead; anywhere inside a body it is a parameter. `null` is refused **at every depth** — omitting a key is the only spelling of \"not set\"."
|
|
509
|
+
},
|
|
510
|
+
"parameters": {
|
|
511
|
+
"type": "object",
|
|
512
|
+
"propertyNames": {
|
|
513
|
+
"type": "string",
|
|
514
|
+
"minLength": 2,
|
|
515
|
+
"maxLength": 63,
|
|
516
|
+
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
517
|
+
"patternErrorMessage": "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore — `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused."
|
|
518
|
+
},
|
|
519
|
+
"additionalProperties": {
|
|
520
|
+
"type": "object",
|
|
521
|
+
"properties": {
|
|
522
|
+
"type": {
|
|
523
|
+
"type": "string",
|
|
524
|
+
"enum": [
|
|
525
|
+
"bool",
|
|
526
|
+
"byte",
|
|
527
|
+
"char",
|
|
528
|
+
"int8",
|
|
529
|
+
"uint8",
|
|
530
|
+
"int16",
|
|
531
|
+
"uint16",
|
|
532
|
+
"int32",
|
|
533
|
+
"uint32",
|
|
534
|
+
"int64",
|
|
535
|
+
"uint64",
|
|
536
|
+
"float32",
|
|
537
|
+
"float64",
|
|
538
|
+
"string",
|
|
539
|
+
"wstring"
|
|
540
|
+
],
|
|
541
|
+
"description": "The ROS 2 primitive a value of this parameter must be, spelled the way ROS 2 spells it — `float64`, not `double`. It **decides which other constraints are allowed at all**: `min_value` and `max_value` need a numeric type, `regex` needs a string one, and a constraint on the wrong type is refused rather than quietly ignored.",
|
|
542
|
+
"enumDescriptions": [
|
|
543
|
+
"A `true`/`false` flag. The one type that takes no constraint at all: no bounds, no `regex`, no `enum`.",
|
|
544
|
+
"One raw octet, `0` to `255`, carrying no character meaning. It counts as an integer here, so bounds and an `enum` apply to it.",
|
|
545
|
+
"A single-octet character code, `0` to `255`. ROS 2 keeps it apart from `byte` although the width is the same, and it travels as a number rather than as a one-character string.",
|
|
546
|
+
"A whole number from `-128` to `127`.",
|
|
547
|
+
"A whole number from `0` to `255`.",
|
|
548
|
+
"A whole number from `-32768` to `32767`.",
|
|
549
|
+
"A whole number from `0` to `65535`.",
|
|
550
|
+
"A whole number from `-2147483648` to `2147483647` — the usual choice for a count or an index.",
|
|
551
|
+
"A whole number from `0` to `4294967295`.",
|
|
552
|
+
"A whole number from `-9223372036854775808` to `9223372036854775807`.",
|
|
553
|
+
"A whole number from `0` to `18446744073709551615`.",
|
|
554
|
+
"A single-precision number, roughly seven significant digits.",
|
|
555
|
+
"A double-precision number, roughly fifteen significant digits. This is what other languages call `double`; ROS 2 spells it `float64` and so does this field.",
|
|
556
|
+
"Text, carried as UTF-8. One of the two types a `regex` may constrain.",
|
|
557
|
+
"Text as wide characters, and rare — nearly every ROS 2 interface uses `string`. It takes a `regex` on the same terms."
|
|
558
|
+
]
|
|
559
|
+
},
|
|
560
|
+
"default": {
|
|
561
|
+
"anyOf": [
|
|
562
|
+
{
|
|
563
|
+
"type": "number"
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
"type": "string"
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
"type": "boolean"
|
|
570
|
+
}
|
|
571
|
+
],
|
|
572
|
+
"description": "The value used when a caller omits this parameter: **without a `default` the parameter is required**, because the message cannot be built without it. It must itself satisfy `min_value`, `max_value`, `enum` and `regex` — a default the constraints reject is refused here rather than becoming the one value that reaches the robot unchecked."
|
|
573
|
+
},
|
|
574
|
+
"min_value": {
|
|
575
|
+
"type": "number",
|
|
576
|
+
"description": "The lowest value a caller may send; numeric types only. It is **enforced in the cloud, before anything reaches the robot** — this is where a speed limit actually holds, rather than in the app that is supposed to respect it.",
|
|
577
|
+
"examples": [
|
|
578
|
+
-0.5
|
|
579
|
+
]
|
|
580
|
+
},
|
|
581
|
+
"max_value": {
|
|
582
|
+
"type": "number",
|
|
583
|
+
"description": "The highest value a caller may send; numeric types only, and it may not sit below `min_value`. A reversed pair is refused at parse time, because nothing downstream catches it and every call would then fail against a bound no value can satisfy.",
|
|
584
|
+
"examples": [
|
|
585
|
+
0.5
|
|
586
|
+
]
|
|
587
|
+
},
|
|
588
|
+
"enum": {
|
|
589
|
+
"minItems": 1,
|
|
590
|
+
"type": "array",
|
|
591
|
+
"items": {
|
|
592
|
+
"anyOf": [
|
|
593
|
+
{
|
|
594
|
+
"type": "string"
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
"type": "number"
|
|
598
|
+
}
|
|
599
|
+
]
|
|
600
|
+
},
|
|
601
|
+
"description": "The complete set of values a caller may send. Integer and string types only — **never a float**, because equality on floating point is unreliable and an enumerated float list is a trap that only shows up in operation. Every entry must match `type`, and a `default` must be one of them."
|
|
602
|
+
},
|
|
603
|
+
"regex": {
|
|
604
|
+
"type": "string",
|
|
605
|
+
"minLength": 1,
|
|
606
|
+
"description": "A pattern the value must match; string types only. It is compiled as a JavaScript regular expression and is **not anchored**, so `[a-z]+` accepts any value that merely contains a lowercase run — a pattern meant to cover the whole value writes its own `^` and `$`.",
|
|
607
|
+
"examples": [
|
|
608
|
+
"^[a-z_]+$"
|
|
609
|
+
]
|
|
610
|
+
},
|
|
611
|
+
"description": {
|
|
612
|
+
"description": "What this parameter means, in the developer's own words, and documentation only — the robot does nothing with it. It travels into the input schema `robot_describe` publishes for this call, beside the bounds, so `type` and the range say what the value *is* and this is the only place that says what it *does*.",
|
|
613
|
+
"examples": [
|
|
614
|
+
"What a caller is choosing when they set this."
|
|
615
|
+
],
|
|
616
|
+
"type": "string",
|
|
617
|
+
"minLength": 1,
|
|
618
|
+
"maxLength": 500
|
|
619
|
+
}
|
|
620
|
+
},
|
|
621
|
+
"required": [
|
|
622
|
+
"type"
|
|
623
|
+
],
|
|
624
|
+
"additionalProperties": false,
|
|
625
|
+
"defaultSnippets": [
|
|
626
|
+
{
|
|
627
|
+
"label": "a parameter, with its bounds",
|
|
628
|
+
"description": "One hole a caller fills: what type it is, what values it may take, and what it means. Without a `default` it is required, and the bounds are enforced in the cloud before anything reaches the robot.",
|
|
629
|
+
"body": {
|
|
630
|
+
"type": "${2:float64}",
|
|
631
|
+
"min_value": -0.5,
|
|
632
|
+
"max_value": 0.5,
|
|
633
|
+
"description": "${3:What a caller is choosing when they set this.}"
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
]
|
|
637
|
+
},
|
|
638
|
+
"description": "The holes in this entry's `message` that a caller fills, keyed by **parameter name** rather than by field path — so the name survives the field moving inside the message, and a caller sends something that means what it says. Every declared parameter must appear somewhere in the message and every `${name}` in the message must be declared; either half alone is an error.",
|
|
639
|
+
"defaultSnippets": [
|
|
640
|
+
{
|
|
641
|
+
"label": "a parameter, with its bounds",
|
|
642
|
+
"description": "One hole a caller fills: what type it is, what values it may take, and what it means. Without a `default` it is required, and the bounds are enforced in the cloud before anything reaches the robot.",
|
|
643
|
+
"body": {
|
|
644
|
+
"${1:speed}": {
|
|
645
|
+
"type": "${2:float64}",
|
|
646
|
+
"min_value": -0.5,
|
|
647
|
+
"max_value": 0.5,
|
|
648
|
+
"description": "${3:What a caller is choosing when they set this.}"
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
]
|
|
653
|
+
},
|
|
654
|
+
"description": {
|
|
655
|
+
"description": "What this action does, in the developer's own words — documentation for the console and for MCP clients, which is all it is: the robot does nothing with it. It is carried verbatim into `robot_describe` and read by a model that has never seen this robot. The action is offered whenever the role grants it; without one it is offered with `description: null`, and the model has nothing but the slug.",
|
|
656
|
+
"examples": [
|
|
657
|
+
"Drives to a target pose on the map."
|
|
658
|
+
],
|
|
659
|
+
"type": "string",
|
|
660
|
+
"minLength": 1,
|
|
661
|
+
"maxLength": 2000
|
|
662
|
+
}
|
|
663
|
+
},
|
|
664
|
+
"required": [
|
|
665
|
+
"ros_name",
|
|
666
|
+
"type"
|
|
667
|
+
],
|
|
668
|
+
"additionalProperties": false,
|
|
669
|
+
"defaultSnippets": [
|
|
670
|
+
{
|
|
671
|
+
"label": "an action",
|
|
672
|
+
"description": "One thing the robot does on request, reported as a job with progress.",
|
|
673
|
+
"body": {
|
|
674
|
+
"ros_name": "${2:/navigate_to_pose}",
|
|
675
|
+
"type": "${3:nav2_msgs/action/NavigateToPose}",
|
|
676
|
+
"description": "${4:Drives to a target pose on the map.}"
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
]
|
|
680
|
+
},
|
|
681
|
+
"description": "Things the robot does on request that take time, each reported as a job with progress. **At most one job runs per action slug**: a second call is refused `busy`, and every observer of that slug watches the same job. Keys are slugs, one namespace across all five exposure sections, which is what lets a role grant say `{robot, slug}` without naming a kind; `bridge_state`, `robot_details` and `bridge_pressure` are built-in, and `history` is reserved because `GET …/jobs/history` would shadow an action of that name; all four are refused when the document is validated.",
|
|
682
|
+
"defaultSnippets": [
|
|
683
|
+
{
|
|
684
|
+
"label": "an action",
|
|
685
|
+
"description": "One thing the robot does on request, reported as a job with progress.",
|
|
686
|
+
"body": {
|
|
687
|
+
"${1:navigate}": {
|
|
688
|
+
"ros_name": "${2:/navigate_to_pose}",
|
|
689
|
+
"type": "${3:nav2_msgs/action/NavigateToPose}",
|
|
690
|
+
"description": "${4:Drives to a target pose on the map.}"
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
]
|
|
695
|
+
},
|
|
696
|
+
"services": {
|
|
697
|
+
"type": "object",
|
|
698
|
+
"propertyNames": {
|
|
699
|
+
"type": "string",
|
|
700
|
+
"minLength": 2,
|
|
701
|
+
"maxLength": 63,
|
|
702
|
+
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
703
|
+
"patternErrorMessage": "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore — `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused."
|
|
704
|
+
},
|
|
705
|
+
"additionalProperties": {
|
|
706
|
+
"type": "object",
|
|
707
|
+
"properties": {
|
|
708
|
+
"ros_name": {
|
|
709
|
+
"type": "string",
|
|
710
|
+
"maxLength": 255,
|
|
711
|
+
"pattern": "^\\/[A-Za-z_][A-Za-z0-9_]*(?:\\/[A-Za-z_][A-Za-z0-9_]*)*$",
|
|
712
|
+
"description": "The ROS service the robot answers on, as an absolute graph name. The call is one request and one reply with no progress in between, so whatever this service does has to finish inside that reply; anything long-running belongs in `actions`.",
|
|
713
|
+
"patternErrorMessage": "A ROS graph name is absolute: it begins with a slash, and each segment after a slash starts with a letter or an underscore and continues with letters, digits and underscores — `/camera/image_raw`. A relative name, a trailing slash, a dash or a dot is refused.",
|
|
714
|
+
"examples": [
|
|
715
|
+
"/reset_odometry"
|
|
716
|
+
]
|
|
717
|
+
},
|
|
718
|
+
"type": {
|
|
719
|
+
"type": "string",
|
|
720
|
+
"maxLength": 255,
|
|
721
|
+
"pattern": "^[a-z][a-z0-9_]*\\/(?:msg|srv|action)\\/[A-Za-z][A-Za-z0-9]*$",
|
|
722
|
+
"description": "The service type `ros_name` implements, with the `srv` segment in the middle — `std_srvs/srv/Trigger`. A type whose request has no fields, like `Trigger`, needs neither `message` nor `parameters`: there is nothing to fill.",
|
|
723
|
+
"patternErrorMessage": "A ROS 2 type name has three segments: the package, then `msg`, `srv` or `action`, then the type — `sensor_msgs/msg/BatteryState`, `std_srvs/srv/Trigger`, `nav2_msgs/action/NavigateToPose`. The middle segment is the one usually left out. The package is lower-case with underscores; the type itself is letters and digits, conventionally CamelCase.",
|
|
724
|
+
"examples": [
|
|
725
|
+
"std_srvs/srv/Trigger"
|
|
726
|
+
]
|
|
727
|
+
},
|
|
728
|
+
"message": {
|
|
729
|
+
"description": "The message as it will be sent, written out in full: literals are fixed, `${name}` is a hole a caller fills, and a field written `0.0` is one no client can change. Directly after `message:` a `${name}` standing alone names a shared message instead; anywhere inside a body it is a parameter. `null` is refused **at every depth** — omitting a key is the only spelling of \"not set\"."
|
|
730
|
+
},
|
|
731
|
+
"parameters": {
|
|
732
|
+
"type": "object",
|
|
733
|
+
"propertyNames": {
|
|
734
|
+
"type": "string",
|
|
735
|
+
"minLength": 2,
|
|
736
|
+
"maxLength": 63,
|
|
737
|
+
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
738
|
+
"patternErrorMessage": "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore — `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused."
|
|
739
|
+
},
|
|
740
|
+
"additionalProperties": {
|
|
741
|
+
"type": "object",
|
|
742
|
+
"properties": {
|
|
743
|
+
"type": {
|
|
744
|
+
"type": "string",
|
|
745
|
+
"enum": [
|
|
746
|
+
"bool",
|
|
747
|
+
"byte",
|
|
748
|
+
"char",
|
|
749
|
+
"int8",
|
|
750
|
+
"uint8",
|
|
751
|
+
"int16",
|
|
752
|
+
"uint16",
|
|
753
|
+
"int32",
|
|
754
|
+
"uint32",
|
|
755
|
+
"int64",
|
|
756
|
+
"uint64",
|
|
757
|
+
"float32",
|
|
758
|
+
"float64",
|
|
759
|
+
"string",
|
|
760
|
+
"wstring"
|
|
761
|
+
],
|
|
762
|
+
"description": "The ROS 2 primitive a value of this parameter must be, spelled the way ROS 2 spells it — `float64`, not `double`. It **decides which other constraints are allowed at all**: `min_value` and `max_value` need a numeric type, `regex` needs a string one, and a constraint on the wrong type is refused rather than quietly ignored.",
|
|
763
|
+
"enumDescriptions": [
|
|
764
|
+
"A `true`/`false` flag. The one type that takes no constraint at all: no bounds, no `regex`, no `enum`.",
|
|
765
|
+
"One raw octet, `0` to `255`, carrying no character meaning. It counts as an integer here, so bounds and an `enum` apply to it.",
|
|
766
|
+
"A single-octet character code, `0` to `255`. ROS 2 keeps it apart from `byte` although the width is the same, and it travels as a number rather than as a one-character string.",
|
|
767
|
+
"A whole number from `-128` to `127`.",
|
|
768
|
+
"A whole number from `0` to `255`.",
|
|
769
|
+
"A whole number from `-32768` to `32767`.",
|
|
770
|
+
"A whole number from `0` to `65535`.",
|
|
771
|
+
"A whole number from `-2147483648` to `2147483647` — the usual choice for a count or an index.",
|
|
772
|
+
"A whole number from `0` to `4294967295`.",
|
|
773
|
+
"A whole number from `-9223372036854775808` to `9223372036854775807`.",
|
|
774
|
+
"A whole number from `0` to `18446744073709551615`.",
|
|
775
|
+
"A single-precision number, roughly seven significant digits.",
|
|
776
|
+
"A double-precision number, roughly fifteen significant digits. This is what other languages call `double`; ROS 2 spells it `float64` and so does this field.",
|
|
777
|
+
"Text, carried as UTF-8. One of the two types a `regex` may constrain.",
|
|
778
|
+
"Text as wide characters, and rare — nearly every ROS 2 interface uses `string`. It takes a `regex` on the same terms."
|
|
779
|
+
]
|
|
780
|
+
},
|
|
781
|
+
"default": {
|
|
782
|
+
"anyOf": [
|
|
783
|
+
{
|
|
784
|
+
"type": "number"
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
"type": "string"
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
"type": "boolean"
|
|
791
|
+
}
|
|
792
|
+
],
|
|
793
|
+
"description": "The value used when a caller omits this parameter: **without a `default` the parameter is required**, because the message cannot be built without it. It must itself satisfy `min_value`, `max_value`, `enum` and `regex` — a default the constraints reject is refused here rather than becoming the one value that reaches the robot unchecked."
|
|
794
|
+
},
|
|
795
|
+
"min_value": {
|
|
796
|
+
"type": "number",
|
|
797
|
+
"description": "The lowest value a caller may send; numeric types only. It is **enforced in the cloud, before anything reaches the robot** — this is where a speed limit actually holds, rather than in the app that is supposed to respect it.",
|
|
798
|
+
"examples": [
|
|
799
|
+
-0.5
|
|
800
|
+
]
|
|
801
|
+
},
|
|
802
|
+
"max_value": {
|
|
803
|
+
"type": "number",
|
|
804
|
+
"description": "The highest value a caller may send; numeric types only, and it may not sit below `min_value`. A reversed pair is refused at parse time, because nothing downstream catches it and every call would then fail against a bound no value can satisfy.",
|
|
805
|
+
"examples": [
|
|
806
|
+
0.5
|
|
807
|
+
]
|
|
808
|
+
},
|
|
809
|
+
"enum": {
|
|
810
|
+
"minItems": 1,
|
|
811
|
+
"type": "array",
|
|
812
|
+
"items": {
|
|
813
|
+
"anyOf": [
|
|
814
|
+
{
|
|
815
|
+
"type": "string"
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
"type": "number"
|
|
819
|
+
}
|
|
820
|
+
]
|
|
821
|
+
},
|
|
822
|
+
"description": "The complete set of values a caller may send. Integer and string types only — **never a float**, because equality on floating point is unreliable and an enumerated float list is a trap that only shows up in operation. Every entry must match `type`, and a `default` must be one of them."
|
|
823
|
+
},
|
|
824
|
+
"regex": {
|
|
825
|
+
"type": "string",
|
|
826
|
+
"minLength": 1,
|
|
827
|
+
"description": "A pattern the value must match; string types only. It is compiled as a JavaScript regular expression and is **not anchored**, so `[a-z]+` accepts any value that merely contains a lowercase run — a pattern meant to cover the whole value writes its own `^` and `$`.",
|
|
828
|
+
"examples": [
|
|
829
|
+
"^[a-z_]+$"
|
|
830
|
+
]
|
|
831
|
+
},
|
|
832
|
+
"description": {
|
|
833
|
+
"description": "What this parameter means, in the developer's own words, and documentation only — the robot does nothing with it. It travels into the input schema `robot_describe` publishes for this call, beside the bounds, so `type` and the range say what the value *is* and this is the only place that says what it *does*.",
|
|
834
|
+
"examples": [
|
|
835
|
+
"What a caller is choosing when they set this."
|
|
836
|
+
],
|
|
837
|
+
"type": "string",
|
|
838
|
+
"minLength": 1,
|
|
839
|
+
"maxLength": 500
|
|
840
|
+
}
|
|
841
|
+
},
|
|
842
|
+
"required": [
|
|
843
|
+
"type"
|
|
844
|
+
],
|
|
845
|
+
"additionalProperties": false,
|
|
846
|
+
"defaultSnippets": [
|
|
847
|
+
{
|
|
848
|
+
"label": "a parameter, with its bounds",
|
|
849
|
+
"description": "One hole a caller fills: what type it is, what values it may take, and what it means. Without a `default` it is required, and the bounds are enforced in the cloud before anything reaches the robot.",
|
|
850
|
+
"body": {
|
|
851
|
+
"type": "${2:float64}",
|
|
852
|
+
"min_value": -0.5,
|
|
853
|
+
"max_value": 0.5,
|
|
854
|
+
"description": "${3:What a caller is choosing when they set this.}"
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
]
|
|
858
|
+
},
|
|
859
|
+
"description": "The holes in this entry's `message` that a caller fills, keyed by **parameter name** rather than by field path — so the name survives the field moving inside the message, and a caller sends something that means what it says. Every declared parameter must appear somewhere in the message and every `${name}` in the message must be declared; either half alone is an error.",
|
|
860
|
+
"defaultSnippets": [
|
|
861
|
+
{
|
|
862
|
+
"label": "a parameter, with its bounds",
|
|
863
|
+
"description": "One hole a caller fills: what type it is, what values it may take, and what it means. Without a `default` it is required, and the bounds are enforced in the cloud before anything reaches the robot.",
|
|
864
|
+
"body": {
|
|
865
|
+
"${1:speed}": {
|
|
866
|
+
"type": "${2:float64}",
|
|
867
|
+
"min_value": -0.5,
|
|
868
|
+
"max_value": 0.5,
|
|
869
|
+
"description": "${3:What a caller is choosing when they set this.}"
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
]
|
|
874
|
+
},
|
|
875
|
+
"description": {
|
|
876
|
+
"description": "What this service does, in the developer's own words. The robot does nothing with it — the readers are the console and MCP clients, and without one the service is still offered, with `description: null`, exactly as for an action. It sits on the configuration rather than on the app, so one wording is true for every app that reaches this robot.",
|
|
877
|
+
"examples": [
|
|
878
|
+
"Resets odometry to the origin."
|
|
879
|
+
],
|
|
880
|
+
"type": "string",
|
|
881
|
+
"minLength": 1,
|
|
882
|
+
"maxLength": 2000
|
|
883
|
+
}
|
|
884
|
+
},
|
|
885
|
+
"required": [
|
|
886
|
+
"ros_name",
|
|
887
|
+
"type"
|
|
888
|
+
],
|
|
889
|
+
"additionalProperties": false,
|
|
890
|
+
"defaultSnippets": [
|
|
891
|
+
{
|
|
892
|
+
"label": "a service",
|
|
893
|
+
"description": "One request, one reply, no progress in between.",
|
|
894
|
+
"body": {
|
|
895
|
+
"ros_name": "${2:/reset_odometry}",
|
|
896
|
+
"type": "${3:std_srvs/srv/Trigger}",
|
|
897
|
+
"description": "${4:Resets odometry to the origin.}"
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
]
|
|
901
|
+
},
|
|
902
|
+
"description": "ROS service calls the robot answers — one request, one reply. Unlike an action a service reports **no progress** and the call returns with its result already on the job, so there is nothing left to observe; a second concurrent call is still refused `busy`, exactly as for an action. Keys are slugs, one namespace across all five exposure sections, which is what lets a role grant say `{robot, slug}` without naming a kind; `bridge_state`, `robot_details` and `bridge_pressure` are built-in, and `history` is reserved because `GET …/jobs/history` would shadow an action of that name; all four are refused when the document is validated.",
|
|
903
|
+
"defaultSnippets": [
|
|
904
|
+
{
|
|
905
|
+
"label": "a service",
|
|
906
|
+
"description": "One request, one reply, no progress in between.",
|
|
907
|
+
"body": {
|
|
908
|
+
"${1:reset_odometry}": {
|
|
909
|
+
"ros_name": "${2:/reset_odometry}",
|
|
910
|
+
"type": "${3:std_srvs/srv/Trigger}",
|
|
911
|
+
"description": "${4:Resets odometry to the origin.}"
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
]
|
|
916
|
+
},
|
|
917
|
+
"publishers": {
|
|
918
|
+
"type": "object",
|
|
919
|
+
"propertyNames": {
|
|
920
|
+
"type": "string",
|
|
921
|
+
"minLength": 2,
|
|
922
|
+
"maxLength": 63,
|
|
923
|
+
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
924
|
+
"patternErrorMessage": "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore — `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused."
|
|
925
|
+
},
|
|
926
|
+
"additionalProperties": {
|
|
927
|
+
"type": "object",
|
|
928
|
+
"properties": {
|
|
929
|
+
"topic": {
|
|
930
|
+
"type": "string",
|
|
931
|
+
"maxLength": 255,
|
|
932
|
+
"pattern": "^\\/[A-Za-z_][A-Za-z0-9_]*(?:\\/[A-Za-z_][A-Za-z0-9_]*)*$",
|
|
933
|
+
"description": "The ROS topic the message is published onto, as an absolute graph name. **No client ever names a topic**: a caller addresses this entry by its slug, so the topics an app can write to are exactly the ones written in this file.",
|
|
934
|
+
"patternErrorMessage": "A ROS graph name is absolute: it begins with a slash, and each segment after a slash starts with a letter or an underscore and continues with letters, digits and underscores — `/camera/image_raw`. A relative name, a trailing slash, a dash or a dot is refused.",
|
|
935
|
+
"examples": [
|
|
936
|
+
"/cmd_vel"
|
|
937
|
+
]
|
|
938
|
+
},
|
|
939
|
+
"type": {
|
|
940
|
+
"type": "string",
|
|
941
|
+
"maxLength": 255,
|
|
942
|
+
"pattern": "^[a-z][a-z0-9_]*\\/(?:msg|srv|action)\\/[A-Za-z][A-Za-z0-9]*$",
|
|
943
|
+
"description": "The message type of `topic`, spelled the way ROS 2 spells it, with the `msg` segment. It fixes the shape that `message` and `failsafe.message` must both fill, which is why one publisher carries one type and a second type needs a second publisher.",
|
|
944
|
+
"patternErrorMessage": "A ROS 2 type name has three segments: the package, then `msg`, `srv` or `action`, then the type — `sensor_msgs/msg/BatteryState`, `std_srvs/srv/Trigger`, `nav2_msgs/action/NavigateToPose`. The middle segment is the one usually left out. The package is lower-case with underscores; the type itself is letters and digits, conventionally CamelCase.",
|
|
945
|
+
"examples": [
|
|
946
|
+
"geometry_msgs/msg/Twist"
|
|
947
|
+
]
|
|
948
|
+
},
|
|
949
|
+
"message": {
|
|
950
|
+
"description": "The message as it will be sent, written out in full: literals are fixed, `${name}` is a hole a caller fills, and a field written `0.0` is one no client can change. Directly after `message:` a `${name}` standing alone names a shared message instead; anywhere inside a body it is a parameter. `null` is refused **at every depth** — omitting a key is the only spelling of \"not set\"."
|
|
951
|
+
},
|
|
952
|
+
"parameters": {
|
|
953
|
+
"type": "object",
|
|
954
|
+
"propertyNames": {
|
|
955
|
+
"type": "string",
|
|
956
|
+
"minLength": 2,
|
|
957
|
+
"maxLength": 63,
|
|
958
|
+
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
959
|
+
"patternErrorMessage": "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore — `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused."
|
|
960
|
+
},
|
|
961
|
+
"additionalProperties": {
|
|
962
|
+
"type": "object",
|
|
963
|
+
"properties": {
|
|
964
|
+
"type": {
|
|
965
|
+
"type": "string",
|
|
966
|
+
"enum": [
|
|
967
|
+
"bool",
|
|
968
|
+
"byte",
|
|
969
|
+
"char",
|
|
970
|
+
"int8",
|
|
971
|
+
"uint8",
|
|
972
|
+
"int16",
|
|
973
|
+
"uint16",
|
|
974
|
+
"int32",
|
|
975
|
+
"uint32",
|
|
976
|
+
"int64",
|
|
977
|
+
"uint64",
|
|
978
|
+
"float32",
|
|
979
|
+
"float64",
|
|
980
|
+
"string",
|
|
981
|
+
"wstring"
|
|
982
|
+
],
|
|
983
|
+
"description": "The ROS 2 primitive a value of this parameter must be, spelled the way ROS 2 spells it — `float64`, not `double`. It **decides which other constraints are allowed at all**: `min_value` and `max_value` need a numeric type, `regex` needs a string one, and a constraint on the wrong type is refused rather than quietly ignored.",
|
|
984
|
+
"enumDescriptions": [
|
|
985
|
+
"A `true`/`false` flag. The one type that takes no constraint at all: no bounds, no `regex`, no `enum`.",
|
|
986
|
+
"One raw octet, `0` to `255`, carrying no character meaning. It counts as an integer here, so bounds and an `enum` apply to it.",
|
|
987
|
+
"A single-octet character code, `0` to `255`. ROS 2 keeps it apart from `byte` although the width is the same, and it travels as a number rather than as a one-character string.",
|
|
988
|
+
"A whole number from `-128` to `127`.",
|
|
989
|
+
"A whole number from `0` to `255`.",
|
|
990
|
+
"A whole number from `-32768` to `32767`.",
|
|
991
|
+
"A whole number from `0` to `65535`.",
|
|
992
|
+
"A whole number from `-2147483648` to `2147483647` — the usual choice for a count or an index.",
|
|
993
|
+
"A whole number from `0` to `4294967295`.",
|
|
994
|
+
"A whole number from `-9223372036854775808` to `9223372036854775807`.",
|
|
995
|
+
"A whole number from `0` to `18446744073709551615`.",
|
|
996
|
+
"A single-precision number, roughly seven significant digits.",
|
|
997
|
+
"A double-precision number, roughly fifteen significant digits. This is what other languages call `double`; ROS 2 spells it `float64` and so does this field.",
|
|
998
|
+
"Text, carried as UTF-8. One of the two types a `regex` may constrain.",
|
|
999
|
+
"Text as wide characters, and rare — nearly every ROS 2 interface uses `string`. It takes a `regex` on the same terms."
|
|
1000
|
+
]
|
|
1001
|
+
},
|
|
1002
|
+
"default": {
|
|
1003
|
+
"anyOf": [
|
|
1004
|
+
{
|
|
1005
|
+
"type": "number"
|
|
1006
|
+
},
|
|
1007
|
+
{
|
|
1008
|
+
"type": "string"
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
"type": "boolean"
|
|
1012
|
+
}
|
|
1013
|
+
],
|
|
1014
|
+
"description": "The value used when a caller omits this parameter: **without a `default` the parameter is required**, because the message cannot be built without it. It must itself satisfy `min_value`, `max_value`, `enum` and `regex` — a default the constraints reject is refused here rather than becoming the one value that reaches the robot unchecked."
|
|
1015
|
+
},
|
|
1016
|
+
"min_value": {
|
|
1017
|
+
"type": "number",
|
|
1018
|
+
"description": "The lowest value a caller may send; numeric types only. It is **enforced in the cloud, before anything reaches the robot** — this is where a speed limit actually holds, rather than in the app that is supposed to respect it.",
|
|
1019
|
+
"examples": [
|
|
1020
|
+
-0.5
|
|
1021
|
+
]
|
|
1022
|
+
},
|
|
1023
|
+
"max_value": {
|
|
1024
|
+
"type": "number",
|
|
1025
|
+
"description": "The highest value a caller may send; numeric types only, and it may not sit below `min_value`. A reversed pair is refused at parse time, because nothing downstream catches it and every call would then fail against a bound no value can satisfy.",
|
|
1026
|
+
"examples": [
|
|
1027
|
+
0.5
|
|
1028
|
+
]
|
|
1029
|
+
},
|
|
1030
|
+
"enum": {
|
|
1031
|
+
"minItems": 1,
|
|
1032
|
+
"type": "array",
|
|
1033
|
+
"items": {
|
|
1034
|
+
"anyOf": [
|
|
1035
|
+
{
|
|
1036
|
+
"type": "string"
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
"type": "number"
|
|
1040
|
+
}
|
|
1041
|
+
]
|
|
1042
|
+
},
|
|
1043
|
+
"description": "The complete set of values a caller may send. Integer and string types only — **never a float**, because equality on floating point is unreliable and an enumerated float list is a trap that only shows up in operation. Every entry must match `type`, and a `default` must be one of them."
|
|
1044
|
+
},
|
|
1045
|
+
"regex": {
|
|
1046
|
+
"type": "string",
|
|
1047
|
+
"minLength": 1,
|
|
1048
|
+
"description": "A pattern the value must match; string types only. It is compiled as a JavaScript regular expression and is **not anchored**, so `[a-z]+` accepts any value that merely contains a lowercase run — a pattern meant to cover the whole value writes its own `^` and `$`.",
|
|
1049
|
+
"examples": [
|
|
1050
|
+
"^[a-z_]+$"
|
|
1051
|
+
]
|
|
1052
|
+
},
|
|
1053
|
+
"description": {
|
|
1054
|
+
"description": "What this parameter means, in the developer's own words, and documentation only — the robot does nothing with it. It travels into the input schema `robot_describe` publishes for this call, beside the bounds, so `type` and the range say what the value *is* and this is the only place that says what it *does*.",
|
|
1055
|
+
"examples": [
|
|
1056
|
+
"What a caller is choosing when they set this."
|
|
1057
|
+
],
|
|
1058
|
+
"type": "string",
|
|
1059
|
+
"minLength": 1,
|
|
1060
|
+
"maxLength": 500
|
|
1061
|
+
}
|
|
1062
|
+
},
|
|
1063
|
+
"required": [
|
|
1064
|
+
"type"
|
|
1065
|
+
],
|
|
1066
|
+
"additionalProperties": false,
|
|
1067
|
+
"defaultSnippets": [
|
|
1068
|
+
{
|
|
1069
|
+
"label": "a parameter, with its bounds",
|
|
1070
|
+
"description": "One hole a caller fills: what type it is, what values it may take, and what it means. Without a `default` it is required, and the bounds are enforced in the cloud before anything reaches the robot.",
|
|
1071
|
+
"body": {
|
|
1072
|
+
"type": "${2:float64}",
|
|
1073
|
+
"min_value": -0.5,
|
|
1074
|
+
"max_value": 0.5,
|
|
1075
|
+
"description": "${3:What a caller is choosing when they set this.}"
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
]
|
|
1079
|
+
},
|
|
1080
|
+
"description": "The holes in this entry's `message` that a caller fills, keyed by **parameter name** rather than by field path — so the name survives the field moving inside the message, and a caller sends something that means what it says. Every declared parameter must appear somewhere in the message and every `${name}` in the message must be declared; either half alone is an error.",
|
|
1081
|
+
"defaultSnippets": [
|
|
1082
|
+
{
|
|
1083
|
+
"label": "a parameter, with its bounds",
|
|
1084
|
+
"description": "One hole a caller fills: what type it is, what values it may take, and what it means. Without a `default` it is required, and the bounds are enforced in the cloud before anything reaches the robot.",
|
|
1085
|
+
"body": {
|
|
1086
|
+
"${1:speed}": {
|
|
1087
|
+
"type": "${2:float64}",
|
|
1088
|
+
"min_value": -0.5,
|
|
1089
|
+
"max_value": 0.5,
|
|
1090
|
+
"description": "${3:What a caller is choosing when they set this.}"
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
]
|
|
1095
|
+
},
|
|
1096
|
+
"failsafe": {
|
|
1097
|
+
"type": "object",
|
|
1098
|
+
"properties": {
|
|
1099
|
+
"timeout_ms": {
|
|
1100
|
+
"type": "integer",
|
|
1101
|
+
"exclusiveMinimum": 0,
|
|
1102
|
+
"maximum": 60000,
|
|
1103
|
+
"description": "How long the bridge waits for the client's next send before sending the failsafe message itself, in milliseconds. The deadline runs **on the robot**, so it still fires when the link to the cloud is what failed — which is the case it exists for.",
|
|
1104
|
+
"examples": [
|
|
1105
|
+
500,
|
|
1106
|
+
1000
|
|
1107
|
+
]
|
|
1108
|
+
},
|
|
1109
|
+
"message": {
|
|
1110
|
+
"description": "What the bridge sends once `timeout_ms` runs out — for a drive command, a zero twist. It must be safe in **every** state, because it is sent precisely when nobody is watching any more, and it may hold no placeholder: there is no caller left to fill one."
|
|
1111
|
+
}
|
|
1112
|
+
},
|
|
1113
|
+
"required": [
|
|
1114
|
+
"timeout_ms",
|
|
1115
|
+
"message"
|
|
1116
|
+
],
|
|
1117
|
+
"additionalProperties": false,
|
|
1118
|
+
"description": "What the bridge sends **by itself** once a client stops sending, and how long it waits first. This is the format's safety story in one field: a client that crashes, loses its connection or whose operator closes the window does not leave a robot driving. The message may hold no placeholder, inline or through a shared message — there is nobody left to fill one.",
|
|
1119
|
+
"defaultSnippets": [
|
|
1120
|
+
{
|
|
1121
|
+
"label": "a deadline, and the message it sends",
|
|
1122
|
+
"description": "Half a second of silence and then a zero twist. The deadline runs on the robot, so it still fires when the link to the cloud is what failed — which is the case it exists for.",
|
|
1123
|
+
"body": {
|
|
1124
|
+
"timeout_ms": 500,
|
|
1125
|
+
"message": {
|
|
1126
|
+
"linear": {
|
|
1127
|
+
"x": 0
|
|
1128
|
+
},
|
|
1129
|
+
"angular": {
|
|
1130
|
+
"z": 0
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
]
|
|
1136
|
+
},
|
|
1137
|
+
"quiet_timeout_ms": {
|
|
1138
|
+
"type": "integer",
|
|
1139
|
+
"minimum": 0,
|
|
1140
|
+
"maximum": 600000,
|
|
1141
|
+
"description": "How long this publisher must stay silent before a **different** user may send to it. Whoever sends holds it implicitly exclusive, with no session and no lock, so this one number is the whole handover policy: too short and two operators fight over one robot, too long and a crashed client blocks it for everyone.",
|
|
1142
|
+
"examples": [
|
|
1143
|
+
2000
|
|
1144
|
+
]
|
|
1145
|
+
},
|
|
1146
|
+
"description": {
|
|
1147
|
+
"description": "What sending to this publisher does, in the developer's own words. It is documentation for the console and for MCP clients — the robot does nothing with it — and as for actions and services, the publisher is offered whether or not one is written, with `description: null` when it is not. A caller sends here repeatedly and continuously rather than once, which is why this kind alone carries `failsafe` and `quiet_timeout_ms`.",
|
|
1148
|
+
"examples": [
|
|
1149
|
+
"Velocity command. If sending stops, the robot stops."
|
|
1150
|
+
],
|
|
1151
|
+
"type": "string",
|
|
1152
|
+
"minLength": 1,
|
|
1153
|
+
"maxLength": 2000
|
|
1154
|
+
}
|
|
1155
|
+
},
|
|
1156
|
+
"required": [
|
|
1157
|
+
"topic",
|
|
1158
|
+
"type",
|
|
1159
|
+
"message",
|
|
1160
|
+
"failsafe",
|
|
1161
|
+
"quiet_timeout_ms"
|
|
1162
|
+
],
|
|
1163
|
+
"additionalProperties": false,
|
|
1164
|
+
"defaultSnippets": [
|
|
1165
|
+
{
|
|
1166
|
+
"label": "a publisher, with its parameters and its failsafe",
|
|
1167
|
+
"description": "A topic clients may send to: what is fixed, what a caller fills, and what the bridge sends by itself once the caller falls silent.",
|
|
1168
|
+
"body": {
|
|
1169
|
+
"topic": "${2:/cmd_vel}",
|
|
1170
|
+
"type": "${3:geometry_msgs/msg/Twist}",
|
|
1171
|
+
"message": {
|
|
1172
|
+
"linear": {
|
|
1173
|
+
"x": "\\${speed}"
|
|
1174
|
+
},
|
|
1175
|
+
"angular": {
|
|
1176
|
+
"z": "\\${turn}"
|
|
1177
|
+
}
|
|
1178
|
+
},
|
|
1179
|
+
"parameters": {
|
|
1180
|
+
"speed": {
|
|
1181
|
+
"type": "float64",
|
|
1182
|
+
"min_value": -0.5,
|
|
1183
|
+
"max_value": 0.5,
|
|
1184
|
+
"default": 0
|
|
1185
|
+
},
|
|
1186
|
+
"turn": {
|
|
1187
|
+
"type": "float64",
|
|
1188
|
+
"min_value": -0.5,
|
|
1189
|
+
"max_value": 0.5,
|
|
1190
|
+
"default": 0
|
|
1191
|
+
}
|
|
1192
|
+
},
|
|
1193
|
+
"failsafe": {
|
|
1194
|
+
"timeout_ms": 500,
|
|
1195
|
+
"message": {
|
|
1196
|
+
"linear": {
|
|
1197
|
+
"x": 0
|
|
1198
|
+
},
|
|
1199
|
+
"angular": {
|
|
1200
|
+
"z": 0
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
},
|
|
1204
|
+
"quiet_timeout_ms": 2000,
|
|
1205
|
+
"description": "${4:Velocity command. If sending stops, the robot stops.}"
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
]
|
|
1209
|
+
},
|
|
1210
|
+
"description": "Topics clients may send to, and where the format's whole safety story lives. The `message` template fixes every value a caller cannot change, and **`failsafe` is required**: once a client falls silent the bridge sends the failsafe message itself, so an operator whose window closed does not leave a robot driving. Keys are slugs, one namespace across all five exposure sections, which is what lets a role grant say `{robot, slug}` without naming a kind; `bridge_state`, `robot_details` and `bridge_pressure` are built-in, and `history` is reserved because `GET …/jobs/history` would shadow an action of that name; all four are refused when the document is validated.",
|
|
1211
|
+
"defaultSnippets": [
|
|
1212
|
+
{
|
|
1213
|
+
"label": "a publisher, with its parameters and its failsafe",
|
|
1214
|
+
"description": "A topic clients may send to: what is fixed, what a caller fills, and what the bridge sends by itself once the caller falls silent.",
|
|
1215
|
+
"body": {
|
|
1216
|
+
"${1:drive}": {
|
|
1217
|
+
"topic": "${2:/cmd_vel}",
|
|
1218
|
+
"type": "${3:geometry_msgs/msg/Twist}",
|
|
1219
|
+
"message": {
|
|
1220
|
+
"linear": {
|
|
1221
|
+
"x": "\\${speed}"
|
|
1222
|
+
},
|
|
1223
|
+
"angular": {
|
|
1224
|
+
"z": "\\${turn}"
|
|
1225
|
+
}
|
|
1226
|
+
},
|
|
1227
|
+
"parameters": {
|
|
1228
|
+
"speed": {
|
|
1229
|
+
"type": "float64",
|
|
1230
|
+
"min_value": -0.5,
|
|
1231
|
+
"max_value": 0.5,
|
|
1232
|
+
"default": 0
|
|
1233
|
+
},
|
|
1234
|
+
"turn": {
|
|
1235
|
+
"type": "float64",
|
|
1236
|
+
"min_value": -0.5,
|
|
1237
|
+
"max_value": 0.5,
|
|
1238
|
+
"default": 0
|
|
1239
|
+
}
|
|
1240
|
+
},
|
|
1241
|
+
"failsafe": {
|
|
1242
|
+
"timeout_ms": 500,
|
|
1243
|
+
"message": {
|
|
1244
|
+
"linear": {
|
|
1245
|
+
"x": 0
|
|
1246
|
+
},
|
|
1247
|
+
"angular": {
|
|
1248
|
+
"z": 0
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
},
|
|
1252
|
+
"quiet_timeout_ms": 2000,
|
|
1253
|
+
"description": "${4:Velocity command. If sending stops, the robot stops.}"
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
]
|
|
1258
|
+
},
|
|
1259
|
+
"cameras": {
|
|
1260
|
+
"type": "object",
|
|
1261
|
+
"propertyNames": {
|
|
1262
|
+
"type": "string",
|
|
1263
|
+
"minLength": 2,
|
|
1264
|
+
"maxLength": 63,
|
|
1265
|
+
"pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
1266
|
+
"patternErrorMessage": "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore — `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused."
|
|
1267
|
+
},
|
|
1268
|
+
"additionalProperties": {
|
|
1269
|
+
"type": "object",
|
|
1270
|
+
"properties": {
|
|
1271
|
+
"source": {
|
|
1272
|
+
"oneOf": [
|
|
1273
|
+
{
|
|
1274
|
+
"type": "object",
|
|
1275
|
+
"properties": {
|
|
1276
|
+
"kind": {
|
|
1277
|
+
"type": "string",
|
|
1278
|
+
"const": "ros",
|
|
1279
|
+
"description": "Selects the ROS image-topic source: this camera then carries `topic` and `type`, and no field of another kind."
|
|
1280
|
+
},
|
|
1281
|
+
"topic": {
|
|
1282
|
+
"type": "string",
|
|
1283
|
+
"maxLength": 255,
|
|
1284
|
+
"pattern": "^\\/[A-Za-z_][A-Za-z0-9_]*(?:\\/[A-Za-z_][A-Za-z0-9_]*)*$",
|
|
1285
|
+
"description": "The ROS image topic the bridge subscribes to, as an absolute graph name. Clients never name it — they address the camera by its slug — so the topic can be renamed on the robot without an app changing.",
|
|
1286
|
+
"patternErrorMessage": "A ROS graph name is absolute: it begins with a slash, and each segment after a slash starts with a letter or an underscore and continues with letters, digits and underscores — `/camera/image_raw`. A relative name, a trailing slash, a dash or a dot is refused.",
|
|
1287
|
+
"examples": [
|
|
1288
|
+
"/camera/image_raw"
|
|
1289
|
+
]
|
|
1290
|
+
},
|
|
1291
|
+
"type": {
|
|
1292
|
+
"type": "string",
|
|
1293
|
+
"maxLength": 255,
|
|
1294
|
+
"pattern": "^[a-z][a-z0-9_]*\\/(?:msg|srv|action)\\/[A-Za-z][A-Za-z0-9]*$",
|
|
1295
|
+
"description": "The message type of `topic`: `sensor_msgs/msg/Image` for raw frames, `sensor_msgs/msg/CompressedImage` for a camera that already encodes. Declared here rather than introspected, so a camera can be configured for a robot that has never connected.",
|
|
1296
|
+
"patternErrorMessage": "A ROS 2 type name has three segments: the package, then `msg`, `srv` or `action`, then the type — `sensor_msgs/msg/BatteryState`, `std_srvs/srv/Trigger`, `nav2_msgs/action/NavigateToPose`. The middle segment is the one usually left out. The package is lower-case with underscores; the type itself is letters and digits, conventionally CamelCase.",
|
|
1297
|
+
"examples": [
|
|
1298
|
+
"sensor_msgs/msg/Image"
|
|
1299
|
+
]
|
|
1300
|
+
}
|
|
1301
|
+
},
|
|
1302
|
+
"required": [
|
|
1303
|
+
"kind",
|
|
1304
|
+
"topic",
|
|
1305
|
+
"type"
|
|
1306
|
+
],
|
|
1307
|
+
"additionalProperties": false,
|
|
1308
|
+
"description": "Frames come from an image topic the robot already publishes. It is the only source the bridge **subscribes** to rather than opens, so it needs no URL, no device and nobody to authenticate to.",
|
|
1309
|
+
"defaultSnippets": [
|
|
1310
|
+
{
|
|
1311
|
+
"label": "ros — an image topic the robot already publishes",
|
|
1312
|
+
"description": "Subscribes to a topic that is already there; nothing is opened and there is nobody to authenticate to.",
|
|
1313
|
+
"body": {
|
|
1314
|
+
"kind": "ros",
|
|
1315
|
+
"topic": "${1:/camera/image_raw}",
|
|
1316
|
+
"type": "${2|sensor_msgs/msg/Image,sensor_msgs/msg/CompressedImage|}"
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
]
|
|
1320
|
+
},
|
|
1321
|
+
{
|
|
1322
|
+
"type": "object",
|
|
1323
|
+
"properties": {
|
|
1324
|
+
"kind": {
|
|
1325
|
+
"type": "string",
|
|
1326
|
+
"const": "rtsp",
|
|
1327
|
+
"description": "Selects the RTSP source: this camera then carries `url`, and optionally `transport` and `credentials`."
|
|
1328
|
+
},
|
|
1329
|
+
"url": {
|
|
1330
|
+
"type": "string",
|
|
1331
|
+
"minLength": 1,
|
|
1332
|
+
"maxLength": 2048,
|
|
1333
|
+
"pattern": "^rtsps?:\\/\\/",
|
|
1334
|
+
"description": "Where the stream lives, reached from the robot rather than from the cloud. **`rtsp://` or `rtsps://` only** — the bridge opens this with a library that would equally honour `file:`, so an unconstrained URL would turn a configuration document into arbitrary file access on the robot. The bridge re-checks the scheme itself, so a validator that changed could not make a robot serve files.",
|
|
1335
|
+
"patternErrorMessage": "The URL has to begin with `rtsp://` or `rtsps://` — `rtsp://cam-1.plant.local/stream1`. No other scheme is accepted: the bridge opens this with a library that would equally honour `file:`.",
|
|
1336
|
+
"examples": [
|
|
1337
|
+
"rtsp://cam-1.plant.local/stream1"
|
|
1338
|
+
]
|
|
1339
|
+
},
|
|
1340
|
+
"transport": {
|
|
1341
|
+
"description": "How the RTSP payload is carried. Omitted means `tcp`: `udp` loses frames on a congested link and loses them silently, so the result looks like a failing camera rather than like a choice made here.",
|
|
1342
|
+
"enumDescriptions": [
|
|
1343
|
+
"The frames are interleaved into the RTSP connection itself, which is what a congested or lossy link needs — nothing is dropped on the way. This is what an omitted `transport` means.",
|
|
1344
|
+
"The frames travel in their own UDP stream: lower latency on a quiet network, and silent frame loss on any other."
|
|
1345
|
+
],
|
|
1346
|
+
"type": "string",
|
|
1347
|
+
"enum": [
|
|
1348
|
+
"tcp",
|
|
1349
|
+
"udp"
|
|
1350
|
+
]
|
|
1351
|
+
},
|
|
1352
|
+
"credentials": {
|
|
1353
|
+
"type": "object",
|
|
1354
|
+
"properties": {
|
|
1355
|
+
"username": {
|
|
1356
|
+
"description": "The account name the camera expects. For MJPEG the bridge sends a real HTTP `Authorization: Basic` header and leaves the URL untouched. RTSP offers no such channel through ffmpeg, so there the name goes inside the connect URL instead — built fresh for that one call and never written back into the stored document.",
|
|
1357
|
+
"examples": [
|
|
1358
|
+
"ops"
|
|
1359
|
+
],
|
|
1360
|
+
"type": "string",
|
|
1361
|
+
"minLength": 1,
|
|
1362
|
+
"maxLength": 128
|
|
1363
|
+
},
|
|
1364
|
+
"password": {
|
|
1365
|
+
"description": "The password for `username`. **There is no secret store behind this**: the value written here is the value stored, so treat it as readable by everyone who may read this robot's configuration, now and in its history.",
|
|
1366
|
+
"type": "string",
|
|
1367
|
+
"minLength": 1,
|
|
1368
|
+
"maxLength": 128
|
|
1369
|
+
}
|
|
1370
|
+
},
|
|
1371
|
+
"additionalProperties": false,
|
|
1372
|
+
"description": "Username and password for the stream, standing **in clear text in the document**. A published version is immutable, so a password here cannot be removed from history or rotated without republishing — which is why the publish audit event carries only the version number and never the document body. Userinfo in the `url` works too; an explicit block here wins over it.",
|
|
1373
|
+
"defaultSnippets": [
|
|
1374
|
+
{
|
|
1375
|
+
"label": "username and password",
|
|
1376
|
+
"description": "Both fields, in clear text — which is what this block is. The password default is deliberately not a password: `CHANGE-ME` is stored like any other value, but it is **visible** rather than plausible, so a reviewer reading the diff sees it and the camera rejects it at connect time — where a default that looked like a password would simply be published and kept.",
|
|
1377
|
+
"body": {
|
|
1378
|
+
"username": "${1:ops}",
|
|
1379
|
+
"password": "${2:CHANGE-ME}"
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
]
|
|
1383
|
+
}
|
|
1384
|
+
},
|
|
1385
|
+
"required": [
|
|
1386
|
+
"kind",
|
|
1387
|
+
"url"
|
|
1388
|
+
],
|
|
1389
|
+
"additionalProperties": false,
|
|
1390
|
+
"description": "Frames come from an RTSP stream the robot itself can reach — a network camera on its own LAN. The bridge opens the connection; the cloud never does, and never needs a route to the camera.",
|
|
1391
|
+
"defaultSnippets": [
|
|
1392
|
+
{
|
|
1393
|
+
"label": "rtsp — a network camera the robot itself can reach",
|
|
1394
|
+
"description": "A stream the bridge opens over RTSP. The scheme is written out because the format constrains it; the host and the path are what vary.",
|
|
1395
|
+
"body": {
|
|
1396
|
+
"kind": "rtsp",
|
|
1397
|
+
"url": "rtsp://${1:cam-1.plant.local}/${2:stream1}"
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
]
|
|
1401
|
+
},
|
|
1402
|
+
{
|
|
1403
|
+
"type": "object",
|
|
1404
|
+
"properties": {
|
|
1405
|
+
"kind": {
|
|
1406
|
+
"type": "string",
|
|
1407
|
+
"const": "mjpeg",
|
|
1408
|
+
"description": "Selects the MJPEG-over-HTTP source: this camera then carries `url`, and optionally `credentials`."
|
|
1409
|
+
},
|
|
1410
|
+
"url": {
|
|
1411
|
+
"type": "string",
|
|
1412
|
+
"minLength": 1,
|
|
1413
|
+
"maxLength": 2048,
|
|
1414
|
+
"pattern": "^https?:\\/\\/",
|
|
1415
|
+
"description": "Where the stream lives. **`http://` or `https://` only** — as for the `rtsp` URL, the bridge opens it with a library that would also serve `file:`. Plain `http://` is permitted because these cameras usually sit on the robot's own network, but Basic credentials on such a URL then travel in the clear.",
|
|
1416
|
+
"patternErrorMessage": "The URL has to begin with `http://` or `https://` — `http://cam-1.plant.local/video.mjpg`. No other scheme is accepted: the bridge opens this with a library that would equally serve `file:`.",
|
|
1417
|
+
"examples": [
|
|
1418
|
+
"http://cam-1.plant.local/video.mjpg"
|
|
1419
|
+
]
|
|
1420
|
+
},
|
|
1421
|
+
"credentials": {
|
|
1422
|
+
"type": "object",
|
|
1423
|
+
"properties": {
|
|
1424
|
+
"username": {
|
|
1425
|
+
"description": "The account name the camera expects. For MJPEG the bridge sends a real HTTP `Authorization: Basic` header and leaves the URL untouched. RTSP offers no such channel through ffmpeg, so there the name goes inside the connect URL instead — built fresh for that one call and never written back into the stored document.",
|
|
1426
|
+
"examples": [
|
|
1427
|
+
"ops"
|
|
1428
|
+
],
|
|
1429
|
+
"type": "string",
|
|
1430
|
+
"minLength": 1,
|
|
1431
|
+
"maxLength": 128
|
|
1432
|
+
},
|
|
1433
|
+
"password": {
|
|
1434
|
+
"description": "The password for `username`. **There is no secret store behind this**: the value written here is the value stored, so treat it as readable by everyone who may read this robot's configuration, now and in its history.",
|
|
1435
|
+
"type": "string",
|
|
1436
|
+
"minLength": 1,
|
|
1437
|
+
"maxLength": 128
|
|
1438
|
+
}
|
|
1439
|
+
},
|
|
1440
|
+
"additionalProperties": false,
|
|
1441
|
+
"description": "Username and password for the stream, standing **in clear text in the document**. A published version is immutable, so a password here cannot be removed from history or rotated without republishing — which is why the publish audit event carries only the version number and never the document body. Userinfo in the `url` works too; an explicit block here wins over it.",
|
|
1442
|
+
"defaultSnippets": [
|
|
1443
|
+
{
|
|
1444
|
+
"label": "username and password",
|
|
1445
|
+
"description": "Both fields, in clear text — which is what this block is. The password default is deliberately not a password: `CHANGE-ME` is stored like any other value, but it is **visible** rather than plausible, so a reviewer reading the diff sees it and the camera rejects it at connect time — where a default that looked like a password would simply be published and kept.",
|
|
1446
|
+
"body": {
|
|
1447
|
+
"username": "${1:ops}",
|
|
1448
|
+
"password": "${2:CHANGE-ME}"
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
]
|
|
1452
|
+
}
|
|
1453
|
+
},
|
|
1454
|
+
"required": [
|
|
1455
|
+
"kind",
|
|
1456
|
+
"url"
|
|
1457
|
+
],
|
|
1458
|
+
"additionalProperties": false,
|
|
1459
|
+
"description": "Frames come from an MJPEG stream over HTTP — one JPEG after another, the simplest network source there is. Unlike `rtsp` there is no `transport` to choose: it is HTTP, and any `credentials` therefore travel as HTTP Basic.",
|
|
1460
|
+
"defaultSnippets": [
|
|
1461
|
+
{
|
|
1462
|
+
"label": "mjpeg — one JPEG after another over HTTP",
|
|
1463
|
+
"description": "The simplest network source there is. `https://` is accepted too, and is what any credentials on this URL need.",
|
|
1464
|
+
"body": {
|
|
1465
|
+
"kind": "mjpeg",
|
|
1466
|
+
"url": "http://${1:cam-1.plant.local}/${2:video.mjpg}"
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
]
|
|
1470
|
+
},
|
|
1471
|
+
{
|
|
1472
|
+
"type": "object",
|
|
1473
|
+
"properties": {
|
|
1474
|
+
"kind": {
|
|
1475
|
+
"type": "string",
|
|
1476
|
+
"const": "v4l2",
|
|
1477
|
+
"description": "Selects the local capture-device source: this camera then carries `device` and nothing else."
|
|
1478
|
+
},
|
|
1479
|
+
"device": {
|
|
1480
|
+
"type": "string",
|
|
1481
|
+
"minLength": 1,
|
|
1482
|
+
"maxLength": 128,
|
|
1483
|
+
"pattern": "^\\/dev\\/[A-Za-z0-9][A-Za-z0-9._/-]*$",
|
|
1484
|
+
"description": "The capture device, resolved on the robot and never by the cloud; a `/dev/v4l/by-id/...` symlink survives a reboot that renumbers `/dev/video0`. **Constrained to `/dev/`** — the string reaches OpenCV, which will just as happily open an ordinary video file or an `http://` URL and publish its pixels to the cloud. The bridge re-derives the same constraint rather than trusting the wire.",
|
|
1485
|
+
"patternErrorMessage": "A capture device is a path under `/dev/`, and the character straight after it is a letter or a digit — `/dev/video0`, or a stable `/dev/v4l/by-id/...` symlink. Nothing outside `/dev/` is accepted: the string reaches OpenCV, which would as happily open an ordinary file.",
|
|
1486
|
+
"examples": [
|
|
1487
|
+
"/dev/video0"
|
|
1488
|
+
]
|
|
1489
|
+
}
|
|
1490
|
+
},
|
|
1491
|
+
"required": [
|
|
1492
|
+
"kind",
|
|
1493
|
+
"device"
|
|
1494
|
+
],
|
|
1495
|
+
"additionalProperties": false,
|
|
1496
|
+
"description": "Frames come from a capture device attached to the robot itself, such as a USB camera on `/dev/video0`. Nothing leaves the robot to fetch them, and there is nothing to authenticate to, so this source takes no `credentials`.",
|
|
1497
|
+
"defaultSnippets": [
|
|
1498
|
+
{
|
|
1499
|
+
"label": "v4l2 — a capture device attached to the robot",
|
|
1500
|
+
"description": "A USB camera on the robot itself. A `/dev/v4l/by-id/...` symlink survives a reboot that renumbers `/dev/video0`.",
|
|
1501
|
+
"body": {
|
|
1502
|
+
"kind": "v4l2",
|
|
1503
|
+
"device": "${1:/dev/video0}"
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
]
|
|
1507
|
+
}
|
|
1508
|
+
],
|
|
1509
|
+
"description": "Where this camera's frames come from. `kind` picks one of four sources and fixes which other fields the source may carry, so an impossible camera is unrepresentable rather than merely invalid — there is no way to write an RTSP camera with a ROS topic."
|
|
1510
|
+
},
|
|
1511
|
+
"width": {
|
|
1512
|
+
"type": "integer",
|
|
1513
|
+
"exclusiveMinimum": 0,
|
|
1514
|
+
"maximum": 7680,
|
|
1515
|
+
"description": "The width the bridge scales frames to before sending, in pixels — what the bridge produces, not what the sensor captures; a snapshot can arrive narrower, since the bridge reduces both dimensions together to fit its JPEG byte ceiling. It stands in the configuration and never in a viewer's request, so no client can make the robot encode a larger frame than the developer allowed.",
|
|
1516
|
+
"examples": [
|
|
1517
|
+
1280
|
|
1518
|
+
]
|
|
1519
|
+
},
|
|
1520
|
+
"height": {
|
|
1521
|
+
"type": "integer",
|
|
1522
|
+
"exclusiveMinimum": 0,
|
|
1523
|
+
"maximum": 4320,
|
|
1524
|
+
"description": "The height the bridge scales every frame to, in pixels; with `width` it is the size the live stream carries. A snapshot can arrive **smaller** than this — its JPEG has a byte ceiling, and the bridge gives up quality first and then resolution to fit, reporting the size it actually encoded.",
|
|
1525
|
+
"examples": [
|
|
1526
|
+
720
|
|
1527
|
+
]
|
|
1528
|
+
},
|
|
1529
|
+
"fps": {
|
|
1530
|
+
"type": "integer",
|
|
1531
|
+
"exclusiveMinimum": 0,
|
|
1532
|
+
"maximum": 60,
|
|
1533
|
+
"description": "How many frames a second the bridge forwards, at most. It is a ceiling, not a clock: a camera that delivers ten frames a second stays at ten. Both modes read the same throttled pipeline, so this also bounds how fresh a snapshot can be.",
|
|
1534
|
+
"examples": [
|
|
1535
|
+
15
|
|
1536
|
+
]
|
|
1537
|
+
},
|
|
1538
|
+
"bitrate_kbps": {
|
|
1539
|
+
"type": "integer",
|
|
1540
|
+
"exclusiveMinimum": 0,
|
|
1541
|
+
"maximum": 50000,
|
|
1542
|
+
"description": "The ceiling for the **live** encoding, in kilobits per second — this is what bounds a watched camera against the robot's uplink. Snapshots are not covered by it: they are JPEGs under their own byte ceiling. Raising `width`, `height` or `fps` against a fixed bitrate buys blur, not detail.",
|
|
1543
|
+
"examples": [
|
|
1544
|
+
2000
|
|
1545
|
+
]
|
|
1546
|
+
},
|
|
1547
|
+
"snapshot_interval_seconds": {
|
|
1548
|
+
"type": "integer",
|
|
1549
|
+
"minimum": 1,
|
|
1550
|
+
"maximum": 3600,
|
|
1551
|
+
"description": "How often a still frame is captured, in seconds. **It runs whether or not anyone is watching**, unlike the live stream, which the cloud refcounts — first viewer starts it, last one ends it. The cloud caches the one frame and serves every reader from it, so a hundred pollers cost the robot exactly one image per interval.",
|
|
1552
|
+
"examples": [
|
|
1553
|
+
5
|
|
1554
|
+
]
|
|
1555
|
+
},
|
|
1556
|
+
"description": {
|
|
1557
|
+
"description": "What this camera shows, in the developer's own words — documentation for whoever reads the configuration, for the console and for MCP clients; the robot does nothing with it. A camera without one is still offered, with `description: null`, as for actions, services and publishers. What `camera_snapshot` serves is the latest snapshot with its age; a live session is never a tool.",
|
|
1558
|
+
"examples": [
|
|
1559
|
+
"Forward-facing camera on the mast."
|
|
1560
|
+
],
|
|
1561
|
+
"type": "string",
|
|
1562
|
+
"minLength": 1,
|
|
1563
|
+
"maxLength": 2000
|
|
1564
|
+
}
|
|
1565
|
+
},
|
|
1566
|
+
"required": [
|
|
1567
|
+
"source",
|
|
1568
|
+
"width",
|
|
1569
|
+
"height",
|
|
1570
|
+
"fps",
|
|
1571
|
+
"bitrate_kbps",
|
|
1572
|
+
"snapshot_interval_seconds"
|
|
1573
|
+
],
|
|
1574
|
+
"additionalProperties": false,
|
|
1575
|
+
"defaultSnippets": [
|
|
1576
|
+
{
|
|
1577
|
+
"label": "a camera",
|
|
1578
|
+
"description": "A complete camera entry with every required field.",
|
|
1579
|
+
"body": {
|
|
1580
|
+
"source": {
|
|
1581
|
+
"kind": "v4l2",
|
|
1582
|
+
"device": "${2:/dev/video0}"
|
|
1583
|
+
},
|
|
1584
|
+
"width": 1280,
|
|
1585
|
+
"height": 720,
|
|
1586
|
+
"fps": 15,
|
|
1587
|
+
"bitrate_kbps": 2000,
|
|
1588
|
+
"snapshot_interval_seconds": 5,
|
|
1589
|
+
"description": "${3:Forward-facing camera on the mast.}"
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
]
|
|
1593
|
+
},
|
|
1594
|
+
"description": "Video the robot streams, and the still frames the cloud serves from it. `width`, `height`, `fps` and `bitrate_kbps` are what **the bridge produces before sending**, not what the camera captures — they live in the configuration rather than in a viewer's request precisely so that no viewer can make a robot send more. Keys are slugs, one namespace across all five exposure sections, which is what lets a role grant say `{robot, slug}` without naming a kind; `bridge_state`, `robot_details` and `bridge_pressure` are built-in, and `history` is reserved because `GET …/jobs/history` would shadow an action of that name; all four are refused when the document is validated.",
|
|
1595
|
+
"defaultSnippets": [
|
|
1596
|
+
{
|
|
1597
|
+
"label": "a camera",
|
|
1598
|
+
"description": "A complete camera entry with every required field.",
|
|
1599
|
+
"body": {
|
|
1600
|
+
"${1:front}": {
|
|
1601
|
+
"source": {
|
|
1602
|
+
"kind": "v4l2",
|
|
1603
|
+
"device": "${2:/dev/video0}"
|
|
1604
|
+
},
|
|
1605
|
+
"width": 1280,
|
|
1606
|
+
"height": 720,
|
|
1607
|
+
"fps": 15,
|
|
1608
|
+
"bitrate_kbps": 2000,
|
|
1609
|
+
"snapshot_interval_seconds": 5,
|
|
1610
|
+
"description": "${3:Forward-facing camera on the mast.}"
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
]
|
|
1615
|
+
}
|
|
1616
|
+
},
|
|
1617
|
+
"required": [
|
|
1618
|
+
"fleetless"
|
|
1619
|
+
],
|
|
1620
|
+
"additionalProperties": false
|
|
1621
|
+
},
|
|
1622
|
+
{
|
|
1623
|
+
"type": "null"
|
|
1624
|
+
}
|
|
1625
|
+
]
|
|
1626
|
+
},
|
|
1627
|
+
"source": {
|
|
1628
|
+
"type": "string"
|
|
1629
|
+
},
|
|
1630
|
+
"updated_at": {
|
|
1631
|
+
"anyOf": [
|
|
1632
|
+
{
|
|
1633
|
+
"type": "string",
|
|
1634
|
+
"format": "date-time",
|
|
1635
|
+
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
|
|
1636
|
+
},
|
|
1637
|
+
{
|
|
1638
|
+
"type": "null"
|
|
1639
|
+
}
|
|
1640
|
+
]
|
|
1641
|
+
},
|
|
1642
|
+
"issues": {
|
|
1643
|
+
"type": "array",
|
|
1644
|
+
"items": {
|
|
1645
|
+
"type": "object",
|
|
1646
|
+
"properties": {
|
|
1647
|
+
"path": {
|
|
1648
|
+
"type": "string",
|
|
1649
|
+
"minLength": 1
|
|
1650
|
+
},
|
|
1651
|
+
"slug": {
|
|
1652
|
+
"anyOf": [
|
|
1653
|
+
{
|
|
1654
|
+
"type": "string"
|
|
1655
|
+
},
|
|
1656
|
+
{
|
|
1657
|
+
"type": "null"
|
|
1658
|
+
}
|
|
1659
|
+
]
|
|
1660
|
+
},
|
|
1661
|
+
"code": {
|
|
1662
|
+
"type": "string",
|
|
1663
|
+
"minLength": 1
|
|
1664
|
+
},
|
|
1665
|
+
"message": {
|
|
1666
|
+
"type": "string",
|
|
1667
|
+
"minLength": 1
|
|
1668
|
+
},
|
|
1669
|
+
"severity": {
|
|
1670
|
+
"type": "string",
|
|
1671
|
+
"enum": [
|
|
1672
|
+
"error",
|
|
1673
|
+
"warning"
|
|
1674
|
+
]
|
|
1675
|
+
}
|
|
1676
|
+
},
|
|
1677
|
+
"required": [
|
|
1678
|
+
"path",
|
|
1679
|
+
"slug",
|
|
1680
|
+
"code",
|
|
1681
|
+
"message",
|
|
1682
|
+
"severity"
|
|
1683
|
+
],
|
|
1684
|
+
"additionalProperties": false
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
},
|
|
1688
|
+
"required": [
|
|
1689
|
+
"doc",
|
|
1690
|
+
"source",
|
|
1691
|
+
"updated_at",
|
|
1692
|
+
"issues"
|
|
1693
|
+
],
|
|
1694
|
+
"additionalProperties": false
|
|
1695
|
+
}
|