@narumitw/pi-usage 0.57.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.ts CHANGED
@@ -520,10 +520,163 @@ function decimalAmount(value) {
520
520
  return typeof value === "string" && value.length <= 64 && /^(?:0|[1-9]\d*)(?:\.\d+)?$/u.test(value);
521
521
  }
522
522
 
523
+ // src/providers/fireworks.ts
524
+ var NANOS_PER_UNIT = 1000000000n;
525
+ var CURRENCY_PATTERN = /^[A-Z]{3}$/u;
526
+ var ACCOUNT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$/u;
527
+ var INTEGER_PATTERN = /^-?\d+$/u;
528
+ var INT64_MIN = -(2n ** 63n);
529
+ var INT64_MAX = 2n ** 63n - 1n;
530
+ var MAX_UNITS_CHARS = 20;
531
+ var MAX_NANOS_CHARS = 11;
532
+ var SERIES_KEYS = ["serverless", "dedicated", "training", "other"];
533
+ var SERIES_LABELS = {
534
+ serverless: "Serverless",
535
+ dedicated: "Dedicated deployments",
536
+ training: "Training",
537
+ other: "Other"
538
+ };
539
+ function isFireworksAccountId(value) {
540
+ return typeof value === "string" && ACCOUNT_ID_PATTERN.test(value);
541
+ }
542
+ function normalizeFireworksAccountsPayload(payload) {
543
+ if (!Array.isArray(payload.accounts)) {
544
+ throw new Error("Fireworks accounts response did not contain an accounts array.");
545
+ }
546
+ const accounts = [];
547
+ for (const raw of payload.accounts) {
548
+ const account = asObject3(raw);
549
+ if (!account) throw new Error("Fireworks accounts response row was not an object.");
550
+ if (typeof account.name !== "string") {
551
+ throw new Error("Fireworks accounts response omitted the account resource name.");
552
+ }
553
+ const match = /^accounts\/([^/]+)$/u.exec(account.name);
554
+ if (!match || !isFireworksAccountId(match[1])) {
555
+ throw new Error("Fireworks accounts response returned an unsafe account resource name.");
556
+ }
557
+ const accountId = match[1];
558
+ if (accounts.includes(accountId)) {
559
+ throw new Error(`Fireworks accounts response repeated ${accountId}.`);
560
+ }
561
+ accounts.push(accountId);
562
+ }
563
+ return accounts;
564
+ }
565
+ function normalizeFireworksBillingSummaryPayload(payload, accountId, capturedAt) {
566
+ if (!isFireworksAccountId(accountId)) {
567
+ throw new Error("Fireworks billing summary received an unsafe account identifier.");
568
+ }
569
+ if (payload.lineItems !== void 0 && !Array.isArray(payload.lineItems)) {
570
+ throw new Error("Fireworks billing summary lineItems was not an array.");
571
+ }
572
+ const totals = /* @__PURE__ */ new Map();
573
+ for (const raw of payload.lineItems ?? []) {
574
+ const lineItem = asObject3(raw);
575
+ if (!lineItem) throw new Error("Fireworks billing line item was not an object.");
576
+ const cost = moneyAmount(lineItem.totalCost, "line item total cost");
577
+ const series = seriesKey(lineItem.series);
578
+ let amounts = totals.get(cost.currency);
579
+ if (!amounts) {
580
+ amounts = /* @__PURE__ */ new Map();
581
+ totals.set(cost.currency, amounts);
582
+ }
583
+ amounts.set(series, (amounts.get(series) ?? 0n) + cost.amount);
584
+ }
585
+ const metrics = [];
586
+ for (const [currency, amounts] of totals) {
587
+ metrics.push({
588
+ id: `${currency.toLowerCase()}-total`,
589
+ label: "Total spend",
590
+ value: formatMoneyAmount(sumSeries(amounts)),
591
+ unit: "currency",
592
+ currency
593
+ });
594
+ for (const series of SERIES_KEYS) {
595
+ const amount = amounts.get(series);
596
+ if (amount === void 0) continue;
597
+ metrics.push({
598
+ id: `${currency.toLowerCase()}-${series}`,
599
+ label: SERIES_LABELS[series],
600
+ value: formatMoneyAmount(amount),
601
+ unit: "currency",
602
+ currency
603
+ });
604
+ }
605
+ }
606
+ const notes = [
607
+ "Rated line items may differ from the final invoice once credits or adjustments are applied."
608
+ ];
609
+ if (metrics.length === 0) {
610
+ notes.push("Fireworks returned no rated line items for the last 30 days.");
611
+ }
612
+ return {
613
+ providerId: "fireworks",
614
+ providerName: "Fireworks",
615
+ capturedAt,
616
+ source: "fireworks-billing-summary",
617
+ semantics: { kind: "api-key", label: "Fireworks API spend" },
618
+ accountLabel: sanitizeDisplayText(accountId, 80),
619
+ buckets: [],
620
+ metrics,
621
+ notes
622
+ };
623
+ }
624
+ function moneyAmount(value, description) {
625
+ const money = asObject3(value);
626
+ if (!money) throw new Error(`Fireworks billing ${description} was not a money object.`);
627
+ const currency = typeof money.currencyCode === "string" ? money.currencyCode : void 0;
628
+ if (!currency || !CURRENCY_PATTERN.test(currency)) {
629
+ throw new Error(`Fireworks billing ${description} currency was not an ISO 4217 code.`);
630
+ }
631
+ const units = money.units === void 0 ? 0n : integerComponent(money.units, description, "whole units", MAX_UNITS_CHARS);
632
+ if (units < INT64_MIN || units > INT64_MAX) {
633
+ throw new Error(`Fireworks billing ${description} whole units exceeded the int64 range.`);
634
+ }
635
+ const nanos = money.nanos === void 0 ? 0n : integerComponent(money.nanos, description, "nano units", MAX_NANOS_CHARS);
636
+ if (nanos <= -NANOS_PER_UNIT || nanos >= NANOS_PER_UNIT) {
637
+ throw new Error(`Fireworks billing ${description} nano units exceeded the Money range.`);
638
+ }
639
+ if (units > 0n && nanos < 0n || units < 0n && nanos > 0n) {
640
+ throw new Error(`Fireworks billing ${description} mixed unit and nano signs.`);
641
+ }
642
+ return { currency, amount: units * NANOS_PER_UNIT + nanos };
643
+ }
644
+ function integerComponent(value, description, component, maxChars) {
645
+ const text = typeof value === "number" && Number.isSafeInteger(value) ? String(value) : typeof value === "string" && INTEGER_PATTERN.test(value) ? value : void 0;
646
+ if (text === void 0 || text.length > maxChars) {
647
+ throw new Error(`Fireworks billing ${description} ${component} was not a bounded integer.`);
648
+ }
649
+ return BigInt(text);
650
+ }
651
+ function seriesKey(value) {
652
+ if (value === void 0 || value === null) return "other";
653
+ if (typeof value !== "string") throw new Error("Fireworks billing line item series was invalid.");
654
+ if (value === "SERVERLESS") return "serverless";
655
+ if (value === "DEDICATED_DEPLOYMENT") return "dedicated";
656
+ if (value === "TRAINING") return "training";
657
+ return "other";
658
+ }
659
+ function sumSeries(amounts) {
660
+ let total = 0n;
661
+ for (const amount of amounts.values()) total += amount;
662
+ return total;
663
+ }
664
+ function formatMoneyAmount(amount) {
665
+ const negative = amount < 0n;
666
+ const magnitude = negative ? -amount : amount;
667
+ const units = magnitude / NANOS_PER_UNIT;
668
+ const nanos = (magnitude % NANOS_PER_UNIT).toString().padStart(9, "0").replace(/0+$/u, "");
669
+ return `${negative ? "-" : ""}${units.toString()}${nanos ? `.${nanos}` : ""}`;
670
+ }
671
+ function asObject3(value) {
672
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
673
+ return value;
674
+ }
675
+
523
676
  // src/providers/github-copilot.ts
