@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/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.5/node_modules/@fleetless/contracts/dist/common.js
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.5/node_modules/@fleetless/contracts/dist/mcp.js
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.5/node_modules/@fleetless/contracts/dist/protocol.js
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.5/node_modules/@fleetless/contracts/dist/assets.js
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 it has to pre-fetch."
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, which is 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."
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, and the reason two robots sharing a mesh cost one copy. It is exposed because it is the only way a client can tell "this is the same mesh I already have" across robots.'
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. A developer whose URDF names one of the latter is entitled to be told so."
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 is a dead end that sends a developer hunting through a workspace by hand; the references are what they can act on, so the references travel."
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. It is validated rather than ignored, so a caller naming a source that does not exist yet learns that instead of silently getting a bridge sync."
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 actually 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.'
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, which is deliberately separate from what has been transferred. `null` when no bridge is connected \u2014 distinct from `false`, because "no robot is online to ask" and "the robot has no URDF" send a developer to two 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.'
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.5/node_modules/@fleetless/contracts/dist/config.js
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.5/node_modules/@fleetless/contracts/dist/alerts.js
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.5/node_modules/@fleetless/contracts/dist/config.js
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 later. 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.",
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.5/node_modules/@fleetless/contracts/dist/introspection.js
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.5/node_modules/@fleetless/contracts/dist/jobs.js
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: state is observed by slug, and this id is what a cancel names when a caller wants to stop one specific job rather than whatever is running."
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 and is said out loud rather than left reading `running` because nobody contradicted it."
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 it is **adoption time**, not the real start, because the cloud never minted it and has no honest alternative."
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 it is kept so a history page can still render them, and nothing writes it any more."
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, which is the same id the invocation was answered with \u2014 so a caller that kept a job id can find its durable record here later."
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.5/node_modules/@fleetless/contracts/dist/protocol.js
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 otherwise: same
2142
- * token, same version, same frame. But they must end differently — after a
2143
- * dropped connection the running jobs are still running, after a restart
2144
- * their results are gone forever. Asking the bridge to enumerate what it
2145
- * still has settles it without either side guessing: the cloud marks every
2146
- * job it believes is running on this robot and that is *not* named here as
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
- * This deliberately needs no persistence at the bridge. A live process
2150
- * lists its live jobs; a process that just started lists none, because it
2151
- * has none — which is exactly the truth the cloud needs. A breadcrumb file
2152
- * would only add a window in which the crash beat the write.
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; a bridge
2155
- * with no jobs and a bridge that does not report them both mean the cloud
2156
- * has nothing to keep alive.
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.5/node_modules/@fleetless/contracts/dist/config-issues.js
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.5/node_modules/@fleetless/contracts/dist/rest.js
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 was applied. The whole resource comes back, not only the fields that changed."
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 itself, 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."
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. This is 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."
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 itself. A service call is awaited to completion, so there is no job to observe afterwards and no id to hold on to."
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.5/node_modules/@fleetless/contracts/dist/realtime.js
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.5/node_modules/@fleetless/contracts/dist/client-auth.js
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.5/node_modules/@fleetless/contracts/dist/apps.js
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 to disambiguate with, so a collision is refused with `identifier_taken`."
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
- * That is worth saying out loud because it changes what a wrong value costs.
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 they are 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` is a `validation_error` rather than a user with no role. An invitation resolves the role when it is 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."
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: the secret itself is never carried by this shape."
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 answer to the request that created or rotated the key."
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, because no route renames or deletes any role."
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.5/node_modules/@fleetless/contracts/dist/app-users.js
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.5/node_modules/@fleetless/contracts/dist/identity.js
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 was applied. The whole resource comes back, not only the fields that changed."
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 rather than being a filter it applies."
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 is a member of the team and has a tier; the optional version of this field existed only while the org also held people with no console powers to grade, and that pool is gone."
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.5/node_modules/@fleetless/contracts/dist/app-users.js
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]+)*$/, "a provider slug is lowercase and hyphen-separated, starting with a letter");
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 at all. `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.'
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. It is what lets a developer's user list say where an account came from without a second request."
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 at all. 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."
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.5/node_modules/@fleetless/contracts/dist/client-auth.js
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, as its globally unique identifier \u2014 the lowercase, underscore-separated string the developer chose when the app was created. There is no organisation context at login, so this is what decides which app the credentials are checked for."
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 a caller asked for is the end state they get."
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 chose this name about 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."
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` when its registration is gone and there is no longer anything to have named. **Unverified** \u2014 see `client_name_verified`."
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 chose this name about 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."
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 rather than left to be inferred from which id happens to be set."
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.5/node_modules/@fleetless/contracts/dist/realtime.js
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
- * It is **not** the robot's own words. Nothing sanitises
4268
- * `bridgeCameraState.error.message`, and a camera password reaches a
4269
- * developer surface through exactly that route — which is why the cloud maps
4270
- * a robot's diagnosis to fixed strings rather than forwarding it.
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.5/node_modules/@fleetless/contracts/dist/audit.js
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 auditActor = z15.object({
4362
- kind: z15.enum(["developer", "end_user", "app_user", "server_key", "bridge"]),
4363
- id: z15.uuid(),
4364
- label: z15.string().min(1).max(200)
4365
- });
4366
- var auditEvent = z15.object({
4367
- id: z15.uuid(),
4368
- org_id: z15.uuid(),
4369
- at: z15.iso.datetime(),
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: z15.number().int().positive(),
4402
+ seq: z16.number().int().positive(),
4389
4403
  actor: auditActor,
4390
4404
  /** Stable dotted name, e.g. `app_user.login`, `config.published`. */
4391
- action: z15.string().min(1).max(80),
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: z15.object({
4397
- kind: z15.string().min(1).max(40),
4398
- id: z15.string().min(1),
4399
- label: z15.string().min(1).max(200)
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: z15.record(z15.string(), z15.unknown()).nullable()
4427
+ details: z16.record(z16.string(), z16.unknown()).nullable()
4414
4428
  });
4415
4429
  var auditTimestampMs = wireTimestampMs;
4416
- var auditQuery = z15.object({
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: z15.union([z15.string().regex(/^\d{1,4}$/), z15.number().int()]).transform((v) => Number(v)).pipe(z15.number().int().positive().max(500)).optional(),
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: z15.string().min(1).max(80).optional(),
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 sentence on
4433
- * `action` above — a filter that matches more than it says is not one —
4434
- * still stands; this is a different question with a name that says which
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 anyone should have to guess the meaning of.
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: z15.string().min(1).max(80).optional(),
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: z15.uuid().optional(),
4470
+ actor_id: z16.uuid().optional(),
4458
4471
  /** Only events about this kind of target, e.g. `robot`. */
4459
- target_kind: z15.string().min(1).max(40).optional(),
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 = z15.object({
4476
- events: z15.array(auditEvent),
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: z15.number().int().positive().nullable()
4500
+ next_cursor: z16.number().int().positive().nullable()
4488
4501
  });
4489
4502
 
4490
- // node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/errors.js
4491
- import { z as z16 } from "zod";
4492
- var apiError = z16.object({
4493
- code: z16.string().min(1),
4494
- message: z16.string().min(1),
4495
- details: z16.unknown().optional()
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 = z16.object({
4498
- field: z16.string().min(1),
4510
+ var parameterViolation = z17.object({
4511
+ field: z17.string().min(1),
4499
4512
  /** Which rule failed — `min`, `max`, `enum`, `pattern`, `required`, `undeclared`. */
4500
- rule: z16.string().min(1),
4501
- message: z16.string().min(1)
4513
+ rule: z17.string().min(1),
4514
+ message: z17.string().min(1)
4502
4515
  });
4503
- var parameterInvalidDetails = z16.object({
4504
- violations: z16.array(parameterViolation).min(1)
4516
+ var parameterInvalidDetails = z17.object({
4517
+ violations: z17.array(parameterViolation).min(1)
4505
4518
  });
4506
4519
 
4507
- // node_modules/.pnpm/@fleetless+contracts@1.0.5/node_modules/@fleetless/contracts/dist/oauth.js
4508
- import { z as z17 } from "zod";
4509
- var oauthErrorCode = z17.enum([
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 = z17.object({
4535
+ var oauthError = z18.object({
4523
4536
  error: oauthErrorCode,
4524
- error_description: z17.string().min(1).max(500).optional(),
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: z17.string().min(1).max(500).optional(),
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: z17.string().min(1).max(60).optional()
4557
+ fleetless_code: z18.string().min(1).max(60).optional()
4545
4558
  });
4546
- var redirectUri = z17.string().min(1).max(2e3).refine((v) => {
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 = z17.enum(["S256"]);
4574
+ var codeChallengeMethod = z18.enum(["S256"]);
4562
4575
  var MCP_DCR_MAX_REDIRECT_URIS = 5;
4563
- var dynamicClientRegistrationRequest = z17.object({
4564
- redirect_uris: z17.array(redirectUri).min(1).max(MCP_DCR_MAX_REDIRECT_URIS).meta({
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. There must be 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.`
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: z17.string().min(1).max(200).optional().meta({
4568
- description: `The name the client calls itself. Optional \u2014 a registration without one is recorded under a default name, per RFC 7591's making every metadata field optional. 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"*.`
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: z17.enum(["none"]).optional().meta({
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: z17.array(z17.enum(["authorization_code", "refresh_token"])).optional().meta({
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: z17.array(z17.enum(["code"])).optional().meta({
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: z17.string().max(500).optional().meta({
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 = z17.object({
4586
- client_id: z17.string().min(1).max(200).meta({
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: z17.string().min(1).max(200).meta({
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: z17.array(redirectUri).meta({
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: z17.array(z17.string()).meta({
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: z17.array(z17.string()).meta({
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: z17.literal("none").meta({
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: z17.number().int().nonnegative().meta({
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: z17.literal(0).meta({
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 = z17.object({
4612
- grant_type: z17.literal("authorization_code").meta({
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: z17.string().min(1).max(500).meta({
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: z17.string().min(1).max(200).meta({
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: z17.string().regex(/^[A-Za-z0-9\-._~]{43,128}$/, "code_verifier must be 43-128 unreserved characters (RFC 7636 \xA74.1)").meta({
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: z17.url().optional().meta({
4628
- description: "The resource the token is being 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."
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 = z17.object({
4634
- access_token: z17.string().min(1).meta({
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: z17.literal("Bearer").meta({
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: z17.number().int().positive().meta({
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: z17.string().min(1).optional().meta({
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: z17.string().max(500).optional().meta({
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 = z17.object({
4651
- issuer: z17.url().meta({
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: z17.url().meta({
4655
- description: "The URL a client sends the user to in order to authorize."
4667
+ authorization_endpoint: z18.url().meta({
4668
+ description: "Where a client sends the user to authorize."
4656
4669
  }),
4657
- token_endpoint: z17.url().meta({
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: z17.url().optional().meta({
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: z17.array(z17.literal("code")).meta({
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: z17.array(z17.enum(["authorization_code", "refresh_token"])).meta({
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: z17.array(codeChallengeMethod).meta({
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: z17.array(z17.literal("none")).meta({
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: z17.array(z17.string()).optional().meta({
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 = z17.object({
4680
- resource: z17.url().meta({
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: z17.array(z17.url()).min(1).meta({
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: z17.array(z17.literal("header")).meta({
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: z17.array(z17.string()).optional().meta({
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 = z17.object({
4694
- redirect_to: z17.string().min(1).max(2e3)
4706
+ var oauthRedirectResponse = z18.object({
4707
+ redirect_to: z18.string().min(1).max(2e3)
4695
4708
  });
4696
- var oauthAuthorizeQuery = z17.object({
4697
- response_type: z17.literal("code").meta({
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: z17.string().min(1).meta({
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: z17.string().min(1).meta({
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: z17.string().min(1).meta({
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 actually has to match."
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: z17.literal("S256").meta({
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: z17.string().optional().meta({
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: z17.string().optional().meta({
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.5/node_modules/@fleetless/contracts/dist/routes.js
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 it is the identifier the dynamic registration issued." }
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 it is the identifier the dynamic registration issued." }],
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; the cloud already knows which kind it is." }
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
  }