omnigateway 0.12.12 → 0.12.13

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 (56) hide show
  1. package/README.md +1 -0
  2. package/bin/omni.js +378 -21
  3. package/gateway.js +346 -13
  4. package/package.json +1 -1
  5. package/public/assets/{Chip-D4qvhu1D.js → Chip-D0XSbQUr.js} +1 -1
  6. package/public/assets/{CopyValue-SnsT2LML.js → CopyValue-DAlmkISB.js} +3 -3
  7. package/public/assets/{Field-BYBri2NV.js → Field-DG1317zr.js} +1 -1
  8. package/public/assets/Lamp-Dk9XrTCg.js +26 -0
  9. package/public/assets/{Panel-CWmOTDAP.js → Panel-CsovmtxF.js} +23 -4
  10. package/public/assets/Rack-CDUOJkS2.js +483 -0
  11. package/public/assets/{Readout-BTh732hP.js → Readout-DlT5oxGO.js} +1 -1
  12. package/public/assets/RequestTable-i3TtCB9m.js +93 -0
  13. package/public/assets/{SummaryDeck-7ivuca39.js → SummaryDeck-DrzTQQo5.js} +2 -2
  14. package/public/assets/{Toggle-t82IhqEt.js → Toggle-CdMD58sy.js} +1 -1
  15. package/public/assets/{TokenBreakdown-D1Wtza1y.js → TokenBreakdown-BC6nqo1v.js} +1 -1
  16. package/public/assets/{WindowChart-BeGPsccS.js → WindowChart-Bqbl-KFO.js} +2 -2
  17. package/public/assets/_app-CSru2YTE.js +1 -0
  18. package/public/assets/_app.accounts-B2lQkMb0.js +70 -0
  19. package/public/assets/_app.console-a2TJDoBC.js +24 -0
  20. package/public/assets/_app.database-CqSoTi2l.js +24 -0
  21. package/public/assets/_app.index-BV1kJJjA.js +67 -0
  22. package/public/assets/_app.keys-Dq5O0AG9.js +92 -0
  23. package/public/assets/_app.logs-PjRK5wqy.js +46 -0
  24. package/public/assets/_app.models-BfmeeB5e.js +148 -0
  25. package/public/assets/_app.plugins._pluginId-Owb05aFv.js +20 -0
  26. package/public/assets/{_app.settings-C3GzeJ4q.js → _app.settings-5i-vvT-H.js} +7 -7
  27. package/public/assets/_app.usage--5qFtU1W.js +76 -0
  28. package/public/assets/chevron-down-Bb5ZGvwk.js +1 -0
  29. package/public/assets/chevron-right-hwF5mD6-.js +1 -0
  30. package/public/assets/{client-k1tkFSNH.js → client-BcXc3lek.js} +4 -4
  31. package/public/assets/{dist-D03q-60i.js → dist-BBGxva0Y.js} +1 -1
  32. package/public/assets/index-D1RDaq3l.js +14 -0
  33. package/public/assets/{login-DW2Ipp21.js → login-GB0ZGUvA.js} +4 -4
  34. package/public/assets/plus-BgcHxxIr.js +1 -0
  35. package/public/assets/{preload-helper-BPaHpbO_.js → preload-helper-B7KDmXJ9.js} +1 -1
  36. package/public/assets/rotate-ccw-Do43Argv.js +1 -0
  37. package/public/assets/{shared-Dtkw6SE0.js → shared-DDpLUhCp.js} +2 -2
  38. package/public/assets/{trash-D4QWzEK_.js → trash-00950_yY.js} +1 -1
  39. package/public/index.html +5 -5
  40. package/public/assets/Lamp-DkvXKB-B.js +0 -26
  41. package/public/assets/Rack-CEuCdVXc.js +0 -483
  42. package/public/assets/RequestTable-D8L6iVJV.js +0 -93
  43. package/public/assets/_app-Drf03PWk.js +0 -1
  44. package/public/assets/_app.accounts-W1USUo2Q.js +0 -70
  45. package/public/assets/_app.console-Co9d2ZiD.js +0 -44
  46. package/public/assets/_app.database-CtFJLcfE.js +0 -24
  47. package/public/assets/_app.index-DigYfob3.js +0 -67
  48. package/public/assets/_app.keys-B1fRMyU-.js +0 -92
  49. package/public/assets/_app.logs-CFixPsUw.js +0 -60
  50. package/public/assets/_app.models-Dpl1O2RE.js +0 -148
  51. package/public/assets/_app.plugins._pluginId-BK0ADugW.js +0 -20
  52. package/public/assets/_app.usage-D2qlcEp4.js +0 -76
  53. package/public/assets/chevron-down-Bs0tSOyA.js +0 -1
  54. package/public/assets/chevron-right-DSaI1kFo.js +0 -1
  55. package/public/assets/index-Bk9sabex.js +0 -14
  56. package/public/assets/plus-BCfjC6nk.js +0 -1
package/gateway.js CHANGED
@@ -30177,12 +30177,18 @@ var ANTHROPIC_MODELS = {
30177
30177
  };
30178
30178
  var ONE_M_SUFFIX = "[1m]";
30179
30179
  var DATED_SUFFIX = /-\d{8}$/;
