@fleetless/sdk 3.0.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/CONTRIBUTING.md +76 -71
- package/README.md +66 -344
- package/SECURITY.md +11 -11
- package/dist/index.cjs +311 -209
- package/dist/index.d.cts +331 -94
- package/dist/index.d.ts +331 -94
- package/dist/index.js +311 -209
- package/package.json +9 -5
- package/RELEASING.md +0 -195
package/dist/index.cjs
CHANGED
|
@@ -334,7 +334,7 @@ function createAssetsApi(http) {
|
|
|
334
334
|
finish(
|
|
335
335
|
null,
|
|
336
336
|
new Error(
|
|
337
|
-
`createMeshLoader and prepareUrdfScene were both installed on the same LoadingManager for robot ${robotId}. Use one or the other on a given manager, not both \u2014 see the
|
|
337
|
+
`createMeshLoader and prepareUrdfScene were both installed on the same LoadingManager for robot ${robotId}. Use one or the other on a given manager, not both \u2014 see the SDK reference.`
|
|
338
338
|
)
|
|
339
339
|
);
|
|
340
340
|
return;
|
|
@@ -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.1.0/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.1.0/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,17 +514,17 @@ 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.1.0/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.1.0/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({
|
|
524
524
|
id: import_zod3.z.uuid().meta({ description: "The asset's id in the store." }),
|
|
525
525
|
robot_id: import_zod3.z.uuid().meta({ description: "The robot this asset belongs to." }),
|
|
526
526
|
kind: assetKind.meta({
|
|
527
|
-
description: "What the file is: the `urdf` itself, a `mesh` it references, a `texture` a mesh or the URDF paints with, or `other`. A renderer decides from this alone, before fetching anything, what
|
|
527
|
+
description: "What the file is: the `urdf` itself, a `mesh` it references, a `texture` a mesh or the URDF paints with, or `other`. A renderer decides from this alone, before fetching anything, what to pre-fetch."
|
|
528
528
|
}),
|
|
529
529
|
/**
|
|
530
530
|
* What the robot called it — for a mesh, the `package://` URI the URDF
|
|
@@ -559,7 +559,7 @@ var asset = import_zod3.z.object({
|
|
|
559
559
|
* rules, one enforced and one only documented, is how a traversal gets in.
|
|
560
560
|
*/
|
|
561
561
|
name: import_zod3.z.string().min(1).max(500).meta({
|
|
562
|
-
description: "What the robot called it \u2014 for a mesh, the `package://` URI the URDF references, verbatim,
|
|
562
|
+
description: "What the robot called it \u2014 for a mesh, the `package://` URI the URDF references, verbatim, the only string a developer can match against their own workspace. A file the URDF never names (an image a `.dae` loads for itself) is named by joining the mesh's own directory with that internal reference."
|
|
563
563
|
}),
|
|
564
564
|
media_type: import_zod3.z.string().min(1).max(120).meta({
|
|
565
565
|
description: "The media type of the stored bytes, as the producer reported it."
|
|
@@ -576,7 +576,7 @@ var asset = import_zod3.z.object({
|
|
|
576
576
|
* predictable failure of a store that hides it.
|
|
577
577
|
*/
|
|
578
578
|
sha256: import_zod3.z.string().regex(/^[a-f0-9]{64}$/).meta({
|
|
579
|
-
description: 'The content hash, lowercase hex
|
|
579
|
+
description: 'The content hash, lowercase hex. Exposed because it is the only way a client can tell "this is the same mesh I already have" across robots \u2014 the reason two robots sharing a mesh cost one copy.'
|
|
580
580
|
}),
|
|
581
581
|
created_at: import_zod3.z.iso.datetime().meta({
|
|
582
582
|
description: "When the asset was first stored, as an ISO 8601 timestamp."
|
|
@@ -604,18 +604,18 @@ var urdfCompleteness = import_zod3.z.object({
|
|
|
604
604
|
*/
|
|
605
605
|
missing: import_zod3.z.array(import_zod3.z.object({
|
|
606
606
|
uri: import_zod3.z.string().min(1).max(500).meta({
|
|
607
|
-
description: "The reference, verbatim, that no stored asset answers \u2014 a `package://` URI the workspace does not hold, or an absolute or bare relative path nothing will ever fetch.
|
|
607
|
+
description: "The reference, verbatim, that no stored asset answers \u2014 a `package://` URI the workspace does not hold, or an absolute or bare relative path nothing will ever fetch."
|
|
608
608
|
}),
|
|
609
609
|
element: import_zod3.z.enum(["mesh", "texture"]).meta({
|
|
610
610
|
description: "Which kind of reference it was: geometry the URDF names as a `mesh`, or a `texture` a surface paints with. Without it a client reports a missing texture as a missing mesh, contradicting `mesh_count` beside it."
|
|
611
611
|
})
|
|
612
612
|
})).meta({
|
|
613
|
-
description: "The references nothing in the store answers, each with the element that asked for it. A bare count
|
|
613
|
+
description: "The references nothing in the store answers, each with the element that asked for it. A bare count would send a developer hunting through the workspace by hand; the references are what they can act on."
|
|
614
614
|
})
|
|
615
615
|
});
|
|
616
616
|
var assetSyncRequest = import_zod3.z.object({
|
|
617
617
|
source: import_zod3.z.enum(["bridge"]).meta({
|
|
618
|
-
description: "Where the bytes come from. `bridge` is the only value today: the connected bridge reads them from the robot's own workspace.
|
|
618
|
+
description: "Where the bytes come from. `bridge` is the only value today: the connected bridge reads them from the robot's own workspace. Validated rather than ignored, so a caller naming an unknown source is told so instead of silently getting a bridge sync."
|
|
619
619
|
})
|
|
620
620
|
}).strict();
|
|
621
621
|
var assetSyncResponse = import_zod3.z.object({
|
|
@@ -629,7 +629,7 @@ var assetTooLargeDetails = import_zod3.z.object({
|
|
|
629
629
|
description: "The upload ceiling, in bytes."
|
|
630
630
|
}),
|
|
631
631
|
size_bytes: import_zod3.z.number().int().positive().meta({
|
|
632
|
-
description: 'How large the refused file
|
|
632
|
+
description: 'How large the refused file is, in bytes. With `limit_bytes` beside it a developer can tell whether to shrink the mesh or raise the limit; "too large" alone answers neither.'
|
|
633
633
|
})
|
|
634
634
|
});
|
|
635
635
|
var assetFailureKind = import_zod3.z.enum(["unresolvable", "upload_failed", "refused", "too_large"]);
|
|
@@ -729,8 +729,6 @@ var assetListResponse = import_zod3.z.object({
|
|
|
729
729
|
description: "Every asset stored for this robot: the URDF, the meshes it references, and the textures those paint with."
|
|
730
730
|
}),
|
|
731
731
|
/**
|
|
732
|
-
* The sync running right now, or `null`.
|
|
733
|
-
*
|
|
734
732
|
* **This field exists for the reload case.** A client that holds the
|
|
735
733
|
* `sync_id` only in memory loses its progress display on a refresh, and the
|
|
736
734
|
* state is still there server-side under `GET .../assets/sync/<id>` —
|
|
@@ -761,7 +759,7 @@ var assetListResponse = import_zod3.z.object({
|
|
|
761
759
|
* crash, and no amount of polling shortens it.
|
|
762
760
|
*/
|
|
763
761
|
urdf_available: import_zod3.z.boolean().nullable().meta({
|
|
764
|
-
description: 'What the connected bridge says it *could* transfer
|
|
762
|
+
description: 'What the connected bridge says it *could* transfer \u2014 deliberately separate from what has been transferred. `null` when no bridge is connected, distinct from `false`: "no robot is online to ask" and "the robot has no URDF" send a developer to different places. After a publisher is killed rather than shut down this can read `true` for some seconds, on the underlying DDS liveliness timeout rather than on any check made here.'
|
|
765
763
|
})
|
|
766
764
|
});
|
|
767
765
|
var missingAssetQuery = import_zod3.z.object({
|
|
@@ -775,10 +773,10 @@ var assetSyncBusyDetails = import_zod3.z.object({
|
|
|
775
773
|
started_at_ms: import_zod3.z.number().int().nonnegative()
|
|
776
774
|
});
|
|
777
775
|
|
|
778
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
776
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/config.js
|
|
779
777
|
var import_zod5 = require("zod");
|
|
780
778
|
|
|
781
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
779
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/alerts.js
|
|
782
780
|
var import_zod4 = require("zod");
|
|
783
781
|
var alertRowCondition = import_zod4.z.discriminatedUnion("kind", [
|
|
784
782
|
import_zod4.z.strictObject({
|
|
@@ -848,7 +846,7 @@ var putDatapointDisplayRequest = import_zod4.z.object({
|
|
|
848
846
|
y_max: import_zod4.z.number().finite().nullable()
|
|
849
847
|
}).strict();
|
|
850
848
|
|
|
851
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
849
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/config.js
|
|
852
850
|
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
851
|
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
852
|
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.";
|
|
@@ -1240,7 +1238,7 @@ var datapointConfig = strictObject({
|
|
|
1240
1238
|
examples: [2, 0.5]
|
|
1241
1239
|
}).optional(),
|
|
1242
1240
|
description: serviceDescription.meta({
|
|
1243
|
-
description: "Prose about what this value is, for whoever meets it in the console
|
|
1241
|
+
description: "Prose about what this value is, for whoever meets it in the console. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all \u2014 but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
|
|
1244
1242
|
/**
|
|
1245
1243
|
* The sentence both datapoint snippets already place here, verbatim. Its
|
|
1246
1244
|
* four siblings — an action's, a service's, a publisher's, a camera's —
|
|
@@ -1895,7 +1893,7 @@ var configState = import_zod5.z.object({
|
|
|
1895
1893
|
applied_errors: import_zod5.z.array(applyError).nullable()
|
|
1896
1894
|
});
|
|
1897
1895
|
|
|
1898
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
1896
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/introspection.js
|
|
1899
1897
|
var import_zod6 = require("zod");
|
|
1900
1898
|
var rosGraphEntry = import_zod6.z.object({
|
|
1901
1899
|
name: rosName,
|
|
@@ -1934,22 +1932,22 @@ var typeDefinition = import_zod6.z.discriminatedUnion("kind", [
|
|
|
1934
1932
|
})
|
|
1935
1933
|
]);
|
|
1936
1934
|
|
|
1937
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
1935
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/jobs.js
|
|
1938
1936
|
var import_zod7 = require("zod");
|
|
1939
1937
|
var jobState = import_zod7.z.enum(["running", "succeeded", "failed", "cancelled", "lost"]);
|
|
1940
1938
|
var job = import_zod7.z.object({
|
|
1941
1939
|
id: import_zod7.z.uuid().meta({
|
|
1942
|
-
description: "The job's id, minted by the cloud when the invocation is accepted. Informative
|
|
1940
|
+
description: "The job's id, minted by the cloud when the invocation is accepted. Informative \u2014 state is observed by slug; a cancel names this id to stop one specific job rather than whatever is running."
|
|
1943
1941
|
}),
|
|
1944
1942
|
robot_id: import_zod7.z.uuid().meta({ description: "The robot this job is running on." }),
|
|
1945
1943
|
slug: slug.meta({
|
|
1946
1944
|
description: "The action or service this job is running, as the published configuration exposes it. One slug carries one job at a time, so every observer of that slug sees the same one."
|
|
1947
1945
|
}),
|
|
1948
1946
|
state: jobState.meta({
|
|
1949
|
-
description: "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome \u2014 the bridge restarted mid-job and the result is gone \u2014
|
|
1947
|
+
description: "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome \u2014 the bridge restarted mid-job and the result is gone \u2014 stated rather than left reading `running` by default."
|
|
1950
1948
|
}),
|
|
1951
1949
|
started_at: import_zod7.z.iso.datetime().meta({
|
|
1952
|
-
description: "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge
|
|
1950
|
+
description: "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge, this is **adoption time**, not the real start \u2014 the cloud never minted it."
|
|
1953
1951
|
}),
|
|
1954
1952
|
updated_at: import_zod7.z.iso.datetime().meta({
|
|
1955
1953
|
description: "When this job last changed, as an ISO 8601 timestamp."
|
|
@@ -2032,7 +2030,7 @@ var jobQueueFullDetails = import_zod7.z.object({
|
|
|
2032
2030
|
var JOB_RUN_PAGE_MAX = 200;
|
|
2033
2031
|
var jobActor = import_zod7.z.object({
|
|
2034
2032
|
kind: import_zod7.z.enum(["developer", "end_user", "app_user", "server_key"]).meta({
|
|
2035
|
-
description: "What the caller was acting as: a `developer` in the console, an `app_user` of one app, or a `server_key` used by server-side code. A bridge invokes nothing, so it is deliberately not a case here. `end_user` appears only on runs recorded before app users replaced the organisation-wide user pool \u2014
|
|
2033
|
+
description: "What the caller was acting as: a `developer` in the console, an `app_user` of one app, or a `server_key` used by server-side code. A bridge invokes nothing, so it is deliberately not a case here. `end_user` appears only on runs recorded before app users replaced the organisation-wide user pool \u2014 kept so old runs still render; nothing writes it now."
|
|
2036
2034
|
}),
|
|
2037
2035
|
id: import_zod7.z.uuid().meta({
|
|
2038
2036
|
description: "The id of the Fleetless user, app user or server key that invoked the run."
|
|
@@ -2049,7 +2047,7 @@ var jobActor = import_zod7.z.object({
|
|
|
2049
2047
|
var jobRunKind = import_zod7.z.enum(["action", "service"]);
|
|
2050
2048
|
var jobRun = import_zod7.z.object({
|
|
2051
2049
|
id: import_zod7.z.uuid().meta({
|
|
2052
|
-
description: "The run's id
|
|
2050
|
+
description: "The run's id \u2014 the same id the invocation was answered with, so a caller that kept a job id can find its durable record here later."
|
|
2053
2051
|
}),
|
|
2054
2052
|
robot_id: import_zod7.z.uuid().meta({ description: "The robot the run happened on." }),
|
|
2055
2053
|
slug: slug.meta({
|
|
@@ -2152,7 +2150,7 @@ var jobRunSummary = import_zod7.z.object({
|
|
|
2152
2150
|
since_ms: import_zod7.z.number().int().nonnegative()
|
|
2153
2151
|
});
|
|
2154
2152
|
|
|
2155
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
2153
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/protocol.js
|
|
2156
2154
|
var MAX_PATIENCE_MS = 12e4;
|
|
2157
2155
|
var MIN_PATIENCE_MS = 1e3;
|
|
2158
2156
|
var activeJob = import_zod8.z.object({
|
|
@@ -2168,22 +2166,21 @@ var bridgeHello = import_zod8.z.object({
|
|
|
2168
2166
|
/**
|
|
2169
2167
|
* Every job this bridge still knows about, right now.
|
|
2170
2168
|
*
|
|
2171
|
-
* A reconnect and a restart look **identical** on the wire
|
|
2172
|
-
*
|
|
2173
|
-
*
|
|
2174
|
-
*
|
|
2175
|
-
*
|
|
2176
|
-
*
|
|
2177
|
-
* `lost`.
|
|
2169
|
+
* A reconnect and a restart look **identical** on the wire — same token,
|
|
2170
|
+
* same version, same frame — but must end differently: after a dropped
|
|
2171
|
+
* connection the running jobs are still running, after a restart their
|
|
2172
|
+
* results are gone forever. Enumerating what the bridge still has settles
|
|
2173
|
+
* it without either side guessing: the cloud marks every job it believed
|
|
2174
|
+
* running that is *not* named here as `lost`.
|
|
2178
2175
|
*
|
|
2179
|
-
*
|
|
2180
|
-
*
|
|
2181
|
-
*
|
|
2182
|
-
*
|
|
2176
|
+
* Deliberately needs no persistence at the bridge: a live process lists its
|
|
2177
|
+
* live jobs, a process that just started lists none — exactly the truth
|
|
2178
|
+
* the cloud needs. A breadcrumb file would only add a window in which the
|
|
2179
|
+
* crash beat the write.
|
|
2183
2180
|
*
|
|
2184
|
-
* Defaulted, so a bridge that sends no such field still parses;
|
|
2185
|
-
*
|
|
2186
|
-
*
|
|
2181
|
+
* Defaulted, so a bridge that sends no such field still parses; no jobs
|
|
2182
|
+
* and no report both mean the same thing to the cloud: nothing to keep
|
|
2183
|
+
* alive.
|
|
2187
2184
|
*/
|
|
2188
2185
|
active_jobs: import_zod8.z.array(activeJob).max(500).default([])
|
|
2189
2186
|
});
|
|
@@ -2551,11 +2548,11 @@ var bridgeCameraState = import_zod8.z.object({
|
|
|
2551
2548
|
request_id: import_zod8.z.string().min(1).max(64).nullable()
|
|
2552
2549
|
});
|
|
2553
2550
|
|
|
2554
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
2551
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/config-issues.js
|
|
2555
2552
|
var EXPOSURE_SECTIONS = ["datapoints", "actions", "services", "publishers", "cameras"];
|
|
2556
2553
|
var EXPOSURE_SECTION_NAMES = new Set(EXPOSURE_SECTIONS);
|
|
2557
2554
|
|
|
2558
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
2555
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/rest.js
|
|
2559
2556
|
var import_zod9 = require("zod");
|
|
2560
2557
|
var robot = import_zod9.z.object({
|
|
2561
2558
|
id: import_zod9.z.uuid().meta({
|
|
@@ -2570,7 +2567,7 @@ var robot = import_zod9.z.object({
|
|
|
2570
2567
|
});
|
|
2571
2568
|
var patchRobotResponse = import_zod9.z.object({
|
|
2572
2569
|
robot: robot.meta({
|
|
2573
|
-
description: "The robot as it now stands, after the patch
|
|
2570
|
+
description: "The robot as it now stands, after the patch. The whole resource comes back, not only the changed fields."
|
|
2574
2571
|
})
|
|
2575
2572
|
});
|
|
2576
2573
|
var createRobotRequest = import_zod9.z.object({
|
|
@@ -2600,10 +2597,10 @@ var robotListResponse = import_zod9.z.object({
|
|
|
2600
2597
|
var datapointValue = import_zod9.z.object({
|
|
2601
2598
|
slug: slug.meta({ description: "The datapoint this value belongs to." }),
|
|
2602
2599
|
value: import_zod9.z.unknown().meta({
|
|
2603
|
-
description: "The value
|
|
2600
|
+
description: "The value, shaped by the datapoint: a number, a boolean, a string, or the whole ROS message where the configuration names no field inside it. Any `scale` and `offset` the configuration declares have already been applied, at the robot."
|
|
2604
2601
|
}),
|
|
2605
2602
|
timestamp_ms: import_zod9.z.number().int().nonnegative().meta({
|
|
2606
|
-
description: "When the value was captured, as a unix timestamp in milliseconds.
|
|
2603
|
+
description: "When the value was captured, as a unix timestamp in milliseconds. The **bridge's capture time**, never the time the cloud received it \u2014 the one exception is the built-in `bridge_state`, which the cloud observes by construction."
|
|
2607
2604
|
})
|
|
2608
2605
|
});
|
|
2609
2606
|
var robotDetailResponse = import_zod9.z.object({
|
|
@@ -2745,7 +2742,7 @@ var invokeResponse = import_zod9.z.object({
|
|
|
2745
2742
|
});
|
|
2746
2743
|
var serviceCallResponse = import_zod9.z.object({
|
|
2747
2744
|
result: import_zod9.z.unknown().meta({
|
|
2748
|
-
description: "What the service returned, shaped by the ROS service
|
|
2745
|
+
description: "What the service returned, shaped by the ROS service. A service call is awaited to completion, so there is no job to observe afterwards and no id to hold on to."
|
|
2749
2746
|
})
|
|
2750
2747
|
});
|
|
2751
2748
|
var invokeOrServiceResponse = import_zod9.z.union([invokeResponse, serviceCallResponse]);
|
|
@@ -3315,13 +3312,13 @@ var slugUsageResponse = import_zod9.z.object({
|
|
|
3315
3312
|
alert_count: import_zod9.z.number().int().nonnegative()
|
|
3316
3313
|
});
|
|
3317
3314
|
|
|
3318
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3315
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/realtime.js
|
|
3319
3316
|
var import_zod14 = require("zod");
|
|
3320
3317
|
|
|
3321
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3318
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/client-auth.js
|
|
3322
3319
|
var import_zod13 = require("zod");
|
|
3323
3320
|
|
|
3324
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3321
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/apps.js
|
|
3325
3322
|
var import_zod10 = require("zod");
|
|
3326
3323
|
var appIdentifier = slug;
|
|
3327
3324
|
var app = import_zod10.z.object({
|
|
@@ -3335,7 +3332,7 @@ var app = import_zod10.z.object({
|
|
|
3335
3332
|
description: "The display name, shown in the console and available to the developer's own pages through the `app.name` mail-template variable. Free text, changed through `PATCH /api/apps/:id`."
|
|
3336
3333
|
}),
|
|
3337
3334
|
identifier: appIdentifier.meta({
|
|
3338
|
-
description: "The stable handle a client sends at login, lowercase and underscore-separated. **Globally unique, not per organisation** \u2014 `clientLoginRequest` carries no org context
|
|
3335
|
+
description: "The stable handle a client sends at login, lowercase and underscore-separated. **Globally unique, not per organisation** \u2014 `clientLoginRequest` carries no org context, so a collision is refused with `identifier_taken`."
|
|
3339
3336
|
}),
|
|
3340
3337
|
/** Robots are referenced individually; tags never grant rights. */
|
|
3341
3338
|
robot_ids: import_zod10.z.array(import_zod10.z.uuid()).meta({
|
|
@@ -3353,7 +3350,7 @@ var app = import_zod10.z.object({
|
|
|
3353
3350
|
* (`createAppUserRequest`, `createAppInvitationRequest`) rather than
|
|
3354
3351
|
* pre-filling a form.
|
|
3355
3352
|
*
|
|
3356
|
-
*
|
|
3353
|
+
* Worth saying: it changes what a wrong value costs.
|
|
3357
3354
|
* A prefill somebody can see and correct became a default applied on the
|
|
3358
3355
|
* server, and the obvious next question — should it be required instead? —
|
|
3359
3356
|
* has a deliberate answer: no, because an invitation resolves the role at
|
|
@@ -3370,7 +3367,7 @@ var app = import_zod10.z.object({
|
|
|
3370
3367
|
* check that, so `PATCH /api/apps/:id` does.
|
|
3371
3368
|
*/
|
|
3372
3369
|
default_role_id: import_zod10.z.uuid().nullable().meta({
|
|
3373
|
-
description: "The role an app user gets when
|
|
3370
|
+
description: "The role an app user gets when created or invited without an explicit one. `null` means this app has not chosen a default, the normal state of an app created before its roles were configured \u2014 and then a create or invite that omits `role_id` gets `validation_error`, not a user with no role. An invitation resolves the role when issued, so changing this never re-aims an outstanding one. The role must belong to this app, which `PATCH /api/apps/:id` checks and the schema cannot."
|
|
3374
3371
|
}),
|
|
3375
3372
|
created_at: import_zod10.z.iso.datetime().meta({
|
|
3376
3373
|
description: "When the app was created, as an ISO 8601 timestamp. `GET /api/apps` orders by this field."
|
|
@@ -3406,7 +3403,7 @@ var updateAppRequest = import_zod10.z.object({
|
|
|
3406
3403
|
var serverKeyToken = import_zod10.z.string().regex(/^flk_[0-9a-f]{32}$/);
|
|
3407
3404
|
var serverKey = import_zod10.z.object({
|
|
3408
3405
|
id: import_zod10.z.uuid().meta({
|
|
3409
|
-
description: "The key row, and what the rotate and delete routes address. It is not the key:
|
|
3406
|
+
description: "The key row, and what the rotate and delete routes address. It is not the key: this shape never carries the secret."
|
|
3410
3407
|
}),
|
|
3411
3408
|
app_id: import_zod10.z.uuid().meta({
|
|
3412
3409
|
description: "The app whose full rights this key carries. A key is never shared between apps."
|
|
@@ -3424,7 +3421,7 @@ var serverKey = import_zod10.z.object({
|
|
|
3424
3421
|
});
|
|
3425
3422
|
var serverKeyListResponse = import_zod10.z.object({
|
|
3426
3423
|
server_keys: import_zod10.z.array(serverKey).meta({
|
|
3427
|
-
description: "The app's server keys as metadata, oldest first by `created_at`. The raw secret is not here and never will be: it exists once, in the
|
|
3424
|
+
description: "The app's server keys as metadata, oldest first by `created_at`. The raw secret is not here and never will be: it exists once, in the response that created or rotated the key."
|
|
3428
3425
|
})
|
|
3429
3426
|
});
|
|
3430
3427
|
var createServerKeyResponse = import_zod10.z.object({
|
|
@@ -3442,7 +3439,7 @@ var role = import_zod10.z.object({
|
|
|
3442
3439
|
description: "The role's name, shown wherever a user's access is chosen. The two roles every app starts with are named `observe` and `operate`."
|
|
3443
3440
|
}),
|
|
3444
3441
|
builtin: import_zod10.z.boolean().meta({
|
|
3445
|
-
description: "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` \u2014 the flag exists so the console can explain where they came from, not to protect them. It does not make them renamable or deletable
|
|
3442
|
+
description: "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` \u2014 the flag exists so the console can explain where they came from, not to protect them. It does not make them renamable or deletable \u2014 no route does that for any role."
|
|
3446
3443
|
})
|
|
3447
3444
|
});
|
|
3448
3445
|
var roleListResponse = import_zod10.z.object({
|
|
@@ -3503,10 +3500,10 @@ var rolePermissions = import_zod10.z.object({
|
|
|
3503
3500
|
})
|
|
3504
3501
|
});
|
|
3505
3502
|
|
|
3506
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3503
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/app-users.js
|
|
3507
3504
|
var import_zod12 = require("zod");
|
|
3508
3505
|
|
|
3509
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3506
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/identity.js
|
|
3510
3507
|
var import_zod11 = require("zod");
|
|
3511
3508
|
var password = import_zod11.z.string().min(12).max(256);
|
|
3512
3509
|
var USER_DISPLAY_NAME_MAX = 120;
|
|
@@ -3524,7 +3521,7 @@ var org = import_zod11.z.object({
|
|
|
3524
3521
|
});
|
|
3525
3522
|
var patchOrgResponse = import_zod11.z.object({
|
|
3526
3523
|
org: org.meta({
|
|
3527
|
-
description: "The organisation as it now stands, after the patch
|
|
3524
|
+
description: "The organisation as it now stands, after the patch. The whole resource comes back, not only the changed fields."
|
|
3528
3525
|
})
|
|
3529
3526
|
});
|
|
3530
3527
|
var fleetlessUser = import_zod11.z.object({
|
|
@@ -3532,7 +3529,7 @@ var fleetlessUser = import_zod11.z.object({
|
|
|
3532
3529
|
description: "The Fleetless user in the API, assigned by the cloud and stable for the life of the account."
|
|
3533
3530
|
}),
|
|
3534
3531
|
org_id: import_zod11.z.uuid().meta({
|
|
3535
|
-
description: "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at
|
|
3532
|
+
description: "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at, not a filter it applies."
|
|
3536
3533
|
}),
|
|
3537
3534
|
email: import_zod11.z.email().meta({
|
|
3538
3535
|
description: "The address the account is identified by, **globally unique** across every organisation. Immutable after creation: it is what every invitation, reset link and audit line names."
|
|
@@ -3541,7 +3538,7 @@ var fleetlessUser = import_zod11.z.object({
|
|
|
3541
3538
|
description: "Optional human name, shown by the console instead of the address where present. Self-service through `PATCH /api/auth/me`; never used for authentication. `null` when the person never supplied one."
|
|
3542
3539
|
}),
|
|
3543
3540
|
tier: orgAdminTier.meta({
|
|
3544
|
-
description: "The console powers this person holds. **Required** \u2014 every Fleetless user
|
|
3541
|
+
description: "The console powers this person holds. **Required** \u2014 every Fleetless user has a tier; it was optional only while the org also held people with no console powers to grade, and that pool is gone."
|
|
3545
3542
|
}),
|
|
3546
3543
|
created_at: import_zod11.z.iso.datetime().meta({
|
|
3547
3544
|
description: "When the account was created, as an ISO 8601 timestamp."
|
|
@@ -3668,9 +3665,9 @@ var authMeResponse = import_zod11.z.object({ org, user: fleetlessUser });
|
|
|
3668
3665
|
var patchOrgRequest = import_zod11.z.object({ name: import_zod11.z.string().min(1).max(120) }).strict();
|
|
3669
3666
|
var patchAuthMeRequest = import_zod11.z.object({ display_name: import_zod11.z.string().min(1).max(USER_DISPLAY_NAME_MAX).nullable() }).strict();
|
|
3670
3667
|
|
|
3671
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3668
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/app-users.js
|
|
3672
3669
|
var APP_USER_DISPLAY_NAME_MAX = 120;
|
|
3673
|
-
var providerSlug = import_zod12.z.string().max(40).regex(/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/, "
|
|
3670
|
+
var providerSlug = import_zod12.z.string().max(40).regex(/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/, "must be lowercase and hyphen-separated, starting with a letter");
|
|
3674
3671
|
var appUserStatus = import_zod12.z.enum(["pending_verification", "active", "blocked"]);
|
|
3675
3672
|
var appUser = import_zod12.z.object({
|
|
3676
3673
|
id: import_zod12.z.uuid().meta({
|
|
@@ -3698,10 +3695,10 @@ var appUser = import_zod12.z.object({
|
|
|
3698
3695
|
* accepted — it does not mean blocked and it does not mean without access.
|
|
3699
3696
|
*/
|
|
3700
3697
|
has_password: import_zod12.z.boolean().meta({
|
|
3701
|
-
description: 'Whether this account has a Fleetless-held password
|
|
3698
|
+
description: 'Whether this account has a Fleetless-held password. `false` is an identity-provider-only account, or an invitation not yet accepted \u2014 it does not mean blocked and it does not mean without access. No hash, no algorithm and no "last changed" travels here, and nothing on the wire can say whether a password is strong or already known to somebody else.'
|
|
3702
3699
|
}),
|
|
3703
3700
|
providers: import_zod12.z.array(providerSlug).max(20).meta({
|
|
3704
|
-
description: "The slugs of the identity providers this account is linked to, empty for a password-only user.
|
|
3701
|
+
description: "The slugs of the identity providers this account is linked to, empty for a password-only user. Lets a developer's user list say where an account came from without a second request."
|
|
3705
3702
|
}),
|
|
3706
3703
|
last_login_at: import_zod12.z.iso.datetime().nullable().meta({
|
|
3707
3704
|
description: "When this user last signed in, or `null` if they never have. Required and nullable rather than optional, so *never logged in* stays distinguishable from *this field was not loaded*."
|
|
@@ -3795,7 +3792,7 @@ var appOidcProvider = import_zod12.z.object({
|
|
|
3795
3792
|
description: "Whether a federated login may join an **existing** app user with the same address. It needs the provider to assert `email_verified` as well: either condition alone is account takeover, since a provider that lets anyone type any address into a profile would otherwise hand over every matching account, and a developer who connects a provider for a subset of their users would otherwise silently merge strangers."
|
|
3796
3793
|
}),
|
|
3797
3794
|
enabled: import_zod12.z.boolean().meta({
|
|
3798
|
-
description: "Whether this provider is offered
|
|
3795
|
+
description: "Whether this provider is offered. A disabled provider disappears from `GET /api/client/providers` and refuses a start with `provider_disabled`, without the row and its linked identities being deleted."
|
|
3799
3796
|
}),
|
|
3800
3797
|
created_at: import_zod12.z.iso.datetime().meta({ description: "When the provider was configured, as an ISO 8601 timestamp." })
|
|
3801
3798
|
}).strict();
|
|
@@ -3919,10 +3916,10 @@ var mailOutcome = import_zod12.z.object({
|
|
|
3919
3916
|
})
|
|
3920
3917
|
});
|
|
3921
3918
|
|
|
3922
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3919
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/client-auth.js
|
|
3923
3920
|
var clientLoginRequest = import_zod13.z.object({
|
|
3924
3921
|
app_identifier: appIdentifier.meta({
|
|
3925
|
-
description: "The app being logged in to
|
|
3922
|
+
description: "The app being logged in to: its globally unique, lowercase, underscore-separated identifier, chosen by the developer at creation. There is no organisation context at login, so this is what decides which app the credentials are checked for."
|
|
3926
3923
|
}),
|
|
3927
3924
|
email: import_zod13.z.email().meta({
|
|
3928
3925
|
description: "The app user's address. Addresses are unique **per app**, not across Fleetless: the same address may be an unrelated account in another app of the same organisation, so this pair is what identifies a person here."
|
|
@@ -3938,7 +3935,7 @@ var clientRefreshRequest = import_zod13.z.object({
|
|
|
3938
3935
|
});
|
|
3939
3936
|
var clientLogoutRequest = import_zod13.z.object({
|
|
3940
3937
|
refresh_token: import_zod13.z.string().min(1).meta({
|
|
3941
|
-
description: "Any refresh token of the session to end. The whole token family is revoked server-side, so a token stolen before this call stops working too \u2014 clearing a client-side store is a gesture, not a revocation. The answer is `204`: a token the server does not recognise gets it too, since the end state
|
|
3938
|
+
description: "Any refresh token of the session to end. The whole token family is revoked server-side, so a token stolen before this call stops working too \u2014 clearing a client-side store is a gesture, not a revocation. The answer is `204`: a token the server does not recognise gets it too, since that is the end state being asked for."
|
|
3942
3939
|
})
|
|
3943
3940
|
});
|
|
3944
3941
|
var clientRegisterRequest = import_zod13.z.object({
|
|
@@ -4054,7 +4051,7 @@ var clientMcpInteraction = import_zod13.z.object({
|
|
|
4054
4051
|
app_id: import_zod13.z.uuid().meta({ description: "The app this authorization is for. The approving token's `app_id` must match it \u2014 an interaction of one app cannot be approved with a session from another." }),
|
|
4055
4052
|
client_name: import_zod13.z.string().nullable().meta({ description: "What the MCP client calls itself, or `null` if it named nothing. **Unverified** \u2014 see `client_name_verified`." }),
|
|
4056
4053
|
client_name_verified: import_zod13.z.literal(false).meta({
|
|
4057
|
-
description: "Always `false`. The client registered itself without authentication and
|
|
4054
|
+
description: "Always `false`. The client registered itself without authentication and named itself, so it must be rendered as a claim and never as an identity. There is no verified case, which is why this is a literal and not a boolean: a `true` branch would be dead code that looked like a safeguard."
|
|
4058
4055
|
}),
|
|
4059
4056
|
scopes: import_zod13.z.array(import_zod13.z.string()).meta({ description: "The scopes the client asked for, to show the person before they approve." }),
|
|
4060
4057
|
already_granted: import_zod13.z.boolean().meta({ description: "Whether this user has already approved this client. It is a record of what they answered last time, and **this route makes no second use of it**: an app that skips its own consent screen when this is `true` is the only thing deciding that, and approve succeeds identically for a user who holds no grant at all. The standing grant is read elsewhere, on every request to the app's MCP endpoint. Withdrawing it is `DELETE /api/client/mcp/grants/:clientId` for the person themselves and `DELETE /api/apps/:id/users/:userId/mcp-grants/:clientId` for the developer. A withdrawal makes this `false` again at the next authorization **and stops the client at its very next MCP call**, unexpired access token and all \u2014 up to fifteen minutes of it \u2014 because the endpoint keys that check on the `client_id` the token carries." }),
|
|
@@ -4070,10 +4067,10 @@ var mcpConsentGrant = import_zod13.z.object({
|
|
|
4070
4067
|
description: "The MCP client this consent is for, as its dynamic registration was issued. It is the value the withdrawal routes take in their path, and it is the only stable handle on a client \u2014 the name beside it is not one."
|
|
4071
4068
|
}),
|
|
4072
4069
|
client_name: import_zod13.z.string().nullable().meta({
|
|
4073
|
-
description: "What the client calls itself, or `null`
|
|
4070
|
+
description: "What the client calls itself, or `null` once its registration is gone. **Unverified** \u2014 see `client_name_verified`."
|
|
4074
4071
|
}),
|
|
4075
4072
|
client_name_verified: import_zod13.z.literal(false).meta({
|
|
4076
|
-
description: "Always `false`. The client registered itself without authentication and
|
|
4073
|
+
description: "Always `false`. The client registered itself without authentication and named itself, so it must be rendered as a claim and never as an identity. There is no verified case, which is why this is a literal and not a boolean: a `true` branch would be dead code that looked like a safeguard."
|
|
4077
4074
|
}),
|
|
4078
4075
|
granted_at: import_zod13.z.iso.datetime().meta({
|
|
4079
4076
|
description: "When the consent was last given. A withdrawal followed by a fresh approval moves it, because the second approval is the agreement that stands \u2014 it is not a record of the first time anybody ever said yes."
|
|
@@ -4086,7 +4083,7 @@ var mcpConsentGrantListResponse = import_zod13.z.object({
|
|
|
4086
4083
|
});
|
|
4087
4084
|
var clientIdentity = import_zod13.z.object({
|
|
4088
4085
|
kind: import_zod13.z.enum(["developer", "app_user", "server_key"]).meta({
|
|
4089
|
-
description: "Which of the three kinds of caller this is: a `developer` working through the console, an `app_user` holding a token from a client login, or a `server_key` used by server-side code. Stated outright
|
|
4086
|
+
description: "Which of the three kinds of caller this is: a `developer` working through the console, an `app_user` holding a token from a client login, or a `server_key` used by server-side code. Stated outright, not inferred from which id is set."
|
|
4090
4087
|
}),
|
|
4091
4088
|
developer_id: import_zod13.z.uuid().nullable().meta({
|
|
4092
4089
|
description: "The Fleetless user behind this session, or `null` when `kind` is not `developer`."
|
|
@@ -4108,7 +4105,7 @@ var clientIdentity = import_zod13.z.object({
|
|
|
4108
4105
|
})
|
|
4109
4106
|
});
|
|
4110
4107
|
|
|
4111
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
4108
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/realtime.js
|
|
4112
4109
|
var clientAuth = import_zod14.z.object({
|
|
4113
4110
|
type: import_zod14.z.literal("auth"),
|
|
4114
4111
|
token: import_zod14.z.string().min(1)
|
|
@@ -4294,10 +4291,10 @@ var liveSessionEvent = import_zod14.z.object({
|
|
|
4294
4291
|
/**
|
|
4295
4292
|
* **Classified text the cloud produced, never text the robot sent.**
|
|
4296
4293
|
*
|
|
4297
|
-
*
|
|
4298
|
-
*
|
|
4299
|
-
*
|
|
4300
|
-
*
|
|
4294
|
+
* Nothing sanitises `bridgeCameraState.error.message`, and a camera
|
|
4295
|
+
* password reaches a developer surface through exactly that route — which
|
|
4296
|
+
* is why the cloud maps a robot's diagnosis to fixed strings rather than
|
|
4297
|
+
* forwarding it.
|
|
4301
4298
|
*
|
|
4302
4299
|
* So: `null` unless the cloud itself has something classified to say. If a
|
|
4303
4300
|
* developer needs the robot's own diagnosis later, it arrives as a mapped
|
|
@@ -4386,17 +4383,34 @@ var orgEventDropped = import_zod14.z.object({
|
|
|
4386
4383
|
dropped: import_zod14.z.number().int().positive()
|
|
4387
4384
|
}).strict();
|
|
4388
4385
|
|
|
4389
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
4386
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/client-robots.js
|
|
4390
4387
|
var import_zod15 = require("zod");
|
|
4391
|
-
var
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
})
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4388
|
+
var clientRobotListItem = import_zod15.z.object({
|
|
4389
|
+
...robot.shape,
|
|
4390
|
+
bridge_state: bridgeState.meta({
|
|
4391
|
+
description: "The built-in `bridge_state` datapoint as the cloud observes it right now: whether the bridge is connected, and its latency when it is."
|
|
4392
|
+
}),
|
|
4393
|
+
published_version: import_zod15.z.number().int().positive().nullable().meta({
|
|
4394
|
+
description: 'The published configuration version, or `null` when nothing has been published yet. A robot with nothing published is still listed \u2014 "not configured yet" is a real state, and the caller is entitled to it \u2014 and its datasheet answers an empty exposure list.'
|
|
4395
|
+
})
|
|
4396
|
+
});
|
|
4397
|
+
var clientRobotListResponse = import_zod15.z.object({
|
|
4398
|
+
robots: import_zod15.z.array(clientRobotListItem).meta({
|
|
4399
|
+
description: "Every robot the caller reaches, in name order with the id as the tiebreak. An app user reaches the robots their app attaches on which their role grants at least one slug or capability; a server key reaches every robot its app attaches; a developer reaches every robot of the organisation."
|
|
4400
|
+
})
|
|
4401
|
+
});
|
|
4402
|
+
|
|
4403
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/audit.js
|
|
4404
|
+
var import_zod16 = require("zod");
|
|
4405
|
+
var auditActor = import_zod16.z.object({
|
|
4406
|
+
kind: import_zod16.z.enum(["developer", "end_user", "app_user", "server_key", "bridge"]),
|
|
4407
|
+
id: import_zod16.z.uuid(),
|
|
4408
|
+
label: import_zod16.z.string().min(1).max(200)
|
|
4409
|
+
});
|
|
4410
|
+
var auditEvent = import_zod16.z.object({
|
|
4411
|
+
id: import_zod16.z.uuid(),
|
|
4412
|
+
org_id: import_zod16.z.uuid(),
|
|
4413
|
+
at: import_zod16.z.iso.datetime(),
|
|
4400
4414
|
/**
|
|
4401
4415
|
* A monotonic counter, ascending in write order, unique across the log.
|
|
4402
4416
|
*
|
|
@@ -4415,18 +4429,18 @@ var auditEvent = import_zod15.z.object({
|
|
|
4415
4429
|
* Required, not optional: an event without a sequence cannot be ordered
|
|
4416
4430
|
* against one that has it, and a log with two orderings has none.
|
|
4417
4431
|
*/
|
|
4418
|
-
seq:
|
|
4432
|
+
seq: import_zod16.z.number().int().positive(),
|
|
4419
4433
|
actor: auditActor,
|
|
4420
4434
|
/** Stable dotted name, e.g. `app_user.login`, `config.published`. */
|
|
4421
|
-
action:
|
|
4435
|
+
action: import_zod16.z.string().min(1).max(80),
|
|
4422
4436
|
/**
|
|
4423
4437
|
* What the action was about, if anything — a robot, an app, a user. Free
|
|
4424
4438
|
* of ids the console cannot resolve: carry the label with it.
|
|
4425
4439
|
*/
|
|
4426
|
-
target:
|
|
4427
|
-
kind:
|
|
4428
|
-
id:
|
|
4429
|
-
label:
|
|
4440
|
+
target: import_zod16.z.object({
|
|
4441
|
+
kind: import_zod16.z.string().min(1).max(40),
|
|
4442
|
+
id: import_zod16.z.string().min(1),
|
|
4443
|
+
label: import_zod16.z.string().min(1).max(200)
|
|
4430
4444
|
}).nullable(),
|
|
4431
4445
|
/**
|
|
4432
4446
|
* Action-specific extras.
|
|
@@ -4440,10 +4454,10 @@ var auditEvent = import_zod15.z.object({
|
|
|
4440
4454
|
* So: never credentials, never tokens. That is a rule, not a guarantee the
|
|
4441
4455
|
* schema enforces.
|
|
4442
4456
|
*/
|
|
4443
|
-
details:
|
|
4457
|
+
details: import_zod16.z.record(import_zod16.z.string(), import_zod16.z.unknown()).nullable()
|
|
4444
4458
|
});
|
|
4445
4459
|
var auditTimestampMs = wireTimestampMs;
|
|
4446
|
-
var auditQuery =
|
|
4460
|
+
var auditQuery = import_zod16.z.object({
|
|
4447
4461
|
/** Only events with a smaller `seq` — the next, older page. */
|
|
4448
4462
|
before_seq: wireSeqCursor.optional(),
|
|
4449
4463
|
/**
|
|
@@ -4452,19 +4466,18 @@ var auditQuery = import_zod15.z.object({
|
|
|
4452
4466
|
* coercion's result in either `io` direction, so the artifact would describe
|
|
4453
4467
|
* a shape a query string can never carry.
|
|
4454
4468
|
*/
|
|
4455
|
-
limit:
|
|
4469
|
+
limit: import_zod16.z.union([import_zod16.z.string().regex(/^\d{1,4}$/), import_zod16.z.number().int()]).transform((v) => Number(v)).pipe(import_zod16.z.number().int().positive().max(500)).optional(),
|
|
4456
4470
|
/** Exact action name, e.g. `config.published`. No prefix matching: a filter that matches more than it says is not one. */
|
|
4457
|
-
action:
|
|
4471
|
+
action: import_zod16.z.string().min(1).max(80).optional(),
|
|
4458
4472
|
/**
|
|
4459
4473
|
* Everything under a dotted prefix, e.g. `server_key.` for all three
|
|
4460
4474
|
* server-key actions.
|
|
4461
4475
|
*
|
|
4462
|
-
* **A separate parameter, not a widening of `action`.** The
|
|
4463
|
-
* `action` above
|
|
4464
|
-
*
|
|
4465
|
-
* one it is. Setting both is refused rather than resolved, because a query
|
|
4476
|
+
* **A separate parameter, not a widening of `action`.** The rule on
|
|
4477
|
+
* `action` above still stands; this is a different question with a name
|
|
4478
|
+
* that says which one it is. Setting both is refused, not resolved —
|
|
4466
4479
|
* naming an exact action *and* a prefix is a caller mistake, not a
|
|
4467
|
-
* combination
|
|
4480
|
+
* combination to guess the meaning of.
|
|
4468
4481
|
*
|
|
4469
4482
|
* **The published artifact cannot express that refusal**: a cross-field
|
|
4470
4483
|
* `.refine()` has no JSON Schema rendering, so `audit-query.schema.json`
|
|
@@ -4472,7 +4485,7 @@ var auditQuery = import_zod15.z.object({
|
|
|
4472
4485
|
* happily. The cloud is the only enforcement point — the same residual
|
|
4473
4486
|
* `orgLatencyQuery` and `orgUsageQuery` already name.
|
|
4474
4487
|
*/
|
|
4475
|
-
action_prefix:
|
|
4488
|
+
action_prefix: import_zod16.z.string().min(1).max(80).optional(),
|
|
4476
4489
|
/**
|
|
4477
4490
|
* Only events by this actor.
|
|
4478
4491
|
*
|
|
@@ -4484,9 +4497,9 @@ var auditQuery = import_zod15.z.object({
|
|
|
4484
4497
|
* Not an injection question — the query is parameterised either way. It is a
|
|
4485
4498
|
* **500 where a 400 belongs**, and a 500 is the answer that explains nothing.
|
|
4486
4499
|
*/
|
|
4487
|
-
actor_id:
|
|
4500
|
+
actor_id: import_zod16.z.uuid().optional(),
|
|
4488
4501
|
/** Only events about this kind of target, e.g. `robot`. */
|
|
4489
|
-
target_kind:
|
|
4502
|
+
target_kind: import_zod16.z.string().min(1).max(40).optional(),
|
|
4490
4503
|
/**
|
|
4491
4504
|
* Absolute bounds in unix milliseconds, **half-open `[from, to)`** — the
|
|
4492
4505
|
* same rule the history shapes follow.
|
|
@@ -4502,8 +4515,8 @@ var auditQuery = import_zod15.z.object({
|
|
|
4502
4515
|
message: "action and action_prefix cannot be combined",
|
|
4503
4516
|
path: ["action_prefix"]
|
|
4504
4517
|
});
|
|
4505
|
-
var auditListResponse =
|
|
4506
|
-
events:
|
|
4518
|
+
var auditListResponse = import_zod16.z.object({
|
|
4519
|
+
events: import_zod16.z.array(auditEvent),
|
|
4507
4520
|
/**
|
|
4508
4521
|
* The `seq` a caller sends as `before_seq` to keep reading — or `null` when
|
|
4509
4522
|
* there is nothing further.
|
|
@@ -4514,29 +4527,29 @@ var auditListResponse = import_zod15.z.object({
|
|
|
4514
4527
|
* not mean *no more* here. The same distinction `historySamples` was given
|
|
4515
4528
|
* `truncated` for.
|
|
4516
4529
|
*/
|
|
4517
|
-
next_cursor:
|
|
4530
|
+
next_cursor: import_zod16.z.number().int().positive().nullable()
|
|
4518
4531
|
});
|
|
4519
4532
|
|
|
4520
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
4521
|
-
var
|
|
4522
|
-
var apiError =
|
|
4523
|
-
code:
|
|
4524
|
-
message:
|
|
4525
|
-
details:
|
|
4533
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/errors.js
|
|
4534
|
+
var import_zod17 = require("zod");
|
|
4535
|
+
var apiError = import_zod17.z.object({
|
|
4536
|
+
code: import_zod17.z.string().min(1),
|
|
4537
|
+
message: import_zod17.z.string().min(1),
|
|
4538
|
+
details: import_zod17.z.unknown().optional()
|
|
4526
4539
|
});
|
|
4527
|
-
var parameterViolation =
|
|
4528
|
-
field:
|
|
4540
|
+
var parameterViolation = import_zod17.z.object({
|
|
4541
|
+
field: import_zod17.z.string().min(1),
|
|
4529
4542
|
/** Which rule failed — `min`, `max`, `enum`, `pattern`, `required`, `undeclared`. */
|
|
4530
|
-
rule:
|
|
4531
|
-
message:
|
|
4543
|
+
rule: import_zod17.z.string().min(1),
|
|
4544
|
+
message: import_zod17.z.string().min(1)
|
|
4532
4545
|
});
|
|
4533
|
-
var parameterInvalidDetails =
|
|
4534
|
-
violations:
|
|
4546
|
+
var parameterInvalidDetails = import_zod17.z.object({
|
|
4547
|
+
violations: import_zod17.z.array(parameterViolation).min(1)
|
|
4535
4548
|
});
|
|
4536
4549
|
|
|
4537
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
4538
|
-
var
|
|
4539
|
-
var oauthErrorCode =
|
|
4550
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/oauth.js
|
|
4551
|
+
var import_zod18 = require("zod");
|
|
4552
|
+
var oauthErrorCode = import_zod18.z.enum([
|
|
4540
4553
|
"invalid_request",
|
|
4541
4554
|
"invalid_client",
|
|
4542
4555
|
"invalid_grant",
|
|
@@ -4549,11 +4562,11 @@ var oauthErrorCode = import_zod17.z.enum([
|
|
|
4549
4562
|
/** RFC 8707: the `resource` named is not one this server issues tokens for. */
|
|
4550
4563
|
"invalid_target"
|
|
4551
4564
|
]);
|
|
4552
|
-
var oauthError =
|
|
4565
|
+
var oauthError = import_zod18.z.object({
|
|
4553
4566
|
error: oauthErrorCode,
|
|
4554
|
-
error_description:
|
|
4567
|
+
error_description: import_zod18.z.string().min(1).max(500).optional(),
|
|
4555
4568
|
/** Echoed back per RFC 6749 §4.1.2.1 so a client can match the response. */
|
|
4556
|
-
state:
|
|
4569
|
+
state: import_zod18.z.string().min(1).max(500).optional(),
|
|
4557
4570
|
/**
|
|
4558
4571
|
* **A Fleetless reason carried inside a standard envelope, and it exists
|
|
4559
4572
|
* because the alternative lost a distinction.**
|
|
@@ -4571,9 +4584,9 @@ var oauthError = import_zod17.z.object({
|
|
|
4571
4584
|
* our own tooling switches on. RFC 6749 §5.2 permits additional members, and
|
|
4572
4585
|
* a client that ignores this one still behaves correctly.
|
|
4573
4586
|
*/
|
|
4574
|
-
fleetless_code:
|
|
4587
|
+
fleetless_code: import_zod18.z.string().min(1).max(60).optional()
|
|
4575
4588
|
});
|
|
4576
|
-
var redirectUri =
|
|
4589
|
+
var redirectUri = import_zod18.z.string().min(1).max(2e3).refine((v) => {
|
|
4577
4590
|
let url;
|
|
4578
4591
|
try {
|
|
4579
4592
|
url = new URL(v);
|
|
@@ -4588,161 +4601,161 @@ var redirectUri = import_zod17.z.string().min(1).max(2e3).refine((v) => {
|
|
|
4588
4601
|
return ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
|
|
4589
4602
|
return false;
|
|
4590
4603
|
}, { message: "redirect_uri must be an https URL, or http on an explicit loopback address, and carry no fragment" });
|
|
4591
|
-
var codeChallengeMethod =
|
|
4604
|
+
var codeChallengeMethod = import_zod18.z.enum(["S256"]);
|
|
4592
4605
|
var MCP_DCR_MAX_REDIRECT_URIS = 5;
|
|
4593
|
-
var dynamicClientRegistrationRequest =
|
|
4594
|
-
redirect_uris:
|
|
4595
|
-
description: `Where the authorization code may be returned, and the one field a registration cannot omit. Each must be an \`https\` URL, or \`http\` on an explicit loopback address for a native app that cannot hold a certificate, and none may carry a fragment.
|
|
4606
|
+
var dynamicClientRegistrationRequest = import_zod18.z.object({
|
|
4607
|
+
redirect_uris: import_zod18.z.array(redirectUri).min(1).max(MCP_DCR_MAX_REDIRECT_URIS).meta({
|
|
4608
|
+
description: `Where the authorization code may be returned, and the one field a registration cannot omit. Each must be an \`https\` URL, or \`http\` on an explicit loopback address for a native app that cannot hold a certificate, and none may carry a fragment. Between \`1\` and \`${MCP_DCR_MAX_REDIRECT_URIS}\` of them; duplicates are collapsed rather than counted twice. Matched **exactly** at the authorize step against what was registered here.`
|
|
4596
4609
|
}),
|
|
4597
|
-
client_name:
|
|
4598
|
-
description:
|
|
4610
|
+
client_name: import_zod18.z.string().min(1).max(200).optional().meta({
|
|
4611
|
+
description: 'The name the client calls itself. Optional: RFC 7591 makes every metadata field optional, so a registration without one is recorded under a default name. It is **not** vouched for by Fleetless and must never be rendered as if it were: a self-registered client chooses this string, and one has called itself *"Fleetless Official Helper"*.'
|
|
4599
4612
|
}),
|
|
4600
|
-
token_endpoint_auth_method:
|
|
4613
|
+
token_endpoint_auth_method: import_zod18.z.enum(["none"]).optional().meta({
|
|
4601
4614
|
description: "`none`, RFC 7591's value for a public client, and the only value either server registers. Any other value is **refused rather than silently downgraded**: a client that believes it holds a secret and does not has a wrong mental model of its own security. There is no client secret to hold \u2014 mandatory PKCE (`S256`) is the defence."
|
|
4602
4615
|
}),
|
|
4603
|
-
grant_types:
|
|
4616
|
+
grant_types: import_zod18.z.array(import_zod18.z.enum(["authorization_code", "refresh_token"])).optional().meta({
|
|
4604
4617
|
description: "Accepted for conformance with RFC 7591 and then **ignored**. What comes back is what was actually granted, which \xA73.2.1 permits a server to substitute: `authorization_code` and nothing else, so a client that asks for `refresh_token` is registered and told plainly that it did not get one."
|
|
4605
4618
|
}),
|
|
4606
|
-
response_types:
|
|
4619
|
+
response_types: import_zod18.z.array(import_zod18.z.enum(["code"])).optional().meta({
|
|
4607
4620
|
description: "Accepted for conformance and then **ignored**; the response names `code`, which is the only response type OAuth 2.1 leaves, the implicit grant having been removed."
|
|
4608
4621
|
}),
|
|
4609
|
-
scope:
|
|
4622
|
+
scope: import_zod18.z.string().max(500).optional().meta({
|
|
4610
4623
|
description: "Accepted for conformance and then **ignored**. This authorization server issues no scopes at all, which is why the registration answer carries no `scope` field to echo one back in."
|
|
4611
4624
|
})
|
|
4612
4625
|
}).meta({
|
|
4613
4626
|
description: "What an MCP client sends to register itself, per RFC 7591. Unknown metadata is ignored rather than refused (\xA73.1), and the answer states what was actually granted rather than what was asked for (\xA73.2.1)."
|
|
4614
4627
|
});
|
|
4615
|
-
var dynamicClientRegistrationResponse =
|
|
4616
|
-
client_id:
|
|
4628
|
+
var dynamicClientRegistrationResponse = import_zod18.z.object({
|
|
4629
|
+
client_id: import_zod18.z.string().min(1).max(200).meta({
|
|
4617
4630
|
description: "The identifier this client sends at the authorize and token endpoints. Opaque, and not the app identifier."
|
|
4618
4631
|
}),
|
|
4619
|
-
client_name:
|
|
4632
|
+
client_name: import_zod18.z.string().min(1).max(200).meta({
|
|
4620
4633
|
description: "The name the client registered under, echoed back. Chosen by the client and not vouched for by Fleetless."
|
|
4621
4634
|
}),
|
|
4622
|
-
redirect_uris:
|
|
4635
|
+
redirect_uris: import_zod18.z.array(redirectUri).meta({
|
|
4623
4636
|
description: "The redirect URIs this registration was accepted for. A code is returned to one of these and nowhere else."
|
|
4624
4637
|
}),
|
|
4625
|
-
grant_types:
|
|
4638
|
+
grant_types: import_zod18.z.array(import_zod18.z.string()).meta({
|
|
4626
4639
|
description: 'The grants this client may use. Always exactly `["authorization_code"]` \u2014 a client that asked for `refresh_token` is registered and told here that it did not get one, which is the substitution RFC 7591 \xA73.2.1 permits.'
|
|
4627
4640
|
}),
|
|
4628
|
-
response_types:
|
|
4641
|
+
response_types: import_zod18.z.array(import_zod18.z.string()).meta({
|
|
4629
4642
|
description: "The response types this client may ask for: `code`."
|
|
4630
4643
|
}),
|
|
4631
|
-
token_endpoint_auth_method:
|
|
4644
|
+
token_endpoint_auth_method: import_zod18.z.literal("none").meta({
|
|
4632
4645
|
description: "`none` \u2014 this server registers public clients only, and PKCE rather than a secret is what protects the exchange."
|
|
4633
4646
|
}),
|
|
4634
|
-
client_id_issued_at:
|
|
4647
|
+
client_id_issued_at: import_zod18.z.number().int().nonnegative().meta({
|
|
4635
4648
|
description: "When the registration was created, in seconds since the epoch, per RFC 7591."
|
|
4636
4649
|
}),
|
|
4637
|
-
client_secret_expires_at:
|
|
4650
|
+
client_secret_expires_at: import_zod18.z.literal(0).meta({
|
|
4638
4651
|
description: "Always `0`, which is RFC 7591's way of saying the client secret never expires \u2014 there is none. The **registration** itself does expire: a self-registered client that never completes a flow is an unauthenticated write somebody left behind."
|
|
4639
4652
|
})
|
|
4640
4653
|
});
|
|
4641
|
-
var oauthTokenRequest =
|
|
4642
|
-
grant_type:
|
|
4654
|
+
var oauthTokenRequest = import_zod18.z.object({
|
|
4655
|
+
grant_type: import_zod18.z.literal("authorization_code").meta({
|
|
4643
4656
|
description: "Always `authorization_code`: this request exchanges the code from the authorize redirect for tokens. Any other value \u2014 `refresh_token` included \u2014 is `unsupported_grant_type`, refused before the code is looked up."
|
|
4644
4657
|
}),
|
|
4645
|
-
code:
|
|
4658
|
+
code: import_zod18.z.string().min(1).max(500).meta({
|
|
4646
4659
|
description: "The authorization code from the redirect. It may be exchanged once; a second presentation is `invalid_grant`, the same answer a fabricated code gets."
|
|
4647
4660
|
}),
|
|
4648
4661
|
redirect_uri: redirectUri.meta({
|
|
4649
4662
|
description: "The same redirect URI the authorize request used. It is compared, not merely recorded."
|
|
4650
4663
|
}),
|
|
4651
|
-
client_id:
|
|
4664
|
+
client_id: import_zod18.z.string().min(1).max(200).meta({
|
|
4652
4665
|
description: "The client making the exchange, as registered."
|
|
4653
4666
|
}),
|
|
4654
|
-
code_verifier:
|
|
4667
|
+
code_verifier: import_zod18.z.string().regex(/^[A-Za-z0-9\-._~]{43,128}$/, "code_verifier must be 43-128 unreserved characters (RFC 7636 \xA74.1)").meta({
|
|
4655
4668
|
description: "The PKCE verifier whose `S256` hash was sent as the challenge at the authorize step. Between `43` and `128` unreserved characters, per RFC 7636 \xA74.1 \u2014 it is compared rather than parsed, so a length nobody checks is a length an attacker chooses. PKCE is mandatory for every client under OAuth 2.1."
|
|
4656
4669
|
}),
|
|
4657
|
-
resource:
|
|
4658
|
-
description: "The resource the token is
|
|
4670
|
+
resource: import_zod18.z.url().optional().meta({
|
|
4671
|
+
description: "The resource the token is requested for, per RFC 8707. It must match the audience the code was authorized for, or the answer is `invalid_target`; omitted, the code's own audience stands. It becomes the token's `aud`, and a resource refuses a token whose audience names something else \u2014 which is what keeps a token minted for one app out of another app's endpoint."
|
|
4659
4672
|
})
|
|
4660
4673
|
}).meta({
|
|
4661
4674
|
description: "RFC 6749 \xA74.1.3's authorization-code exchange with PKCE, as either MCP authorization server reads it. Sent as `application/x-www-form-urlencoded`, per \xA74.1.3, though the server accepts a JSON body too."
|
|
4662
4675
|
});
|
|
4663
|
-
var oauthTokenResponse =
|
|
4664
|
-
access_token:
|
|
4676
|
+
var oauthTokenResponse = import_zod18.z.object({
|
|
4677
|
+
access_token: import_zod18.z.string().min(1).meta({
|
|
4665
4678
|
description: "The bearer token. It is the same token the client login mints \u2014 only the envelope differs, because an RFC-compliant client parses this one and knows nothing about Fleetless."
|
|
4666
4679
|
}),
|
|
4667
|
-
token_type:
|
|
4680
|
+
token_type: import_zod18.z.literal("Bearer").meta({
|
|
4668
4681
|
description: "`Bearer`. RFC 6749 \xA75.1 makes the value case-insensitive for a client reading it; this is the spelling this server emits."
|
|
4669
4682
|
}),
|
|
4670
|
-
expires_in:
|
|
4683
|
+
expires_in: import_zod18.z.number().int().positive().meta({
|
|
4671
4684
|
description: "How long the access token is valid, in **seconds**, per RFC 6749 \xA75.1. Not a timestamp, and not milliseconds."
|
|
4672
4685
|
}),
|
|
4673
|
-
refresh_token:
|
|
4686
|
+
refresh_token: import_zod18.z.string().min(1).optional().meta({
|
|
4674
4687
|
description: "The refresh token, when one was issued. It rotates on every use."
|
|
4675
4688
|
}),
|
|
4676
|
-
scope:
|
|
4689
|
+
scope: import_zod18.z.string().max(500).optional().meta({
|
|
4677
4690
|
description: "The scopes the issued token actually carries, space-separated."
|
|
4678
4691
|
})
|
|
4679
4692
|
});
|
|
4680
|
-
var authorizationServerMetadata =
|
|
4681
|
-
issuer:
|
|
4693
|
+
var authorizationServerMetadata = import_zod18.z.object({
|
|
4694
|
+
issuer: import_zod18.z.url().meta({
|
|
4682
4695
|
description: "The issuer identifier of this authorization server, per RFC 8414 \xA72. It is what a client checks a token's `iss` against."
|
|
4683
4696
|
}),
|
|
4684
|
-
authorization_endpoint:
|
|
4685
|
-
description: "
|
|
4697
|
+
authorization_endpoint: import_zod18.z.url().meta({
|
|
4698
|
+
description: "Where a client sends the user to authorize."
|
|
4686
4699
|
}),
|
|
4687
|
-
token_endpoint:
|
|
4700
|
+
token_endpoint: import_zod18.z.url().meta({
|
|
4688
4701
|
description: "The URL where a client exchanges an authorization code, or a refresh token, for tokens."
|
|
4689
4702
|
}),
|
|
4690
|
-
registration_endpoint:
|
|
4703
|
+
registration_endpoint: import_zod18.z.url().optional().meta({
|
|
4691
4704
|
description: "The URL where a client may register itself, per RFC 7591. Absent when the app does not accept dynamic clients."
|
|
4692
4705
|
}),
|
|
4693
|
-
response_types_supported:
|
|
4706
|
+
response_types_supported: import_zod18.z.array(import_zod18.z.literal("code")).meta({
|
|
4694
4707
|
description: "The response types this server offers: `code` only, the implicit grant being gone with OAuth 2.1."
|
|
4695
4708
|
}),
|
|
4696
|
-
grant_types_supported:
|
|
4709
|
+
grant_types_supported: import_zod18.z.array(import_zod18.z.enum(["authorization_code", "refresh_token"])).meta({
|
|
4697
4710
|
description: "The grants this server offers. OAuth 2.1 removes the implicit and password grants, so neither appears here."
|
|
4698
4711
|
}),
|
|
4699
|
-
code_challenge_methods_supported:
|
|
4712
|
+
code_challenge_methods_supported: import_zod18.z.array(codeChallengeMethod).meta({
|
|
4700
4713
|
description: "The PKCE challenge methods accepted: `S256` only. `plain` is not offered \u2014 a challenge equal to its verifier defends against nothing, and offering it would make a downgrade negotiable."
|
|
4701
4714
|
}),
|
|
4702
|
-
token_endpoint_auth_methods_supported:
|
|
4715
|
+
token_endpoint_auth_methods_supported: import_zod18.z.array(import_zod18.z.literal("none")).meta({
|
|
4703
4716
|
description: "How a client authenticates at the token endpoint: `none`, the public-client method, with PKCE protecting the exchange."
|
|
4704
4717
|
}),
|
|
4705
|
-
scopes_supported:
|
|
4718
|
+
scopes_supported: import_zod18.z.array(import_zod18.z.string()).optional().meta({
|
|
4706
4719
|
description: "The scopes this server knows about, where it publishes a list."
|
|
4707
4720
|
})
|
|
4708
4721
|
});
|
|
4709
|
-
var protectedResourceMetadata =
|
|
4710
|
-
resource:
|
|
4722
|
+
var protectedResourceMetadata = import_zod18.z.object({
|
|
4723
|
+
resource: import_zod18.z.url().meta({
|
|
4711
4724
|
description: "The resource identifier this document describes, per RFC 9728. A token whose audience names something else is rejected here rather than merely noted."
|
|
4712
4725
|
}),
|
|
4713
|
-
authorization_servers:
|
|
4726
|
+
authorization_servers: import_zod18.z.array(import_zod18.z.url()).min(1).meta({
|
|
4714
4727
|
description: "The authorization servers that may issue tokens for this resource. There is always at least one."
|
|
4715
4728
|
}),
|
|
4716
|
-
bearer_methods_supported:
|
|
4729
|
+
bearer_methods_supported: import_zod18.z.array(import_zod18.z.literal("header")).meta({
|
|
4717
4730
|
description: "How a token may be presented: in the `Authorization` header only, never in a query parameter or a form field."
|
|
4718
4731
|
}),
|
|
4719
|
-
scopes_supported:
|
|
4732
|
+
scopes_supported: import_zod18.z.array(import_zod18.z.string()).optional().meta({
|
|
4720
4733
|
description: "The scopes this resource understands, where it publishes a list."
|
|
4721
4734
|
})
|
|
4722
4735
|
});
|
|
4723
|
-
var oauthRedirectResponse =
|
|
4724
|
-
redirect_to:
|
|
4736
|
+
var oauthRedirectResponse = import_zod18.z.object({
|
|
4737
|
+
redirect_to: import_zod18.z.string().min(1).max(2e3)
|
|
4725
4738
|
});
|
|
4726
|
-
var oauthAuthorizeQuery =
|
|
4727
|
-
response_type:
|
|
4739
|
+
var oauthAuthorizeQuery = import_zod18.z.object({
|
|
4740
|
+
response_type: import_zod18.z.literal("code").meta({
|
|
4728
4741
|
description: "Always `code`. RFC 6749 \xA74.1.2.1 names `unsupported_response_type` for any other value, but `oauthErrorCode` has no such member \u2014 this server issues no other grant from this endpoint \u2014 so an unsupported value comes back on the callback as `invalid_request`."
|
|
4729
4742
|
}),
|
|
4730
|
-
client_id:
|
|
4743
|
+
client_id: import_zod18.z.string().min(1).meta({
|
|
4731
4744
|
description: "The OAuth client, self-registered or the one well-known central client \u2014 **not** the app identifier. Unknown, expired-dynamic and mismatched clients all collapse into the same `400 invalid_client`, answered without a redirect."
|
|
4732
4745
|
}),
|
|
4733
|
-
redirect_uri:
|
|
4746
|
+
redirect_uri: import_zod18.z.string().min(1).meta({
|
|
4734
4747
|
description: "One of the client's registered redirect URIs, compared **exactly** \u2014 string equality against the registered list, never a prefix or a host match. Both the shape (`redirectUri`) and the registration are checked, and a failure of either is a `400 invalid_request` with no redirect."
|
|
4735
4748
|
}),
|
|
4736
|
-
code_challenge:
|
|
4737
|
-
description: "The PKCE challenge; the verifier is presented at the token endpoint. Only non-emptiness is checked here \u2014 length and alphabet are not \u2014 since the verifier is what
|
|
4749
|
+
code_challenge: import_zod18.z.string().min(1).meta({
|
|
4750
|
+
description: "The PKCE challenge; the verifier is presented at the token endpoint. Only non-emptiness is checked here \u2014 length and alphabet are not \u2014 since the verifier is what has to match."
|
|
4738
4751
|
}),
|
|
4739
|
-
code_challenge_method:
|
|
4752
|
+
code_challenge_method: import_zod18.z.literal("S256").meta({
|
|
4740
4753
|
description: "Only `S256`. `plain` is refused: a challenge equal to its verifier defends against nothing."
|
|
4741
4754
|
}),
|
|
4742
|
-
state:
|
|
4755
|
+
state: import_zod18.z.string().optional().meta({
|
|
4743
4756
|
description: "Returned unchanged on the callback, and on the error redirect too, so a client can bind either answer to its own request."
|
|
4744
4757
|
}),
|
|
4745
|
-
resource:
|
|
4758
|
+
resource: import_zod18.z.string().optional().meta({
|
|
4746
4759
|
description: "RFC 8707 resource indicator: the API origin or the MCP endpoint the token is for. Checked against the resources this server issues tokens for **on behalf of this client's app**; a mismatch is `invalid_target` on the callback."
|
|
4747
4760
|
})
|
|
4748
4761
|
// **No `scope`, because this authorization server issues none.** The field
|
|
@@ -4755,7 +4768,7 @@ var oauthAuthorizeQuery = import_zod17.z.object({
|
|
|
4755
4768
|
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
4769
|
});
|
|
4757
4770
|
|
|
4758
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
4771
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/routes.js
|
|
4759
4772
|
var MCP_APP = MCP_APP_PATHS(":appIdentifier");
|
|
4760
4773
|
var APP_IDENTIFIER = {
|
|
4761
4774
|
name: "appIdentifier",
|
|
@@ -4960,6 +4973,24 @@ var ROUTES = [
|
|
|
4960
4973
|
transport: "http",
|
|
4961
4974
|
notes: 'HTML. An unknown, spent or expired token renders one "link no longer valid" page at `410` \u2014 they are one refusal on the wire already, and splitting them here would tell a stranger which tokens ever existed. No rate limiter: the GET changes nothing, and the POST it leads to is limited per IP.'
|
|
4962
4975
|
},
|
|
4976
|
+
{
|
|
4977
|
+
method: "GET",
|
|
4978
|
+
path: "/favicon.svg",
|
|
4979
|
+
section: "client-auth",
|
|
4980
|
+
summary: "Serves the Fleetless icon for the auth portal's and the MCP welcome page's browser tab.",
|
|
4981
|
+
audience: "internal",
|
|
4982
|
+
auth: "none",
|
|
4983
|
+
rateLimited: false,
|
|
4984
|
+
ownerTier: false,
|
|
4985
|
+
status: 200,
|
|
4986
|
+
params: [],
|
|
4987
|
+
query: null,
|
|
4988
|
+
request: null,
|
|
4989
|
+
response: null,
|
|
4990
|
+
errors: [],
|
|
4991
|
+
transport: "http",
|
|
4992
|
+
notes: "An SVG, not JSON. Those pages carry a Content-Security-Policy that admits no `data:` image, so the icon is a file on their own origin \u2014 the one source `img-src 'self'` names. Cached for a day: the bytes change when the brand does, not per deploy."
|
|
4993
|
+
},
|
|
4963
4994
|
{
|
|
4964
4995
|
method: "POST",
|
|
4965
4996
|
path: "/api/auth/password/reset/confirm",
|
|
@@ -5424,7 +5455,7 @@ var ROUTES = [
|
|
|
5424
5455
|
params: [
|
|
5425
5456
|
{ name: "id", description: "The app's uuid, as returned by `POST /api/apps` or listed by `GET /api/apps`." },
|
|
5426
5457
|
{ name: "userId", description: "The app user's uuid, from `GET /api/apps/:id/users`; a user of another app answers `404`." },
|
|
5427
|
-
{ name: "clientId", description: "The MCP client, as `GET /api/apps/:id/users/:userId/mcp-grants` reports its `client_id`. Not a uuid \u2014
|
|
5458
|
+
{ name: "clientId", description: "The MCP client, as `GET /api/apps/:id/users/:userId/mcp-grants` reports its `client_id`. Not a uuid \u2014 the identifier dynamic registration issued." }
|
|
5428
5459
|
],
|
|
5429
5460
|
query: null,
|
|
5430
5461
|
request: null,
|
|
@@ -6821,7 +6852,7 @@ var ROUTES = [
|
|
|
6821
6852
|
rateLimited: false,
|
|
6822
6853
|
ownerTier: false,
|
|
6823
6854
|
status: 204,
|
|
6824
|
-
params: [{ name: "clientId", description: "The MCP client, as `GET /api/client/mcp/grants` reports its `client_id`. Not a uuid \u2014
|
|
6855
|
+
params: [{ name: "clientId", description: "The MCP client, as `GET /api/client/mcp/grants` reports its `client_id`. Not a uuid \u2014 the identifier dynamic registration issued." }],
|
|
6825
6856
|
query: null,
|
|
6826
6857
|
request: null,
|
|
6827
6858
|
response: null,
|
|
@@ -7012,6 +7043,43 @@ var ROUTES = [
|
|
|
7012
7043
|
transport: "http",
|
|
7013
7044
|
notes: "For a client caller the grant check runs **before** any existence lookup, with no extra query on either path to time: a denied slug and a nonexistent one must be one answer. That is why an ungranted slug is `403 forbidden` while a granted-but-unconfigured one is `404 unknown_datapoint` and a configured one with no sample yet is `404 no_data` \u2014 three facts a caller who is entitled to them needs told apart. The plane built-ins (`bridge_state`, `robot_details`) answer here too, without appearing in any document."
|
|
7014
7045
|
},
|
|
7046
|
+
/* ------------------------------------------ discovery: the REST twins of the two MCP tools a session starts from */
|
|
7047
|
+
{
|
|
7048
|
+
method: "GET",
|
|
7049
|
+
path: "/api/client/robots",
|
|
7050
|
+
section: "robots",
|
|
7051
|
+
summary: "Lists the robots the caller reaches, with bridge state and the published configuration version.",
|
|
7052
|
+
audience: "client",
|
|
7053
|
+
auth: "developer_or_client",
|
|
7054
|
+
rateLimited: false,
|
|
7055
|
+
ownerTier: false,
|
|
7056
|
+
status: 200,
|
|
7057
|
+
params: [],
|
|
7058
|
+
query: null,
|
|
7059
|
+
request: null,
|
|
7060
|
+
response: clientRobotListResponse,
|
|
7061
|
+
errors: [...CLIENT_GUARD],
|
|
7062
|
+
transport: "http",
|
|
7063
|
+
notes: "**The REST twin of the MCP tool `robots_list`**, and the one robot question no robot-scoped route can answer: which robots may I name at all. An app user sees the robots their app attaches on which their role grants at least one slug or capability; a server key sees every robot its app attaches; a developer bearer sees the organisation's robots. Name order, id as the tiebreak. A robot on which the role grants nothing is absent rather than listed empty \u2014 the same answer `robots_list` gives, for the same reason: reach is a grant, not an attachment. Under `/api/client/` because it names no robot; every robot-scoped read stays under `/api/robots/:id/\u2026`."
|
|
7064
|
+
},
|
|
7065
|
+
{
|
|
7066
|
+
method: "GET",
|
|
7067
|
+
path: "/api/robots/:id/datasheet",
|
|
7068
|
+
section: "robots",
|
|
7069
|
+
summary: "Describes everything the caller's role lets them do on one robot, with parameter schemas.",
|
|
7070
|
+
audience: "client",
|
|
7071
|
+
auth: "developer_or_client",
|
|
7072
|
+
rateLimited: false,
|
|
7073
|
+
ownerTier: false,
|
|
7074
|
+
status: 200,
|
|
7075
|
+
params: [{ name: "id", description: "The robot's uuid, as `GET /api/client/robots` lists it." }],
|
|
7076
|
+
query: null,
|
|
7077
|
+
request: null,
|
|
7078
|
+
response: mcpRobotDatasheet,
|
|
7079
|
+
errors: [...CLIENT_GUARD, "invalid_uuid", "not_found"],
|
|
7080
|
+
transport: "http",
|
|
7081
|
+
notes: "**The REST twin of the MCP tool `robot_describe`**: one answer per robot \u2014 every datapoint, action, service, publisher and camera the role grants, each with its `input_schema` where it takes parameters, plus the two capabilities that gate whole features, `action_history` and `assets`. A robot with nothing published answers an empty `exposures` list, never a refusal. A robot the caller does not reach \u2014 not attached to their app, or attached with a role that grants nothing on it \u2014 answers `404` exactly as one that does not exist. The app-user datapoint and camera listings under this prefix stay; this is the one read that also names actions, services, publishers and capabilities, which is what an app needs before it can draw a screen."
|
|
7082
|
+
},
|
|
7015
7083
|
/* ------------------------------------------------- config (draft/publish) */
|
|
7016
7084
|
{
|
|
7017
7085
|
method: "GET",
|
|
@@ -7334,7 +7402,7 @@ var ROUTES = [
|
|
|
7334
7402
|
status: 202,
|
|
7335
7403
|
params: [
|
|
7336
7404
|
{ name: "id", description: "The robot's uuid; an end user reaches it through an app that attaches it." },
|
|
7337
|
-
{ name: "slug", description: "The action or service slug from the published configuration
|
|
7405
|
+
{ name: "slug", description: "The action or service slug from the published configuration \u2014 the cloud already knows which kind." }
|
|
7338
7406
|
],
|
|
7339
7407
|
query: null,
|
|
7340
7408
|
request: invokeRequest,
|
|
@@ -8332,21 +8400,21 @@ function assertValidJobId(jobId) {
|
|
|
8332
8400
|
if (jobId === void 0 || jobId === null || typeof jobId === "string") return;
|
|
8333
8401
|
throw new FleetlessError(
|
|
8334
8402
|
"invalid_option",
|
|
8335
|
-
`cancel()'s third argument must be a job id (string), null, or omitted \u2014 got ${typeof jobId === "object" ? "an object" : typeof jobId}. If you are passing an options object (e.g. {timeoutMs}) as the third argument, note the signature changed in this release: cancel(robotId, slug) is unchanged, but a third positional argument is now the job id to cancel and options moved to a fourth argument \u2014 cancel(robotId, slug, jobId, options). See the
|
|
8403
|
+
`cancel()'s third argument must be a job id (string), null, or omitted \u2014 got ${typeof jobId === "object" ? "an object" : typeof jobId}. If you are passing an options object (e.g. {timeoutMs}) as the third argument, note the signature changed in this release: cancel(robotId, slug) is unchanged, but a third positional argument is now the job id to cancel and options moved to a fourth argument \u2014 cancel(robotId, slug, jobId, options). See the Actions section of the SDK reference at https://docs.fleetless.dev/reference/sdk/`
|
|
8336
8404
|
);
|
|
8337
8405
|
}
|
|
8338
8406
|
function createRealtimeCommandTransport(channel) {
|
|
8339
8407
|
return {
|
|
8340
|
-
// Declared `async` deliberately, unlike `cancel`/`publish` below: it
|
|
8408
|
+
// Declared `async` deliberately, unlike `cancel`/`publish` below: it's
|
|
8341
8409
|
// the only one of the three that can refuse *before* sending anything
|
|
8342
|
-
// (`resolveLocalWaitMs`'s `invalid_option`), and every caller of
|
|
8343
|
-
//
|
|
8344
|
-
//
|
|
8345
|
-
//
|
|
8346
|
-
//
|
|
8347
|
-
//
|
|
8348
|
-
//
|
|
8349
|
-
//
|
|
8410
|
+
// (`resolveLocalWaitMs`'s `invalid_option`), and every caller of this
|
|
8411
|
+
// interface — starting with this file's own `sendCommand` callers —
|
|
8412
|
+
// assumes `CommandTransport.invoke` always returns a promise, never
|
|
8413
|
+
// throws synchronously. Without `async` here, a synchronous throw from
|
|
8414
|
+
// `resolveLocalWaitMs` would escape as a thrown exception instead of a
|
|
8415
|
+
// rejection, breaking that assumption for a caller that isn't itself
|
|
8416
|
+
// inside an `async` function (e.g. a test calling this transport
|
|
8417
|
+
// directly).
|
|
8350
8418
|
async invoke(robotId, slug2, params, options) {
|
|
8351
8419
|
const timeoutMs = resolveLocalWaitMs(options ?? {}, DEFAULT_COMMAND_TIMEOUT_MS);
|
|
8352
8420
|
const frame = {
|
|
@@ -8501,11 +8569,30 @@ function createJobSubscriptions(channel, slugSubscriptions) {
|
|
|
8501
8569
|
}
|
|
8502
8570
|
|
|
8503
8571
|
// src/jobs.ts
|
|
8572
|
+
function historyQueryString2(options) {
|
|
8573
|
+
const params = new URLSearchParams();
|
|
8574
|
+
if (options.slug !== void 0) params.set("slug", options.slug);
|
|
8575
|
+
if (options.state !== void 0) params.set("state", options.state);
|
|
8576
|
+
if (options.kind !== void 0) params.set("kind", options.kind);
|
|
8577
|
+
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
8578
|
+
if (options.beforeSeq !== void 0) params.set("before_seq", String(options.beforeSeq));
|
|
8579
|
+
if (options.fromMs !== void 0) params.set("from_ms", String(options.fromMs));
|
|
8580
|
+
if (options.toMs !== void 0) params.set("to_ms", String(options.toMs));
|
|
8581
|
+
return params.toString();
|
|
8582
|
+
}
|
|
8504
8583
|
function createJobsApi(http) {
|
|
8505
8584
|
return {
|
|
8506
8585
|
async list(robotId) {
|
|
8507
8586
|
const response = await http.request(`/api/robots/${pathSegment(robotId)}/jobs`, {});
|
|
8508
8587
|
return response.jobs;
|
|
8588
|
+
},
|
|
8589
|
+
async history(robotId, options = {}) {
|
|
8590
|
+
const query = historyQueryString2(options);
|
|
8591
|
+
const page = await http.request(
|
|
8592
|
+
`/api/robots/${pathSegment(robotId)}/jobs/history${query ? `?${query}` : ""}`,
|
|
8593
|
+
{}
|
|
8594
|
+
);
|
|
8595
|
+
return page;
|
|
8509
8596
|
}
|
|
8510
8597
|
};
|
|
8511
8598
|
}
|
|
@@ -8546,12 +8633,11 @@ var RealtimeChannel = class {
|
|
|
8546
8633
|
}
|
|
8547
8634
|
/**
|
|
8548
8635
|
* Increments on every successful authentication (first connect and every
|
|
8549
|
-
* reconnect). A command sent on one
|
|
8550
|
-
*
|
|
8551
|
-
*
|
|
8552
|
-
*
|
|
8553
|
-
*
|
|
8554
|
-
* than after a timeout elapses.
|
|
8636
|
+
* reconnect). A command sent on one socket can only be answered on that
|
|
8637
|
+
* socket — comparing the epoch at send time against the current one is
|
|
8638
|
+
* how a caller (see `commands.ts`) tells "still waiting on the connection
|
|
8639
|
+
* it was sent over" from "that connection is gone and a new one has taken
|
|
8640
|
+
* its place", the moment it happens rather than after a timeout elapses.
|
|
8555
8641
|
*/
|
|
8556
8642
|
get connectionEpoch() {
|
|
8557
8643
|
return this.#connectionEpoch;
|
|
@@ -8733,6 +8819,20 @@ var RealtimeChannel = class {
|
|
|
8733
8819
|
}
|
|
8734
8820
|
};
|
|
8735
8821
|
|
|
8822
|
+
// src/robots.ts
|
|
8823
|
+
function createRobotsApi(http) {
|
|
8824
|
+
return {
|
|
8825
|
+
async list() {
|
|
8826
|
+
const response = await http.request("/api/client/robots", {});
|
|
8827
|
+
return response.robots;
|
|
8828
|
+
},
|
|
8829
|
+
async describe(robotId) {
|
|
8830
|
+
const sheet = await http.request(`/api/robots/${pathSegment(robotId)}/datasheet`, {});
|
|
8831
|
+
return sheet;
|
|
8832
|
+
}
|
|
8833
|
+
};
|
|
8834
|
+
}
|
|
8835
|
+
|
|
8736
8836
|
// src/services.ts
|
|
8737
8837
|
var DEFAULT_RESULT_TIMEOUT_MS = 3e4;
|
|
8738
8838
|
function isTerminal(state) {
|
|
@@ -8852,7 +8952,7 @@ function createSlugSubscriptions(channel) {
|
|
|
8852
8952
|
// src/token-store.ts
|
|
8853
8953
|
var InMemoryTokenStore = class {
|
|
8854
8954
|
#session = null;
|
|
8855
|
-
/**
|
|
8955
|
+
/** Nothing is loaded from anywhere — a client built with it starts logged out. */
|
|
8856
8956
|
constructor() {
|
|
8857
8957
|
}
|
|
8858
8958
|
/** Returns the session held in memory, or `null` if there is none. */
|
|
@@ -8907,6 +9007,7 @@ function createClient(options) {
|
|
|
8907
9007
|
const cameras = createCamerasApi(http);
|
|
8908
9008
|
const jobs = createJobsApi(http);
|
|
8909
9009
|
const assets = createAssetsApi(http);
|
|
9010
|
+
const robots = createRobotsApi(http);
|
|
8910
9011
|
if (options.serverKey === void 0) {
|
|
8911
9012
|
const baseLogout = auth.logout.bind(auth);
|
|
8912
9013
|
auth = {
|
|
@@ -8927,6 +9028,7 @@ function createClient(options) {
|
|
|
8927
9028
|
cameras,
|
|
8928
9029
|
jobs,
|
|
8929
9030
|
assets,
|
|
9031
|
+
robots,
|
|
8930
9032
|
close() {
|
|
8931
9033
|
channel.close();
|
|
8932
9034
|
}
|