@fleetless/sdk 3.0.1 → 3.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 +93 -10
- package/CONTRIBUTING.md +4 -3
- package/README.md +40 -14
- package/RELEASING.md +195 -0
- package/dist/index.cjs +28 -28
- package/dist/index.d.cts +0 -6
- package/dist/index.d.ts +0 -6
- package/dist/index.js +28 -28
- package/package.json +6 -9
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,73 @@ All notable changes to `@fleetless/sdk`. The format follows Keep a Changelog; th
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [3.0.2] — 2026-09-07
|
|
8
|
+
|
|
9
|
+
No API change, and one documentation example that now compiles. The guard
|
|
10
|
+
that was supposed to prove 3.0.1 clean was **green over four internal
|
|
11
|
+
references still inside the published bundle**, so it was rebuilt around the
|
|
12
|
+
question rather than around three directory names.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **Four internal references in the published 3.0.1 bundle**, which the guard
|
|
17
|
+
could not see: an internal decision label in `dist/index.js` and
|
|
18
|
+
`dist/index.cjs`, and a sentence in both about how a defect class had been
|
|
19
|
+
found. Measured against the registry rather than against this tree — 3.0.1
|
|
20
|
+
scores 4, 3.0.2 scores 0, and 3.0.0 scores 423.
|
|
21
|
+
- **The README's error-handling example did not compile.** `details` is
|
|
22
|
+
`unknown` on `FleetlessError`, deliberately, and the block read fields off
|
|
23
|
+
it directly — two `TS2339`s in a TypeScript SDK's own README, in the block a
|
|
24
|
+
reader copies first. It now narrows before reading, and it is opted in to
|
|
25
|
+
the documentation type check so it cannot drift again.
|
|
26
|
+
- **`Node 20 or newer` was listed as a runtime for the whole SDK.** Node 20
|
|
27
|
+
has no global `WebSocket` — it is behind a flag there — so anything realtime
|
|
28
|
+
fails on it with the SDK's own `no_websocket`. The README names Node 22, and
|
|
29
|
+
says what to pass on Node 20 instead.
|
|
30
|
+
- **Everything published pointed at a public repository that does not exist
|
|
31
|
+
yet.** `repository`, `bugs.url`, the README, `CONTRIBUTING.md` and
|
|
32
|
+
`RELEASING.md` all named it, and its four relative links resolved to 404s on
|
|
33
|
+
the npm page. They point at the maintainer address until the repository is
|
|
34
|
+
opened.
|
|
35
|
+
- **`CONTRIBUTING.md` linked `RELEASING.md`, which the tarball did not
|
|
36
|
+
carry.** `RELEASING.md` ships now, and every relative link in every shipped
|
|
37
|
+
document is checked against the tarball's own entries.
|
|
38
|
+
- **Each published file declares exactly one licence.** The bundler inlines
|
|
39
|
+
`@fleetless/contracts`, which is Apache-2.0, and the declaration files
|
|
40
|
+
carried six of its SPDX identifiers below this package's MIT one. A licence
|
|
41
|
+
scan over `node_modules` reported Apache obligations for a package that
|
|
42
|
+
ships no Apache text. The inherited identifiers are stripped at build time.
|
|
43
|
+
- **A CI comment sent whoever was reading a red publish job to a README
|
|
44
|
+
section that does not exist**, and `RELEASING.md` told a maintainer the
|
|
45
|
+
suite runs against a fake `fetch` — which `CONTRIBUTING.md` devotes a
|
|
46
|
+
section to denying, and which is untrue of three of the nineteen suites.
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
|
|
50
|
+
- **The guard scans what becomes public, computed rather than named.** The
|
|
51
|
+
union of what `npm pack` reports, what `git ls-files` reports and a walk of
|
|
52
|
+
every directory in `files`. Ten tracked files were outside the previous
|
|
53
|
+
shape, two of them published bytes; one of those two had already carried an
|
|
54
|
+
internal host to the registry inside `devDependencies`.
|
|
55
|
+
- **Only the German scan strips anything**, and only URLs and single-token
|
|
56
|
+
code spans. Stripping links and backticks before every class made the six
|
|
57
|
+
shipped documents blind to an internal hostname inside a markdown link.
|
|
58
|
+
- **Eighteen detectors, each carrying its own two fixtures**, with a floor
|
|
59
|
+
over the count. Seven of the previous fifteen had no fixture proving they
|
|
60
|
+
could fire, and two could be deleted with the whole suite green.
|
|
61
|
+
- **The tarball guard asserts absence as well as presence**: nothing outside
|
|
62
|
+
`files`, no sourcemaps, no source. It reads every dependency section rather
|
|
63
|
+
than the runtime one alone, and runs the marker detectors over the packed
|
|
64
|
+
manifest.
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- **`test/readme-pointers.test.ts`**: every pointer from the code into the
|
|
69
|
+
README resolves to a heading that is there. That was the defect 3.0.1 fixed
|
|
70
|
+
and left unguarded, one of them inside a runtime error message.
|
|
71
|
+
- **`scripts/verify-commit-messages.mjs`**, wired into the verify job. A
|
|
72
|
+
commit message is public the moment it is pushed.
|
|
73
|
+
|
|
7
74
|
## [3.0.1] — 2026-09-07
|
|
8
75
|
|
|
9
76
|
No API change. This release exists because **3.0.0 published internal material
|
|
@@ -18,17 +85,33 @@ without it.
|
|
|
18
85
|
comment beside it. In 3.0.0 those paths encoded a `git+ssh://` dependency
|
|
19
86
|
specifier, so an internal hostname appeared **48 times** across the two
|
|
20
87
|
bundles; the inlined comments carried German paragraphs and internal defect
|
|
21
|
-
ids with them.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
88
|
+
ids with them.
|
|
89
|
+
|
|
90
|
+
The table this entry first carried cited "the same seventeen detectors",
|
|
91
|
+
while the entry below it said the guard had fifteen classes and the guard
|
|
92
|
+
itself asserted fifteen. No seventeen-detector artefact ever existed, so
|
|
93
|
+
the numbers could not be reproduced from anything shipped. They are
|
|
94
|
+
replaced here by a measurement of the two **published tarballs** with the
|
|
95
|
+
guard as rebuilt in 3.0.2, which is a thing a reader can run:
|
|
96
|
+
|
|
97
|
+
| class | 3.0.0 | 3.0.1 |
|
|
98
|
+
|---|---:|---:|
|
|
99
|
+
| terse schedule label | 134 | 0 |
|
|
27
100
|
| German prose | 58 | 0 |
|
|
28
|
-
| internal
|
|
29
|
-
| internal decision
|
|
30
|
-
|
|
|
31
|
-
|
|
|
101
|
+
| internal host or workspace name | 49 | 0 |
|
|
102
|
+
| internal decision label | 44 | **2** |
|
|
103
|
+
| a reference to this package rather than the reader's | 30 | 0 |
|
|
104
|
+
| internal review codename | 28 | 0 |
|
|
105
|
+
| a reference to a document the reader does not have | 26 | 0 |
|
|
106
|
+
| longer schedule label | 16 | 0 |
|
|
107
|
+
| how the behaviour was found | 16 | **2** |
|
|
108
|
+
| internal defect id | 14 | 0 |
|
|
109
|
+
| internal feature id | 4 | 0 |
|
|
110
|
+
| the reference robot by name | 4 | 0 |
|
|
111
|
+
| **total** | **423** | **4** |
|
|
112
|
+
|
|
113
|
+
The four remaining in 3.0.1 are the subject of 3.0.2 below. They are here
|
|
114
|
+
rather than in that entry because this is the entry that claimed zero.
|
|
32
115
|
|
|
33
116
|
None of it appeared in this repository's own source, which is why no sweep of
|
|
34
117
|
the source had found it. The fix is the contracts pin — `@fleetless/contracts`
|
package/CONTRIBUTING.md
CHANGED
|
@@ -88,9 +88,10 @@ came back and on which line.
|
|
|
88
88
|
|
|
89
89
|
## Pull requests
|
|
90
90
|
|
|
91
|
-
**
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
**The public repository is not open yet.** It will be, and this section
|
|
92
|
+
describes how it will work then. Until it exists, send patches and questions to
|
|
93
|
+
<hello@fleetless.dev>. Either way, raise anything that changes an existing
|
|
94
|
+
method signature or what a method sends before you write it, so we can say what
|
|
94
95
|
else has to move with it.
|
|
95
96
|
|
|
96
97
|
**CI runs on GitLab.** This repository is mirrored from an internal GitLab
|
package/README.md
CHANGED
|
@@ -12,8 +12,12 @@ behind roles. The SDK covers all of it: datapoints (read, subscribe, recorded
|
|
|
12
12
|
history), actions, services, publishers, cameras, jobs, assets and URDF, and
|
|
13
13
|
the whole client auth API your own sign-in UI calls. It is framework-agnostic,
|
|
14
14
|
ships ESM and CJS with its own types, and runs wherever a `fetch` and a
|
|
15
|
-
`WebSocket` exist — a browser, a mobile webview, Node
|
|
16
|
-
server-side with a server key instead of a user session.
|
|
15
|
+
`WebSocket` exist — a browser, a mobile webview, Node 22 or newer, and
|
|
16
|
+
server-side with a server key instead of a user session. On Node 20 the REST
|
|
17
|
+
half works as it stands, but there is no global `WebSocket` (it is behind
|
|
18
|
+
`--experimental-websocket` there), so anything realtime needs one supplied:
|
|
19
|
+
`createClient({ …, WebSocket: (await import('ws')).WebSocket })`, or the flag.
|
|
20
|
+
Without it, `subscribe` calls `onError` with `no_websocket`.
|
|
17
21
|
|
|
18
22
|
**The mental model in one paragraph.** A robot runs the Fleetless bridge, a
|
|
19
23
|
ROS 2 node. In the [Fleetless Console](https://console.fleetless.dev) a
|
|
@@ -262,7 +266,7 @@ it is not true. So *how often* and *when* to publish is your application's
|
|
|
262
266
|
decision, made where it can see the user's intent:
|
|
263
267
|
|
|
264
268
|
```ts
|
|
265
|
-
// A control loop is yours to write, and yours to stop.
|
|
269
|
+
// A control loop is yours to write, and yours to stop. `speed` is your app's.
|
|
266
270
|
const timer = setInterval(() => {
|
|
267
271
|
void client.publishers.publish(robotId, 'cmd_vel', { 'linear.x': speed })
|
|
268
272
|
}, 100)
|
|
@@ -300,27 +304,48 @@ Every refusal arrives as a `FleetlessError` with a stable `code`. Branch on the
|
|
|
300
304
|
code; never parse the message, which is written for a developer reading a
|
|
301
305
|
console and may change.
|
|
302
306
|
|
|
303
|
-
|
|
304
|
-
|
|
307
|
+
`error.details` is typed `unknown`, deliberately: it is whatever the server
|
|
308
|
+
sent, and the SDK does not pretend to have validated it. Narrow it before you
|
|
309
|
+
read a field. `BusyDetails` and `RateLimitDetails` are exported as types, and
|
|
310
|
+
`parameterInvalidDetails` as a runtime schema you can `parse`.
|
|
311
|
+
|
|
312
|
+
```ts checked
|
|
313
|
+
import { createClient, FleetlessError } from '@fleetless/sdk'
|
|
314
|
+
import type { BusyDetails, RateLimitDetails } from '@fleetless/sdk'
|
|
315
|
+
|
|
316
|
+
declare function showRunning(job: BusyDetails['running'] | undefined): void
|
|
317
|
+
declare function showOffline(): void
|
|
318
|
+
declare function showNotAllowed(): void
|
|
319
|
+
declare function showRetryLater(ms: number | undefined): void
|
|
320
|
+
declare function showUnexpected(error: FleetlessError): void
|
|
321
|
+
|
|
322
|
+
const client = createClient({ apiUrl: 'https://api.fleetless.dev', appIdentifier: 'warehouse_dash' })
|
|
323
|
+
const robotId = '4f2c1a90-7b3e-4d51-9c86-0a1b2c3d4e5f'
|
|
324
|
+
|
|
325
|
+
/** `details` is server-shaped: check the field is there before reading it. */
|
|
326
|
+
const field = <T,>(details: unknown, key: string): T | undefined =>
|
|
327
|
+
typeof details === 'object' && details !== null && key in details
|
|
328
|
+
? ((details as Record<string, unknown>)[key] as T)
|
|
329
|
+
: undefined
|
|
305
330
|
|
|
306
331
|
try {
|
|
307
332
|
await client.actions.invoke(robotId, 'dock', {})
|
|
308
333
|
} catch (error) {
|
|
309
334
|
if (!(error instanceof FleetlessError)) throw error
|
|
310
335
|
switch (error.code) {
|
|
311
|
-
case 'busy':
|
|
312
|
-
case 'robot_offline':
|
|
313
|
-
case 'forbidden':
|
|
314
|
-
case 'rate_limited':
|
|
315
|
-
default:
|
|
336
|
+
case 'busy': showRunning(field<BusyDetails['running']>(error.details, 'running')); break
|
|
337
|
+
case 'robot_offline': showOffline(); break
|
|
338
|
+
case 'forbidden': showNotAllowed(); break
|
|
339
|
+
case 'rate_limited': showRetryLater(field<RateLimitDetails['retry_after_ms']>(error.details, 'retry_after_ms')); break
|
|
340
|
+
default: showUnexpected(error); break
|
|
316
341
|
}
|
|
317
342
|
}
|
|
318
343
|
```
|
|
319
344
|
|
|
320
345
|
- **`rate_limited` is surfaced, never retried behind your back.** The SDK does
|
|
321
|
-
not sleep and re-send. `
|
|
322
|
-
|
|
323
|
-
|
|
346
|
+
not sleep and re-send. `details.retry_after_ms` carries the wait the platform
|
|
347
|
+
asked for, when it sent one — read it defensively, since a refusal from an
|
|
348
|
+
intermediary may carry no `details` at all.
|
|
324
349
|
- **The one retry the SDK does perform is `token_expired`**, and only once: it
|
|
325
350
|
refreshes the session and re-sends the same request. That refresh is
|
|
326
351
|
single-flight, so ten concurrent calls meeting an expired token share one.
|
|
@@ -361,7 +386,8 @@ belongs to the platform instead.
|
|
|
361
386
|
|
|
362
387
|
## Contributing
|
|
363
388
|
|
|
364
|
-
|
|
389
|
+
The public repository is not open yet. Until it is, send patches and questions
|
|
390
|
+
to <hello@fleetless.dev>. Read
|
|
365
391
|
[CONTRIBUTING.md](CONTRIBUTING.md) first: it covers the setup, the checks, the
|
|
366
392
|
Contributor Licence Agreement, and the one rule this repository is strict about
|
|
367
393
|
— the auth suites drive the SDK's own `fetch` against a real `node:http`
|
package/RELEASING.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Releasing `@fleetless/sdk`
|
|
2
|
+
|
|
3
|
+
Maintainer notes: the development setup, the checks, and the release
|
|
4
|
+
procedure. This file is not part of the published package.
|
|
5
|
+
|
|
6
|
+
**CI runs on GitLab; the repository will mirror to GitHub.** The pipeline
|
|
7
|
+
that verifies and publishes this package lives on an internal GitLab instance,
|
|
8
|
+
and that is the only thing that publishes to npm. The public repository does not
|
|
9
|
+
exist yet; when it does it is a mirror, where issues and pull requests arrive
|
|
10
|
+
and no check runs. A contributor's pull request is verified by a maintainer
|
|
11
|
+
running the same commands locally — see
|
|
12
|
+
[CONTRIBUTING.md](CONTRIBUTING.md), which says so to the contributor as well.
|
|
13
|
+
|
|
14
|
+
A mirror pushes what it is given, so **everything in a commit becomes public
|
|
15
|
+
the moment it is pushed**, including the commit message and every file the
|
|
16
|
+
branch touched.
|
|
17
|
+
## Development setup
|
|
18
|
+
|
|
19
|
+
Node 22 via `nvm`, and pnpm through corepack:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
nvm use 22
|
|
23
|
+
corepack enable
|
|
24
|
+
pnpm install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**That install needs nothing private.** `@fleetless/contracts` — the source
|
|
28
|
+
of truth for every wire type this SDK reads or writes — is a devDependency on
|
|
29
|
+
the public npm package, pinned to an exact version. It used to be a private
|
|
30
|
+
`git+ssh://` URL that only somebody with GitLab group access could install;
|
|
31
|
+
that is over. The published SDK is unaffected either way: `tsup` inlines
|
|
32
|
+
contracts' types into `dist/`, so a consumer of `@fleetless/sdk` never
|
|
33
|
+
resolves it.
|
|
34
|
+
|
|
35
|
+
Never redefine a shape the SDK sends to or reads from the API. Import it from
|
|
36
|
+
`@fleetless/contracts` instead.
|
|
37
|
+
|
|
38
|
+
## Checks
|
|
39
|
+
|
|
40
|
+
| Command | What it does |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `pnpm typecheck` | `tsc --noEmit` over the package. |
|
|
43
|
+
| `pnpm test` | vitest. Most suites drive a fake `fetch` and a fake WebSocket; the three auth suites drive the SDK's own default `fetch` against a real `node:http` server (`test/local-api.ts`). No cloud needed. |
|
|
44
|
+
| `pnpm build` | tsup into `dist/` — ESM, CJS and `.d.ts`. |
|
|
45
|
+
| `pnpm run test:pack` | `npm pack`s the tarball, installs it into a bare project with nothing but `typescript`, and typechecks and runs real usage against it. This is what catches a `dist/index.d.ts` that still imports from `@fleetless/contracts` — a devDependency, so a consumer of the SDK never installs it. |
|
|
46
|
+
|
|
47
|
+
Two further scripts check the built package against a **running** cloud
|
|
48
|
+
(`./infra/dev.sh` in the umbrella repo). Each one runs against `dist/`, not
|
|
49
|
+
`src/`, so run `pnpm build` first, and each fails by name on a missing
|
|
50
|
+
variable rather than defaulting quietly.
|
|
51
|
+
|
|
52
|
+
- `pnpm run verify:live` — a `busy` refusal carrying the job that is already
|
|
53
|
+
running, `command_outcome_unknown` and its documented recovery, a
|
|
54
|
+
`parameter_invalid` naming the flat key, job-id-addressed cancel, and
|
|
55
|
+
per-session camera release. Needs `FLEETLESS_API_URL` (or `API`),
|
|
56
|
+
`APP_IDENTIFIER`, `EMAIL`, `PASSWORD`, `ROBOT_ID`, `ACTION_SLUG`,
|
|
57
|
+
`SERVICE_SLUG`, `CAMERA_SLUG`, and two optional identities:
|
|
58
|
+
- `SECOND_EMAIL` — a second, distinct **app user** with the same role,
|
|
59
|
+
without which the busy check only proves that a second request is refused,
|
|
60
|
+
not that a different user's is.
|
|
61
|
+
- `OBSERVER_EMAIL` — a **third** app user, and the one whose password check
|
|
62
|
+
[6] rotates and restores. Leave it unset and the round trip runs on
|
|
63
|
+
`EMAIL`, the identity every other check in the file signs in as; the
|
|
64
|
+
script says so out loud when that happens. `infra/seed-dev.mjs --env`
|
|
65
|
+
exports all three.
|
|
66
|
+
|
|
67
|
+
Since 3.0.0 it also drives the client auth API as check [6]: `listProviders`
|
|
68
|
+
without a session, `login`, `me` (kind, app, role, address), `logout`, the
|
|
69
|
+
`changePassword` round trip with its restore, and the reset acknowledgement
|
|
70
|
+
compared byte for byte across a known and an unknown address. What it does
|
|
71
|
+
**not** drive is anything needing a mailed token — `register`,
|
|
72
|
+
`verifyEmail`, `acceptInvitation`, `confirmPasswordReset` — or the federated
|
|
73
|
+
and MCP-consent flows. Those are `infra/browser/app-auth-check.mjs`'s
|
|
74
|
+
subject, which reads maildev and drives a real Keycloak.
|
|
75
|
+
- `pnpm run verify:history` — a relative range and its absolute equivalent
|
|
76
|
+
returning the same samples, aggregation matching arithmetic done here from
|
|
77
|
+
the raw rows, and the `not_recorded` / `not_aggregatable` refusals. Needs
|
|
78
|
+
`FLEETLESS_API_URL`, `APP_IDENTIFIER`, `EMAIL`, `PASSWORD`, `ROBOT_ID`,
|
|
79
|
+
`RECORDED_NUMERIC_SLUG`, `LIVE_ONLY_SLUG`, and optionally
|
|
80
|
+
`NON_NUMERIC_RECORDED_SLUG`.
|
|
81
|
+
(`verify:hosted-login` is gone. The hosted login flow it drove —
|
|
82
|
+
`beginHostedLogin` / `completeHostedLogin` and the app OAuth client behind
|
|
83
|
+
them — was removed in 3.0.0 along with the script and its `package.json`
|
|
84
|
+
entry.)
|
|
85
|
+
|
|
86
|
+
`infra/seed-dev.mjs --env` in the umbrella repo exports most of those
|
|
87
|
+
variables for a freshly seeded world.
|
|
88
|
+
|
|
89
|
+
## Commits
|
|
90
|
+
|
|
91
|
+
[Conventional Commits](https://www.conventionalcommits.org/). English, for
|
|
92
|
+
code, comments, commit messages and everything else that lands in the
|
|
93
|
+
repository.
|
|
94
|
+
|
|
95
|
+
## Releasing
|
|
96
|
+
|
|
97
|
+
Every version on npm is published by this repository's GitLab pipeline from a
|
|
98
|
+
release tag. `npm publish` by hand is retired.
|
|
99
|
+
|
|
100
|
+
1. Add the version's entry to `CHANGELOG.md` and set `version` in
|
|
101
|
+
`package.json` to the same number.
|
|
102
|
+
2. Commit (`chore(release): X.Y.Z`), push, and wait for the branch pipeline's
|
|
103
|
+
`verify` job to go green.
|
|
104
|
+
3. `git tag vX.Y.Z && git push origin vX.Y.Z`. The tag pipeline runs `verify`
|
|
105
|
+
again and then `publish`.
|
|
106
|
+
4. Check the registry yourself. The `publish` job already asserts the first
|
|
107
|
+
line; this is the independent look, and the second line is the one that
|
|
108
|
+
says which dist-tag moved.
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
npm view @fleetless/sdk@X.Y.Z version # answers X.Y.Z
|
|
112
|
+
npm view @fleetless/sdk dist-tags # latest -> X.Y.Z, or next -> X.Y.Z
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Name the version. A bare `npm view @fleetless/sdk version` resolves the
|
|
116
|
+
`latest` dist-tag, so after a pre-release publish it answers the *previous*
|
|
117
|
+
stable release and reads as a publish that did not happen.
|
|
118
|
+
|
|
119
|
+
A pre-release tag — `vX.Y.Z-beta.1`, `vX.Y.Z-rc.2` — publishes under the npm
|
|
120
|
+
dist-tag `next` instead of `latest`. Nothing else about it differs, and it is
|
|
121
|
+
exactly why step 4 names the version.
|
|
122
|
+
|
|
123
|
+
**A red `publish` job does not mean nothing was published.** The job runs
|
|
124
|
+
`npm publish` and then looks the version up on the registry for about two
|
|
125
|
+
minutes; npm answers reads from a replica that lags a publish, so the lookup
|
|
126
|
+
can time out on a version that did land. The job says so itself, and it is
|
|
127
|
+
worth repeating here because a red pipeline invites exactly one reaction —
|
|
128
|
+
press retry — and that reaction cannot work: npm refuses to publish over an
|
|
129
|
+
existing version, so the retry ends in a 403 that reads like a broken
|
|
130
|
+
pipeline rather than like a release that already happened.
|
|
131
|
+
|
|
132
|
+
> publish may have succeeded; the registry has not served the version yet; do
|
|
133
|
+
> NOT retry this job (npm refuses to republish a version) — check
|
|
134
|
+
> `npm view @fleetless/sdk@$VERSION` by hand
|
|
135
|
+
|
|
136
|
+
If the hand check answers the version, the release is done: move the dist-tag
|
|
137
|
+
by hand if it is wrong (`npm dist-tag add @fleetless/sdk@X.Y.Z latest`) and
|
|
138
|
+
leave the job red. If it answers nothing after several minutes, the publish
|
|
139
|
+
genuinely did not land and the job can be retried.
|
|
140
|
+
|
|
141
|
+
**A red `publish` job that ends in `npm error code EOTP` published nothing.**
|
|
142
|
+
npm is asking for a one-time password, which a pipeline cannot supply: the
|
|
143
|
+
token in `NPM_TOKEN` does not bypass two-factor authentication, or the
|
|
144
|
+
package's *Publishing access* setting on npmjs.com disallows tokens. Fix the
|
|
145
|
+
token (a granular access token created with *Bypass two-factor
|
|
146
|
+
authentication*) or the package setting (*Require two-factor authentication
|
|
147
|
+
or an automation token*), then retry the job — nothing reached the registry,
|
|
148
|
+
so a retry is safe here — measured on a pipeline that hit it.
|
|
149
|
+
|
|
150
|
+
**The pipeline refuses a tag whose version disagrees with `package.json`.**
|
|
151
|
+
`scripts/verify-version-tag.mjs` is the one place that rule lives; `verify`
|
|
152
|
+
runs it first on a tag pipeline, so a mistyped tag fails in seconds and
|
|
153
|
+
`publish` never starts (measured: `verify-version-tag: tag v9.9.9 names
|
|
154
|
+
9.9.9 but package.json says 1.0.0`, publish skipped).
|
|
155
|
+
|
|
156
|
+
Removing that bad tag takes the API, not git. `v*` is a **protected** tag
|
|
157
|
+
pattern, so a delete over git is refused by the server with nothing but
|
|
158
|
+
`! [remote rejected] v9.9.9 (pre-receive hook declined)`:
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
git tag -d vX.Y.Z # local
|
|
162
|
+
glab api -X DELETE projects/37/repository/tags/vX.Y.Z # remote
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Then fix `package.json` and tag again.
|
|
166
|
+
|
|
167
|
+
### The one-time setting
|
|
168
|
+
|
|
169
|
+
It is already in place; it is written down because nothing in this repository
|
|
170
|
+
would tell you it exists if it were removed.
|
|
171
|
+
|
|
172
|
+
There used to be a second one — `fleetless/fleetless-sdk` on the job-token
|
|
173
|
+
allow-list of `fleetless/fleetless-contracts`, so the pipeline could rewrite
|
|
174
|
+
the private `git+ssh://` contracts URL to HTTPS with `CI_JOB_TOKEN`. Contracts
|
|
175
|
+
is a public npm package now, the rewrite is gone from `.gitlab-ci.yml`, and
|
|
176
|
+
the allow-list entry buys this project nothing. Removing it is safe; leaving
|
|
177
|
+
it is harmless.
|
|
178
|
+
|
|
179
|
+
- **`NPM_TOKEN` is a protected, masked CI variable on this project**, holding
|
|
180
|
+
an npm granular automation token with publish rights on `@fleetless/sdk`.
|
|
181
|
+
Protected means an unprotected ref receives an *empty* value rather than no
|
|
182
|
+
value, which is why the tag pattern `v*` is a protected tag and why the
|
|
183
|
+
`publish` job's first action is to refuse an empty `NPM_TOKEN` by name.
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
glab api projects/37/protected_tags # v*, create access: Maintainers
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The token reaches npm through an `.npmrc` written in the job's working
|
|
190
|
+
directory holding `//registry.npmjs.org/:_authToken=${NPM_TOKEN}` **literally**
|
|
191
|
+
— npm expands the variable when it reads the file, so the secret itself never
|
|
192
|
+
lands on disk, and `after_script` removes the file either way. It cannot be
|
|
193
|
+
passed as an environment assignment instead: `NPM_CONFIG_//registry…` is not a
|
|
194
|
+
valid shell identifier, so both `bash` and `dash` parse it as a command name.
|
|
195
|
+
|
package/dist/index.cjs
CHANGED
|
@@ -144,7 +144,7 @@ var HttpClient = class {
|
|
|
144
144
|
* RequestOptionsFor<P>` would type-check by construction — the cast
|
|
145
145
|
* bypasses the very check this exists to add, which is the identical
|
|
146
146
|
* "special case that quietly exempts calls from the general rule" shape
|
|
147
|
-
* this file has already
|
|
147
|
+
* this file has already made once. Every current call site to a route
|
|
148
148
|
* with no body already passes `{}` explicitly for exactly this reason.
|
|
149
149
|
*/
|
|
150
150
|
async request(path, options) {
|
|
@@ -441,7 +441,7 @@ function createAssetsApi(http) {
|
|
|
441
441
|
};
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
444
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/common.js
|
|
445
445
|
var import_zod = require("zod");
|
|
446
446
|
var SLUG_RULE = "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore \u2014 `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused.";
|
|
447
447
|
var slug = import_zod.z.string().min(2).max(63).regex(/^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/, SLUG_RULE);
|
|
@@ -465,7 +465,7 @@ var applyError = import_zod.z.object({
|
|
|
465
465
|
details: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
466
466
|
});
|
|
467
467
|
|
|
468
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
468
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/mcp.js
|
|
469
469
|
var import_zod2 = require("zod");
|
|
470
470
|
function mcpAppEndpointPath(appIdentifier2) {
|
|
471
471
|
return `/mcp/${appIdentifier2}`;
|
|
@@ -514,10 +514,10 @@ var mcpRolePreviewResponse = import_zod2.z.object({
|
|
|
514
514
|
});
|
|
515
515
|
var MCP_ASSET_LINK_TTL_MS = 15 * 60 * 1e3;
|
|
516
516
|
|
|
517
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
517
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/protocol.js
|
|
518
518
|
var import_zod8 = require("zod");
|
|
519
519
|
|
|
520
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
520
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/assets.js
|
|
521
521
|
var import_zod3 = require("zod");
|
|
522
522
|
var assetKind = import_zod3.z.enum(["urdf", "mesh", "texture", "other"]);
|
|
523
523
|
var asset = import_zod3.z.object({
|
|
@@ -775,10 +775,10 @@ var assetSyncBusyDetails = import_zod3.z.object({
|
|
|
775
775
|
started_at_ms: import_zod3.z.number().int().nonnegative()
|
|
776
776
|
});
|
|
777
777
|
|
|
778
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
778
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/config.js
|
|
779
779
|
var import_zod5 = require("zod");
|
|
780
780
|
|
|
781
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
781
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/alerts.js
|
|
782
782
|
var import_zod4 = require("zod");
|
|
783
783
|
var alertRowCondition = import_zod4.z.discriminatedUnion("kind", [
|
|
784
784
|
import_zod4.z.strictObject({
|
|
@@ -848,7 +848,7 @@ var putDatapointDisplayRequest = import_zod4.z.object({
|
|
|
848
848
|
y_max: import_zod4.z.number().finite().nullable()
|
|
849
849
|
}).strict();
|
|
850
850
|
|
|
851
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
851
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/config.js
|
|
852
852
|
var RTSP_URL_RULE = "The URL has to begin with `rtsp://` or `rtsps://` \u2014 `rtsp://cam-1.plant.local/stream1`. No other scheme is accepted: the bridge opens this with a library that would equally honour `file:`.";
|
|
853
853
|
var MJPEG_URL_RULE = "The URL has to begin with `http://` or `https://` \u2014 `http://cam-1.plant.local/video.mjpg`. No other scheme is accepted: the bridge opens this with a library that would equally serve `file:`.";
|
|
854
854
|
var DEVICE_PATH_RULE = "A capture device is a path under `/dev/`, and the character straight after it is a letter or a digit \u2014 `/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.";
|
|
@@ -1895,7 +1895,7 @@ var configState = import_zod5.z.object({
|
|
|
1895
1895
|
applied_errors: import_zod5.z.array(applyError).nullable()
|
|
1896
1896
|
});
|
|
1897
1897
|
|
|
1898
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
1898
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/introspection.js
|
|
1899
1899
|
var import_zod6 = require("zod");
|
|
1900
1900
|
var rosGraphEntry = import_zod6.z.object({
|
|
1901
1901
|
name: rosName,
|
|
@@ -1934,7 +1934,7 @@ var typeDefinition = import_zod6.z.discriminatedUnion("kind", [
|
|
|
1934
1934
|
})
|
|
1935
1935
|
]);
|
|
1936
1936
|
|
|
1937
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
1937
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/jobs.js
|
|
1938
1938
|
var import_zod7 = require("zod");
|
|
1939
1939
|
var jobState = import_zod7.z.enum(["running", "succeeded", "failed", "cancelled", "lost"]);
|
|
1940
1940
|
var job = import_zod7.z.object({
|
|
@@ -2152,7 +2152,7 @@ var jobRunSummary = import_zod7.z.object({
|
|
|
2152
2152
|
since_ms: import_zod7.z.number().int().nonnegative()
|
|
2153
2153
|
});
|
|
2154
2154
|
|
|
2155
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
2155
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/protocol.js
|
|
2156
2156
|
var MAX_PATIENCE_MS = 12e4;
|
|
2157
2157
|
var MIN_PATIENCE_MS = 1e3;
|
|
2158
2158
|
var activeJob = import_zod8.z.object({
|
|
@@ -2551,11 +2551,11 @@ var bridgeCameraState = import_zod8.z.object({
|
|
|
2551
2551
|
request_id: import_zod8.z.string().min(1).max(64).nullable()
|
|
2552
2552
|
});
|
|
2553
2553
|
|
|
2554
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
2554
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/config-issues.js
|
|
2555
2555
|
var EXPOSURE_SECTIONS = ["datapoints", "actions", "services", "publishers", "cameras"];
|
|
2556
2556
|
var EXPOSURE_SECTION_NAMES = new Set(EXPOSURE_SECTIONS);
|
|
2557
2557
|
|
|
2558
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
2558
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/rest.js
|
|
2559
2559
|
var import_zod9 = require("zod");
|
|
2560
2560
|
var robot = import_zod9.z.object({
|
|
2561
2561
|
id: import_zod9.z.uuid().meta({
|
|
@@ -3315,13 +3315,13 @@ var slugUsageResponse = import_zod9.z.object({
|
|
|
3315
3315
|
alert_count: import_zod9.z.number().int().nonnegative()
|
|
3316
3316
|
});
|
|
3317
3317
|
|
|
3318
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3318
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/realtime.js
|
|
3319
3319
|
var import_zod14 = require("zod");
|
|
3320
3320
|
|
|
3321
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3321
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/client-auth.js
|
|
3322
3322
|
var import_zod13 = require("zod");
|
|
3323
3323
|
|
|
3324
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3324
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/apps.js
|
|
3325
3325
|
var import_zod10 = require("zod");
|
|
3326
3326
|
var appIdentifier = slug;
|
|
3327
3327
|
var app = import_zod10.z.object({
|
|
@@ -3503,10 +3503,10 @@ var rolePermissions = import_zod10.z.object({
|
|
|
3503
3503
|
})
|
|
3504
3504
|
});
|
|
3505
3505
|
|
|
3506
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3506
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/app-users.js
|
|
3507
3507
|
var import_zod12 = require("zod");
|
|
3508
3508
|
|
|
3509
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3509
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/identity.js
|
|
3510
3510
|
var import_zod11 = require("zod");
|
|
3511
3511
|
var password = import_zod11.z.string().min(12).max(256);
|
|
3512
3512
|
var USER_DISPLAY_NAME_MAX = 120;
|
|
@@ -3668,7 +3668,7 @@ var authMeResponse = import_zod11.z.object({ org, user: fleetlessUser });
|
|
|
3668
3668
|
var patchOrgRequest = import_zod11.z.object({ name: import_zod11.z.string().min(1).max(120) }).strict();
|
|
3669
3669
|
var patchAuthMeRequest = import_zod11.z.object({ display_name: import_zod11.z.string().min(1).max(USER_DISPLAY_NAME_MAX).nullable() }).strict();
|
|
3670
3670
|
|
|
3671
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3671
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/app-users.js
|
|
3672
3672
|
var APP_USER_DISPLAY_NAME_MAX = 120;
|
|
3673
3673
|
var providerSlug = import_zod12.z.string().max(40).regex(/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/, "a provider slug is lowercase and hyphen-separated, starting with a letter");
|
|
3674
3674
|
var appUserStatus = import_zod12.z.enum(["pending_verification", "active", "blocked"]);
|
|
@@ -3919,7 +3919,7 @@ var mailOutcome = import_zod12.z.object({
|
|
|
3919
3919
|
})
|
|
3920
3920
|
});
|
|
3921
3921
|
|
|
3922
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3922
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/client-auth.js
|
|
3923
3923
|
var clientLoginRequest = import_zod13.z.object({
|
|
3924
3924
|
app_identifier: appIdentifier.meta({
|
|
3925
3925
|
description: "The app being logged in to, as its globally unique identifier \u2014 the lowercase, underscore-separated string the developer chose when the app was created. There is no organisation context at login, so this is what decides which app the credentials are checked for."
|
|
@@ -4108,7 +4108,7 @@ var clientIdentity = import_zod13.z.object({
|
|
|
4108
4108
|
})
|
|
4109
4109
|
});
|
|
4110
4110
|
|
|
4111
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
4111
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/realtime.js
|
|
4112
4112
|
var clientAuth = import_zod14.z.object({
|
|
4113
4113
|
type: import_zod14.z.literal("auth"),
|
|
4114
4114
|
token: import_zod14.z.string().min(1)
|
|
@@ -4386,7 +4386,7 @@ var orgEventDropped = import_zod14.z.object({
|
|
|
4386
4386
|
dropped: import_zod14.z.number().int().positive()
|
|
4387
4387
|
}).strict();
|
|
4388
4388
|
|
|
4389
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
4389
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/audit.js
|
|
4390
4390
|
var import_zod15 = require("zod");
|
|
4391
4391
|
var auditActor = import_zod15.z.object({
|
|
4392
4392
|
kind: import_zod15.z.enum(["developer", "end_user", "app_user", "server_key", "bridge"]),
|
|
@@ -4517,7 +4517,7 @@ var auditListResponse = import_zod15.z.object({
|
|
|
4517
4517
|
next_cursor: import_zod15.z.number().int().positive().nullable()
|
|
4518
4518
|
});
|
|
4519
4519
|
|
|
4520
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
4520
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/errors.js
|
|
4521
4521
|
var import_zod16 = require("zod");
|
|
4522
4522
|
var apiError = import_zod16.z.object({
|
|
4523
4523
|
code: import_zod16.z.string().min(1),
|
|
@@ -4534,7 +4534,7 @@ var parameterInvalidDetails = import_zod16.z.object({
|
|
|
4534
4534
|
violations: import_zod16.z.array(parameterViolation).min(1)
|
|
4535
4535
|
});
|
|
4536
4536
|
|
|
4537
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
4537
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/oauth.js
|
|
4538
4538
|
var import_zod17 = require("zod");
|
|
4539
4539
|
var oauthErrorCode = import_zod17.z.enum([
|
|
4540
4540
|
"invalid_request",
|
|
@@ -4755,7 +4755,7 @@ var oauthAuthorizeQuery = import_zod17.z.object({
|
|
|
4755
4755
|
description: "The authorization request an MCP client sends, per RFC 6749 \xA74.1.1 with mandatory PKCE. The handler reads it parameter by parameter rather than through one parse, because the answers differ: `client_id` and `redirect_uri` are refused flat, with no redirect, since until both are confirmed there is no trusted target to bounce a browser to, and everything after them is reported to the client's own callback as query parameters."
|
|
4756
4756
|
});
|
|
4757
4757
|
|
|
4758
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
4758
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/routes.js
|
|
4759
4759
|
var MCP_APP = MCP_APP_PATHS(":appIdentifier");
|
|
4760
4760
|
var APP_IDENTIFIER = {
|
|
4761
4761
|
name: "appIdentifier",
|
|
@@ -6355,7 +6355,7 @@ var ROUTES = [
|
|
|
6355
6355
|
response: null,
|
|
6356
6356
|
errors: ["not_found", "unauthorized", "forbidden"],
|
|
6357
6357
|
transport: "http",
|
|
6358
|
-
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 \u2014 the argument is in `MCP_PROTOCOL_VERSION`'s own note, and a per-process session map is what breaks at the second cloud instance \u2014 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 two differ: 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* \u2014
|
|
6358
|
+
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 \u2014 the argument is in `MCP_PROTOCOL_VERSION`'s own note, and a per-process session map is what breaks at the second cloud instance \u2014 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 two differ: 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* \u2014 so an unregistered verb and an unknown app would answer identically. 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* \u2014 the app, its switch, then the bearer, in the order `POST` describes \u2014 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."
|
|
6359
6359
|
},
|
|
6360
6360
|
{
|
|
6361
6361
|
method: "DELETE",
|
|
@@ -7320,7 +7320,7 @@ var ROUTES = [
|
|
|
7320
7320
|
response: jobRunListResponse,
|
|
7321
7321
|
errors: [...CLIENT_GUARD, "invalid_uuid", "not_found", "capability_required", "validation_error"],
|
|
7322
7322
|
transport: "http",
|
|
7323
|
-
notes: "Needs the `action_history` capability, and **this route is what makes that switch mean something** \u2014 it was unkeepable while nothing durable recorded what had run. Two residuals worth stating rather than implying away. `history` is a syntactically valid slug and
|
|
7323
|
+
notes: "Needs the `action_history` capability, and **this route is what makes that switch mean something** \u2014 it was unkeepable while nothing durable recorded what had run. Two residuals worth stating rather than implying away. `history` is a syntactically valid slug, and a static segment matches before a parameter, so a robot with a service literally slugged `history` can no longer be **read** through `GET /api/robots/:id/jobs/:slug`; invoking, cancelling and the listing are unaffected. And a run row names its actor by email address, so an end user holding this capability learns which other people have been driving the machine. `robot_id` in the query is shared with the org-wide read; a *different* one here is refused rather than quietly answered about the robot in the path."
|
|
7324
7324
|
},
|
|
7325
7325
|
{
|
|
7326
7326
|
method: "POST",
|
|
@@ -8339,7 +8339,7 @@ function createRealtimeCommandTransport(channel) {
|
|
|
8339
8339
|
return {
|
|
8340
8340
|
// Declared `async` deliberately, unlike `cancel`/`publish` below: it is
|
|
8341
8341
|
// the only one of the three that can refuse *before* sending anything
|
|
8342
|
-
// (resolveLocalWaitMs's `invalid_option
|
|
8342
|
+
// (`resolveLocalWaitMs`'s `invalid_option`), and every caller of
|
|
8343
8343
|
// this interface — starting with this file's own `sendCommand` callers
|
|
8344
8344
|
// — is entitled to assume `CommandTransport.invoke` always returns a
|
|
8345
8345
|
// promise rather than throwing synchronously. Without `async` here, a
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Jobs: one running unit of work on a robot — an action
|
|
@@ -234,7 +233,6 @@ declare const historyBucketsResponse: z.ZodObject<{
|
|
|
234
233
|
}, z.core.$strip>;
|
|
235
234
|
type HistoryBucketsResponse = z.infer<typeof historyBucketsResponse>;
|
|
236
235
|
|
|
237
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
238
236
|
|
|
239
237
|
/**
|
|
240
238
|
* One datapoint sample pushed to a subscriber. The current value arrives
|
|
@@ -250,7 +248,6 @@ declare const datapointEvent: z.ZodObject<{
|
|
|
250
248
|
}, z.core.$strip>;
|
|
251
249
|
type DatapointEvent = z.infer<typeof datapointEvent>;
|
|
252
250
|
|
|
253
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
254
251
|
|
|
255
252
|
/**
|
|
256
253
|
* Access plus refresh. The access token is short-lived; the
|
|
@@ -267,7 +264,6 @@ declare const sessionTokens: z.ZodObject<{
|
|
|
267
264
|
}, z.core.$strip>;
|
|
268
265
|
type SessionTokens = z.infer<typeof sessionTokens>;
|
|
269
266
|
|
|
270
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
271
267
|
|
|
272
268
|
/**
|
|
273
269
|
* **Why a federated sign-in ended without a session, in a code the app can
|
|
@@ -416,7 +412,6 @@ declare const clientIdentity: z.ZodObject<{
|
|
|
416
412
|
}, z.core.$strip>;
|
|
417
413
|
type ClientIdentity = z.infer<typeof clientIdentity>;
|
|
418
414
|
|
|
419
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
420
415
|
|
|
421
416
|
declare const asset: z.ZodObject<{
|
|
422
417
|
id: z.ZodUUID;
|
|
@@ -551,7 +546,6 @@ declare const assetListResponse: z.ZodObject<{
|
|
|
551
546
|
}, z.core.$strip>;
|
|
552
547
|
type AssetListResponse = z.infer<typeof assetListResponse>;
|
|
553
548
|
|
|
554
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
555
549
|
|
|
556
550
|
/**
|
|
557
551
|
* One violated parameter rule. `details` on the envelope stays `unknown` — codes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Jobs: one running unit of work on a robot — an action
|
|
@@ -234,7 +233,6 @@ declare const historyBucketsResponse: z.ZodObject<{
|
|
|
234
233
|
}, z.core.$strip>;
|
|
235
234
|
type HistoryBucketsResponse = z.infer<typeof historyBucketsResponse>;
|
|
236
235
|
|
|
237
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
238
236
|
|
|
239
237
|
/**
|
|
240
238
|
* One datapoint sample pushed to a subscriber. The current value arrives
|
|
@@ -250,7 +248,6 @@ declare const datapointEvent: z.ZodObject<{
|
|
|
250
248
|
}, z.core.$strip>;
|
|
251
249
|
type DatapointEvent = z.infer<typeof datapointEvent>;
|
|
252
250
|
|
|
253
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
254
251
|
|
|
255
252
|
/**
|
|
256
253
|
* Access plus refresh. The access token is short-lived; the
|
|
@@ -267,7 +264,6 @@ declare const sessionTokens: z.ZodObject<{
|
|
|
267
264
|
}, z.core.$strip>;
|
|
268
265
|
type SessionTokens = z.infer<typeof sessionTokens>;
|
|
269
266
|
|
|
270
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
271
267
|
|
|
272
268
|
/**
|
|
273
269
|
* **Why a federated sign-in ended without a session, in a code the app can
|
|
@@ -416,7 +412,6 @@ declare const clientIdentity: z.ZodObject<{
|
|
|
416
412
|
}, z.core.$strip>;
|
|
417
413
|
type ClientIdentity = z.infer<typeof clientIdentity>;
|
|
418
414
|
|
|
419
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
420
415
|
|
|
421
416
|
declare const asset: z.ZodObject<{
|
|
422
417
|
id: z.ZodUUID;
|
|
@@ -551,7 +546,6 @@ declare const assetListResponse: z.ZodObject<{
|
|
|
551
546
|
}, z.core.$strip>;
|
|
552
547
|
type AssetListResponse = z.infer<typeof assetListResponse>;
|
|
553
548
|
|
|
554
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
555
549
|
|
|
556
550
|
/**
|
|
557
551
|
* One violated parameter rule. `details` on the envelope stays `unknown` — codes
|
package/dist/index.js
CHANGED
|
@@ -114,7 +114,7 @@ var HttpClient = class {
|
|
|
114
114
|
* RequestOptionsFor<P>` would type-check by construction — the cast
|
|
115
115
|
* bypasses the very check this exists to add, which is the identical
|
|
116
116
|
* "special case that quietly exempts calls from the general rule" shape
|
|
117
|
-
* this file has already
|
|
117
|
+
* this file has already made once. Every current call site to a route
|
|
118
118
|
* with no body already passes `{}` explicitly for exactly this reason.
|
|
119
119
|
*/
|
|
120
120
|
async request(path, options) {
|
|
@@ -411,7 +411,7 @@ function createAssetsApi(http) {
|
|
|
411
411
|
};
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
414
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/common.js
|
|
415
415
|
import { z } from "zod";
|
|
416
416
|
var SLUG_RULE = "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore \u2014 `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused.";
|
|
417
417
|
var slug = z.string().min(2).max(63).regex(/^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/, SLUG_RULE);
|
|
@@ -435,7 +435,7 @@ var applyError = z.object({
|
|
|
435
435
|
details: z.record(z.string(), z.unknown()).optional()
|
|
436
436
|
});
|
|
437
437
|
|
|
438
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
438
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/mcp.js
|
|
439
439
|
import { z as z2 } from "zod";
|
|
440
440
|
function mcpAppEndpointPath(appIdentifier2) {
|
|
441
441
|
return `/mcp/${appIdentifier2}`;
|
|
@@ -484,10 +484,10 @@ var mcpRolePreviewResponse = z2.object({
|
|
|
484
484
|
});
|
|
485
485
|
var MCP_ASSET_LINK_TTL_MS = 15 * 60 * 1e3;
|
|
486
486
|
|
|
487
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
487
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/protocol.js
|
|
488
488
|
import { z as z8 } from "zod";
|
|
489
489
|
|
|
490
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
490
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/assets.js
|
|
491
491
|
import { z as z3 } from "zod";
|
|
492
492
|
var assetKind = z3.enum(["urdf", "mesh", "texture", "other"]);
|
|
493
493
|
var asset = z3.object({
|
|
@@ -745,10 +745,10 @@ var assetSyncBusyDetails = z3.object({
|
|
|
745
745
|
started_at_ms: z3.number().int().nonnegative()
|
|
746
746
|
});
|
|
747
747
|
|
|
748
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
748
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/config.js
|
|
749
749
|
import { z as z5 } from "zod";
|
|
750
750
|
|
|
751
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
751
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/alerts.js
|
|
752
752
|
import { z as z4 } from "zod";
|
|
753
753
|
var alertRowCondition = z4.discriminatedUnion("kind", [
|
|
754
754
|
z4.strictObject({
|
|
@@ -818,7 +818,7 @@ var putDatapointDisplayRequest = z4.object({
|
|
|
818
818
|
y_max: z4.number().finite().nullable()
|
|
819
819
|
}).strict();
|
|
820
820
|
|
|
821
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
821
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/config.js
|
|
822
822
|
var RTSP_URL_RULE = "The URL has to begin with `rtsp://` or `rtsps://` \u2014 `rtsp://cam-1.plant.local/stream1`. No other scheme is accepted: the bridge opens this with a library that would equally honour `file:`.";
|
|
823
823
|
var MJPEG_URL_RULE = "The URL has to begin with `http://` or `https://` \u2014 `http://cam-1.plant.local/video.mjpg`. No other scheme is accepted: the bridge opens this with a library that would equally serve `file:`.";
|
|
824
824
|
var DEVICE_PATH_RULE = "A capture device is a path under `/dev/`, and the character straight after it is a letter or a digit \u2014 `/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.";
|
|
@@ -1865,7 +1865,7 @@ var configState = z5.object({
|
|
|
1865
1865
|
applied_errors: z5.array(applyError).nullable()
|
|
1866
1866
|
});
|
|
1867
1867
|
|
|
1868
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
1868
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/introspection.js
|
|
1869
1869
|
import { z as z6 } from "zod";
|
|
1870
1870
|
var rosGraphEntry = z6.object({
|
|
1871
1871
|
name: rosName,
|
|
@@ -1904,7 +1904,7 @@ var typeDefinition = z6.discriminatedUnion("kind", [
|
|
|
1904
1904
|
})
|
|
1905
1905
|
]);
|
|
1906
1906
|
|
|
1907
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
1907
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/jobs.js
|
|
1908
1908
|
import { z as z7 } from "zod";
|
|
1909
1909
|
var jobState = z7.enum(["running", "succeeded", "failed", "cancelled", "lost"]);
|
|
1910
1910
|
var job = z7.object({
|
|
@@ -2122,7 +2122,7 @@ var jobRunSummary = z7.object({
|
|
|
2122
2122
|
since_ms: z7.number().int().nonnegative()
|
|
2123
2123
|
});
|
|
2124
2124
|
|
|
2125
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
2125
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/protocol.js
|
|
2126
2126
|
var MAX_PATIENCE_MS = 12e4;
|
|
2127
2127
|
var MIN_PATIENCE_MS = 1e3;
|
|
2128
2128
|
var activeJob = z8.object({
|
|
@@ -2521,11 +2521,11 @@ var bridgeCameraState = z8.object({
|
|
|
2521
2521
|
request_id: z8.string().min(1).max(64).nullable()
|
|
2522
2522
|
});
|
|
2523
2523
|
|
|
2524
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
2524
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/config-issues.js
|
|
2525
2525
|
var EXPOSURE_SECTIONS = ["datapoints", "actions", "services", "publishers", "cameras"];
|
|
2526
2526
|
var EXPOSURE_SECTION_NAMES = new Set(EXPOSURE_SECTIONS);
|
|
2527
2527
|
|
|
2528
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
2528
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/rest.js
|
|
2529
2529
|
import { z as z9 } from "zod";
|
|
2530
2530
|
var robot = z9.object({
|
|
2531
2531
|
id: z9.uuid().meta({
|
|
@@ -3285,13 +3285,13 @@ var slugUsageResponse = z9.object({
|
|
|
3285
3285
|
alert_count: z9.number().int().nonnegative()
|
|
3286
3286
|
});
|
|
3287
3287
|
|
|
3288
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3288
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/realtime.js
|
|
3289
3289
|
import { z as z14 } from "zod";
|
|
3290
3290
|
|
|
3291
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3291
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/client-auth.js
|
|
3292
3292
|
import { z as z13 } from "zod";
|
|
3293
3293
|
|
|
3294
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3294
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/apps.js
|
|
3295
3295
|
import { z as z10 } from "zod";
|
|
3296
3296
|
var appIdentifier = slug;
|
|
3297
3297
|
var app = z10.object({
|
|
@@ -3473,10 +3473,10 @@ var rolePermissions = z10.object({
|
|
|
3473
3473
|
})
|
|
3474
3474
|
});
|
|
3475
3475
|
|
|
3476
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3476
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/app-users.js
|
|
3477
3477
|
import { z as z12 } from "zod";
|
|
3478
3478
|
|
|
3479
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3479
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/identity.js
|
|
3480
3480
|
import { z as z11 } from "zod";
|
|
3481
3481
|
var password = z11.string().min(12).max(256);
|
|
3482
3482
|
var USER_DISPLAY_NAME_MAX = 120;
|
|
@@ -3638,7 +3638,7 @@ var authMeResponse = z11.object({ org, user: fleetlessUser });
|
|
|
3638
3638
|
var patchOrgRequest = z11.object({ name: z11.string().min(1).max(120) }).strict();
|
|
3639
3639
|
var patchAuthMeRequest = z11.object({ display_name: z11.string().min(1).max(USER_DISPLAY_NAME_MAX).nullable() }).strict();
|
|
3640
3640
|
|
|
3641
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3641
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/app-users.js
|
|
3642
3642
|
var APP_USER_DISPLAY_NAME_MAX = 120;
|
|
3643
3643
|
var providerSlug = z12.string().max(40).regex(/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/, "a provider slug is lowercase and hyphen-separated, starting with a letter");
|
|
3644
3644
|
var appUserStatus = z12.enum(["pending_verification", "active", "blocked"]);
|
|
@@ -3889,7 +3889,7 @@ var mailOutcome = z12.object({
|
|
|
3889
3889
|
})
|
|
3890
3890
|
});
|
|
3891
3891
|
|
|
3892
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
3892
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/client-auth.js
|
|
3893
3893
|
var clientLoginRequest = z13.object({
|
|
3894
3894
|
app_identifier: appIdentifier.meta({
|
|
3895
3895
|
description: "The app being logged in to, as its globally unique identifier \u2014 the lowercase, underscore-separated string the developer chose when the app was created. There is no organisation context at login, so this is what decides which app the credentials are checked for."
|
|
@@ -4078,7 +4078,7 @@ var clientIdentity = z13.object({
|
|
|
4078
4078
|
})
|
|
4079
4079
|
});
|
|
4080
4080
|
|
|
4081
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
4081
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/realtime.js
|
|
4082
4082
|
var clientAuth = z14.object({
|
|
4083
4083
|
type: z14.literal("auth"),
|
|
4084
4084
|
token: z14.string().min(1)
|
|
@@ -4356,7 +4356,7 @@ var orgEventDropped = z14.object({
|
|
|
4356
4356
|
dropped: z14.number().int().positive()
|
|
4357
4357
|
}).strict();
|
|
4358
4358
|
|
|
4359
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
4359
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/audit.js
|
|
4360
4360
|
import { z as z15 } from "zod";
|
|
4361
4361
|
var auditActor = z15.object({
|
|
4362
4362
|
kind: z15.enum(["developer", "end_user", "app_user", "server_key", "bridge"]),
|
|
@@ -4487,7 +4487,7 @@ var auditListResponse = z15.object({
|
|
|
4487
4487
|
next_cursor: z15.number().int().positive().nullable()
|
|
4488
4488
|
});
|
|
4489
4489
|
|
|
4490
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
4490
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/errors.js
|
|
4491
4491
|
import { z as z16 } from "zod";
|
|
4492
4492
|
var apiError = z16.object({
|
|
4493
4493
|
code: z16.string().min(1),
|
|
@@ -4504,7 +4504,7 @@ var parameterInvalidDetails = z16.object({
|
|
|
4504
4504
|
violations: z16.array(parameterViolation).min(1)
|
|
4505
4505
|
});
|
|
4506
4506
|
|
|
4507
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
4507
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/oauth.js
|
|
4508
4508
|
import { z as z17 } from "zod";
|
|
4509
4509
|
var oauthErrorCode = z17.enum([
|
|
4510
4510
|
"invalid_request",
|
|
@@ -4725,7 +4725,7 @@ var oauthAuthorizeQuery = z17.object({
|
|
|
4725
4725
|
description: "The authorization request an MCP client sends, per RFC 6749 \xA74.1.1 with mandatory PKCE. The handler reads it parameter by parameter rather than through one parse, because the answers differ: `client_id` and `redirect_uri` are refused flat, with no redirect, since until both are confirmed there is no trusted target to bounce a browser to, and everything after them is reported to the client's own callback as query parameters."
|
|
4726
4726
|
});
|
|
4727
4727
|
|
|
4728
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0.
|
|
4728
|
+
// node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/routes.js
|
|
4729
4729
|
var MCP_APP = MCP_APP_PATHS(":appIdentifier");
|
|
4730
4730
|
var APP_IDENTIFIER = {
|
|
4731
4731
|
name: "appIdentifier",
|
|
@@ -6325,7 +6325,7 @@ var ROUTES = [
|
|
|
6325
6325
|
response: null,
|
|
6326
6326
|
errors: ["not_found", "unauthorized", "forbidden"],
|
|
6327
6327
|
transport: "http",
|
|
6328
|
-
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 \u2014 the argument is in `MCP_PROTOCOL_VERSION`'s own note, and a per-process session map is what breaks at the second cloud instance \u2014 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 two differ: 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* \u2014
|
|
6328
|
+
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 \u2014 the argument is in `MCP_PROTOCOL_VERSION`'s own note, and a per-process session map is what breaks at the second cloud instance \u2014 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 two differ: 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* \u2014 so an unregistered verb and an unknown app would answer identically. 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* \u2014 the app, its switch, then the bearer, in the order `POST` describes \u2014 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."
|
|
6329
6329
|
},
|
|
6330
6330
|
{
|
|
6331
6331
|
method: "DELETE",
|
|
@@ -7290,7 +7290,7 @@ var ROUTES = [
|
|
|
7290
7290
|
response: jobRunListResponse,
|
|
7291
7291
|
errors: [...CLIENT_GUARD, "invalid_uuid", "not_found", "capability_required", "validation_error"],
|
|
7292
7292
|
transport: "http",
|
|
7293
|
-
notes: "Needs the `action_history` capability, and **this route is what makes that switch mean something** \u2014 it was unkeepable while nothing durable recorded what had run. Two residuals worth stating rather than implying away. `history` is a syntactically valid slug and
|
|
7293
|
+
notes: "Needs the `action_history` capability, and **this route is what makes that switch mean something** \u2014 it was unkeepable while nothing durable recorded what had run. Two residuals worth stating rather than implying away. `history` is a syntactically valid slug, and a static segment matches before a parameter, so a robot with a service literally slugged `history` can no longer be **read** through `GET /api/robots/:id/jobs/:slug`; invoking, cancelling and the listing are unaffected. And a run row names its actor by email address, so an end user holding this capability learns which other people have been driving the machine. `robot_id` in the query is shared with the org-wide read; a *different* one here is refused rather than quietly answered about the robot in the path."
|
|
7294
7294
|
},
|
|
7295
7295
|
{
|
|
7296
7296
|
method: "POST",
|
|
@@ -8309,7 +8309,7 @@ function createRealtimeCommandTransport(channel) {
|
|
|
8309
8309
|
return {
|
|
8310
8310
|
// Declared `async` deliberately, unlike `cancel`/`publish` below: it is
|
|
8311
8311
|
// the only one of the three that can refuse *before* sending anything
|
|
8312
|
-
// (resolveLocalWaitMs's `invalid_option
|
|
8312
|
+
// (`resolveLocalWaitMs`'s `invalid_option`), and every caller of
|
|
8313
8313
|
// this interface — starting with this file's own `sendCommand` callers
|
|
8314
8314
|
// — is entitled to assume `CommandTransport.invoke` always returns a
|
|
8315
8315
|
// promise rather than throwing synchronously. Without `async` here, a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fleetless/sdk",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "The official TypeScript SDK for Fleetless client apps \u2014 a ROS 2 robot as a REST and realtime API, cameras, jobs, and the app's own user accounts, federated sign-in and MCP consent.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -8,13 +8,8 @@
|
|
|
8
8
|
"email": "hello@fleetless.dev",
|
|
9
9
|
"url": "https://dehne-robotik.de"
|
|
10
10
|
},
|
|
11
|
-
"repository": {
|
|
12
|
-
"type": "git",
|
|
13
|
-
"url": "https://github.com/fleetless/sdk"
|
|
14
|
-
},
|
|
15
11
|
"homepage": "https://docs.fleetless.dev/reference/sdk/",
|
|
16
12
|
"bugs": {
|
|
17
|
-
"url": "https://github.com/fleetless/sdk/issues",
|
|
18
13
|
"email": "hello@fleetless.dev"
|
|
19
14
|
},
|
|
20
15
|
"keywords": [
|
|
@@ -56,7 +51,8 @@
|
|
|
56
51
|
"CHANGELOG.md",
|
|
57
52
|
"SECURITY.md",
|
|
58
53
|
"CONTRIBUTING.md",
|
|
59
|
-
"CODE_OF_CONDUCT.md"
|
|
54
|
+
"CODE_OF_CONDUCT.md",
|
|
55
|
+
"RELEASING.md"
|
|
60
56
|
],
|
|
61
57
|
"engines": {
|
|
62
58
|
"node": ">=20"
|
|
@@ -69,13 +65,14 @@
|
|
|
69
65
|
"test": "vitest run",
|
|
70
66
|
"test:pack": "node scripts/verify-published-types.mjs",
|
|
71
67
|
"verify:live": "node scripts/verify-live.mjs",
|
|
72
|
-
"verify:history": "node scripts/verify-history.mjs"
|
|
68
|
+
"verify:history": "node scripts/verify-history.mjs",
|
|
69
|
+
"verify:commits": "node scripts/verify-commit-messages.mjs"
|
|
73
70
|
},
|
|
74
71
|
"dependencies": {
|
|
75
72
|
"zod": "^4.0.0"
|
|
76
73
|
},
|
|
77
74
|
"devDependencies": {
|
|
78
|
-
"@fleetless/contracts": "1.0.
|
|
75
|
+
"@fleetless/contracts": "1.0.5",
|
|
79
76
|
"@types/node": "^22.20.1",
|
|
80
77
|
"tsup": "^8.3.0",
|
|
81
78
|
"typedoc": "0.28.20",
|