30180
- function anthropicReasoningForm(model) {
30180
+ function anthropicChoice(model) {
30181
30181
  let id = model.trim();
30182
30182
  if (id.toLowerCase().endsWith(ONE_M_SUFFIX))
30183
30183
  id = id.slice(0, -ONE_M_SUFFIX.length).trim();
30184
30184
  id = id.replace(DATED_SUFFIX, "");
30185
- return ANTHROPIC_MODELS.models.find((m) => m.id === id)?.reasoningForm ?? "adaptive";
30185
+ return ANTHROPIC_MODELS.models.find((m) => m.id === id);
30186
+ }
30187
+ function anthropicReasoningForm(model) {
30188
+ return anthropicChoice(model)?.reasoningForm ?? "adaptive";
30189
+ }
30190
+ function anthropicMaxOutputTokens(model) {
30191
+ return anthropicChoice(model)?.limits.maxOutputTokens;
30186
30192
  }
30187
30193
 
30188
30194
  // packages/providers/src/anthropic/descriptor.ts
@@ -31181,7 +31187,8 @@ function codecAdapter(id, capabilities2, codec2, origins) {
31181
31187
  credentials: req.credentials,
31182
31188
  fail,
31183
31189
  ...req.requestId === undefined ? {} : { requestId: req.requestId },
31184
- ...req.autoCache === undefined ? {} : { autoCacheEnabled: req.autoCache }
31190
+ ...req.autoCache === undefined ? {} : { autoCacheEnabled: req.autoCache },
31191
+ ...req.maxOutputTokens === undefined ? {} : { maxOutputTokens: req.maxOutputTokens }
31185
31192
  }));
31186
31193
  const described = built?.request ?? {};
31187
31194
  const request2 = {
@@ -32902,6 +32909,12 @@ function addAutoCacheBreakpoints(body, req, note) {
32902
32909
  lastHistory.cache_control = { type: "ephemeral" };
32903
32910
  note("anthropic:history-cache-breakpoint-added");
32904
32911
  }
32912
+ function defaultMaxTokens(model, saved) {
32913
+ const ceiling = anthropicMaxOutputTokens(model);
32914
+ if (saved === undefined)
32915
+ return ceiling ?? 4096;
32916
+ return ceiling === undefined ? saved : Math.min(saved, ceiling);
32917
+ }
32905
32918
  function toWire(req, model, opts) {
32906
32919
  const cloak = opts.cloak ?? null;
32907
32920
  const degradations = [];
@@ -32948,7 +32961,7 @@ function toWire(req, model, opts) {
32948
32961
  }
32949
32962
  return [{ role: m.role, content: encodeSystemTurn(m.content, cloak) }];
32950
32963
  }),
32951
- max_tokens: req.maxTokens ?? 4096,
32964
+ max_tokens: req.maxTokens ?? defaultMaxTokens(model, opts.maxOutputTokens),
32952
32965
  stream: req.stream
32953
32966
  };
