@dotobokuri/fleet-console 1.57.1 → 1.58.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.
Files changed (66) hide show
  1. package/dist/cli.mjs +92 -7
  2. package/dist/client/assets/{_baseUniq-dvybACFL.js → _baseUniq-DJ4C0UQF.js} +1 -1
  3. package/dist/client/assets/{arc-BM1EQJ5i.js → arc-DkQKttnC.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-Q4EWVU46-C5NpB_9B.js → architectureDiagram-Q4EWVU46-BpuET3uK.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-DXYQGD6D-BemCHdjP.js → blockDiagram-DXYQGD6D-CatlulZp.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AHTNJAMY-BB7Qe4id.js → c4Diagram-AHTNJAMY-BPfWevDh.js} +1 -1
  7. package/dist/client/assets/channel-DDcyY5eB.js +1 -0
  8. package/dist/client/assets/{chunk-4BX2VUAB-CqmbYj1h.js → chunk-4BX2VUAB-a7lkla2P.js} +1 -1
  9. package/dist/client/assets/{chunk-4TB4RGXK-DY8csFL8.js → chunk-4TB4RGXK-Bh-20OVN.js} +1 -1
  10. package/dist/client/assets/{chunk-55IACEB6-Bc2JNsBB.js → chunk-55IACEB6-DiPm-_AS.js} +1 -1
  11. package/dist/client/assets/{chunk-EDXVE4YY-B7fkHilr.js → chunk-EDXVE4YY-BcGep4mz.js} +1 -1
  12. package/dist/client/assets/{chunk-FMBD7UC4-CKS1kchI.js → chunk-FMBD7UC4-Cn0A6Wm1.js} +1 -1
  13. package/dist/client/assets/{chunk-OYMX7WX6-BoSMhV9B.js → chunk-OYMX7WX6-DNTIDjw4.js} +1 -1
  14. package/dist/client/assets/{chunk-QZHKN3VN-B6NI5Nv8.js → chunk-QZHKN3VN-cji_BhbG.js} +1 -1
  15. package/dist/client/assets/{chunk-YZCP3GAM-DDwSQ8RI.js → chunk-YZCP3GAM-B6I5kLlB.js} +1 -1
  16. package/dist/client/assets/classDiagram-6PBFFD2Q-CqqUqRwS.js +1 -0
  17. package/dist/client/assets/classDiagram-v2-HSJHXN6E-CqqUqRwS.js +1 -0
  18. package/dist/client/assets/clone-CjMEm_X8.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-S5V4N54A-VjTSxeFA.js → cose-bilkent-S5V4N54A-_Xmui3Dj.js} +1 -1
  20. package/dist/client/assets/{dagre-KV5264BT-BQSQIPKP.js → dagre-KV5264BT-BI_m6tGN.js} +1 -1
  21. package/dist/client/assets/{diagram-5BDNPKRD-C6e_Ovvo.js → diagram-5BDNPKRD-JDBqcRRR.js} +1 -1
  22. package/dist/client/assets/{diagram-G4DWMVQ6-D6vqaIMN.js → diagram-G4DWMVQ6-CdIHKaZT.js} +1 -1
  23. package/dist/client/assets/{diagram-MMDJMWI5-DFxtu5Sa.js → diagram-MMDJMWI5-CULX6wM5.js} +1 -1
  24. package/dist/client/assets/{diagram-TYMM5635-BcYaszXB.js → diagram-TYMM5635-C1JXiI2W.js} +1 -1
  25. package/dist/client/assets/{erDiagram-SMLLAGMA-DLSCg-9y.js → erDiagram-SMLLAGMA-RJI8bZrW.js} +1 -1
  26. package/dist/client/assets/{flowDiagram-DWJPFMVM-_kx-OfzH.js → flowDiagram-DWJPFMVM-CSrF7U0E.js} +1 -1
  27. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-DluEefPv.js → ganttDiagram-T4ZO3ILL-CHp3LObA.js} +1 -1
  28. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-BDHypYJk.js → gitGraphDiagram-UUTBAWPF-BEeoTjqh.js} +1 -1
  29. package/dist/client/assets/{graph-CHPq19s3.js → graph-DNvMdh3j.js} +1 -1
  30. package/dist/client/assets/index-BG857Idy.css +1 -0
  31. package/dist/client/assets/index-CCNdSGui.js +483 -0
  32. package/dist/client/assets/{infoDiagram-42DDH7IO-BhntKSlv.js → infoDiagram-42DDH7IO-31kh8JMu.js} +1 -1
  33. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-Vo0lWYeZ.js → ishikawaDiagram-UXIWVN3A-DZgEgolJ.js} +1 -1
  34. package/dist/client/assets/{journeyDiagram-VCZTEJTY-B-jydfQ3.js → journeyDiagram-VCZTEJTY-DfZ3i3Bu.js} +1 -1
  35. package/dist/client/assets/{kanban-definition-6JOO6SKY-CR7_O2tt.js → kanban-definition-6JOO6SKY-giivFfnv.js} +1 -1
  36. package/dist/client/assets/{layout-C7qyIJFE.js → layout-DdVRNrH8.js} +1 -1
  37. package/dist/client/assets/{linear-ClKfz3Xw.js → linear-3cmeYdGh.js} +1 -1
  38. package/dist/client/assets/{mermaid.core-eHgX1dDu.js → mermaid.core-Cjqy_Y5Z.js} +4 -4
  39. package/dist/client/assets/{min-CBcfPH4t.js → min-Cgsd0one.js} +1 -1
  40. package/dist/client/assets/{mindmap-definition-QFDTVHPH-BrjeA0rz.js → mindmap-definition-QFDTVHPH-PfuGubxC.js} +1 -1
  41. package/dist/client/assets/{pieDiagram-DEJITSTG-Bx3gB3ns.js → pieDiagram-DEJITSTG-B9O6H439.js} +1 -1
  42. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-Y_8L8YCa.js → quadrantDiagram-34T5L4WZ-DJH7RQzs.js} +1 -1
  43. package/dist/client/assets/{requirementDiagram-MS252O5E-BByFdhc4.js → requirementDiagram-MS252O5E-BZHTYV2q.js} +1 -1
  44. package/dist/client/assets/{sankeyDiagram-XADWPNL6-Du7m7fuy.js → sankeyDiagram-XADWPNL6-B5aGVyLG.js} +1 -1
  45. package/dist/client/assets/{sequenceDiagram-FGHM5R23-DovViawI.js → sequenceDiagram-FGHM5R23-D2890FYU.js} +1 -1
  46. package/dist/client/assets/{stateDiagram-FHFEXIEX-7fVJHvbx.js → stateDiagram-FHFEXIEX-DWmhZMWZ.js} +1 -1
  47. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-DoQKtZ4H.js +1 -0
  48. package/dist/client/assets/{timeline-definition-GMOUNBTQ-Dgsxq4-O.js → timeline-definition-GMOUNBTQ-B0pTOfcD.js} +1 -1
  49. package/dist/client/assets/{vennDiagram-DHZGUBPP-D2tu5aMx.js → vennDiagram-DHZGUBPP-D-S4XLnE.js} +1 -1
  50. package/dist/client/assets/{wardley-RL74JXVD-C4IVs_k4.js → wardley-RL74JXVD-ClPA8Fc3.js} +1 -1
  51. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-DCVn_9G_.js → wardleyDiagram-NUSXRM2D-JJgj7CxJ.js} +1 -1
  52. package/dist/client/assets/{xychartDiagram-5P7HB3ND-D1TZkyWh.js → xychartDiagram-5P7HB3ND-BBIy9-Kq.js} +1 -1
  53. package/dist/client/index.html +2 -2
  54. package/dist/fleet-plugins/ledger/routes.mjs +35 -0
  55. package/dist/fleet-plugins/quota/routes.mjs +484 -63
  56. package/dist/fleet-plugins/scuttlebutt/routes.mjs +92 -7
  57. package/dist/fleet-plugins/terminal/routes.mjs +1379 -543
  58. package/dist/fleet.mjs +1603 -676
  59. package/package.json +1 -1
  60. package/dist/client/assets/channel-t207uO3V.js +0 -1
  61. package/dist/client/assets/classDiagram-6PBFFD2Q-Dg0z8qFh.js +0 -1
  62. package/dist/client/assets/classDiagram-v2-HSJHXN6E-Dg0z8qFh.js +0 -1
  63. package/dist/client/assets/clone-B47TzRxK.js +0 -1
  64. package/dist/client/assets/index-D6_LDhkV.css +0 -1
  65. package/dist/client/assets/index-DeiAnv0J.js +0 -483
  66. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-CQPjJLLI.js +0 -1
