@dotobokuri/fleet-console 1.38.0 → 1.40.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 +43 -1090
- package/dist/client/assets/{_baseUniq-DjwZK6Tv.js → _baseUniq-Bao5GOIa.js} +1 -1
- package/dist/client/assets/{arc-CsNVntqy.js → arc-DnhHMLy9.js} +1 -1
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-Bi8y-wkL.js → architectureDiagram-Q4EWVU46-DAbURr0a.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-BB2SFu76.js → blockDiagram-DXYQGD6D-DE1raZQ6.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-BBxhzDJZ.js → c4Diagram-AHTNJAMY-beLZlUB3.js} +1 -1
- package/dist/client/assets/channel-Bn1wyUhs.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-CHPrg3nb.js → chunk-4BX2VUAB-DEqdunZS.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-DLvawyNq.js → chunk-4TB4RGXK-D8i5nx4L.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-CLptp70m.js → chunk-55IACEB6-eM3-fH_e.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-DS0cHoGJ.js → chunk-EDXVE4YY-nQoyt_Pd.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-DHhgFtRl.js → chunk-FMBD7UC4-D8Wq8CYj.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-CK20Lrh5.js → chunk-OYMX7WX6-Cx4Wr2ZT.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-BWu1gka4.js → chunk-QZHKN3VN-BM2v7Krr.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-Ce_uXK7W.js → chunk-YZCP3GAM-zTniPxm7.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-DBE3Ix3c.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-DBE3Ix3c.js +1 -0
- package/dist/client/assets/clone-COL47Wy_.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-lmqr4Zox.js → cose-bilkent-S5V4N54A-B5pl2A_p.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-Cqf_UZn0.js → dagre-KV5264BT-KHPgW9C2.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-B0F2kqdZ.js → diagram-5BDNPKRD-DCv28c7S.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-Y5Zcbc98.js → diagram-G4DWMVQ6-mT5WQ415.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-B-vHgBZG.js → diagram-MMDJMWI5-iKYfjiRC.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-BfBWDsiG.js → diagram-TYMM5635-_uqBP7vH.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-CB6H1ShS.js → erDiagram-SMLLAGMA-DxNN1WhN.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-BX9hx0Y6.js → flowDiagram-DWJPFMVM-DGTsrBvM.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-CHQBgJ4X.js → ganttDiagram-T4ZO3ILL-C17hg4lr.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-kbhc2fZE.js → gitGraphDiagram-UUTBAWPF-CeJ25hAd.js} +1 -1
- package/dist/client/assets/{graph-yw0uXaMX.js → graph-BQvb-BfT.js} +1 -1
- package/dist/client/assets/index-Ca6HkGPQ.css +1 -0
- package/dist/client/assets/index-Cr3Xq85X.js +452 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-BunmSTvD.js → infoDiagram-42DDH7IO-BctsofOZ.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-pI7F4-Qw.js → ishikawaDiagram-UXIWVN3A-PiPmxcg9.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-CncxtERF.js → journeyDiagram-VCZTEJTY-BwkjYq3n.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-CuD4mvBd.js → kanban-definition-6JOO6SKY-CSV4dYfF.js} +1 -1
- package/dist/client/assets/{layout-DGY5fGik.js → layout-DdReLJnP.js} +1 -1
- package/dist/client/assets/{linear-BYUWMe2Y.js → linear-B54euy_k.js} +1 -1
- package/dist/client/assets/{mermaid.core-Drc--ixx.js → mermaid.core-CF92I5mT.js} +4 -4
- package/dist/client/assets/{min-CJlVQqXb.js → min-BcETQ2qJ.js} +1 -1
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-BAzsMkbE.js → mindmap-definition-QFDTVHPH-CrmVPycO.js} +1 -1
- package/dist/client/assets/{pieDiagram-DEJITSTG-DHoVF7we.js → pieDiagram-DEJITSTG-B5d4jR0H.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-DGVM0S1B.js → quadrantDiagram-34T5L4WZ-DlgCNExa.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-Djmc_jRD.js → requirementDiagram-MS252O5E-CeuVib_X.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-B09zCkQb.js → sankeyDiagram-XADWPNL6-Bzxv73ik.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-BrFxwAwj.js → sequenceDiagram-FGHM5R23-B0Ondg0u.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-xzpsyAQ4.js → stateDiagram-FHFEXIEX-CuNHg5DJ.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BVPbUfrG.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-Kl-ES34i.js → timeline-definition-GMOUNBTQ-DXcye6z7.js} +1 -1
- package/dist/client/assets/{vennDiagram-DHZGUBPP-w_yD8Rg6.js → vennDiagram-DHZGUBPP-CL4rbpzL.js} +1 -1
- package/dist/client/assets/{wardley-RL74JXVD-CaZiB9zS.js → wardley-RL74JXVD-rVZBTA_R.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-CjUg4N9u.js → wardleyDiagram-NUSXRM2D-Cb8Arj1F.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-DsY1zkO5.js → xychartDiagram-5P7HB3ND-C-qp4Say.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/theme-boot.js +5 -4
- package/dist/fleet-plugins/ledger/routes.mjs +0 -33
- package/dist/fleet-plugins/quota/routes.mjs +672 -0
- package/dist/fleet-plugins/scuttlebutt/routes.mjs +1 -351
- package/dist/fleet-plugins/skills/routes.mjs +0 -33
- package/dist/fleet-plugins/terminal/routes.mjs +462 -2017
- package/package.json +1 -1
- package/dist/client/assets/channel-Ddn4Rope.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-qu46c_rd.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-qu46c_rd.js +0 -1
- package/dist/client/assets/clone-DDNlSib4.js +0 -1
- package/dist/client/assets/index-B1Ew8y-o.css +0 -1
- package/dist/client/assets/index-CWK41DSi.js +0 -452
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-CxkWw9en.js +0 -1
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFile } from 'child_process';
|
|
3
|
+
import { constants } from 'fs';
|
|
4
|
+
import fs from 'fs/promises';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { promisify } from 'util';
|
|
8
|
+
|
|
9
|
+
// sdk/plugin/node.ts
|
|
10
|
+
function definePlugin(definition) {
|
|
11
|
+
return definition;
|
|
12
|
+
}
|
|
13
|
+
function registerRouter(ctx, path2, handler) {
|
|
14
|
+
ctx.registerRouter(normalizePath(path2), handler);
|
|
15
|
+
}
|
|
16
|
+
function normalizePath(path2) {
|
|
17
|
+
if (path2.length === 0 || path2 === "/") return "/";
|
|
18
|
+
return path2;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ../fleet-plugins/quota/server/handlers.ts
|
|
22
|
+
async function handleSummary(req, res, ctx, service) {
|
|
23
|
+
if (req.method !== "GET") {
|
|
24
|
+
ctx.host.http.writeJson(res, 405, { error: "method_not_allowed" });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (!ctx.host.security.isTerminalAuthorized(req)) {
|
|
28
|
+
ctx.host.http.writeJson(res, 401, { error: "unauthorized" });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
32
|
+
ctx.host.http.writeJson(res, 200, await service.getSummary({ force: url.searchParams.get("force") === "1" }));
|
|
33
|
+
}
|
|
34
|
+
async function handleConnect(req, res, ctx, service, serializeSettings) {
|
|
35
|
+
if (req.method !== "POST") {
|
|
36
|
+
ctx.host.http.writeJson(res, 405, { error: "method_not_allowed" });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!ctx.host.security.isTerminalAuthorized(req)) {
|
|
40
|
+
ctx.host.http.writeJson(res, 401, { error: "unauthorized" });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const contentType = req.headers["content-type"];
|
|
44
|
+
const mediaType = typeof contentType === "string" ? contentType.split(";", 1)[0]?.trim().toLowerCase() : void 0;
|
|
45
|
+
if (mediaType !== "application/json") {
|
|
46
|
+
ctx.host.http.writeJson(res, 415, { error: "unsupported_media_type" });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
let body;
|
|
50
|
+
try {
|
|
51
|
+
body = await ctx.host.http.readJsonBody(req);
|
|
52
|
+
} catch {
|
|
53
|
+
body = null;
|
|
54
|
+
}
|
|
55
|
+
if (!body || Object.keys(body).length !== 2 || body.provider !== "claude" && body.provider !== "cursor" || typeof body.connected !== "boolean") {
|
|
56
|
+
ctx.host.http.writeJson(res, 400, { error: "invalid_connect_request" });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
await serializeSettings(async () => {
|
|
60
|
+
const stored = await ctx.host.storage.readJson("quota", "settings");
|
|
61
|
+
const settings = stored !== null && typeof stored === "object" && !Array.isArray(stored) ? stored : {};
|
|
62
|
+
const next = {
|
|
63
|
+
...typeof settings.claudeConnected === "boolean" ? { claudeConnected: settings.claudeConnected } : {},
|
|
64
|
+
...typeof settings.cursorConnected === "boolean" ? { cursorConnected: settings.cursorConnected } : {},
|
|
65
|
+
...body.provider === "claude" ? { claudeConnected: body.connected } : { cursorConnected: body.connected }
|
|
66
|
+
};
|
|
67
|
+
await ctx.host.storage.writeJson("quota", "settings", next);
|
|
68
|
+
});
|
|
69
|
+
ctx.host.http.writeJson(res, 200, await service.getSummary({ forceProvider: body.provider }));
|
|
70
|
+
}
|
|
71
|
+
var execFileAsync = promisify(execFile);
|
|
72
|
+
var MAX_CREDENTIAL_BYTES = 65536;
|
|
73
|
+
var CREDENTIAL_OPEN_FLAGS = process.platform === "win32" ? constants.O_RDONLY : constants.O_RDONLY | constants.O_NONBLOCK;
|
|
74
|
+
async function readBoundedFile(filePath, maxBytes) {
|
|
75
|
+
const handle = await fs.open(filePath, CREDENTIAL_OPEN_FLAGS);
|
|
76
|
+
try {
|
|
77
|
+
const stat = await handle.stat();
|
|
78
|
+
if (!stat.isFile() || stat.size > maxBytes) return null;
|
|
79
|
+
const buffer = Buffer.allocUnsafe(maxBytes + 1);
|
|
80
|
+
let totalBytes = 0;
|
|
81
|
+
while (totalBytes < buffer.byteLength) {
|
|
82
|
+
const { bytesRead } = await handle.read(
|
|
83
|
+
buffer,
|
|
84
|
+
totalBytes,
|
|
85
|
+
buffer.byteLength - totalBytes,
|
|
86
|
+
totalBytes
|
|
87
|
+
);
|
|
88
|
+
if (bytesRead === 0) break;
|
|
89
|
+
totalBytes += bytesRead;
|
|
90
|
+
}
|
|
91
|
+
return totalBytes > maxBytes ? null : buffer.subarray(0, totalBytes).toString("utf8");
|
|
92
|
+
} finally {
|
|
93
|
+
await handle.close();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
var defaultCredentialDeps = {
|
|
97
|
+
platform: process.platform,
|
|
98
|
+
homedir: os.homedir,
|
|
99
|
+
env: process.env,
|
|
100
|
+
readBounded: readBoundedFile,
|
|
101
|
+
execFile: async (file, args, options) => {
|
|
102
|
+
const result = await execFileAsync(file, [...args], options);
|
|
103
|
+
return result.stdout;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
function record(value) {
|
|
107
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
108
|
+
}
|
|
109
|
+
function optionalString(value) {
|
|
110
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
111
|
+
}
|
|
112
|
+
function optionalTrimmedString(value) {
|
|
113
|
+
if (typeof value !== "string") return void 0;
|
|
114
|
+
const trimmed = value.trim();
|
|
115
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
116
|
+
}
|
|
117
|
+
function optionalEpoch(value) {
|
|
118
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
119
|
+
}
|
|
120
|
+
function parseClaudeCredentialJson(raw, method) {
|
|
121
|
+
if (raw.length > MAX_CREDENTIAL_BYTES) return null;
|
|
122
|
+
try {
|
|
123
|
+
const parsed = record(JSON.parse(raw));
|
|
124
|
+
if (!parsed) return null;
|
|
125
|
+
const oauth = record(parsed.claudeAiOauth);
|
|
126
|
+
const accessToken = optionalString(oauth?.accessToken ?? parsed.accessToken);
|
|
127
|
+
if (!accessToken) return null;
|
|
128
|
+
return {
|
|
129
|
+
accessToken,
|
|
130
|
+
expiresAt: optionalEpoch(oauth?.expiresAt ?? parsed.expiresAt),
|
|
131
|
+
subscriptionType: optionalString(oauth?.subscriptionType ?? parsed.subscriptionType),
|
|
132
|
+
method
|
|
133
|
+
};
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function resolveClaudeCredentials(deps) {
|
|
139
|
+
if (deps.platform === "darwin") {
|
|
140
|
+
try {
|
|
141
|
+
const raw = await deps.execFile(
|
|
142
|
+
"security",
|
|
143
|
+
["find-generic-password", "-s", "Claude Code-credentials", "-w"],
|
|
144
|
+
{ timeout: 5e3 }
|
|
145
|
+
);
|
|
146
|
+
const credential = raw.length <= MAX_CREDENTIAL_BYTES ? parseClaudeCredentialJson(raw, "keychain") : null;
|
|
147
|
+
if (credential) return credential;
|
|
148
|
+
} catch {
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const configDir = deps.env.CLAUDE_CONFIG_DIR || path.join(deps.homedir(), ".claude");
|
|
152
|
+
try {
|
|
153
|
+
const raw = await deps.readBounded(path.join(configDir, ".credentials.json"), MAX_CREDENTIAL_BYTES);
|
|
154
|
+
return raw === null || raw.length > MAX_CREDENTIAL_BYTES ? null : parseClaudeCredentialJson(raw, "file");
|
|
155
|
+
} catch {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async function resolveCodexCredentials(deps) {
|
|
160
|
+
const home = deps.env.CODEX_HOME || path.join(deps.homedir(), ".codex");
|
|
161
|
+
try {
|
|
162
|
+
const raw = await deps.readBounded(path.join(home, "auth.json"), MAX_CREDENTIAL_BYTES);
|
|
163
|
+
if (raw === null || raw.length > MAX_CREDENTIAL_BYTES) return null;
|
|
164
|
+
const parsed = record(JSON.parse(raw));
|
|
165
|
+
const tokens = record(parsed?.tokens);
|
|
166
|
+
const accessToken = optionalString(tokens?.access_token);
|
|
167
|
+
if (!accessToken) return null;
|
|
168
|
+
return { accessToken, accountId: optionalString(tokens?.account_id) };
|
|
169
|
+
} catch {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function parseCursorCredentialJson(raw, method) {
|
|
174
|
+
if (raw.length > MAX_CREDENTIAL_BYTES) return null;
|
|
175
|
+
try {
|
|
176
|
+
const parsed = record(JSON.parse(raw));
|
|
177
|
+
if (!parsed) return null;
|
|
178
|
+
const tokens = record(parsed.tokens);
|
|
179
|
+
const cursorAuth = record(parsed.cursorAuth);
|
|
180
|
+
const accessToken = [
|
|
181
|
+
parsed.accessToken,
|
|
182
|
+
parsed.access_token,
|
|
183
|
+
tokens?.accessToken,
|
|
184
|
+
tokens?.access_token,
|
|
185
|
+
cursorAuth?.accessToken,
|
|
186
|
+
cursorAuth?.access_token
|
|
187
|
+
].map(optionalTrimmedString).find((value) => value !== void 0);
|
|
188
|
+
return accessToken ? { accessToken, method } : null;
|
|
189
|
+
} catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async function resolveCursorCredentials(deps) {
|
|
194
|
+
if (deps.platform === "darwin") {
|
|
195
|
+
try {
|
|
196
|
+
const raw = await deps.execFile(
|
|
197
|
+
"security",
|
|
198
|
+
["find-generic-password", "-s", "cursor-access-token", "-a", "cursor-user", "-w"],
|
|
199
|
+
{ timeout: 5e3 }
|
|
200
|
+
);
|
|
201
|
+
const accessToken = raw.length <= MAX_CREDENTIAL_BYTES ? optionalTrimmedString(raw) : void 0;
|
|
202
|
+
if (accessToken) return { accessToken, method: "keychain" };
|
|
203
|
+
} catch {
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const home = deps.homedir();
|
|
207
|
+
const authFile = deps.platform === "win32" ? path.join(deps.env.APPDATA || path.join(home, "AppData", "Roaming"), "Cursor", "auth.json") : deps.platform === "darwin" ? path.join(home, ".cursor", "auth.json") : path.join(deps.env.XDG_CONFIG_HOME || path.join(home, ".config"), "cursor", "auth.json");
|
|
208
|
+
try {
|
|
209
|
+
const raw = await deps.readBounded(authFile, MAX_CREDENTIAL_BYTES);
|
|
210
|
+
return raw === null || raw.length > MAX_CREDENTIAL_BYTES ? null : parseCursorCredentialJson(raw, "file");
|
|
211
|
+
} catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ../fleet-plugins/quota/server/providers.ts
|
|
217
|
+
var REQUEST_TIMEOUT_MS = 1e4;
|
|
218
|
+
var MAX_RESPONSE_BYTES = 262144;
|
|
219
|
+
var MAX_WINDOWS = 8;
|
|
220
|
+
var MAX_CREDIT_ENTRIES = 256;
|
|
221
|
+
var ProviderHttpError = class extends Error {
|
|
222
|
+
constructor(statusCode) {
|
|
223
|
+
super(statusCode === void 0 ? "Provider request failed" : `Provider request failed (${statusCode})`);
|
|
224
|
+
this.statusCode = statusCode;
|
|
225
|
+
}
|
|
226
|
+
statusCode;
|
|
227
|
+
};
|
|
228
|
+
var ProviderResponseTooLargeError = class extends Error {
|
|
229
|
+
constructor() {
|
|
230
|
+
super("Provider response too large");
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
function object(value) {
|
|
234
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
235
|
+
}
|
|
236
|
+
function array(value) {
|
|
237
|
+
return Array.isArray(value) ? value : [];
|
|
238
|
+
}
|
|
239
|
+
function safeTimestamp(value) {
|
|
240
|
+
let result;
|
|
241
|
+
if (typeof value === "number" && Number.isFinite(value)) result = value < 1e12 ? value * 1e3 : value;
|
|
242
|
+
if (typeof value === "string") {
|
|
243
|
+
const trimmed = value.trim();
|
|
244
|
+
if (/^[1-9]\d{9,14}$/.test(trimmed)) {
|
|
245
|
+
const parsed = Number(trimmed);
|
|
246
|
+
if (Number.isFinite(parsed)) result = parsed < 1e12 ? parsed * 1e3 : parsed;
|
|
247
|
+
} else {
|
|
248
|
+
const parsed = Date.parse(value);
|
|
249
|
+
if (Number.isFinite(parsed)) result = parsed;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (result === void 0 || !Number.isFinite(result) || result < 0) return void 0;
|
|
253
|
+
const rounded = Math.round(result);
|
|
254
|
+
return Number.isSafeInteger(rounded) ? rounded : void 0;
|
|
255
|
+
}
|
|
256
|
+
function percent(value) {
|
|
257
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 0;
|
|
258
|
+
return Math.min(100, Math.max(0, Math.round(value)));
|
|
259
|
+
}
|
|
260
|
+
function validatedString(value, pattern) {
|
|
261
|
+
if (typeof value !== "string") return void 0;
|
|
262
|
+
const trimmed = value.trim();
|
|
263
|
+
if (!pattern.test(trimmed)) return void 0;
|
|
264
|
+
if (/^bearer /i.test(trimmed)) return void 0;
|
|
265
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(trimmed)) return void 0;
|
|
266
|
+
if (/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$/.test(trimmed)) return void 0;
|
|
267
|
+
return trimmed;
|
|
268
|
+
}
|
|
269
|
+
function titleCase(value) {
|
|
270
|
+
const validated = validatedString(value, /^[A-Za-z0-9][A-Za-z0-9 .+-]{0,23}$/);
|
|
271
|
+
if (!validated) return void 0;
|
|
272
|
+
if (/^\d+$/.test(validated)) return void 0;
|
|
273
|
+
return `${validated[0]?.toUpperCase() ?? ""}${validated.slice(1)}`;
|
|
274
|
+
}
|
|
275
|
+
function modelLabel(value) {
|
|
276
|
+
return validatedString(value, /^[A-Za-z0-9][A-Za-z0-9 .+-]{0,39}$/) ?? "Model";
|
|
277
|
+
}
|
|
278
|
+
function firstClaudePercent(row) {
|
|
279
|
+
for (const key of ["percent", "used_percentage", "utilization"]) {
|
|
280
|
+
const value = row[key];
|
|
281
|
+
if (typeof value === "number" && Number.isFinite(value)) return percent(value);
|
|
282
|
+
}
|
|
283
|
+
return void 0;
|
|
284
|
+
}
|
|
285
|
+
function claudeWindow(id, source, label) {
|
|
286
|
+
const row = object(source);
|
|
287
|
+
if (!row) return null;
|
|
288
|
+
const usedPercent = firstClaudePercent(row);
|
|
289
|
+
if (usedPercent === void 0) return null;
|
|
290
|
+
const resetsAt = safeTimestamp(row.resets_at);
|
|
291
|
+
return {
|
|
292
|
+
id,
|
|
293
|
+
...label ? { label } : {},
|
|
294
|
+
usedPercent,
|
|
295
|
+
...resetsAt !== void 0 ? { resetsAt } : {}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function parseClaudeUsage(payload) {
|
|
299
|
+
const root = object(payload) ?? {};
|
|
300
|
+
const namedSession = claudeWindow("session", root.five_hour);
|
|
301
|
+
const namedWeekly = claudeWindow("weekly", root.seven_day);
|
|
302
|
+
let fallbackSession = null;
|
|
303
|
+
let fallbackWeekly = null;
|
|
304
|
+
const modelWindows = [];
|
|
305
|
+
const limits = array(root.limits);
|
|
306
|
+
for (let index = 0; index < limits.length && index < MAX_CREDIT_ENTRIES; index += 1) {
|
|
307
|
+
const item = limits[index];
|
|
308
|
+
const limit = object(item);
|
|
309
|
+
if (!limit) continue;
|
|
310
|
+
if (limit.kind === "session" && !namedSession && !fallbackSession) {
|
|
311
|
+
fallbackSession = claudeWindow("session", limit);
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (limit.kind === "weekly_all" && !namedWeekly && !fallbackWeekly) {
|
|
315
|
+
fallbackWeekly = claudeWindow("weekly", limit);
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (limit.kind === "weekly_scoped" && modelWindows.length < MAX_WINDOWS) {
|
|
319
|
+
const model = object(object(limit.scope)?.model);
|
|
320
|
+
const row = claudeWindow("model", limit, modelLabel(model?.display_name));
|
|
321
|
+
if (row) modelWindows.push(row);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (modelWindows.length === 0) {
|
|
325
|
+
for (const alias of [root.fable_weekly, root.fable_seven_day, root.seven_day_fable]) {
|
|
326
|
+
const row = claudeWindow("model", alias, "Fable");
|
|
327
|
+
if (row) {
|
|
328
|
+
modelWindows.push(row);
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const windows = [];
|
|
334
|
+
for (const row of [namedSession ?? fallbackSession, namedWeekly ?? fallbackWeekly, ...modelWindows]) {
|
|
335
|
+
if (row) windows.push(row);
|
|
336
|
+
if (windows.length === MAX_WINDOWS) break;
|
|
337
|
+
}
|
|
338
|
+
return { windows };
|
|
339
|
+
}
|
|
340
|
+
function codexWindow(source, fallbackId) {
|
|
341
|
+
const row = object(source);
|
|
342
|
+
if (!row) return null;
|
|
343
|
+
const seconds = typeof row.limit_window_seconds === "number" ? row.limit_window_seconds : 0;
|
|
344
|
+
const minutes = seconds / 60;
|
|
345
|
+
const id = minutes >= 240 && minutes <= 360 ? "session" : minutes >= 8064 && minutes <= 12096 ? "weekly" : fallbackId;
|
|
346
|
+
return {
|
|
347
|
+
id,
|
|
348
|
+
usedPercent: percent(row.used_percent),
|
|
349
|
+
...safeTimestamp(row.reset_at) !== void 0 ? { resetsAt: safeTimestamp(row.reset_at) } : {}
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function parseCodexUsage(payload) {
|
|
353
|
+
const root = object(payload) ?? {};
|
|
354
|
+
const rateLimit = object(root.rate_limit) ?? {};
|
|
355
|
+
return {
|
|
356
|
+
windows: [
|
|
357
|
+
codexWindow(rateLimit.primary_window, "session"),
|
|
358
|
+
codexWindow(rateLimit.secondary_window, "weekly")
|
|
359
|
+
].filter((row) => row !== null),
|
|
360
|
+
plan: titleCase(root.plan_type)
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function parseCursorUsage(payload) {
|
|
364
|
+
const root = object(payload);
|
|
365
|
+
const planUsage = object(root?.planUsage);
|
|
366
|
+
if (root?.enabled === false || !planUsage) return { status: "no_subscription" };
|
|
367
|
+
const resetsAt = safeTimestamp(root?.billingCycleEnd);
|
|
368
|
+
const windows = [];
|
|
369
|
+
for (const [value, label] of [
|
|
370
|
+
[planUsage.totalPercentUsed, void 0],
|
|
371
|
+
[planUsage.autoPercentUsed, "Auto"],
|
|
372
|
+
[planUsage.apiPercentUsed, "API"]
|
|
373
|
+
]) {
|
|
374
|
+
if (typeof value !== "number" || !Number.isFinite(value)) continue;
|
|
375
|
+
windows.push({
|
|
376
|
+
id: "cycle",
|
|
377
|
+
...label ? { label } : {},
|
|
378
|
+
usedPercent: percent(value),
|
|
379
|
+
...resetsAt !== void 0 ? { resetsAt } : {}
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
if (windows.length === 0) return { status: "no_subscription" };
|
|
383
|
+
const cycleStart = safeTimestamp(root?.billingCycleStart);
|
|
384
|
+
const cycleEnd = safeTimestamp(root?.billingCycleEnd);
|
|
385
|
+
let cycleDays;
|
|
386
|
+
if (cycleStart !== void 0 && cycleEnd !== void 0 && cycleEnd > cycleStart) {
|
|
387
|
+
const days = Math.round((cycleEnd - cycleStart) / 864e5);
|
|
388
|
+
if (days >= 1 && days <= 400) cycleDays = days;
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
status: "ok",
|
|
392
|
+
windows,
|
|
393
|
+
...cycleDays !== void 0 ? { cycleDays } : {}
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
function parseResetCredits(payload) {
|
|
397
|
+
const root = object(payload);
|
|
398
|
+
if (!root || !Number.isSafeInteger(root.available_count) || root.available_count < 0) return void 0;
|
|
399
|
+
const credits = array(root.credits);
|
|
400
|
+
let nextExpiresAt;
|
|
401
|
+
for (let index = 0; index < credits.length && index < MAX_CREDIT_ENTRIES; index += 1) {
|
|
402
|
+
const credit = object(credits[index]);
|
|
403
|
+
if (credit?.status !== "available") continue;
|
|
404
|
+
const expiry = safeTimestamp(credit.expires_at);
|
|
405
|
+
if (expiry !== void 0 && (nextExpiresAt === void 0 || expiry < nextExpiresAt)) {
|
|
406
|
+
nextExpiresAt = expiry;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
available: root.available_count,
|
|
411
|
+
...nextExpiresAt !== void 0 ? { nextExpiresAt } : {}
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
async function requestJson(fetchImpl, url, init) {
|
|
415
|
+
const controller = new AbortController();
|
|
416
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
417
|
+
try {
|
|
418
|
+
const response = await fetchImpl(url, {
|
|
419
|
+
...init,
|
|
420
|
+
signal: controller.signal,
|
|
421
|
+
redirect: "error"
|
|
422
|
+
});
|
|
423
|
+
if (response.url !== "" && response.url !== url) throw new ProviderHttpError();
|
|
424
|
+
if (!response.ok) throw new ProviderHttpError(response.status);
|
|
425
|
+
const contentLength = Number(response.headers.get("content-length"));
|
|
426
|
+
if (Number.isFinite(contentLength) && contentLength > MAX_RESPONSE_BYTES) {
|
|
427
|
+
controller.abort();
|
|
428
|
+
throw new ProviderResponseTooLargeError();
|
|
429
|
+
}
|
|
430
|
+
if (!response.body) {
|
|
431
|
+
const text = await response.text();
|
|
432
|
+
if (text.length > MAX_RESPONSE_BYTES) {
|
|
433
|
+
controller.abort();
|
|
434
|
+
throw new ProviderResponseTooLargeError();
|
|
435
|
+
}
|
|
436
|
+
return JSON.parse(text);
|
|
437
|
+
}
|
|
438
|
+
const reader = response.body.getReader();
|
|
439
|
+
const chunks = [];
|
|
440
|
+
let totalBytes = 0;
|
|
441
|
+
while (true) {
|
|
442
|
+
const { done, value } = await reader.read();
|
|
443
|
+
if (done) break;
|
|
444
|
+
totalBytes += value.byteLength;
|
|
445
|
+
if (totalBytes > MAX_RESPONSE_BYTES) {
|
|
446
|
+
controller.abort();
|
|
447
|
+
throw new ProviderResponseTooLargeError();
|
|
448
|
+
}
|
|
449
|
+
chunks.push(value);
|
|
450
|
+
}
|
|
451
|
+
const body = new Uint8Array(totalBytes);
|
|
452
|
+
let offset = 0;
|
|
453
|
+
for (const chunk of chunks) {
|
|
454
|
+
body.set(chunk, offset);
|
|
455
|
+
offset += chunk.byteLength;
|
|
456
|
+
}
|
|
457
|
+
return JSON.parse(new TextDecoder().decode(body));
|
|
458
|
+
} finally {
|
|
459
|
+
clearTimeout(timer);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
async function getJson(fetchImpl, url, headers) {
|
|
463
|
+
return requestJson(fetchImpl, url, { method: "GET", headers });
|
|
464
|
+
}
|
|
465
|
+
async function postJson(fetchImpl, url, headers) {
|
|
466
|
+
return requestJson(fetchImpl, url, { method: "POST", headers, body: "{}" });
|
|
467
|
+
}
|
|
468
|
+
function expired(error) {
|
|
469
|
+
return error instanceof ProviderHttpError && (error.statusCode === 401 || error.statusCode === 403) ? { status: "expired" } : null;
|
|
470
|
+
}
|
|
471
|
+
async function fetchClaudeUsage(deps = {}) {
|
|
472
|
+
const credentials = await resolveClaudeCredentials(deps.credentials ?? defaultCredentialDeps);
|
|
473
|
+
if (!credentials) return { status: "signed_out" };
|
|
474
|
+
if (credentials.expiresAt !== void 0 && credentials.expiresAt <= (deps.now ?? Date.now)()) {
|
|
475
|
+
return { status: "expired", method: credentials.method };
|
|
476
|
+
}
|
|
477
|
+
try {
|
|
478
|
+
const parsed = parseClaudeUsage(await getJson(
|
|
479
|
+
deps.fetch ?? fetch,
|
|
480
|
+
"https://api.anthropic.com/api/oauth/usage",
|
|
481
|
+
{
|
|
482
|
+
Authorization: `Bearer ${credentials.accessToken}`,
|
|
483
|
+
"anthropic-beta": "oauth-2025-04-20",
|
|
484
|
+
"User-Agent": "claude-code/2.1.0"
|
|
485
|
+
}
|
|
486
|
+
));
|
|
487
|
+
return {
|
|
488
|
+
status: "ok",
|
|
489
|
+
method: credentials.method,
|
|
490
|
+
plan: titleCase(credentials.subscriptionType),
|
|
491
|
+
windows: parsed.windows,
|
|
492
|
+
fetchedAt: (deps.now ?? Date.now)()
|
|
493
|
+
};
|
|
494
|
+
} catch (error) {
|
|
495
|
+
const result = expired(error);
|
|
496
|
+
if (result) return { ...result, method: credentials.method };
|
|
497
|
+
throw error;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
async function fetchCodexUsage(deps = {}) {
|
|
501
|
+
const credentials = await resolveCodexCredentials(deps.credentials ?? defaultCredentialDeps);
|
|
502
|
+
if (!credentials) return { status: "signed_out" };
|
|
503
|
+
const headers = {
|
|
504
|
+
Authorization: `Bearer ${credentials.accessToken}`,
|
|
505
|
+
"User-Agent": "codex-cli",
|
|
506
|
+
"OpenAI-Beta": "codex-1",
|
|
507
|
+
originator: "Codex Desktop"
|
|
508
|
+
};
|
|
509
|
+
if (credentials.accountId) headers["ChatGPT-Account-Id"] = credentials.accountId;
|
|
510
|
+
try {
|
|
511
|
+
const fetchImpl = deps.fetch ?? fetch;
|
|
512
|
+
const usage = parseCodexUsage(await getJson(fetchImpl, "https://chatgpt.com/backend-api/wham/usage", headers));
|
|
513
|
+
let credits;
|
|
514
|
+
try {
|
|
515
|
+
credits = parseResetCredits(await getJson(
|
|
516
|
+
fetchImpl,
|
|
517
|
+
"https://chatgpt.com/backend-api/wham/rate-limit-reset-credits",
|
|
518
|
+
headers
|
|
519
|
+
));
|
|
520
|
+
} catch {
|
|
521
|
+
credits = void 0;
|
|
522
|
+
}
|
|
523
|
+
return {
|
|
524
|
+
status: "ok",
|
|
525
|
+
plan: usage.plan,
|
|
526
|
+
windows: usage.windows,
|
|
527
|
+
...credits ? { credits } : {},
|
|
528
|
+
fetchedAt: (deps.now ?? Date.now)()
|
|
529
|
+
};
|
|
530
|
+
} catch (error) {
|
|
531
|
+
const result = expired(error);
|
|
532
|
+
if (result) return result;
|
|
533
|
+
throw error;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
async function fetchCursorUsage(deps = {}) {
|
|
537
|
+
const credentials = await resolveCursorCredentials(deps.credentials ?? defaultCredentialDeps);
|
|
538
|
+
if (!credentials) return { status: "signed_out" };
|
|
539
|
+
const headers = {
|
|
540
|
+
Authorization: `Bearer ${credentials.accessToken}`,
|
|
541
|
+
"Content-Type": "application/json",
|
|
542
|
+
"Connect-Protocol-Version": "1"
|
|
543
|
+
};
|
|
544
|
+
try {
|
|
545
|
+
const fetchImpl = deps.fetch ?? fetch;
|
|
546
|
+
const usage = parseCursorUsage(await postJson(
|
|
547
|
+
fetchImpl,
|
|
548
|
+
"https://api2.cursor.sh/aiserver.v1.DashboardService/GetCurrentPeriodUsage",
|
|
549
|
+
headers
|
|
550
|
+
));
|
|
551
|
+
if (usage.status === "no_subscription") return { status: "no_subscription", method: credentials.method };
|
|
552
|
+
let plan;
|
|
553
|
+
try {
|
|
554
|
+
const planRoot = object(await postJson(
|
|
555
|
+
fetchImpl,
|
|
556
|
+
"https://api2.cursor.sh/aiserver.v1.DashboardService/GetPlanInfo",
|
|
557
|
+
headers
|
|
558
|
+
));
|
|
559
|
+
plan = titleCase(object(planRoot?.planInfo)?.planName ?? planRoot?.planName);
|
|
560
|
+
} catch {
|
|
561
|
+
plan = void 0;
|
|
562
|
+
}
|
|
563
|
+
return {
|
|
564
|
+
status: "ok",
|
|
565
|
+
method: credentials.method,
|
|
566
|
+
...plan ? { plan } : {},
|
|
567
|
+
...usage.cycleDays !== void 0 ? { cycleDays: usage.cycleDays } : {},
|
|
568
|
+
windows: usage.windows,
|
|
569
|
+
fetchedAt: (deps.now ?? Date.now)()
|
|
570
|
+
};
|
|
571
|
+
} catch (error) {
|
|
572
|
+
const result = expired(error);
|
|
573
|
+
if (result) return { ...result, method: credentials.method };
|
|
574
|
+
throw error;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
function sanitizeProviderError(error) {
|
|
578
|
+
if (error instanceof ProviderResponseTooLargeError) return "Provider response too large";
|
|
579
|
+
if (error instanceof DOMException && error.name === "AbortError") return "Provider request timed out";
|
|
580
|
+
if (error instanceof ProviderHttpError && error.statusCode !== void 0) {
|
|
581
|
+
return `Provider request failed (${error.statusCode})`;
|
|
582
|
+
}
|
|
583
|
+
return "Provider request failed";
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// ../fleet-plugins/quota/server/service.ts
|
|
587
|
+
var CACHE_TTL_MS = 12e4;
|
|
588
|
+
var STALE_TTL_MS = 18e5;
|
|
589
|
+
function isProviderSuccess(value) {
|
|
590
|
+
return value.status === "ok" && Array.isArray(value.windows) && typeof value.fetchedAt === "number";
|
|
591
|
+
}
|
|
592
|
+
function createQuotaService(deps) {
|
|
593
|
+
const now = deps.now ?? Date.now;
|
|
594
|
+
const fetchers = {
|
|
595
|
+
claude: deps.fetchClaude ?? (() => fetchClaudeUsage()),
|
|
596
|
+
codex: deps.fetchCodex ?? (() => fetchCodexUsage()),
|
|
597
|
+
cursor: deps.fetchCursor ?? (() => fetchCursorUsage())
|
|
598
|
+
};
|
|
599
|
+
const cache = /* @__PURE__ */ new Map();
|
|
600
|
+
const lastGood = /* @__PURE__ */ new Map();
|
|
601
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
602
|
+
async function load(id, force) {
|
|
603
|
+
if (id === "claude" && !await deps.isClaudeConnected() || id === "cursor" && !await deps.isCursorConnected()) {
|
|
604
|
+
return { status: "not_connected", method: (deps.platform ?? process.platform) === "darwin" ? "keychain" : "file" };
|
|
605
|
+
}
|
|
606
|
+
const cached = cache.get(id);
|
|
607
|
+
if (!force && cached && cached.expiresAt > now()) {
|
|
608
|
+
const staleStillValid = cached.value.status !== "stale" || typeof cached.value.fetchedAt === "number" && now() - cached.value.fetchedAt <= STALE_TTL_MS;
|
|
609
|
+
if (staleStillValid) return cached.value;
|
|
610
|
+
cache.delete(id);
|
|
611
|
+
}
|
|
612
|
+
const pending = inFlight.get(id);
|
|
613
|
+
if (pending) return pending;
|
|
614
|
+
const task = fetchers[id]().then((value) => {
|
|
615
|
+
if (isProviderSuccess(value)) lastGood.set(id, value);
|
|
616
|
+
cache.set(id, { value, expiresAt: now() + CACHE_TTL_MS });
|
|
617
|
+
return value;
|
|
618
|
+
}).catch((error) => {
|
|
619
|
+
const previous = lastGood.get(id);
|
|
620
|
+
const message = sanitizeProviderError(error);
|
|
621
|
+
const failedAt = now();
|
|
622
|
+
const value = previous && failedAt - previous.fetchedAt <= STALE_TTL_MS ? { ...previous, status: "stale", message } : { status: "error", message };
|
|
623
|
+
const expiresAt = value.status === "stale" && previous ? Math.min(failedAt + CACHE_TTL_MS, previous.fetchedAt + STALE_TTL_MS) : failedAt + CACHE_TTL_MS;
|
|
624
|
+
cache.set(id, { value, expiresAt });
|
|
625
|
+
return value;
|
|
626
|
+
}).finally(() => inFlight.delete(id));
|
|
627
|
+
inFlight.set(id, task);
|
|
628
|
+
return task;
|
|
629
|
+
}
|
|
630
|
+
return {
|
|
631
|
+
async getSummary(options = {}) {
|
|
632
|
+
const [claude, codex, cursor] = await Promise.all([
|
|
633
|
+
load("claude", options.force === true || options.forceProvider === "claude"),
|
|
634
|
+
load("codex", options.force === true || options.forceProvider === "codex"),
|
|
635
|
+
load("cursor", options.force === true || options.forceProvider === "cursor")
|
|
636
|
+
]);
|
|
637
|
+
return { providers: { claude, codex, cursor } };
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// ../fleet-plugins/quota/routes.ts
|
|
643
|
+
var routes_default = definePlugin({
|
|
644
|
+
id: "quota",
|
|
645
|
+
register(ctx) {
|
|
646
|
+
let settingsMutation = Promise.resolve();
|
|
647
|
+
const serializeSettings = (operation) => {
|
|
648
|
+
const result = settingsMutation.then(operation, operation);
|
|
649
|
+
settingsMutation = result.then(() => void 0, () => void 0);
|
|
650
|
+
return result;
|
|
651
|
+
};
|
|
652
|
+
const isConnected = async (provider) => {
|
|
653
|
+
const value = await ctx.host.storage.readJson("quota", "settings");
|
|
654
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && value[`${provider}Connected`] === true;
|
|
655
|
+
};
|
|
656
|
+
const service = createQuotaService({
|
|
657
|
+
platform: process.platform,
|
|
658
|
+
isClaudeConnected: () => isConnected("claude"),
|
|
659
|
+
isCursorConnected: () => isConnected("cursor")
|
|
660
|
+
});
|
|
661
|
+
registerRouter(ctx, "summary", async ({ req, res }) => {
|
|
662
|
+
await handleSummary(req, res, ctx, service);
|
|
663
|
+
return true;
|
|
664
|
+
});
|
|
665
|
+
registerRouter(ctx, "connect", async ({ req, res }) => {
|
|
666
|
+
await handleConnect(req, res, ctx, service, serializeSettings);
|
|
667
|
+
return true;
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
export { routes_default as default };
|