32954
32967
  if (systemCache.retarget !== undefined) {
@@ -33087,7 +33100,8 @@ var anthropicCodec = {
33087
33100
  const { body, degradations } = toWire(input2.request, input2.model, {
33088
33101
  oauth,
33089
33102
  cloak,
33090
- autoCache: input2.autoCacheEnabled === true
33103
+ autoCache: input2.autoCacheEnabled === true,
33104
+ ...input2.maxOutputTokens === undefined ? {} : { maxOutputTokens: input2.maxOutputTokens }
33091
33105
  });
33092
33106
  const firstUser = input2.request.messages.find((m) => m.role === "user");
33093
33107
  const firstText = firstUser?.content.find((b) => b.type === "text");
@@ -33420,8 +33434,125 @@ var anthropicOAuthFlow = {
33420
33434
  if (!usageReadable(res.status, "anthropic"))
33421
33435
  return null;
33422
33436
  return parseAnthropicUsage(parsed(res.body), now());
33437
+ },
33438
+ async* resetCredits({ secrets }) {
33439
+ if (secrets.accessToken === null)
33440
+ return null;
33441
+ const res = yield getJsonRequest(RESET_STATUS_URL, anthropicProfile, {
33442
+ accessToken: secrets.accessToken,
33443
+ extraHeaders: SIDE_HEADERS
33444
+ });
33445
+ if (!usageReadable(res.status, "anthropic"))
33446
+ return null;
33447
+ const root = recordOf(parsed(res.body));
33448
+ return root === null ? null : parseResetGrants(root.cedar_ember ?? null);
33449
+ },
33450
+ async* redeemReset({ secrets, creditId, requestId, fail }) {
33451
+ if (secrets.accessToken === null)
33452
+ throw fail("AUTH", "credential holds no access token");
33453
+ const grantId = grantOf(creditId);
33454
+ if (grantId === null)
33455
+ throw fail("CONFLICT", "that reset credit is not available");
33456
+ const profile = yield getJsonRequest(PROFILE_URL, anthropicProfile, {
33457
+ accessToken: secrets.accessToken,
33458
+ extraHeaders: SIDE_HEADERS
33459
+ });
33460
+ if (profile.status < 200 || profile.status >= 300) {
33461
+ throw fail(redeemErrorCode(profile.status), `profile read refused: http_${profile.status}`, {
33462
+ status: profile.status
33463
+ });
33464
+ }
33465
+ const orgId = orgIdOf(parsed(profile.body));
33466
+ if (orgId === null)
33467
+ throw fail("UPSTREAM", "profile names no organization");
33468
+ const res = yield postJsonRequest(`https://api.anthropic.com/api/organizations/${orgId}/reset_rate_limits`, anthropicProfile, {
33469
+ contentType: "application/json",
33470
+ body: JSON.stringify({ program: RESET_PROGRAM, grant_id: grantId, request_id: requestId }),
33471
+ extraHeaders: [["Authorization", `Bearer ${secrets.accessToken}`], ...SIDE_HEADERS]
33472
+ });
33473
+ if (res.status < 200 || res.status >= 300) {
33474
+ throw fail(redeemErrorCode(res.status), `reset claim refused: http_${res.status}`, {
33475
+ status: res.status
33476
+ });
33477
+ }
33478
+ return claimOutcome(parsed(res.body), fail);
33423
33479
  }
33424
33480
  };
33481
+ var RESET_PROGRAM = "cedar_ember";
33482
+ var RESET_STATUS_URL = `${USAGE_URL}?cedar_ember=1&skip_spend=1`;
33483
+ var PROFILE_URL = "https://api.anthropic.com/api/oauth/profile";
33484
+ var ORG_ID = /^[A-Za-z0-9-]{1,64}$/;
33485
+ var SIDE_HEADERS = [["anthropic-beta", "oauth-2025-04-20"]];
33486
+ var USE_SUFFIX = /-u(\d+)$/;
33487
+ function creditIdOf(grantId, left) {
33488
+ return `${grantId}-u${left}`;
33489
+ }
33490
+ function grantOf(creditId) {
33491
+ const grant = creditId.replace(USE_SUFFIX, "");
33492
+ return grant === creditId || grant === "" ? null : grant;
33493
+ }
33494
+ function orgIdOf(value) {
33495
+ const org = recordOf(recordOf(value)?.organization);
33496
+ const uuid3 = org?.uuid;
33497
+ return typeof uuid3 === "string" && ORG_ID.test(uuid3) ? uuid3 : null;
33498
+ }
33499
+ function parseResetGrants(value) {
33500
+ if (value === null)
33501
+ return { available: 0, credits: [] };
33502
+ const block = recordOf(value);
33503
+ if (block === null || !Array.isArray(block.grants))
33504
+ return null;
33505
+ const next = typeof block.next_grant_id === "string" ? block.next_grant_id : null;
33506
+ const atLimit = block.at_limit === true;
33507
+ let available = 0;
33508
+ const credits = block.grants.flatMap((raw) => {
33509
+ const g = recordOf(raw);
33510
+ if (g === null || typeof g.id !== "string")
33511
+ return [];
33512
+ const left = typeof g.resets_left === "number" ? g.resets_left : 0;
33513
+ const claimable = left > 0 && g.usable_now === true && g.paused !== true && (g.use_requires_limit !== true || atLimit) && (next === null || next === g.id);
33514
+ if (claimable)
33515
+ available += left;
33516
+ return [
33517
+ {
33518
+ id: creditIdOf(g.id, left),
33519
+ status: claimable ? "available" : left > 0 ? "unavailable" : "used",
33520
+ title: typeof g.label === "string" && g.label !== "" ? g.label : null,
33521
+ grantedAt: isoOrNull(g.starts_at),
33522
+ expiresAt: isoOrNull(g.ends_at)
33523
+ }
33524
+ ];
33525
+ });
33526
+ return { available, credits };
33527
+ }
33528
+ function claimOutcome(value, fail) {
33529
+ const body = recordOf(value);
33530
+ const result = typeof body?.result === "string" ? body.result : null;
33531
+ if (result === "reset") {
33532
+ return { windowsReset: Array.isArray(body?.cleared) ? body.cleared.length : null };
33533
+ }
33534
+ if (result === "cooldown")
33535
+ throw fail("RATE_LIMIT", "reset claim refused: cooldown");
33536
+ if (result === "already_used" || result === "not_limited" || result === "ineligible") {
33537
+ throw fail("CONFLICT", `reset claim refused: ${result}`);
33538
+ }
33539
+ throw fail("UPSTREAM", "reset claim answered without a reset");
33540
+ }
33541
+ function redeemErrorCode(status) {
33542
+ if (status === 429)
33543
+ return "RATE_LIMIT";
33544
+ if (status === 401 || status === 403)
33545
+ return "AUTH";
33546
+ if (status >= 400 && status < 500)
33547
+ return "CONFLICT";
33548
+ return "UPSTREAM";
33549
+ }
33550
+ function isoOrNull(value) {
33551
+ if (typeof value !== "string")
33552
+ return null;
33553
+ const at = Date.parse(value);
33554
+ return Number.isNaN(at) ? null : at;
33555
+ }
33425
33556
  // packages/providers/src/antigravity/codec.ts
33426
33557
  import { createHash as createHash2 } from "crypto";
33427
33558
 
@@ -34743,7 +34874,7 @@ var grokOAuthFlow = {
34743
34874
 
34744
34875
  // packages/providers/src/kilo/oauth.ts
34745
34876
  var CODES_URL = "https://api.kilo.ai/api/device-auth/codes";
34746
- var PROFILE_URL = "https://api.kilo.ai/api/profile";
34877
+ var PROFILE_URL2 = "https://api.kilo.ai/api/profile";
34747
34878
  var FALLBACK_VERIFICATION_URL = "https://kilo.ai";
34748
34879
  var DEFAULT_EXPIRES_SECONDS = 300;
34749
34880
  var POLL_INTERVAL_SECONDS = 3;
@@ -34775,7 +34906,7 @@ function deviceCodeFrom(value, fail) {
34775
34906
  }
34776
34907
  async function* orgIdFor(accessToken) {
34777
34908
  try {
34778
- const res = yield getJsonRequest(PROFILE_URL, kiloProfile, { accessToken });
34909
+ const res = yield getJsonRequest(PROFILE_URL2, kiloProfile, { accessToken });
34779
34910
  if (res.status < 200 || res.status >= 300)
34780
34911
  return null;
34781
34912
  const record2 = recordFrom(parsed(res.body));
@@ -35283,6 +35414,7 @@ var AUTHORIZE_URL3 = "https://auth.openai.com/oauth/authorize";
35283
35414
  var TOKEN_URL5 = "https://auth.openai.com/oauth/token";
35284
35415
  var SCOPES4 = "openid profile email offline_access";
35285
35416
  var USAGE_URL3 = "https://chatgpt.com/backend-api/wham/usage";
35417
+ var RESET_CREDITS_URL = "https://chatgpt.com/backend-api/wham/rate-limit-reset-credits";
35286
35418
  function isRecord7(value) {
35287
35419
  return typeof value === "object" && value !== null;
35288
35420
  }
@@ -35436,8 +35568,78 @@ var openaiOAuthFlow = {
35436
35568
  if (!usageReadable(res.status, "openai"))
35437
35569
  return null;
35438
35570
  return parseOpenAIUsage(parsed(res.body), now());
35571
+ },
35572
+ async* resetCredits({ secrets, providerData }) {
35573
+ if (secrets.accessToken === null)
35574
+ return null;
35575
+ const res = yield getJsonRequest(RESET_CREDITS_URL, openaiProfile, {
35576
+ accessToken: secrets.accessToken,
35577
+ extraHeaders: accountHeader(providerData)
35578
+ });
35579
+ if (!usageReadable(res.status, "openai"))
35580
+ return null;
35581
+ return parseResetCredits(parsed(res.body));
35582
+ },
35583
+ async* redeemReset({ secrets, providerData, creditId, requestId, fail }) {
35584
+ if (secrets.accessToken === null)
35585
+ throw fail("AUTH", "credential holds no access token");
35586
+ const res = yield postJsonRequest(`${RESET_CREDITS_URL}/consume`, openaiProfile, {
35587
+ contentType: "application/json",
35588
+ body: JSON.stringify({ credit_id: creditId, redeem_request_id: requestId }),
35589
+ extraHeaders: [
35590
+ ["Authorization", `Bearer ${secrets.accessToken}`],
35591
+ ...accountHeader(providerData)
35592
+ ]
35593
+ });
35594
+ if (res.status < 200 || res.status >= 300) {
35595
+ throw fail(redeemErrorCode2(res.status), `reset credit redeem refused: http_${res.status}`, {
35596
+ status: res.status
35597
+ });
35598
+ }
35599
+ const body = recordOf(parsed(res.body));
35600
+ const windowsReset = body === null ? null : numberOf(body, ["windows_reset"]);
35601
+ return { windowsReset };
35439
35602
  }
35440
35603
  };
35604
+ function accountHeader(providerData) {
35605
+ const accountId = accountIdFromProviderData(providerData);
35606
+ return accountId === null ? [] : [["chatgpt-account-id", accountId]];
35607
+ }
35608
+ function redeemErrorCode2(status) {
35609
+ if (status === 429)
35610
+ return "RATE_LIMIT";
35611
+ if (status === 401 || status === 403)
35612
+ return "AUTH";
35613
+ if (status >= 400 && status < 500)
35614
+ return "CONFLICT";
35615
+ return "UPSTREAM";
35616
+ }
35617
+ function isoOrNull2(value) {
35618
+ if (typeof value !== "string")
35619
+ return null;
35620
+ const at = Date.parse(value);
35621
+ return Number.isFinite(at) ? at : null;
35622
+ }
35623
+ function parseResetCredits(value) {
35624
+ const root = recordOf(value);
35625
+ if (root === null || !Array.isArray(root.credits))
35626
+ return null;
35627
+ const credits = root.credits.flatMap((raw) => {
35628
+ const c = recordOf(raw);
35629
+ if (c === null || typeof c.id !== "string" || typeof c.status !== "string")
35630
+ return [];
35631
+ return [
35632
+ {
35633
+ id: c.id,
35634
+ status: c.status,
35635
+ title: typeof c.title === "string" ? c.title : null,
35636
+ grantedAt: isoOrNull2(c.granted_at),
35637
+ expiresAt: isoOrNull2(c.expires_at)
35638
+ }
35639
+ ];
35640
+ });
35641
+ return { available: credits.filter((c) => c.status === "available").length, credits };
35642
+ }
35441
35643
 
35442
35644
  // packages/providers/src/builtinOAuth.ts
35443
35645
  function builtinOAuthFlows() {
@@ -38375,6 +38577,11 @@ var quotaRefreshSchema = exports_external.discriminatedUnion("kind", [
38375
38577
  }).strict(),
38376
38578
  exports_external.object({ kind: exports_external.literal("all") }).strict()
38377
38579
  ]);
38580
+ var accountIdSchema = exports_external.string().trim().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/, "credentialId must be an account id");
38581
+ var redeemResetSchema = exports_external.object({
38582
+ credentialId: accountIdSchema,
38583
+ creditId: exports_external.string().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/).optional()
38584
+ }).strict();
38378
38585
  var dimensionSchema = exports_external.enum([
38379
38586
  "credential",
38380
38587
  "model",
@@ -40267,6 +40474,29 @@ function oauthAdapter(id, flow, opts = {}) {
40267
40474
  return Promise.resolve(null);
40268
40475
  return runStep(id, "usage", step, deps, origins);
40269
40476
  }
40477
+ },
40478
+ ...flow.resetCredits === undefined ? {} : {
40479
+ resetCredits: (secrets, deps, providerData) => {
40480
+ const step = flow.resetCredits?.({ secrets, providerData, ...helpers(deps) });
40481
+ if (step === undefined)
40482
+ return Promise.resolve(null);
40483
+ return runStep(id, "resetCredits", step, deps, origins);
40484
+ }
40485
+ },
40486
+ ...flow.redeemReset === undefined ? {} : {
40487
+ redeemReset: (secrets, deps, providerData, creditId, requestId) => {
40488
+ const step = flow.redeemReset?.({
40489
+ secrets,
40490
+ providerData,
40491
+ creditId,
40492
+ requestId,
40493
+ ...helpers(deps)
40494
+ });
40495
+ if (step === undefined) {
40496
+ return Promise.reject(flowFailure(id, "redeemReset", "has no redeem step"));
40497
+ }
40498
+ return runStep(id, "redeemReset", step, deps, origins);
40499
+ }
40270
40500
  }
40271
40501
  };
