@fleetless/sdk 4.3.0 → 4.4.0-next.1

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.cjs CHANGED
@@ -275,7 +275,7 @@ function isRenderKind(kind) {
275
275
  }
276
276
  }
277
277
  }
278
- async function forEachWithConcurrency(items, limit, fn) {
278
+ async function forEachWithConcurrency(items, limit2, fn) {
279
279
  let next = 0;
280
280
  let failed = false;
281
281
  let firstError;
@@ -293,7 +293,7 @@ async function forEachWithConcurrency(items, limit, fn) {
293
293
  }
294
294
  }
295
295
  }
296
- await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => worker()));
296
+ await Promise.all(Array.from({ length: Math.min(limit2, items.length) }, () => worker()));
297
297
  if (failed) throw firstError;
298
298
  }
299
299
  function createAssetsApi(http) {
@@ -442,7 +442,7 @@ function createAssetsApi(http) {
442
442
  };
443
443
  }
444
444
 
445
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/common.js
445
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/common.js
446
446
  var import_zod = require("zod");
447
447
  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.";
448
448
  var slug = import_zod.z.string().min(2).max(63).regex(/^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/, SLUG_RULE);
@@ -473,7 +473,7 @@ var applyError = import_zod.z.object({
473
473
  details: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
474
474
  });
475
475
 
476
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/mcp.js
476
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/mcp.js
477
477
  var import_zod2 = require("zod");