@@ -2,7 +2,7 @@
2
2
  import { execFile } from 'node:child_process';
3
3
  import * as fs4 from 'node:fs';
4
4
  import fs4__default, { constants } from 'node:fs';
5
- import fs5 from 'node:fs/promises';
5
+ import fs32 from 'node:fs/promises';
6
6
  import * as os3 from 'node:os';
7
7
  import os3__default from 'node:os';
8
8
  import { promisify } from 'node:util';
@@ -19,16 +19,16 @@ var __export = (target, all) => {
19
19
  function definePlugin(definition) {
20
20
  return definition;
21
21
  }
22
- function registerRouter(ctx, path5, handler, catalog) {
22
+ function registerRouter(ctx, path6, handler, catalog) {
23
23
  if (catalog) {
24
- ctx.registerRouter(normalizePath(path5), handler, catalog);
24
+ ctx.registerRouter(normalizePath(path6), handler, catalog);
25
25
  return;
26
26
  }
27
- ctx.registerRouter(normalizePath(path5), handler);
27
+ ctx.registerRouter(normalizePath(path6), handler);
28
28
  }
29
- function normalizePath(path5) {
30
- if (path5.length === 0 || path5 === "/") return "/";
31
- return path5;
29
+ function normalizePath(path6) {
30
+ if (path6.length === 0 || path6 === "/") return "/";
31
+ return path6;
32
32
  }
33
33
 
34
34
  // ../../node_modules/.pnpm/zod@4.4.1/node_modules/zod/v4/classic/external.js
@@ -793,10 +793,10 @@ function mergeDefs(...defs) {
793
793
  function cloneDef(schema) {
794
794
  return mergeDefs(schema._zod.def);
795
795
  }
796
- function getElementAtPath(obj, path5) {
797
- if (!path5)
796
+ function getElementAtPath(obj, path6) {
797
+ if (!path6)
798
798
  return obj;
799
- return path5.reduce((acc, key) => acc?.[key], obj);
799
+ return path6.reduce((acc, key) => acc?.[key], obj);
800
800
  }
801
801
  function promiseAllObject(promisesObj) {
802
802
  const keys = Object.keys(promisesObj);
@@ -1205,11 +1205,11 @@ function explicitlyAborted(x, startIndex = 0) {
1205
1205
  }
1206
1206
  return false;
1207
1207
  }
1208
- function prefixIssues(path5, issues) {
1208
+ function prefixIssues(path6, issues) {
1209
1209
  return issues.map((iss) => {
1210
1210
  var _a3;
1211
1211
  (_a3 = iss).path ?? (_a3.path = []);
1212
- iss.path.unshift(path5);
1212
+ iss.path.unshift(path6);
1213
1213
  return iss;
1214
1214
  });
1215
1215
  }
@@ -1356,16 +1356,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
1356
1356
  }
1357
1357
  function formatError(error51, mapper = (issue2) => issue2.message) {
1358
1358
  const fieldErrors = { _errors: [] };
1359
- const processError = (error52, path5 = []) => {
1359
+ const processError = (error52, path6 = []) => {
1360
1360
  for (const issue2 of error52.issues) {
1361
1361
  if (issue2.code === "invalid_union" && issue2.errors.length) {
1362
- issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
1362
+ issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
1363
1363
  } else if (issue2.code === "invalid_key") {
1364
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1364
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
1365
1365
  } else if (issue2.code === "invalid_element") {
1366
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1366
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
1367
1367
  } else {
1368
- const fullpath = [...path5, ...issue2.path];
1368
+ const fullpath = [...path6, ...issue2.path];
1369
1369
  if (fullpath.length === 0) {
1370
1370
  fieldErrors._errors.push(mapper(issue2));
1371
1371
  } else {
@@ -1392,17 +1392,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
1392
1392
  }
1393
1393
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
1394
1394
  const result = { errors: [] };
1395
- const processError = (error52, path5 = []) => {
1395
+ const processError = (error52, path6 = []) => {
1396
1396
  var _a3, _b;
1397
1397
  for (const issue2 of error52.issues) {
1398
1398
  if (issue2.code === "invalid_union" && issue2.errors.length) {
1399
- issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
1399
+ issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
1400
1400
  } else if (issue2.code === "invalid_key") {
1401
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1401
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
1402
1402
  } else if (issue2.code === "invalid_element") {
1403
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1403
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
1404
1404
  } else {
1405
- const fullpath = [...path5, ...issue2.path];
1405
+ const fullpath = [...path6, ...issue2.path];
1406
1406
  if (fullpath.length === 0) {
1407
1407
  result.errors.push(mapper(issue2));
1408
1408
  continue;
@@ -1434,8 +1434,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
1434
1434
  }
1435
1435
  function toDotPath(_path) {
1436
1436
  const segs = [];
1437
- const path5 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1438
- for (const seg of path5) {
1437
+ const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1438
+ for (const seg of path6) {
1439
1439
  if (typeof seg === "number")
1440
1440
  segs.push(`[${seg}]`);
1441
1441
  else if (typeof seg === "symbol")
@@ -14099,13 +14099,13 @@ function resolveRef(ref, ctx) {
14099
14099
  if (!ref.startsWith("#")) {
14100
14100
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
14101
14101
  }
14102
- const path5 = ref.slice(1).split("/").filter(Boolean);
14103
- if (path5.length === 0) {
14102
+ const path6 = ref.slice(1).split("/").filter(Boolean);
14103
+ if (path6.length === 0) {
14104
14104
  return ctx.rootSchema;
14105
14105
  }
14106
14106
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
14107
- if (path5[0] === defsKey) {
14108
- const key = path5[1];
14107
+ if (path6[0] === defsKey) {
14108
+ const key = path6[1];
14109
14109
  if (!key || !ctx.defs[key]) {
14110
14110
  throw new Error(`Reference not found: ${ref}`);
14111
14111
  }
@@ -17093,8 +17093,8 @@ function bootEnumDescriptorProto(init) {
17093
17093
  }
17094
17094
 
17095
17095
  // ../../node_modules/.pnpm/@bufbuild+protobuf@2.13.0/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/message.js
17096
- function messageDesc(file2, path5, ...paths) {
17097
- return paths.reduce((acc, cur) => acc.nestedMessages[cur], file2.messages[path5]);
17096
+ function messageDesc(file2, path6, ...paths) {
17097
+ return paths.reduce((acc, cur) => acc.nestedMessages[cur], file2.messages[path6]);
17098
17098
  }
17099
17099
 
17100
17100
  // ../../node_modules/.pnpm/@bufbuild+protobuf@2.13.0/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/descriptor_pb.js
@@ -17820,7 +17820,7 @@ var benchmarks_default = {
17820
17820
  cursorbench: {
17821
17821
  name: "CursorBench",
17822
17822
  benchVersion: "3.2",
17823
- observedAt: "2026-07-30T00:00:00Z",
17823
+ observedAt: "2026-08-13T00:00:00Z",
17824
17824
  url: "https://cursor.com/cursorbench",
17825
17825
  method: "Third-party agentic-coding benchmark: ambiguous multi-file tasks from real Cursor sessions, run in Cursor's own harness. Scores are evidence about the vendor model and transfer across gateway providers serving it; token and step figures are harness-relative efficiency signals, not billing facts.",
17826
17826
  routingTieBandPoints: 2
@@ -17938,6 +17938,31 @@ var benchmarks_default = {
17938
17938
  },
17939
17939
  caveat: "Bench footnote: an earlier snapshot of Cursor's codebase was unintentionally present in this model's training data, inflating scores by an unknown amount; the advantage does not transfer to other repositories."
17940
17940
  },
17941
+ "grok-4.6": {
17942
+ source: "cursorbench",
17943
+ rungs: {
17944
+ low: {
17945
+ score: 61,
17946
+ tokensPerTask: 10658,
17947
+ stepsPerTask: 23
17948
+ },
17949
+ medium: {
17950
+ score: 67.1,
17951
+ tokensPerTask: 17942,
17952
+ stepsPerTask: 29
17953
+ },
17954
+ high: {
17955
+ score: 69.9,
17956
+ tokensPerTask: 32449,
17957
+ stepsPerTask: 39
17958
+ },
17959
+ xhigh: {
17960
+ score: 70.8,
17961
+ tokensPerTask: 41136,
17962
+ stepsPerTask: 46
17963
+ }
17964
+ }
17965
+ },
17941
17966
  "kimi-k3": {
17942
17967
  source: "cursorbench",
17943
17968
  rungs: {
@@ -17986,7 +18011,7 @@ var benchmarks_default = {
17986
18011
  };
17987
18012
  var models_default = {
17988
18013
  version: 1,
17989
- updatedAt: "2026-08-11T00:00:00Z",
18014
+ updatedAt: "2026-08-14T00:00:00Z",
17990
18015
  providers: {
17991
18016
  codex: {
17992
18017
  name: "Codex",
@@ -18128,7 +18153,7 @@ var models_default = {
18128
18153
  cursor: {
18129
18154
  name: "Cursor",
18130
18155
  defaultModel: "auto",
18131
- source: "Cursor GetUsableModels + Run conversationCheckpointUpdate.token_details.max_tokens (cli-2026.07.08-0c04a8a; auto, composer-2.5, and grok-4.5 families observed 2026-08-01); quotaScope from DashboardService/GetCurrentPeriodUsage autoBucketModels (2026-08-05)",
18156
+ source: "Cursor GetUsableModels + Run conversationCheckpointUpdate.token_details.max_tokens (cli-2026.07.08-0c04a8a; auto, composer-2.5, and grok-4.5 families observed 2026-08-01); cursor-agent 2026.08.11-e8db854 observed 2026-08-13 for grok-4.6; quotaScope from DashboardService/GetCurrentPeriodUsage autoBucketModels (2026-08-05)",
18132
18157
  models: [
18133
18158
  {
18134
18159
  modelId: "auto",
@@ -18187,6 +18212,41 @@ var models_default = {
18187
18212
  ],
18188
18213
  upstreamModelIdTemplate: "cursor-grok-4.5-{effort}-fast"
18189
18214
  }
18215
+ },
18216
+ {
18217
+ modelId: "grok-4.6",
18218
+ name: "Grok-4.6",
18219
+ capabilityClass: "flagship",
18220
+ quotaScope: "auto",
18221
+ contextWindow: 256e3,
18222
+ effort: {
18223
+ supported: true,
18224
+ levels: [
18225
+ "low",
18226
+ "medium",
18227
+ "high",
18228
+ "xhigh"
18229
+ ],
18230
+ upstreamModelIdTemplate: "cursor-grok-4.6-{effort}"
18231
+ },
18232
+ benchmarkKey: "grok-4.6"
18233
+ },
18234
+ {
18235
+ modelId: "grok-4.6-fast",
18236
+ name: "Grok-4.6-Fast",
18237
+ capabilityClass: "light",
18238
+ quotaScope: "auto",
18239
+ contextWindow: 256e3,
18240
+ effort: {
18241
+ supported: true,
18242
+ levels: [
18243
+ "low",
18244
+ "medium",
18245
+ "high",
18246
+ "xhigh"
18247
+ ],
18248
+ upstreamModelIdTemplate: "cursor-grok-4.6-{effort}-fast"
18249
+ }
18190
18250
  }
18191
18251
  ]
18192
18252
  },
@@ -18336,12 +18396,36 @@ var models_default = {
18336
18396
  contextWindow: 256e3
18337
18397
  }
18338
18398
  ]
18399
+ },
18400
+ xai: {
18401
+ name: "xAI",
18402
+ defaultModel: "grok-4.6",
18403
+ source: "official Grok CLI 1.0.3 ~/.grok/models_cache.json from GET https://cli-chat-proxy.grok.com/v1/models (observed 2026-08-13)",
18404
+ models: [
18405
+ {
18406
+ modelId: "grok-4.6",
18407
+ name: "Grok-4.6",
18408
+ capabilityClass: "flagship",
18409
+ wire: "responses",
18410
+ contextWindow: 5e5,
18411
+ effort: {
18412
+ supported: true,
18413
+ levels: [
18414
+ "low",
18415
+ "medium",
18416
+ "high",
18417
+ "xhigh"
18418
+ ]
18419
+ },
18420
+ benchmarkKey: "grok-4.6"
18421
+ }
18422
+ ]
18339
18423
  }
18340
18424
  }
18341
18425
  };
18342
18426
  var KIMI_AUTH_PROVIDER_ID = "Claude Code with Moonshot Kimi";
18343
18427
  var KIMI_CODE_API_BASE_URL = "https://api.kimi.com/coding";
18344
- var GATEWAY_PROVIDERS = ["codex", "cursor", "kimi", "opencode"];
18428
+ var GATEWAY_PROVIDERS = ["codex", "xai", "cursor", "opencode", "kimi"];
18345
18429
  var GATEWAY_MODEL_WIRES = ["anthropic", "responses", "chat-completions"];
18346
18430
  var GATEWAY_REASONING_EFFORTS = [
18347
18431
  "low",
@@ -18429,7 +18513,8 @@ var GatewayModelsRegistrySchema = external_exports.object({
18429
18513
  codex: GatewayProviderSchema,
18430
18514
  cursor: GatewayProviderSchema,
18431
18515
  kimi: GatewayProviderSchema,
18432
- opencode: GatewayProviderSchema
18516
+ opencode: GatewayProviderSchema,
18517
+ xai: GatewayProviderSchema
18433
18518
  }).strict()
18434
18519
  }).strict();
18435
18520
  var UNSUPPORTED_GATEWAY_MODEL_EFFORT = Object.freeze({ supported: false });
@@ -18616,8 +18701,8 @@ function validateRegistry(value) {
18616
18701
  if (model.quotaScope && provider !== "cursor") {
18617
18702
  throw new Error(`Gateway quota scope is only supported by Cursor: ${provider}/${model.modelId}`);
18618
18703
  }
18619
- if (model.wire && provider !== "opencode") {
18620
- throw new Error(`Gateway model wire is only supported by OpenCode: ${provider}/${model.modelId}`);
18704
+ if (model.wire && provider !== "opencode" && provider !== "xai") {
18705
+ throw new Error(`Gateway model wire is not supported by provider: ${provider}/${model.modelId}`);
18621
18706
  }
18622
18707
  if (model.effort?.supported) {
18623
18708
  if (new Set(model.effort.levels).size !== model.effort.levels.length) {
@@ -18913,7 +18998,7 @@ var execFileAsync = promisify(execFile);
18913
18998
  var MAX_CREDENTIAL_BYTES = 65536;
18914
18999
  var CREDENTIAL_OPEN_FLAGS = process.platform === "win32" ? constants.O_RDONLY : constants.O_RDONLY | constants.O_NONBLOCK;
18915
19000
  async function readBoundedFile(filePath, maxBytes) {
18916
- const handle = await fs5.open(filePath, CREDENTIAL_OPEN_FLAGS);
19001
+ const handle = await fs32.open(filePath, CREDENTIAL_OPEN_FLAGS);
18917
19002
  try {
18918
19003
  const stat2 = await handle.stat();
18919
19004
  if (!stat2.isFile() || stat2.size > maxBytes) return null;
@@ -19145,9 +19230,9 @@ function parseResetCredits(payload) {
19145
19230
  for (let index = 0; index < credits.length && index < MAX_CREDIT_ENTRIES; index += 1) {
19146
19231
  const credit = object2(credits[index]);
19147
19232
  if (credit?.status !== "available") continue;
19148
- const expiry = safeTimestamp(credit.expires_at);
19149
- if (expiry !== void 0 && (nextExpiresAt === void 0 || expiry < nextExpiresAt)) {
19150
- nextExpiresAt = expiry;
19233
+ const expiry2 = safeTimestamp(credit.expires_at);
19234
+ if (expiry2 !== void 0 && (nextExpiresAt === void 0 || expiry2 < nextExpiresAt)) {
19235
+ nextExpiresAt = expiry2;
19151
19236
  }
19152
19237
  }
19153
19238
  return {
@@ -19167,7 +19252,7 @@ async function fetchCodexUsage(deps = {}) {
19167
19252
  if (credentials.accountId) headers["ChatGPT-Account-Id"] = credentials.accountId;
19168
19253
  try {
19169
19254
  const fetchImpl = deps.fetch ?? fetch;
19170
- const usage3 = parseCodexUsage(await getJson(fetchImpl, "https://chatgpt.com/backend-api/wham/usage", headers));
19255
+ const usage4 = parseCodexUsage(await getJson(fetchImpl, "https://chatgpt.com/backend-api/wham/usage", headers));
19171
19256
  let credits;
19172
19257
  try {
19173
19258
  credits = parseResetCredits(await getJson(
@@ -19180,8 +19265,8 @@ async function fetchCodexUsage(deps = {}) {
19180
19265
  }
19181
19266
  return {
19182
19267
  status: "ok",
19183
- plan: usage3.plan,
19184
- windows: usage3.windows,
19268
+ plan: usage4.plan,
19269
+ windows: usage4.windows,
19185
19270
  ...credits ? { credits } : {},
19186
19271
  fetchedAt: (deps.now ?? Date.now)()
19187
19272
  };
@@ -19295,12 +19380,12 @@ async function fetchCursorUsage(deps = {}) {
19295
19380
  };
19296
19381
  try {
19297
19382
  const fetchImpl = deps.fetch ?? fetch;
19298
- const usage3 = parseCursorUsage(await postJson(
19383
+ const usage4 = parseCursorUsage(await postJson(
19299
19384
  fetchImpl,
19300
19385
  "https://api2.cursor.sh/aiserver.v1.DashboardService/GetCurrentPeriodUsage",
19301
19386
  headers
19302
19387
  ));
19303
- if (usage3.status === "no_subscription") return { status: "no_subscription", method: credentials.method };
19388
+ if (usage4.status === "no_subscription") return { status: "no_subscription", method: credentials.method };
19304
19389
  let plan;
19305
19390
  try {
19306
19391
  const planRoot = object2(await postJson(
@@ -19316,8 +19401,8 @@ async function fetchCursorUsage(deps = {}) {
19316
19401
  status: "ok",
19317
19402
  method: credentials.method,
19318
19403
  ...plan ? { plan } : {},
19319
- ...usage3.cycleDays !== void 0 ? { cycleDays: usage3.cycleDays } : {},
19320
- windows: usage3.windows,
19404
+ ...usage4.cycleDays !== void 0 ? { cycleDays: usage4.cycleDays } : {},
19405
+ windows: usage4.windows,
19321
19406
  fetchedAt: (deps.now ?? Date.now)()
19322
19407
  };
19323
19408
  } catch (error51) {
@@ -19402,16 +19487,16 @@ async function fetchKimiUsage(deps = {}) {
19402
19487
  const apiKey = await deps.authService.getApiKey(KIMI_AUTH_PROVIDER_ID);
19403
19488
  if (!apiKey) return { status: "signed_out" };
19404
19489
  try {
19405
- const usage3 = parseKimiUsage(await getJson(
19490
+ const usage4 = parseKimiUsage(await getJson(
19406
19491
  deps.fetch ?? fetch,
19407
19492
  `${KIMI_CODE_API_BASE_URL}/v1/usages`,
19408
19493
  { Authorization: `Bearer ${apiKey}`, Accept: "application/json" }
19409
19494
  ));
19410
- if (usage3.status === "no_subscription") return { status: "no_subscription" };
19495
+ if (usage4.status === "no_subscription") return { status: "no_subscription" };
19411
19496
  return {
19412
19497
  status: "ok",
19413
- ...usage3.plan ? { plan: usage3.plan } : {},
19414
- windows: usage3.windows,
19498
+ ...usage4.plan ? { plan: usage4.plan } : {},
19499
+ windows: usage4.windows,
19415
19500
  fetchedAt: (deps.now ?? Date.now)()
19416
19501
  };
19417
19502
  } catch (error51) {
@@ -19621,6 +19706,268 @@ async function fetchOpencodeUsage(deps = {}) {
19621
19706
  fetchedAt: now()
19622
19707
  };
19623
19708
  }
19709
+ var XAI_OIDC_ISSUER = "https://auth.x.ai";
19710
+ var XAI_CLI_REFRESH_URL = "https://auth.x.ai/oauth2/token";
19711
+ var XAI_CLI_DEFAULT_CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
19712
+ var XAI_CLI_REFRESH_BUFFER_MS = 5 * 60 * 1e3;
19713
+ var REFRESH_TIMEOUT_MS = 15e3;
19714
+ var FALLBACK_ACCESS_TOKEN_TTL_MS = 36e5;
19715
+ function xaiCliAuthFilePath(deps) {
19716
+ return path32__default.join(deps.env.GROK_HOME || path32__default.join(deps.homedir(), ".grok"), "auth.json");
19717
+ }
19718
+ function expiry(value) {
19719
+ if (typeof value !== "string" && typeof value !== "number") return void 0;
19720
+ const parsed = typeof value === "number" ? value : Date.parse(value);
19721
+ if (!Number.isFinite(parsed)) return void 0;
19722
+ return parsed < 1e12 ? Math.round(parsed * 1e3) : Math.round(parsed);
19723
+ }
19724
+ function jwtExpiry(token) {
19725
+ const parts = token.split(".");
19726
+ if (parts.length < 2) return void 0;
19727
+ try {
19728
+ const payload = JSON.parse(Buffer.from(parts[1] ?? "", "base64url").toString("utf8"));
19729
+ const record4 = credentialRecord(payload);
19730
+ const exp = record4?.exp;
19731
+ if (typeof exp !== "number" || !Number.isFinite(exp)) return void 0;
19732
+ return Math.round(exp * 1e3);
19733
+ } catch {
19734
+ return void 0;
19735
+ }
19736
+ }
19737
+ function effectiveExpiry(entry, accessToken) {
19738
+ const fromEntry = expiry(entry.expires_at) ?? expiry(entry.expires);
19739
+ const fromJwt = jwtExpiry(accessToken);
19740
+ if (fromEntry !== void 0 && fromJwt !== void 0) return Math.min(fromEntry, fromJwt);
19741
+ return fromEntry ?? fromJwt;
19742
+ }
19743
+ function clientId(entryKey, entry) {
19744
+ const fromEntry = optionalTrimmedString(entry.oidc_client_id);
19745
+ if (fromEntry) return fromEntry;
19746
+ const suffix = optionalTrimmedString(entryKey.split("::").at(-1));
19747
+ return suffix ?? XAI_CLI_DEFAULT_CLIENT_ID;
19748
+ }
19749
+ function refreshToken(entry) {
19750
+ return optionalTrimmedString(entry.refresh_token) ?? optionalTrimmedString(entry.refresh);
19751
+ }
19752
+ async function defaultWriteAuthFile(filePath, contents) {
19753
+ const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
19754
+ try {
19755
+ await fs32.writeFile(tmpPath, contents, { encoding: "utf8", mode: 384 });
19756
+ await fs32.rename(tmpPath, filePath);
19757
+ } catch (error51) {
19758
+ await fs32.unlink(tmpPath).catch(() => void 0);
19759
+ throw error51;
19760
+ }
19761
+ }
19762
+ async function persistRefreshedEntry(deps, filePath, entryKey, expected, patch, writeAuthFile) {
19763
+ const raw = await deps.readBounded(filePath, MAX_CREDENTIAL_BYTES);
19764
+ if (raw === null) return;
19765
+ const auth = credentialRecord(JSON.parse(raw));
19766
+ if (!auth) return;
19767
+ const current = credentialRecord(auth[entryKey]);
19768
+ if (!current) return;
19769
+ if (optionalTrimmedString(current.key) !== expected.accessToken) return;
19770
+ if (expected.refreshToken !== void 0 && refreshToken(current) !== expected.refreshToken) return;
19771
+ auth[entryKey] = { ...current, ...patch };
19772
+ await writeAuthFile(filePath, `${JSON.stringify(auth, null, 2)}
19773
+ `);
19774
+ }
19775
+ async function refreshAccessToken(entryKey, entry, fetchImpl, now) {
19776
+ const token = refreshToken(entry);
19777
+ if (!token) return null;
19778
+ const controller = new AbortController();
19779
+ const timer = setTimeout(() => controller.abort(), REFRESH_TIMEOUT_MS);
19780
+ try {
19781
+ const body = new URLSearchParams({
19782
+ grant_type: "refresh_token",
19783
+ client_id: clientId(entryKey, entry),
19784
+ refresh_token: token
19785
+ });
19786
+ const response = await fetchImpl(XAI_CLI_REFRESH_URL, {
19787
+ method: "POST",
19788
+ headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" },
19789
+ body,
19790
+ signal: controller.signal
19791
+ });
19792
+ if (!response.ok) return null;
19793
+ const payload = credentialRecord(await response.json());
19794
+ const accessToken = optionalTrimmedString(payload?.access_token);
19795
+ if (!accessToken) return null;
19796
+ const expiresIn = payload?.expires_in;
19797
+ const expiresAt = typeof expiresIn === "number" && Number.isFinite(expiresIn) && expiresIn > 0 ? now() + Math.round(expiresIn * 1e3) : jwtExpiry(accessToken) ?? now() + FALLBACK_ACCESS_TOKEN_TTL_MS;
19798
+ const rotated = optionalTrimmedString(payload?.refresh_token);
19799
+ return {
19800
+ accessToken,
19801
+ expiresAt,
19802
+ ...rotated === void 0 ? {} : { refreshToken: rotated }
19803
+ };
19804
+ } catch {
19805
+ return null;
19806
+ } finally {
19807
+ clearTimeout(timer);
19808
+ }
19809
+ }
19810
+ async function resolveXaiCliAuth(deps, options = {}) {
19811
+ const now = options.now ?? Date.now;
19812
+ const fetchImpl = options.fetch ?? fetch;
19813
+ const writeAuthFile = options.writeAuthFile ?? defaultWriteAuthFile;
19814
+ const filePath = xaiCliAuthFilePath(deps);
19815
+ try {
19816
+ const raw = await deps.readBounded(filePath, MAX_CREDENTIAL_BYTES);
19817
+ if (raw === null || raw.length > MAX_CREDENTIAL_BYTES) return { status: "signed_out" };
19818
+ const auth = credentialRecord(JSON.parse(raw));
19819
+ if (!auth) return { status: "signed_out" };
19820
+ let sawExpired = false;
19821
+ for (const [entryKey, value] of Object.entries(auth)) {
19822
+ if (!entryKey.startsWith(`${XAI_OIDC_ISSUER}::`)) continue;
19823
+ const entry = credentialRecord(value);
19824
+ const issuer = optionalTrimmedString(entry?.oidc_issuer);
19825
+ const accessToken = optionalTrimmedString(entry?.key);
19826
+ if (!entry || issuer !== XAI_OIDC_ISSUER || !accessToken) continue;
19827
+ const expiresAt = effectiveExpiry(entry, accessToken);
19828
+ const expired2 = expiresAt !== void 0 && expiresAt <= now();
19829
+ const needsRefresh = options.forceRefresh === true || expiresAt !== void 0 && expiresAt - now() <= XAI_CLI_REFRESH_BUFFER_MS;
19830
+ if (needsRefresh) {
19831
+ const refreshed = await refreshAccessToken(entryKey, entry, fetchImpl, now);
19832
+ if (refreshed) {
19833
+ const userId2 = optionalTrimmedString(entry.user_id) ?? optionalTrimmedString(entry.principal_id);
19834
+ const patch = {
19835
+ key: refreshed.accessToken,
19836
+ expires_at: new Date(refreshed.expiresAt).toISOString()
19837
+ };
19838
+ if (refreshed.refreshToken !== void 0) patch.refresh_token = refreshed.refreshToken;
19839
+ try {
19840
+ await persistRefreshedEntry(
19841
+ deps,
19842
+ filePath,
19843
+ entryKey,
19844
+ { accessToken, refreshToken: refreshToken(entry) },
19845
+ patch,
19846
+ writeAuthFile
19847
+ );
19848
+ } catch {
19849
+ }
19850
+ return {
19851
+ status: "ok",
19852
+ credentials: {
19853
+ accessToken: refreshed.accessToken,
19854
+ expiresAt: refreshed.expiresAt,
19855
+ ...userId2 === void 0 ? {} : { userId: userId2 }
19856
+ }
19857
+ };
19858
+ }
19859
+ if (options.forceRefresh === true || expired2) {
19860
+ sawExpired = true;
19861
+ continue;
19862
+ }
19863
+ } else if (expired2) {
19864
+ sawExpired = true;
19865
+ continue;
19866
+ }
19867
+ const userId = optionalTrimmedString(entry.user_id) ?? optionalTrimmedString(entry.principal_id);
19868
+ return {
19869
+ status: "ok",
19870
+ credentials: {
19871
+ accessToken,
19872
+ ...expiresAt === void 0 ? {} : { expiresAt },
19873
+ ...userId === void 0 ? {} : { userId }
19874
+ }
19875
+ };
19876
+ }
19877
+ return { status: sawExpired ? "expired" : "signed_out" };
19878
+ } catch {
19879
+ return { status: "signed_out" };
19880
+ }
19881
+ }
19882
+ var XAI_CLI_CREDITS_URL = "https://cli-chat-proxy.grok.com/v1/billing?format=credits";
19883
+ var WEEKLY_PERIOD = "USAGE_PERIOD_TYPE_WEEKLY";
19884
+ function parseXaiCredits(payload) {
19885
+ const root = object2(payload);
19886
+ const config2 = object2(root?.config);
19887
+ const period = object2(config2?.currentPeriod);
19888
+ const periodType = typeof period?.type === "string" ? period.type.trim() : "";
19889
+ if (!config2 || !period || periodType.length === 0) return null;
19890
+ const startsAt = safeTimestamp(period.start);
19891
+ const resetsAt = safeTimestamp(period.end);
19892
+ if (startsAt === void 0 || resetsAt === void 0 || resetsAt <= startsAt) return null;
19893
+ if (periodType !== WEEKLY_PERIOD) return { status: "other" };
19894
+ let usedPercent = 0;
19895
+ if (config2.creditUsagePercent !== void 0) {
19896
+ if (typeof config2.creditUsagePercent !== "number" || !Number.isFinite(config2.creditUsagePercent)) {
19897
+ return null;
19898
+ }
19899
+ usedPercent = percent(config2.creditUsagePercent);
19900
+ }
19901
+ return {
19902
+ status: "weekly",
19903
+ window: {
19904
+ id: "weekly",
19905
+ usedPercent,
19906
+ resetsAt,
19907
+ period: {
19908
+ durationMs: resetsAt - startsAt,
19909
+ durationBasis: "upstream",
19910
+ startsAt,
19911
+ startsAtBasis: "upstream"
19912
+ }
19913
+ }
19914
+ };
19915
+ }
19916
+ function creditsHeaders(credentials) {
19917
+ const headers = {
19918
+ Accept: "application/json",
19919
+ Authorization: `Bearer ${credentials.accessToken}`,
19920
+ "X-XAI-Token-Auth": "xai-grok-cli"
19921
+ };
19922
+ if (credentials.userId) headers["x-userid"] = credentials.userId;
19923
+ return headers;
19924
+ }
19925
+ async function fetchXaiUsage(deps = {}) {
19926
+ const credentialDeps = deps.credentials ?? defaultCredentialDeps;
19927
+ const now = deps.now ?? Date.now;
19928
+ const fetchImpl = deps.fetch ?? fetch;
19929
+ const auth = await resolveXaiCliAuth(credentialDeps, { now, fetch: fetchImpl });
19930
+ if (auth.status !== "ok") return { status: auth.status };
19931
+ const load = (credentials) => getJson(
19932
+ fetchImpl,
19933
+ XAI_CLI_CREDITS_URL,
19934
+ creditsHeaders(credentials)
19935
+ );
19936
+ try {
19937
+ let credentials = auth.credentials;
19938
+ let payload;
19939
+ try {
19940
+ payload = await load(credentials);
19941
+ } catch (error51) {
19942
+ if (!expired(error51)) throw error51;
19943
+ const retried = await resolveXaiCliAuth(credentialDeps, {
19944
+ now,
19945
+ fetch: fetchImpl,
19946
+ forceRefresh: true
19947
+ });
19948
+ if (retried.status !== "ok") return { status: "expired" };
19949
+ credentials = retried.credentials;
19950
+ try {
19951
+ payload = await load(credentials);
19952
+ } catch (retryError) {
19953
+ const result = expired(retryError);
19954
+ if (result) return result;
19955
+ throw retryError;
19956
+ }
19957
+ }
19958
+ const parsed = parseXaiCredits(payload);
19959
+ if (!parsed) throw new Error("Grok returned an unsupported quota response");
19960
+ return {
19961
+ status: "ok",
19962
+ windows: parsed.status === "weekly" ? [parsed.window] : [],
19963
+ fetchedAt: now()
19964
+ };
19965
+ } catch (error51) {
19966
+ const result = expired(error51);
19967
+ if (result) return result;
19968
+ throw error51;
19969
+ }
19970
+ }
19624
19971
  var CACHE_TTL_MS = 12e4;
19625
19972
  var STALE_TTL_MS = 18e5;
19626
19973
  function createAiGatewayQuotaCollectors(deps) {
@@ -19636,7 +19983,8 @@ function createAiGatewayQuotaCollectors(deps) {
19636
19983
  fetchCodex: () => fetchCodexUsage(providerDeps),
19637
19984
  fetchCursor: () => fetchCursorUsage(providerDeps),
19638
19985
  fetchKimi: () => fetchKimiUsage(providerDeps),
19639
- fetchOpencode: () => fetchOpencodeUsage(providerDeps)
19986
+ fetchOpencode: () => fetchOpencodeUsage(providerDeps),
19987
+ fetchXai: () => fetchXaiUsage(providerDeps)
19640
19988
  };
19641
19989
  }
19642
19990
  function isProviderSuccess(value) {
@@ -19649,7 +19997,8 @@ function createQuotaService(deps) {
19649
19997
  codex: deps.fetchCodex,
19650
19998
  cursor: deps.fetchCursor,
19651
19999
  kimi: deps.fetchKimi,
19652
- opencode: deps.fetchOpencode
20000
+ opencode: deps.fetchOpencode,
20001
+ xai: deps.fetchXai ?? (async () => ({ status: "signed_out" }))
19653
20002
  };
19654
20003
  const cache = /* @__PURE__ */ new Map();
19655
20004
  const lastGood = /* @__PURE__ */ new Map();
@@ -19692,12 +20041,13 @@ function createQuotaService(deps) {
19692
20041
  }
19693
20042
  return {
19694
20043
  async getSummary(options = {}) {
19695
- const [claude, codex, cursor, kimi, opencode] = await Promise.all([
20044
+ const [claude, codex, cursor, kimi, opencode, xai] = await Promise.all([
19696
20045
  load("claude", options.force === true || options.forceProvider === "claude"),
19697
20046
  load("codex", options.force === true || options.forceProvider === "codex"),
19698
20047
  load("cursor", options.force === true || options.forceProvider === "cursor"),
19699
20048
  load("kimi", options.force === true || options.forceProvider === "kimi"),
19700
- load("opencode", options.force === true || options.forceProvider === "opencode")
20049
+ load("opencode", options.force === true || options.forceProvider === "opencode"),
20050
+ load("xai", options.force === true || options.forceProvider === "xai")
19701
20051
  ]);
19702
20052
  return {
19703
20053
  providers: {
@@ -19705,7 +20055,8 @@ function createQuotaService(deps) {
19705
20055
  codex: withRisk(codex),
19706
20056
  cursor: withRisk(cursor),
19707
20057
  kimi: withRisk(kimi),
19708
- opencode: withRisk(opencode)
20058
+ opencode: withRisk(opencode),
20059
+ xai: withRisk(xai)
19709
20060
  }
19710
20061
  };
19711
20062
  }
@@ -20568,7 +20919,36 @@ messageDesc(cursorAgentFile, 63);
20568
20919
  messageDesc(cursorAgentFile, 70);
20569
20920
  new Set(REASONING_EFFORTS);
20570
20921
 
20922
+ // ../fleet-plugins/quota/provider-order.ts
20923
+ var PROVIDER_ORDER_DEFAULT = ["claude", "codex", "xai", "cursor", "opencode", "kimi"];
20924
+ function isProviderId(value) {
20925
+ return PROVIDER_ORDER_DEFAULT.includes(value);
20926
+ }
20927
+ function sanitizeProviderOrder(value) {
20928
+ const order = [];
20929
+ if (Array.isArray(value)) {
20930
+ for (const entry of value) {
20931
+ if (isProviderId(entry) && !order.includes(entry)) order.push(entry);
20932
+ }
20933
+ }
20934
+ for (const id of PROVIDER_ORDER_DEFAULT) {
20935
+ if (!order.includes(id)) order.push(id);
20936
+ }
20937
+ return order;
20938
+ }
20939
+
20571
20940
  // ../fleet-plugins/quota/server/handlers.ts
20941
+ async function readStoredSettings(ctx) {
20942
+ const stored = await ctx.host.storage.readJson("quota", "settings");
20943
+ return stored !== null && typeof stored === "object" && !Array.isArray(stored) ? stored : {};
20944
+ }
20945
+ function retainedSettings(settings) {
20946
+ return {
20947
+ ...typeof settings.claudeConnected === "boolean" ? { claudeConnected: settings.claudeConnected } : {},
20948
+ ...typeof settings.cursorConnected === "boolean" ? { cursorConnected: settings.cursorConnected } : {},
20949
+ ...Array.isArray(settings.providerOrder) ? { providerOrder: sanitizeProviderOrder(settings.providerOrder) } : {}
20950
+ };
20951
+ }
20572
20952
  async function handleSummary(req, res, ctx, service) {
20573
20953
  if (req.method !== "GET") {
20574
20954
  ctx.host.http.writeJson(res, 405, { error: "method_not_allowed" });
@@ -20579,7 +20959,9 @@ async function handleSummary(req, res, ctx, service) {
20579
20959
  return;
20580
20960
  }
20581
20961
  const url2 = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
20582
- ctx.host.http.writeJson(res, 200, await service.getSummary({ force: url2.searchParams.get("force") === "1" }));
20962
+ const summary = await service.getSummary({ force: url2.searchParams.get("force") === "1" });
20963
+ const providerOrder = sanitizeProviderOrder((await readStoredSettings(ctx)).providerOrder);
20964
+ ctx.host.http.writeJson(res, 200, { ...summary, providerOrder });
20583
20965
  }
20584
20966
  async function handleConnect(req, res, ctx, service, serializeSettings) {
20585
20967
  if (req.method !== "POST") {
@@ -20607,16 +20989,51 @@ async function handleConnect(req, res, ctx, service, serializeSettings) {
20607
20989
  return;
20608
20990
  }
20609
20991
  await serializeSettings(async () => {
20610
- const stored = await ctx.host.storage.readJson("quota", "settings");
20611
- const settings = stored !== null && typeof stored === "object" && !Array.isArray(stored) ? stored : {};
20612
20992
  const next = {
20613
- ...typeof settings.claudeConnected === "boolean" ? { claudeConnected: settings.claudeConnected } : {},
20614
- ...typeof settings.cursorConnected === "boolean" ? { cursorConnected: settings.cursorConnected } : {},
20993
+ ...retainedSettings(await readStoredSettings(ctx)),
20615
20994
  ...body.provider === "claude" ? { claudeConnected: body.connected } : { cursorConnected: body.connected }
20616
20995
  };
20617
20996
  await ctx.host.storage.writeJson("quota", "settings", next);
20618
20997
  });
20619
- ctx.host.http.writeJson(res, 200, await service.getSummary({ forceProvider: body.provider }));
20998
+ const summary = await service.getSummary({ forceProvider: body.provider });
20999
+ const providerOrder = sanitizeProviderOrder((await readStoredSettings(ctx)).providerOrder);
21000
+ ctx.host.http.writeJson(res, 200, { ...summary, providerOrder });
21001
+ }
21002
+ async function handleOrder(req, res, ctx, serializeSettings) {
21003
+ if (req.method !== "POST") {
21004
+ ctx.host.http.writeJson(res, 405, { error: "method_not_allowed" });
21005
+ return;
21006
+ }
21007
+ if (!ctx.host.security.isTerminalAuthorized(req)) {
21008
+ ctx.host.http.writeJson(res, 401, { error: "unauthorized" });
21009
+ return;
21010
+ }
21011
+ const contentType = req.headers["content-type"];
21012
+ const mediaType = typeof contentType === "string" ? contentType.split(";", 1)[0]?.trim().toLowerCase() : void 0;
21013
+ if (mediaType !== "application/json") {
21014
+ ctx.host.http.writeJson(res, 415, { error: "unsupported_media_type" });
21015
+ return;
21016
+ }
21017
+ let body;
21018
+ try {
21019
+ body = await ctx.host.http.readJsonBody(req);
21020
+ } catch {
21021
+ body = null;
21022
+ }
21023
+ const order = body?.order;
21024
+ if (!body || Object.keys(body).length !== 1 || !Array.isArray(order) || order.length !== PROVIDER_ORDER_DEFAULT.length || !order.every(isProviderId) || new Set(order).size !== order.length) {
21025
+ ctx.host.http.writeJson(res, 400, { error: "invalid_order_request" });
21026
+ return;
21027
+ }
21028
+ const providerOrder = order;
21029
+ await serializeSettings(async () => {
21030
+ const next = {
21031
+ ...retainedSettings(await readStoredSettings(ctx)),
21032
+ providerOrder
21033
+ };
21034
+ await ctx.host.storage.writeJson("quota", "settings", next);
21035
+ });
21036
+ ctx.host.http.writeJson(res, 200, { providerOrder });
20620
21037
  }
20621
21038
 
20622
21039
  // ../fleet-plugins/quota/routes.ts
@@ -20649,6 +21066,10 @@ var routes_default = definePlugin({
20649
21066
  await handleConnect(req, res, ctx, service, serializeSettings);
20650
21067
  return true;
20651
21068
  }, { method: "POST", path: "", summary: "Update provider quota connection state.", category: "Quota Plugin", gate: "origin-write", transport: "http" });
21069
+ registerRouter(ctx, "order", async ({ req, res }) => {
21070
+ await handleOrder(req, res, ctx, serializeSettings);
21071
+ return true;
21072
+ }, { method: "POST", path: "", summary: "Persist the provider card order.", category: "Quota Plugin", gate: "origin-write", transport: "http" });
20652
21073
  }
20653
21074
  });
20654
21075