40272
40502
  if (flow.kind === "pkce")
@@ -40302,11 +40532,32 @@ function isAuthorizeStart(value) {
40302
40532
  const candidate = value;
40303
40533
  return typeof candidate.authorizeUrl === "string" && typeof candidate.pending === "object" && candidate.pending !== null;
40304
40534
  }
40535
+ function isResetCreditsOrNull(value) {
40536
+ if (value === null)
40537
+ return true;
40538
+ if (typeof value !== "object")
40539
+ return false;
40540
+ const candidate = value;
40541
+ return typeof candidate.available === "number" && Array.isArray(candidate.credits) && candidate.credits.every((c) => {
40542
+ if (typeof c !== "object" || c === null)
40543
+ return false;
40544
+ const row = c;
40545
+ return typeof row.id === "string" && typeof row.status === "string" && (row.title === null || typeof row.title === "string") && (row.grantedAt === null || typeof row.grantedAt === "number") && (row.expiresAt === null || typeof row.expiresAt === "number");
40546
+ });
40547
+ }
40548
+ function isResetRedeemed(value) {
40549
+ if (typeof value !== "object" || value === null)
40550
+ return false;
40551
+ const held = value.windowsReset;
40552
+ return held === null || typeof held === "number";
40553
+ }
40305
40554
  var RETURN_SHAPE = {
40306
40555
  start: isAuthorizeStart,
40307
40556
  begin: isAuthorizeStart,
40308
40557
  exchange: isFlowResult,
40309
- refresh: isFlowResult
40558
+ refresh: isFlowResult,
40559
+ resetCredits: isResetCreditsOrNull,
40560
+ redeemReset: isResetRedeemed
40310
40561
  };
