@fleetless/contracts 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -2
- package/CODE_OF_CONDUCT.md +83 -0
- package/CONTRIBUTING.md +136 -0
- package/README.md +49 -13
- package/SECURITY.md +55 -0
- package/artifacts/openapi.json +1 -1
- package/artifacts/routes.json +1 -1
- package/dist/alerts.d.ts +19 -24
- package/dist/alerts.js +18 -24
- package/dist/app-users.d.ts +7 -6
- package/dist/app-users.js +6 -6
- package/dist/apps.d.ts +21 -25
- package/dist/apps.js +40 -51
- package/dist/assets.d.ts +70 -132
- package/dist/assets.js +130 -223
- package/dist/audit.d.ts +11 -11
- package/dist/audit.js +25 -51
- package/dist/client-auth.d.ts +4 -4
- package/dist/client-auth.js +3 -4
- package/dist/common.d.ts +27 -35
- package/dist/common.js +26 -35
- package/dist/config-issues.d.ts +4 -3
- package/dist/config-issues.js +7 -6
- package/dist/config.d.ts +31 -37
- package/dist/config.js +81 -110
- package/dist/errors.d.ts +4 -3
- package/dist/errors.js +61 -87
- package/dist/identity.d.ts +18 -21
- package/dist/identity.js +17 -21
- package/dist/index.d.ts +4 -4
- package/dist/index.js +12 -13
- package/dist/introspection.d.ts +7 -6
- package/dist/introspection.js +6 -6
- package/dist/jobs.d.ts +12 -12
- package/dist/jobs.js +20 -25
- package/dist/mcp.d.ts +11 -12
- package/dist/mcp.js +10 -12
- package/dist/oauth.d.ts +13 -18
- package/dist/oauth.js +13 -19
- package/dist/protocol.d.ts +51 -62
- package/dist/protocol.js +107 -139
- package/dist/realtime.d.ts +53 -68
- package/dist/realtime.js +77 -103
- package/dist/rest.d.ts +182 -243
- package/dist/rest.js +301 -395
- package/dist/routes.d.ts +4 -3
- package/dist/routes.js +3 -2
- package/package.json +12 -7
package/CHANGELOG.md
CHANGED
|
@@ -5,15 +5,81 @@ follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
|
|
|
5
5
|
project uses [semantic versioning](https://semver.org/spec/v2.0.0.html) over
|
|
6
6
|
the wire shapes.
|
|
7
7
|
|
|
8
|
+
## [1.0.2] — 2026-09-07
|
|
9
|
+
|
|
10
|
+
A documentation and packaging release. (1.0.1 was tagged and never published:
|
|
11
|
+
its `verify` job went red on a licence-header guard that swept the pipeline's
|
|
12
|
+
own scratch file. The tag pattern is protected and cannot be moved, so the
|
|
13
|
+
release carries the next number. Nothing was ever served as 1.0.1.)
|
|
14
|
+
|
|
15
|
+
**No wire shape changes**, and nothing generated from a schema changes either — `artifacts/schema/` and
|
|
16
|
+
`artifacts/schema-outgoing/` are byte-identical to 1.0.0. What changes is what
|
|
17
|
+
the package says about itself.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **The internal engineering prose is gone from the published bytes.** Doc
|
|
22
|
+
comments in the source were written for the people who built this and `tsc`
|
|
23
|
+
carries them into `dist/*.js` and `dist/*.d.ts`, so 1.0.0 shipped German
|
|
24
|
+
paragraphs, a reference robot's workspace paths, measured mesh sizes and
|
|
25
|
+
internal tracker ids to anyone who hovered a symbol in an editor. Every
|
|
26
|
+
comment that ships has been rewritten for a reader who has only this package.
|
|
27
|
+
A test now greps the built `dist/` and `artifacts/` for those markers and
|
|
28
|
+
fails on a hit.
|
|
29
|
+
- **The `artifacts/` table in the README described `schema-outgoing/`
|
|
30
|
+
backwards.** It said those files were the messages the cloud sends to a
|
|
31
|
+
bridge. They are the opposite: the frames a bridge **sends**, rendered in
|
|
32
|
+
output mode, and validating incoming cloud frames against them fails on every
|
|
33
|
+
real frame. The table now says what each directory holds and why there are
|
|
34
|
+
two.
|
|
35
|
+
- **The README claimed the robot-side bridge validates incoming frames against
|
|
36
|
+
`artifacts/schema/` at runtime with Python's `jsonschema`.** It does not. The
|
|
37
|
+
bridge does not depend on this package at all — it vendors its own copies —
|
|
38
|
+
and only its test harness imports `jsonschema`. There is no runtime validation
|
|
39
|
+
of incoming frames against these schemas.
|
|
40
|
+
- **The security reporting address was in nothing the package served.**
|
|
41
|
+
`SECURITY.md` was not in `files`, and the README linked to it relatively.
|
|
42
|
+
`SECURITY.md`, `CONTRIBUTING.md` and `CODE_OF_CONDUCT.md` now ship, and the
|
|
43
|
+
address is written out in the README under its own heading rather than behind
|
|
44
|
+
a link.
|
|
45
|
+
- **Twenty-two of the forty published files carried no SPDX header.**
|
|
46
|
+
Declaration emit drops a leading `//` comment and two `.js` files had theirs
|
|
47
|
+
elided, so more than half of what a licence scanner sees was unmarked. Every
|
|
48
|
+
file in `dist/` now carries `// SPDX-License-Identifier: Apache-2.0`, and the
|
|
49
|
+
pack check asserts it over the tarball's own bytes.
|
|
50
|
+
- **The CHANGELOG's account of 1.0.0 was wrong about which server it matched**
|
|
51
|
+
— see the corrected note below.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- `zod` moves from `^4.0.0` to `^4.4.3`. It stays a range on purpose, and the
|
|
56
|
+
README now says why: zod is a peer in everything but name, and an exact pin
|
|
57
|
+
forces a second copy on any consumer whose lockfile resolves a different
|
|
58
|
+
patch. `^4.4.3` is the floor these schemas are built, tested and exported
|
|
59
|
+
against, rather than a version nobody has run.
|
|
60
|
+
- `bugs` gains an email address, so a report has somewhere to go from the npm
|
|
61
|
+
page.
|
|
62
|
+
|
|
8
63
|
## [1.0.0] — 2026-09-07
|
|
9
64
|
|
|
10
|
-
The first published version. Nothing about the shapes changes with it: they
|
|
11
|
-
exactly the shapes the Fleetless cloud serves at release 0.17.0, and they have
|
|
65
|
+
The first published version. Nothing about the shapes changes with it: they have
|
|
12
66
|
been the contract between the cloud, the console, the SDK and the robot-side
|
|
13
67
|
bridge for as long as those have existed. What changes is who can read them —
|
|
14
68
|
until now this package was resolvable only from a private git URL, so nobody
|
|
15
69
|
outside the project could build a Fleetless client from source.
|
|
16
70
|
|
|
71
|
+
**Corrected in 1.0.2:** this entry originally said these were "exactly the
|
|
72
|
+
shapes the Fleetless cloud serves at release 0.17.0". They are not. This package
|
|
73
|
+
tracks the cloud's `main` branch, not its releases, and 1.0.0 was cut from a
|
|
74
|
+
commit that already carried the MCP consent-withdrawal change — so the route
|
|
75
|
+
notes and the OpenAPI description for
|
|
76
|
+
`DELETE /api/client/mcp/grants/:clientId` and its developer twin state that
|
|
77
|
+
withdrawal ends a session at the client's very next call, which the deployed
|
|
78
|
+
0.17.0 does not do. On 0.17.0 a withdrawn client keeps working for the remaining
|
|
79
|
+
lifetime of its access token, up to fifteen minutes. **A shape or a note here may
|
|
80
|
+
precede the release that serves it**; check the platform's own release notes
|
|
81
|
+
before treating one as an operational control.
|
|
82
|
+
|
|
17
83
|
Consumers should pin an exact version. A range cannot express "compatible with
|
|
18
84
|
the server you are talking to", and that is the only compatibility question
|
|
19
85
|
these schemas answer. Any change that makes a previously valid message invalid
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
|
|
6
|
+
|
|
7
|
+
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
|
8
|
+
|
|
9
|
+
## Our Standards
|
|
10
|
+
|
|
11
|
+
Examples of behavior that contributes to a positive environment for our community include:
|
|
12
|
+
|
|
13
|
+
* Demonstrating empathy and kindness toward other people
|
|
14
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
|
15
|
+
* Giving and gracefully accepting constructive feedback
|
|
16
|
+
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
|
|
17
|
+
* Focusing on what is best not just for us as individuals, but for the overall community
|
|
18
|
+
|
|
19
|
+
Examples of unacceptable behavior include:
|
|
20
|
+
|
|
21
|
+
* The use of sexualized language or imagery, and sexual attention or advances of any kind
|
|
22
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
23
|
+
* Public or private harassment
|
|
24
|
+
* Publishing others' private information, such as a physical or email address, without their explicit permission
|
|
25
|
+
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
26
|
+
|
|
27
|
+
## Enforcement Responsibilities
|
|
28
|
+
|
|
29
|
+
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
|
30
|
+
|
|
31
|
+
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
|
|
32
|
+
|
|
33
|
+
## Scope
|
|
34
|
+
|
|
35
|
+
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
|
36
|
+
|
|
37
|
+
## Enforcement
|
|
38
|
+
|
|
39
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at hello@fleetless.dev. All complaints will be reviewed and investigated promptly and fairly.
|
|
40
|
+
|
|
41
|
+
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
|
42
|
+
|
|
43
|
+
## Enforcement Guidelines
|
|
44
|
+
|
|
45
|
+
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
|
|
46
|
+
|
|
47
|
+
### 1. Correction
|
|
48
|
+
|
|
49
|
+
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
|
|
50
|
+
|
|
51
|
+
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
|
|
52
|
+
|
|
53
|
+
### 2. Warning
|
|
54
|
+
|
|
55
|
+
**Community Impact**: A violation through a single incident or series of actions.
|
|
56
|
+
|
|
57
|
+
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
|
|
58
|
+
|
|
59
|
+
### 3. Temporary Ban
|
|
60
|
+
|
|
61
|
+
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
|
|
62
|
+
|
|
63
|
+
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
|
|
64
|
+
|
|
65
|
+
### 4. Permanent Ban
|
|
66
|
+
|
|
67
|
+
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
|
|
68
|
+
|
|
69
|
+
**Consequence**: A permanent ban from any sort of public interaction within the community.
|
|
70
|
+
|
|
71
|
+
## Attribution
|
|
72
|
+
|
|
73
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
74
|
+
|
|
75
|
+
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
|
76
|
+
|
|
77
|
+
For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
|
|
78
|
+
|
|
79
|
+
[homepage]: https://www.contributor-covenant.org
|
|
80
|
+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
81
|
+
[Mozilla CoC]: https://github.com/mozilla/diversity
|
|
82
|
+
[FAQ]: https://www.contributor-covenant.org/faq
|
|
83
|
+
[translations]: https://www.contributor-covenant.org/translations
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Contributing to `@fleetless/contracts`
|
|
2
|
+
|
|
3
|
+
This package is the single source of wire truth for Fleetless: every shape the
|
|
4
|
+
API accepts or returns, and every message the robot-side bridge exchanges with
|
|
5
|
+
the cloud, is written here once as a zod schema and exported as JSON Schema and
|
|
6
|
+
OpenAPI for consumers that are not TypeScript.
|
|
7
|
+
|
|
8
|
+
That makes a change here a cross-repository change. A schema is not a detail of
|
|
9
|
+
one program; it is the agreement between several.
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
Node 22 and pnpm (through corepack):
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
nvm use 22
|
|
17
|
+
corepack enable
|
|
18
|
+
pnpm install
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
There are no private dependencies. `pnpm install` works from a clean checkout
|
|
22
|
+
with nothing but a network connection to the npm registry.
|
|
23
|
+
|
|
24
|
+
## The checks
|
|
25
|
+
|
|
26
|
+
| Command | What it does |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `pnpm typecheck` | `tsc --noEmit` over `src/`, then again over `test/` with its own project. Both halves, always — the root project is `src`-only and says nothing about test files. |
|
|
29
|
+
| `pnpm test` | The vitest suite. No network, no server. |
|
|
30
|
+
| `pnpm build` | `tsc` into `dist/`. |
|
|
31
|
+
| `pnpm artifacts` | Regenerates `artifacts/` from the zod schemas. |
|
|
32
|
+
| `pnpm run test:pack` | Packs the tarball, asserts what is and is not inside it, then installs it into a scratch project and imports it for real. |
|
|
33
|
+
|
|
34
|
+
To run one test file, use `pnpm vitest run test/<name>.test.ts`. Do **not**
|
|
35
|
+
use `pnpm test -- <pattern>`: it does not filter, it runs the whole suite, and
|
|
36
|
+
the exit code you read is the suite's.
|
|
37
|
+
|
|
38
|
+
## Changing a schema
|
|
39
|
+
|
|
40
|
+
1. Edit the zod schema in `src/`.
|
|
41
|
+
2. Run `pnpm artifacts`. This regenerates `artifacts/`, which is **committed**.
|
|
42
|
+
A test fails if the committed artifacts are stale, and so does CI.
|
|
43
|
+
3. Run `pnpm typecheck && pnpm test`.
|
|
44
|
+
4. Commit the schema change and the regenerated artifacts together. A commit
|
|
45
|
+
that changes one without the other is a commit that publishes a lie to every
|
|
46
|
+
consumer validating against the artifact.
|
|
47
|
+
|
|
48
|
+
Every source file carries the SPDX header `// SPDX-License-Identifier:
|
|
49
|
+
Apache-2.0` as its first line. A test asserts this over the whole set — and the
|
|
50
|
+
directory list is derived from the repository rather than written down, so a new
|
|
51
|
+
top-level directory is swept the day it exists.
|
|
52
|
+
|
|
53
|
+
The published files carry it too, which `tsc` does not do on its own:
|
|
54
|
+
declaration emit drops a leading comment, so `scripts/stamp-dist.mjs` puts the
|
|
55
|
+
header back on every file in `dist/` and `pnpm run test:pack` asserts it over
|
|
56
|
+
the tarball's own bytes.
|
|
57
|
+
|
|
58
|
+
**Nothing internal reaches the published bytes.** Doc comments in `src/` are
|
|
59
|
+
carried into `dist/*.js` and `dist/*.d.ts` by `tsc`, and every
|
|
60
|
+
`.meta({ description })` is copied into the JSON Schema and OpenAPI artifacts —
|
|
61
|
+
so a comment written for the people who build this is a comment an editor shows
|
|
62
|
+
to somebody who installed it. Write a description as what a field means to a
|
|
63
|
+
caller, never as how the behaviour was found, on which machine, or under which
|
|
64
|
+
internal ticket. `test/published-prose.test.ts` greps the built output and fails
|
|
65
|
+
on German prose, an internal ticket id, a developer machine path or a
|
|
66
|
+
first-name attribution.
|
|
67
|
+
|
|
68
|
+
## Pull requests
|
|
69
|
+
|
|
70
|
+
**Pull requests are welcome on GitHub**, at
|
|
71
|
+
<https://github.com/fleetless/contracts>. Open an issue first for anything that
|
|
72
|
+
changes an existing shape, so we can say what else has to move with it.
|
|
73
|
+
|
|
74
|
+
**CI runs on GitLab.** This repository is mirrored from an internal GitLab
|
|
75
|
+
instance, which is where the pipeline that verifies and publishes it lives. You
|
|
76
|
+
will not see a check run on your GitHub pull request; a maintainer runs the
|
|
77
|
+
same checks, and the result comes back as a review comment. Run `pnpm
|
|
78
|
+
typecheck && pnpm test && pnpm artifacts` yourself before you open the pull
|
|
79
|
+
request and you will have seen everything CI would tell you.
|
|
80
|
+
|
|
81
|
+
## The CLA
|
|
82
|
+
|
|
83
|
+
The first pull request you open will ask you to sign a Contributor Licence
|
|
84
|
+
Agreement. It grants Dehne Robotik GmbH the right to license your contribution
|
|
85
|
+
under terms other than Apache-2.0 in future — that is the whole of what it is
|
|
86
|
+
for, and it is why the project can relicense its own code later without having
|
|
87
|
+
to find every past contributor. It does not take your copyright away and it
|
|
88
|
+
does not stop you from using your own contribution however you like.
|
|
89
|
+
|
|
90
|
+
## Commit messages
|
|
91
|
+
|
|
92
|
+
[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/):
|
|
93
|
+
`feat:`, `fix:`, `docs:`, `chore:`, `ci:`, `refactor:`, `test:`. A change that
|
|
94
|
+
alters an existing wire shape is a breaking change; say so with a `!` and a
|
|
95
|
+
`BREAKING CHANGE:` footer, because it decides the next version number.
|
|
96
|
+
|
|
97
|
+
Everything in this repository is written in **English** — code, comments,
|
|
98
|
+
commit messages, documentation.
|
|
99
|
+
|
|
100
|
+
## Code of conduct
|
|
101
|
+
|
|
102
|
+
By participating you agree to the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
## Releasing (maintainers)
|
|
106
|
+
|
|
107
|
+
The pipeline publishes, and `npm publish` from a working tree is refused by a
|
|
108
|
+
`prepublishOnly` script — the rule has a mechanism rather than only a sentence.
|
|
109
|
+
(The pipeline is unaffected: it publishes the tarball `verify` packed, and npm
|
|
110
|
+
runs no prepare lifecycle for a tarball argument.)
|
|
111
|
+
|
|
112
|
+
1. Update `CHANGELOG.md` and set the new version in `package.json`. **CI checks
|
|
113
|
+
both**, in `scripts/verify-version-tag.mjs`: `package.json` must equal the
|
|
114
|
+
tag without its `v`, and `CHANGELOG.md` must carry a dated heading reading
|
|
115
|
+
exactly `## [X.Y.Z] — YYYY-MM-DD`. `CHANGELOG.md` ships inside the tarball,
|
|
116
|
+
so a forgotten entry documents the wrong version to every consumer and npm
|
|
117
|
+
will not take a version back.
|
|
118
|
+
|
|
119
|
+
The same script refuses a release tag that would move npm's `latest`
|
|
120
|
+
backwards — a backported `v1.0.1` published while `latest` is `2.0.0` would
|
|
121
|
+
make `npm i @fleetless/contracts`, the command the README gives outsiders,
|
|
122
|
+
install a package a major version behind.
|
|
123
|
+
2. Commit, push, and let the `verify` job go green on the branch.
|
|
124
|
+
3. Tag `vX.Y.Z` (or `vX.Y.Z-beta.N` for a pre-release, which publishes to the
|
|
125
|
+
`next` dist-tag) and push the tag. The tag pipeline runs `verify` again and
|
|
126
|
+
then `publish`.
|
|
127
|
+
|
|
128
|
+
`publish` needs an `NPM_TOKEN` variable, protected and masked. Protected means
|
|
129
|
+
an unprotected ref receives an empty value rather than a missing one, so the
|
|
130
|
+
tag pattern must be protected too; the job names that case before it can fail
|
|
131
|
+
on it obliquely.
|
|
132
|
+
|
|
133
|
+
**If the publish job goes red after `npm publish` has run, do not press
|
|
134
|
+
retry.** npm refuses to republish a version, so a retry fails with a 403 that
|
|
135
|
+
reads like a broken pipeline rather than like a release that already happened.
|
|
136
|
+
Check `npm view @fleetless/contracts@<version>` first.
|
package/README.md
CHANGED
|
@@ -49,15 +49,28 @@ import openapi from '@fleetless/contracts/artifacts/openapi.json' with { type: '
|
|
|
49
49
|
|
|
50
50
|
| Path | What it is |
|
|
51
51
|
|---|---|
|
|
52
|
-
| `artifacts/openapi.json` | The OpenAPI description of the REST API. |
|
|
53
|
-
| `artifacts/routes.json` | Every route with its request and response schema names. |
|
|
54
|
-
| `artifacts/schema/*.json` | One JSON Schema per wire shape, named after the shape
|
|
55
|
-
| `artifacts/schema-outgoing/*.json` |
|
|
52
|
+
| `artifacts/openapi.json` | The OpenAPI 3.1 description of the REST API, generated from the route manifest and the schemas. |
|
|
53
|
+
| `artifacts/routes.json` | Every route with its request and response schema names, and the notes that explain each one. |
|
|
54
|
+
| `artifacts/schema/*.json` | One JSON Schema per wire shape, named after the shape, rendered as **a receiver validates an incoming document**. |
|
|
55
|
+
| `artifacts/schema-outgoing/*.json` | Twelve of those same shapes rendered again as **a sender must produce them** — the frames a robot's bridge sends to the cloud. |
|
|
56
56
|
|
|
57
|
-
The
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
The two schema directories are the same shapes in zod's two rendering modes,
|
|
58
|
+
and the difference matters if you are writing a bridge.
|
|
59
|
+
|
|
60
|
+
`artifacts/schema/` is *input* mode: what a receiver accepts. Fleetless
|
|
61
|
+
receivers strip unknown keys rather than refusing them, so these documents omit
|
|
62
|
+
`additionalProperties: false`, and a field with a default is not marked
|
|
63
|
+
required.
|
|
64
|
+
|
|
65
|
+
`artifacts/schema-outgoing/` is *output* mode, for the twelve frames a bridge
|
|
66
|
+
**sends**. There a relaxed schema points the wrong way: a misspelled key in an
|
|
67
|
+
outgoing frame would pass an input-mode check and then be silently dropped by
|
|
68
|
+
the server. Validate what you send against `schema-outgoing/`, and what you
|
|
69
|
+
receive against `schema/`.
|
|
70
|
+
|
|
71
|
+
The artifacts are generated and committed rather than produced at install time
|
|
72
|
+
because a consumer in another language needs them without running a TypeScript
|
|
73
|
+
build.
|
|
61
74
|
|
|
62
75
|
## Versioning
|
|
63
76
|
|
|
@@ -69,6 +82,13 @@ client may ignore, or a description is a minor or a patch.
|
|
|
69
82
|
Pin an exact version. These shapes describe a running server, and "compatible
|
|
70
83
|
with the server you are talking to" is not something a range can express.
|
|
71
84
|
|
|
85
|
+
**The one dependency, `zod`, is deliberately a range and not a pin.** zod is a
|
|
86
|
+
peer in everything but name: a consumer that imports both this package and zod
|
|
87
|
+
must get one copy, or a schema from here fails an `instanceof` check against
|
|
88
|
+
their own. An exact pin here forces a second copy on anyone whose lockfile
|
|
89
|
+
resolves a different patch. The range floor is the version these schemas are
|
|
90
|
+
built, tested and exported against, and a zod major is a major here too.
|
|
91
|
+
|
|
72
92
|
## Documentation
|
|
73
93
|
|
|
74
94
|
The API reference at
|
|
@@ -76,12 +96,28 @@ The API reference at
|
|
|
76
96
|
generated from these artifacts and is the place to read what a field means.
|
|
77
97
|
This package is the place to read what it *is*.
|
|
78
98
|
|
|
79
|
-
##
|
|
99
|
+
## Reporting a security issue
|
|
100
|
+
|
|
101
|
+
Email **security@fleetless.dev**. Please do not open a public issue for a
|
|
102
|
+
security report.
|
|
103
|
+
|
|
104
|
+
**A wrong schema is a security report.** If a schema here accepts a value that
|
|
105
|
+
should never reach a server — an identifier that escapes its own grammar, a
|
|
106
|
+
missing bound, a union that admits a shape the API does not expect — that is a
|
|
107
|
+
vulnerability in this package, even though nothing in it executes anything. So
|
|
108
|
+
is a generated artifact that disagrees with the zod schema it came from, because
|
|
109
|
+
consumers that are not TypeScript validate against the artifact and never see
|
|
110
|
+
the zod.
|
|
111
|
+
|
|
112
|
+
`SECURITY.md` ships inside the published package and has the full policy: what
|
|
113
|
+
is in scope, what is not, and the response times we hold ourselves to.
|
|
114
|
+
|
|
115
|
+
## Contributing
|
|
80
116
|
|
|
81
|
-
|
|
82
|
-
request process.
|
|
83
|
-
|
|
84
|
-
|
|
117
|
+
`CONTRIBUTING.md` ships inside the published package and has the setup, the
|
|
118
|
+
checks and the pull request process. In short: `pnpm install`, then `pnpm
|
|
119
|
+
typecheck && pnpm test`; every schema change regenerates `artifacts/` with
|
|
120
|
+
`pnpm artifacts` and commits it in the same commit.
|
|
85
121
|
|
|
86
122
|
## Licence
|
|
87
123
|
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Reporting a vulnerability
|
|
4
|
+
|
|
5
|
+
Email **security@fleetless.dev**. Please do not open a public GitHub issue for
|
|
6
|
+
a security report.
|
|
7
|
+
|
|
8
|
+
Include what you found, the schema or artifact it concerns, and — if you have
|
|
9
|
+
one — a value that the schema accepts and should not, or rejects and should
|
|
10
|
+
not. A minimal reproduction against a published version of this package is the
|
|
11
|
+
most useful thing you can send.
|
|
12
|
+
|
|
13
|
+
We acknowledge every report within **3 working days** and follow up with
|
|
14
|
+
either a fix or a written plan within **30 days**. If a report leads to a
|
|
15
|
+
released fix, we credit you by name unless you ask us not to.
|
|
16
|
+
|
|
17
|
+
## What is in scope
|
|
18
|
+
|
|
19
|
+
This repository is a schema library. It defines the wire shapes of the
|
|
20
|
+
Fleetless API and the bridge–cloud protocol as zod schemas, and exports them
|
|
21
|
+
as JSON Schema and OpenAPI documents under `artifacts/`.
|
|
22
|
+
|
|
23
|
+
**A wrong schema is a security report.** If a schema accepts a value that
|
|
24
|
+
should never reach a server — an identifier that escapes its own grammar, a
|
|
25
|
+
bound that is missing, a union that admits a shape the API does not expect, a
|
|
26
|
+
field that a validator lets through unchecked — that is a vulnerability in
|
|
27
|
+
this package, even though nothing here executes it. The same is true of a
|
|
28
|
+
generated artifact that disagrees with the zod schema it came from, because
|
|
29
|
+
non-TypeScript consumers validate against the artifact and never see the zod.
|
|
30
|
+
|
|
31
|
+
So, in scope:
|
|
32
|
+
|
|
33
|
+
- the zod schemas in `src/`;
|
|
34
|
+
- the JSON Schema and OpenAPI artifacts in `artifacts/`, including any
|
|
35
|
+
disagreement between an artifact and its source schema;
|
|
36
|
+
- the build and export tooling in `scripts/`;
|
|
37
|
+
- the published npm package `@fleetless/contracts` and its contents.
|
|
38
|
+
|
|
39
|
+
## What is not in scope
|
|
40
|
+
|
|
41
|
+
**The Fleetless cloud is not in this repository.** A server that fails to
|
|
42
|
+
enforce a schema, an authentication or authorisation flaw, a rate limit, a
|
|
43
|
+
data leak from an API endpoint — none of those live here, and none of them can
|
|
44
|
+
be fixed by a change to this package. Report them to the same address; say
|
|
45
|
+
which service you were looking at, and we will route it. What we cannot do is
|
|
46
|
+
treat this repository's issue tracker as the place where they are tracked.
|
|
47
|
+
|
|
48
|
+
Also out of scope here: the Fleetless console, the bridge, the SDK, the
|
|
49
|
+
documentation site, and any deployment of Fleetless operated by someone else.
|
|
50
|
+
|
|
51
|
+
## Supported versions
|
|
52
|
+
|
|
53
|
+
The latest published minor of `@fleetless/contracts` receives fixes. Older
|
|
54
|
+
minors do not; a security fix is released as a new patch on the current minor,
|
|
55
|
+
and consumers pin exact versions, so upgrading is the remedy.
|
package/artifacts/openapi.json
CHANGED
|
@@ -3643,7 +3643,7 @@
|
|
|
3643
3643
|
}
|
|
3644
3644
|
}
|
|
3645
3645
|
},
|
|
3646
|
-
"description": "MCP's Streamable HTTP gives this path three verbs: `POST` carries JSON-RPC, `GET` opens the server-initiated SSE stream, and `DELETE` ends a session. This server has no sessions — the argument is in `MCP_PROTOCOL_VERSION`'s own note, and
|
|
3646
|
+
"description": "MCP's Streamable HTTP gives this path three verbs: `POST` carries JSON-RPC, `GET` opens the server-initiated SSE stream, and `DELETE` ends a session. This server has no sessions — the argument is in `MCP_PROTOCOL_VERSION`'s own note, and a per-process session map is what breaks at the second cloud instance — so `GET` and `DELETE` answer `405`, which is what a client is built to fall back from. \n\n**The `405` is this cloud's own answer, not the SDK's**, and the difference was measured: MCP SDK 1.30.0 opens an SSE stream on `GET` (`handleGetRequest`) and answers `200` on `DELETE` (`handleDeleteRequest`), neither of which a stateless server has any business doing, so the cloud writes the `405` itself in the transport's own JSON-RPC error shape with `Allow: POST`. \n\n**The row exists so that the `405` is not a `404`.** An unregistered verb answers `404`, and at a path whose last segment is an app identifier a `404` already means *no such app* — one answer for two states, which is the failure this project keeps paying for. Registering the verb lets the endpoint say \"this app's server is here; this verb is not part of it\". The central `/mcp` registers neither verb and does not need to: its path takes no parameter, so nothing can misread its `404`. \n\n**The `405` body is the transport's JSON-RPC error object, not the `apiError` envelope.** The three codes above are the refusals that come *first* — the app, its switch, then the bearer, in the order `POST` describes — and they are `apiError` because they are answered before the transport is reached at all. If this server ever becomes stateful, this row and the `DELETE` beside it are where that lands, and the cloud's route-manifest test is what would make both repositories notice."
|
|
3647
3647
|
},
|
|
3648
3648
|
"delete": {
|
|
3649
3649
|
"operationId": "delete_mcp_appIdentifier",
|
package/artifacts/routes.json
CHANGED
|
@@ -2370,7 +2370,7 @@
|
|
|
2370
2370
|
"forbidden"
|
|
2371
2371
|
],
|
|
2372
2372
|
"transport": "http",
|
|
2373
|
-
"notes": "MCP's Streamable HTTP gives this path three verbs: `POST` carries JSON-RPC, `GET` opens the server-initiated SSE stream, and `DELETE` ends a session. This server has no sessions — the argument is in `MCP_PROTOCOL_VERSION`'s own note, and
|
|
2373
|
+
"notes": "MCP's Streamable HTTP gives this path three verbs: `POST` carries JSON-RPC, `GET` opens the server-initiated SSE stream, and `DELETE` ends a session. This server has no sessions — the argument is in `MCP_PROTOCOL_VERSION`'s own note, and a per-process session map is what breaks at the second cloud instance — so `GET` and `DELETE` answer `405`, which is what a client is built to fall back from. \n\n**The `405` is this cloud's own answer, not the SDK's**, and the difference was measured: MCP SDK 1.30.0 opens an SSE stream on `GET` (`handleGetRequest`) and answers `200` on `DELETE` (`handleDeleteRequest`), neither of which a stateless server has any business doing, so the cloud writes the `405` itself in the transport's own JSON-RPC error shape with `Allow: POST`. \n\n**The row exists so that the `405` is not a `404`.** An unregistered verb answers `404`, and at a path whose last segment is an app identifier a `404` already means *no such app* — one answer for two states, which is the failure this project keeps paying for. Registering the verb lets the endpoint say \"this app's server is here; this verb is not part of it\". The central `/mcp` registers neither verb and does not need to: its path takes no parameter, so nothing can misread its `404`. \n\n**The `405` body is the transport's JSON-RPC error object, not the `apiError` envelope.** The three codes above are the refusals that come *first* — the app, its switch, then the bearer, in the order `POST` describes — and they are `apiError` because they are answered before the transport is reached at all. If this server ever becomes stateful, this row and the `DELETE` beside it are where that lands, and the cloud's route-manifest test is what would make both repositories notice."
|
|
2374
2374
|
},
|
|
2375
2375
|
{
|
|
2376
2376
|
"method": "DELETE",
|
package/dist/alerts.d.ts
CHANGED
|
@@ -1,28 +1,24 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
/**
|
|
3
|
-
|
|
4
|
-
*
|
|
4
|
+
/**
|
|
5
|
+
* Datapoint alerts and per-datapoint chart display config.
|
|
5
6
|
*
|
|
6
|
-
* An alert is a **state machine** (`ok ⇄ firing`), not a fire-once event
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* cloud at ingest.
|
|
7
|
+
* An alert is a **state machine** (`ok ⇄ firing`), not a fire-once event. The
|
|
8
|
+
* definition and the runtime state (`state`, `state_since`, `last_value`) are
|
|
9
|
+
* read together, and the cloud evaluates the alert at ingest.
|
|
10
10
|
*
|
|
11
|
-
* **
|
|
12
|
-
* is `config.ts`'s `datapointAlert`, nested under the datapoint it watches;
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* versioned document.
|
|
11
|
+
* **The definitions live in the configuration document.** The alert definition
|
|
12
|
+
* is `config.ts`'s `datapointAlert`, nested under the datapoint it watches; the
|
|
13
|
+
* chart bounds are `datapointChart`. They therefore take effect on publish
|
|
14
|
+
* rather than immediately, and in exchange every change to them is versioned,
|
|
15
|
+
* comparable and revertible. The runtime state stays in the database: it has no
|
|
16
|
+
* business in a versioned document.
|
|
18
17
|
*
|
|
19
|
-
* **What is
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* `cooldown_minutes`, `recipients` and `notify_on_resolve`, and the two
|
|
24
|
-
* bounds that guarded them. No alert can send mail, so nothing here describes
|
|
25
|
-
* one.
|
|
18
|
+
* **What is here is the read surface.** `GET /api/robots/:id/alerts` and
|
|
19
|
+
* `GET /api/org/alerts` answer with the definition joined to its state, and the
|
|
20
|
+
* shapes below are what they answer with. No alert sends mail, so nothing here
|
|
21
|
+
* describes one.
|
|
26
22
|
*/
|
|
27
23
|
/**
|
|
28
24
|
* `above`/`below` compare the numeric sample value (already scale/offset
|
|
@@ -104,10 +100,9 @@ export type AlertState = z.infer<typeof alertState>;
|
|
|
104
100
|
* document and the runtime state out of `datapoint_alert_state`, and the
|
|
105
101
|
* cloud joins the two per request (`routes/alerts.ts`'s `toWire`).
|
|
106
102
|
*
|
|
107
|
-
* **
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* the three had nothing behind them well before they were deleted.
|
|
103
|
+
* **There are no mail settings here.** The configuration format has no mail
|
|
104
|
+
* fields, so no alert can be configured to send one, and a shape describing
|
|
105
|
+
* recipients would describe a delivery path that does not exist.
|
|
111
106
|
*/
|
|
112
107
|
export declare const datapointAlertRow: z.ZodObject<{
|
|
113
108
|
id: z.ZodUUID;
|
package/dist/alerts.js
CHANGED
|
@@ -2,29 +2,24 @@
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { slug } from './common.js';
|
|
4
4
|
/**
|
|
5
|
-
|
|
6
|
-
*
|
|
5
|
+
/**
|
|
6
|
+
* Datapoint alerts and per-datapoint chart display config.
|
|
7
7
|
*
|
|
8
|
-
* An alert is a **state machine** (`ok ⇄ firing`), not a fire-once event
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* cloud at ingest.
|
|
8
|
+
* An alert is a **state machine** (`ok ⇄ firing`), not a fire-once event. The
|
|
9
|
+
* definition and the runtime state (`state`, `state_since`, `last_value`) are
|
|
10
|
+
* read together, and the cloud evaluates the alert at ingest.
|
|
12
11
|
*
|
|
13
|
-
* **
|
|
14
|
-
* is `config.ts`'s `datapointAlert`, nested under the datapoint it watches;
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* versioned document.
|
|
12
|
+
* **The definitions live in the configuration document.** The alert definition
|
|
13
|
+
* is `config.ts`'s `datapointAlert`, nested under the datapoint it watches; the
|
|
14
|
+
* chart bounds are `datapointChart`. They therefore take effect on publish
|
|
15
|
+
* rather than immediately, and in exchange every change to them is versioned,
|
|
16
|
+
* comparable and revertible. The runtime state stays in the database: it has no
|
|
17
|
+
* business in a versioned document.
|
|
20
18
|
*
|
|
21
|
-
* **What is
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* `cooldown_minutes`, `recipients` and `notify_on_resolve`, and the two
|
|
26
|
-
* bounds that guarded them. No alert can send mail, so nothing here describes
|
|
27
|
-
* one.
|
|
19
|
+
* **What is here is the read surface.** `GET /api/robots/:id/alerts` and
|
|
20
|
+
* `GET /api/org/alerts` answer with the definition joined to its state, and the
|
|
21
|
+
* shapes below are what they answer with. No alert sends mail, so nothing here
|
|
22
|
+
* describes one.
|
|
28
23
|
*/
|
|
29
24
|
/**
|
|
30
25
|
* `above`/`below` compare the numeric sample value (already scale/offset
|
|
@@ -102,10 +97,9 @@ export const alertState = z.enum(['ok', 'firing']);
|
|
|
102
97
|
* document and the runtime state out of `datapoint_alert_state`, and the
|
|
103
98
|
* cloud joins the two per request (`routes/alerts.ts`'s `toWire`).
|
|
104
99
|
*
|
|
105
|
-
* **
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* the three had nothing behind them well before they were deleted.
|
|
100
|
+
* **There are no mail settings here.** The configuration format has no mail
|
|
101
|
+
* fields, so no alert can be configured to send one, and a shape describing
|
|
102
|
+
* recipients would describe a delivery path that does not exist.
|
|
109
103
|
*/
|
|
110
104
|
export const datapointAlertRow = z.object({
|
|
111
105
|
id: z.uuid(),
|
package/dist/app-users.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
/**
|
|
3
4
|
* **App users: the per-app identity space** (spec `2026-09-05-app-user-auth`,
|
|
@@ -47,12 +48,12 @@ export declare const providerSlug: z.ZodString;
|
|
|
47
48
|
/**
|
|
48
49
|
* **The three states an app user can be in, and the order is the lifecycle.**
|
|
49
50
|
*
|
|
50
|
-
* - `pending_verification` — self-registered, mail sent, cannot log in yet
|
|
51
|
-
*
|
|
52
|
-
*
|
|
51
|
+
* - `pending_verification` — self-registered, mail sent, cannot log in yet.
|
|
52
|
+
* Without this state a domain allow-list would prove nothing: anybody could
|
|
53
|
+
* claim any address at an allowed domain.
|
|
53
54
|
* - `active` — may log in.
|
|
54
55
|
* - `blocked` — may not, and every refusal is the same `invalid_credentials`
|
|
55
|
-
* a wrong password gets
|
|
56
|
+
* a wrong password gets. A block that announced itself would be an
|
|
56
57
|
* account-enumeration oracle with an extra step.
|
|
57
58
|
*
|
|
58
59
|
* `pending_verification` is reached exactly once and left only by spending the
|
|
@@ -442,7 +443,7 @@ export type MailTemplateKind = z.infer<typeof mailTemplateKind>;
|
|
|
442
443
|
*/
|
|
443
444
|
export declare const MAIL_TEMPLATE_VARIABLES: readonly ["app.name", "org.name", "user.email", "user.display_name", "role.name", "link", "expires_in_hours"];
|
|
444
445
|
/**
|
|
445
|
-
* **The Fleetless default text for the three app mails
|
|
446
|
+
* **The Fleetless default text for the three app mails.**
|
|
446
447
|
*
|
|
447
448
|
* It lives here rather than in the cloud because two products send the same
|
|
448
449
|
* words: the cloud renders these when an app has no template of its own, and
|
|
@@ -476,7 +477,7 @@ export declare const MAIL_TEMPLATE_VARIABLES: readonly ["app.name", "org.name",
|
|
|
476
477
|
* that one is written by an authenticated developer about somebody they
|
|
477
478
|
* invited.
|
|
478
479
|
*
|
|
479
|
-
* **`expires_in_hours` is the only lifetime variable
|
|
480
|
+
* **`expires_in_hours` is the only lifetime variable a template gets**, and
|
|
480
481
|
* the three values are 1, 24 and 168. "The next 168 hours" is not how a person
|
|
481
482
|
* says a week, so each default converts: 48 and up reads in days, exactly one
|
|
482
483
|
* reads "1 hour", everything else reads in hours. The conversion is in the
|