524
677
  function normalizeGitHubCopilotUsagePayload(payload, capturedAt) {
525
- const snapshots = asObject3(payload.quota_snapshots);
526
- const premium = asObject3(snapshots?.premium_interactions);
678
+ const snapshots = asObject4(payload.quota_snapshots);
679
+ const premium = asObject4(snapshots?.premium_interactions);
527
680
  const metrics = [];
528
681
  let semanticsLabel;
529
682
  let bucket;
@@ -564,8 +717,8 @@ function normalizeGitHubCopilotUsagePayload(payload, capturedAt) {
564
717
  };
565
718
  }
566
719
  } else {
567
- const limited = asObject3(payload.limited_user_quotas);
568
- const monthly = asObject3(payload.monthly_quotas);
720
+ const limited = asObject4(payload.limited_user_quotas);
721
+ const monthly = asObject4(payload.monthly_quotas);
569
722
  const remaining = asNonnegativeNumber(limited?.chat);
570
723
  const entitlement = asNonnegativeNumber(monthly?.chat);
571
724
  if (remaining === void 0 || entitlement === void 0) {
@@ -604,7 +757,7 @@ function resetTimestamp(payload) {
604
757
  const milliseconds = Date.parse(raw);
605
758
  return Number.isNaN(milliseconds) ? {} : { resetsAt: Math.floor(milliseconds / 1e3) };
606
759
  }
607
- function asObject3(value) {
760
+ function asObject4(value) {
608
761
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
609
762
  return value;
610
763
  }
@@ -627,7 +780,7 @@ var DAILY_WINDOW_MINUTES = 1440;
627
780
  var WEEKLY_WINDOW_MINUTES = 10080;
628
781
  var FIXED_POINT_UNITS_PER_CENT = 1e6;
629
782
  function normalizeKimiCodingUsagePayload(payload, capturedAt) {
630
- const root = asObject4(payload);
783
+ const root = asObject5(payload);
631
784
  if (!root) throw new Error("Kimi Coding usage response was not an object.");
632
785
  const candidates = [];
633
786
  let omittedWindow = false;
@@ -636,7 +789,7 @@ function normalizeKimiCodingUsagePayload(payload, capturedAt) {
636
789
  else if (root.usage !== void 0) omittedWindow = true;
637
790
  if (Array.isArray(root.limits)) {
638
791
  for (const raw of root.limits) {
639
- const item = asObject4(raw);
792
+ const item = asObject5(raw);
640
793
  const windowMinutes = parseWindowMinutes(item?.window);
641
794
  const label = sanitizedLabel(item?.name);
642
795
  const bucket = windowMinutes === void 0 ? void 0 : parseUsageRow(item?.detail, windowMinutes, label ?? defaultWindowLabel(windowMinutes));
@@ -673,7 +826,7 @@ function normalizeKimiCodingUsagePayload(payload, capturedAt) {
673
826
  };
674
827
  }
675
828
  function parseUsageRow(value, windowMinutes, label) {
676
- const row = asObject4(value);
829
+ const row = asObject5(value);
677
830
  if (!row) return void 0;
678
831
  const used = asNonnegativeInteger2(row.used);
679
832
  const limit = asNonnegativeInteger2(row.limit);
@@ -691,7 +844,7 @@ function parseUsageRow(value, windowMinutes, label) {
691
844
  };
692
845
  }
693
846
  function parseWindowMinutes(value) {
694
- const window = asObject4(value);
847
+ const window = asObject5(value);
695
848
  if (!window) return void 0;
696
849
  const duration = asPositiveInteger(window.duration);
697
850
  if (duration === void 0) return void 0;
@@ -701,8 +854,8 @@ function parseWindowMinutes(value) {
701
854
  return Number.isSafeInteger(minutes) ? minutes : void 0;
702
855
  }
703
856
  function parseBoosterWallet(value) {
704
- const wallet = asObject4(value);
705
- const balance = asObject4(wallet?.balance);
857
+ const wallet = asObject5(value);
858
+ const balance = asObject5(wallet?.balance);
706
859
  if (!wallet || !balance || balance.type !== "BOOSTER") return [];
707
860
  const totalRaw = asPositiveInteger(balance.amount);
708
861
  if (totalRaw === void 0) return [];
@@ -753,7 +906,7 @@ function parseBoosterWallet(value) {
753
906
  return metrics;
754
907
  }
755
908
  function parseMoney(value) {
756
- const money = asObject4(value);
909
+ const money = asObject5(value);
757
910
  if (!money) return void 0;
758
911
  const cents = asNonnegativeInteger2(money.priceInCents);
759
912
  if (cents === void 0) return void 0;
@@ -767,7 +920,7 @@ function fixedPointToMajor(value) {
767
920
  const major = roundedCents / 100;
768
921
  return Number.isSafeInteger(roundedCents) && Number.isFinite(major) ? major : void 0;
769
922
  }
770
- function asObject4(value) {
923
+ function asObject5(value) {
771
924
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
772
925
  return value;
773
926
  }
@@ -841,12 +994,12 @@ var ZEN_WINDOWS = [
841
994
  { key: "monthly", label: "Monthly" }
842
995
  ];
843
996
  function normalizeOpenCodeZenPayload(payload, capturedAt) {
844
- const usage = asObject5(payload.usage);
997
+ const usage = asObject6(payload.usage);
845
998
  if (!usage) throw new Error("OpenCode Zen usage response was not an object.");
846
999
  const buckets = [];
847
1000
  const notes = [];
848
1001
  for (const window of ZEN_WINDOWS) {
849
- const raw = asObject5(usage[window.key]);
1002
+ const raw = asObject6(usage[window.key]);
850
1003
  if (!raw) continue;
851
1004
  const status = asString3(raw.status);
852
1005
  if (status !== "ok" && status !== "rate-limited") {
@@ -883,7 +1036,7 @@ function normalizeOpenCodeZenPayload(payload, capturedAt) {
883
1036
  ...notes.length > 0 ? { notes } : {}
884
1037
  };
885
1038
  }
886
- function asObject5(value) {
1039
+ function asObject6(value) {
887
1040
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
888
1041
  return value;
889
1042
  }
@@ -907,7 +1060,7 @@ function clampPercent2(value) {
907
1060
 
908
1061
  // src/providers/openrouter.ts
909
1062
  function normalizeOpenRouterKeyPayload(payload, capturedAt) {
910
- const data = asObject6(payload.data);
1063
+ const data = asObject7(payload.data);
911
1064
  if (!data) throw new Error("OpenRouter key response data was not an object.");
912
1065
  const limit = asNonnegativeNumber3(data.limit);
913
1066
  const remaining = asNonnegativeNumber3(data.limit_remaining);
@@ -953,7 +1106,7 @@ function addUsageMetric(metrics, id, label, value) {
953
1106
  if (amount === void 0) return;
954
1107
  metrics.push({ id, label, value: amount, unit: "usd" });
955
1108
  }
956
- function asObject6(value) {
1109
+ function asObject7(value) {
957
1110
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
958
1111
  return value;
959
1112
  }
@@ -1131,13 +1284,13 @@ function isRecord2(value) {
1131
1284
  var FIVE_HOUR_WINDOW_MINUTES2 = 300;
1132
1285
  var WEEKLY_WINDOW_MINUTES2 = 10080;
1133
1286
  function normalizeZaiQuotaPayload(providerId, providerName, payload, capturedAt) {
1134
- const data = asObject7(payload.data);
1287
+ const data = asObject8(payload.data);
1135
1288
  if (!data) throw new Error("Z.AI quota response data was not an object.");
1136
1289
  const limits = Array.isArray(data.limits) ? data.limits : [];
1137
1290
  const buckets = [];
1138
1291
  const metrics = [];
1139
1292
  for (const raw of limits) {
1140
- const limit = asObject7(raw);
1293
+ const limit = asObject8(raw);
1141
1294
  if (!limit) continue;
1142
1295
  const type = asString5(limit.type);
1143
1296
  const unit = asNonnegativeNumber4(limit.unit);
@@ -1209,7 +1362,7 @@ function addCountBucket(buckets, limit, id, label, windowMinutes) {
1209
1362
  function addUsageDetailMetrics(metrics, value) {
1210
1363
  if (!Array.isArray(value)) return;
1211
1364
  for (const raw of value) {
1212
- const detail = asObject7(raw);
1365
+ const detail = asObject8(raw);
1213
1366
  if (!detail) continue;
1214
1367
  const label = asString5(detail.modelCode);
1215
1368
  const usage = asNonnegativeNumber4(detail.usage);
@@ -1217,7 +1370,7 @@ function addUsageDetailMetrics(metrics, value) {
1217
1370
  metrics.push({ id: `mcp-${kebabCase(label)}`, label, value: usage, unit: "count" });
1218
1371
  }
1219
1372
  }
1220
- function asObject7(value) {
1373
+ function asObject8(value) {
1221
1374
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1222
1375
  return value;
1223
1376
  }
@@ -1244,6 +1397,9 @@ function clampPercent3(value) {
1244
1397
  // src/query.ts
1245
1398
  var CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
1246
1399
  var DEEPSEEK_BALANCE_URL = "https://api.deepseek.com/user/balance";
1400
+ var FIREWORKS_BILLING_SUMMARY_ORIGIN = "https://api.fireworks.ai";
1401
+ var FIREWORKS_SPEND_WINDOW_DAYS = 30;
1402
+ var FIREWORKS_MAX_ACCOUNT_PAGES = 5;
1247
1403
  var GITHUB_COPILOT_USAGE_URL = "https://api.github.com/copilot_internal/user";
1248
1404
  var OPENROUTER_KEY_URL = "https://openrouter.ai/api/v1/key";
1249
1405
  var OPENCODE_GO_USAGE_URL = "https://opencode.ai/zen/go/v1/usage";
@@ -1331,6 +1487,35 @@ var SUPPORTED_ADAPTERS = [
1331
1487
  return normalizeOpenRouterKeyPayload(payload, Date.now());
1332
1488
  }
1333
1489
  },
1490
+ {
1491
+ id: "fireworks",
1492
+ displayName: "Fireworks",
1493
+ semantics: { kind: "api-key", label: "Fireworks API spend" },
1494
+ async query(auth, signal, timeoutMs, guard, settings) {
1495
+ if (!guard) throw new Error("Fireworks API spend requires request-boundary revalidation.");
1496
+ const startedAt = Date.now();
1497
+ await guard();
1498
+ const accountId = await resolveFireworksAccountId(
1499
+ auth,
1500
+ signal,
1501
+ remainingTimeout(timeoutMs, startedAt, "resolving the Fireworks account"),
1502
+ guard,
1503
+ settings?.fireworksAccountId
1504
+ );
1505
+ await guard();
1506
+ const billingWindowAt = Date.now();
1507
+ const payload = await fetchProviderJson(
1508
+ fireworksBillingSummaryUrl(accountId, billingWindowAt),
1509
+ auth,
1510
+ signal,
1511
+ remainingTimeout(timeoutMs, startedAt, "fetching Fireworks rated spend"),
1512
+ "Fireworks billing summary endpoint",
1513
+ { redirect: "error" }
1514
+ );
1515
+ await guard();
1516
+ return normalizeFireworksBillingSummaryPayload(payload, accountId, Date.now());
1517
+ }
1518
+ },
1334
1519
  {
1335
1520
  id: "opencode-go",
1336
1521
  displayName: "OpenCode Go",
@@ -1535,9 +1720,9 @@ async function resolveUsageAuth(ctx, adapter, salt = AUTH_FINGERPRINT_SALT, cred
1535
1720
  model
1536
1721
  };
1537
1722
  }
1538
- async function queryProviderUsage(adapter, auth, signal, timeoutMs, guard) {
1723
+ async function queryProviderUsage(adapter, auth, signal, timeoutMs, guard, settings) {
1539
1724
  try {
1540
- return await adapter.query(auth, signal, timeoutMs, guard);
1725
+ return await adapter.query(auth, signal, timeoutMs, guard, settings);
1541
1726
  } catch (error) {
1542
1727
  if (isStaleExtensionContextError(error) || isAbortError(error)) throw error;
1543
1728
  throw new Error(redactUsageError(errorMessage(error), auth.secrets));
@@ -1678,7 +1863,7 @@ function resolveXaiUsageAuth(auth, model, salt, candidates) {
1678
1863
  const matches = [];
1679
1864
  for (const candidate of candidates) {
1680
1865
  try {
1681
- const credential = asObject8(candidate);
1866
+ const credential = asObject9(candidate);
1682
1867
  if (credential?.type !== "oauth") continue;
1683
1868
  sawOAuth = true;
1684
1869
  if (credential.access !== resolvedAccess) continue;
@@ -1732,7 +1917,7 @@ function resolveGitHubCopilotUsageAuth(auth, model, salt, candidates, standalone
1732
1917
  const matches = /* @__PURE__ */ new Map();
1733
1918
  for (const candidate of candidates) {
1734
1919
  try {
1735
- const credential = asObject8(candidate);
1920
+ const credential = asObject9(candidate);
1736
1921
  if (credential?.type !== "oauth") continue;
1737
1922
  sawOAuth = true;
1738
1923
  const storedAccess2 = typeof credential.access === "string" && credential.access ? credential.access : void 0;
@@ -1794,7 +1979,7 @@ function bearerToken(authorization) {
1794
1979
  const match = /^Bearer\s+(.+)$/iu.exec(authorization ?? "");
1795
1980
  return match?.[1];
1796
1981
  }
1797
- function asObject8(value) {
1982
+ function asObject9(value) {
1798
1983
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1799
1984
  return value;
1800
1985
  }
@@ -1814,6 +1999,7 @@ function hasOfficialUrlOrigin(value, providerId) {
1814
1999
  const url = new URL(value);
1815
2000
  if (providerId === "openai-codex") return url.origin === "https://chatgpt.com";
1816
2001
  if (providerId === "deepseek") return url.origin === "https://api.deepseek.com";
2002
+ if (providerId === "fireworks") return url.origin === FIREWORKS_BILLING_SUMMARY_ORIGIN;
1817
2003
  if (providerId === "openrouter") return url.origin === "https://openrouter.ai";
1818
2004
  if (providerId === "opencode-go") return url.origin === "https://opencode.ai";
1819
2005
  if (providerId === "kimi-coding") return url.origin === "https://api.kimi.com";
@@ -1843,11 +2029,87 @@ function validatedXaiUserId(value) {
1843
2029
  }
1844
2030
  return value;
1845
2031
  }
1846
- function remainingTimeout(timeoutMs, startedAt) {
2032
+ function remainingTimeout(timeoutMs, startedAt, description = "fetching xAI consumer usage") {
1847
2033
  const remaining = timeoutMs - (Date.now() - startedAt);
1848
- if (remaining <= 0) throw new Error("Timed out while fetching xAI consumer usage.");
2034
+ if (remaining <= 0) throw new Error(`Timed out while ${description}.`);
1849
2035
  return remaining;
1850
2036
  }
2037
+ async function resolveFireworksAccountId(auth, signal, timeoutMs, guard, configuredAccountId) {
2038
+ if (configuredAccountId !== void 0 && !isFireworksAccountId(configuredAccountId)) {
2039
+ throw new Error("The Fireworks account setting was not a safe account slug.");
2040
+ }
2041
+ const startedAt = Date.now();
2042
+ const accounts = [];
2043
+ let pageToken;
2044
+ for (let page = 0; page < FIREWORKS_MAX_ACCOUNT_PAGES; page += 1) {
2045
+ await guard();
2046
+ const payload = await fetchProviderJson(
2047
+ fireworksAccountsUrl(pageToken),
2048
+ auth,
2049
+ signal,
2050
+ remainingTimeout(timeoutMs, startedAt, "fetching Fireworks accounts"),
2051
+ "Fireworks accounts endpoint",
2052
+ { redirect: "error" }
2053
+ );
2054
+ for (const accountId of normalizeFireworksAccountsPayload(
2055
+ payload
2056
+ )) {
2057
+ if (accounts.includes(accountId)) {
2058
+ throw new Error(`Fireworks accounts listing repeated ${accountId}.`);
2059
+ }
2060
+ accounts.push(accountId);
2061
+ if (configuredAccountId === accountId) return accountId;
2062
+ }
2063
+ pageToken = fireworksNextPageToken(payload.nextPageToken);
2064
+ if (!pageToken) break;
2065
+ }
2066
+ if (pageToken) {
2067
+ throw new Error(
2068
+ configuredAccountId ? `The configured Fireworks account was not found within the first ${FIREWORKS_MAX_ACCOUNT_PAGES} listing pages.` : `Fireworks account listing exceeded ${FIREWORKS_MAX_ACCOUNT_PAGES} pages; set fireworksAccountId in pi-usage.json to an account returned in those pages.`
2069
+ );
2070
+ }
2071
+ if (accounts.length === 0) {
2072
+ throw new Error("Fireworks account discovery returned no accounts for this API key.");
2073
+ }
2074
+ if (configuredAccountId) {
2075
+ throw new Error(
2076
+ "The configured Fireworks account does not match an account visible to this API key."
2077
+ );
2078
+ }
2079
+ if (accounts.length === 1) return accounts[0];
2080
+ const preview = accounts.slice(0, 8).join(", ");
2081
+ const suffix = accounts.length > 8 ? ` \u2026and ${accounts.length - 8} more` : "";
2082
+ throw new Error(
2083
+ `The Fireworks key can see ${accounts.length} accounts (${preview}${suffix}); set fireworksAccountId in pi-usage.json to one of them.`
2084
+ );
2085
+ }
2086
+ function fireworksAccountsUrl(pageToken) {
2087
+ const url = new URL("/v1/accounts", FIREWORKS_BILLING_SUMMARY_ORIGIN);
2088
+ url.searchParams.set("pageSize", "200");
2089
+ if (pageToken !== void 0) url.searchParams.set("pageToken", pageToken);
2090
+ return url.toString();
2091
+ }
2092
+ function fireworksNextPageToken(value) {
2093
+ if (value === void 0 || value === null) return void 0;
2094
+ if (typeof value !== "string" || !value || value.length > 512) {
2095
+ throw new Error("Fireworks accounts listing returned an invalid page token.");
2096
+ }
2097
+ return value;
2098
+ }
2099
+ function fireworksBillingSummaryUrl(accountId, startedAt) {
2100
+ const dayMs = 24 * 60 * 60 * 1e3;
2101
+ const dayFloor = (time) => `${new Date(time).toISOString().slice(0, 10)}T00:00:00Z`;
2102
+ const url = new URL(
2103
+ `/v1/accounts/${accountId}/billing/summary`,
2104
+ FIREWORKS_BILLING_SUMMARY_ORIGIN
2105
+ );
2106
+ url.searchParams.set(
2107
+ "startTime",
2108
+ dayFloor(startedAt - (FIREWORKS_SPEND_WINDOW_DAYS - 1) * dayMs)
2109
+ );
2110
+ url.searchParams.set("endTime", dayFloor(startedAt + dayMs));
2111
+ return url.toString();
2112
+ }
1851
2113
  function zaiMonitorUrl(baseUrl) {
1852
2114
  const base = baseUrl?.trim();
1853
2115
  if (!base) throw new Error("Z.AI model base URL is unavailable.");
@@ -2001,7 +2263,7 @@ function normalizeCodexResetCreditsPayload(payload) {
2001
2263
  if (rawCredits !== void 0 && !Array.isArray(rawCredits)) {
2002
2264
  throw new Error("Codex reset credits response returned invalid credits.");
2003
2265
  }
2004
- const options = (rawCredits ?? []).map(asObject9).filter((credit) => Boolean(credit)).filter((credit) => credit.status === "available" && credit.reset_type === "codex_rate_limits").map(normalizeResetOption).sort(
2266
+ const options = (rawCredits ?? []).map(asObject10).filter((credit) => Boolean(credit)).filter((credit) => credit.status === "available" && credit.reset_type === "codex_rate_limits").map(normalizeResetOption).sort(
2005
2267
  (left, right) => (left.expiresAt ?? Number.MAX_SAFE_INTEGER) - (right.expiresAt ?? Number.MAX_SAFE_INTEGER)
2006
2268
  ).slice(0, Math.min(availableCount, MAX_RESET_OPTIONS));
2007
2269
  if (availableCount > 0 && options.length === 0) {
@@ -2016,7 +2278,7 @@ function selectCodexResetCredential(candidates, resolvedAccess, resolvedAccountI
2016
2278
  const matches = /* @__PURE__ */ new Map();
2017
2279
  for (const candidate of candidates) {
2018
2280
  try {
2019
- const credential = asObject9(candidate);
2281
+ const credential = asObject10(candidate);
2020
2282
  if (credential?.type !== "oauth") continue;
2021
2283
  sawOAuth = true;
2022
2284
  const storedAccess = asNonemptyString(credential.access);
@@ -2055,7 +2317,7 @@ function codexAccountIdFromAccessToken(access) {
2055
2317
  const parts = access.split(".");
2056
2318
  if (parts.length !== 3 || !parts[1]) return void 0;
2057
2319
  const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
2058
- const claims = asObject9(asObject9(payload)?.["https://api.openai.com/auth"]);
2320
+ const claims = asObject10(asObject10(payload)?.["https://api.openai.com/auth"]);
2059
2321
  return validHeaderValue(claims?.chatgpt_account_id);
2060
2322
  } catch {
2061
2323
  return void 0;
@@ -2087,7 +2349,7 @@ function normalizeResetOption(credit) {
2087
2349
  function isCodexResetOutcomeCode(value) {
2088
2350
  return value === "reset" || value === "nothing_to_reset" || value === "no_credit" || value === "already_redeemed";
2089
2351
  }
2090
- function asObject9(value) {
2352
+ function asObject10(value) {
2091
2353
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2092
2354
  return value;
2093
2355
  }
@@ -2128,12 +2390,13 @@ var BAR_SEGMENTS = 20;
2128
2390
  var VALUE_COLUMN = 29;
2129
2391
  function formatUsageReport(report, displayState) {
2130
2392
  const stateLabel = displayState === "current" ? "Current" : "Configured";
2131
- const title = report.providerId === "deepseek" ? "DeepSeek API Balance" : `${report.providerName} Usage`;
2393
+ const title = report.providerId === "deepseek" ? "DeepSeek API Balance" : report.providerId === "fireworks" ? "Fireworks API Spend" : `${report.providerName} Usage`;
2132
2394
  const lines = [`${title} \xB7 ${stateLabel}`];
2133
2395
  if (report.accountLabel) lines.push(`Account: ${report.accountLabel}`);
2134
2396
  lines.push(`Semantics: ${report.semantics.label}`, "");
2135
2397
  if (report.providerId === "openai-codex") formatCodexReport(lines, report);
2136
2398
  else if (report.providerId === "deepseek") formatDeepSeekReport(lines, report);
2399
+ else if (report.providerId === "fireworks") formatFireworksReport(lines, report);
2137
2400
  else if (report.providerId === "github-copilot") formatGitHubCopilotReport(lines, report);
2138
2401
  else if (report.providerId === "openrouter") formatOpenRouterReport(lines, report);
2139
2402
  else if (report.providerId === "opencode-go") formatOpenCodeZenReport(lines, report);
@@ -2152,6 +2415,7 @@ function formatUsageStatusline(report, model, now = Date.now(), showCodexResetCo
2152
2415
  return formatCodexStatusline(report, model, now, showCodexResetCountdown);
2153
2416
  }
2154
2417
  if (report.providerId === "deepseek") return formatDeepSeekStatusline(report);
2418
+ if (report.providerId === "fireworks") return formatFireworksStatusline(report);
2155
2419
  if (report.providerId === "github-copilot") return formatGitHubCopilotStatusline(report);
2156
2420
  if (report.providerId === "openrouter") {
2157
2421
  const limit = report.buckets.find((bucket) => bucket.id === "key-limit");
@@ -2222,6 +2486,29 @@ function formatDeepSeekStatusline(report) {
2222
2486
  });
2223
2487
  return totals.length > 0 ? `deepseek ${totals.join(" \xB7 ")}` : "deepseek balance unavailable";
2224
2488
  }
2489
+ function formatFireworksReport(lines, report) {
2490
+ lines.push(`${"Spend window:".padEnd(VALUE_COLUMN)}Last 30 days (rated)`);
2491
+ for (const currency of fireworksCurrencies(report)) {
2492
+ lines.push("", `${currency} rated spend:`);
2493
+ for (const metric of report.metrics) {
2494
+ if (metric.currency !== currency) continue;
2495
+ lines.push(`${`${metric.label}:`.padEnd(VALUE_COLUMN)}${currency} ${metric.value}`);
2496
+ }
2497
+ }
2498
+ }
2499
+ function formatFireworksStatusline(report) {
2500
+ const totals = report.metrics.filter((metric) => metric.id.endsWith("-total"));
2501
+ if (totals.length === 0) return "fireworks no rated usage";
2502
+ return `fireworks ${totals.map((metric) => `${metric.currency} ${metric.value}`).join(" \xB7 ")}`;
2503
+ }
2504
+ function fireworksCurrencies(report) {
2505
+ const currencies = [];
2506
+ for (const metric of report.metrics) {
2507
+ if (!metric.currency || currencies.includes(metric.currency)) continue;
2508
+ currencies.push(metric.currency);
2509
+ }
2510
+ return currencies;
2511
+ }
2225
2512
  function formatGitHubCopilotReport(lines, report) {
2226
2513
  const quota = findGitHubCopilotQuota(report);
2227
2514
  if (!quota || quota.limit === void 0 || quota.remaining === void 0) {
@@ -2583,9 +2870,13 @@ function normalizeUsageSettings(value) {
2583
2870
  if (Object.hasOwn(value, "codexStatusResetCountdown") && typeof value.codexStatusResetCountdown !== "boolean") {
2584
2871
  return void 0;
2585
2872
  }
2873
+ if (Object.hasOwn(value, "fireworksAccountId") && !isFireworksAccountId(value.fireworksAccountId)) {
2874
+ return void 0;
2875
+ }
2586
2876
  return {
2587
2877
  codexFastMode: typeof value.codexFastMode === "boolean" ? value.codexFastMode : DEFAULT_USAGE_SETTINGS.codexFastMode,
2588
- codexStatusResetCountdown: typeof value.codexStatusResetCountdown === "boolean" ? value.codexStatusResetCountdown : DEFAULT_USAGE_SETTINGS.codexStatusResetCountdown
2878
+ codexStatusResetCountdown: typeof value.codexStatusResetCountdown === "boolean" ? value.codexStatusResetCountdown : DEFAULT_USAGE_SETTINGS.codexStatusResetCountdown,
2879
+ ...isFireworksAccountId(value.fireworksAccountId) ? { fireworksAccountId: value.fireworksAccountId } : {}
2589
2880
  };
2590
2881
  }
2591
2882
  async function loadUsageSettings(path = usageSettingsPath(), signal) {
@@ -2669,7 +2960,11 @@ async function saveUsageSettingsPatch(path, patch, operations, signal) {
2669
2960
  if (latest.kind === "invalid") {
2670
2961
  throw new Error("Cannot overwrite an invalid pi-usage.json; repair it and reload first");
2671
2962
  }
2672
- const document = { ...latest.document, ...patch };
2963
+ const document = { ...latest.document };
2964
+ for (const [key, value] of Object.entries(patch)) {
2965
+ if (value === void 0) delete document[key];
2966
+ else document[key] = value;
2967
+ }
2673
2968
  const settings = normalizeUsageSettings(document);
2674
2969
  if (!settings) throw new Error("Refusing to save invalid pi-usage settings");
2675
2970
  const directory = dirname(path);
@@ -2715,7 +3010,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
2715
3010
  // src/codex-fast-runtime.ts
2716
3011
  var NO_FAST_REQUEST = /* @__PURE__ */ Symbol("no-fast-request");
2717
3012
  var FAST_USAGE_WARNING = "Fast is about 1.5\xD7 faster and uses more of your plan allowance.";
2718
- function registerCodexFastMode(pi, settingsRuntime, refreshStatus) {
3013
+ function registerCodexFastMode(pi, settingsRuntime, refreshStatus, options = {}) {
2719
3014
  let sessionController = new AbortController();
2720
3015
  let generation = 0;
2721
3016
  const pendingFastRequests = /* @__PURE__ */ new Map();
@@ -2771,37 +3066,42 @@ function registerCodexFastMode(pi, settingsRuntime, refreshStatus) {
2771
3066
  await toggle(ctx, !availability.enabled);
2772
3067
  }
2773
3068
  });
2774
- pi.on("session_start", async (_event, ctx) => {
3069
+ const prepareSession = (ctx) => {
3070
+ const sessionId = ctx.sessionManager.getSessionId();
2775
3071
  generation += 1;
2776
3072
  sessionController.abort();
2777
3073
  pendingFastRequests.clear();
2778
3074
  sessionController = new AbortController();
2779
3075
  const ownerGeneration = generation;
2780
- const sessionId = ctx.sessionManager.getSessionId();
2781
- let state;
2782
- try {
2783
- state = await settingsRuntime.reload(sessionController.signal);
2784
- } catch (error) {
2785
- if (sessionController.signal.aborted || ownerGeneration !== generation) return;
2786
- if (ctx.hasUI) {
3076
+ return (async () => {
3077
+ let state;
3078
+ try {
3079
+ state = await settingsRuntime.reload(sessionController.signal);
3080
+ } catch (error) {
3081
+ if (sessionController.signal.aborted || ownerGeneration !== generation) return;
3082
+ if (ctx.hasUI) {
3083
+ ctx.ui.notify(
3084
+ `Could not load pi-usage.json; using defaults. ${errorMessage(error)}`,
3085
+ "warning"
3086
+ );
3087
+ }
3088
+ return;
3089
+ }
3090
+ if (sessionController.signal.aborted || ownerGeneration !== generation || ctx.sessionManager.getSessionId() !== sessionId) {
3091
+ return;
3092
+ }
3093
+ if (ctx.hasUI && state.kind === "invalid") {
2787
3094
  ctx.ui.notify(
2788
- `Could not load pi-usage.json; using defaults. ${errorMessage(error)}`,
3095
+ `Invalid pi-usage.json; using defaults without overwriting it. ${state.issue}`,
2789
3096
  "warning"
2790
3097
  );
2791
3098
  }
2792
- return;
2793
- }
2794
- if (sessionController.signal.aborted || ownerGeneration !== generation || ctx.sessionManager.getSessionId() !== sessionId) {
2795
- return;
2796
- }
2797
- if (ctx.hasUI && state.kind === "invalid") {
2798
- ctx.ui.notify(
2799
- `Invalid pi-usage.json; using defaults without overwriting it. ${state.issue}`,
2800
- "warning"
2801
- );
2802
- }
2803
- refreshStatus(ctx);
2804
- });
3099
+ refreshStatus(ctx);
3100
+ })();
3101
+ };
3102
+ if (options.registerSessionStart !== false) {
3103
+ pi.on("session_start", async (_event, ctx) => prepareSession(ctx));
3104
+ }
2805
3105
  pi.on("before_provider_request", (event, ctx) => {
2806
3106
  const rewritten = rewriteCodexFastPayload(
2807
3107
  event.payload,
@@ -2834,6 +3134,7 @@ function registerCodexFastMode(pi, settingsRuntime, refreshStatus) {
2834
3134
  await settingsRuntime.flush();
2835
3135
  });
2836
3136
  return {
3137
+ prepareSession,
2837
3138
  availability(model) {
2838
3139
  return codexFastAvailability(model, settingsRuntime.get().settings.codexFastMode);
2839
3140
  },
@@ -2912,6 +3213,8 @@ import {
2912
3213
  SettingsList,
2913
3214
  Text
2914
3215
  } from "@earendil-works/pi-tui";
3216
+ var AUTO = "Auto";
3217
+ var EDIT = "Edit\u2026";
2915
3218
  var OFF = "Off";
2916
3219
  var ON = "On";
2917
3220
  async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent, onApplied) {
@@ -2919,7 +3222,23 @@ async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent,
2919
3222
  if (ctx.hasUI) ctx.ui.notify(`Edit settings manually: ${settingsRuntime.get().path}`, "info");
2920
3223
  return false;
2921
3224
  }
2922
- if (parentSignal.aborted || !isCurrent()) return false;
3225
+ let changed = false;
3226
+ while (!parentSignal.aborted && isCurrent()) {
3227
+ const result = await showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, onApplied);
3228
+ if (!result) return changed;
3229
+ changed ||= result.changed;
3230
+ if (!result.editFireworksAccount) return changed;
3231
+ changed ||= await editFireworksAccount(
3232
+ ctx,
3233
+ settingsRuntime,
3234
+ parentSignal,
3235
+ isCurrent,
3236
+ onApplied
3237
+ );
3238
+ }
3239
+ return changed;
3240
+ }
3241
+ async function showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, onApplied) {
2923
3242
  return ctx.ui.custom((tui, theme, _keybindings, done) => {
2924
3243
  const localController = new AbortController();
2925
3244
  const signal = AbortSignal.any([parentSignal, localController.signal]);
@@ -2927,6 +3246,7 @@ async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent,
2927
3246
  let closing = false;
2928
3247
  let saveQueue = Promise.resolve();
2929
3248
  const state = settingsRuntime.get();
3249
+ const fireworksValue = state.settings.fireworksAccountId ?? AUTO;
2930
3250
  const items = [
2931
3251
  {
2932
3252
  id: "codexFastMode",
@@ -2941,6 +3261,13 @@ async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent,
2941
3261
  description: "Show time remaining until each Codex usage limit resets.",
2942
3262
  currentValue: state.settings.codexStatusResetCountdown ? ON : OFF,
2943
3263
  values: [OFF, ON]
3264
+ },
3265
+ {
3266
+ id: "fireworksAccountId",
3267
+ label: "Fireworks account",
3268
+ description: "Select Edit to enter a visible account slug, or submit blank to clear it.",
3269
+ currentValue: fireworksValue,
3270
+ values: state.settings.fireworksAccountId ? [state.settings.fireworksAccountId, EDIT] : [AUTO, EDIT]
2944
3271
  }
2945
3272
  ];
2946
3273
  const container = new Container();
@@ -2950,7 +3277,36 @@ async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent,
2950
3277
  if (closing) return;
2951
3278
  closing = true;
2952
3279
  localController.abort();
2953
- done(changed);
3280
+ done({ changed, editFireworksAccount: false });
3281
+ };
3282
+ const queueUpdate = (id, requested, display) => {
3283
+ saveQueue = saveQueue.then(async () => {
3284
+ const previous = settingsRuntime.get().settings[id];
3285
+ if (settingsRuntime.get().kind === "invalid") {
3286
+ settingsList.updateValue(id, displaySetting(id, previous));
3287
+ if (!signal.aborted && isCurrent()) {
3288
+ ctx.ui.notify("Repair pi-usage.json and reload before changing settings.", "error");
3289
+ tui.requestRender();
3290
+ }
3291
+ return;
3292
+ }
3293
+ try {
3294
+ await settingsRuntime.update({ [id]: requested }, signal);
3295
+ } catch (error) {
3296
+ if (signal.aborted || !isCurrent()) return;
3297
+ settingsList.updateValue(id, displaySetting(id, previous));
3298
+ ctx.ui.notify(`Could not save pi-usage.json: ${errorMessage(error)}`, "error");
3299
+ tui.requestRender();
3300
+ return;
3301
+ }
3302
+ if (previous !== requested) {
3303
+ changed = true;
3304
+ onApplied(id);
3305
+ }
3306
+ if (signal.aborted || !isCurrent()) return;
3307
+ settingsList.updateValue(id, display);
3308
+ tui.requestRender();
3309
+ });
2954
3310
  };
2955
3311
  settingsList = new SettingsList(
2956
3312
  items,
@@ -2958,35 +3314,18 @@ async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent,
2958
3314
  getSettingsListTheme(),
2959
3315
  (id, value) => {
2960
3316
  if (closing || signal.aborted || !isCurrent()) return;
2961
- const settingId = id;
2962
- const requested = value !== OFF;
2963
- saveQueue = saveQueue.then(async () => {
2964
- const previous = settingsRuntime.get().settings[settingId];
2965
- if (settingsRuntime.get().kind === "invalid") {
2966
- settingsList.updateValue(id, displayValue(previous));
2967
- if (!signal.aborted && isCurrent()) {
2968
- ctx.ui.notify("Repair pi-usage.json and reload before changing settings.", "error");
2969
- tui.requestRender();
2970
- }
2971
- return;
2972
- }
2973
- try {
2974
- await settingsRuntime.update({ [settingId]: requested }, signal);
2975
- } catch (error) {
2976
- if (signal.aborted || !isCurrent()) return;
2977
- settingsList.updateValue(id, displayValue(previous));
2978
- ctx.ui.notify(`Could not save pi-usage.json: ${errorMessage(error)}`, "error");
2979
- tui.requestRender();
2980
- return;
2981
- }
2982
- if (previous !== requested) {
2983
- changed = true;
2984
- onApplied(settingId);
3317
+ if (id === "fireworksAccountId") {
3318
+ if (value === EDIT) {
3319
+ saveQueue = saveQueue.then(() => {
3320
+ if (closing || signal.aborted || !isCurrent()) return;
3321
+ closing = true;
3322
+ done({ changed, editFireworksAccount: true });
3323
+ });
2985
3324
  }
2986
- if (signal.aborted || !isCurrent()) return;
2987
- settingsList.updateValue(id, displayValue(requested));
2988
- tui.requestRender();
2989
- });
3325
+ return;
3326
+ }
3327
+ const settingId = id;
3328
+ queueUpdate(settingId, value !== OFF, value);
2990
3329
  },
2991
3330
  cancel
2992
3331
  );
@@ -3008,8 +3347,41 @@ async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent,
3008
3347
  };
3009
3348
  });
3010
3349
  }
3011
- function displayValue(enabled) {
3012
- return enabled ? ON : OFF;
3350
+ async function editFireworksAccount(ctx, settingsRuntime, signal, isCurrent, onApplied) {
3351
+ while (!signal.aborted && isCurrent()) {
3352
+ const state = settingsRuntime.get();
3353
+ if (state.kind === "invalid") {
3354
+ ctx.ui.notify("Repair pi-usage.json and reload before changing settings.", "error");
3355
+ return false;
3356
+ }
3357
+ const entered = await ctx.ui.input(
3358
+ "Fireworks account slug \xB7 submit blank for Auto",
3359
+ state.settings.fireworksAccountId ?? "Example: acme",
3360
+ { signal }
3361
+ );
3362
+ if (signal.aborted || !isCurrent() || entered === void 0) return false;
3363
+ const normalized = entered.trim();
3364
+ const requested = normalized || void 0;
3365
+ if (requested !== void 0 && !isFireworksAccountId(requested)) {
3366
+ ctx.ui.notify("Enter a URL-safe Fireworks account slug.", "warning");
3367
+ continue;
3368
+ }
3369
+ if (requested === state.settings.fireworksAccountId) return false;
3370
+ try {
3371
+ await settingsRuntime.update({ fireworksAccountId: requested }, signal);
3372
+ } catch (error) {
3373
+ if (signal.aborted || !isCurrent()) return false;
3374
+ ctx.ui.notify(`Could not save pi-usage.json: ${errorMessage(error)}`, "error");
3375
+ return false;
3376
+ }
3377
+ onApplied("fireworksAccountId");
3378
+ return true;
3379
+ }
3380
+ return false;
3381
+ }
3382
+ function displaySetting(id, value) {
3383
+ if (id === "fireworksAccountId") return typeof value === "string" ? value : AUTO;
3384
+ return value ? ON : OFF;
3013
3385
  }
3014
3386
 
3015
3387
  // src/usage.ts
@@ -3152,6 +3524,8 @@ function usageExtension(pi, dependencies = {}) {
3152
3524
  const expectedSessionGeneration = sessionGeneration;
3153
3525
  const expectedSessionId = ctx.sessionManager.getSessionId();
3154
3526
  const expectedModelIdentity = modelIdentity(ctx.model);
3527
+ const expectedFireworksAccountId = adapter.id === "fireworks" ? settingsRuntime.get().settings.fireworksAccountId : void 0;
3528
+ const querySettings = adapter.id === "fireworks" ? { fireworksAccountId: expectedFireworksAccountId } : void 0;
3155
3529
  let auth;
3156
3530
  try {
3157
3531
  auth = await awaitWithDeadline(
@@ -3175,8 +3549,8 @@ function usageExtension(pi, dependencies = {}) {
3175
3549
  }
3176
3550
  };
3177
3551
  }
3178
- const requiresRequestBoundaryGuard = adapter.id === "deepseek" || adapter.id === "xai";
3179
- const requestContextChanged = () => expectedSessionGeneration !== sessionGeneration || ctx.sessionManager.getSessionId() !== expectedSessionId || modelIdentity(ctx.model) !== expectedModelIdentity;
3552
+ const requiresRequestBoundaryGuard = ["deepseek", "fireworks", "xai"].includes(adapter.id);
3553
+ const requestContextChanged = () => expectedSessionGeneration !== sessionGeneration || ctx.sessionManager.getSessionId() !== expectedSessionId || modelIdentity(ctx.model) !== expectedModelIdentity || adapter.id === "fireworks" && settingsRuntime.get().settings.fireworksAccountId !== expectedFireworksAccountId;
3180
3554
  if (requiresRequestBoundaryGuard && requestContextChanged()) throw abortError();
3181
3555
  if (!auth) {
3182
3556
  if (displayState === "current") {
@@ -3193,10 +3567,11 @@ function usageExtension(pi, dependencies = {}) {
3193
3567
  authState: "unavailable"
3194
3568
  };
3195
3569
  }
3570
+ const queryFingerprint = adapter.id === "fireworks" ? `${auth.fingerprint}:account:${expectedFireworksAccountId ?? "auto"}` : auth.fingerprint;
3196
3571
  if (displayState === "current") {
3197
- transitionCurrentIdentity(`${adapter.id}:${auth.fingerprint}`, adapter.id);
3572
+ transitionCurrentIdentity(`${adapter.id}:${queryFingerprint}`, adapter.id);
3198
3573
  }
3199
- const cached = !force ? cache.get(adapter.id, auth.fingerprint) : void 0;
3574
+ const cached = !force ? cache.get(adapter.id, queryFingerprint) : void 0;
3200
3575
  if (cached) {
3201
3576
  return {
3202
3577
  state: {
@@ -3209,7 +3584,7 @@ function usageExtension(pi, dependencies = {}) {
3209
3584
  fingerprint: auth.fingerprint
3210
3585
  };
3211
3586
  }
3212
- const failureKey = `${adapter.id}:${auth.fingerprint}`;
3587
+ const failureKey = `${adapter.id}:${queryFingerprint}`;
3213
3588
  const previousFailure = failureBackoff.get(failureKey);
3214
3589
  if (!force && previousFailure && previousFailure.until > Date.now()) {
3215
3590
  return {
@@ -3247,10 +3622,17 @@ function usageExtension(pi, dependencies = {}) {
3247
3622
  throw abortError();
3248
3623
  }
3249
3624
  } : void 0;
3250
- const report = await queryProviderUsage(adapter, auth, signal, remainingMs, guard);
3625
+ const report = await queryProviderUsage(
3626
+ adapter,
3627
+ auth,
3628
+ signal,
3629
+ remainingMs,
3630
+ guard,
3631
+ querySettings
3632
+ );
3251
3633
  if (guard) await guard();
3252
3634
  if (latestQueries.get(failureKey) === queryId) {
3253
- cache.set(adapter.id, auth.fingerprint, report);
3635
+ cache.set(adapter.id, queryFingerprint, report);
3254
3636
  failureBackoff.delete(failureKey);
3255
3637
  }
3256
3638
  return {
@@ -3880,7 +4262,7 @@ function usageExtension(pi, dependencies = {}) {
3880
4262
  }
3881
4263
  }
3882
4264
  });
3883
- pi.on("session_start", (_event, ctx) => {
4265
+ pi.on("session_start", async (_event, ctx) => {
3884
4266
  sessionGeneration += 1;
3885
4267
  statusGeneration += 1;
3886
4268
  clearStatusTimers();
@@ -3888,7 +4270,13 @@ function usageExtension(pi, dependencies = {}) {
3888
4270
  activeControllers.clear();
3889
4271
  statusController = void 0;
3890
4272
  sessionActive = true;
3891
- startStatusRefresh(ctx, ctx.model, false);
4273
+ const ownerGeneration = sessionGeneration;
4274
+ try {
4275
+ await fastRuntime.prepareSession(ctx);
4276
+ } catch (error) {
4277
+ if (isStaleExtensionContextError(error) || ownerGeneration !== sessionGeneration) return;
4278
+ throw error;
4279
+ }
3892
4280
  });
3893
4281
  pi.on("session_tree", (_event, ctx) => {
3894
4282
  startStatusRefresh(ctx, ctx.model, false);
@@ -3916,7 +4304,8 @@ function usageExtension(pi, dependencies = {}) {
3916
4304
  fastRuntime = registerCodexFastMode(
3917
4305
  pi,
3918
4306
  settingsRuntime,
3919
- (ctx) => startStatusRefresh(ctx, ctx.model, false)
4307
+ (ctx) => startStatusRefresh(ctx, ctx.model, false),
4308
+ { registerSessionStart: false }
3920
4309
  );
3921
4310
  }
3922
4311
  export {
@@ -3949,6 +4338,8 @@ export {
3949
4338
  normalizeCodexBackendPayload,
3950
4339
  normalizeCodexResetCreditsPayload,
3951
4340
  normalizeDeepSeekBalancePayload,
4341
+ normalizeFireworksAccountsPayload,
4342
+ normalizeFireworksBillingSummaryPayload,
3952
4343
  normalizeGitHubCopilotUsagePayload,
3953
4344
  normalizeKimiCodingUsagePayload,
3954
4345
  normalizeOpenCodeZenPayload,