40311
40562
 
40312
40563
  // packages/control/src/oauth/index.ts
@@ -40523,6 +40774,11 @@ function validateOAuthFlow(id, value) {
40523
40774
  if (value.usage !== undefined) {
40524
40775
  checkAt(typeof value.usage === "function", id, "oauth.usage", "a function");
40525
40776
  }
40777
+ for (const fn of ["resetCredits", "redeemReset"]) {
40778
+ if (value[fn] !== undefined) {
40779
+ checkAt(typeof value[fn] === "function", id, `oauth.${fn}`, "a function");
40780
+ }
40781
+ }
40526
40782
  if (kind === "device") {
40527
40783
  checkAt(typeof value.begin === "function", id, "oauth.begin", "a function");
40528
40784
  checkAt(typeof value.needsDeviceId === "boolean", id, "oauth.needsDeviceId", "a boolean");
@@ -40721,6 +40977,52 @@ async function accountQuotaHistory(deps, input2) {
40721
40977
  truncated
40722
40978
  };
40723
40979
  }
40980
+ // packages/control/src/quota/resets.ts
40981
+ async function resettable(deps, credentialId) {
40982
+ const credential = await deps.store.credentials.get(credentialId.trim());
40983
+ if (credential === null)
40984
+ throw new GatewayError("BAD_REQUEST", "no such credential");
40985
+ const provider = deps.providers[credential.provider];
40986
+ const list = provider?.resetCredits;
40987
+ const redeem = provider?.redeemReset;
40988
+ if (credential.authType !== "oauth" || list === undefined || redeem === undefined) {
40989
+ throw new GatewayError("BAD_REQUEST", "this account's provider has no quota resets");
40990
+ }
40991
+ return { credential, list, redeem };
40992
+ }
40993
+ async function listFor(deps, r) {
40994
+ const secrets = await usageSecretsFor(deps, r.credential);
40995
+ const read = await r.list(secrets, { http: deps.http, now: deps.now }, r.credential.providerData);
40996
+ if (read === null) {
40997
+ throw new GatewayError("UPSTREAM", "provider answered the reset list with nothing usable", {
40998
+ gatewayAuthored: true
40999
+ });
41000
+ }
41001
+ return read;
41002
+ }
41003
+ async function listResetCredits(deps, credentialId) {
41004
+ const r = await resettable(deps, credentialId);
41005
+ return { credentialId: r.credential.id, ...await listFor(deps, r) };
41006
+ }
41007
+ async function redeemResetCredit(deps, request2) {
41008
+ const r = await resettable(deps, request2.credentialId);
41009
+ return deps.coord.mutex.withLock(PROBE_LOCK_PREFIX + r.credential.id, PROBE_LOCK_TTL_MS, PROBE_LOCK_WAIT_MS, () => redeemLocked(deps, r, request2));
41010
+ }
41011
+ async function redeemLocked(deps, r, request2) {
41012
+ const available = (await listFor(deps, r)).credits.filter((c) => c.status === "available").sort((a, b) => (a.expiresAt ?? Number.POSITIVE_INFINITY) - (b.expiresAt ?? Number.POSITIVE_INFINITY));
41013
+ const credit = request2.creditId === undefined ? available[0] : available.find((c) => c.id === request2.creditId);
41014
+ if (credit === undefined) {
41015
+ throw new GatewayError("CONFLICT", request2.creditId === undefined ? "no reset credit is available for this account" : "that reset credit is not available");
41016
+ }
41017
+ const secrets = await usageSecretsFor(deps, r.credential);
41018
+ const { windowsReset } = await r.redeem(secrets, { http: deps.http, now: deps.now }, r.credential.providerData, credit.id, crypto.randomUUID());
41019
+ let quotaRefreshed = false;
41020
+ try {
41021
+ quotaRefreshed = await probe(deps, r.credential) !== null;
41022
+ } catch {}
41023
+ return { credentialId: r.credential.id, creditId: credit.id, windowsReset, quotaRefreshed };
41024
+ }
41025
+
40724
41026
  // packages/control/src/quota/poll.ts
40725
41027
  var CONCURRENCY = 4;
40726
41028
  var RATE_LIMIT_COOLDOWN_MS = 180000;
@@ -40728,14 +41030,17 @@ var COOLDOWN_PREFIX = "quota:cooldown:";
40728
41030
  var PROBE_LOCK_PREFIX = "quota:probe:";
40729
41031
  var PROBE_LOCK_TTL_MS = 60000;
40730
41032
  var PROBE_LOCK_WAIT_MS = 30000;
41033
+ async function usageSecretsFor(deps, credential) {
41034
+ const refreshed = credential.hasRefreshToken && credential.expiresAt !== null && credential.expiresAt - SCHEDULER_REFRESH_LEAD_MS <= deps.now() ? await deps.refresh(credential) : null;
41035
+ return refreshed === null ? await credential.openForUsage() : { accessToken: refreshed.accessToken };
41036
+ }
40731
41037
  async function probe(deps, credential) {
40732
41038
  if (credential.authType !== "oauth")
40733
41039
  return null;
40734
41040
  const provider = deps.providers[credential.provider];
40735
41041
  if (provider?.usage === undefined)
40736
41042
  return null;
40737
- const refreshed = credential.hasRefreshToken && credential.expiresAt !== null && credential.expiresAt - SCHEDULER_REFRESH_LEAD_MS <= deps.now() ? await deps.refresh(credential) : null;
40738
- const secrets = refreshed === null ? await credential.openForUsage() : { accessToken: refreshed.accessToken };
41043
+ const secrets = await usageSecretsFor(deps, credential);
40739
41044
  const report = await provider.usage(secrets, { http: deps.http, now: deps.now }, credential.providerData);
40740
41045
  if (report === null || report.windows.length === 0)
40741
41046
  return null;
@@ -40898,6 +41203,12 @@ function quotaOps(deps) {
40898
41203
  outcomes.push(result?.outcome ?? { kind: "failed", credentialId: credential.id, code: "INTERNAL" });
40899
41204
  }
40900
41205
  return { outcomes };
41206
+ },
41207
+ resetCredits: (credentialId) => listResetCredits(deps, credentialId),
41208
+ redeemReset: (request2) => redeemResetCredit(deps, request2),
41209
+ hasResets: (provider) => {
41210
+ const flow = deps.providers[provider];
41211
+ return flow?.resetCredits !== undefined && flow.redeemReset !== undefined;
40901
41212
  }
40902
41213
  };