478
478
  function mcpAppEndpointPath(appIdentifier2) {
479
479
  return `/mcp/${appIdentifier2}`;
@@ -522,10 +522,10 @@ var mcpRolePreviewResponse = import_zod2.z.object({
522
522
  });
523
523
  var MCP_ASSET_LINK_TTL_MS = 15 * 60 * 1e3;
524
524
 
525
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/protocol.js
525
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/protocol.js
526
526
  var import_zod8 = require("zod");
527
527
 
528
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/assets.js
528
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/assets.js
529
529
  var import_zod3 = require("zod");
530
530
  var assetKind = import_zod3.z.enum(["urdf", "mesh", "texture"]);
531
531
  var asset = import_zod3.z.object({
@@ -845,10 +845,10 @@ var assetSyncBusyDetails = import_zod3.z.object({
845
845
  started_at_ms: import_zod3.z.number().int().nonnegative()
846
846
  });
847
847
 
848
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/config.js
848
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/config.js
849
849
  var import_zod5 = require("zod");
850
850
 
851
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/alerts.js
851
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/alerts.js
852
852
  var import_zod4 = require("zod");
853
853
  var alertRowCondition = import_zod4.z.discriminatedUnion("kind", [
854
854
  import_zod4.z.strictObject({
@@ -918,7 +918,7 @@ var putDatapointDisplayRequest = import_zod4.z.object({
918
918
  y_max: import_zod4.z.number().finite().nullable()
919
919
  }).strict();
920
920
 
921
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/config.js
921
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/config.js
922
922
  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:`.";
923
923
  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:`.";
924
924
  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.";
@@ -2010,7 +2010,7 @@ var configState = import_zod5.z.object({
2010
2010
  applied_errors: import_zod5.z.array(applyError).nullable()
2011
2011
  });
2012
2012
 
2013
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/introspection.js
2013
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/introspection.js
2014
2014
  var import_zod6 = require("zod");
2015
2015
  var rosGraphEntry = import_zod6.z.object({
2016
2016
  name: rosName,
@@ -2049,7 +2049,7 @@ var typeDefinition = import_zod6.z.discriminatedUnion("kind", [
2049
2049
  })
2050
2050
  ]);
2051
2051
 
2052
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/jobs.js
2052
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/jobs.js
2053
2053
  var import_zod7 = require("zod");
2054
2054
  var jobState = import_zod7.z.enum(["running", "unknown", "succeeded", "failed", "cancelled", "lost"]);
2055
2055
  var reportedJobState = jobState.exclude(["unknown"]);
@@ -2286,7 +2286,7 @@ var jobRunSummary = import_zod7.z.object({
2286
2286
  since_ms: import_zod7.z.number().int().nonnegative()
2287
2287
  });
2288
2288
 
2289
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/protocol.js
2289
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/protocol.js
2290
2290
  var DAY_MS = 24 * 60 * 60 * 1e3;
2291
2291
  var MAX_PATIENCE_MS = 12e4;
2292
2292
  var MIN_PATIENCE_MS = 1e3;
@@ -2684,11 +2684,11 @@ var bridgeCameraState = import_zod8.z.object({
2684
2684
  request_id: import_zod8.z.string().min(1).max(64).nullable()
2685
2685
  });
2686
2686
 
2687
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/config-issues.js
2687
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/config-issues.js
2688
2688
  var EXPOSURE_SECTIONS = ["datapoints", "actions", "services", "publishers", "cameras"];
2689
2689
  var EXPOSURE_SECTION_NAMES = new Set(EXPOSURE_SECTIONS);
2690
2690
 
2691
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/rest.js
2691
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/rest.js
2692
2692
  var import_zod9 = require("zod");
2693
2693
  var robot = import_zod9.z.object({
2694
2694
  id: import_zod9.z.uuid().meta({
@@ -3453,13 +3453,13 @@ var slugUsageResponse = import_zod9.z.object({
3453
3453
  alert_count: import_zod9.z.number().int().nonnegative()
3454
3454
  });
3455
3455
 
3456
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/realtime.js
3456
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/realtime.js
3457
3457
  var import_zod14 = require("zod");
3458
3458
 
3459
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/client-auth.js
3459
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/client-auth.js
3460
3460
  var import_zod13 = require("zod");
3461
3461
 
3462
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/apps.js
3462
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/apps.js
3463
3463
  var import_zod10 = require("zod");
3464
3464
  var appIdentifier = slug;
3465
3465
  var app = import_zod10.z.object({
@@ -3682,10 +3682,10 @@ var rolePermissions = import_zod10.z.object({
3682
3682
  })
3683
3683
  });
3684
3684
 
3685
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/app-users.js
3685
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/app-users.js
3686
3686
  var import_zod12 = require("zod");
3687
3687
 
3688
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/identity.js
3688
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/identity.js
3689
3689
  var import_zod11 = require("zod");
3690
3690
  var password = import_zod11.z.string().min(12).max(256);
3691
3691
  var USER_DISPLAY_NAME_MAX = 120;
@@ -3903,7 +3903,7 @@ var recoveryCodesResponse = import_zod11.z.object({
3903
3903
  recovery_codes: recoveryCodesList.meta({ description: "The ten new recovery codes, lowercase, shown once. Every earlier code is void." })
3904
3904
  });
3905
3905
 
3906
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/app-users.js
3906
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/app-users.js
3907
3907
  var APP_USER_DISPLAY_NAME_MAX = 120;
3908
3908
  var providerSlug = import_zod12.z.string().max(40).regex(/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/, "must be lowercase and hyphen-separated, starting with a letter");
3909
3909
  var appUserStatus = import_zod12.z.enum(["pending_verification", "active", "blocked"]);
@@ -4214,7 +4214,7 @@ var mailOutcome = import_zod12.z.object({
4214
4214
  })
4215
4215
  });
4216
4216
 
4217
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/client-auth.js
4217
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/client-auth.js
4218
4218
  var clientLoginRequest = import_zod13.z.object({
4219
4219
  app_identifier: appIdentifier.meta({
4220
4220
  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."
@@ -4390,7 +4390,8 @@ var clientOidcErrorCode = import_zod13.z.enum([
4390
4390
  "provider_misconfigured",
4391
4391
  "provider_disabled",
4392
4392
  "invalid_request",
4393
- "quota_exceeded"
4393
+ "quota_exceeded",
4394
+ "plan_limit"
4394
4395
  ]);
4395
4396
  var clientMcpInteraction = import_zod13.z.object({
4396
4397
  id: import_zod13.z.string().meta({ description: "The interaction, as it arrived in the app's `mcp_login_url`. Not a credential: it names a pending request the server already holds, and approving it needs the app user's own access token." }),
@@ -4454,7 +4455,7 @@ var clientIdentity = import_zod13.z.object({
4454
4455
  })
4455
4456
  });
4456
4457
 
4457
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/realtime.js
4458
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/realtime.js
4458
4459
  var clientAuth = import_zod14.z.object({
4459
4460
  type: import_zod14.z.literal("auth"),
4460
4461
  token: import_zod14.z.string().min(1)
@@ -4622,6 +4623,12 @@ var liveSessionEndReason = import_zod14.z.enum([
4622
4623
  "expired",
4623
4624
  /** The robot was deleted out from under the session. */
4624
4625
  "robot_deleted",
4626
+ /**
4627
+ * The organization's live video for app users reached its plan's monthly
4628
+ * limit (2026-10-02, fleetless/fleetless#103); `detail` carries the
4629
+ * sentence the viewer shows.
4630
+ */
4631
+ "plan_limit",
4625
4632
  /**
4626
4633
  * The cloud ended it and cannot say which of the above applied. **Kept
4627
4634
  * deliberately**: a channel that cannot say "I do not know" will say
@@ -4732,7 +4739,7 @@ var orgEventDropped = import_zod14.z.object({
4732
4739
  dropped: import_zod14.z.number().int().positive()
4733
4740
  }).strict();
4734
4741
 
4735
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/feedback.js
4742
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/feedback.js
4736
4743
  var import_zod15 = require("zod");
4737
4744
  var FEEDBACK_KINDS = ["idea", "problem", "question", "other"];
4738
4745
  var feedbackKind = import_zod15.z.enum(FEEDBACK_KINDS);
@@ -4757,7 +4764,7 @@ var feedbackResponse = import_zod15.z.object({
4757
4764
  })
4758
4765
  }).strict();
4759
4766
 
4760
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/client-robots.js
4767
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/client-robots.js
4761
4768
  var import_zod16 = require("zod");
4762
4769
  var clientRobotListItem = import_zod16.z.object({
4763
4770
  ...robot.shape,
@@ -4774,10 +4781,10 @@ var clientRobotListResponse = import_zod16.z.object({
4774
4781
  })
4775
4782
  });
4776
4783
 
4777
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/audit.js
4784
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/audit.js
4778
4785
  var import_zod17 = require("zod");
4779
4786
  var auditActor = import_zod17.z.object({
4780
- kind: import_zod17.z.enum(["developer", "end_user", "app_user", "server_key", "bridge"]),
4787
+ kind: import_zod17.z.enum(["developer", "end_user", "app_user", "server_key", "bridge", "fleetless"]),
4781
4788
  id: import_zod17.z.uuid(),
4782
4789
  label: import_zod17.z.string().min(1).max(200)
4783
4790
  });
@@ -4916,34 +4923,358 @@ var auditListResponse = import_zod17.z.object({
4916
4923
  next_cursor: import_zod17.z.number().int().positive().nullable()
4917
4924
  });
4918
4925
 
4919
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/errors.js
4926
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/errors.js
4927
+ var import_zod19 = require("zod");
4928
+
4929
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/plans.js
4920
4930
  var import_zod18 = require("zod");
4921
- var apiError = import_zod18.z.object({
4922
- code: import_zod18.z.string().min(1),
4923
- message: import_zod18.z.string().min(1),
4924
- details: import_zod18.z.unknown().optional()
4931
+ var planId = import_zod18.z.enum(["basic", "plus", "pro", "enterprise"]);
4932
+ var planLimitKey = import_zod18.z.enum([
4933
+ "seats",
4934
+ "robots",
4935
+ "apps",
4936
+ "app_users",
4937
+ "live_video_ms_per_month",
4938
+ "asset_bytes_per_robot",
4939
+ "history_days",
4940
+ "audit_days"
4941
+ ]);
4942
+ var limit = import_zod18.z.number().int().positive().nullable();
4943
+ var planLimits = import_zod18.z.object({
4944
+ seats: limit.meta({ description: "Developers, owners included, plus pending team invitations." }),
4945
+ robots: limit.meta({ description: "Robots in the org." }),
4946
+ apps: limit.meta({ description: "Apps in the org." }),
4947
+ app_users: limit.meta({ description: "App users across every app of the org, plus pending app-user invitations." }),
4948
+ live_video_ms_per_month: limit.meta({
4949
+ description: "Live video watched by app users in one UTC calendar month, in milliseconds, across the org. Console sessions do not count."
4950
+ }),
4951
+ asset_bytes_per_robot: limit.meta({
4952
+ description: "Asset storage per robot, in bytes (decimal: 1 GB = 1,000,000,000). The org stores up to robots \xD7 this value in total."
4953
+ }),
4954
+ history_days: limit.meta({ description: "Days the org's robot history is kept." }),
4955
+ audit_days: limit.meta({ description: "Days the org's audit log is kept." })
4956
+ });
4957
+ var planFeature = import_zod18.z.enum(["app_mcp", "two_factor", "require_two_factor", "app_oidc", "hosted_logo", "audit_export", "addons"]);
4958
+ var FEATURE_DESCRIPTIONS = {
4959
+ app_mcp: "An app's own MCP endpoint, for its app users.",
4960
+ two_factor: "Two-factor sign-in for developers.",
4961
+ require_two_factor: "An owner may require two-factor sign-in for every developer of the org.",
4962
+ app_oidc: "An app may let its users sign in through an OpenID Connect identity provider.",
4963
+ hosted_logo: "An app's hosted pages show its logo and accent colour, not only its name.",
4964
+ audit_export: "The audit log can be exported as CSV.",
4965
+ addons: "Add-ons can be bought on top of the plan."
4966
+ };
4967
+ var planFeatures = import_zod18.z.object(Object.fromEntries(planFeature.options.map((f) => [f, import_zod18.z.boolean().meta({ description: FEATURE_DESCRIPTIONS[f] })])));
4968
+ var cents = import_zod18.z.number().int().nonnegative();
4969
+ var planPrices = import_zod18.z.object({
4970
+ eur_month: cents.meta({ description: "Per month in euro cents, excluding VAT." }),
4971
+ usd_month: cents.meta({ description: "Per month in US dollar cents, excluding VAT: the euro price \xD7 1.15, rounded up to a whole dollar." }),
4972
+ eur_year: cents.meta({ description: "Per year in euro cents, excluding VAT: twelve months less 15 %." }),
4973
+ usd_year: cents.meta({ description: "Per year in US dollar cents, excluding VAT: the yearly euro price \xD7 1.15, rounded up to a whole dollar." })
4974
+ });
4975
+ var planSupport = import_zod18.z.enum(["community", "email", "priority", "named_contact"]);
4976
+ var planCatalogueEntry = import_zod18.z.object({
4977
+ id: planId.meta({ description: "The plan." }),
4978
+ name: import_zod18.z.string().min(1).meta({ description: "The plan's display name: Basic, Plus, Pro or Enterprise." }),
4979
+ limits: planLimits.meta({ description: "What the plan allows. `null` means by contract." }),
4980
+ features: planFeatures.meta({ description: "What the plan unlocks." }),
4981
+ prices: planPrices.nullable().meta({ description: "`null` for Enterprise: sold by contract, on request." }),
4982
+ support: planSupport.meta({ description: "The support that comes with the plan." })
4983
+ });
4984
+ var addonKey = import_zod18.z.enum(["seats", "robots", "apps", "app_user_packs", "live_video_packs"]);
4985
+ var addonCatalogueEntry = import_zod18.z.object({
4986
+ key: addonKey.meta({ description: "The add-on." }),
4987
+ raises: planLimitKey.meta({ description: "The plan limit this add-on raises." }),
4988
+ per_unit: import_zod18.z.number().int().positive().meta({ description: "How much one unit of this add-on raises `raises` by." }),
4989
+ prices: planPrices.meta({ description: "The price of one unit." })
4990
+ });
4991
+ function usdCentsFromEurCents(eurCents) {
4992
+ return Math.floor((eurCents * 115 + 9999) / 1e4) * 100;
4993
+ }
4994
+ function yearlyEurCents(monthEurCents) {
4995
+ return Math.round(monthEurCents * 12 * 85 / 100);
4996
+ }
4997
+ function pricesFromEurMonth(eurMonthCents) {
4998
+ const eurYear = yearlyEurCents(eurMonthCents);
4999
+ return {
5000
+ eur_month: eurMonthCents,
5001
+ usd_month: usdCentsFromEurCents(eurMonthCents),
5002
+ eur_year: eurYear,
5003
+ usd_year: usdCentsFromEurCents(eurYear)
5004
+ };
5005
+ }
5006
+ var PLANS = {
5007
+ basic: {
5008
+ id: "basic",
5009
+ name: "Basic",
5010
+ limits: {
5011
+ seats: 1,
5012
+ robots: 1,
5013
+ apps: 1,
5014
+ app_users: 10,
5015
+ live_video_ms_per_month: 36e6,
5016
+ asset_bytes_per_robot: 1e9,
5017
+ history_days: 7,
5018
+ audit_days: 7
5019
+ },
5020
+ features: {
5021
+ app_mcp: false,
5022
+ two_factor: false,
5023
+ require_two_factor: false,
5024
+ app_oidc: false,
5025
+ hosted_logo: false,
5026
+ audit_export: false,
5027
+ addons: false
5028
+ },
5029
+ prices: pricesFromEurMonth(0),
5030
+ support: "community"
5031
+ },
5032
+ plus: {
5033
+ id: "plus",
5034
+ name: "Plus",
5035
+ limits: {
5036
+ seats: 3,
5037
+ robots: 3,
5038
+ apps: 3,
5039
+ app_users: 25,
5040
+ live_video_ms_per_month: 36e7,
5041
+ asset_bytes_per_robot: 2e9,
5042
+ history_days: 30,
5043
+ audit_days: 30
5044
+ },
5045
+ features: {
5046
+ app_mcp: true,
5047
+ two_factor: true,
5048
+ require_two_factor: false,
5049
+ app_oidc: true,
5050
+ hosted_logo: true,
5051
+ audit_export: false,
5052
+ addons: false
5053
+ },
5054
+ prices: pricesFromEurMonth(2900),
5055
+ support: "email"
5056
+ },
5057
+ pro: {
5058
+ id: "pro",
5059
+ name: "Pro",
5060
+ limits: {
5061
+ seats: 5,
5062
+ robots: 5,
5063
+ apps: 5,
5064
+ app_users: 50,
5065
+ live_video_ms_per_month: 9e8,
5066
+ asset_bytes_per_robot: 3e9,
5067
+ history_days: 90,
5068
+ audit_days: 90
5069
+ },
5070
+ features: {
5071
+ app_mcp: true,
5072
+ two_factor: true,
5073
+ require_two_factor: true,
5074
+ app_oidc: true,
5075
+ hosted_logo: true,
5076
+ audit_export: true,
5077
+ addons: true
5078
+ },
5079
+ prices: pricesFromEurMonth(14900),
5080
+ support: "priority"
5081
+ },
5082
+ enterprise: {
5083
+ id: "enterprise",
5084
+ name: "Enterprise",
5085
+ limits: {
5086
+ seats: null,
5087
+ robots: null,
5088
+ apps: null,
5089
+ app_users: null,
5090
+ live_video_ms_per_month: null,
5091
+ asset_bytes_per_robot: null,
5092
+ history_days: null,
5093
+ audit_days: null
5094
+ },
5095
+ features: {
5096
+ app_mcp: true,
5097
+ two_factor: true,
5098
+ require_two_factor: true,
5099
+ app_oidc: true,
5100
+ hosted_logo: true,
5101
+ audit_export: true,
5102
+ addons: true
5103
+ },
5104
+ prices: null,
5105
+ support: "named_contact"
5106
+ }
5107
+ };
5108
+ var ADDONS = {
5109
+ seats: { key: "seats", raises: "seats", per_unit: 1, prices: pricesFromEurMonth(900) },
5110
+ robots: { key: "robots", raises: "robots", per_unit: 1, prices: pricesFromEurMonth(1900) },
5111
+ apps: { key: "apps", raises: "apps", per_unit: 1, prices: pricesFromEurMonth(900) },
5112
+ app_user_packs: { key: "app_user_packs", raises: "app_users", per_unit: 5, prices: pricesFromEurMonth(1e3) },
5113
+ live_video_packs: { key: "live_video_packs", raises: "live_video_ms_per_month", per_unit: 9e8, prices: pricesFromEurMonth(900) }
5114
+ };
5115
+ var planCurrency = import_zod18.z.enum(["eur", "usd"]);
5116
+ var addonCount = import_zod18.z.number().int().nonnegative();
5117
+ var orgAddons = import_zod18.z.object({
5118
+ seats: addonCount.meta({ description: "Extra developer seats, one each." }),
5119
+ robots: addonCount.meta({ description: "Extra robots, one each." }),
5120
+ apps: addonCount.meta({ description: "Extra apps, one each." }),
5121
+ app_user_packs: addonCount.meta({ description: "Packs of five extra app users." }),
5122
+ live_video_packs: addonCount.meta({ description: "Packs of 250 extra hours of app-user live video per month." })
5123
+ });
5124
+ var usageCount = import_zod18.z.number().int().nonnegative();
5125
+ var orgPlanUsage = import_zod18.z.object({
5126
+ seats: usageCount.meta({ description: "Developers, owners included, plus pending team invitations." }),
5127
+ robots: usageCount.meta({ description: "Robots in the org." }),
5128
+ apps: usageCount.meta({ description: "Apps in the org." }),
5129
+ app_users: usageCount.meta({ description: "App users across every app, plus pending app-user invitations." }),
5130
+ live_video_ms_this_month: usageCount.meta({
5131
+ description: "Live video watched by app users in the current UTC calendar month, in milliseconds. Console sessions do not count."
5132
+ }),
5133
+ asset_bytes: usageCount.meta({ description: "Bytes the assets of every robot of the org occupy, together." })
5134
+ });
5135
+ var planChangeKeep = import_zod18.z.object({
5136
+ robots: import_zod18.z.array(import_zod18.z.uuid()).meta({ description: "The robots that stay, by id. Every other robot is deleted when the change takes effect." }),
5137
+ apps: import_zod18.z.array(import_zod18.z.uuid()).meta({ description: "The apps that stay, by id. Every other app is deleted when the change takes effect." }),
5138
+ app_users: import_zod18.z.array(import_zod18.z.uuid()).meta({
5139
+ description: "The app users that stay, by id, across every app. Every other app user is deleted when the change takes effect."
5140
+ }),
5141
+ developers: import_zod18.z.array(import_zod18.z.uuid()).meta({
5142
+ description: "The developers that stay, by user id, owners never among them: every owner stays. Every other developer is removed from the org when the change takes effect."
5143
+ })
5144
+ }).strict();
5145
+ var planChangeReason = import_zod18.z.enum(["downgrade", "cancel", "migration", "lock"]);
5146
+ var pendingPlanChange = import_zod18.z.object({
5147
+ target_plan: planId.meta({ description: "The plan the org moves to." }),
5148
+ reason: planChangeReason.meta({ description: "Why the change is pending: `downgrade`, `cancel`, `migration` or `lock`." }),
5149
+ effective_at: import_zod18.z.iso.datetime().nullable().meta({
5150
+ description: "When the change takes effect. `null` only for a move off the beta whose date is not set yet."
5151
+ }),
5152
+ keep: planChangeKeep.nullable().meta({
5153
+ description: "What stays. `null` when the org already fits the target plan and nothing is deleted."
5154
+ }),
5155
+ history_days_after: import_zod18.z.number().int().positive().meta({
5156
+ description: "Days of history and audit log the org keeps on the target plan."
5157
+ }),
5158
+ chosen_by: import_zod18.z.uuid().meta({ description: "The user id of the owner who chose the change, or the nil UUID when Fleetless queued it." }),
5159
+ chosen_at: import_zod18.z.iso.datetime().meta({ description: "When the change was chosen." })
5160
+ });
5161
+ var orgLock = import_zod18.z.object({
5162
+ reason: import_zod18.z.enum(["payment", "migration"]).meta({
5163
+ description: "`payment`: a payment is missing. `migration`: the org did not choose what stays when the beta ended."
5164
+ }),
5165
+ since: import_zod18.z.iso.datetime().meta({ description: "When the org was locked." })
5166
+ });
5167
+ var orgPlan = import_zod18.z.object({
5168
+ plan: planId.meta({ description: "The org's current plan." }),
5169
+ currency: planCurrency.meta({ description: "The currency the org's prices are shown and billed in." }),
5170
+ period_ends_at: import_zod18.z.iso.datetime().meta({
5171
+ description: "The end of the organization's current billing period; while no payment period exists yet, the end of the current UTC calendar month. A pending downward plan change normally takes effect at this exact instant \u2014 except one chosen while the org was locked, which lands at once, and the platform's own move off the beta, which lands at its switch date instead."
5172
+ }),
5173
+ addons: orgAddons.meta({ description: "The add-ons the org has bought. All zero on a plan without the `addons` feature." }),
5174
+ limits: planLimits.meta({
5175
+ description: "The org's effective limits: the plan's, raised by its add-ons, or an operator's override in their place. `null` means unlimited for a count, and 90 days for `history_days` and `audit_days`."
5176
+ }),
5177
+ features: planFeatures.meta({ description: "What the org's plan unlocks." }),
5178
+ usage: orgPlanUsage.meta({ description: "What the org uses now, counted the way each limit counts it." }),
5179
+ pending_change: pendingPlanChange.nullable().meta({ description: "A move to a lower plan that has not taken effect yet, or `null`." }),
5180
+ lock: orgLock.nullable().meta({ description: "Why and since when the org is locked, or `null` when it is not." }),
5181
+ switch: import_zod18.z.object({
5182
+ at: import_zod18.z.iso.datetime().nullable().meta({
5183
+ description: "When the org moves from the beta onto its plan. `null` while the date is not set."
5184
+ }),
5185
+ needs_choice: import_zod18.z.boolean().meta({
5186
+ description: "Whether the org uses more than Basic allows, so an owner has to choose what stays before `at`."
5187
+ })
5188
+ }).nullable().meta({ description: "Set only while the org is still on the beta; `null` for every other org." })
4925
5189
  });
4926
- var parameterViolation = import_zod18.z.object({
4927
- field: import_zod18.z.string().min(1),
5190
+ var planChangeRequest = import_zod18.z.object({
5191
+ target_plan: planId.meta({ description: "The lower plan to move to; `basic` cancels." }),
5192
+ keep: planChangeKeep.nullable().meta({
5193
+ description: "What stays. `null` when the org already fits the target plan, so nothing is deleted."
5194
+ })
5195
+ }).strict();
5196
+ var planOverrides = import_zod18.z.object(Object.fromEntries(planLimitKey.options.map((k) => [
5197
+ k,
5198
+ limit.optional().meta({ description: `Replaces the plan's \`${k}\`. \`null\` clears the override.` })
5199
+ ]))).strict();
5200
+ var adminPlanChangeRequest = import_zod18.z.object({
5201
+ plan: planId.meta({ description: "The plan the org is on after this request." }),
5202
+ addons: orgAddons.partial().strict().optional().meta({ description: "Add-on counts to set. Counts not named stay as they are." }),
5203
+ overrides: planOverrides.optional().meta({ description: "Limits to override. `null` clears an override." }),
5204
+ currency: planCurrency.optional().meta({ description: "The currency the org is billed in." }),
5205
+ period_ends_at: import_zod18.z.iso.datetime().nullable().optional().meta({
5206
+ description: "The end of the org's billing period. `null` falls back to the end of the current UTC month."
5207
+ })
5208
+ }).strict();
5209
+
5210
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/errors.js
5211
+ var apiError = import_zod19.z.object({
5212
+ code: import_zod19.z.string().min(1),
5213
+ message: import_zod19.z.string().min(1),
5214
+ details: import_zod19.z.unknown().optional()
5215
+ });
5216
+ var parameterViolation = import_zod19.z.object({
5217
+ field: import_zod19.z.string().min(1),
4928
5218
  /** Which rule failed — `min`, `max`, `enum`, `pattern`, `required`, `undeclared`. */
4929
- rule: import_zod18.z.string().min(1),
4930
- message: import_zod18.z.string().min(1)
5219
+ rule: import_zod19.z.string().min(1),
5220
+ message: import_zod19.z.string().min(1)
4931
5221
  });
4932
- var parameterInvalidDetails = import_zod18.z.object({
4933
- violations: import_zod18.z.array(parameterViolation).min(1)
5222
+ var parameterInvalidDetails = import_zod19.z.object({
5223
+ violations: import_zod19.z.array(parameterViolation).min(1)
4934
5224
  });
4935
- var cancelRejectedDetails = import_zod18.z.object({
4936
- goals: import_zod18.z.array(bridgeCancelResultEntry).min(1)
5225
+ var cancelRejectedDetails = import_zod19.z.object({
5226
+ goals: import_zod19.z.array(bridgeCancelResultEntry).min(1)
4937
5227
  });
4938
- var invalidCodeDetails = import_zod18.z.object({
4939
- attempts_left: import_zod18.z.number().int().min(0).meta({
5228
+ var invalidCodeDetails = import_zod19.z.object({
5229
+ attempts_left: import_zod19.z.number().int().min(0).meta({
4940
5230
  description: "How many more wrong codes this code or challenge takes before it is spent. `0` means the next attempt answers `410 token_spent`."
4941
5231
  })
4942
5232
  });
5233
+ var planLimitDetails = import_zod19.z.object({
5234
+ limit: planLimitKey.exclude(["history_days", "audit_days"]).meta({ description: "The limit the action would exceed." }),
5235
+ used: import_zod19.z.number().int().nonnegative().meta({ description: "How much of the limit the org uses now." }),
5236
+ max: import_zod19.z.number().int().nonnegative().meta({
5237
+ description: "The limit. For `asset_bytes_per_robot`, the org's whole pool: robots \xD7 the plan's bytes per robot."
5238
+ }),
5239
+ plan: planId.meta({ description: "The plan whose limit refused: the target plan while a move to a lower plan is pending." }),
5240
+ lifted_by: import_zod19.z.object({
5241
+ plan: planId.nullable().meta({ description: "The cheapest higher plan that raises this limit, or `null` when none does." }),
5242
+ addon: addonKey.nullable().meta({
5243
+ description: "The add-on that raises this limit, when the org's plan can buy add-ons; otherwise `null`."
5244
+ })
5245
+ }).meta({ description: "What would lift the limit." })
5246
+ });
5247
+ var assetPlanLimitDetails = planLimitDetails.extend({
5248
+ ...assetStoreRefusedDetails.shape,
5249
+ store_bytes: assetStoreRefusedDetails.shape.store_bytes.meta({
5250
+ description: "The org's asset pool, in bytes: `robots \xD7 asset_bytes_per_robot`."
5251
+ }),
5252
+ used_bytes: assetStoreRefusedDetails.shape.used_bytes.meta({
5253
+ description: "Bytes the org's assets occupy, across every robot, before this upload."
5254
+ })
5255
+ });
5256
+ var planRequiredDetails = import_zod19.z.object({
5257
+ feature: planFeature.meta({ description: "The feature the action needs." }),
5258
+ plan: planId.meta({ description: "The org's current plan." }),
5259
+ required_plan: planId.meta({ description: "The cheapest plan that has the feature." })
5260
+ });
5261
+ var orgLockedDetails = import_zod19.z.object({
5262
+ reason: import_zod19.z.enum(["payment", "migration"]).meta({
5263
+ description: "`payment`: a payment is missing. `migration`: the org did not choose what stays when the beta ended."
5264
+ })
5265
+ });
5266
+ var fileTooLargeDetails = import_zod19.z.object({
5267
+ max_bytes: import_zod19.z.number().int().positive().meta({
5268
+ description: "The most one asset file can be, in bytes: `ASSET_FILE_MAX_BYTES`, the same on every plan."
5269
+ }),
5270
+ size_bytes: import_zod19.z.number().int().positive().nullable().meta({
5271
+ description: "The refused file's size, in bytes: the announced size, or the bytes that arrived when none (or a false one) was announced; `null` only when the body limit stopped the upload and nobody counted the bytes."
5272
+ })
5273
+ });
4943
5274
 
4944
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/oauth.js
4945
- var import_zod19 = require("zod");
4946
- var oauthErrorCode = import_zod19.z.enum([
5275
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/oauth.js
5276
+ var import_zod20 = require("zod");
5277
+ var oauthErrorCode = import_zod20.z.enum([
4947
5278
  "invalid_request",
4948
5279
  "invalid_client",
4949
5280
  "invalid_grant",
@@ -4956,11 +5287,11 @@ var oauthErrorCode = import_zod19.z.enum([
4956
5287
  /** RFC 8707: the `resource` named is not one this server issues tokens for. */
4957
5288
  "invalid_target"
4958
5289
  ]);
4959
- var oauthError = import_zod19.z.object({
5290
+ var oauthError = import_zod20.z.object({
4960
5291
  error: oauthErrorCode,
4961
- error_description: import_zod19.z.string().min(1).max(500).optional(),
5292
+ error_description: import_zod20.z.string().min(1).max(500).optional(),
4962
5293
  /** Echoed back per RFC 6749 §4.1.2.1 so a client can match the response. */
4963
- state: import_zod19.z.string().min(1).max(500).optional(),
5294
+ state: import_zod20.z.string().min(1).max(500).optional(),
4964
5295
  /**
4965
5296
  * **A Fleetless reason carried inside a standard envelope, and it exists
4966
5297
  * because the alternative lost a distinction.**
@@ -4978,9 +5309,9 @@ var oauthError = import_zod19.z.object({
4978
5309
  * our own tooling switches on. RFC 6749 §5.2 permits additional members, and
4979
5310
  * a client that ignores this one still behaves correctly.
4980
5311
  */
4981
- fleetless_code: import_zod19.z.string().min(1).max(60).optional()
5312
+ fleetless_code: import_zod20.z.string().min(1).max(60).optional()
4982
5313
  });
4983
- var redirectUri = import_zod19.z.string().min(1).max(2e3).refine((v) => {
5314
+ var redirectUri = import_zod20.z.string().min(1).max(2e3).refine((v) => {
4984
5315
  let url;
4985
5316
  try {
4986
5317
  url = new URL(v);
@@ -4995,180 +5326,180 @@ var redirectUri = import_zod19.z.string().min(1).max(2e3).refine((v) => {
4995
5326
  return ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
4996
5327
  return false;
4997
5328
  }, { message: "redirect_uri must be an https URL, or http on an explicit loopback address, and carry no fragment" });
4998
- var codeChallengeMethod = import_zod19.z.enum(["S256"]);
5329
+ var codeChallengeMethod = import_zod20.z.enum(["S256"]);
4999
5330
  var MCP_DCR_MAX_REDIRECT_URIS = 5;
5000
- var dynamicClientRegistrationRequest = import_zod19.z.object({
5001
- redirect_uris: import_zod19.z.array(redirectUri).min(1).max(MCP_DCR_MAX_REDIRECT_URIS).meta({
5331
+ var dynamicClientRegistrationRequest = import_zod20.z.object({
5332
+ redirect_uris: import_zod20.z.array(redirectUri).min(1).max(MCP_DCR_MAX_REDIRECT_URIS).meta({
5002
5333
  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.`
5003
5334
  }),
5004
- client_name: import_zod19.z.string().min(1).max(200).optional().meta({
5335
+ client_name: import_zod20.z.string().min(1).max(200).optional().meta({
5005
5336
  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"*.'
5006
5337
  }),
5007
- token_endpoint_auth_method: import_zod19.z.enum(["none"]).optional().meta({
5338
+ token_endpoint_auth_method: import_zod20.z.enum(["none"]).optional().meta({
5008
5339
  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."
5009
5340
  }),
5010
- grant_types: import_zod19.z.array(import_zod19.z.enum(["authorization_code", "refresh_token"])).optional().meta({
5341
+ grant_types: import_zod20.z.array(import_zod20.z.enum(["authorization_code", "refresh_token"])).optional().meta({
5011
5342
  description: "Accepted for conformance with RFC 7591 and then **ignored**: both MCP authorization servers grant `authorization_code` and `refresh_token` to every registration, and the answer states what was granted (\xA73.2.1) rather than what was asked."
5012
5343
  }),
5013
- response_types: import_zod19.z.array(import_zod19.z.enum(["code"])).optional().meta({
5344
+ response_types: import_zod20.z.array(import_zod20.z.enum(["code"])).optional().meta({
5014
5345
  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."
5015
5346
  }),
5016
- scope: import_zod19.z.string().max(500).optional().meta({
5347
+ scope: import_zod20.z.string().max(500).optional().meta({
5017
5348
  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."
5018
5349
  })
5019
5350
  }).meta({
5020
5351
  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)."
5021
5352
  });
5022
- var dynamicClientRegistrationResponse = import_zod19.z.object({
5023
- client_id: import_zod19.z.string().min(1).max(200).meta({
5353
+ var dynamicClientRegistrationResponse = import_zod20.z.object({
5354
+ client_id: import_zod20.z.string().min(1).max(200).meta({
5024
5355
  description: "The identifier this client sends at the authorize and token endpoints. Opaque, and not the app identifier."
5025
5356
  }),
5026
- client_name: import_zod19.z.string().min(1).max(200).meta({
5357
+ client_name: import_zod20.z.string().min(1).max(200).meta({
5027
5358
  description: "The name the client registered under, echoed back. Chosen by the client and not vouched for by Fleetless."
5028
5359
  }),
5029
- redirect_uris: import_zod19.z.array(redirectUri).meta({
5360
+ redirect_uris: import_zod20.z.array(redirectUri).meta({
5030
5361
  description: "The redirect URIs this registration was accepted for. A code is returned to one of these and nowhere else."
5031
5362
  }),
5032
- grant_types: import_zod19.z.array(import_zod19.z.string()).meta({
5363
+ grant_types: import_zod20.z.array(import_zod20.z.string()).meta({
5033
5364
  description: 'The grants this client may use. Always exactly `["authorization_code", "refresh_token"]` \u2014 an exchange mints a refresh token and the token endpoint rotates it.'
5034
5365
  }),
5035
- response_types: import_zod19.z.array(import_zod19.z.string()).meta({
5366
+ response_types: import_zod20.z.array(import_zod20.z.string()).meta({
5036
5367
  description: "The response types this client may ask for: `code`."
5037
5368
  }),
5038
- token_endpoint_auth_method: import_zod19.z.literal("none").meta({
5369
+ token_endpoint_auth_method: import_zod20.z.literal("none").meta({
5039
5370
  description: "`none` \u2014 this server registers public clients only, and PKCE rather than a secret is what protects the exchange."
5040
5371
  }),
5041
- client_id_issued_at: import_zod19.z.number().int().nonnegative().meta({
5372
+ client_id_issued_at: import_zod20.z.number().int().nonnegative().meta({
5042
5373
  description: "When the registration was created, in seconds since the epoch, per RFC 7591."
5043
5374
  }),
5044
- client_secret_expires_at: import_zod19.z.literal(0).meta({
5375
+ client_secret_expires_at: import_zod20.z.literal(0).meta({
5045
5376
  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."
5046
5377
  })
5047
5378
  });
5048
- var oauthCodeTokenRequest = import_zod19.z.object({
5049
- grant_type: import_zod19.z.literal("authorization_code").meta({
5379
+ var oauthCodeTokenRequest = import_zod20.z.object({
5380
+ grant_type: import_zod20.z.literal("authorization_code").meta({
5050
5381
  description: "`authorization_code`: this request exchanges the code from the authorize redirect for an access token and a refresh token."
5051
5382
  }),
5052
- code: import_zod19.z.string().min(1).max(500).meta({
5383
+ code: import_zod20.z.string().min(1).max(500).meta({
5053
5384
  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."
5054
5385
  }),
5055
5386
  redirect_uri: redirectUri.meta({
5056
5387
  description: "The same redirect URI the authorize request used. It is compared, not merely recorded."
5057
5388
  }),
5058
- client_id: import_zod19.z.string().min(1).max(200).meta({
5389
+ client_id: import_zod20.z.string().min(1).max(200).meta({
5059
5390
  description: "The client making the exchange, as registered."
5060
5391
  }),
5061
- code_verifier: import_zod19.z.string().regex(/^[A-Za-z0-9\-._~]{43,128}$/, "code_verifier must be 43-128 unreserved characters (RFC 7636 \xA74.1)").meta({
5392
+ code_verifier: import_zod20.z.string().regex(/^[A-Za-z0-9\-._~]{43,128}$/, "code_verifier must be 43-128 unreserved characters (RFC 7636 \xA74.1)").meta({
5062
5393
  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."
5063
5394
  }),
5064
- resource: import_zod19.z.url().optional().meta({
5395
+ resource: import_zod20.z.url().optional().meta({
5065
5396
  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."
5066
5397
  })
5067
5398
  }).meta({
5068
5399
  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."
5069
5400
  });
5070
- var oauthRefreshTokenRequest = import_zod19.z.object({
5071
- grant_type: import_zod19.z.literal("refresh_token").meta({
5401
+ var oauthRefreshTokenRequest = import_zod20.z.object({
5402
+ grant_type: import_zod20.z.literal("refresh_token").meta({
5072
5403
  description: "`refresh_token`: this request rotates a refresh token into a new access token and a new refresh token. The presented token is consumed; presenting it again revokes the whole session."
5073
5404
  }),
5074
- refresh_token: import_zod19.z.string().min(1).max(500).meta({
5405
+ refresh_token: import_zod20.z.string().min(1).max(500).meta({
5075
5406
  description: "The refresh token from the last token response. Bound to the client that received it and to one identity space: presented by another client, or at the other MCP server, it is `invalid_grant` and stays unconsumed."
5076
5407
  }),
5077
- client_id: import_zod19.z.string().min(1).max(200).meta({
5408
+ client_id: import_zod20.z.string().min(1).max(200).meta({
5078
5409
  description: "The client the refresh token was issued to, as registered. A refresh token is not transferable between clients."
5079
5410
  }),
5080
- resource: import_zod19.z.url().optional().meta({
5411
+ resource: import_zod20.z.url().optional().meta({
5081
5412
  description: "The resource the new token is for, per RFC 8707. Optional; when named it must be the audience the session was issued for, or the answer is `invalid_target` and the refresh token is left untouched. The successor carries the same audience either way."
5082
5413
  })
5083
5414
  }).meta({
5084
5415
  description: "RFC 6749 \xA76's refresh, as either MCP authorization server reads it. Every use rotates: the answer carries a new refresh token and the presented one is dead."
5085
5416
  });
5086
- var oauthTokenRequest = import_zod19.z.discriminatedUnion("grant_type", [oauthCodeTokenRequest, oauthRefreshTokenRequest]).meta({
5417
+ var oauthTokenRequest = import_zod20.z.discriminatedUnion("grant_type", [oauthCodeTokenRequest, oauthRefreshTokenRequest]).meta({
5087
5418
  description: "What an MCP token endpoint accepts: the authorization-code exchange, or a refresh. Any other `grant_type` is `unsupported_grant_type`, refused before a lookup happens."
5088
5419
  });
5089
- var oauthTokenResponse = import_zod19.z.object({
5090
- access_token: import_zod19.z.string().min(1).meta({
5420
+ var oauthTokenResponse = import_zod20.z.object({
5421
+ access_token: import_zod20.z.string().min(1).meta({
5091
5422
  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."
5092
5423
  }),
5093
- token_type: import_zod19.z.literal("Bearer").meta({
5424
+ token_type: import_zod20.z.literal("Bearer").meta({
5094
5425
  description: "`Bearer`. RFC 6749 \xA75.1 makes the value case-insensitive for a client reading it; this is the spelling this server emits."
5095
5426
  }),
5096
- expires_in: import_zod19.z.number().int().positive().meta({
5427
+ expires_in: import_zod20.z.number().int().positive().meta({
5097
5428
  description: "How long the access token is valid, in **seconds**, per RFC 6749 \xA75.1. Not a timestamp, and not milliseconds."
5098
5429
  }),
5099
- refresh_token: import_zod19.z.string().min(1).optional().meta({
5430
+ refresh_token: import_zod20.z.string().min(1).optional().meta({
5100
5431
  description: "The refresh token. Both MCP token endpoints issue one on every exchange and every refresh; it rotates on every use, lives ninety days from its last use, and dies with the account's sessions \u2014 a block, a password change, a withdrawn consent. The console's own OAuth portal issues none."
5101
5432
  }),
5102
- scope: import_zod19.z.string().max(500).optional().meta({
5433
+ scope: import_zod20.z.string().max(500).optional().meta({
5103
5434
  description: "The scopes the issued token actually carries, space-separated."
5104
5435
  })
5105
5436
  });
5106
- var authorizationServerMetadata = import_zod19.z.object({
5107
- issuer: import_zod19.z.url().meta({
5437
+ var authorizationServerMetadata = import_zod20.z.object({
5438
+ issuer: import_zod20.z.url().meta({
5108
5439
  description: "The issuer identifier of this authorization server, per RFC 8414 \xA72. It is what a client checks a token's `iss` against."
5109
5440
  }),
5110
- authorization_endpoint: import_zod19.z.url().meta({
5441
+ authorization_endpoint: import_zod20.z.url().meta({
5111
5442
  description: "Where a client sends the user to authorize."
5112
5443
  }),
5113
- token_endpoint: import_zod19.z.url().meta({
5444
+ token_endpoint: import_zod20.z.url().meta({
5114
5445
  description: "The URL where a client exchanges an authorization code, or a refresh token, for tokens."
5115
5446
  }),
5116
- registration_endpoint: import_zod19.z.url().optional().meta({
5447
+ registration_endpoint: import_zod20.z.url().optional().meta({
5117
5448
  description: "The URL where a client may register itself, per RFC 7591. Absent when the app does not accept dynamic clients."
5118
5449
  }),
5119
- response_types_supported: import_zod19.z.array(import_zod19.z.literal("code")).meta({
5450
+ response_types_supported: import_zod20.z.array(import_zod20.z.literal("code")).meta({
5120
5451
  description: "The response types this server offers: `code` only, the implicit grant being gone with OAuth 2.1."
5121
5452
  }),
5122
- grant_types_supported: import_zod19.z.array(import_zod19.z.enum(["authorization_code", "refresh_token"])).meta({
5453
+ grant_types_supported: import_zod20.z.array(import_zod20.z.enum(["authorization_code", "refresh_token"])).meta({
5123
5454
  description: "The grants this server offers: `authorization_code` and `refresh_token`. OAuth 2.1 removes the implicit and password grants, so neither appears here."
5124
5455
  }),
5125
- code_challenge_methods_supported: import_zod19.z.array(codeChallengeMethod).meta({
5456
+ code_challenge_methods_supported: import_zod20.z.array(codeChallengeMethod).meta({
5126
5457
  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."
5127
5458
  }),
5128
- token_endpoint_auth_methods_supported: import_zod19.z.array(import_zod19.z.literal("none")).meta({
5459
+ token_endpoint_auth_methods_supported: import_zod20.z.array(import_zod20.z.literal("none")).meta({
5129
5460
  description: "How a client authenticates at the token endpoint: `none`, the public-client method, with PKCE protecting the exchange."
5130
5461
  }),
5131
- scopes_supported: import_zod19.z.array(import_zod19.z.string()).optional().meta({
5462
+ scopes_supported: import_zod20.z.array(import_zod20.z.string()).optional().meta({
5132
5463
  description: "The scopes this server knows about, where it publishes a list."
5133
5464
  })
5134
5465
  });
5135
- var protectedResourceMetadata = import_zod19.z.object({
5136
- resource: import_zod19.z.url().meta({
5466
+ var protectedResourceMetadata = import_zod20.z.object({
5467
+ resource: import_zod20.z.url().meta({
5137
5468
  description: "The resource identifier this document describes, per RFC 9728. A token whose audience names something else is rejected here rather than merely noted."
5138
5469
  }),
5139
- authorization_servers: import_zod19.z.array(import_zod19.z.url()).min(1).meta({
5470
+ authorization_servers: import_zod20.z.array(import_zod20.z.url()).min(1).meta({
5140
5471
  description: "The authorization servers that may issue tokens for this resource. There is always at least one."
5141
5472
  }),
5142
- bearer_methods_supported: import_zod19.z.array(import_zod19.z.literal("header")).meta({
5473
+ bearer_methods_supported: import_zod20.z.array(import_zod20.z.literal("header")).meta({
5143
5474
  description: "How a token may be presented: in the `Authorization` header only, never in a query parameter or a form field."
5144
5475
  }),
5145
- scopes_supported: import_zod19.z.array(import_zod19.z.string()).optional().meta({
5476
+ scopes_supported: import_zod20.z.array(import_zod20.z.string()).optional().meta({
5146
5477
  description: "The scopes this resource understands, where it publishes a list."
5147
5478
  })
5148
5479
  });
5149
- var oauthRedirectResponse = import_zod19.z.object({
5150
- redirect_to: import_zod19.z.string().min(1).max(2e3)
5480
+ var oauthRedirectResponse = import_zod20.z.object({
5481
+ redirect_to: import_zod20.z.string().min(1).max(2e3)
5151
5482
  });
5152
- var oauthAuthorizeQuery = import_zod19.z.object({
5153
- response_type: import_zod19.z.literal("code").meta({
5483
+ var oauthAuthorizeQuery = import_zod20.z.object({
5484
+ response_type: import_zod20.z.literal("code").meta({
5154
5485
  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`."
5155
5486
  }),
5156
- client_id: import_zod19.z.string().min(1).meta({
5487
+ client_id: import_zod20.z.string().min(1).meta({
5157
5488
  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."
5158
5489
  }),
5159
- redirect_uri: import_zod19.z.string().min(1).meta({
5490
+ redirect_uri: import_zod20.z.string().min(1).meta({
5160
5491
  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."
5161
5492
  }),
5162
- code_challenge: import_zod19.z.string().min(1).meta({
5493
+ code_challenge: import_zod20.z.string().min(1).meta({
5163
5494
  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."
5164
5495
  }),
5165
- code_challenge_method: import_zod19.z.literal("S256").meta({
5496
+ code_challenge_method: import_zod20.z.literal("S256").meta({
5166
5497
  description: "Only `S256`. `plain` is refused: a challenge equal to its verifier defends against nothing."
5167
5498
  }),
5168
- state: import_zod19.z.string().optional().meta({
5499
+ state: import_zod20.z.string().optional().meta({
5169
5500
  description: "Returned unchanged on the callback, and on the error redirect too, so a client can bind either answer to its own request."
5170
5501
  }),
5171
- resource: import_zod19.z.string().optional().meta({
5502
+ resource: import_zod20.z.string().optional().meta({
5172
5503
  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."
5173
5504
  })
5174
5505
  // **No `scope`, because this authorization server issues none.** The field
@@ -5181,7 +5512,7 @@ var oauthAuthorizeQuery = import_zod19.z.object({
5181
5512
  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."
5182
5513
  });
5183
5514
 
5184
- // node_modules/.pnpm/@fleetless+contracts@6.0.0/node_modules/@fleetless/contracts/dist/routes.js
5515
+ // node_modules/.pnpm/@fleetless+contracts@6.2.0-next.2/node_modules/@fleetless/contracts/dist/routes.js
5185
5516
  var MCP_APP = MCP_APP_PATHS(":appIdentifier");
5186
5517
  var APP_IDENTIFIER = {
5187
5518
  name: "appIdentifier",
@@ -8818,6 +9149,79 @@ var ROUTES = [
8818
9149
  transport: "http",
8819
9150
  notes: "The message is stored before any mail is tried, so `202` means it is kept whatever `mail` says: `sent`, `failed`, or `not_configured` when this cloud has no feedback address. At most 10 messages per developer per hour; the 11th answers `429 rate_limited` with `retry_after_ms`. Replies come by mail, to the sender's address."
8820
9151
  },
9152
+ /* ----------------------------------------------------------- org (plan) */
9153
+ {
9154
+ method: "GET",
9155
+ path: "/api/org/plan",
9156
+ section: "org",
9157
+ summary: "Reads the org's plan: its limits, its usage against them, its add-ons and any change already queued.",
9158
+ audience: "developer",
9159
+ auth: "developer",
9160
+ rateLimited: false,
9161
+ ownerTier: false,
9162
+ status: 200,
9163
+ params: [],
9164
+ query: null,
9165
+ request: null,
9166
+ response: orgPlan,
9167
+ errors: [...DEVELOPER_GUARD],
9168
+ transport: "http",
9169
+ notes: "The one read the console's Plan & billing page, its usage and limit gauges, and every upgrade prompt and feature gate draw from \u2014 nothing else computes `limits` or `usage` on its own. `limits` is already the effective ceiling, the catalogue row raised by `addons` or replaced by an operator's override, so a consumer never recomputes it from the catalogue. `usage` is counted fresh on every call, never cached. `switch` is present only for an organization still on the beta that has not yet landed on a priced plan."
9170
+ },
9171
+ {
9172
+ method: "PUT",
9173
+ path: "/api/org/plan/change",
9174
+ section: "org",
9175
+ summary: "Moves the org's plan down \u2014 a lower plan or a cancellation to Basic \u2014 queuing the change rather than applying it at once.",
9176
+ audience: "developer",
9177
+ auth: "developer",
9178
+ rateLimited: false,
9179
+ ownerTier: true,
9180
+ status: 200,
9181
+ params: [],
9182
+ query: null,
9183
+ request: planChangeRequest,
9184
+ response: orgPlan,
9185
+ errors: [...DEVELOPER_GUARD, "tier_required", "validation_error", "plan_limit", "target_state_conflict"],
9186
+ transport: "http",
9187
+ notes: "Owner tier, and **downward only**: this route moves the org to a lower plan or cancels it outright to Basic. It never moves the org up \u2014 until payment exists, an upgrade or an add-on is not this route's job at all, and is handled today as a Feedback request that Fleetless then applies through the admin route. `409 target_state_conflict` names `target_plan` with rule `not_lower` when the chosen plan is not below the org's current one; with rule `locked_basic_only` when the org is locked (`orgLock`) and the chosen plan is anything but Basic; and with rule `migration_basic_only` when the org is still on the beta, awaiting the switch to priced plans, and the chosen plan is anything but Basic \u2014 that choice is exactly what the org lands on at the switch. An owner is never named in `keep` and always stays, but still counts against the target plan's `seats`; `409 plan_limit` names `seats` when the owners alone already exceed it, and names whichever other limit `keep` still exceeds otherwise. `keep` is `null` when the org's current usage already fits the target plan outright and nothing is deleted; named, it lists exactly the robots, apps, app users and developers that stay. The choice is stored as `pending_change` and takes effect at `period_ends_at` \u2014 **everything of the chosen kind not named in `keep` is deleted at that instant, never before** \u2014 except a choice made while the org is locked, which takes effect at once, and a beta org's choice, which takes effect at the switch date instead. Anything created while the choice is pending is checked against the target plan too and, when it passes, is folded into `keep`, so exactly what the confirmation counted is what is actually deleted. A later `PUT` replaces a still-pending choice outright."
9188
+ },
9189
+ {
9190
+ method: "DELETE",
9191
+ path: "/api/org/plan/change",
9192
+ section: "org",
9193
+ summary: "Withdraws a plan change that was queued but has not taken effect yet.",
9194
+ audience: "developer",
9195
+ auth: "developer",
9196
+ rateLimited: false,
9197
+ ownerTier: true,
9198
+ status: 204,
9199
+ params: [],
9200
+ query: null,
9201
+ request: null,
9202
+ response: null,
9203
+ errors: [...DEVELOPER_GUARD, "tier_required", "not_found"],
9204
+ transport: "http",
9205
+ notes: "Owner tier. `404 not_found` when the org has no `pending_change` to withdraw. The org stays on its current plan, unchanged, as if the choice had never been made; a developer who wants a different one sends a new `PUT`, which would have replaced this one outright anyway."
9206
+ },
9207
+ {
9208
+ method: "PATCH",
9209
+ path: "/api/admin/orgs/:id/plan",
9210
+ section: "org",
9211
+ summary: "Changes an org's plan, add-ons, limit overrides, currency or billing period as the operator.",
9212
+ audience: "internal",
9213
+ auth: "ops",
9214
+ rateLimited: true,
9215
+ ownerTier: false,
9216
+ status: 200,
9217
+ params: [{ name: "id", description: "The org's uuid, whose plan, add-ons or overrides the operator is changing." }],
9218
+ query: null,
9219
+ request: adminPlanChangeRequest,
9220
+ response: orgPlan,
9221
+ errors: ["unauthorized", "not_found", "validation_error", "plan_limit", "rate_limited"],
9222
+ transport: "http",
9223
+ notes: "Every public host answers `404 not_found` for every `/api/admin/*` path \u2014 this route is reachable only on the cloud's private address \u2014 and that same address answers `404` here too while `OPS_API_TOKEN` is not configured, so a door with no key behind it reads exactly like one nobody opened. An unknown `:id` is the same `404`. Applies at once, never queued as a `pending_change`, and only when the org's current usage fits the result: `409 plan_limit` when a lower plan, a lowered override or a removed add-on would leave the org over a limit \u2014 the operator never deletes an org's things, only the owner's own choice does. On success it also withdraws any `pending_change`, ends a beta org's wait for the switch and lifts a lock. Audited as `org.plan_changed` with the `fleetless` actor, never a developer's \u2014 the row names what an operator did, not who in the org asked for it."
9224
+ },
8821
9225
  /* ------------------------------------------------- assets (robot upload) */
8822
9226
  {
8823
9227
  method: "POST",
@@ -8833,9 +9237,9 @@ var ROUTES = [
8833
9237
  query: null,
8834
9238
  request: null,
8835
9239
  response: asset,
8836
- errors: ["unauthorized", "rate_limited", "validation_error", "not_found", "quota_exceeded", "bad_request"],
9240
+ errors: ["unauthorized", "rate_limited", "validation_error", "not_found", "file_too_large", "plan_limit", "quota_exceeded", "bad_request"],
8837
9241
  transport: "http",
8838
- notes: "The body is the **raw file bytes**, not JSON, so it has no request schema; everything about the file \u2014 its kind, its name, its sync id and its announced size \u2014 rides in the `x-fleetless-asset-*` headers `ASSET_UPLOAD_HEADERS` names. The credential is a short-lived upload token minted by `POST /api/robots/:id/assets/sync`, verified in a `preParsing` hook so a refusal precedes the work rather than following it: a `preHandler` would already have buffered the whole file. **Nothing is refused for its own size** \u2014 the robot's asset store is the only limit, so the announced size is checked there against `ROBOT_ASSET_STORE_BYTES` and a file with no room left answers `409 quota_exceeded` carrying `store_bytes`, `used_bytes` and `size_bytes`, while the sync carries on with the next file. Past that, the server's own body limit answers a bare `413 bad_request` with none of those numbers in it. Rate limited per robot inside that same hook, which is why `rateLimited` is `false`: there is no rate-limiting preHandler registered on this route. The URDF itself is never refused for the store; only meshes and textures are charged against it."
9242
+ notes: "The body is the **raw file bytes**, not JSON, so it has no request schema; everything about the file \u2014 its kind, its name, its sync id and its announced size \u2014 rides in the `x-fleetless-asset-*` headers `ASSET_UPLOAD_HEADERS` names. The credential is a short-lived upload token minted by `POST /api/robots/:id/assets/sync`, verified in a `preParsing` hook so a refusal precedes the work rather than following it: a `preHandler` would already have buffered the whole file. **One file can be at most `ASSET_FILE_MAX_BYTES`**, on every plan and for every kind, the URDF included: an announced size over it answers `413 file_too_large` with `max_bytes` and `size_bytes` before a byte is buffered; without a truthful size, a body over the limit answers the same code with the bytes that arrived as `size_bytes`, or with `size_bytes: null` when it ran past the server's body limit and nobody counted the bytes. Retrying does not help. A file that fits but finds the store full answers `409 plan_limit` (or `409 quota_exceeded` for an organisation still on the beta) carrying `store_bytes`, `used_bytes` and `size_bytes`, while the sync carries on with the next file. Rate limited per robot inside that same hook, which is why `rateLimited` is `false`: there is no rate-limiting preHandler registered on this route. The URDF itself is never refused for the store; only meshes and textures are charged against it."
8839
9243
  },
8840
9244
  /* ------------------------------------ realtime and bridge transports */
8841
9245
  {