@dotobokuri/fleet-console 1.57.2 → 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.
- package/dist/cli.mjs +30 -5
- package/dist/client/assets/{_baseUniq-dvybACFL.js → _baseUniq-DJ4C0UQF.js} +1 -1
- package/dist/client/assets/{arc-BM1EQJ5i.js → arc-DkQKttnC.js} +1 -1
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-C5NpB_9B.js → architectureDiagram-Q4EWVU46-BpuET3uK.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-BemCHdjP.js → blockDiagram-DXYQGD6D-CatlulZp.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-BB7Qe4id.js → c4Diagram-AHTNJAMY-BPfWevDh.js} +1 -1
- package/dist/client/assets/channel-DDcyY5eB.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-CqmbYj1h.js → chunk-4BX2VUAB-a7lkla2P.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-DY8csFL8.js → chunk-4TB4RGXK-Bh-20OVN.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-Bc2JNsBB.js → chunk-55IACEB6-DiPm-_AS.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-B7fkHilr.js → chunk-EDXVE4YY-BcGep4mz.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-CKS1kchI.js → chunk-FMBD7UC4-Cn0A6Wm1.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-BoSMhV9B.js → chunk-OYMX7WX6-DNTIDjw4.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-B6NI5Nv8.js → chunk-QZHKN3VN-cji_BhbG.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-DDwSQ8RI.js → chunk-YZCP3GAM-B6I5kLlB.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-CqqUqRwS.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-CqqUqRwS.js +1 -0
- package/dist/client/assets/clone-CjMEm_X8.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-VjTSxeFA.js → cose-bilkent-S5V4N54A-_Xmui3Dj.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-BQSQIPKP.js → dagre-KV5264BT-BI_m6tGN.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-C6e_Ovvo.js → diagram-5BDNPKRD-JDBqcRRR.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-D6vqaIMN.js → diagram-G4DWMVQ6-CdIHKaZT.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-DFxtu5Sa.js → diagram-MMDJMWI5-CULX6wM5.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-BcYaszXB.js → diagram-TYMM5635-C1JXiI2W.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-DLSCg-9y.js → erDiagram-SMLLAGMA-RJI8bZrW.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-_kx-OfzH.js → flowDiagram-DWJPFMVM-CSrF7U0E.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-DluEefPv.js → ganttDiagram-T4ZO3ILL-CHp3LObA.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-BDHypYJk.js → gitGraphDiagram-UUTBAWPF-BEeoTjqh.js} +1 -1
- package/dist/client/assets/{graph-CHPq19s3.js → graph-DNvMdh3j.js} +1 -1
- package/dist/client/assets/index-BG857Idy.css +1 -0
- package/dist/client/assets/index-CCNdSGui.js +483 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-BhntKSlv.js → infoDiagram-42DDH7IO-31kh8JMu.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-Vo0lWYeZ.js → ishikawaDiagram-UXIWVN3A-DZgEgolJ.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-B-jydfQ3.js → journeyDiagram-VCZTEJTY-DfZ3i3Bu.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-CR7_O2tt.js → kanban-definition-6JOO6SKY-giivFfnv.js} +1 -1
- package/dist/client/assets/{layout-C7qyIJFE.js → layout-DdVRNrH8.js} +1 -1
- package/dist/client/assets/{linear-ClKfz3Xw.js → linear-3cmeYdGh.js} +1 -1
- package/dist/client/assets/{mermaid.core-eHgX1dDu.js → mermaid.core-Cjqy_Y5Z.js} +4 -4
- package/dist/client/assets/{min-CBcfPH4t.js → min-Cgsd0one.js} +1 -1
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-BrjeA0rz.js → mindmap-definition-QFDTVHPH-PfuGubxC.js} +1 -1
- package/dist/client/assets/{pieDiagram-DEJITSTG-Bx3gB3ns.js → pieDiagram-DEJITSTG-B9O6H439.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-Y_8L8YCa.js → quadrantDiagram-34T5L4WZ-DJH7RQzs.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-BByFdhc4.js → requirementDiagram-MS252O5E-BZHTYV2q.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-Du7m7fuy.js → sankeyDiagram-XADWPNL6-B5aGVyLG.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-DovViawI.js → sequenceDiagram-FGHM5R23-D2890FYU.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-7fVJHvbx.js → stateDiagram-FHFEXIEX-DWmhZMWZ.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-DoQKtZ4H.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-Dgsxq4-O.js → timeline-definition-GMOUNBTQ-B0pTOfcD.js} +1 -1
- package/dist/client/assets/{vennDiagram-DHZGUBPP-D2tu5aMx.js → vennDiagram-DHZGUBPP-D-S4XLnE.js} +1 -1
- package/dist/client/assets/{wardley-RL74JXVD-C4IVs_k4.js → wardley-RL74JXVD-ClPA8Fc3.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-DCVn_9G_.js → wardleyDiagram-NUSXRM2D-JJgj7CxJ.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-D1TZkyWh.js → xychartDiagram-5P7HB3ND-BBIy9-Kq.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/fleet-plugins/ledger/routes.mjs +35 -0
- package/dist/fleet-plugins/quota/routes.mjs +422 -61
- package/dist/fleet-plugins/scuttlebutt/routes.mjs +30 -5
- package/dist/fleet-plugins/terminal/routes.mjs +1317 -541
- package/dist/fleet.mjs +1541 -674
- package/package.json +1 -1
- package/dist/client/assets/channel-t207uO3V.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-Dg0z8qFh.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-Dg0z8qFh.js +0 -1
- package/dist/client/assets/clone-B47TzRxK.js +0 -1
- package/dist/client/assets/index-D6_LDhkV.css +0 -1
- package/dist/client/assets/index-DeiAnv0J.js +0 -483
- 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
|
|
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,
|
|
22
|
+
function registerRouter(ctx, path6, handler, catalog) {
|
|
23
23
|
if (catalog) {
|
|
24
|
-
ctx.registerRouter(normalizePath(
|
|
24
|
+
ctx.registerRouter(normalizePath(path6), handler, catalog);
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
|
-
ctx.registerRouter(normalizePath(
|
|
27
|
+
ctx.registerRouter(normalizePath(path6), handler);
|
|
28
28
|
}
|
|
29
|
-
function normalizePath(
|
|
30
|
-
if (
|
|
31
|
-
return
|
|
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,
|
|
797
|
-
if (!
|
|
796
|
+
function getElementAtPath(obj, path6) {
|
|
797
|
+
if (!path6)
|
|
798
798
|
return obj;
|
|
799
|
-
return
|
|
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(
|
|
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(
|
|
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,
|
|
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 }, [...
|
|
1362
|
+
issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
|
|
1363
1363
|
} else if (issue2.code === "invalid_key") {
|
|
1364
|
-
processError({ issues: issue2.issues }, [...
|
|
1364
|
+
processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
|
|
1365
1365
|
} else if (issue2.code === "invalid_element") {
|
|
1366
|
-
processError({ issues: issue2.issues }, [...
|
|
1366
|
+
processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
|
|
1367
1367
|
} else {
|
|
1368
|
-
const fullpath = [...
|
|
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,
|
|
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 }, [...
|
|
1399
|
+
issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
|
|
1400
1400
|
} else if (issue2.code === "invalid_key") {
|
|
1401
|
-
processError({ issues: issue2.issues }, [...
|
|
1401
|
+
processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
|
|
1402
1402
|
} else if (issue2.code === "invalid_element") {
|
|
1403
|
-
processError({ issues: issue2.issues }, [...
|
|
1403
|
+
processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
|
|
1404
1404
|
} else {
|
|
1405
|
-
const fullpath = [...
|
|
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
|
|
1438
|
-
for (const seg of
|
|
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
|
|
14103
|
-
if (
|
|
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 (
|
|
14108
|
-
const key =
|
|
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,
|
|
17097
|
-
return paths.reduce((acc, cur) => acc.nestedMessages[cur], file2.messages[
|
|
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
|
|
@@ -18011,7 +18011,7 @@ var benchmarks_default = {
|
|
|
18011
18011
|
};
|
|
18012
18012
|
var models_default = {
|
|
18013
18013
|
version: 1,
|
|
18014
|
-
updatedAt: "2026-08-
|
|
18014
|
+
updatedAt: "2026-08-14T00:00:00Z",
|
|
18015
18015
|
providers: {
|
|
18016
18016
|
codex: {
|
|
18017
18017
|
name: "Codex",
|
|
@@ -18396,12 +18396,36 @@ var models_default = {
|
|
|
18396
18396
|
contextWindow: 256e3
|
|
18397
18397
|
}
|
|
18398
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
|
+
]
|
|
18399
18423
|
}
|
|
18400
18424
|
}
|
|
18401
18425
|
};
|
|
18402
18426
|
var KIMI_AUTH_PROVIDER_ID = "Claude Code with Moonshot Kimi";
|
|
18403
18427
|
var KIMI_CODE_API_BASE_URL = "https://api.kimi.com/coding";
|
|
18404
|
-
var GATEWAY_PROVIDERS = ["codex", "
|
|
18428
|
+
var GATEWAY_PROVIDERS = ["codex", "xai", "cursor", "opencode", "kimi"];
|
|
18405
18429
|
var GATEWAY_MODEL_WIRES = ["anthropic", "responses", "chat-completions"];
|
|
18406
18430
|
var GATEWAY_REASONING_EFFORTS = [
|
|
18407
18431
|
"low",
|
|
@@ -18489,7 +18513,8 @@ var GatewayModelsRegistrySchema = external_exports.object({
|
|
|
18489
18513
|
codex: GatewayProviderSchema,
|
|
18490
18514
|
cursor: GatewayProviderSchema,
|
|
18491
18515
|
kimi: GatewayProviderSchema,
|
|
18492
|
-
opencode: GatewayProviderSchema
|
|
18516
|
+
opencode: GatewayProviderSchema,
|
|
18517
|
+
xai: GatewayProviderSchema
|
|
18493
18518
|
}).strict()
|
|
18494
18519
|
}).strict();
|
|
18495
18520
|
var UNSUPPORTED_GATEWAY_MODEL_EFFORT = Object.freeze({ supported: false });
|
|
@@ -18676,8 +18701,8 @@ function validateRegistry(value) {
|
|
|
18676
18701
|
if (model.quotaScope && provider !== "cursor") {
|
|
18677
18702
|
throw new Error(`Gateway quota scope is only supported by Cursor: ${provider}/${model.modelId}`);
|
|
18678
18703
|
}
|
|
18679
|
-
if (model.wire && provider !== "opencode") {
|
|
18680
|
-
throw new Error(`Gateway model wire is
|
|
18704
|
+
if (model.wire && provider !== "opencode" && provider !== "xai") {
|
|
18705
|
+
throw new Error(`Gateway model wire is not supported by provider: ${provider}/${model.modelId}`);
|
|
18681
18706
|
}
|
|
18682
18707
|
if (model.effort?.supported) {
|
|
18683
18708
|
if (new Set(model.effort.levels).size !== model.effort.levels.length) {
|
|
@@ -18973,7 +18998,7 @@ var execFileAsync = promisify(execFile);
|
|
|
18973
18998
|
var MAX_CREDENTIAL_BYTES = 65536;
|
|
18974
18999
|
var CREDENTIAL_OPEN_FLAGS = process.platform === "win32" ? constants.O_RDONLY : constants.O_RDONLY | constants.O_NONBLOCK;
|
|
18975
19000
|
async function readBoundedFile(filePath, maxBytes) {
|
|
18976
|
-
const handle = await
|
|
19001
|
+
const handle = await fs32.open(filePath, CREDENTIAL_OPEN_FLAGS);
|
|
18977
19002
|
try {
|
|
18978
19003
|
const stat2 = await handle.stat();
|
|
18979
19004
|
if (!stat2.isFile() || stat2.size > maxBytes) return null;
|
|
@@ -19205,9 +19230,9 @@ function parseResetCredits(payload) {
|
|
|
19205
19230
|
for (let index = 0; index < credits.length && index < MAX_CREDIT_ENTRIES; index += 1) {
|
|
19206
19231
|
const credit = object2(credits[index]);
|
|
19207
19232
|
if (credit?.status !== "available") continue;
|
|
19208
|
-
const
|
|
19209
|
-
if (
|
|
19210
|
-
nextExpiresAt =
|
|
19233
|
+
const expiry2 = safeTimestamp(credit.expires_at);
|
|
19234
|
+
if (expiry2 !== void 0 && (nextExpiresAt === void 0 || expiry2 < nextExpiresAt)) {
|
|
19235
|
+
nextExpiresAt = expiry2;
|
|
19211
19236
|
}
|
|
19212
19237
|
}
|
|
19213
19238
|
return {
|
|
@@ -19227,7 +19252,7 @@ async function fetchCodexUsage(deps = {}) {
|
|
|
19227
19252
|
if (credentials.accountId) headers["ChatGPT-Account-Id"] = credentials.accountId;
|
|
19228
19253
|
try {
|
|
19229
19254
|
const fetchImpl = deps.fetch ?? fetch;
|
|
19230
|
-
const
|
|
19255
|
+
const usage4 = parseCodexUsage(await getJson(fetchImpl, "https://chatgpt.com/backend-api/wham/usage", headers));
|
|
19231
19256
|
let credits;
|
|
19232
19257
|
try {
|
|
19233
19258
|
credits = parseResetCredits(await getJson(
|
|
@@ -19240,8 +19265,8 @@ async function fetchCodexUsage(deps = {}) {
|
|
|
19240
19265
|
}
|
|
19241
19266
|
return {
|
|
19242
19267
|
status: "ok",
|
|
19243
|
-
plan:
|
|
19244
|
-
windows:
|
|
19268
|
+
plan: usage4.plan,
|
|
19269
|
+
windows: usage4.windows,
|
|
19245
19270
|
...credits ? { credits } : {},
|
|
19246
19271
|
fetchedAt: (deps.now ?? Date.now)()
|
|
19247
19272
|
};
|
|
@@ -19355,12 +19380,12 @@ async function fetchCursorUsage(deps = {}) {
|
|
|
19355
19380
|
};
|
|
19356
19381
|
try {
|
|
19357
19382
|
const fetchImpl = deps.fetch ?? fetch;
|
|
19358
|
-
const
|
|
19383
|
+
const usage4 = parseCursorUsage(await postJson(
|
|
19359
19384
|
fetchImpl,
|
|
19360
19385
|
"https://api2.cursor.sh/aiserver.v1.DashboardService/GetCurrentPeriodUsage",
|
|
19361
19386
|
headers
|
|
19362
19387
|
));
|
|
19363
|
-
if (
|
|
19388
|
+
if (usage4.status === "no_subscription") return { status: "no_subscription", method: credentials.method };
|
|
19364
19389
|
let plan;
|
|
19365
19390
|
try {
|
|
19366
19391
|
const planRoot = object2(await postJson(
|
|
@@ -19376,8 +19401,8 @@ async function fetchCursorUsage(deps = {}) {
|
|
|
19376
19401
|
status: "ok",
|
|
19377
19402
|
method: credentials.method,
|
|
19378
19403
|
...plan ? { plan } : {},
|
|
19379
|
-
...
|
|
19380
|
-
windows:
|
|
19404
|
+
...usage4.cycleDays !== void 0 ? { cycleDays: usage4.cycleDays } : {},
|
|
19405
|
+
windows: usage4.windows,
|
|
19381
19406
|
fetchedAt: (deps.now ?? Date.now)()
|
|
19382
19407
|
};
|
|
19383
19408
|
} catch (error51) {
|
|
@@ -19462,16 +19487,16 @@ async function fetchKimiUsage(deps = {}) {
|
|
|
19462
19487
|
const apiKey = await deps.authService.getApiKey(KIMI_AUTH_PROVIDER_ID);
|
|
19463
19488
|
if (!apiKey) return { status: "signed_out" };
|
|
19464
19489
|
try {
|
|
19465
|
-
const
|
|
19490
|
+
const usage4 = parseKimiUsage(await getJson(
|
|
19466
19491
|
deps.fetch ?? fetch,
|
|
19467
19492
|
`${KIMI_CODE_API_BASE_URL}/v1/usages`,
|
|
19468
19493
|
{ Authorization: `Bearer ${apiKey}`, Accept: "application/json" }
|
|
19469
19494
|
));
|
|
19470
|
-
if (
|
|
19495
|
+
if (usage4.status === "no_subscription") return { status: "no_subscription" };
|
|
19471
19496
|
return {
|
|
19472
19497
|
status: "ok",
|
|
19473
|
-
...
|
|
19474
|
-
windows:
|
|
19498
|
+
...usage4.plan ? { plan: usage4.plan } : {},
|
|
19499
|
+
windows: usage4.windows,
|
|
19475
19500
|
fetchedAt: (deps.now ?? Date.now)()
|
|
19476
19501
|
};
|
|
19477
19502
|
} catch (error51) {
|
|
@@ -19681,6 +19706,268 @@ async function fetchOpencodeUsage(deps = {}) {
|
|
|
19681
19706
|
fetchedAt: now()
|
|
19682
19707
|
};
|
|
19683
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
|
+
}
|
|
19684
19971
|
var CACHE_TTL_MS = 12e4;
|
|
19685
19972
|
var STALE_TTL_MS = 18e5;
|
|
19686
19973
|
function createAiGatewayQuotaCollectors(deps) {
|
|
@@ -19696,7 +19983,8 @@ function createAiGatewayQuotaCollectors(deps) {
|
|
|
19696
19983
|
fetchCodex: () => fetchCodexUsage(providerDeps),
|
|
19697
19984
|
fetchCursor: () => fetchCursorUsage(providerDeps),
|
|
19698
19985
|
fetchKimi: () => fetchKimiUsage(providerDeps),
|
|
19699
|
-
fetchOpencode: () => fetchOpencodeUsage(providerDeps)
|
|
19986
|
+
fetchOpencode: () => fetchOpencodeUsage(providerDeps),
|
|
19987
|
+
fetchXai: () => fetchXaiUsage(providerDeps)
|
|
19700
19988
|
};
|
|
19701
19989
|
}
|
|
19702
19990
|
function isProviderSuccess(value) {
|
|
@@ -19709,7 +19997,8 @@ function createQuotaService(deps) {
|
|
|
19709
19997
|
codex: deps.fetchCodex,
|
|
19710
19998
|
cursor: deps.fetchCursor,
|
|
19711
19999
|
kimi: deps.fetchKimi,
|
|
19712
|
-
opencode: deps.fetchOpencode
|
|
20000
|
+
opencode: deps.fetchOpencode,
|
|
20001
|
+
xai: deps.fetchXai ?? (async () => ({ status: "signed_out" }))
|
|
19713
20002
|
};
|
|
19714
20003
|
const cache = /* @__PURE__ */ new Map();
|
|
19715
20004
|
const lastGood = /* @__PURE__ */ new Map();
|
|
@@ -19752,12 +20041,13 @@ function createQuotaService(deps) {
|
|
|
19752
20041
|
}
|
|
19753
20042
|
return {
|
|
19754
20043
|
async getSummary(options = {}) {
|
|
19755
|
-
const [claude, codex, cursor, kimi, opencode] = await Promise.all([
|
|
20044
|
+
const [claude, codex, cursor, kimi, opencode, xai] = await Promise.all([
|
|
19756
20045
|
load("claude", options.force === true || options.forceProvider === "claude"),
|
|
19757
20046
|
load("codex", options.force === true || options.forceProvider === "codex"),
|
|
19758
20047
|
load("cursor", options.force === true || options.forceProvider === "cursor"),
|
|
19759
20048
|
load("kimi", options.force === true || options.forceProvider === "kimi"),
|
|
19760
|
-
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")
|
|
19761
20051
|
]);
|
|
19762
20052
|
return {
|
|
19763
20053
|
providers: {
|
|
@@ -19765,7 +20055,8 @@ function createQuotaService(deps) {
|
|
|
19765
20055
|
codex: withRisk(codex),
|
|
19766
20056
|
cursor: withRisk(cursor),
|
|
19767
20057
|
kimi: withRisk(kimi),
|
|
19768
|
-
opencode: withRisk(opencode)
|
|
20058
|
+
opencode: withRisk(opencode),
|
|
20059
|
+
xai: withRisk(xai)
|
|
19769
20060
|
}
|
|
19770
20061
|
};
|
|
19771
20062
|
}
|
|
@@ -20628,7 +20919,36 @@ messageDesc(cursorAgentFile, 63);
|
|
|
20628
20919
|
messageDesc(cursorAgentFile, 70);
|
|
20629
20920
|
new Set(REASONING_EFFORTS);
|
|
20630
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
|
+
|
|
20631
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
|
+
}
|
|
20632
20952
|
async function handleSummary(req, res, ctx, service) {
|
|
20633
20953
|
if (req.method !== "GET") {
|
|
20634
20954
|
ctx.host.http.writeJson(res, 405, { error: "method_not_allowed" });
|
|
@@ -20639,7 +20959,9 @@ async function handleSummary(req, res, ctx, service) {
|
|
|
20639
20959
|
return;
|
|
20640
20960
|
}
|
|
20641
20961
|
const url2 = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
20642
|
-
|
|
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 });
|
|
20643
20965
|
}
|
|
20644
20966
|
async function handleConnect(req, res, ctx, service, serializeSettings) {
|
|
20645
20967
|
if (req.method !== "POST") {
|
|
@@ -20667,16 +20989,51 @@ async function handleConnect(req, res, ctx, service, serializeSettings) {
|
|
|
20667
20989
|
return;
|
|
20668
20990
|
}
|
|
20669
20991
|
await serializeSettings(async () => {
|
|
20670
|
-
const stored = await ctx.host.storage.readJson("quota", "settings");
|
|
20671
|
-
const settings = stored !== null && typeof stored === "object" && !Array.isArray(stored) ? stored : {};
|
|
20672
20992
|
const next = {
|
|
20673
|
-
...
|
|
20674
|
-
...typeof settings.cursorConnected === "boolean" ? { cursorConnected: settings.cursorConnected } : {},
|
|
20993
|
+
...retainedSettings(await readStoredSettings(ctx)),
|
|
20675
20994
|
...body.provider === "claude" ? { claudeConnected: body.connected } : { cursorConnected: body.connected }
|
|
20676
20995
|
};
|
|
20677
20996
|
await ctx.host.storage.writeJson("quota", "settings", next);
|
|
20678
20997
|
});
|
|
20679
|
-
|
|
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 });
|
|
20680
21037
|
}
|
|
20681
21038
|
|
|
20682
21039
|
// ../fleet-plugins/quota/routes.ts
|
|
@@ -20709,6 +21066,10 @@ var routes_default = definePlugin({
|
|
|
20709
21066
|
await handleConnect(req, res, ctx, service, serializeSettings);
|
|
20710
21067
|
return true;
|
|
20711
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" });
|
|
20712
21073
|
}
|
|
20713
21074
|
});
|
|
20714
21075
|
|