40903
41214
  }
@@ -57094,6 +57405,12 @@ function adminRoutes(deps) {
57094
57405
  });
57095
57406
  };
57096
57407
  const changed = (topic) => deps.broadcaster?.invalidate(topic);
57408
+ const quotaOrRefuse = () => {
57409
+ if (deps.quota === undefined) {
57410
+ throw new GatewayError("INTERNAL", "quota operations are not available on this process");
57411
+ }
57412
+ return deps.quota;
57413
+ };
57097
57414
  return new Elysia().onError(apiErrorHandler).get("/api/status", async ({ request: request2 }) => {
57098
57415
  const token = readCookie(request2, ADMIN_COOKIE);
57099
57416
  const principal = token === null ? null : await deps.admin.verify(token);
@@ -57182,6 +57499,18 @@ function adminRoutes(deps) {
57182
57499
  changed("res:quota");
57183
57500
  }
57184
57501
  return result;
57502
+ }).get("/api/credentials/:id/resets", async ({ request: request2, params }) => {
57503
+ await requireAdmin(request2, deps.admin);
57504
+ return quotaOrRefuse().resetCredits(params.id);
57505
+ }).post("/api/credentials/:id/resets/redeem", async ({ request: request2, params }) => {
57506
+ await requireAdmin(request2, deps.admin);
57507
+ const raw = await readJsonRecord(request2);
57508
+ if (raw === null)
57509
+ throw new GatewayError("BAD_REQUEST", "body must be a JSON object");
57510
+ const body2 = parseOrThrow(redeemResetSchema, { ...raw, credentialId: params.id });
57511
+ const result = await quotaOrRefuse().redeemReset(body2);
57512
+ changed("res:quota");
57513
+ return result;
57185
57514
  }).patch("/api/credentials/:id", async ({ request: request2, params }) => {
57186
57515
  await requireAdmin(request2, deps.admin);
57187
57516
  await patchCredential(deps, params.id, await readJson(request2));
@@ -57197,7 +57526,10 @@ function adminRoutes(deps) {
57197
57526
  return { models: await listModels(deps.store) };
57198
57527
  }).get("/api/catalog", async ({ request: request2 }) => {
57199
57528
  await requireReader(request2, deps.admin);
57200
- return { providers: providerCatalog(reportCatalogProblem) };
57529
+ const quota = deps.quota;
57530
+ return {
57531
+ providers: providerCatalog(reportCatalogProblem).map((provider) => quota?.hasResets(provider.id) === true ? { ...provider, quotaResets: true } : provider)
57532
+ };
57201
57533
  }).put("/api/models/:id", async ({ request: request2, params }) => {
57202
57534
  await requireAdmin(request2, deps.admin);
57203
57535
  await putModel(deps.store, params.id, await readJson(request2));
@@ -60084,7 +60416,8 @@ async function attempt(opts) {
60084
60416
  http,
60085
60417
  signal,
60086
60418
  ...opts.requestId === undefined ? {} : { requestId: opts.requestId },
60087
- ...opts.autoCache === undefined ? {} : { autoCache: opts.autoCache }
60419
+ ...opts.autoCache === undefined ? {} : { autoCache: opts.autoCache },
60420
+ ...candidate.target.maxOutputTokens === undefined ? {} : { maxOutputTokens: candidate.target.maxOutputTokens }
60088
60421
  });
60089
60422
  }
60090
60423
 
@@ -63124,7 +63457,7 @@ function createRing(limits = DEFAULT_RING_LIMITS) {
63124
63457
  }
63125
63458
 
63126
63459
  // apps/gateway/src/version.ts
63127
- var VERSION = "0.12.12";
63460
+ var VERSION = "0.12.13";
63128
63461
 
63129
63462
  // apps/gateway/src/app.ts
63130
63463
  var ADMIN_SESSION_TTL_MS = 12 * 60 * 60 * 1000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnigateway",
3
- "version": "0.12.12",
3
+ "version": "0.12.13",
4
4
  "description": "Self-hosted AI gateway with Anthropic- and OpenAI-compatible APIs, an admin console, and a CLI",
5
5
  "license": "MIT",
6
6
  "author": "Harismawan <mail@harismawan.com>",
@@ -1,4 +1,4 @@
1
- import{w as e}from"./Rack-CEuCdVXc.js";import{jsx as t}from"react/jsx-runtime";import n from"styled-components";var r={neutral:{fg:`var(--ink-dim)`,bg:`transparent`},ok:{fg:`var(--ok)`,bg:`var(--ok-wash)`},warn:{fg:`var(--warn)`,bg:`var(--warn-wash)`},down:{fg:`var(--down)`,bg:`var(--down-wash)`},accent:{fg:`var(--accent)`,bg:`var(--accent-wash)`}},i=n.span`
1
+ import{w as e}from"./Rack-CDUOJkS2.js";import{jsx as t}from"react/jsx-runtime";import n from"styled-components";var r={neutral:{fg:`var(--ink-dim)`,bg:`transparent`},ok:{fg:`var(--ok)`,bg:`var(--ok-wash)`},warn:{fg:`var(--warn)`,bg:`var(--warn-wash)`},down:{fg:`var(--down)`,bg:`var(--down-wash)`},accent:{fg:`var(--accent)`,bg:`var(--accent-wash)`}},i=n.span`
2
2
  display: inline-flex;
3
3
  align-items: center;
4
4
  line-height: 1;
@@ -1,4 +1,4 @@
1
- import{C as e,O as t}from"./Panel-CWmOTDAP.js";import{A as n}from"./Rack-CEuCdVXc.js";import{useCallback as r,useEffect as i,useRef as a,useState as o}from"react";import{jsx as s,jsxs as c}from"react/jsx-runtime";import l from"styled-components";var u={name:`check`,size:24,node:[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]};u.node;var d=n(u),f={name:`copy`,size:24,node:[[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]};f.node;var p=n(f);function m(e){if(e===null||typeof getSelection!=`function`)return!1;let t=getSelection();if(t===null)return!1;try{let n=document.createRange();return n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n),!0}catch{return!1}}async function h(e,t){if(typeof navigator<`u`&&navigator.clipboard!==void 0)try{return await navigator.clipboard.writeText(e),`copied`}catch{}let n=m(t??null);if(n&&typeof document.execCommand==`function`)try{if(document.execCommand(`copy`))return`copied`}catch{}return n?`selected`:`failed`}var g=l.div`
1
+ import{A as e,T as t}from"./Panel-CsovmtxF.js";import{A as n}from"./Rack-CDUOJkS2.js";import{useCallback as r,useEffect as i,useRef as a,useState as o}from"react";import{jsx as s,jsxs as c}from"react/jsx-runtime";import l from"styled-components";var u={name:`check`,size:24,node:[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]};u.node;var d=n(u),f={name:`copy`,size:24,node:[[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]};f.node;var p=n(f);function m(e){if(e===null||typeof getSelection!=`function`)return!1;let t=getSelection();if(t===null)return!1;try{let n=document.createRange();return n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n),!0}catch{return!1}}async function h(e,t){if(typeof navigator<`u`&&navigator.clipboard!==void 0)try{return await navigator.clipboard.writeText(e),`copied`}catch{}let n=m(t??null);if(n&&typeof document.execCommand==`function`)try{if(document.execCommand(`copy`))return`copied`}catch{}return n?`selected`:`failed`}var g=l.div`
2
2
  display: flex;
3
3
  align-items: stretch;
4
4
  gap: 0;
@@ -14,7 +14,7 @@ import{C as e,O as t}from"./Panel-CWmOTDAP.js";import{A as n}from"./Rack-CEuCdVX
14
14
  font-size: 12px;
15
15
  word-break: break-all;
16
16
  user-select: all;
17
- `,v=l(t)`
17
+ `,v=l(e)`
18
18
  border: 0;
19
19
  border-left: 1px solid ${({theme:e})=>e.color.ruleStrong};
20
20
  border-radius: 0;
@@ -24,4 +24,4 @@ import{C as e,O as t}from"./Panel-CWmOTDAP.js";import{A as n}from"./Rack-CEuCdVX
24
24
  `,y=l.p`
25
25
  font-size: 11px;
26
26
  color: ${({theme:e})=>e.color.warn};
27
- `,b={selected:`Your browser would not copy it. The value is selected — copy it with your keyboard.`,failed:`Your browser would not copy it. Select the value and copy it by hand.`};function x({value:t,label:n}){let[l,u]=o(null),f=a(null);i(()=>{if(l!==`copied`)return;let e=setTimeout(()=>u(null),1600);return()=>clearTimeout(e)},[l]);let m=r(()=>{h(t,f.current).then(u)},[t]),x=l===`copied`,S=l===null?void 0:b[l];return c(e,{$gap:1,children:[c(g,{children:[s(_,{ref:f,children:t}),s(v,{type:`button`,$variant:`ghost`,onClick:m,"aria-label":x?`Copied`:n,title:x?`Copied`:n,children:s(x?d:p,{})})]}),S===void 0?null:s(y,{role:`status`,children:S})]})}export{x as t};
27
+ `,b={selected:`Your browser would not copy it. The value is selected — copy it with your keyboard.`,failed:`Your browser would not copy it. Select the value and copy it by hand.`};function x({value:e,label:n}){let[l,u]=o(null),f=a(null);i(()=>{if(l!==`copied`)return;let e=setTimeout(()=>u(null),1600);return()=>clearTimeout(e)},[l]);let m=r(()=>{h(e,f.current).then(u)},[e]),x=l===`copied`,S=l===null?void 0:b[l];return c(t,{$gap:1,children:[c(g,{children:[s(_,{ref:f,children:e}),s(v,{type:`button`,$variant:`ghost`,onClick:m,"aria-label":x?`Copied`:n,title:x?`Copied`:n,children:s(x?d:p,{})})]}),S===void 0?null:s(y,{role:`status`,children:S})]})}export{x as t};
@@ -1,4 +1,4 @@
1
- import{_ as e}from"./Panel-CWmOTDAP.js";import{useId as t}from"react";import{jsx as n,jsxs as r}from"react/jsx-runtime";import i from"styled-components";var a=`
1
+ import{y as e}from"./Panel-CsovmtxF.js";import{useId as t}from"react";import{jsx as n,jsxs as r}from"react/jsx-runtime";import i from"styled-components";var a=`
2
2
  width: 100%;
3
3
  border-radius: 3px;
4
4
  transition: border-color 120ms ease;