@jeffusion/bungee-core 6.8.2 → 6.10.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.
@@ -15214,6 +15214,7 @@ class LogQueryService {
15214
15214
  transformer,
15215
15215
  success,
15216
15216
  transportOutcome,
15217
+ transportType,
15217
15218
  searchTerm,
15218
15219
  requestType,
15219
15220
  sortBy = "timestamp",
@@ -15229,6 +15230,12 @@ class LogQueryService {
15229
15230
  whereClauses.push("timestamp < ?");
15230
15231
  whereParams.push(endTime);
15231
15232
  }
15233
+ if (transportType !== undefined) {
15234
+ if (transportType !== "http" && transportType !== "websocket")
15235
+ throw new Error("Invalid transport type");
15236
+ whereClauses.push("transport_type = ?");
15237
+ whereParams.push(transportType);
15238
+ }
15232
15239
  if (method) {
15233
15240
  whereClauses.push("method = ?");
15234
15241
  whereParams.push(method);
@@ -15330,6 +15337,7 @@ class LogQueryService {
15330
15337
  transformer,
15331
15338
  success,
15332
15339
  transportOutcome,
15340
+ transportType,
15333
15341
  searchTerm,
15334
15342
  requestType,
15335
15343
  sortBy = "timestamp",
@@ -15342,6 +15350,12 @@ class LogQueryService {
15342
15350
  } = params;
15343
15351
  const rowWhereClauses = [];
15344
15352
  const rowWhereParams = [];
15353
+ if (transportType !== undefined) {
15354
+ if (transportType !== "http" && transportType !== "websocket")
15355
+ throw new Error("Invalid transport type");
15356
+ rowWhereClauses.push("transport_type = ?");
15357
+ rowWhereParams.push(transportType);
15358
+ }
15345
15359
  if (method) {
15346
15360
  rowWhereClauses.push("method = ?");
15347
15361
  rowWhereParams.push(method);
@@ -15673,6 +15687,7 @@ class LogQueryService {
15673
15687
  const headers = [
15674
15688
  "requestId",
15675
15689
  "timestamp",
15690
+ "transportType",
15676
15691
  "method",
15677
15692
  "path",
15678
15693
  "query",
@@ -15699,6 +15714,7 @@ class LogQueryService {
15699
15714
  return [
15700
15715
  entry.requestId,
15701
15716
  new Date(entry.timestamp).toISOString(),
15717
+ entry.transportType ?? "",
15702
15718
  entry.method,
15703
15719
  `"${entry.path}"`,
15704
15720
  entry.query ? `"${entry.query}"` : "",
@@ -15946,6 +15962,7 @@ class LogQueryService {
15946
15962
  requestId: row.request_id,
15947
15963
  timestamp: row.timestamp,
15948
15964
  method: row.method,
15965
+ transportType: row.transport_type,
15949
15966
  path: row.path,
15950
15967
  query: row.query || undefined,
15951
15968
  status: row.status,
@@ -15992,10 +16009,14 @@ function getSingleParam(url, name) {
15992
16009
  const values = url.searchParams.getAll(name);
15993
16010
  return values.length === 0 ? undefined : values.length === 1 && values[0] !== "" ? values[0] : null;
15994
16011
  }
15995
- function getOutcomeFilters(url) {
16012
+ function getLogFilters(url) {
15996
16013
  const values = url.searchParams.getAll("status");
15997
16014
  const statuses = values.map((value) => /^\d{3}$/.test(value) ? Number(value) : NaN);
15998
16015
  const transport = getSingleParam(url, "transportOutcome");
16016
+ const transportType = getSingleParam(url, "transportType");
16017
+ if (transportType !== undefined && transportType !== "http" && transportType !== "websocket") {
16018
+ return Response.json({ error: "transportType must be http or websocket" }, { status: 400 });
16019
+ }
15999
16020
  if (statuses.some((value) => !Number.isInteger(value) || value < 100 || value > 599)) {
16000
16021
  return Response.json({ error: "status must contain HTTP status codes between 100 and 599" }, { status: 400 });
16001
16022
  }
@@ -16004,6 +16025,7 @@ function getOutcomeFilters(url) {
16004
16025
  }
16005
16026
  return {
16006
16027
  status: statuses.length === 0 ? undefined : statuses.length === 1 ? statuses[0] : [...new Set(statuses)],
16028
+ transportType,
16007
16029
  transportOutcome: transport
16008
16030
  };
16009
16031
  }
@@ -16039,7 +16061,7 @@ class LogsHandler {
16039
16061
  try {
16040
16062
  const url = new URL(req.url);
16041
16063
  const groupBy = url.searchParams.get("groupBy");
16042
- const outcomeFilters = getOutcomeFilters(url);
16064
+ const outcomeFilters = getLogFilters(url);
16043
16065
  if (outcomeFilters instanceof Response)
16044
16066
  return outcomeFilters;
16045
16067
  const params = {
@@ -16277,7 +16299,7 @@ class LogsHandler {
16277
16299
  const format = url.searchParams.get("format") || "json";
16278
16300
  if (format !== "json" && format !== "csv")
16279
16301
  return Response.json({ error: "format must be json or csv" }, { status: 400 });
16280
- const outcomeFilters = getOutcomeFilters(url);
16302
+ const outcomeFilters = getLogFilters(url);
16281
16303
  if (outcomeFilters instanceof Response)
16282
16304
  return outcomeFilters;
16283
16305
  const params = {
@@ -17604,6 +17626,16 @@ var ACCESS_BASELINE = {
17604
17626
  }
17605
17627
  };
17606
17628
 
17629
+ // src/migrations/access-log-transport-type.ts
17630
+ var ACCESS_LOG_TRANSPORT_TYPE = {
17631
+ version: "005",
17632
+ name: "access_log_transport_type",
17633
+ up(db) {
17634
+ db.run(`ALTER TABLE access_logs ADD COLUMN transport_type TEXT
17635
+ CHECK (transport_type IS NULL OR transport_type IN ('http', 'websocket'))`);
17636
+ }
17637
+ };
17638
+
17607
17639
  // src/migrations/migration-manager.ts
17608
17640
  import { Database as Database4 } from "bun:sqlite";
17609
17641
  import { mkdirSync as mkdirSync2, existsSync } from "fs";
@@ -18074,7 +18106,7 @@ class MigrationManager {
18074
18106
  }
18075
18107
 
18076
18108
  // src/migrations/index.ts
18077
- var migrations = [ACCESS_BASELINE, LLM_TPS_SAMPLES, LLM_TPS_MODEL, LLM_TPS_UPSTREAM_HOST];
18109
+ var migrations = [ACCESS_BASELINE, LLM_TPS_SAMPLES, LLM_TPS_MODEL, LLM_TPS_UPSTREAM_HOST, ACCESS_LOG_TRANSPORT_TYPE];
18078
18110
 
18079
18111
  // src/master-runtime/observability-worker.ts
18080
18112
  function headerEntries(headers) {
@@ -1281,7 +1281,7 @@ function validStoredAccount(value) {
1281
1281
  if (typeof value !== "object" || value === null || Array.isArray(value))
1282
1282
  return false;
1283
1283
  const account = value;
1284
- return typeof account.id === "string" && account.id.length > 0 && account.id.length <= 128 && typeof account.label === "string" && account.label.length > 0 && account.label.length <= MAX_TEXT && ["active", "disabled", "reauth_required", "revoked"].includes(account.status) && Number.isSafeInteger(account.generation) && account.generation > 0 && (account.loginFence === undefined || Number.isSafeInteger(account.loginFence) && account.loginFence > 0) && (account.credentialValid === undefined || typeof account.credentialValid === "boolean") && (account.autoResetCredits === undefined || typeof account.autoResetCredits === "boolean") && (account.siwc === undefined || validSiwcMetadata(account.siwc)) && validAutoResetAttempts(account.autoResetAttempts) && Number.isFinite(account.createdAt) && Number.isFinite(account.updatedAt);
1284
+ return typeof account.id === "string" && account.id.length > 0 && account.id.length <= 128 && typeof account.label === "string" && account.label.length > 0 && account.label.length <= MAX_TEXT && ["active", "disabled", "reauth_required", "revoked"].includes(account.status) && Number.isSafeInteger(account.generation) && account.generation > 0 && (account.loginFence === undefined || Number.isSafeInteger(account.loginFence) && account.loginFence > 0) && (account.credentialValid === undefined || typeof account.credentialValid === "boolean") && (account.autoResetCredits === undefined || typeof account.autoResetCredits === "boolean") && (account.autoResetLeadMinutes === undefined || Number.isInteger(account.autoResetLeadMinutes) && account.autoResetLeadMinutes >= 1 && account.autoResetLeadMinutes <= 10080) && (account.autoResetOnLowBalance === undefined || typeof account.autoResetOnLowBalance === "boolean") && (account.autoResetRemainingPercent === undefined || Number.isInteger(account.autoResetRemainingPercent) && account.autoResetRemainingPercent >= 0 && account.autoResetRemainingPercent <= 100) && (account.siwc === undefined || validSiwcMetadata(account.siwc)) && validAutoResetAttempts(account.autoResetAttempts) && Number.isFinite(account.createdAt) && Number.isFinite(account.updatedAt);
1285
1285
  }
1286
1286
  function decode(value) {
1287
1287
  if (value === undefined)
@@ -1326,6 +1326,9 @@ function accountListItem(account) {
1326
1326
  expiresAt: account.expiresAt,
1327
1327
  identity: copyIdentity(account.identity),
1328
1328
  autoResetCredits: !account.siwc && account.autoResetCredits === true,
1329
+ autoResetLeadMinutes: account.autoResetLeadMinutes ?? 30,
1330
+ autoResetOnLowBalance: !account.siwc && account.autoResetOnLowBalance === true,
1331
+ autoResetRemainingPercent: account.autoResetRemainingPercent ?? 10,
1329
1332
  pendingAutoReset: !account.siwc && pending ? { ...pending } : undefined
1330
1333
  };
1331
1334
  }
@@ -1539,8 +1542,8 @@ class AccountStore {
1539
1542
  return copyAccount(account);
1540
1543
  });
1541
1544
  }
1542
- async setAutoResetCredits(id, enabled) {
1543
- if (typeof enabled !== "boolean")
1545
+ async setAutoResetCredits(id, enabled, options = {}) {
1546
+ if (enabled !== undefined && typeof enabled !== "boolean" || options.lowBalanceEnabled !== undefined && typeof options.lowBalanceEnabled !== "boolean" || options.leadMinutes !== undefined && (!Number.isInteger(options.leadMinutes) || options.leadMinutes < 1 || options.leadMinutes > 10080) || options.remainingPercent !== undefined && (!Number.isInteger(options.remainingPercent) || options.remainingPercent < 0 || options.remainingPercent > 100))
1544
1547
  throw new AccountControlError("invalid_input");
1545
1548
  return this.mutate((aggregate) => {
1546
1549
  const account = aggregate.accounts.find((item) => item.id === id);
@@ -1550,7 +1553,10 @@ class AccountStore {
1550
1553
  throw new AccountControlError("revoked");
1551
1554
  if (account.siwc)
1552
1555
  throw new AccountControlError("unsupported_operation");
1553
- account.autoResetCredits = enabled;
1556
+ account.autoResetCredits = enabled ?? account.autoResetCredits ?? false;
1557
+ account.autoResetLeadMinutes = options.leadMinutes ?? account.autoResetLeadMinutes ?? 30;
1558
+ account.autoResetOnLowBalance = options.lowBalanceEnabled ?? account.autoResetOnLowBalance ?? false;
1559
+ account.autoResetRemainingPercent = options.remainingPercent ?? account.autoResetRemainingPercent ?? 10;
1554
1560
  account.updatedAt = Date.now();
1555
1561
  return copyAccount(account);
1556
1562
  });
@@ -1558,7 +1564,7 @@ class AccountStore {
1558
1564
  async claimAutoReset(id, credit, now) {
1559
1565
  return this.mutate((aggregate) => {
1560
1566
  const account = aggregate.accounts.find((item) => item.id === id);
1561
- if (!account || account.siwc || !accountListItem(account).available || account.autoResetCredits !== true)
1567
+ if (!account || account.siwc || !accountListItem(account).available || !(account.autoResetCredits || account.autoResetOnLowBalance))
1562
1568
  return;
1563
1569
  const attempts = account.autoResetAttempts ?? [];
1564
1570
  if (attempts.some((item) => !item.completed || item.creditId === credit.id))
@@ -2554,8 +2560,17 @@ var defaultTimer = {
2554
2560
  },
2555
2561
  cancel: (handle) => clearTimeout(handle)
2556
2562
  };
2557
- function isAutoResetDue(credit, now) {
2558
- return credit.status === "available" && typeof credit.expiresAt === "number" && Number.isFinite(credit.expiresAt) && credit.expiresAt > now && credit.expiresAt - now <= AUTO_RESET_LEAD_MS;
2563
+ function isAutoResetDue(credit, now, leadMinutes = 30) {
2564
+ return credit.status === "available" && typeof credit.expiresAt === "number" && Number.isFinite(credit.expiresAt) && credit.expiresAt > now && credit.expiresAt - now <= leadMinutes * 60000;
2565
+ }
2566
+ function isLowBalance(usage, remainingPercent, now) {
2567
+ if (usage.state !== "fresh" || usage.error)
2568
+ return false;
2569
+ const windows = [usage.primary, usage.secondary].filter((window) => window !== undefined && Number.isFinite(window.windowSeconds) && window.windowSeconds > 18000);
2570
+ const longest = Math.max(...windows.map((window) => window.windowSeconds));
2571
+ const candidates = windows.filter((window) => window.windowSeconds === longest);
2572
+ const window = candidates[0];
2573
+ return candidates.length === 1 && window !== undefined && Number.isFinite(window.usedPercent) && window.usedPercent >= 0 && window.usedPercent <= 100 && window.resetAt > now && window.resetAt - now <= window.windowSeconds * 1000 && 100 - window.usedPercent <= remainingPercent;
2559
2574
  }
2560
2575
 
2561
2576
  class AutoResetScheduler {
@@ -2605,7 +2620,7 @@ class AutoResetScheduler {
2605
2620
  }
2606
2621
  async scan() {
2607
2622
  const accounts = await this.accounts.list();
2608
- const eligible = new Set(accounts.filter((account) => account.available && account.autoResetCredits && (!account.pendingAutoReset || this.accountRuns.has(account.id))).map((account) => account.id));
2623
+ const eligible = new Set(accounts.filter((account) => account.available && (account.autoResetCredits || account.autoResetOnLowBalance) && (!account.pendingAutoReset || this.accountRuns.has(account.id))).map((account) => account.id));
2609
2624
  for (const id of this.plans.keys())
2610
2625
  if (!eligible.has(id))
2611
2626
  this.cancelPlan(id);
@@ -2626,7 +2641,12 @@ class AutoResetScheduler {
2626
2641
  if ((snapshot.resetCredits.availableCount ?? 0) <= 0)
2627
2642
  continue;
2628
2643
  const credits = [...snapshot.resetCredits.credits ?? []].filter((credit) => credit.status === "available" && typeof credit.expiresAt === "number" && Number.isFinite(credit.expiresAt) && credit.expiresAt > this.now()).sort((a, b) => a.expiresAt - b.expiresAt);
2629
- const plan = { credits };
2644
+ const plan = {
2645
+ credits,
2646
+ leadMinutes: account.autoResetLeadMinutes,
2647
+ expiryEnabled: account.autoResetCredits,
2648
+ lowBalance: account.autoResetOnLowBalance && isLowBalance(snapshot.usage, account.autoResetRemainingPercent, this.now())
2649
+ };
2630
2650
  this.plans.set(account.id, plan);
2631
2651
  await this.executePlan(account.id, plan);
2632
2652
  } catch {}
@@ -2647,7 +2667,11 @@ class AutoResetScheduler {
2647
2667
  this.cancelPlan(accountRef);
2648
2668
  return;
2649
2669
  }
2650
- const dueAt = plan.credits[0].expiresAt - AUTO_RESET_LEAD_MS;
2670
+ if (!plan.expiryEnabled) {
2671
+ this.cancelPlan(accountRef);
2672
+ return;
2673
+ }
2674
+ const dueAt = plan.credits[0].expiresAt - plan.leadMinutes * 60000;
2651
2675
  plan.handle = this.timer.schedule(() => this.executePlan(accountRef, plan), Math.min(MAX_TIMER_DELAY_MS, Math.max(0, dueAt - this.now())));
2652
2676
  }
2653
2677
  executePlan(accountRef, plan) {
@@ -2659,32 +2683,51 @@ class AutoResetScheduler {
2659
2683
  return;
2660
2684
  try {
2661
2685
  const account = accountListItem(await this.accounts.get(accountRef));
2662
- if (!account.available || !account.autoResetCredits || account.pendingAutoReset) {
2686
+ if (!account.available || !(account.autoResetCredits || account.autoResetOnLowBalance) || account.pendingAutoReset) {
2663
2687
  this.cancelPlan(accountRef, plan);
2664
2688
  return;
2665
2689
  }
2690
+ plan.leadMinutes = account.autoResetLeadMinutes;
2691
+ plan.expiryEnabled = account.autoResetCredits;
2666
2692
  while (this.plans.get(accountRef) === plan && plan.credits.length > 0) {
2667
2693
  if (this.disposed || this.signal.aborted)
2668
2694
  return;
2669
2695
  const credit = plan.credits[0];
2670
- if (credit.expiresAt - AUTO_RESET_LEAD_MS > this.now())
2696
+ const latestConfig = accountListItem(await this.accounts.get(accountRef));
2697
+ plan.leadMinutes = latestConfig.autoResetLeadMinutes;
2698
+ plan.expiryEnabled = latestConfig.autoResetCredits;
2699
+ const balanceAttempt = plan.lowBalance && latestConfig.autoResetOnLowBalance;
2700
+ if (!balanceAttempt && (!plan.expiryEnabled || credit.expiresAt - plan.leadMinutes * 60000 > this.now()))
2671
2701
  break;
2672
2702
  plan.credits.shift();
2673
- if (!isAutoResetDue(credit, this.now()))
2703
+ if (balanceAttempt)
2704
+ plan.lowBalance = false;
2705
+ if (!balanceAttempt && !isAutoResetDue(credit, this.now(), plan.leadMinutes))
2674
2706
  continue;
2675
2707
  const attempt = await this.accounts.claimAutoReset(accountRef, credit, this.now());
2676
- if (!attempt)
2708
+ if (!attempt) {
2677
2709
  continue;
2710
+ }
2678
2711
  let deferred;
2679
2712
  try {
2680
2713
  const result = await this.usage.consume(accountRef, attempt.redeemRequestId, credit.id, this.signal, {
2681
2714
  canConsume: async (selected) => {
2682
- const latest = await this.accounts.get(accountRef);
2683
- if (this.disposed || this.signal.aborted || latest.autoResetCredits !== true || latest.status !== "active")
2715
+ const fresh = balanceAttempt ? await this.usage.get(accountRef, this.signal, { forceFresh: true }).catch(() => {
2716
+ return;
2717
+ }) : undefined;
2718
+ const latest = accountListItem(await this.accounts.get(accountRef));
2719
+ if (this.disposed || this.signal.aborted || !latest.available)
2720
+ return false;
2721
+ if (balanceAttempt) {
2722
+ const first = fresh?.resetCredits.state === "fresh" && (fresh.resetCredits.availableCount ?? 0) > 0 ? [...fresh.resetCredits.credits ?? []].filter((item) => item.status === "available" && Number.isFinite(item.expiresAt) && item.expiresAt > this.now()).sort((a, b) => a.expiresAt - b.expiresAt)[0] : undefined;
2723
+ if (latest.autoResetOnLowBalance && fresh !== undefined && first?.id === selected.id && isLowBalance(fresh.usage, latest.autoResetRemainingPercent, this.now()))
2724
+ return true;
2725
+ }
2726
+ if (!latest.autoResetCredits)
2684
2727
  return false;
2685
- if (selected.status === "available" && typeof selected.expiresAt === "number" && Number.isFinite(selected.expiresAt) && selected.expiresAt - AUTO_RESET_LEAD_MS > this.now())
2728
+ if (selected.status === "available" && typeof selected.expiresAt === "number" && Number.isFinite(selected.expiresAt) && selected.expiresAt - latest.autoResetLeadMinutes * 60000 > this.now())
2686
2729
  deferred = selected;
2687
- return isAutoResetDue(selected, this.now());
2730
+ return isAutoResetDue(selected, this.now(), latest.autoResetLeadMinutes);
2688
2731
  }
2689
2732
  });
2690
2733
  if (result.outcome === "reset_outcome_unknown") {
@@ -3202,10 +3245,14 @@ class ChatgptControl {
3202
3245
  }) },
3203
3246
  { path: "/accounts/auto-reset", methods: ["POST"], handler: "setAutoResetCredits", invoke: invoke(async (context) => {
3204
3247
  const body = await this.readJson(context);
3205
- exactKeys(body, ["accountRef", "enabled"]);
3206
- if (typeof body.enabled !== "boolean")
3248
+ exactKeys(body, ["accountRef", "enabled", "leadMinutes", "lowBalanceEnabled", "remainingPercent"]);
3249
+ if ("enabled" in body && typeof body.enabled !== "boolean" || "lowBalanceEnabled" in body && typeof body.lowBalanceEnabled !== "boolean" || "leadMinutes" in body && (typeof body.leadMinutes !== "number" || !Number.isInteger(body.leadMinutes) || body.leadMinutes < 1 || body.leadMinutes > 10080) || "remainingPercent" in body && (typeof body.remainingPercent !== "number" || !Number.isInteger(body.remainingPercent) || body.remainingPercent < 0 || body.remainingPercent > 100))
3207
3250
  throw new ControlError("invalid_input");
3208
- return jsonResponse({ account: accountListItem(await this.accounts.setAutoResetCredits(requiredText(body.accountRef, 128), body.enabled)) });
3251
+ return jsonResponse({ account: accountListItem(await this.accounts.setAutoResetCredits(requiredText(body.accountRef, 128), body.enabled, {
3252
+ leadMinutes: body.leadMinutes,
3253
+ lowBalanceEnabled: body.lowBalanceEnabled,
3254
+ remainingPercent: body.remainingPercent
3255
+ })) });
3209
3256
  }) },
3210
3257
  { path: "/accounts/draft", methods: ["POST"], handler: "createDraft", invoke: invoke((context) => this.draft(context, "codex")) },
3211
3258
  { path: "/accounts/siwc/draft", methods: ["POST"], handler: "createSiwcDraft", invoke: invoke((context) => this.draft(context, "siwc")) },
@@ -3381,4 +3428,4 @@ export {
3381
3428
  rpc
3382
3429
  };
3383
3430
 
3384
- //# debugId=ADBAC825AA911C0864756E2164756E21
3431
+ //# debugId=88F390C0C885560A64756E2164756E21