@fleetless/contracts 1.0.0 → 1.0.3
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 +97 -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 +11 -11
- package/artifacts/routes.json +12 -12
- package/artifacts/schema/create-app-oidc-provider-request.schema.json +1 -1
- package/dist/alerts.d.ts +23 -28
- package/dist/alerts.js +23 -29
- package/dist/app-users.d.ts +18 -19
- package/dist/app-users.js +18 -20
- package/dist/apps.d.ts +21 -25
- package/dist/apps.js +42 -52
- package/dist/assets.d.ts +70 -132
- package/dist/assets.js +130 -223
- package/dist/audit.d.ts +14 -15
- package/dist/audit.js +28 -55
- package/dist/client-auth.d.ts +9 -9
- package/dist/client-auth.js +8 -9
- package/dist/common.d.ts +29 -37
- package/dist/common.js +28 -37
- package/dist/config-issues.d.ts +23 -25
- package/dist/config-issues.js +17 -17
- package/dist/config.d.ts +37 -44
- package/dist/config.js +145 -187
- package/dist/errors.d.ts +4 -3
- package/dist/errors.js +83 -116
- package/dist/identity.d.ts +24 -27
- package/dist/identity.js +23 -27
- package/dist/index.d.ts +4 -4
- package/dist/index.js +14 -15
- package/dist/introspection.d.ts +7 -6
- package/dist/introspection.js +6 -6
- package/dist/jobs.d.ts +16 -16
- package/dist/jobs.js +24 -29
- package/dist/mcp.d.ts +14 -15
- package/dist/mcp.js +12 -14
- package/dist/oauth.d.ts +21 -27
- package/dist/oauth.js +33 -43
- package/dist/protocol.d.ts +51 -62
- package/dist/protocol.js +107 -139
- package/dist/realtime.d.ts +53 -68
- package/dist/realtime.js +78 -104
- package/dist/rest.d.ts +183 -244
- package/dist/rest.js +305 -399
- package/dist/routes.d.ts +4 -3
- package/dist/routes.js +33 -32
- package/package.json +12 -7
package/dist/routes.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
1
2
|
/**
|
|
2
3
|
* **The route manifest: every HTTP route the cloud registers, declared once.**
|
|
3
4
|
*
|
|
@@ -62,9 +63,9 @@ export interface RouteEntry {
|
|
|
62
63
|
* It exists because the OpenAPI render marked every request body `required`,
|
|
63
64
|
* which documented a refusal `POST /api/robots/:id/jobs/:slug/cancel` does not
|
|
64
65
|
* make: it parses `request.body ?? {}`, and a bodyless `POST` was every
|
|
65
|
-
* caller's shape before `job_id` existed. Getting
|
|
66
|
-
* direction
|
|
67
|
-
*
|
|
66
|
+
* caller's shape before `job_id` existed. Getting it wrong in the other
|
|
67
|
+
* direction — a bodyless `POST` with a JSON content type rejected outright —
|
|
68
|
+
* makes whole routes unreachable, so this is a fact worth carrying rather
|
|
68
69
|
* than a default worth assuming.
|
|
69
70
|
*/
|
|
70
71
|
readonly requestOptional?: true;
|
package/dist/routes.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
1
2
|
import { appListResponse, createAppRequest, createServerKeyResponse, app as appSchema, role, roleListResponse, rolePermissions, serverKeyListResponse, updateAppRequest, } from './apps.js';
|
|
2
3
|
import { alertListResponse, orgAlertsQuery, orgFiringAlertsResponse } from './alerts.js';
|
|
3
4
|
import { asset, assetListResponse, assetSyncRequest, assetSyncResponse, assetSyncStatus, missingAssetQuery } from './assets.js';
|
|
@@ -62,16 +63,16 @@ export const IN_HANDLER_ROUTES = [
|
|
|
62
63
|
* **The three refusals every `auth: 'developer'` route inherits from its guard**,
|
|
63
64
|
* spelled once rather than retyped eighty times.
|
|
64
65
|
*
|
|
65
|
-
* They are the arms of
|
|
66
|
-
*
|
|
66
|
+
* They are the arms of the developer guard: no bearer or an unverifiable one is
|
|
67
|
+
* `401 unauthorized`, an expired one `401 token_expired`, a
|
|
67
68
|
* vanished account or a bumped `token_version` `401 token_revoked`.
|
|
68
69
|
*
|
|
69
70
|
* **Three, not four: `403 forbidden` went with the Org Admins group.** It stood
|
|
70
71
|
* for "an account that is no longer in the org's Org Admins group", and the
|
|
71
|
-
* two
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
72
|
+
* two identity spaces leave a Fleetless user who IS the team, and the guard's
|
|
73
|
+
* refusal reasons are `'expired' | 'revoked' | 'invalid'` — 401 codes only. A
|
|
74
|
+
* removed team member gets `401 token_revoked`; a reader of the API reference
|
|
75
|
+
* who branched on
|
|
75
76
|
* `forbidden` to render "you lost console access" was branching on an answer no
|
|
76
77
|
* developer-guarded route can send. The `forbidden` producers that remain
|
|
77
78
|
* (`history.ts`, `commands.ts`, `cameras.ts`, `robots.ts`, `mcp.ts`) all sit on
|
|
@@ -92,15 +93,15 @@ const DEVELOPER_GUARD = ['unauthorized', 'token_expired', 'token_revoked'];
|
|
|
92
93
|
* **The same four codes, not five.** `sendTokenRefusal` has a fifth arm,
|
|
93
94
|
* `account_blocked`, and this list carried it for exactly one commit. Nothing
|
|
94
95
|
* reaches it: `TokenRefusalReason` admits `'blocked'`, but no site in
|
|
95
|
-
*
|
|
96
|
-
* `'revoked'` and `'forbidden'`.
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
96
|
+
* the cloud constructs one — the only reasons ever returned are `'invalid'`,
|
|
97
|
+
* `'revoked'` and `'forbidden'`. Access is withdrawn by removing an assignment
|
|
98
|
+
* rather than by blocking an account, so an app user who loses access loses it
|
|
99
|
+
* because no assignment resolves, and there is no blocked state left to
|
|
100
|
+
* re-check.
|
|
100
101
|
*
|
|
101
102
|
* Listing it would have documented a refusal no caller can receive — the same
|
|
102
|
-
* mistake as the `invalid_token` above
|
|
103
|
-
*
|
|
103
|
+
* mistake as the `invalid_token` above. No check here catches it, because a
|
|
104
|
+
* code in `ERROR_CODES` satisfies every one of them.
|
|
104
105
|
*
|
|
105
106
|
* It keeps `forbidden`, which `DEVELOPER_GUARD` no longer carries: this guard's
|
|
106
107
|
* routes have live 403 producers — a slug the role does not grant
|
|
@@ -419,7 +420,7 @@ export const ROUTES = [
|
|
|
419
420
|
'listing beside it. `POST /api/client/register` answers `202` to the same fact, because there the caller is a stranger. `404 not_found` ' +
|
|
420
421
|
'is the app, or a `role_id` that is not a role of it — a role of another app is refused rather than stored, since a user holding one ' +
|
|
421
422
|
'would carry rights nothing in this app can resolve. **The password policy answers `400 validation_error`**, not a code of its own: the ' +
|
|
422
|
-
'twelve-character minimum is the `password` field\'s schema rule, and every route
|
|
423
|
+
'twelve-character minimum is the `password` field\'s schema rule, and every route that takes a password refuses a ' +
|
|
423
424
|
'short one exactly the way it refuses any other malformed field. An account created here is `active` immediately: a developer entering ' +
|
|
424
425
|
'somebody by hand has made the decision the verification mail automates, and its address counts as proven. `409 target_state_conflict` ' +
|
|
425
426
|
'names `default_role_id` when `role_id` is absent and the app has no default role, or its default names a role that no longer resolves ' +
|
|
@@ -589,7 +590,7 @@ export const ROUTES = [
|
|
|
589
590
|
'`POST /api/client/invitations/accept`, the same answer one that expired or never existed gets — the developer withdrew it deliberately, ' +
|
|
590
591
|
'and an answer saying so would tell whoever still holds the link that it was once real.',
|
|
591
592
|
},
|
|
592
|
-
/*
|
|
593
|
+
/* ------------------------------------------- the app's OIDC providers */
|
|
593
594
|
{
|
|
594
595
|
method: 'GET', path: '/api/apps/:id/oidc-providers', section: 'apps',
|
|
595
596
|
summary: "Lists every OIDC provider configured on the app, enabled or not.",
|
|
@@ -974,7 +975,7 @@ export const ROUTES = [
|
|
|
974
975
|
audience: 'internal', auth: 'none', rateLimited: true, ownerTier: false, status: 200,
|
|
975
976
|
params: [], query: null, request: null, response: null,
|
|
976
977
|
errors: ['rate_limited', 'validation_error', 'token_spent'], transport: 'http',
|
|
977
|
-
notes: 'The identifier-first step, with nothing left to identify: Fleetless users are password-only
|
|
978
|
+
notes: 'The identifier-first step, with nothing left to identify: Fleetless users are password-only, so **this step does not ' +
|
|
978
979
|
'read the address at all** — it renders the password card for a known address, an unknown one and an empty one alike, and the login ' +
|
|
979
980
|
'step below answers the same `401` for all three. That is a property of the shape rather than of two branches agreeing: there is no ' +
|
|
980
981
|
'lookup here whose result could differ. A browser form post gets the password card; a JSON caller gets `{ "next" }`, which has no ' +
|
|
@@ -1132,10 +1133,10 @@ export const ROUTES = [
|
|
|
1132
1133
|
'a token belongs to its own app\'s endpoint. The code is `forbidden` rather than `mcp_disabled` because nothing is switched off — the ' +
|
|
1133
1134
|
'caller is at the wrong server — and per-app sign-in mints exactly such tokens, so the two states must not share a word. ' +
|
|
1134
1135
|
'`mcp_access_denied` is gone with the per-user override and the ' +
|
|
1135
|
-
'group flag it read: every Fleetless user has MCP access here
|
|
1136
|
+
'group flag it read: every Fleetless user has MCP access here. Stateless: a fresh transport per request, no session id, nothing ' +
|
|
1136
1137
|
'survives the call.',
|
|
1137
1138
|
},
|
|
1138
|
-
/*
|
|
1139
|
+
/* --------------------------------------------- mcp (one app's own server) */
|
|
1139
1140
|
{
|
|
1140
1141
|
method: 'POST', path: MCP_APP.endpoint, section: 'mcp',
|
|
1141
1142
|
summary: "One app's MCP endpoint: the same stateless Streamable HTTP transport, carrying that app's robots.",
|
|
@@ -1171,13 +1172,13 @@ export const ROUTES = [
|
|
|
1171
1172
|
params: [APP_IDENTIFIER], query: null, request: null, response: null,
|
|
1172
1173
|
errors: ['not_found', 'unauthorized', 'forbidden'], transport: 'http',
|
|
1173
1174
|
notes: 'MCP\'s Streamable HTTP gives this path three verbs: `POST` carries JSON-RPC, `GET` opens the server-initiated SSE stream, and `DELETE` ' +
|
|
1174
|
-
'ends a session. This server has no sessions — the argument is in `MCP_PROTOCOL_VERSION`\'s own note, and
|
|
1175
|
-
'
|
|
1176
|
-
'\n\n**The `405` is this cloud\'s own answer, not the SDK\'s**, and the
|
|
1175
|
+
'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 ' +
|
|
1176
|
+
'what breaks at the second cloud instance — so `GET` and `DELETE` answer `405`, which is what a client is built to fall back from. ' +
|
|
1177
|
+
'\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 ' +
|
|
1177
1178
|
'`GET` (`handleGetRequest`) and answers `200` on `DELETE` (`handleDeleteRequest`), neither of which a stateless server has any ' +
|
|
1178
1179
|
'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 ' +
|
|
1179
1180
|
'`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 ' +
|
|
1180
|
-
'
|
|
1181
|
+
'one answer for two states. Registering the verb lets the endpoint say "this app\'s server is here; this verb is not ' +
|
|
1181
1182
|
'part of it". The central `/mcp` registers neither verb and does not need to: its path takes no parameter, so nothing can misread its ' +
|
|
1182
1183
|
'`404`. \n\n**The `405` body is the transport\'s JSON-RPC error object, not the `apiError` envelope.** The three codes above are the ' +
|
|
1183
1184
|
'refusals that come *first* — the app, its switch, then the bearer, in the order `POST` describes — and they are `apiError` because ' +
|
|
@@ -1227,7 +1228,7 @@ export const ROUTES = [
|
|
|
1227
1228
|
'canonical public base, never from the friendly `mcp.fleetless.dev` alias or the request\'s `Host`**, because a client checks a minted ' +
|
|
1228
1229
|
'token\'s `iss` and `aud` against these exact strings. \n\n**Unlike the central document, `authorization_endpoint` does not move to an ' +
|
|
1229
1230
|
'auth-portal origin**, and there is nothing here for one to serve: this authorization step renders no Fleetless page at all. It ' +
|
|
1230
|
-
'redirects to the app\'s own `mcp_login_url
|
|
1231
|
+
'redirects to the app\'s own `mcp_login_url`, which is on the developer\'s origin already.',
|
|
1231
1232
|
},
|
|
1232
1233
|
{
|
|
1233
1234
|
method: 'POST', path: MCP_APP.register, section: 'mcp',
|
|
@@ -1235,7 +1236,7 @@ export const ROUTES = [
|
|
|
1235
1236
|
audience: 'client', auth: 'none', rateLimited: true, ownerTier: false, status: 201,
|
|
1236
1237
|
params: [APP_IDENTIFIER], query: null, request: dynamicClientRegistrationRequest, response: dynamicClientRegistrationResponse,
|
|
1237
1238
|
errors: ['rate_limited', 'not_found'], transport: 'http',
|
|
1238
|
-
notes: 'RFC 7591, the same wire and the same handler as `POST /mcp/oauth/register` —
|
|
1239
|
+
notes: 'RFC 7591, the same wire and the same handler as `POST /mcp/oauth/register` — one implementation, because a ' +
|
|
1239
1240
|
'second answer to "is this redirect URI acceptable" would agree with the first only by luck. The request schema is what the endpoint ' +
|
|
1240
1241
|
'accepts rather than what it parses, for the reason that row gives: §3.2.2 needs two distinguishable refusals and one `safeParse` ' +
|
|
1241
1242
|
'failure offers one. `client_name` and `redirect_uris` are read; `grant_types`, `response_types` and `scope` are accepted and ignored, ' +
|
|
@@ -1255,7 +1256,7 @@ export const ROUTES = [
|
|
|
1255
1256
|
params: [APP_IDENTIFIER], query: oauthAuthorizeQuery, request: null, response: null,
|
|
1256
1257
|
errors: ['not_found', 'target_state_conflict'], transport: 'http',
|
|
1257
1258
|
notes: 'The same query as `GET /mcp/oauth/authorize`, read the same way — parameter by parameter, because the answers differ and one parse ' +
|
|
1258
|
-
'would collapse them. \n\n**Fleetless renders no page here
|
|
1259
|
+
'would collapse them. \n\n**Fleetless renders no page here**, and that is the whole of it. The route writes an interaction — ten minutes, as the OIDC ones live ' +
|
|
1259
1260
|
'— and redirects to `appAuthConfig.mcp_login_url` with `{interaction}` filled in. The app then authenticates the person with its own ' +
|
|
1260
1261
|
'UI, reads `GET /api/client/mcp/interactions/:id` to show the client\'s claimed name and the scopes it asked for, and calls approve or ' +
|
|
1261
1262
|
'deny. \n\nClient and `redirect_uri` are validated first and a failure there never redirects — the open-redirect discipline `GET ' +
|
|
@@ -1272,7 +1273,7 @@ export const ROUTES = [
|
|
|
1272
1273
|
'under `/api/client/mcp/interactions/:id`. `409 ' +
|
|
1273
1274
|
'target_state_conflict` names `mcp_login_url` with rule `not_set`: MCP is enabled and no page is configured to send the person to. It ' +
|
|
1274
1275
|
'is the same code and the same shape `send_mail` answers for an unconfigured `invite_url`, and the refusal is the honest one — ' +
|
|
1275
|
-
'Fleetless has nowhere to redirect, and rendering a page of its own
|
|
1276
|
+
'Fleetless has nowhere to redirect, and rendering a page of its own would contradict the rule that Fleetless shows an app user no page.',
|
|
1276
1277
|
},
|
|
1277
1278
|
{
|
|
1278
1279
|
method: 'POST', path: MCP_APP.token, section: 'mcp',
|
|
@@ -1515,7 +1516,7 @@ export const ROUTES = [
|
|
|
1515
1516
|
'\n\n**It lists `rate_limited` and no other code, because every sign-in outcome it has is a redirect.** Success and failure alike are ' +
|
|
1516
1517
|
'a `302` to the app\'s own ' +
|
|
1517
1518
|
'`redirect_uri`: `?code=…&state=…` when a session was resolved, `?error=<clientOidcErrorCode>&state=…` when it was not, so the app ' +
|
|
1518
|
-
'renders its own message and can bind either answer to the request it started. Fleetless shows an app user no page
|
|
1519
|
+
'renders its own message and can bind either answer to the request it started. Fleetless shows an app user no page. \n\n**The one ' +
|
|
1519
1520
|
'exception is a `state` that resolves to no interaction** — unknown, hand-edited, or past its ten minutes. Then there is no confirmed ' +
|
|
1520
1521
|
'redirect target to carry the answer to, and bouncing a browser to an unvalidated one is the hole the whole flow is arranged to avoid, ' +
|
|
1521
1522
|
'so the cloud renders an HTML problem page at `400`. That is the only Fleetless-rendered surface an app user can reach. It is HTML ' +
|
|
@@ -1538,7 +1539,7 @@ export const ROUTES = [
|
|
|
1538
1539
|
'is, not how vague the answer is: a mailed one-time code is a credential, an interaction id names a pending request, and the two ' +
|
|
1539
1540
|
'deserve different advice on the app\'s own page.',
|
|
1540
1541
|
},
|
|
1541
|
-
/*
|
|
1542
|
+
/* ------------------------------- the app's own MCP consent screen */
|
|
1542
1543
|
{
|
|
1543
1544
|
method: 'GET', path: '/api/client/mcp/interactions/:id', section: 'client-auth',
|
|
1544
1545
|
summary: 'Reads a pending MCP authorization so the app can draw its own consent screen.',
|
|
@@ -1573,7 +1574,7 @@ export const ROUTES = [
|
|
|
1573
1574
|
query: null, request: null, response: clientMcpInteractionDecisionResponse,
|
|
1574
1575
|
errors: [...CLIENT_GUARD, 'rate_limited', 'interaction_expired', 'mcp_disabled'], transport: 'http',
|
|
1575
1576
|
notes: 'The person is already signed in **at the app**, by whatever means that app uses, and this is the app telling Fleetless what they ' +
|
|
1576
|
-
'decided. Fleetless never sees that sign-in
|
|
1577
|
+
'decided. Fleetless never sees that sign-in. \n\nThe guard admits all three caller kinds and the handler ' +
|
|
1577
1578
|
'takes one: a developer bearer or a server key reaching this is `401 unauthorized`, because a consent is a person\'s and a server key ' +
|
|
1578
1579
|
'is not a person — the same shape `POST /api/client/password/change` has. `403 mcp_disabled` is the app\'s switch, re-read here as it is ' +
|
|
1579
1580
|
'on every request — and it is the one refusal on this surface that names the switch, because reaching it needs an app-user session ' +
|
|
@@ -1614,7 +1615,7 @@ export const ROUTES = [
|
|
|
1614
1615
|
params: [], query: null, request: null, response: mcpConsentGrantListResponse,
|
|
1615
1616
|
errors: [...CLIENT_GUARD], transport: 'http',
|
|
1616
1617
|
notes: '**So the developer\'s app can offer a "connected apps" screen of its own**, which is the only place an end user could ever be shown ' +
|
|
1617
|
-
'this: Fleetless renders no page for an app\'s users
|
|
1618
|
+
'this: Fleetless renders no page for an app\'s users, and the console is the developer\'s tool rather than their customers\'. ' +
|
|
1618
1619
|
'\n\nThe answer is about the bearer\'s own account and takes no user id — there is no id to pass and therefore nothing to pass the ' +
|
|
1619
1620
|
'wrong one. The guard admits all three caller kinds because it is shared, and the handler takes one: a developer bearer or a server ' +
|
|
1620
1621
|
'key is `401 unauthorized`, the shape `POST /api/client/password/change` has, because a consent is a person\'s and a server key is not ' +
|
|
@@ -1936,7 +1937,7 @@ export const ROUTES = [
|
|
|
1936
1937
|
errors: [...CLIENT_GUARD, 'invalid_uuid', 'not_found', 'capability_required', 'validation_error'], transport: 'http',
|
|
1937
1938
|
notes: 'Needs the `action_history` capability, and **this route is what makes that switch mean something** — it was unkeepable while nothing ' +
|
|
1938
1939
|
'durable recorded what had run. Two residuals worth stating rather than implying away. `history` is a syntactically valid slug and ' +
|
|
1939
|
-
'
|
|
1940
|
+
'The router matches a static segment first, so a robot with a service literally slugged `history` can no longer be **read** through ' +
|
|
1940
1941
|
'`GET /api/robots/:id/jobs/:slug`; invoking, cancelling and the listing are unaffected. And a run row names its actor by email address, ' +
|
|
1941
1942
|
'so an end user holding this capability learns which other people have been driving the machine. `robot_id` in the query is shared with ' +
|
|
1942
1943
|
'the org-wide read; a *different* one here is refused rather than quietly answered about the robot in the path.',
|
|
@@ -2277,7 +2278,7 @@ export const ROUTES = [
|
|
|
2277
2278
|
'upload token minted by `POST /api/robots/:id/assets/sync`, verified in a `preParsing` hook so a refusal precedes the work rather than ' +
|
|
2278
2279
|
'following it: a `preHandler` would already have buffered the whole file. The announced size is refused there too, before a single byte ' +
|
|
2279
2280
|
'is read — it is an announcement and not a proof, so it only ever rejects early and never accepts early, and a body that lies small is ' +
|
|
2280
|
-
'still caught by the real length check. Past both,
|
|
2281
|
+
'still caught by the real length check. Past both, the server\'s own body limit answers a bare `413 bad_request` with neither ceiling nor ' +
|
|
2281
2282
|
'size in it. Rate limited per robot inside that same hook, which is why `rateLimited` is `false`: there is no rate-limiting preHandler ' +
|
|
2282
2283
|
'registered on this route.',
|
|
2283
2284
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fleetless/contracts",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Fleetless wire contracts: the bridge-cloud protocol, the REST API schemas and the error codes, as zod schemas with generated JSON Schema and OpenAPI artifacts.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Dehne Robotik GmbH",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"url": "https://github.com/fleetless/contracts"
|
|
11
11
|
},
|
|
12
12
|
"bugs": {
|
|
13
|
-
"url": "https://github.com/fleetless/contracts/issues"
|
|
13
|
+
"url": "https://github.com/fleetless/contracts/issues",
|
|
14
|
+
"email": "security@fleetless.dev"
|
|
14
15
|
},
|
|
15
16
|
"keywords": [
|
|
16
17
|
"fleetless",
|
|
@@ -36,21 +37,25 @@
|
|
|
36
37
|
"LICENSE",
|
|
37
38
|
"NOTICE",
|
|
38
39
|
"README.md",
|
|
39
|
-
"CHANGELOG.md"
|
|
40
|
+
"CHANGELOG.md",
|
|
41
|
+
"SECURITY.md",
|
|
42
|
+
"CONTRIBUTING.md",
|
|
43
|
+
"CODE_OF_CONDUCT.md"
|
|
40
44
|
],
|
|
41
45
|
"engines": {
|
|
42
46
|
"node": ">=20"
|
|
43
47
|
},
|
|
44
48
|
"scripts": {
|
|
45
49
|
"typecheck": "tsc --noEmit && tsc --noEmit -p test/tsconfig.json",
|
|
46
|
-
"build": "tsc",
|
|
47
|
-
"prepare": "tsc",
|
|
50
|
+
"build": "tsc && node scripts/stamp-dist.mjs",
|
|
51
|
+
"prepare": "tsc && node scripts/stamp-dist.mjs",
|
|
48
52
|
"test": "vitest run",
|
|
49
53
|
"artifacts": "tsx scripts/export-schemas.ts",
|
|
50
|
-
"test:pack": "node scripts/verify-pack.mjs"
|
|
54
|
+
"test:pack": "node scripts/verify-pack.mjs",
|
|
55
|
+
"prepublishOnly": "node scripts/refuse-manual-publish.mjs"
|
|
51
56
|
},
|
|
52
57
|
"dependencies": {
|
|
53
|
-
"zod": "^4.
|
|
58
|
+
"zod": "^4.4.3"
|
|
54
59
|
},
|
|
55
60
|
"devDependencies": {
|
|
56
61
|
"@types/node": "^26.2.0",
|