@fleetless/sdk 3.0.3 → 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 +11 -0
- package/README.md +2 -0
- package/dist/index.cjs +294 -191
- package/dist/index.d.cts +260 -10
- package/dist/index.d.ts +260 -10
- package/dist/index.js +294 -191
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -411,7 +411,7 @@ function createAssetsApi(http) {
|
|
|
411
411
|
};
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
414
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/common.js
|
|
415
415
|
import { z } from "zod";
|
|
416
416
|
var SLUG_RULE = "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore \u2014 `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused.";
|
|
417
417
|
var slug = z.string().min(2).max(63).regex(/^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/, SLUG_RULE);
|
|
@@ -435,7 +435,7 @@ var applyError = z.object({
|
|
|
435
435
|
details: z.record(z.string(), z.unknown()).optional()
|
|
436
436
|
});
|
|
437
437
|
|
|
438
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
438
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/mcp.js
|
|
439
439
|
import { z as z2 } from "zod";
|
|
440
440
|
function mcpAppEndpointPath(appIdentifier2) {
|
|
441
441
|
return `/mcp/${appIdentifier2}`;
|
|
@@ -484,17 +484,17 @@ var mcpRolePreviewResponse = z2.object({
|
|
|
484
484
|
});
|
|
485
485
|
var MCP_ASSET_LINK_TTL_MS = 15 * 60 * 1e3;
|
|
486
486
|
|
|
487
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
487
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/protocol.js
|
|
488
488
|
import { z as z8 } from "zod";
|
|
489
489
|
|
|
490
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
490
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/assets.js
|
|
491
491
|
import { z as z3 } from "zod";
|
|
492
492
|
var assetKind = z3.enum(["urdf", "mesh", "texture", "other"]);
|
|
493
493
|
var asset = z3.object({
|
|
494
494
|
id: z3.uuid().meta({ description: "The asset's id in the store." }),
|
|
495
495
|
robot_id: z3.uuid().meta({ description: "The robot this asset belongs to." }),
|
|
496
496
|
kind: assetKind.meta({
|
|
497
|
-
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
|
|
497
|
+
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."
|
|
498
498
|
}),
|
|
499
499
|
/**
|
|
500
500
|
* What the robot called it — for a mesh, the `package://` URI the URDF
|
|
@@ -529,7 +529,7 @@ var asset = z3.object({
|
|
|
529
529
|
* rules, one enforced and one only documented, is how a traversal gets in.
|
|
530
530
|
*/
|
|
531
531
|
name: z3.string().min(1).max(500).meta({
|
|
532
|
-
description: "What the robot called it \u2014 for a mesh, the `package://` URI the URDF references, verbatim,
|
|
532
|
+
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."
|
|
533
533
|
}),
|
|
534
534
|
media_type: z3.string().min(1).max(120).meta({
|
|
535
535
|
description: "The media type of the stored bytes, as the producer reported it."
|
|
@@ -546,7 +546,7 @@ var asset = z3.object({
|
|
|
546
546
|
* predictable failure of a store that hides it.
|
|
547
547
|
*/
|
|
548
548
|
sha256: z3.string().regex(/^[a-f0-9]{64}$/).meta({
|
|
549
|
-
description: 'The content hash, lowercase hex
|
|
549
|
+
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.'
|
|
550
550
|
}),
|
|
551
551
|
created_at: z3.iso.datetime().meta({
|
|
552
552
|
description: "When the asset was first stored, as an ISO 8601 timestamp."
|
|
@@ -574,18 +574,18 @@ var urdfCompleteness = z3.object({
|
|
|
574
574
|
*/
|
|
575
575
|
missing: z3.array(z3.object({
|
|
576
576
|
uri: z3.string().min(1).max(500).meta({
|
|
577
|
-
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.
|
|
577
|
+
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."
|
|
578
578
|
}),
|
|
579
579
|
element: z3.enum(["mesh", "texture"]).meta({
|
|
580
580
|
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."
|
|
581
581
|
})
|
|
582
582
|
})).meta({
|
|
583
|
-
description: "The references nothing in the store answers, each with the element that asked for it. A bare count
|
|
583
|
+
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."
|
|
584
584
|
})
|
|
585
585
|
});
|
|
586
586
|
var assetSyncRequest = z3.object({
|
|
587
587
|
source: z3.enum(["bridge"]).meta({
|
|
588
|
-
description: "Where the bytes come from. `bridge` is the only value today: the connected bridge reads them from the robot's own workspace.
|
|
588
|
+
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."
|
|
589
589
|
})
|
|
590
590
|
}).strict();
|
|
591
591
|
var assetSyncResponse = z3.object({
|
|
@@ -599,7 +599,7 @@ var assetTooLargeDetails = z3.object({
|
|
|
599
599
|
description: "The upload ceiling, in bytes."
|
|
600
600
|
}),
|
|
601
601
|
size_bytes: z3.number().int().positive().meta({
|
|
602
|
-
description: 'How large the refused file
|
|
602
|
+
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.'
|
|
603
603
|
})
|
|
604
604
|
});
|
|
605
605
|
var assetFailureKind = z3.enum(["unresolvable", "upload_failed", "refused", "too_large"]);
|
|
@@ -699,8 +699,6 @@ var assetListResponse = z3.object({
|
|
|
699
699
|
description: "Every asset stored for this robot: the URDF, the meshes it references, and the textures those paint with."
|
|
700
700
|
}),
|
|
701
701
|
/**
|
|
702
|
-
* The sync running right now, or `null`.
|
|
703
|
-
*
|
|
704
702
|
* **This field exists for the reload case.** A client that holds the
|
|
705
703
|
* `sync_id` only in memory loses its progress display on a refresh, and the
|
|
706
704
|
* state is still there server-side under `GET .../assets/sync/<id>` —
|
|
@@ -731,7 +729,7 @@ var assetListResponse = z3.object({
|
|
|
731
729
|
* crash, and no amount of polling shortens it.
|
|
732
730
|
*/
|
|
733
731
|
urdf_available: z3.boolean().nullable().meta({
|
|
734
|
-
description: 'What the connected bridge says it *could* transfer
|
|
732
|
+
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.'
|
|
735
733
|
})
|
|
736
734
|
});
|
|
737
735
|
var missingAssetQuery = z3.object({
|
|
@@ -745,10 +743,10 @@ var assetSyncBusyDetails = z3.object({
|
|
|
745
743
|
started_at_ms: z3.number().int().nonnegative()
|
|
746
744
|
});
|
|
747
745
|
|
|
748
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
746
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/config.js
|
|
749
747
|
import { z as z5 } from "zod";
|
|
750
748
|
|
|
751
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
749
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/alerts.js
|
|
752
750
|
import { z as z4 } from "zod";
|
|
753
751
|
var alertRowCondition = z4.discriminatedUnion("kind", [
|
|
754
752
|
z4.strictObject({
|
|
@@ -818,7 +816,7 @@ var putDatapointDisplayRequest = z4.object({
|
|
|
818
816
|
y_max: z4.number().finite().nullable()
|
|
819
817
|
}).strict();
|
|
820
818
|
|
|
821
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
819
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/config.js
|
|
822
820
|
var RTSP_URL_RULE = "The URL has to begin with `rtsp://` or `rtsps://` \u2014 `rtsp://cam-1.plant.local/stream1`. No other scheme is accepted: the bridge opens this with a library that would equally honour `file:`.";
|
|
823
821
|
var MJPEG_URL_RULE = "The URL has to begin with `http://` or `https://` \u2014 `http://cam-1.plant.local/video.mjpg`. No other scheme is accepted: the bridge opens this with a library that would equally serve `file:`.";
|
|
824
822
|
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.";
|
|
@@ -1210,7 +1208,7 @@ var datapointConfig = strictObject({
|
|
|
1210
1208
|
examples: [2, 0.5]
|
|
1211
1209
|
}).optional(),
|
|
1212
1210
|
description: serviceDescription.meta({
|
|
1213
|
-
description: "Prose about what this value is, for whoever meets it in the console
|
|
1211
|
+
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.",
|
|
1214
1212
|
/**
|
|
1215
1213
|
* The sentence both datapoint snippets already place here, verbatim. Its
|
|
1216
1214
|
* four siblings — an action's, a service's, a publisher's, a camera's —
|
|
@@ -1865,7 +1863,7 @@ var configState = z5.object({
|
|
|
1865
1863
|
applied_errors: z5.array(applyError).nullable()
|
|
1866
1864
|
});
|
|
1867
1865
|
|
|
1868
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
1866
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/introspection.js
|
|
1869
1867
|
import { z as z6 } from "zod";
|
|
1870
1868
|
var rosGraphEntry = z6.object({
|
|
1871
1869
|
name: rosName,
|
|
@@ -1904,22 +1902,22 @@ var typeDefinition = z6.discriminatedUnion("kind", [
|
|
|
1904
1902
|
})
|
|
1905
1903
|
]);
|
|
1906
1904
|
|
|
1907
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
1905
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/jobs.js
|
|
1908
1906
|
import { z as z7 } from "zod";
|
|
1909
1907
|
var jobState = z7.enum(["running", "succeeded", "failed", "cancelled", "lost"]);
|
|
1910
1908
|
var job = z7.object({
|
|
1911
1909
|
id: z7.uuid().meta({
|
|
1912
|
-
description: "The job's id, minted by the cloud when the invocation is accepted. Informative
|
|
1910
|
+
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."
|
|
1913
1911
|
}),
|
|
1914
1912
|
robot_id: z7.uuid().meta({ description: "The robot this job is running on." }),
|
|
1915
1913
|
slug: slug.meta({
|
|
1916
1914
|
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."
|
|
1917
1915
|
}),
|
|
1918
1916
|
state: jobState.meta({
|
|
1919
|
-
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
|
|
1917
|
+
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."
|
|
1920
1918
|
}),
|
|
1921
1919
|
started_at: z7.iso.datetime().meta({
|
|
1922
|
-
description: "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge
|
|
1920
|
+
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."
|
|
1923
1921
|
}),
|
|
1924
1922
|
updated_at: z7.iso.datetime().meta({
|
|
1925
1923
|
description: "When this job last changed, as an ISO 8601 timestamp."
|
|
@@ -2002,7 +2000,7 @@ var jobQueueFullDetails = z7.object({
|
|
|
2002
2000
|
var JOB_RUN_PAGE_MAX = 200;
|
|
2003
2001
|
var jobActor = z7.object({
|
|
2004
2002
|
kind: z7.enum(["developer", "end_user", "app_user", "server_key"]).meta({
|
|
2005
|
-
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
|
|
2003
|
+
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."
|
|
2006
2004
|
}),
|
|
2007
2005
|
id: z7.uuid().meta({
|
|
2008
2006
|
description: "The id of the Fleetless user, app user or server key that invoked the run."
|
|
@@ -2019,7 +2017,7 @@ var jobActor = z7.object({
|
|
|
2019
2017
|
var jobRunKind = z7.enum(["action", "service"]);
|
|
2020
2018
|
var jobRun = z7.object({
|
|
2021
2019
|
id: z7.uuid().meta({
|
|
2022
|
-
description: "The run's id
|
|
2020
|
+
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."
|
|
2023
2021
|
}),
|
|
2024
2022
|
robot_id: z7.uuid().meta({ description: "The robot the run happened on." }),
|
|
2025
2023
|
slug: slug.meta({
|
|
@@ -2122,7 +2120,7 @@ var jobRunSummary = z7.object({
|
|
|
2122
2120
|
since_ms: z7.number().int().nonnegative()
|
|
2123
2121
|
});
|
|
2124
2122
|
|
|
2125
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
2123
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/protocol.js
|
|
2126
2124
|
var MAX_PATIENCE_MS = 12e4;
|
|
2127
2125
|
var MIN_PATIENCE_MS = 1e3;
|
|
2128
2126
|
var activeJob = z8.object({
|
|
@@ -2138,22 +2136,21 @@ var bridgeHello = z8.object({
|
|
|
2138
2136
|
/**
|
|
2139
2137
|
* Every job this bridge still knows about, right now.
|
|
2140
2138
|
*
|
|
2141
|
-
* A reconnect and a restart look **identical** on the wire
|
|
2142
|
-
*
|
|
2143
|
-
*
|
|
2144
|
-
*
|
|
2145
|
-
*
|
|
2146
|
-
*
|
|
2147
|
-
* `lost`.
|
|
2139
|
+
* A reconnect and a restart look **identical** on the wire — same token,
|
|
2140
|
+
* same version, same frame — but must end differently: after a dropped
|
|
2141
|
+
* connection the running jobs are still running, after a restart their
|
|
2142
|
+
* results are gone forever. Enumerating what the bridge still has settles
|
|
2143
|
+
* it without either side guessing: the cloud marks every job it believed
|
|
2144
|
+
* running that is *not* named here as `lost`.
|
|
2148
2145
|
*
|
|
2149
|
-
*
|
|
2150
|
-
*
|
|
2151
|
-
*
|
|
2152
|
-
*
|
|
2146
|
+
* Deliberately needs no persistence at the bridge: a live process lists its
|
|
2147
|
+
* live jobs, a process that just started lists none — exactly the truth
|
|
2148
|
+
* the cloud needs. A breadcrumb file would only add a window in which the
|
|
2149
|
+
* crash beat the write.
|
|
2153
2150
|
*
|
|
2154
|
-
* Defaulted, so a bridge that sends no such field still parses;
|
|
2155
|
-
*
|
|
2156
|
-
*
|
|
2151
|
+
* Defaulted, so a bridge that sends no such field still parses; no jobs
|
|
2152
|
+
* and no report both mean the same thing to the cloud: nothing to keep
|
|
2153
|
+
* alive.
|
|
2157
2154
|
*/
|
|
2158
2155
|
active_jobs: z8.array(activeJob).max(500).default([])
|
|
2159
2156
|
});
|
|
@@ -2521,11 +2518,11 @@ var bridgeCameraState = z8.object({
|
|
|
2521
2518
|
request_id: z8.string().min(1).max(64).nullable()
|
|
2522
2519
|
});
|
|
2523
2520
|
|
|
2524
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
2521
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/config-issues.js
|
|
2525
2522
|
var EXPOSURE_SECTIONS = ["datapoints", "actions", "services", "publishers", "cameras"];
|
|
2526
2523
|
var EXPOSURE_SECTION_NAMES = new Set(EXPOSURE_SECTIONS);
|
|
2527
2524
|
|
|
2528
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
2525
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/rest.js
|
|
2529
2526
|
import { z as z9 } from "zod";
|
|
2530
2527
|
var robot = z9.object({
|
|
2531
2528
|
id: z9.uuid().meta({
|
|
@@ -2540,7 +2537,7 @@ var robot = z9.object({
|
|
|
2540
2537
|
});
|
|
2541
2538
|
var patchRobotResponse = z9.object({
|
|
2542
2539
|
robot: robot.meta({
|
|
2543
|
-
description: "The robot as it now stands, after the patch
|
|
2540
|
+
description: "The robot as it now stands, after the patch. The whole resource comes back, not only the changed fields."
|
|
2544
2541
|
})
|
|
2545
2542
|
});
|
|
2546
2543
|
var createRobotRequest = z9.object({
|
|
@@ -2570,10 +2567,10 @@ var robotListResponse = z9.object({
|
|
|
2570
2567
|
var datapointValue = z9.object({
|
|
2571
2568
|
slug: slug.meta({ description: "The datapoint this value belongs to." }),
|
|
2572
2569
|
value: z9.unknown().meta({
|
|
2573
|
-
description: "The value
|
|
2570
|
+
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."
|
|
2574
2571
|
}),
|
|
2575
2572
|
timestamp_ms: z9.number().int().nonnegative().meta({
|
|
2576
|
-
description: "When the value was captured, as a unix timestamp in milliseconds.
|
|
2573
|
+
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."
|
|
2577
2574
|
})
|
|
2578
2575
|
});
|
|
2579
2576
|
var robotDetailResponse = z9.object({
|
|
@@ -2715,7 +2712,7 @@ var invokeResponse = z9.object({
|
|
|
2715
2712
|
});
|
|
2716
2713
|
var serviceCallResponse = z9.object({
|
|
2717
2714
|
result: z9.unknown().meta({
|
|
2718
|
-
description: "What the service returned, shaped by the ROS service
|
|
2715
|
+
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."
|
|
2719
2716
|
})
|
|
2720
2717
|
});
|
|
2721
2718
|
var invokeOrServiceResponse = z9.union([invokeResponse, serviceCallResponse]);
|
|
@@ -3285,13 +3282,13 @@ var slugUsageResponse = z9.object({
|
|
|
3285
3282
|
alert_count: z9.number().int().nonnegative()
|
|
3286
3283
|
});
|
|
3287
3284
|
|
|
3288
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3285
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/realtime.js
|
|
3289
3286
|
import { z as z14 } from "zod";
|
|
3290
3287
|
|
|
3291
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3288
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/client-auth.js
|
|
3292
3289
|
import { z as z13 } from "zod";
|
|
3293
3290
|
|
|
3294
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3291
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/apps.js
|
|
3295
3292
|
import { z as z10 } from "zod";
|
|
3296
3293
|
var appIdentifier = slug;
|
|
3297
3294
|
var app = z10.object({
|
|
@@ -3305,7 +3302,7 @@ var app = z10.object({
|
|
|
3305
3302
|
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`."
|
|
3306
3303
|
}),
|
|
3307
3304
|
identifier: appIdentifier.meta({
|
|
3308
|
-
description: "The stable handle a client sends at login, lowercase and underscore-separated. **Globally unique, not per organisation** \u2014 `clientLoginRequest` carries no org context
|
|
3305
|
+
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`."
|
|
3309
3306
|
}),
|
|
3310
3307
|
/** Robots are referenced individually; tags never grant rights. */
|
|
3311
3308
|
robot_ids: z10.array(z10.uuid()).meta({
|
|
@@ -3323,7 +3320,7 @@ var app = z10.object({
|
|
|
3323
3320
|
* (`createAppUserRequest`, `createAppInvitationRequest`) rather than
|
|
3324
3321
|
* pre-filling a form.
|
|
3325
3322
|
*
|
|
3326
|
-
*
|
|
3323
|
+
* Worth saying: it changes what a wrong value costs.
|
|
3327
3324
|
* A prefill somebody can see and correct became a default applied on the
|
|
3328
3325
|
* server, and the obvious next question — should it be required instead? —
|
|
3329
3326
|
* has a deliberate answer: no, because an invitation resolves the role at
|
|
@@ -3340,7 +3337,7 @@ var app = z10.object({
|
|
|
3340
3337
|
* check that, so `PATCH /api/apps/:id` does.
|
|
3341
3338
|
*/
|
|
3342
3339
|
default_role_id: z10.uuid().nullable().meta({
|
|
3343
|
-
description: "The role an app user gets when
|
|
3340
|
+
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."
|
|
3344
3341
|
}),
|
|
3345
3342
|
created_at: z10.iso.datetime().meta({
|
|
3346
3343
|
description: "When the app was created, as an ISO 8601 timestamp. `GET /api/apps` orders by this field."
|
|
@@ -3376,7 +3373,7 @@ var updateAppRequest = z10.object({
|
|
|
3376
3373
|
var serverKeyToken = z10.string().regex(/^flk_[0-9a-f]{32}$/);
|
|
3377
3374
|
var serverKey = z10.object({
|
|
3378
3375
|
id: z10.uuid().meta({
|
|
3379
|
-
description: "The key row, and what the rotate and delete routes address. It is not the key:
|
|
3376
|
+
description: "The key row, and what the rotate and delete routes address. It is not the key: this shape never carries the secret."
|
|
3380
3377
|
}),
|
|
3381
3378
|
app_id: z10.uuid().meta({
|
|
3382
3379
|
description: "The app whose full rights this key carries. A key is never shared between apps."
|
|
@@ -3394,7 +3391,7 @@ var serverKey = z10.object({
|
|
|
3394
3391
|
});
|
|
3395
3392
|
var serverKeyListResponse = z10.object({
|
|
3396
3393
|
server_keys: z10.array(serverKey).meta({
|
|
3397
|
-
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
|
|
3394
|
+
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."
|
|
3398
3395
|
})
|
|
3399
3396
|
});
|
|
3400
3397
|
var createServerKeyResponse = z10.object({
|
|
@@ -3412,7 +3409,7 @@ var role = z10.object({
|
|
|
3412
3409
|
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`."
|
|
3413
3410
|
}),
|
|
3414
3411
|
builtin: z10.boolean().meta({
|
|
3415
|
-
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
|
|
3412
|
+
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."
|
|
3416
3413
|
})
|
|
3417
3414
|
});
|
|
3418
3415
|
var roleListResponse = z10.object({
|
|
@@ -3473,10 +3470,10 @@ var rolePermissions = z10.object({
|
|
|
3473
3470
|
})
|
|
3474
3471
|
});
|
|
3475
3472
|
|
|
3476
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3473
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/app-users.js
|
|
3477
3474
|
import { z as z12 } from "zod";
|
|
3478
3475
|
|
|
3479
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3476
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/identity.js
|
|
3480
3477
|
import { z as z11 } from "zod";
|
|
3481
3478
|
var password = z11.string().min(12).max(256);
|
|
3482
3479
|
var USER_DISPLAY_NAME_MAX = 120;
|
|
@@ -3494,7 +3491,7 @@ var org = z11.object({
|
|
|
3494
3491
|
});
|
|
3495
3492
|
var patchOrgResponse = z11.object({
|
|
3496
3493
|
org: org.meta({
|
|
3497
|
-
description: "The organisation as it now stands, after the patch
|
|
3494
|
+
description: "The organisation as it now stands, after the patch. The whole resource comes back, not only the changed fields."
|
|
3498
3495
|
})
|
|
3499
3496
|
});
|
|
3500
3497
|
var fleetlessUser = z11.object({
|
|
@@ -3502,7 +3499,7 @@ var fleetlessUser = z11.object({
|
|
|
3502
3499
|
description: "The Fleetless user in the API, assigned by the cloud and stable for the life of the account."
|
|
3503
3500
|
}),
|
|
3504
3501
|
org_id: z11.uuid().meta({
|
|
3505
|
-
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
|
|
3502
|
+
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."
|
|
3506
3503
|
}),
|
|
3507
3504
|
email: z11.email().meta({
|
|
3508
3505
|
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."
|
|
@@ -3511,7 +3508,7 @@ var fleetlessUser = z11.object({
|
|
|
3511
3508
|
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."
|
|
3512
3509
|
}),
|
|
3513
3510
|
tier: orgAdminTier.meta({
|
|
3514
|
-
description: "The console powers this person holds. **Required** \u2014 every Fleetless user
|
|
3511
|
+
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."
|
|
3515
3512
|
}),
|
|
3516
3513
|
created_at: z11.iso.datetime().meta({
|
|
3517
3514
|
description: "When the account was created, as an ISO 8601 timestamp."
|
|
@@ -3638,9 +3635,9 @@ var authMeResponse = z11.object({ org, user: fleetlessUser });
|
|
|
3638
3635
|
var patchOrgRequest = z11.object({ name: z11.string().min(1).max(120) }).strict();
|
|
3639
3636
|
var patchAuthMeRequest = z11.object({ display_name: z11.string().min(1).max(USER_DISPLAY_NAME_MAX).nullable() }).strict();
|
|
3640
3637
|
|
|
3641
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3638
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/app-users.js
|
|
3642
3639
|
var APP_USER_DISPLAY_NAME_MAX = 120;
|
|
3643
|
-
var providerSlug = z12.string().max(40).regex(/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/, "
|
|
3640
|
+
var providerSlug = z12.string().max(40).regex(/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/, "must be lowercase and hyphen-separated, starting with a letter");
|
|
3644
3641
|
var appUserStatus = z12.enum(["pending_verification", "active", "blocked"]);
|
|
3645
3642
|
var appUser = z12.object({
|
|
3646
3643
|
id: z12.uuid().meta({
|
|
@@ -3668,10 +3665,10 @@ var appUser = z12.object({
|
|
|
3668
3665
|
* accepted — it does not mean blocked and it does not mean without access.
|
|
3669
3666
|
*/
|
|
3670
3667
|
has_password: z12.boolean().meta({
|
|
3671
|
-
description: 'Whether this account has a Fleetless-held password
|
|
3668
|
+
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.'
|
|
3672
3669
|
}),
|
|
3673
3670
|
providers: z12.array(providerSlug).max(20).meta({
|
|
3674
|
-
description: "The slugs of the identity providers this account is linked to, empty for a password-only user.
|
|
3671
|
+
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."
|
|
3675
3672
|
}),
|
|
3676
3673
|
last_login_at: z12.iso.datetime().nullable().meta({
|
|
3677
3674
|
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*."
|
|
@@ -3765,7 +3762,7 @@ var appOidcProvider = z12.object({
|
|
|
3765
3762
|
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."
|
|
3766
3763
|
}),
|
|
3767
3764
|
enabled: z12.boolean().meta({
|
|
3768
|
-
description: "Whether this provider is offered
|
|
3765
|
+
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."
|
|
3769
3766
|
}),
|
|
3770
3767
|
created_at: z12.iso.datetime().meta({ description: "When the provider was configured, as an ISO 8601 timestamp." })
|
|
3771
3768
|
}).strict();
|
|
@@ -3889,10 +3886,10 @@ var mailOutcome = z12.object({
|
|
|
3889
3886
|
})
|
|
3890
3887
|
});
|
|
3891
3888
|
|
|
3892
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
3889
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/client-auth.js
|
|
3893
3890
|
var clientLoginRequest = z13.object({
|
|
3894
3891
|
app_identifier: appIdentifier.meta({
|
|
3895
|
-
description: "The app being logged in to
|
|
3892
|
+
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."
|
|
3896
3893
|
}),
|
|
3897
3894
|
email: z13.email().meta({
|
|
3898
3895
|
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."
|
|
@@ -3908,7 +3905,7 @@ var clientRefreshRequest = z13.object({
|
|
|
3908
3905
|
});
|
|
3909
3906
|
var clientLogoutRequest = z13.object({
|
|
3910
3907
|
refresh_token: z13.string().min(1).meta({
|
|
3911
|
-
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
|
|
3908
|
+
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."
|
|
3912
3909
|
})
|
|
3913
3910
|
});
|
|
3914
3911
|
var clientRegisterRequest = z13.object({
|
|
@@ -4024,7 +4021,7 @@ var clientMcpInteraction = z13.object({
|
|
|
4024
4021
|
app_id: z13.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." }),
|
|
4025
4022
|
client_name: z13.string().nullable().meta({ description: "What the MCP client calls itself, or `null` if it named nothing. **Unverified** \u2014 see `client_name_verified`." }),
|
|
4026
4023
|
client_name_verified: z13.literal(false).meta({
|
|
4027
|
-
description: "Always `false`. The client registered itself without authentication and
|
|
4024
|
+
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."
|
|
4028
4025
|
}),
|
|
4029
4026
|
scopes: z13.array(z13.string()).meta({ description: "The scopes the client asked for, to show the person before they approve." }),
|
|
4030
4027
|
already_granted: z13.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." }),
|
|
@@ -4040,10 +4037,10 @@ var mcpConsentGrant = z13.object({
|
|
|
4040
4037
|
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."
|
|
4041
4038
|
}),
|
|
4042
4039
|
client_name: z13.string().nullable().meta({
|
|
4043
|
-
description: "What the client calls itself, or `null`
|
|
4040
|
+
description: "What the client calls itself, or `null` once its registration is gone. **Unverified** \u2014 see `client_name_verified`."
|
|
4044
4041
|
}),
|
|
4045
4042
|
client_name_verified: z13.literal(false).meta({
|
|
4046
|
-
description: "Always `false`. The client registered itself without authentication and
|
|
4043
|
+
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."
|
|
4047
4044
|
}),
|
|
4048
4045
|
granted_at: z13.iso.datetime().meta({
|
|
4049
4046
|
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."
|
|
@@ -4056,7 +4053,7 @@ var mcpConsentGrantListResponse = z13.object({
|
|
|
4056
4053
|
});
|
|
4057
4054
|
var clientIdentity = z13.object({
|
|
4058
4055
|
kind: z13.enum(["developer", "app_user", "server_key"]).meta({
|
|
4059
|
-
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
|
|
4056
|
+
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."
|
|
4060
4057
|
}),
|
|
4061
4058
|
developer_id: z13.uuid().nullable().meta({
|
|
4062
4059
|
description: "The Fleetless user behind this session, or `null` when `kind` is not `developer`."
|
|
@@ -4078,7 +4075,7 @@ var clientIdentity = z13.object({
|
|
|
4078
4075
|
})
|
|
4079
4076
|
});
|
|
4080
4077
|
|
|
4081
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
4078
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/realtime.js
|
|
4082
4079
|
var clientAuth = z14.object({
|
|
4083
4080
|
type: z14.literal("auth"),
|
|
4084
4081
|
token: z14.string().min(1)
|
|
@@ -4264,10 +4261,10 @@ var liveSessionEvent = z14.object({
|
|
|
4264
4261
|
/**
|
|
4265
4262
|
* **Classified text the cloud produced, never text the robot sent.**
|
|
4266
4263
|
*
|
|
4267
|
-
*
|
|
4268
|
-
*
|
|
4269
|
-
*
|
|
4270
|
-
*
|
|
4264
|
+
* Nothing sanitises `bridgeCameraState.error.message`, and a camera
|
|
4265
|
+
* password reaches a developer surface through exactly that route — which
|
|
4266
|
+
* is why the cloud maps a robot's diagnosis to fixed strings rather than
|
|
4267
|
+
* forwarding it.
|
|
4271
4268
|
*
|
|
4272
4269
|
* So: `null` unless the cloud itself has something classified to say. If a
|
|
4273
4270
|
* developer needs the robot's own diagnosis later, it arrives as a mapped
|
|
@@ -4356,17 +4353,34 @@ var orgEventDropped = z14.object({
|
|
|
4356
4353
|
dropped: z14.number().int().positive()
|
|
4357
4354
|
}).strict();
|
|
4358
4355
|
|
|
4359
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
4356
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/client-robots.js
|
|
4360
4357
|
import { z as z15 } from "zod";
|
|
4361
|
-
var
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
})
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4358
|
+
var clientRobotListItem = z15.object({
|
|
4359
|
+
...robot.shape,
|
|
4360
|
+
bridge_state: bridgeState.meta({
|
|
4361
|
+
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."
|
|
4362
|
+
}),
|
|
4363
|
+
published_version: z15.number().int().positive().nullable().meta({
|
|
4364
|
+
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.'
|
|
4365
|
+
})
|
|
4366
|
+
});
|
|
4367
|
+
var clientRobotListResponse = z15.object({
|
|
4368
|
+
robots: z15.array(clientRobotListItem).meta({
|
|
4369
|
+
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."
|
|
4370
|
+
})
|
|
4371
|
+
});
|
|
4372
|
+
|
|
4373
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/audit.js
|
|
4374
|
+
import { z as z16 } from "zod";
|
|
4375
|
+
var auditActor = z16.object({
|
|
4376
|
+
kind: z16.enum(["developer", "end_user", "app_user", "server_key", "bridge"]),
|
|
4377
|
+
id: z16.uuid(),
|
|
4378
|
+
label: z16.string().min(1).max(200)
|
|
4379
|
+
});
|
|
4380
|
+
var auditEvent = z16.object({
|
|
4381
|
+
id: z16.uuid(),
|
|
4382
|
+
org_id: z16.uuid(),
|
|
4383
|
+
at: z16.iso.datetime(),
|
|
4370
4384
|
/**
|
|
4371
4385
|
* A monotonic counter, ascending in write order, unique across the log.
|
|
4372
4386
|
*
|
|
@@ -4385,18 +4399,18 @@ var auditEvent = z15.object({
|
|
|
4385
4399
|
* Required, not optional: an event without a sequence cannot be ordered
|
|
4386
4400
|
* against one that has it, and a log with two orderings has none.
|
|
4387
4401
|
*/
|
|
4388
|
-
seq:
|
|
4402
|
+
seq: z16.number().int().positive(),
|
|
4389
4403
|
actor: auditActor,
|
|
4390
4404
|
/** Stable dotted name, e.g. `app_user.login`, `config.published`. */
|
|
4391
|
-
action:
|
|
4405
|
+
action: z16.string().min(1).max(80),
|
|
4392
4406
|
/**
|
|
4393
4407
|
* What the action was about, if anything — a robot, an app, a user. Free
|
|
4394
4408
|
* of ids the console cannot resolve: carry the label with it.
|
|
4395
4409
|
*/
|
|
4396
|
-
target:
|
|
4397
|
-
kind:
|
|
4398
|
-
id:
|
|
4399
|
-
label:
|
|
4410
|
+
target: z16.object({
|
|
4411
|
+
kind: z16.string().min(1).max(40),
|
|
4412
|
+
id: z16.string().min(1),
|
|
4413
|
+
label: z16.string().min(1).max(200)
|
|
4400
4414
|
}).nullable(),
|
|
4401
4415
|
/**
|
|
4402
4416
|
* Action-specific extras.
|
|
@@ -4410,10 +4424,10 @@ var auditEvent = z15.object({
|
|
|
4410
4424
|
* So: never credentials, never tokens. That is a rule, not a guarantee the
|
|
4411
4425
|
* schema enforces.
|
|
4412
4426
|
*/
|
|
4413
|
-
details:
|
|
4427
|
+
details: z16.record(z16.string(), z16.unknown()).nullable()
|
|
4414
4428
|
});
|
|
4415
4429
|
var auditTimestampMs = wireTimestampMs;
|
|
4416
|
-
var auditQuery =
|
|
4430
|
+
var auditQuery = z16.object({
|
|
4417
4431
|
/** Only events with a smaller `seq` — the next, older page. */
|
|
4418
4432
|
before_seq: wireSeqCursor.optional(),
|
|
4419
4433
|
/**
|
|
@@ -4422,19 +4436,18 @@ var auditQuery = z15.object({
|
|
|
4422
4436
|
* coercion's result in either `io` direction, so the artifact would describe
|
|
4423
4437
|
* a shape a query string can never carry.
|
|
4424
4438
|
*/
|
|
4425
|
-
limit:
|
|
4439
|
+
limit: z16.union([z16.string().regex(/^\d{1,4}$/), z16.number().int()]).transform((v) => Number(v)).pipe(z16.number().int().positive().max(500)).optional(),
|
|
4426
4440
|
/** Exact action name, e.g. `config.published`. No prefix matching: a filter that matches more than it says is not one. */
|
|
4427
|
-
action:
|
|
4441
|
+
action: z16.string().min(1).max(80).optional(),
|
|
4428
4442
|
/**
|
|
4429
4443
|
* Everything under a dotted prefix, e.g. `server_key.` for all three
|
|
4430
4444
|
* server-key actions.
|
|
4431
4445
|
*
|
|
4432
|
-
* **A separate parameter, not a widening of `action`.** The
|
|
4433
|
-
* `action` above
|
|
4434
|
-
*
|
|
4435
|
-
* one it is. Setting both is refused rather than resolved, because a query
|
|
4446
|
+
* **A separate parameter, not a widening of `action`.** The rule on
|
|
4447
|
+
* `action` above still stands; this is a different question with a name
|
|
4448
|
+
* that says which one it is. Setting both is refused, not resolved —
|
|
4436
4449
|
* naming an exact action *and* a prefix is a caller mistake, not a
|
|
4437
|
-
* combination
|
|
4450
|
+
* combination to guess the meaning of.
|
|
4438
4451
|
*
|
|
4439
4452
|
* **The published artifact cannot express that refusal**: a cross-field
|
|
4440
4453
|
* `.refine()` has no JSON Schema rendering, so `audit-query.schema.json`
|
|
@@ -4442,7 +4455,7 @@ var auditQuery = z15.object({
|
|
|
4442
4455
|
* happily. The cloud is the only enforcement point — the same residual
|
|
4443
4456
|
* `orgLatencyQuery` and `orgUsageQuery` already name.
|
|
4444
4457
|
*/
|
|
4445
|
-
action_prefix:
|
|
4458
|
+
action_prefix: z16.string().min(1).max(80).optional(),
|
|
4446
4459
|
/**
|
|
4447
4460
|
* Only events by this actor.
|
|
4448
4461
|
*
|
|
@@ -4454,9 +4467,9 @@ var auditQuery = z15.object({
|
|
|
4454
4467
|
* Not an injection question — the query is parameterised either way. It is a
|
|
4455
4468
|
* **500 where a 400 belongs**, and a 500 is the answer that explains nothing.
|
|
4456
4469
|
*/
|
|
4457
|
-
actor_id:
|
|
4470
|
+
actor_id: z16.uuid().optional(),
|
|
4458
4471
|
/** Only events about this kind of target, e.g. `robot`. */
|
|
4459
|
-
target_kind:
|
|
4472
|
+
target_kind: z16.string().min(1).max(40).optional(),
|
|
4460
4473
|
/**
|
|
4461
4474
|
* Absolute bounds in unix milliseconds, **half-open `[from, to)`** — the
|
|
4462
4475
|
* same rule the history shapes follow.
|
|
@@ -4472,8 +4485,8 @@ var auditQuery = z15.object({
|
|
|
4472
4485
|
message: "action and action_prefix cannot be combined",
|
|
4473
4486
|
path: ["action_prefix"]
|
|
4474
4487
|
});
|
|
4475
|
-
var auditListResponse =
|
|
4476
|
-
events:
|
|
4488
|
+
var auditListResponse = z16.object({
|
|
4489
|
+
events: z16.array(auditEvent),
|
|
4477
4490
|
/**
|
|
4478
4491
|
* The `seq` a caller sends as `before_seq` to keep reading — or `null` when
|
|
4479
4492
|
* there is nothing further.
|
|
@@ -4484,29 +4497,29 @@ var auditListResponse = z15.object({
|
|
|
4484
4497
|
* not mean *no more* here. The same distinction `historySamples` was given
|
|
4485
4498
|
* `truncated` for.
|
|
4486
4499
|
*/
|
|
4487
|
-
next_cursor:
|
|
4500
|
+
next_cursor: z16.number().int().positive().nullable()
|
|
4488
4501
|
});
|
|
4489
4502
|
|
|
4490
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
4491
|
-
import { z as
|
|
4492
|
-
var apiError =
|
|
4493
|
-
code:
|
|
4494
|
-
message:
|
|
4495
|
-
details:
|
|
4503
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/errors.js
|
|
4504
|
+
import { z as z17 } from "zod";
|
|
4505
|
+
var apiError = z17.object({
|
|
4506
|
+
code: z17.string().min(1),
|
|
4507
|
+
message: z17.string().min(1),
|
|
4508
|
+
details: z17.unknown().optional()
|
|
4496
4509
|
});
|
|
4497
|
-
var parameterViolation =
|
|
4498
|
-
field:
|
|
4510
|
+
var parameterViolation = z17.object({
|
|
4511
|
+
field: z17.string().min(1),
|
|
4499
4512
|
/** Which rule failed — `min`, `max`, `enum`, `pattern`, `required`, `undeclared`. */
|
|
4500
|
-
rule:
|
|
4501
|
-
message:
|
|
4513
|
+
rule: z17.string().min(1),
|
|
4514
|
+
message: z17.string().min(1)
|
|
4502
4515
|
});
|
|
4503
|
-
var parameterInvalidDetails =
|
|
4504
|
-
violations:
|
|
4516
|
+
var parameterInvalidDetails = z17.object({
|
|
4517
|
+
violations: z17.array(parameterViolation).min(1)
|
|
4505
4518
|
});
|
|
4506
4519
|
|
|
4507
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
4508
|
-
import { z as
|
|
4509
|
-
var oauthErrorCode =
|
|
4520
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/oauth.js
|
|
4521
|
+
import { z as z18 } from "zod";
|
|
4522
|
+
var oauthErrorCode = z18.enum([
|
|
4510
4523
|
"invalid_request",
|
|
4511
4524
|
"invalid_client",
|
|
4512
4525
|
"invalid_grant",
|
|
@@ -4519,11 +4532,11 @@ var oauthErrorCode = z17.enum([
|
|
|
4519
4532
|
/** RFC 8707: the `resource` named is not one this server issues tokens for. */
|
|
4520
4533
|
"invalid_target"
|
|
4521
4534
|
]);
|
|
4522
|
-
var oauthError =
|
|
4535
|
+
var oauthError = z18.object({
|
|
4523
4536
|
error: oauthErrorCode,
|
|
4524
|
-
error_description:
|
|
4537
|
+
error_description: z18.string().min(1).max(500).optional(),
|
|
4525
4538
|
/** Echoed back per RFC 6749 §4.1.2.1 so a client can match the response. */
|
|
4526
|
-
state:
|
|
4539
|
+
state: z18.string().min(1).max(500).optional(),
|
|
4527
4540
|
/**
|
|
4528
4541
|
* **A Fleetless reason carried inside a standard envelope, and it exists
|
|
4529
4542
|
* because the alternative lost a distinction.**
|
|
@@ -4541,9 +4554,9 @@ var oauthError = z17.object({
|
|
|
4541
4554
|
* our own tooling switches on. RFC 6749 §5.2 permits additional members, and
|
|
4542
4555
|
* a client that ignores this one still behaves correctly.
|
|
4543
4556
|
*/
|
|
4544
|
-
fleetless_code:
|
|
4557
|
+
fleetless_code: z18.string().min(1).max(60).optional()
|
|
4545
4558
|
});
|
|
4546
|
-
var redirectUri =
|
|
4559
|
+
var redirectUri = z18.string().min(1).max(2e3).refine((v) => {
|
|
4547
4560
|
let url;
|
|
4548
4561
|
try {
|
|
4549
4562
|
url = new URL(v);
|
|
@@ -4558,161 +4571,161 @@ var redirectUri = z17.string().min(1).max(2e3).refine((v) => {
|
|
|
4558
4571
|
return ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
|
|
4559
4572
|
return false;
|
|
4560
4573
|
}, { message: "redirect_uri must be an https URL, or http on an explicit loopback address, and carry no fragment" });
|
|
4561
|
-
var codeChallengeMethod =
|
|
4574
|
+
var codeChallengeMethod = z18.enum(["S256"]);
|
|
4562
4575
|
var MCP_DCR_MAX_REDIRECT_URIS = 5;
|
|
4563
|
-
var dynamicClientRegistrationRequest =
|
|
4564
|
-
redirect_uris:
|
|
4565
|
-
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.
|
|
4576
|
+
var dynamicClientRegistrationRequest = z18.object({
|
|
4577
|
+
redirect_uris: z18.array(redirectUri).min(1).max(MCP_DCR_MAX_REDIRECT_URIS).meta({
|
|
4578
|
+
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.`
|
|
4566
4579
|
}),
|
|
4567
|
-
client_name:
|
|
4568
|
-
description:
|
|
4580
|
+
client_name: z18.string().min(1).max(200).optional().meta({
|
|
4581
|
+
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"*.'
|
|
4569
4582
|
}),
|
|
4570
|
-
token_endpoint_auth_method:
|
|
4583
|
+
token_endpoint_auth_method: z18.enum(["none"]).optional().meta({
|
|
4571
4584
|
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."
|
|
4572
4585
|
}),
|
|
4573
|
-
grant_types:
|
|
4586
|
+
grant_types: z18.array(z18.enum(["authorization_code", "refresh_token"])).optional().meta({
|
|
4574
4587
|
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."
|
|
4575
4588
|
}),
|
|
4576
|
-
response_types:
|
|
4589
|
+
response_types: z18.array(z18.enum(["code"])).optional().meta({
|
|
4577
4590
|
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."
|
|
4578
4591
|
}),
|
|
4579
|
-
scope:
|
|
4592
|
+
scope: z18.string().max(500).optional().meta({
|
|
4580
4593
|
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."
|
|
4581
4594
|
})
|
|
4582
4595
|
}).meta({
|
|
4583
4596
|
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)."
|
|
4584
4597
|
});
|
|
4585
|
-
var dynamicClientRegistrationResponse =
|
|
4586
|
-
client_id:
|
|
4598
|
+
var dynamicClientRegistrationResponse = z18.object({
|
|
4599
|
+
client_id: z18.string().min(1).max(200).meta({
|
|
4587
4600
|
description: "The identifier this client sends at the authorize and token endpoints. Opaque, and not the app identifier."
|
|
4588
4601
|
}),
|
|
4589
|
-
client_name:
|
|
4602
|
+
client_name: z18.string().min(1).max(200).meta({
|
|
4590
4603
|
description: "The name the client registered under, echoed back. Chosen by the client and not vouched for by Fleetless."
|
|
4591
4604
|
}),
|
|
4592
|
-
redirect_uris:
|
|
4605
|
+
redirect_uris: z18.array(redirectUri).meta({
|
|
4593
4606
|
description: "The redirect URIs this registration was accepted for. A code is returned to one of these and nowhere else."
|
|
4594
4607
|
}),
|
|
4595
|
-
grant_types:
|
|
4608
|
+
grant_types: z18.array(z18.string()).meta({
|
|
4596
4609
|
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.'
|
|
4597
4610
|
}),
|
|
4598
|
-
response_types:
|
|
4611
|
+
response_types: z18.array(z18.string()).meta({
|
|
4599
4612
|
description: "The response types this client may ask for: `code`."
|
|
4600
4613
|
}),
|
|
4601
|
-
token_endpoint_auth_method:
|
|
4614
|
+
token_endpoint_auth_method: z18.literal("none").meta({
|
|
4602
4615
|
description: "`none` \u2014 this server registers public clients only, and PKCE rather than a secret is what protects the exchange."
|
|
4603
4616
|
}),
|
|
4604
|
-
client_id_issued_at:
|
|
4617
|
+
client_id_issued_at: z18.number().int().nonnegative().meta({
|
|
4605
4618
|
description: "When the registration was created, in seconds since the epoch, per RFC 7591."
|
|
4606
4619
|
}),
|
|
4607
|
-
client_secret_expires_at:
|
|
4620
|
+
client_secret_expires_at: z18.literal(0).meta({
|
|
4608
4621
|
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."
|
|
4609
4622
|
})
|
|
4610
4623
|
});
|
|
4611
|
-
var oauthTokenRequest =
|
|
4612
|
-
grant_type:
|
|
4624
|
+
var oauthTokenRequest = z18.object({
|
|
4625
|
+
grant_type: z18.literal("authorization_code").meta({
|
|
4613
4626
|
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."
|
|
4614
4627
|
}),
|
|
4615
|
-
code:
|
|
4628
|
+
code: z18.string().min(1).max(500).meta({
|
|
4616
4629
|
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."
|
|
4617
4630
|
}),
|
|
4618
4631
|
redirect_uri: redirectUri.meta({
|
|
4619
4632
|
description: "The same redirect URI the authorize request used. It is compared, not merely recorded."
|
|
4620
4633
|
}),
|
|
4621
|
-
client_id:
|
|
4634
|
+
client_id: z18.string().min(1).max(200).meta({
|
|
4622
4635
|
description: "The client making the exchange, as registered."
|
|
4623
4636
|
}),
|
|
4624
|
-
code_verifier:
|
|
4637
|
+
code_verifier: z18.string().regex(/^[A-Za-z0-9\-._~]{43,128}$/, "code_verifier must be 43-128 unreserved characters (RFC 7636 \xA74.1)").meta({
|
|
4625
4638
|
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."
|
|
4626
4639
|
}),
|
|
4627
|
-
resource:
|
|
4628
|
-
description: "The resource the token is
|
|
4640
|
+
resource: z18.url().optional().meta({
|
|
4641
|
+
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."
|
|
4629
4642
|
})
|
|
4630
4643
|
}).meta({
|
|
4631
4644
|
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."
|
|
4632
4645
|
});
|
|
4633
|
-
var oauthTokenResponse =
|
|
4634
|
-
access_token:
|
|
4646
|
+
var oauthTokenResponse = z18.object({
|
|
4647
|
+
access_token: z18.string().min(1).meta({
|
|
4635
4648
|
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."
|
|
4636
4649
|
}),
|
|
4637
|
-
token_type:
|
|
4650
|
+
token_type: z18.literal("Bearer").meta({
|
|
4638
4651
|
description: "`Bearer`. RFC 6749 \xA75.1 makes the value case-insensitive for a client reading it; this is the spelling this server emits."
|
|
4639
4652
|
}),
|
|
4640
|
-
expires_in:
|
|
4653
|
+
expires_in: z18.number().int().positive().meta({
|
|
4641
4654
|
description: "How long the access token is valid, in **seconds**, per RFC 6749 \xA75.1. Not a timestamp, and not milliseconds."
|
|
4642
4655
|
}),
|
|
4643
|
-
refresh_token:
|
|
4656
|
+
refresh_token: z18.string().min(1).optional().meta({
|
|
4644
4657
|
description: "The refresh token, when one was issued. It rotates on every use."
|
|
4645
4658
|
}),
|
|
4646
|
-
scope:
|
|
4659
|
+
scope: z18.string().max(500).optional().meta({
|
|
4647
4660
|
description: "The scopes the issued token actually carries, space-separated."
|
|
4648
4661
|
})
|
|
4649
4662
|
});
|
|
4650
|
-
var authorizationServerMetadata =
|
|
4651
|
-
issuer:
|
|
4663
|
+
var authorizationServerMetadata = z18.object({
|
|
4664
|
+
issuer: z18.url().meta({
|
|
4652
4665
|
description: "The issuer identifier of this authorization server, per RFC 8414 \xA72. It is what a client checks a token's `iss` against."
|
|
4653
4666
|
}),
|
|
4654
|
-
authorization_endpoint:
|
|
4655
|
-
description: "
|
|
4667
|
+
authorization_endpoint: z18.url().meta({
|
|
4668
|
+
description: "Where a client sends the user to authorize."
|
|
4656
4669
|
}),
|
|
4657
|
-
token_endpoint:
|
|
4670
|
+
token_endpoint: z18.url().meta({
|
|
4658
4671
|
description: "The URL where a client exchanges an authorization code, or a refresh token, for tokens."
|
|
4659
4672
|
}),
|
|
4660
|
-
registration_endpoint:
|
|
4673
|
+
registration_endpoint: z18.url().optional().meta({
|
|
4661
4674
|
description: "The URL where a client may register itself, per RFC 7591. Absent when the app does not accept dynamic clients."
|
|
4662
4675
|
}),
|
|
4663
|
-
response_types_supported:
|
|
4676
|
+
response_types_supported: z18.array(z18.literal("code")).meta({
|
|
4664
4677
|
description: "The response types this server offers: `code` only, the implicit grant being gone with OAuth 2.1."
|
|
4665
4678
|
}),
|
|
4666
|
-
grant_types_supported:
|
|
4679
|
+
grant_types_supported: z18.array(z18.enum(["authorization_code", "refresh_token"])).meta({
|
|
4667
4680
|
description: "The grants this server offers. OAuth 2.1 removes the implicit and password grants, so neither appears here."
|
|
4668
4681
|
}),
|
|
4669
|
-
code_challenge_methods_supported:
|
|
4682
|
+
code_challenge_methods_supported: z18.array(codeChallengeMethod).meta({
|
|
4670
4683
|
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."
|
|
4671
4684
|
}),
|
|
4672
|
-
token_endpoint_auth_methods_supported:
|
|
4685
|
+
token_endpoint_auth_methods_supported: z18.array(z18.literal("none")).meta({
|
|
4673
4686
|
description: "How a client authenticates at the token endpoint: `none`, the public-client method, with PKCE protecting the exchange."
|
|
4674
4687
|
}),
|
|
4675
|
-
scopes_supported:
|
|
4688
|
+
scopes_supported: z18.array(z18.string()).optional().meta({
|
|
4676
4689
|
description: "The scopes this server knows about, where it publishes a list."
|
|
4677
4690
|
})
|
|
4678
4691
|
});
|
|
4679
|
-
var protectedResourceMetadata =
|
|
4680
|
-
resource:
|
|
4692
|
+
var protectedResourceMetadata = z18.object({
|
|
4693
|
+
resource: z18.url().meta({
|
|
4681
4694
|
description: "The resource identifier this document describes, per RFC 9728. A token whose audience names something else is rejected here rather than merely noted."
|
|
4682
4695
|
}),
|
|
4683
|
-
authorization_servers:
|
|
4696
|
+
authorization_servers: z18.array(z18.url()).min(1).meta({
|
|
4684
4697
|
description: "The authorization servers that may issue tokens for this resource. There is always at least one."
|
|
4685
4698
|
}),
|
|
4686
|
-
bearer_methods_supported:
|
|
4699
|
+
bearer_methods_supported: z18.array(z18.literal("header")).meta({
|
|
4687
4700
|
description: "How a token may be presented: in the `Authorization` header only, never in a query parameter or a form field."
|
|
4688
4701
|
}),
|
|
4689
|
-
scopes_supported:
|
|
4702
|
+
scopes_supported: z18.array(z18.string()).optional().meta({
|
|
4690
4703
|
description: "The scopes this resource understands, where it publishes a list."
|
|
4691
4704
|
})
|
|
4692
4705
|
});
|
|
4693
|
-
var oauthRedirectResponse =
|
|
4694
|
-
redirect_to:
|
|
4706
|
+
var oauthRedirectResponse = z18.object({
|
|
4707
|
+
redirect_to: z18.string().min(1).max(2e3)
|
|
4695
4708
|
});
|
|
4696
|
-
var oauthAuthorizeQuery =
|
|
4697
|
-
response_type:
|
|
4709
|
+
var oauthAuthorizeQuery = z18.object({
|
|
4710
|
+
response_type: z18.literal("code").meta({
|
|
4698
4711
|
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`."
|
|
4699
4712
|
}),
|
|
4700
|
-
client_id:
|
|
4713
|
+
client_id: z18.string().min(1).meta({
|
|
4701
4714
|
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."
|
|
4702
4715
|
}),
|
|
4703
|
-
redirect_uri:
|
|
4716
|
+
redirect_uri: z18.string().min(1).meta({
|
|
4704
4717
|
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."
|
|
4705
4718
|
}),
|
|
4706
|
-
code_challenge:
|
|
4707
|
-
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
|
|
4719
|
+
code_challenge: z18.string().min(1).meta({
|
|
4720
|
+
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."
|
|
4708
4721
|
}),
|
|
4709
|
-
code_challenge_method:
|
|
4722
|
+
code_challenge_method: z18.literal("S256").meta({
|
|
4710
4723
|
description: "Only `S256`. `plain` is refused: a challenge equal to its verifier defends against nothing."
|
|
4711
4724
|
}),
|
|
4712
|
-
state:
|
|
4725
|
+
state: z18.string().optional().meta({
|
|
4713
4726
|
description: "Returned unchanged on the callback, and on the error redirect too, so a client can bind either answer to its own request."
|
|
4714
4727
|
}),
|
|
4715
|
-
resource:
|
|
4728
|
+
resource: z18.string().optional().meta({
|
|
4716
4729
|
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."
|
|
4717
4730
|
})
|
|
4718
4731
|
// **No `scope`, because this authorization server issues none.** The field
|
|
@@ -4725,7 +4738,7 @@ var oauthAuthorizeQuery = z17.object({
|
|
|
4725
4738
|
description: "The authorization request an MCP client sends, per RFC 6749 \xA74.1.1 with mandatory PKCE. The handler reads it parameter by parameter rather than through one parse, because the answers differ: `client_id` and `redirect_uri` are refused flat, with no redirect, since until both are confirmed there is no trusted target to bounce a browser to, and everything after them is reported to the client's own callback as query parameters."
|
|
4726
4739
|
});
|
|
4727
4740
|
|
|
4728
|
-
// node_modules/.pnpm/@fleetless+contracts@1.0
|
|
4741
|
+
// node_modules/.pnpm/@fleetless+contracts@1.1.0/node_modules/@fleetless/contracts/dist/routes.js
|
|
4729
4742
|
var MCP_APP = MCP_APP_PATHS(":appIdentifier");
|
|
4730
4743
|
var APP_IDENTIFIER = {
|
|
4731
4744
|
name: "appIdentifier",
|
|
@@ -4930,6 +4943,24 @@ var ROUTES = [
|
|
|
4930
4943
|
transport: "http",
|
|
4931
4944
|
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.'
|
|
4932
4945
|
},
|
|
4946
|
+
{
|
|
4947
|
+
method: "GET",
|
|
4948
|
+
path: "/favicon.svg",
|
|
4949
|
+
section: "client-auth",
|
|
4950
|
+
summary: "Serves the Fleetless icon for the auth portal's and the MCP welcome page's browser tab.",
|
|
4951
|
+
audience: "internal",
|
|
4952
|
+
auth: "none",
|
|
4953
|
+
rateLimited: false,
|
|
4954
|
+
ownerTier: false,
|
|
4955
|
+
status: 200,
|
|
4956
|
+
params: [],
|
|
4957
|
+
query: null,
|
|
4958
|
+
request: null,
|
|
4959
|
+
response: null,
|
|
4960
|
+
errors: [],
|
|
4961
|
+
transport: "http",
|
|
4962
|
+
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."
|
|
4963
|
+
},
|
|
4933
4964
|
{
|
|
4934
4965
|
method: "POST",
|
|
4935
4966
|
path: "/api/auth/password/reset/confirm",
|
|
@@ -5394,7 +5425,7 @@ var ROUTES = [
|
|
|
5394
5425
|
params: [
|
|
5395
5426
|
{ name: "id", description: "The app's uuid, as returned by `POST /api/apps` or listed by `GET /api/apps`." },
|
|
5396
5427
|
{ name: "userId", description: "The app user's uuid, from `GET /api/apps/:id/users`; a user of another app answers `404`." },
|
|
5397
|
-
{ name: "clientId", description: "The MCP client, as `GET /api/apps/:id/users/:userId/mcp-grants` reports its `client_id`. Not a uuid \u2014
|
|
5428
|
+
{ 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." }
|
|
5398
5429
|
],
|
|
5399
5430
|
query: null,
|
|
5400
5431
|
request: null,
|
|
@@ -6791,7 +6822,7 @@ var ROUTES = [
|
|
|
6791
6822
|
rateLimited: false,
|
|
6792
6823
|
ownerTier: false,
|
|
6793
6824
|
status: 204,
|
|
6794
|
-
params: [{ name: "clientId", description: "The MCP client, as `GET /api/client/mcp/grants` reports its `client_id`. Not a uuid \u2014
|
|
6825
|
+
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." }],
|
|
6795
6826
|
query: null,
|
|
6796
6827
|
request: null,
|
|
6797
6828
|
response: null,
|
|
@@ -6982,6 +7013,43 @@ var ROUTES = [
|
|
|
6982
7013
|
transport: "http",
|
|
6983
7014
|
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."
|
|
6984
7015
|
},
|
|
7016
|
+
/* ------------------------------------------ discovery: the REST twins of the two MCP tools a session starts from */
|
|
7017
|
+
{
|
|
7018
|
+
method: "GET",
|
|
7019
|
+
path: "/api/client/robots",
|
|
7020
|
+
section: "robots",
|
|
7021
|
+
summary: "Lists the robots the caller reaches, with bridge state and the published configuration version.",
|
|
7022
|
+
audience: "client",
|
|
7023
|
+
auth: "developer_or_client",
|
|
7024
|
+
rateLimited: false,
|
|
7025
|
+
ownerTier: false,
|
|
7026
|
+
status: 200,
|
|
7027
|
+
params: [],
|
|
7028
|
+
query: null,
|
|
7029
|
+
request: null,
|
|
7030
|
+
response: clientRobotListResponse,
|
|
7031
|
+
errors: [...CLIENT_GUARD],
|
|
7032
|
+
transport: "http",
|
|
7033
|
+
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`."
|
|
7034
|
+
},
|
|
7035
|
+
{
|
|
7036
|
+
method: "GET",
|
|
7037
|
+
path: "/api/robots/:id/datasheet",
|
|
7038
|
+
section: "robots",
|
|
7039
|
+
summary: "Describes everything the caller's role lets them do on one robot, with parameter schemas.",
|
|
7040
|
+
audience: "client",
|
|
7041
|
+
auth: "developer_or_client",
|
|
7042
|
+
rateLimited: false,
|
|
7043
|
+
ownerTier: false,
|
|
7044
|
+
status: 200,
|
|
7045
|
+
params: [{ name: "id", description: "The robot's uuid, as `GET /api/client/robots` lists it." }],
|
|
7046
|
+
query: null,
|
|
7047
|
+
request: null,
|
|
7048
|
+
response: mcpRobotDatasheet,
|
|
7049
|
+
errors: [...CLIENT_GUARD, "invalid_uuid", "not_found"],
|
|
7050
|
+
transport: "http",
|
|
7051
|
+
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."
|
|
7052
|
+
},
|
|
6985
7053
|
/* ------------------------------------------------- config (draft/publish) */
|
|
6986
7054
|
{
|
|
6987
7055
|
method: "GET",
|
|
@@ -7304,7 +7372,7 @@ var ROUTES = [
|
|
|
7304
7372
|
status: 202,
|
|
7305
7373
|
params: [
|
|
7306
7374
|
{ name: "id", description: "The robot's uuid; an end user reaches it through an app that attaches it." },
|
|
7307
|
-
{ name: "slug", description: "The action or service slug from the published configuration
|
|
7375
|
+
{ name: "slug", description: "The action or service slug from the published configuration \u2014 the cloud already knows which kind." }
|
|
7308
7376
|
],
|
|
7309
7377
|
query: null,
|
|
7310
7378
|
request: invokeRequest,
|
|
@@ -8471,11 +8539,30 @@ function createJobSubscriptions(channel, slugSubscriptions) {
|
|
|
8471
8539
|
}
|
|
8472
8540
|
|
|
8473
8541
|
// src/jobs.ts
|
|
8542
|
+
function historyQueryString2(options) {
|
|
8543
|
+
const params = new URLSearchParams();
|
|
8544
|
+
if (options.slug !== void 0) params.set("slug", options.slug);
|
|
8545
|
+
if (options.state !== void 0) params.set("state", options.state);
|
|
8546
|
+
if (options.kind !== void 0) params.set("kind", options.kind);
|
|
8547
|
+
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
8548
|
+
if (options.beforeSeq !== void 0) params.set("before_seq", String(options.beforeSeq));
|
|
8549
|
+
if (options.fromMs !== void 0) params.set("from_ms", String(options.fromMs));
|
|
8550
|
+
if (options.toMs !== void 0) params.set("to_ms", String(options.toMs));
|
|
8551
|
+
return params.toString();
|
|
8552
|
+
}
|
|
8474
8553
|
function createJobsApi(http) {
|
|
8475
8554
|
return {
|
|
8476
8555
|
async list(robotId) {
|
|
8477
8556
|
const response = await http.request(`/api/robots/${pathSegment(robotId)}/jobs`, {});
|
|
8478
8557
|
return response.jobs;
|
|
8558
|
+
},
|
|
8559
|
+
async history(robotId, options = {}) {
|
|
8560
|
+
const query = historyQueryString2(options);
|
|
8561
|
+
const page = await http.request(
|
|
8562
|
+
`/api/robots/${pathSegment(robotId)}/jobs/history${query ? `?${query}` : ""}`,
|
|
8563
|
+
{}
|
|
8564
|
+
);
|
|
8565
|
+
return page;
|
|
8479
8566
|
}
|
|
8480
8567
|
};
|
|
8481
8568
|
}
|
|
@@ -8702,6 +8789,20 @@ var RealtimeChannel = class {
|
|
|
8702
8789
|
}
|
|
8703
8790
|
};
|
|
8704
8791
|
|
|
8792
|
+
// src/robots.ts
|
|
8793
|
+
function createRobotsApi(http) {
|
|
8794
|
+
return {
|
|
8795
|
+
async list() {
|
|
8796
|
+
const response = await http.request("/api/client/robots", {});
|
|
8797
|
+
return response.robots;
|
|
8798
|
+
},
|
|
8799
|
+
async describe(robotId) {
|
|
8800
|
+
const sheet = await http.request(`/api/robots/${pathSegment(robotId)}/datasheet`, {});
|
|
8801
|
+
return sheet;
|
|
8802
|
+
}
|
|
8803
|
+
};
|
|
8804
|
+
}
|
|
8805
|
+
|
|
8705
8806
|
// src/services.ts
|
|
8706
8807
|
var DEFAULT_RESULT_TIMEOUT_MS = 3e4;
|
|
8707
8808
|
function isTerminal(state) {
|
|
@@ -8876,6 +8977,7 @@ function createClient(options) {
|
|
|
8876
8977
|
const cameras = createCamerasApi(http);
|
|
8877
8978
|
const jobs = createJobsApi(http);
|
|
8878
8979
|
const assets = createAssetsApi(http);
|
|
8980
|
+
const robots = createRobotsApi(http);
|
|
8879
8981
|
if (options.serverKey === void 0) {
|
|
8880
8982
|
const baseLogout = auth.logout.bind(auth);
|
|
8881
8983
|
auth = {
|
|
@@ -8896,6 +8998,7 @@ function createClient(options) {
|
|
|
8896
8998
|
cameras,
|
|
8897
8999
|
jobs,
|
|
8898
9000
|
assets,
|
|
9001
|
+
robots,
|
|
8899
9002
|
close() {
|
|
8900
9003
|
channel.close();
|
|
8901
9004
|
}
|