@narumitw/pi-usage 0.59.0 → 0.60.1
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/README.md +30 -25
- package/dist/index.ts +889 -338
- package/dist/index.ts.map +4 -4
- package/package.json +1 -1
- package/src/core.ts +28 -2
- package/src/format.ts +18 -8
- package/src/index.ts +14 -1
- package/src/providers/fireworks.ts +112 -0
- package/src/providers/zai.ts +109 -5
- package/src/query.ts +171 -175
- package/src/settings.ts +155 -8
- package/src/types.ts +50 -2
- package/src/usage-settings-ui.ts +88 -183
- package/src/usage-targets.ts +143 -0
- package/src/usage.ts +413 -99
package/dist/index.ts
CHANGED
|
@@ -136,7 +136,20 @@ var UsageCache = class {
|
|
|
136
136
|
};
|
|
137
137
|
function fingerprintResolvedAuth(auth, salt) {
|
|
138
138
|
const headers = Object.entries(auth.headers ?? {}).map(([name, value]) => [name.toLowerCase(), value]).sort(([left], [right]) => left.localeCompare(right));
|
|
139
|
-
const
|
|
139
|
+
const env = Object.entries(auth.env ?? {}).sort(([left], [right]) => left.localeCompare(right));
|
|
140
|
+
const providerHeaders = Object.entries(auth.providerAuth?.headers ?? {}).map(([name, value]) => [name.toLowerCase(), value]).sort(([left], [right]) => left.localeCompare(right));
|
|
141
|
+
const canonical = JSON.stringify({
|
|
142
|
+
apiKey: auth.apiKey ?? "",
|
|
143
|
+
headers,
|
|
144
|
+
baseUrl: auth.baseUrl ?? "",
|
|
145
|
+
env,
|
|
146
|
+
source: auth.source ?? "",
|
|
147
|
+
providerAuth: {
|
|
148
|
+
apiKey: auth.providerAuth?.apiKey ?? "",
|
|
149
|
+
headers: providerHeaders,
|
|
150
|
+
baseUrl: auth.providerAuth?.baseUrl ?? ""
|
|
151
|
+
}
|
|
152
|
+
});
|
|
140
153
|
return createHmac("sha256", salt).update(canonical).digest("hex");
|
|
141
154
|
}
|
|
142
155
|
async function runWithConcurrency(items, limit, worker, signal) {
|
|
@@ -572,6 +585,9 @@ var INT64_MIN = -(2n ** 63n);
|
|
|
572
585
|
var INT64_MAX = 2n ** 63n - 1n;
|
|
573
586
|
var MAX_UNITS_CHARS = 20;
|
|
574
587
|
var MAX_NANOS_CHARS = 11;
|
|
588
|
+
var FIREWORKS_BILLING_SUMMARY_ORIGIN = "https://api.fireworks.ai";
|
|
589
|
+
var FIREWORKS_SPEND_WINDOW_DAYS = 30;
|
|
590
|
+
var FIREWORKS_MAX_ACCOUNT_PAGES = 5;
|
|
575
591
|
var SERIES_KEYS = ["serverless", "dedicated", "training", "other"];
|
|
576
592
|
var SERIES_LABELS = {
|
|
577
593
|
serverless: "Serverless",
|
|
@@ -605,6 +621,67 @@ function normalizeFireworksAccountsPayload(payload) {
|
|
|
605
621
|
}
|
|
606
622
|
return accounts;
|
|
607
623
|
}
|
|
624
|
+
function createFireworksAdapter(fetchProviderJson2) {
|
|
625
|
+
return {
|
|
626
|
+
id: "fireworks",
|
|
627
|
+
displayName: "Fireworks",
|
|
628
|
+
semantics: { kind: "api-key", label: "Fireworks API spend" },
|
|
629
|
+
targets: {
|
|
630
|
+
singularLabel: "account",
|
|
631
|
+
pluralLabel: "accounts",
|
|
632
|
+
async list(auth, signal, timeoutMs, guard) {
|
|
633
|
+
const startedAt = Date.now();
|
|
634
|
+
const accounts = [];
|
|
635
|
+
let pageToken;
|
|
636
|
+
for (let page = 0; page < FIREWORKS_MAX_ACCOUNT_PAGES; page += 1) {
|
|
637
|
+
await guard();
|
|
638
|
+
const payload = await fetchProviderJson2(
|
|
639
|
+
fireworksAccountsUrl(pageToken),
|
|
640
|
+
auth,
|
|
641
|
+
signal,
|
|
642
|
+
remainingTimeout(timeoutMs, startedAt, "fetching Fireworks accounts"),
|
|
643
|
+
"Fireworks accounts endpoint",
|
|
644
|
+
{ redirect: "error" }
|
|
645
|
+
);
|
|
646
|
+
await guard();
|
|
647
|
+
for (const accountId of normalizeFireworksAccountsPayload(payload)) {
|
|
648
|
+
if (accounts.includes(accountId)) {
|
|
649
|
+
throw new Error(`Fireworks accounts listing repeated ${accountId}.`);
|
|
650
|
+
}
|
|
651
|
+
accounts.push(accountId);
|
|
652
|
+
}
|
|
653
|
+
pageToken = fireworksNextPageToken(payload.nextPageToken);
|
|
654
|
+
if (!pageToken) break;
|
|
655
|
+
}
|
|
656
|
+
if (pageToken) {
|
|
657
|
+
throw new Error(
|
|
658
|
+
`Fireworks account listing exceeded ${FIREWORKS_MAX_ACCOUNT_PAGES} pages.`
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
return accounts.map((id) => ({ id, label: id }));
|
|
662
|
+
}
|
|
663
|
+
},
|
|
664
|
+
async query(auth, signal, timeoutMs, guard, targetId) {
|
|
665
|
+
if (!guard) throw new Error("Fireworks API spend requires request-boundary revalidation.");
|
|
666
|
+
if (!isFireworksAccountId(targetId)) {
|
|
667
|
+
throw new Error("Fireworks billing requires a safe selected account slug.");
|
|
668
|
+
}
|
|
669
|
+
const startedAt = Date.now();
|
|
670
|
+
await guard();
|
|
671
|
+
const billingWindowAt = Date.now();
|
|
672
|
+
const payload = await fetchProviderJson2(
|
|
673
|
+
fireworksBillingSummaryUrl(targetId, billingWindowAt),
|
|
674
|
+
auth,
|
|
675
|
+
signal,
|
|
676
|
+
remainingTimeout(timeoutMs, startedAt, "fetching Fireworks rated spend"),
|
|
677
|
+
"Fireworks billing summary endpoint",
|
|
678
|
+
{ redirect: "error" }
|
|
679
|
+
);
|
|
680
|
+
await guard();
|
|
681
|
+
return normalizeFireworksBillingSummaryPayload(payload, targetId, Date.now());
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
}
|
|
608
685
|
function normalizeFireworksBillingSummaryPayload(payload, accountId, capturedAt) {
|
|
609
686
|
if (!isFireworksAccountId(accountId)) {
|
|
610
687
|
throw new Error("Fireworks billing summary received an unsafe account identifier.");
|
|
@@ -711,6 +788,38 @@ function formatMoneyAmount(amount2) {
|
|
|
711
788
|
const nanos = (magnitude % NANOS_PER_UNIT).toString().padStart(9, "0").replace(/0+$/u, "");
|
|
712
789
|
return `${negative ? "-" : ""}${units.toString()}${nanos ? `.${nanos}` : ""}`;
|
|
713
790
|
}
|
|
791
|
+
function fireworksAccountsUrl(pageToken) {
|
|
792
|
+
const url = new URL("/v1/accounts", FIREWORKS_BILLING_SUMMARY_ORIGIN);
|
|
793
|
+
url.searchParams.set("pageSize", "200");
|
|
794
|
+
if (pageToken !== void 0) url.searchParams.set("pageToken", pageToken);
|
|
795
|
+
return url.toString();
|
|
796
|
+
}
|
|
797
|
+
function fireworksNextPageToken(value) {
|
|
798
|
+
if (value === void 0 || value === null) return void 0;
|
|
799
|
+
if (typeof value !== "string" || !value || value.length > 512) {
|
|
800
|
+
throw new Error("Fireworks accounts listing returned an invalid page token.");
|
|
801
|
+
}
|
|
802
|
+
return value;
|
|
803
|
+
}
|
|
804
|
+
function fireworksBillingSummaryUrl(accountId, startedAt) {
|
|
805
|
+
const dayMs = 24 * 60 * 60 * 1e3;
|
|
806
|
+
const dayFloor = (time) => `${new Date(time).toISOString().slice(0, 10)}T00:00:00Z`;
|
|
807
|
+
const url = new URL(
|
|
808
|
+
`/v1/accounts/${accountId}/billing/summary`,
|
|
809
|
+
FIREWORKS_BILLING_SUMMARY_ORIGIN
|
|
810
|
+
);
|
|
811
|
+
url.searchParams.set(
|
|
812
|
+
"startTime",
|
|
813
|
+
dayFloor(startedAt - (FIREWORKS_SPEND_WINDOW_DAYS - 1) * dayMs)
|
|
814
|
+
);
|
|
815
|
+
url.searchParams.set("endTime", dayFloor(startedAt + dayMs));
|
|
816
|
+
return url.toString();
|
|
817
|
+
}
|
|
818
|
+
function remainingTimeout(timeoutMs, startedAt, description) {
|
|
819
|
+
const remaining = timeoutMs - (Date.now() - startedAt);
|
|
820
|
+
if (remaining <= 0) throw new Error(`Timed out while ${description}.`);
|
|
821
|
+
return remaining;
|
|
822
|
+
}
|
|
714
823
|
function asObject4(value) {
|
|
715
824
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
716
825
|
return value;
|
|
@@ -1604,7 +1713,7 @@ function isRecord2(value) {
|
|
|
1604
1713
|
// src/providers/zai.ts
|
|
1605
1714
|
var FIVE_HOUR_WINDOW_MINUTES2 = 300;
|
|
1606
1715
|
var WEEKLY_WINDOW_MINUTES2 = 10080;
|
|
1607
|
-
function normalizeZaiQuotaPayload(providerId, providerName, payload, capturedAt) {
|
|
1716
|
+
function normalizeZaiQuotaPayload(providerId, providerName, payload, capturedAt, plan) {
|
|
1608
1717
|
const data = asObject11(payload.data);
|
|
1609
1718
|
if (!data) throw new Error("Z.AI quota response data was not an object.");
|
|
1610
1719
|
const limits = Array.isArray(data.limits) ? data.limits : [];
|
|
@@ -1620,14 +1729,20 @@ function normalizeZaiQuotaPayload(providerId, providerName, payload, capturedAt)
|
|
|
1620
1729
|
addCountBucket(buckets, limit, "mcp-monthly", "MCP monthly allowance");
|
|
1621
1730
|
addUsageDetailMetrics(metrics, limit.usageDetails);
|
|
1622
1731
|
} else if (isPlanUsage && unit === 3) {
|
|
1623
|
-
addPercentBucket(
|
|
1732
|
+
addPercentBucket(
|
|
1733
|
+
buckets,
|
|
1734
|
+
limit,
|
|
1735
|
+
"five-hour",
|
|
1736
|
+
sessionWindowLabel(limit),
|
|
1737
|
+
sessionWindowMinutes(limit)
|
|
1738
|
+
);
|
|
1624
1739
|
} else if (isPlanUsage && unit === 6) {
|
|
1625
1740
|
const used = asNonnegativeNumber4(limit.currentValue);
|
|
1626
1741
|
const quota = asNonnegativeNumber4(limit.usage);
|
|
1627
1742
|
if (used !== void 0 && quota !== void 0) {
|
|
1628
|
-
addCountBucket(buckets, limit, "weekly", "Weekly window",
|
|
1743
|
+
addCountBucket(buckets, limit, "weekly", "Weekly window", weeklyWindowMinutes(limit));
|
|
1629
1744
|
} else {
|
|
1630
|
-
addPercentBucket(buckets, limit, "weekly", "Weekly window",
|
|
1745
|
+
addPercentBucket(buckets, limit, "weekly", "Weekly window", weeklyWindowMinutes(limit));
|
|
1631
1746
|
}
|
|
1632
1747
|
}
|
|
1633
1748
|
}
|
|
@@ -1636,7 +1751,12 @@ function normalizeZaiQuotaPayload(providerId, providerName, payload, capturedAt)
|
|
|
1636
1751
|
}
|
|
1637
1752
|
const notes = [];
|
|
1638
1753
|
const level = asString5(data.level);
|
|
1639
|
-
|
|
1754
|
+
const planLabel = plan?.name ?? level;
|
|
1755
|
+
if (planLabel) {
|
|
1756
|
+
notes.push(
|
|
1757
|
+
plan?.renewsAt ? `Plan: ${planLabel} \xB7 renews ${plan.renewsAt}` : `Plan: ${planLabel}`
|
|
1758
|
+
);
|
|
1759
|
+
}
|
|
1640
1760
|
return {
|
|
1641
1761
|
providerId,
|
|
1642
1762
|
providerName,
|
|
@@ -1648,6 +1768,57 @@ function normalizeZaiQuotaPayload(providerId, providerName, payload, capturedAt)
|
|
|
1648
1768
|
...notes.length > 0 ? { notes } : {}
|
|
1649
1769
|
};
|
|
1650
1770
|
}
|
|
1771
|
+
function normalizeZaiSubscriptionPayload(payload) {
|
|
1772
|
+
if (payload.success === false) return void 0;
|
|
1773
|
+
if (typeof payload.code === "number" && payload.code !== 0 && payload.code !== 200) {
|
|
1774
|
+
return void 0;
|
|
1775
|
+
}
|
|
1776
|
+
if (!Array.isArray(payload.data)) return void 0;
|
|
1777
|
+
const candidates = [];
|
|
1778
|
+
for (const raw of payload.data) {
|
|
1779
|
+
const entry = asObject11(raw);
|
|
1780
|
+
if (!entry) continue;
|
|
1781
|
+
const name = asString5(entry.productName);
|
|
1782
|
+
if (!name) continue;
|
|
1783
|
+
const renewsAt = planRenewalDate(entry.nextRenewTime);
|
|
1784
|
+
const status = asString5(entry.status)?.toUpperCase();
|
|
1785
|
+
const inCurrentPeriod = asBoolean(entry.inCurrentPeriod);
|
|
1786
|
+
candidates.push({
|
|
1787
|
+
plan: { name, ...renewsAt !== void 0 ? { renewsAt } : {} },
|
|
1788
|
+
...status !== void 0 ? { status } : {},
|
|
1789
|
+
...inCurrentPeriod !== void 0 ? { inCurrentPeriod } : {}
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1792
|
+
const hasStateMetadata = candidates.some(
|
|
1793
|
+
(candidate) => candidate.status !== void 0 || candidate.inCurrentPeriod !== void 0
|
|
1794
|
+
);
|
|
1795
|
+
if (!hasStateMetadata) return candidates[0]?.plan;
|
|
1796
|
+
return candidates.find(
|
|
1797
|
+
(candidate) => candidate.inCurrentPeriod === true && candidate.status === "VALID"
|
|
1798
|
+
)?.plan ?? candidates.find(
|
|
1799
|
+
(candidate) => candidate.inCurrentPeriod === true && candidate.status === void 0
|
|
1800
|
+
)?.plan ?? candidates.find(
|
|
1801
|
+
(candidate) => candidate.status === "VALID" && candidate.inCurrentPeriod === void 0
|
|
1802
|
+
)?.plan;
|
|
1803
|
+
}
|
|
1804
|
+
function planRenewalDate(value) {
|
|
1805
|
+
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}/u.test(value)) return value.slice(0, 10);
|
|
1806
|
+
const millis = asNonnegativeNumber4(value);
|
|
1807
|
+
if (millis === void 0 || millis === 0) return void 0;
|
|
1808
|
+
return new Date(millis).toISOString().slice(0, 10);
|
|
1809
|
+
}
|
|
1810
|
+
function sessionWindowMinutes(limit) {
|
|
1811
|
+
const hours = asPositiveNumber(limit.number);
|
|
1812
|
+
return hours === void 0 ? FIVE_HOUR_WINDOW_MINUTES2 : Math.round(hours * 60);
|
|
1813
|
+
}
|
|
1814
|
+
function sessionWindowLabel(limit) {
|
|
1815
|
+
const minutes = sessionWindowMinutes(limit);
|
|
1816
|
+
return minutes === FIVE_HOUR_WINDOW_MINUTES2 ? "5h window" : `${Math.round(minutes / 60)}h window`;
|
|
1817
|
+
}
|
|
1818
|
+
function weeklyWindowMinutes(limit) {
|
|
1819
|
+
const weeks = asPositiveNumber(limit.number);
|
|
1820
|
+
return weeks === void 0 ? WEEKLY_WINDOW_MINUTES2 : Math.round(weeks * WEEKLY_WINDOW_MINUTES2);
|
|
1821
|
+
}
|
|
1651
1822
|
function addPercentBucket(buckets, limit, id, label, windowMinutes) {
|
|
1652
1823
|
const used = asNonnegativeNumber4(limit.percentage);
|
|
1653
1824
|
if (used === void 0) return;
|
|
@@ -1703,6 +1874,16 @@ function asNonnegativeNumber4(value) {
|
|
|
1703
1874
|
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
|
|
1704
1875
|
return value;
|
|
1705
1876
|
}
|
|
1877
|
+
function asPositiveNumber(value) {
|
|
1878
|
+
const number = asNonnegativeNumber4(value);
|
|
1879
|
+
return number !== void 0 && number > 0 ? number : void 0;
|
|
1880
|
+
}
|
|
1881
|
+
function asBoolean(value) {
|
|
1882
|
+
if (typeof value === "boolean") return value;
|
|
1883
|
+
if (value === 1) return true;
|
|
1884
|
+
if (value === 0) return false;
|
|
1885
|
+
return void 0;
|
|
1886
|
+
}
|
|
1706
1887
|
function asEpochSeconds2(value) {
|
|
1707
1888
|
const millis = asNonnegativeNumber4(value);
|
|
1708
1889
|
if (millis === void 0) return void 0;
|
|
@@ -1715,14 +1896,104 @@ function clampPercent3(value) {
|
|
|
1715
1896
|
return Math.min(100, Math.max(0, value));
|
|
1716
1897
|
}
|
|
1717
1898
|
|
|
1899
|
+
// src/usage-targets.ts
|
|
1900
|
+
var MAX_TARGETS = 1e3;
|
|
1901
|
+
var MAX_TARGET_ID_CHARS = 256;
|
|
1902
|
+
var MAX_TARGET_LABEL_CHARS = 120;
|
|
1903
|
+
var MAX_TARGET_DESCRIPTION_CHARS = 180;
|
|
1904
|
+
async function resolveUsageTarget(adapter, auth, rememberedTargetId, signal, timeoutMs, guard) {
|
|
1905
|
+
if (!adapter.targets) return { kind: "selected" };
|
|
1906
|
+
if (rememberedTargetId !== void 0 && !isBoundedTargetId(rememberedTargetId)) {
|
|
1907
|
+
throw new Error(`The remembered ${adapter.targets.singularLabel} identifier was invalid.`);
|
|
1908
|
+
}
|
|
1909
|
+
const choices = await listUsageTargets(adapter, auth, signal, timeoutMs, guard);
|
|
1910
|
+
if (rememberedTargetId) {
|
|
1911
|
+
return choices.some((choice) => choice.id === rememberedTargetId) ? { kind: "selected", targetId: rememberedTargetId } : { kind: "selection-required", choices };
|
|
1912
|
+
}
|
|
1913
|
+
if (choices.length === 1) return { kind: "selected", targetId: choices[0]?.id };
|
|
1914
|
+
return { kind: "selection-required", choices };
|
|
1915
|
+
}
|
|
1916
|
+
async function listUsageTargets(adapter, auth, signal, timeoutMs, guard) {
|
|
1917
|
+
if (!adapter.targets) return [];
|
|
1918
|
+
const startedAt = Date.now();
|
|
1919
|
+
await guard();
|
|
1920
|
+
const listed = await adapter.targets.list(
|
|
1921
|
+
auth,
|
|
1922
|
+
signal,
|
|
1923
|
+
remainingTargetTimeout(timeoutMs, startedAt),
|
|
1924
|
+
guard
|
|
1925
|
+
);
|
|
1926
|
+
await guard();
|
|
1927
|
+
remainingTargetTimeout(timeoutMs, startedAt);
|
|
1928
|
+
const choices = normalizeUsageTargets(listed);
|
|
1929
|
+
if (choices.length === 0) {
|
|
1930
|
+
throw new Error(`${adapter.targets.pluralLabel} discovery returned no choices.`);
|
|
1931
|
+
}
|
|
1932
|
+
return choices;
|
|
1933
|
+
}
|
|
1934
|
+
function normalizeUsageTargets(targets) {
|
|
1935
|
+
if (!Array.isArray(targets)) throw new Error("Target discovery did not return a choices array.");
|
|
1936
|
+
if (targets.length > MAX_TARGETS) {
|
|
1937
|
+
throw new Error(`Target discovery exceeded ${MAX_TARGETS} choices.`);
|
|
1938
|
+
}
|
|
1939
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1940
|
+
return targets.map((target) => {
|
|
1941
|
+
if (!target || typeof target !== "object" || Array.isArray(target)) {
|
|
1942
|
+
throw new Error("Target discovery returned an invalid choice.");
|
|
1943
|
+
}
|
|
1944
|
+
const { id, label, description } = target;
|
|
1945
|
+
if (!isBoundedTargetId(id)) throw new Error("Target discovery returned an invalid ID.");
|
|
1946
|
+
if (seen.has(id)) throw new Error(`Target discovery repeated ${id}.`);
|
|
1947
|
+
seen.add(id);
|
|
1948
|
+
if (typeof label !== "string") {
|
|
1949
|
+
throw new Error("Target discovery returned an invalid display label.");
|
|
1950
|
+
}
|
|
1951
|
+
if (description !== void 0 && typeof description !== "string") {
|
|
1952
|
+
throw new Error("Target discovery returned an invalid description.");
|
|
1953
|
+
}
|
|
1954
|
+
const safeLabel2 = sanitizeDisplayText(label, MAX_TARGET_LABEL_CHARS);
|
|
1955
|
+
if (!safeLabel2) throw new Error("Target discovery returned an empty display label.");
|
|
1956
|
+
const safeDescription = description ? sanitizeDisplayText(description, MAX_TARGET_DESCRIPTION_CHARS) : void 0;
|
|
1957
|
+
return { id, label: safeLabel2, ...safeDescription ? { description: safeDescription } : {} };
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
function createUsageTargetSelectOptions(targets) {
|
|
1961
|
+
const normalized = normalizeUsageTargets(targets);
|
|
1962
|
+
const ids = /* @__PURE__ */ new Map();
|
|
1963
|
+
const options = normalized.map((target) => {
|
|
1964
|
+
const base = target.description ? `${target.label} \u2014 ${target.description}` : target.label;
|
|
1965
|
+
let option = base;
|
|
1966
|
+
if (ids.has(option)) {
|
|
1967
|
+
const safeId = sanitizeDisplayText(target.id, 80) || "target";
|
|
1968
|
+
option = `${base} \xB7 ${safeId}`;
|
|
1969
|
+
let duplicate = 2;
|
|
1970
|
+
while (ids.has(option)) {
|
|
1971
|
+
option = `${base} \xB7 ${safeId} (${duplicate})`;
|
|
1972
|
+
duplicate += 1;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
ids.set(option, target.id);
|
|
1976
|
+
return option;
|
|
1977
|
+
});
|
|
1978
|
+
return { options, targetIdFor: (option) => ids.get(option) };
|
|
1979
|
+
}
|
|
1980
|
+
function remainingTargetTimeout(timeoutMs, startedAt) {
|
|
1981
|
+
const remaining = timeoutMs - (Date.now() - startedAt);
|
|
1982
|
+
if (remaining <= 0) throw new Error("Timed out while discovering provider targets.");
|
|
1983
|
+
return remaining;
|
|
1984
|
+
}
|
|
1985
|
+
function isBoundedTargetId(value) {
|
|
1986
|
+
return typeof value === "string" && value.length > 0 && value.length <= MAX_TARGET_ID_CHARS && ![...value].some((character) => {
|
|
1987
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
1988
|
+
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
1989
|
+
});
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1718
1992
|
// src/query.ts
|
|
1719
1993
|
var BASETEN_BILLING_USAGE_URL = "https://api.baseten.co/v1/billing/usage_summary";
|
|
1720
1994
|
var BASETEN_USAGE_WINDOW_DAYS = 30;
|
|
1721
1995
|
var CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
1722
1996
|
var DEEPSEEK_BALANCE_URL = "https://api.deepseek.com/user/balance";
|
|
1723
|
-
var FIREWORKS_BILLING_SUMMARY_ORIGIN = "https://api.fireworks.ai";
|
|
1724
|
-
var FIREWORKS_SPEND_WINDOW_DAYS = 30;
|
|
1725
|
-
var FIREWORKS_MAX_ACCOUNT_PAGES = 5;
|
|
1726
1997
|
var GITHUB_COPILOT_USAGE_URL = "https://api.github.com/copilot_internal/user";
|
|
1727
1998
|
var OPENROUTER_KEY_URL = "https://openrouter.ai/api/v1/key";
|
|
1728
1999
|
var VERCEL_AI_GATEWAY_CREDITS_URL = "https://ai-gateway.vercel.sh/v1/credits";
|
|
@@ -1761,7 +2032,7 @@ var SUPPORTED_ADAPTERS = [
|
|
|
1761
2032
|
basetenBillingUsageUrl(windowAt),
|
|
1762
2033
|
auth,
|
|
1763
2034
|
signal,
|
|
1764
|
-
|
|
2035
|
+
remainingTimeout2(timeoutMs, startedAt, "fetching Baseten billing usage"),
|
|
1765
2036
|
"Baseten billing usage endpoint",
|
|
1766
2037
|
{ redirect: "error" }
|
|
1767
2038
|
);
|
|
@@ -1854,7 +2125,7 @@ var SUPPORTED_ADAPTERS = [
|
|
|
1854
2125
|
VERCEL_AI_GATEWAY_CREDITS_URL,
|
|
1855
2126
|
auth,
|
|
1856
2127
|
signal,
|
|
1857
|
-
|
|
2128
|
+
remainingTimeout2(timeoutMs, startedAt, "fetching Vercel AI Gateway credits"),
|
|
1858
2129
|
"Vercel AI Gateway credits endpoint",
|
|
1859
2130
|
{ redirect: "error" }
|
|
1860
2131
|
);
|
|
@@ -1862,35 +2133,7 @@ var SUPPORTED_ADAPTERS = [
|
|
|
1862
2133
|
return normalizeVercelAIGatewayCreditsPayload(payload, Date.now());
|
|
1863
2134
|
}
|
|
1864
2135
|
},
|
|
1865
|
-
|
|
1866
|
-
id: "fireworks",
|
|
1867
|
-
displayName: "Fireworks",
|
|
1868
|
-
semantics: { kind: "api-key", label: "Fireworks API spend" },
|
|
1869
|
-
async query(auth, signal, timeoutMs, guard, settings) {
|
|
1870
|
-
if (!guard) throw new Error("Fireworks API spend requires request-boundary revalidation.");
|
|
1871
|
-
const startedAt = Date.now();
|
|
1872
|
-
await guard();
|
|
1873
|
-
const accountId = await resolveFireworksAccountId(
|
|
1874
|
-
auth,
|
|
1875
|
-
signal,
|
|
1876
|
-
remainingTimeout(timeoutMs, startedAt, "resolving the Fireworks account"),
|
|
1877
|
-
guard,
|
|
1878
|
-
settings?.fireworksAccountId
|
|
1879
|
-
);
|
|
1880
|
-
await guard();
|
|
1881
|
-
const billingWindowAt = Date.now();
|
|
1882
|
-
const payload = await fetchProviderJson(
|
|
1883
|
-
fireworksBillingSummaryUrl(accountId, billingWindowAt),
|
|
1884
|
-
auth,
|
|
1885
|
-
signal,
|
|
1886
|
-
remainingTimeout(timeoutMs, startedAt, "fetching Fireworks rated spend"),
|
|
1887
|
-
"Fireworks billing summary endpoint",
|
|
1888
|
-
{ redirect: "error" }
|
|
1889
|
-
);
|
|
1890
|
-
await guard();
|
|
1891
|
-
return normalizeFireworksBillingSummaryPayload(payload, accountId, Date.now());
|
|
1892
|
-
}
|
|
1893
|
-
},
|
|
2136
|
+
createFireworksAdapter(fetchProviderJson),
|
|
1894
2137
|
{
|
|
1895
2138
|
id: "opencode-go",
|
|
1896
2139
|
displayName: "OpenCode Go",
|
|
@@ -1958,35 +2201,16 @@ var SUPPORTED_ADAPTERS = [
|
|
|
1958
2201
|
id: "zai",
|
|
1959
2202
|
displayName: "Z.AI",
|
|
1960
2203
|
semantics: { kind: "consumer-subscription", label: "GLM Coding Plan usage" },
|
|
1961
|
-
async query(auth, signal, timeoutMs) {
|
|
1962
|
-
|
|
1963
|
-
zaiMonitorUrl(auth.model.baseUrl),
|
|
1964
|
-
zaiMonitorAuth(auth),
|
|
1965
|
-
signal,
|
|
1966
|
-
timeoutMs,
|
|
1967
|
-
"Z.AI quota endpoint"
|
|
1968
|
-
);
|
|
1969
|
-
return normalizeZaiQuotaPayload("zai", "Z.AI", payload, Date.now());
|
|
2204
|
+
async query(auth, signal, timeoutMs, guard) {
|
|
2205
|
+
return queryZaiUsage("zai", "Z.AI", auth, signal, timeoutMs, guard);
|
|
1970
2206
|
}
|
|
1971
2207
|
},
|
|
1972
2208
|
{
|
|
1973
2209
|
id: "zai-coding-cn",
|
|
1974
2210
|
displayName: "Z.AI Coding CN",
|
|
1975
2211
|
semantics: { kind: "consumer-subscription", label: "GLM Coding Plan usage" },
|
|
1976
|
-
async query(auth, signal, timeoutMs) {
|
|
1977
|
-
|
|
1978
|
-
zaiMonitorUrl(auth.model.baseUrl),
|
|
1979
|
-
zaiMonitorAuth(auth),
|
|
1980
|
-
signal,
|
|
1981
|
-
timeoutMs,
|
|
1982
|
-
"Z.AI Coding CN quota endpoint"
|
|
1983
|
-
);
|
|
1984
|
-
return normalizeZaiQuotaPayload(
|
|
1985
|
-
"zai-coding-cn",
|
|
1986
|
-
"Z.AI Coding CN",
|
|
1987
|
-
payload,
|
|
1988
|
-
Date.now()
|
|
1989
|
-
);
|
|
2212
|
+
async query(auth, signal, timeoutMs, guard) {
|
|
2213
|
+
return queryZaiUsage("zai-coding-cn", "Z.AI Coding CN", auth, signal, timeoutMs, guard);
|
|
1990
2214
|
}
|
|
1991
2215
|
}
|
|
1992
2216
|
];
|
|
@@ -2010,7 +2234,7 @@ var XAI_ADAPTER = {
|
|
|
2010
2234
|
XAI_USER_URL,
|
|
2011
2235
|
clientAuth,
|
|
2012
2236
|
signal,
|
|
2013
|
-
|
|
2237
|
+
remainingTimeout2(timeoutMs, startedAt),
|
|
2014
2238
|
"xAI consumer identity endpoint",
|
|
2015
2239
|
{ redirect: "error", userAgent: false }
|
|
2016
2240
|
);
|
|
@@ -2026,7 +2250,7 @@ var XAI_ADAPTER = {
|
|
|
2026
2250
|
XAI_BILLING_URL,
|
|
2027
2251
|
billingAuth,
|
|
2028
2252
|
signal,
|
|
2029
|
-
|
|
2253
|
+
remainingTimeout2(timeoutMs, startedAt),
|
|
2030
2254
|
"xAI consumer billing endpoint",
|
|
2031
2255
|
{ redirect: "error", userAgent: false }
|
|
2032
2256
|
);
|
|
@@ -2054,6 +2278,12 @@ async function resolveUsageAuth(ctx, adapter, salt = AUTH_FINGERPRINT_SALT, cred
|
|
|
2054
2278
|
);
|
|
2055
2279
|
if (!model) return void 0;
|
|
2056
2280
|
const registry = ctx.modelRegistry;
|
|
2281
|
+
const provider = registry.getProvider?.(adapter.id);
|
|
2282
|
+
if (provider?.baseUrl && !hasOfficialUrlOrigin(provider.baseUrl, adapter.id)) {
|
|
2283
|
+
throw new Error(
|
|
2284
|
+
`${adapter.displayName} usage cannot send an overridden provider credential to the official usage endpoint.`
|
|
2285
|
+
);
|
|
2286
|
+
}
|
|
2057
2287
|
let modelAuth;
|
|
2058
2288
|
const currentModel = ctx.model?.provider === adapter.id ? ctx.model : void 0;
|
|
2059
2289
|
const resolveCurrentModelAuth = async () => {
|
|
@@ -2076,25 +2306,64 @@ async function resolveUsageAuth(ctx, adapter, salt = AUTH_FINGERPRINT_SALT, cred
|
|
|
2076
2306
|
);
|
|
2077
2307
|
}
|
|
2078
2308
|
if (resolveSelectedAuthLast) modelAuth = await resolveCurrentModelAuth();
|
|
2309
|
+
if (modelAuth?.baseUrl && !hasOfficialUrlOrigin(modelAuth.baseUrl, adapter.id)) {
|
|
2310
|
+
throw new Error(
|
|
2311
|
+
`${adapter.displayName} usage cannot send model-resolved proxy credentials to the official usage endpoint.`
|
|
2312
|
+
);
|
|
2313
|
+
}
|
|
2079
2314
|
const auth = modelAuth ?? providerResult?.auth;
|
|
2080
2315
|
if (!auth) return void 0;
|
|
2316
|
+
const finalize = (resolved) => {
|
|
2317
|
+
const preservedAuth = { ...providerResult?.auth ?? auth };
|
|
2318
|
+
const env = providerResult?.env ?? modelAuth?.env;
|
|
2319
|
+
const source = providerResult?.source;
|
|
2320
|
+
const effectiveBaseUrl = modelAuth?.baseUrl ?? providerResult?.auth.baseUrl ?? provider?.baseUrl ?? model.baseUrl;
|
|
2321
|
+
const redactionInputs = [
|
|
2322
|
+
preservedAuth.apiKey,
|
|
2323
|
+
...Object.values(preservedAuth.headers ?? {}),
|
|
2324
|
+
...Object.values(env ?? {}),
|
|
2325
|
+
modelAuth?.apiKey,
|
|
2326
|
+
...Object.values(modelAuth?.headers ?? {})
|
|
2327
|
+
].filter((value) => typeof value === "string" && value.length > 0);
|
|
2328
|
+
return {
|
|
2329
|
+
...resolved,
|
|
2330
|
+
auth: preservedAuth,
|
|
2331
|
+
...env ? { env: { ...env } } : {},
|
|
2332
|
+
...source ? { source } : {},
|
|
2333
|
+
effectiveBaseUrl,
|
|
2334
|
+
secrets: [.../* @__PURE__ */ new Set([...resolved.secrets, ...redactionInputs])],
|
|
2335
|
+
fingerprint: fingerprintResolvedAuth(
|
|
2336
|
+
{
|
|
2337
|
+
apiKey: resolved.apiKey,
|
|
2338
|
+
headers: resolved.headers,
|
|
2339
|
+
baseUrl: effectiveBaseUrl,
|
|
2340
|
+
env,
|
|
2341
|
+
source,
|
|
2342
|
+
providerAuth: preservedAuth
|
|
2343
|
+
},
|
|
2344
|
+
salt
|
|
2345
|
+
)
|
|
2346
|
+
};
|
|
2347
|
+
};
|
|
2081
2348
|
if (adapter.id === "github-copilot") {
|
|
2082
2349
|
const offered = candidateReader ? candidateReader(ctx, adapter.id) : fallbackOAuthCredentialCandidates(adapter.id, credentialReader);
|
|
2083
2350
|
if (!offered.ok) {
|
|
2084
2351
|
throw new Error("GitHub Copilot OAuth credential discovery failed closed.");
|
|
2085
2352
|
}
|
|
2086
|
-
return
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2353
|
+
return finalize(
|
|
2354
|
+
resolveGitHubCopilotUsageAuth(
|
|
2355
|
+
auth,
|
|
2356
|
+
model,
|
|
2357
|
+
salt,
|
|
2358
|
+
offered.candidates,
|
|
2359
|
+
offered.offeredCount === 0
|
|
2360
|
+
)
|
|
2092
2361
|
);
|
|
2093
2362
|
}
|
|
2094
2363
|
if (adapter.id === "xai") {
|
|
2095
2364
|
const offered = candidateReader ? candidateReader(ctx, adapter.id) : fallbackOAuthCredentialCandidates(adapter.id, credentialReader);
|
|
2096
2365
|
if (!offered.ok) throw new Error("xAI OAuth credential discovery failed closed.");
|
|
2097
|
-
return resolveXaiUsageAuth(auth, model, salt, offered.candidates);
|
|
2366
|
+
return finalize(resolveXaiUsageAuth(auth, model, salt, offered.candidates));
|
|
2098
2367
|
}
|
|
2099
2368
|
if (adapter.id === "deepseek") {
|
|
2100
2369
|
const resolvedAuthorization = authorizationFrom(auth);
|
|
@@ -2102,10 +2371,10 @@ async function resolveUsageAuth(ctx, adapter, salt = AUTH_FINGERPRINT_SALT, cred
|
|
|
2102
2371
|
if (!access) throw new Error("DeepSeek API balance requires Bearer authentication.");
|
|
2103
2372
|
const authorization2 = `Bearer ${access}`;
|
|
2104
2373
|
const headers2 = { Authorization: authorization2 };
|
|
2105
|
-
return {
|
|
2374
|
+
return finalize({
|
|
2106
2375
|
apiKey: access,
|
|
2107
2376
|
headers: headers2,
|
|
2108
|
-
fingerprint:
|
|
2377
|
+
fingerprint: "",
|
|
2109
2378
|
secrets: [
|
|
2110
2379
|
access,
|
|
2111
2380
|
auth.apiKey,
|
|
@@ -2114,7 +2383,7 @@ async function resolveUsageAuth(ctx, adapter, salt = AUTH_FINGERPRINT_SALT, cred
|
|
|
2114
2383
|
authorization2
|
|
2115
2384
|
].filter((value) => Boolean(value)),
|
|
2116
2385
|
model
|
|
2117
|
-
};
|
|
2386
|
+
});
|
|
2118
2387
|
}
|
|
2119
2388
|
const authorization = authorizationFrom(auth);
|
|
2120
2389
|
if (!authorization) return void 0;
|
|
@@ -2122,17 +2391,41 @@ async function resolveUsageAuth(ctx, adapter, salt = AUTH_FINGERPRINT_SALT, cred
|
|
|
2122
2391
|
const secrets = [auth.apiKey, headerValue(auth.headers, "Authorization"), authorization].filter(
|
|
2123
2392
|
(value) => Boolean(value)
|
|
2124
2393
|
);
|
|
2125
|
-
return {
|
|
2394
|
+
return finalize({
|
|
2126
2395
|
apiKey: auth.apiKey,
|
|
2127
2396
|
headers,
|
|
2128
|
-
fingerprint:
|
|
2397
|
+
fingerprint: "",
|
|
2129
2398
|
secrets,
|
|
2130
2399
|
model
|
|
2131
|
-
};
|
|
2400
|
+
});
|
|
2132
2401
|
}
|
|
2133
|
-
async function queryProviderUsage(adapter, auth, signal, timeoutMs, guard,
|
|
2402
|
+
async function queryProviderUsage(adapter, auth, signal, timeoutMs, guard, targetOrSettings) {
|
|
2403
|
+
const startedAt = Date.now();
|
|
2404
|
+
let targetId = typeof targetOrSettings === "string" ? targetOrSettings : adapter.id === "fireworks" ? targetOrSettings?.fireworksAccountId : void 0;
|
|
2405
|
+
let resolvedLegacyFireworksTarget = false;
|
|
2134
2406
|
try {
|
|
2135
|
-
|
|
2407
|
+
if (adapter.id === "fireworks" && typeof targetOrSettings !== "string" && adapter.targets && guard) {
|
|
2408
|
+
const target = await resolveUsageTarget(
|
|
2409
|
+
adapter,
|
|
2410
|
+
auth,
|
|
2411
|
+
targetId,
|
|
2412
|
+
signal,
|
|
2413
|
+
remainingTimeout2(timeoutMs, startedAt, "resolving the Fireworks account"),
|
|
2414
|
+
guard
|
|
2415
|
+
);
|
|
2416
|
+
if (target.kind === "selection-required") {
|
|
2417
|
+
throw new Error("Fireworks account selection is required.");
|
|
2418
|
+
}
|
|
2419
|
+
targetId = target.targetId;
|
|
2420
|
+
resolvedLegacyFireworksTarget = true;
|
|
2421
|
+
}
|
|
2422
|
+
return await adapter.query(
|
|
2423
|
+
auth,
|
|
2424
|
+
signal,
|
|
2425
|
+
resolvedLegacyFireworksTarget ? remainingTimeout2(timeoutMs, startedAt, `querying ${adapter.displayName} usage`) : timeoutMs,
|
|
2426
|
+
guard,
|
|
2427
|
+
targetId
|
|
2428
|
+
);
|
|
2136
2429
|
} catch (error) {
|
|
2137
2430
|
if (isStaleExtensionContextError(error) || isAbortError(error)) throw error;
|
|
2138
2431
|
throw new Error(redactUsageError(errorMessage(error), auth.secrets));
|
|
@@ -2431,7 +2724,7 @@ function hasOfficialUrlOrigin(value, providerId) {
|
|
|
2431
2724
|
}
|
|
2432
2725
|
if (providerId === "openai-codex") return url.origin === "https://chatgpt.com";
|
|
2433
2726
|
if (providerId === "deepseek") return url.origin === "https://api.deepseek.com";
|
|
2434
|
-
if (providerId === "fireworks") return url.origin ===
|
|
2727
|
+
if (providerId === "fireworks") return url.origin === "https://api.fireworks.ai";
|
|
2435
2728
|
if (providerId === "openrouter") return url.origin === "https://openrouter.ai";
|
|
2436
2729
|
if (providerId === "vercel-ai-gateway") return url.origin === "https://ai-gateway.vercel.sh";
|
|
2437
2730
|
if (providerId === "opencode-go") return url.origin === "https://opencode.ai";
|
|
@@ -2487,7 +2780,7 @@ async function queryMiniMaxUsage(providerId, auth, signal, timeoutMs, guard) {
|
|
|
2487
2780
|
`${MINIMAX_API_ROOTS[providerId]}${path}`,
|
|
2488
2781
|
auth,
|
|
2489
2782
|
signal,
|
|
2490
|
-
|
|
2783
|
+
remainingTimeout2(timeoutMs, startedAt, "fetching MiniMax usage"),
|
|
2491
2784
|
"MiniMax usage endpoint",
|
|
2492
2785
|
{ redirect: "error" }
|
|
2493
2786
|
);
|
|
@@ -2502,98 +2795,25 @@ async function queryMoonshotBalance(providerId, auth, signal, timeoutMs, guard)
|
|
|
2502
2795
|
MOONSHOT_BALANCE_URLS[providerId],
|
|
2503
2796
|
auth,
|
|
2504
2797
|
signal,
|
|
2505
|
-
|
|
2798
|
+
remainingTimeout2(timeoutMs, startedAt, "fetching Moonshot AI balance"),
|
|
2506
2799
|
"Moonshot AI balance endpoint",
|
|
2507
2800
|
{ redirect: "error" }
|
|
2508
2801
|
);
|
|
2509
2802
|
await guard();
|
|
2510
2803
|
return normalizeMoonshotBalancePayload(providerId, payload, Date.now());
|
|
2511
2804
|
}
|
|
2512
|
-
function
|
|
2805
|
+
function remainingTimeout2(timeoutMs, startedAt, description = "fetching xAI consumer usage") {
|
|
2513
2806
|
const remaining = timeoutMs - (Date.now() - startedAt);
|
|
2514
2807
|
if (remaining <= 0) throw new Error(`Timed out while ${description}.`);
|
|
2515
2808
|
return remaining;
|
|
2516
2809
|
}
|
|
2517
|
-
|
|
2518
|
-
if (configuredAccountId !== void 0 && !isFireworksAccountId(configuredAccountId)) {
|
|
2519
|
-
throw new Error("The Fireworks account setting was not a safe account slug.");
|
|
2520
|
-
}
|
|
2521
|
-
const startedAt = Date.now();
|
|
2522
|
-
const accounts = [];
|
|
2523
|
-
let pageToken;
|
|
2524
|
-
for (let page = 0; page < FIREWORKS_MAX_ACCOUNT_PAGES; page += 1) {
|
|
2525
|
-
await guard();
|
|
2526
|
-
const payload = await fetchProviderJson(
|
|
2527
|
-
fireworksAccountsUrl(pageToken),
|
|
2528
|
-
auth,
|
|
2529
|
-
signal,
|
|
2530
|
-
remainingTimeout(timeoutMs, startedAt, "fetching Fireworks accounts"),
|
|
2531
|
-
"Fireworks accounts endpoint",
|
|
2532
|
-
{ redirect: "error" }
|
|
2533
|
-
);
|
|
2534
|
-
for (const accountId of normalizeFireworksAccountsPayload(
|
|
2535
|
-
payload
|
|
2536
|
-
)) {
|
|
2537
|
-
if (accounts.includes(accountId)) {
|
|
2538
|
-
throw new Error(`Fireworks accounts listing repeated ${accountId}.`);
|
|
2539
|
-
}
|
|
2540
|
-
accounts.push(accountId);
|
|
2541
|
-
if (configuredAccountId === accountId) return accountId;
|
|
2542
|
-
}
|
|
2543
|
-
pageToken = fireworksNextPageToken(payload.nextPageToken);
|
|
2544
|
-
if (!pageToken) break;
|
|
2545
|
-
}
|
|
2546
|
-
if (pageToken) {
|
|
2547
|
-
throw new Error(
|
|
2548
|
-
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.`
|
|
2549
|
-
);
|
|
2550
|
-
}
|
|
2551
|
-
if (accounts.length === 0) {
|
|
2552
|
-
throw new Error("Fireworks account discovery returned no accounts for this API key.");
|
|
2553
|
-
}
|
|
2554
|
-
if (configuredAccountId) {
|
|
2555
|
-
throw new Error(
|
|
2556
|
-
"The configured Fireworks account does not match an account visible to this API key."
|
|
2557
|
-
);
|
|
2558
|
-
}
|
|
2559
|
-
if (accounts.length === 1) return accounts[0];
|
|
2560
|
-
const preview = accounts.slice(0, 8).join(", ");
|
|
2561
|
-
const suffix = accounts.length > 8 ? ` \u2026and ${accounts.length - 8} more` : "";
|
|
2562
|
-
throw new Error(
|
|
2563
|
-
`The Fireworks key can see ${accounts.length} accounts (${preview}${suffix}); set fireworksAccountId in pi-usage.json to one of them.`
|
|
2564
|
-
);
|
|
2565
|
-
}
|
|
2566
|
-
function fireworksAccountsUrl(pageToken) {
|
|
2567
|
-
const url = new URL("/v1/accounts", FIREWORKS_BILLING_SUMMARY_ORIGIN);
|
|
2568
|
-
url.searchParams.set("pageSize", "200");
|
|
2569
|
-
if (pageToken !== void 0) url.searchParams.set("pageToken", pageToken);
|
|
2570
|
-
return url.toString();
|
|
2571
|
-
}
|
|
2572
|
-
function fireworksNextPageToken(value) {
|
|
2573
|
-
if (value === void 0 || value === null) return void 0;
|
|
2574
|
-
if (typeof value !== "string" || !value || value.length > 512) {
|
|
2575
|
-
throw new Error("Fireworks accounts listing returned an invalid page token.");
|
|
2576
|
-
}
|
|
2577
|
-
return value;
|
|
2578
|
-
}
|
|
2579
|
-
function fireworksBillingSummaryUrl(accountId, startedAt) {
|
|
2580
|
-
const dayMs = 24 * 60 * 60 * 1e3;
|
|
2581
|
-
const dayFloor = (time) => `${new Date(time).toISOString().slice(0, 10)}T00:00:00Z`;
|
|
2582
|
-
const url = new URL(
|
|
2583
|
-
`/v1/accounts/${accountId}/billing/summary`,
|
|
2584
|
-
FIREWORKS_BILLING_SUMMARY_ORIGIN
|
|
2585
|
-
);
|
|
2586
|
-
url.searchParams.set(
|
|
2587
|
-
"startTime",
|
|
2588
|
-
dayFloor(startedAt - (FIREWORKS_SPEND_WINDOW_DAYS - 1) * dayMs)
|
|
2589
|
-
);
|
|
2590
|
-
url.searchParams.set("endTime", dayFloor(startedAt + dayMs));
|
|
2591
|
-
return url.toString();
|
|
2592
|
-
}
|
|
2593
|
-
function zaiMonitorUrl(baseUrl) {
|
|
2810
|
+
function zaiOrigin(baseUrl) {
|
|
2594
2811
|
const base = baseUrl?.trim();
|
|
2595
2812
|
if (!base) throw new Error("Z.AI model base URL is unavailable.");
|
|
2596
|
-
return
|
|
2813
|
+
return new URL(base).origin;
|
|
2814
|
+
}
|
|
2815
|
+
function zaiMonitorUrl(baseUrl) {
|
|
2816
|
+
return `${zaiOrigin(baseUrl)}/api/monitor/usage/quota/limit`;
|
|
2597
2817
|
}
|
|
2598
2818
|
function zaiMonitorAuth(auth) {
|
|
2599
2819
|
const authorization = headerValue(auth.headers, "Authorization");
|
|
@@ -2601,6 +2821,38 @@ function zaiMonitorAuth(auth) {
|
|
|
2601
2821
|
if (token === void 0 || token === authorization) return auth;
|
|
2602
2822
|
return { ...auth, headers: { ...auth.headers, Authorization: token } };
|
|
2603
2823
|
}
|
|
2824
|
+
async function queryZaiUsage(providerId, providerName, auth, signal, timeoutMs, guard) {
|
|
2825
|
+
if (!guard) throw new Error("Z.AI usage requires request-boundary revalidation.");
|
|
2826
|
+
const startedAt = Date.now();
|
|
2827
|
+
await guard();
|
|
2828
|
+
const payload = await fetchProviderJson(
|
|
2829
|
+
zaiMonitorUrl(auth.model.baseUrl),
|
|
2830
|
+
zaiMonitorAuth(auth),
|
|
2831
|
+
signal,
|
|
2832
|
+
remainingTimeout2(timeoutMs, startedAt, `fetching ${providerName} quota`),
|
|
2833
|
+
`${providerName} quota endpoint`
|
|
2834
|
+
);
|
|
2835
|
+
await guard();
|
|
2836
|
+
const planTimeoutMs = timeoutMs - (Date.now() - startedAt);
|
|
2837
|
+
const plan = await fetchZaiPlan(providerName, auth, signal, planTimeoutMs);
|
|
2838
|
+
return normalizeZaiQuotaPayload(providerId, providerName, payload, Date.now(), plan);
|
|
2839
|
+
}
|
|
2840
|
+
async function fetchZaiPlan(providerName, auth, signal, timeoutMs) {
|
|
2841
|
+
if (timeoutMs <= 0 || signal.aborted) return void 0;
|
|
2842
|
+
try {
|
|
2843
|
+
const payload = await fetchProviderJson(
|
|
2844
|
+
`${zaiOrigin(auth.model.baseUrl)}/api/biz/subscription/list`,
|
|
2845
|
+
zaiMonitorAuth(auth),
|
|
2846
|
+
signal,
|
|
2847
|
+
timeoutMs,
|
|
2848
|
+
`${providerName} plan endpoint`
|
|
2849
|
+
);
|
|
2850
|
+
return normalizeZaiSubscriptionPayload(payload);
|
|
2851
|
+
} catch (error) {
|
|
2852
|
+
if (isAbortError(error)) throw error;
|
|
2853
|
+
return void 0;
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2604
2856
|
function isAbortError(error) {
|
|
2605
2857
|
return error instanceof Error && error.name === "AbortError";
|
|
2606
2858
|
}
|
|
@@ -2928,6 +3180,10 @@ function formatProviderStates(states) {
|
|
|
2928
3180
|
return states.map((state) => {
|
|
2929
3181
|
if (state.status === "ready") return formatUsageReport(state.report, state.displayState);
|
|
2930
3182
|
const label = state.displayState === "current" ? "Current" : "Configured";
|
|
3183
|
+
if (state.status === "selection-required") {
|
|
3184
|
+
return `${state.providerName} \xB7 ${label}
|
|
3185
|
+
Selection required: choose this provider's ${state.singularLabel} by viewing it individually.`;
|
|
3186
|
+
}
|
|
2931
3187
|
const status = state.status === "auth-unavailable" ? "Authentication unavailable" : state.status === "unsupported" ? "Unsupported" : "Query failed";
|
|
2932
3188
|
return `${state.providerName} \xB7 ${label}
|
|
2933
3189
|
${status}: ${state.message}`;
|
|
@@ -3077,6 +3333,10 @@ function formatOpenRouterReport(lines, report2) {
|
|
|
3077
3333
|
}
|
|
3078
3334
|
function formatOpenCodeZenReport(lines, report2) {
|
|
3079
3335
|
for (const bucket of report2.buckets) {
|
|
3336
|
+
if (bucket.unit === "percent" && bucket.used !== void 0) {
|
|
3337
|
+
lines.push(`${`${bucket.label}:`.padEnd(VALUE_COLUMN)}${formatPercentBucket(bucket)}`);
|
|
3338
|
+
continue;
|
|
3339
|
+
}
|
|
3080
3340
|
const reset = bucket.resetsAt ? ` (resets ${formatReset(bucket.resetsAt)})` : "";
|
|
3081
3341
|
const used = bucket.used ?? "unavailable";
|
|
3082
3342
|
lines.push(`${`${bucket.label}:`.padEnd(VALUE_COLUMN)}${used}% used${reset}`);
|
|
@@ -3251,8 +3511,7 @@ function formatXaiReport(lines, report2) {
|
|
|
3251
3511
|
if (included) {
|
|
3252
3512
|
let value = "unavailable";
|
|
3253
3513
|
if (included.unit === "percent" && included.used !== void 0) {
|
|
3254
|
-
value =
|
|
3255
|
-
if (included.remaining !== void 0) value += ` \xB7 ${included.remaining}% left`;
|
|
3514
|
+
value = formatPercentBar(included);
|
|
3256
3515
|
} else if (included.used !== void 0) {
|
|
3257
3516
|
value = `${formatUsd(included.used)} used`;
|
|
3258
3517
|
if (included.limit !== void 0) value += ` of ${formatUsd(included.limit)}`;
|
|
@@ -3277,12 +3536,13 @@ function formatXaiReport(lines, report2) {
|
|
|
3277
3536
|
}
|
|
3278
3537
|
function formatZaiReport(lines, report2) {
|
|
3279
3538
|
for (const bucket of report2.buckets) {
|
|
3539
|
+
if (bucket.unit === "percent" && bucket.used !== void 0) {
|
|
3540
|
+
lines.push(`${`${bucket.label}:`.padEnd(VALUE_COLUMN)}${formatPercentBucket(bucket)}`);
|
|
3541
|
+
continue;
|
|
3542
|
+
}
|
|
3280
3543
|
const reset = bucket.resetsAt ? ` (resets ${formatReset(bucket.resetsAt)})` : "";
|
|
3281
3544
|
let value = "unavailable";
|
|
3282
|
-
if (bucket.
|
|
3283
|
-
value = `${bucket.used}% used`;
|
|
3284
|
-
if (bucket.remaining !== void 0) value += ` \xB7 ${bucket.remaining}% left`;
|
|
3285
|
-
} else if (bucket.used !== void 0 && bucket.limit !== void 0) {
|
|
3545
|
+
if (bucket.used !== void 0 && bucket.limit !== void 0) {
|
|
3286
3546
|
value = `${bucket.used} of ${bucket.limit} used`;
|
|
3287
3547
|
if (bucket.remaining !== void 0) value += ` \xB7 ${bucket.remaining} left`;
|
|
3288
3548
|
} else if (bucket.used !== void 0) {
|
|
@@ -3394,10 +3654,12 @@ function compactLimitLabel(label) {
|
|
|
3394
3654
|
return (suffix || normalized).toLowerCase().replace(/\s+/g, " ");
|
|
3395
3655
|
}
|
|
3396
3656
|
function formatPercentBucket(bucket) {
|
|
3657
|
+
return `${formatPercentBar(bucket)}${bucket.resetsAt ? ` (resets ${formatReset(bucket.resetsAt)})` : ""}`;
|
|
3658
|
+
}
|
|
3659
|
+
function formatPercentBar(bucket) {
|
|
3397
3660
|
const remaining = clampPercent4(bucket.remaining ?? 0);
|
|
3398
3661
|
const filled = Math.round(remaining / 100 * BAR_SEGMENTS);
|
|
3399
|
-
|
|
3400
|
-
return `[${"\u2588".repeat(filled)}${"\u2591".repeat(BAR_SEGMENTS - filled)}] ${remaining.toFixed(0)}% left${reset}`;
|
|
3662
|
+
return `[${"\u2588".repeat(filled)}${"\u2591".repeat(BAR_SEGMENTS - filled)}] ${remaining.toFixed(0)}% left`;
|
|
3401
3663
|
}
|
|
3402
3664
|
function formatWindowLabel(minutes, fallback, compact) {
|
|
3403
3665
|
if (!minutes || !Number.isFinite(minutes) || minutes <= 0) {
|
|
@@ -3458,7 +3720,8 @@ var USAGE_SETTINGS_FILE = "pi-usage.json";
|
|
|
3458
3720
|
var MAX_USAGE_SETTINGS_BYTES = 64 * 1024;
|
|
3459
3721
|
var DEFAULT_USAGE_SETTINGS = Object.freeze({
|
|
3460
3722
|
codexFastMode: false,
|
|
3461
|
-
codexStatusResetCountdown: true
|
|
3723
|
+
codexStatusResetCountdown: true,
|
|
3724
|
+
selectedTargets: Object.freeze({})
|
|
3462
3725
|
});
|
|
3463
3726
|
function usageSettingsPath() {
|
|
3464
3727
|
return join(getAgentDir(), USAGE_SETTINGS_FILE);
|
|
@@ -3474,10 +3737,16 @@ function normalizeUsageSettings(value) {
|
|
|
3474
3737
|
if (Object.hasOwn(value, "fireworksAccountId") && !isFireworksAccountId(value.fireworksAccountId)) {
|
|
3475
3738
|
return void 0;
|
|
3476
3739
|
}
|
|
3740
|
+
const selectedTargets = normalizeSelectedTargets(value.selectedTargets);
|
|
3741
|
+
if (Object.hasOwn(value, "selectedTargets") && !selectedTargets) return void 0;
|
|
3742
|
+
const effectiveTargets = { ...selectedTargets ?? {} };
|
|
3743
|
+
if (!effectiveTargets.fireworks && isFireworksAccountId(value.fireworksAccountId)) {
|
|
3744
|
+
effectiveTargets.fireworks = value.fireworksAccountId;
|
|
3745
|
+
}
|
|
3477
3746
|
return {
|
|
3478
3747
|
codexFastMode: typeof value.codexFastMode === "boolean" ? value.codexFastMode : DEFAULT_USAGE_SETTINGS.codexFastMode,
|
|
3479
3748
|
codexStatusResetCountdown: typeof value.codexStatusResetCountdown === "boolean" ? value.codexStatusResetCountdown : DEFAULT_USAGE_SETTINGS.codexStatusResetCountdown,
|
|
3480
|
-
|
|
3749
|
+
selectedTargets: effectiveTargets
|
|
3481
3750
|
};
|
|
3482
3751
|
}
|
|
3483
3752
|
async function loadUsageSettings(path = usageSettingsPath(), signal) {
|
|
@@ -3553,19 +3822,84 @@ function createUsageSettingsRuntime(options = {}) {
|
|
|
3553
3822
|
state = saved;
|
|
3554
3823
|
return structuredClone(state);
|
|
3555
3824
|
}),
|
|
3825
|
+
updateSelectedTarget: (providerId, targetId, signal, checkPublishedSelection) => enqueue(async () => {
|
|
3826
|
+
const transaction = await saveUsageTargetSelection(
|
|
3827
|
+
path,
|
|
3828
|
+
providerId,
|
|
3829
|
+
targetId,
|
|
3830
|
+
operations,
|
|
3831
|
+
signal
|
|
3832
|
+
);
|
|
3833
|
+
try {
|
|
3834
|
+
await checkPublishedSelection?.();
|
|
3835
|
+
throwIfAborted(signal);
|
|
3836
|
+
} catch (error) {
|
|
3837
|
+
try {
|
|
3838
|
+
await restoreUsageSettingsState(
|
|
3839
|
+
path,
|
|
3840
|
+
transaction.saved,
|
|
3841
|
+
transaction.previous,
|
|
3842
|
+
operations
|
|
3843
|
+
);
|
|
3844
|
+
state = transaction.previous;
|
|
3845
|
+
} catch (rollbackError) {
|
|
3846
|
+
state = await loadUsageSettings(path);
|
|
3847
|
+
throw new AggregateError(
|
|
3848
|
+
[error, rollbackError],
|
|
3849
|
+
"Target selection changed after publication and pi-usage.json rollback failed"
|
|
3850
|
+
);
|
|
3851
|
+
}
|
|
3852
|
+
throw error;
|
|
3853
|
+
}
|
|
3854
|
+
state = transaction.saved;
|
|
3855
|
+
return structuredClone(state);
|
|
3856
|
+
}),
|
|
3556
3857
|
flush: () => queue
|
|
3557
3858
|
};
|
|
3558
3859
|
}
|
|
3559
3860
|
async function saveUsageSettingsPatch(path, patch, operations, signal) {
|
|
3861
|
+
return saveUsageSettingsDocument(
|
|
3862
|
+
path,
|
|
3863
|
+
(document) => {
|
|
3864
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
3865
|
+
if (value === void 0) delete document[key];
|
|
3866
|
+
else document[key] = value;
|
|
3867
|
+
}
|
|
3868
|
+
},
|
|
3869
|
+
operations,
|
|
3870
|
+
signal
|
|
3871
|
+
);
|
|
3872
|
+
}
|
|
3873
|
+
async function saveUsageTargetSelection(path, providerId, targetId, operations, signal) {
|
|
3874
|
+
if (!isProviderId(providerId) || !isBoundedTargetId(targetId)) {
|
|
3875
|
+
throw new Error("Refusing to save an invalid usage target selection");
|
|
3876
|
+
}
|
|
3877
|
+
const previous = await loadUsageSettings(path, signal);
|
|
3878
|
+
const saved = await saveUsageSettingsDocument(
|
|
3879
|
+
path,
|
|
3880
|
+
(document) => {
|
|
3881
|
+
document.selectedTargets = {
|
|
3882
|
+
...normalizeSelectedTargets(document.selectedTargets) ?? {},
|
|
3883
|
+
[providerId]: targetId
|
|
3884
|
+
};
|
|
3885
|
+
if (providerId === "fireworks") delete document.fireworksAccountId;
|
|
3886
|
+
},
|
|
3887
|
+
operations,
|
|
3888
|
+
signal,
|
|
3889
|
+
previous
|
|
3890
|
+
);
|
|
3891
|
+
return { saved, previous };
|
|
3892
|
+
}
|
|
3893
|
+
async function saveUsageSettingsDocument(path, mutate, operations, signal, expected) {
|
|
3560
3894
|
const latest = await loadUsageSettings(path, signal);
|
|
3561
3895
|
if (latest.kind === "invalid") {
|
|
3562
3896
|
throw new Error("Cannot overwrite an invalid pi-usage.json; repair it and reload first");
|
|
3563
3897
|
}
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
if (value === void 0) delete document[key];
|
|
3567
|
-
else document[key] = value;
|
|
3898
|
+
if (expected && !sameUsageSettingsDocument(latest, expected)) {
|
|
3899
|
+
throw new Error("pi-usage.json changed while saving; retry the action");
|
|
3568
3900
|
}
|
|
3901
|
+
const document = { ...latest.document };
|
|
3902
|
+
mutate(document);
|
|
3569
3903
|
const settings = normalizeUsageSettings(document);
|
|
3570
3904
|
if (!settings) throw new Error("Refusing to save invalid pi-usage settings");
|
|
3571
3905
|
const directory = dirname(path);
|
|
@@ -3592,6 +3926,32 @@ async function saveUsageSettingsPatch(path, patch, operations, signal) {
|
|
|
3592
3926
|
}
|
|
3593
3927
|
return { kind: "loaded", path, settings, document };
|
|
3594
3928
|
}
|
|
3929
|
+
async function restoreUsageSettingsState(path, published, previous, operations) {
|
|
3930
|
+
if (previous.kind === "missing") {
|
|
3931
|
+
const current = await loadUsageSettings(path);
|
|
3932
|
+
if (!sameUsageSettingsDocument(current, published)) {
|
|
3933
|
+
throw new Error("pi-usage.json changed before target selection rollback");
|
|
3934
|
+
}
|
|
3935
|
+
await rm(path);
|
|
3936
|
+
return;
|
|
3937
|
+
}
|
|
3938
|
+
if (previous.kind !== "loaded" || !previous.document) {
|
|
3939
|
+
throw new Error("Cannot restore invalid prior pi-usage.json settings");
|
|
3940
|
+
}
|
|
3941
|
+
await saveUsageSettingsDocument(
|
|
3942
|
+
path,
|
|
3943
|
+
(document) => {
|
|
3944
|
+
for (const key of Object.keys(document)) delete document[key];
|
|
3945
|
+
Object.assign(document, previous.document);
|
|
3946
|
+
},
|
|
3947
|
+
operations,
|
|
3948
|
+
void 0,
|
|
3949
|
+
published
|
|
3950
|
+
);
|
|
3951
|
+
}
|
|
3952
|
+
function sameUsageSettingsDocument(left, right) {
|
|
3953
|
+
return left.kind === right.kind && JSON.stringify(left.document) === JSON.stringify(right.document);
|
|
3954
|
+
}
|
|
3595
3955
|
async function chmodPrivate(path) {
|
|
3596
3956
|
await chmod(path, 384);
|
|
3597
3957
|
}
|
|
@@ -3604,6 +3964,19 @@ function isRecord3(value) {
|
|
|
3604
3964
|
function isNodeError(error) {
|
|
3605
3965
|
return error instanceof Error && "code" in error;
|
|
3606
3966
|
}
|
|
3967
|
+
function normalizeSelectedTargets(value) {
|
|
3968
|
+
if (value === void 0) return {};
|
|
3969
|
+
if (!isRecord3(value)) return void 0;
|
|
3970
|
+
const targets = {};
|
|
3971
|
+
for (const [providerId, targetId] of Object.entries(value)) {
|
|
3972
|
+
if (!isProviderId(providerId) || !isBoundedTargetId(targetId)) return void 0;
|
|
3973
|
+
targets[providerId] = targetId;
|
|
3974
|
+
}
|
|
3975
|
+
return targets;
|
|
3976
|
+
}
|
|
3977
|
+
function isProviderId(value) {
|
|
3978
|
+
return /^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$/u.test(value);
|
|
3979
|
+
}
|
|
3607
3980
|
|
|
3608
3981
|
// src/usage.ts
|
|
3609
3982
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
@@ -3814,8 +4187,6 @@ import {
|
|
|
3814
4187
|
SettingsList,
|
|
3815
4188
|
Text
|
|
3816
4189
|
} from "@earendil-works/pi-tui";
|
|
3817
|
-
var AUTO = "Auto";
|
|
3818
|
-
var EDIT = "Edit\u2026";
|
|
3819
4190
|
var OFF = "Off";
|
|
3820
4191
|
var ON = "On";
|
|
3821
4192
|
async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent, onApplied) {
|
|
@@ -3823,31 +4194,13 @@ async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent,
|
|
|
3823
4194
|
if (ctx.hasUI) ctx.ui.notify(`Edit settings manually: ${settingsRuntime.get().path}`, "info");
|
|
3824
4195
|
return false;
|
|
3825
4196
|
}
|
|
3826
|
-
|
|
3827
|
-
while (!parentSignal.aborted && isCurrent()) {
|
|
3828
|
-
const result = await showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, onApplied);
|
|
3829
|
-
if (!result) return changed;
|
|
3830
|
-
changed ||= result.changed;
|
|
3831
|
-
if (!result.editFireworksAccount) return changed;
|
|
3832
|
-
changed ||= await editFireworksAccount(
|
|
3833
|
-
ctx,
|
|
3834
|
-
settingsRuntime,
|
|
3835
|
-
parentSignal,
|
|
3836
|
-
isCurrent,
|
|
3837
|
-
onApplied
|
|
3838
|
-
);
|
|
3839
|
-
}
|
|
3840
|
-
return changed;
|
|
3841
|
-
}
|
|
3842
|
-
async function showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, onApplied) {
|
|
3843
|
-
return ctx.ui.custom((tui, theme, _keybindings, done) => {
|
|
4197
|
+
return await ctx.ui.custom((tui, theme, _keybindings, done) => {
|
|
3844
4198
|
const localController = new AbortController();
|
|
3845
4199
|
const signal = AbortSignal.any([parentSignal, localController.signal]);
|
|
3846
4200
|
let changed = false;
|
|
3847
4201
|
let closing = false;
|
|
3848
4202
|
let saveQueue = Promise.resolve();
|
|
3849
4203
|
const state = settingsRuntime.get();
|
|
3850
|
-
const fireworksValue = state.settings.fireworksAccountId ?? AUTO;
|
|
3851
4204
|
const items = [
|
|
3852
4205
|
{
|
|
3853
4206
|
id: "codexFastMode",
|
|
@@ -3862,13 +4215,6 @@ async function showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, o
|
|
|
3862
4215
|
description: "Show time remaining until each Codex usage limit resets.",
|
|
3863
4216
|
currentValue: state.settings.codexStatusResetCountdown ? ON : OFF,
|
|
3864
4217
|
values: [OFF, ON]
|
|
3865
|
-
},
|
|
3866
|
-
{
|
|
3867
|
-
id: "fireworksAccountId",
|
|
3868
|
-
label: "Fireworks account",
|
|
3869
|
-
description: "Select Edit to enter a visible account slug, or submit blank to clear it.",
|
|
3870
|
-
currentValue: fireworksValue,
|
|
3871
|
-
values: state.settings.fireworksAccountId ? [state.settings.fireworksAccountId, EDIT] : [AUTO, EDIT]
|
|
3872
4218
|
}
|
|
3873
4219
|
];
|
|
3874
4220
|
const container = new Container();
|
|
@@ -3878,13 +4224,13 @@ async function showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, o
|
|
|
3878
4224
|
if (closing) return;
|
|
3879
4225
|
closing = true;
|
|
3880
4226
|
localController.abort();
|
|
3881
|
-
done(
|
|
4227
|
+
done(changed);
|
|
3882
4228
|
};
|
|
3883
4229
|
const queueUpdate = (id, requested, display) => {
|
|
3884
4230
|
saveQueue = saveQueue.then(async () => {
|
|
3885
4231
|
const previous = settingsRuntime.get().settings[id];
|
|
3886
4232
|
if (settingsRuntime.get().kind === "invalid") {
|
|
3887
|
-
settingsList.updateValue(id,
|
|
4233
|
+
settingsList.updateValue(id, previous ? ON : OFF);
|
|
3888
4234
|
if (!signal.aborted && isCurrent()) {
|
|
3889
4235
|
ctx.ui.notify("Repair pi-usage.json and reload before changing settings.", "error");
|
|
3890
4236
|
tui.requestRender();
|
|
@@ -3895,7 +4241,7 @@ async function showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, o
|
|
|
3895
4241
|
await settingsRuntime.update({ [id]: requested }, signal);
|
|
3896
4242
|
} catch (error) {
|
|
3897
4243
|
if (signal.aborted || !isCurrent()) return;
|
|
3898
|
-
settingsList.updateValue(id,
|
|
4244
|
+
settingsList.updateValue(id, previous ? ON : OFF);
|
|
3899
4245
|
ctx.ui.notify(`Could not save pi-usage.json: ${errorMessage(error)}`, "error");
|
|
3900
4246
|
tui.requestRender();
|
|
3901
4247
|
return;
|
|
@@ -3915,18 +4261,7 @@ async function showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, o
|
|
|
3915
4261
|
getSettingsListTheme(),
|
|
3916
4262
|
(id, value) => {
|
|
3917
4263
|
if (closing || signal.aborted || !isCurrent()) return;
|
|
3918
|
-
|
|
3919
|
-
if (value === EDIT) {
|
|
3920
|
-
saveQueue = saveQueue.then(() => {
|
|
3921
|
-
if (closing || signal.aborted || !isCurrent()) return;
|
|
3922
|
-
closing = true;
|
|
3923
|
-
done({ changed, editFireworksAccount: true });
|
|
3924
|
-
});
|
|
3925
|
-
}
|
|
3926
|
-
return;
|
|
3927
|
-
}
|
|
3928
|
-
const settingId = id;
|
|
3929
|
-
queueUpdate(settingId, value !== OFF, value);
|
|
4264
|
+
queueUpdate(id, value !== OFF, value);
|
|
3930
4265
|
},
|
|
3931
4266
|
cancel
|
|
3932
4267
|
);
|
|
@@ -3946,43 +4281,7 @@ async function showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, o
|
|
|
3946
4281
|
parentSignal.removeEventListener("abort", cancel);
|
|
3947
4282
|
}
|
|
3948
4283
|
};
|
|
3949
|
-
});
|
|
3950
|
-
}
|
|
3951
|
-
async function editFireworksAccount(ctx, settingsRuntime, signal, isCurrent, onApplied) {
|
|
3952
|
-
while (!signal.aborted && isCurrent()) {
|
|
3953
|
-
const state = settingsRuntime.get();
|
|
3954
|
-
if (state.kind === "invalid") {
|
|
3955
|
-
ctx.ui.notify("Repair pi-usage.json and reload before changing settings.", "error");
|
|
3956
|
-
return false;
|
|
3957
|
-
}
|
|
3958
|
-
const entered = await ctx.ui.input(
|
|
3959
|
-
"Fireworks account slug \xB7 submit blank for Auto",
|
|
3960
|
-
state.settings.fireworksAccountId ?? "Example: acme",
|
|
3961
|
-
{ signal }
|
|
3962
|
-
);
|
|
3963
|
-
if (signal.aborted || !isCurrent() || entered === void 0) return false;
|
|
3964
|
-
const normalized = entered.trim();
|
|
3965
|
-
const requested = normalized || void 0;
|
|
3966
|
-
if (requested !== void 0 && !isFireworksAccountId(requested)) {
|
|
3967
|
-
ctx.ui.notify("Enter a URL-safe Fireworks account slug.", "warning");
|
|
3968
|
-
continue;
|
|
3969
|
-
}
|
|
3970
|
-
if (requested === state.settings.fireworksAccountId) return false;
|
|
3971
|
-
try {
|
|
3972
|
-
await settingsRuntime.update({ fireworksAccountId: requested }, signal);
|
|
3973
|
-
} catch (error) {
|
|
3974
|
-
if (signal.aborted || !isCurrent()) return false;
|
|
3975
|
-
ctx.ui.notify(`Could not save pi-usage.json: ${errorMessage(error)}`, "error");
|
|
3976
|
-
return false;
|
|
3977
|
-
}
|
|
3978
|
-
onApplied("fireworksAccountId");
|
|
3979
|
-
return true;
|
|
3980
|
-
}
|
|
3981
|
-
return false;
|
|
3982
|
-
}
|
|
3983
|
-
function displaySetting(id, value) {
|
|
3984
|
-
if (id === "fireworksAccountId") return typeof value === "string" ? value : AUTO;
|
|
3985
|
-
return value ? ON : OFF;
|
|
4284
|
+
}) ?? false;
|
|
3986
4285
|
}
|
|
3987
4286
|
|
|
3988
4287
|
// src/usage.ts
|
|
@@ -3999,6 +4298,9 @@ var VIEW_ALL = "View all configured providers\u2026";
|
|
|
3999
4298
|
var CLOSE = "Close";
|
|
4000
4299
|
var SETTINGS = "Settings";
|
|
4001
4300
|
var REDEEM_CODEX_RESET = "Redeem usage limit reset\u2026";
|
|
4301
|
+
var UsageTargetSelectionChangedError = class extends Error {
|
|
4302
|
+
name = "UsageTargetSelectionChangedError";
|
|
4303
|
+
};
|
|
4002
4304
|
function usageExtension(pi, dependencies = {}) {
|
|
4003
4305
|
const credentialReader = dependencies.credentialReader;
|
|
4004
4306
|
const credentialCandidates = createOAuthCredentialCandidateReader(pi, credentialReader);
|
|
@@ -4070,7 +4372,7 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4070
4372
|
if (outcome.state.status !== "ready") {
|
|
4071
4373
|
if (safeSetStatus(
|
|
4072
4374
|
ctx,
|
|
4073
|
-
outcome.state.status === "auth-unavailable" ? "auth unavailable" : "usage error"
|
|
4375
|
+
outcome.state.status === "auth-unavailable" ? "auth unavailable" : outcome.state.status === "selection-required" ? "selection required" : "usage error"
|
|
4074
4376
|
)) {
|
|
4075
4377
|
if (shouldSchedule && sessionActive) scheduleStatusRefresh(ctx, model);
|
|
4076
4378
|
}
|
|
@@ -4123,8 +4425,8 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4123
4425
|
const expectedSessionGeneration = sessionGeneration;
|
|
4124
4426
|
const expectedSessionId = ctx.sessionManager.getSessionId();
|
|
4125
4427
|
const expectedModelIdentity = modelIdentity(ctx.model);
|
|
4126
|
-
const
|
|
4127
|
-
const
|
|
4428
|
+
const expectedTargetId = adapter.targets ? settingsRuntime.get().settings.selectedTargets[adapter.id] : void 0;
|
|
4429
|
+
const providerName = providerDisplayName(ctx, adapter.id);
|
|
4128
4430
|
let auth;
|
|
4129
4431
|
try {
|
|
4130
4432
|
auth = await awaitWithDeadline(
|
|
@@ -4141,25 +4443,26 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4141
4443
|
return {
|
|
4142
4444
|
state: {
|
|
4143
4445
|
providerId: adapter.id,
|
|
4144
|
-
providerName
|
|
4446
|
+
providerName,
|
|
4145
4447
|
displayState,
|
|
4146
4448
|
status: isTimeoutError(error) ? "query-failed" : "auth-unavailable",
|
|
4147
4449
|
message: errorMessage(error)
|
|
4148
4450
|
}
|
|
4149
4451
|
};
|
|
4150
4452
|
}
|
|
4151
|
-
const requiresRequestBoundaryGuard = [
|
|
4453
|
+
const requiresRequestBoundaryGuard = adapter.targets !== void 0 || [
|
|
4152
4454
|
"baseten",
|
|
4153
4455
|
"deepseek",
|
|
4154
|
-
"fireworks",
|
|
4155
4456
|
"minimax",
|
|
4156
4457
|
"minimax-cn",
|
|
4157
4458
|
"moonshotai",
|
|
4158
4459
|
"moonshotai-cn",
|
|
4159
4460
|
"vercel-ai-gateway",
|
|
4160
|
-
"xai"
|
|
4461
|
+
"xai",
|
|
4462
|
+
"zai",
|
|
4463
|
+
"zai-coding-cn"
|
|
4161
4464
|
].includes(adapter.id);
|
|
4162
|
-
const requestContextChanged = () => expectedSessionGeneration !== sessionGeneration || ctx.sessionManager.getSessionId() !== expectedSessionId || modelIdentity(ctx.model) !== expectedModelIdentity || adapter.
|
|
4465
|
+
const requestContextChanged = () => expectedSessionGeneration !== sessionGeneration || ctx.sessionManager.getSessionId() !== expectedSessionId || modelIdentity(ctx.model) !== expectedModelIdentity || adapter.targets !== void 0 && settingsRuntime.get().settings.selectedTargets[adapter.id] !== expectedTargetId;
|
|
4163
4466
|
if (requiresRequestBoundaryGuard && requestContextChanged()) throw abortError();
|
|
4164
4467
|
if (!auth) {
|
|
4165
4468
|
if (displayState === "current") {
|
|
@@ -4168,98 +4471,145 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4168
4471
|
return {
|
|
4169
4472
|
state: {
|
|
4170
4473
|
providerId: adapter.id,
|
|
4171
|
-
providerName
|
|
4474
|
+
providerName,
|
|
4172
4475
|
displayState,
|
|
4173
4476
|
status: "auth-unavailable",
|
|
4174
|
-
message: `No runtime credential is configured for ${
|
|
4477
|
+
message: `No runtime credential is configured for ${providerName}.`
|
|
4175
4478
|
},
|
|
4176
4479
|
authState: "unavailable"
|
|
4177
4480
|
};
|
|
4178
4481
|
}
|
|
4179
|
-
const queryFingerprint = adapter.id === "fireworks" ? `${auth.fingerprint}:account:${expectedFireworksAccountId ?? "auto"}` : auth.fingerprint;
|
|
4180
|
-
if (displayState === "current") {
|
|
4181
|
-
transitionCurrentIdentity(`${adapter.id}:${queryFingerprint}`, adapter.id);
|
|
4182
|
-
}
|
|
4183
|
-
const cached = !force ? cache.get(adapter.id, queryFingerprint) : void 0;
|
|
4184
|
-
if (cached) {
|
|
4185
|
-
return {
|
|
4186
|
-
state: {
|
|
4187
|
-
providerId: adapter.id,
|
|
4188
|
-
providerName: adapter.displayName,
|
|
4189
|
-
displayState,
|
|
4190
|
-
status: "ready",
|
|
4191
|
-
report: cached
|
|
4192
|
-
},
|
|
4193
|
-
fingerprint: auth.fingerprint
|
|
4194
|
-
};
|
|
4195
|
-
}
|
|
4196
|
-
const failureKey = `${adapter.id}:${queryFingerprint}`;
|
|
4197
|
-
const previousFailure = failureBackoff.get(failureKey);
|
|
4198
|
-
if (!force && previousFailure && previousFailure.until > Date.now()) {
|
|
4199
|
-
return {
|
|
4200
|
-
state: {
|
|
4201
|
-
providerId: adapter.id,
|
|
4202
|
-
providerName: adapter.displayName,
|
|
4203
|
-
displayState,
|
|
4204
|
-
status: "query-failed",
|
|
4205
|
-
message: previousFailure.message
|
|
4206
|
-
},
|
|
4207
|
-
fingerprint: auth.fingerprint
|
|
4208
|
-
};
|
|
4209
|
-
}
|
|
4210
|
-
failureBackoff.delete(failureKey);
|
|
4211
|
-
querySequence += 1;
|
|
4212
|
-
const queryId = querySequence;
|
|
4213
|
-
setBoundedMap(latestQueries, failureKey, queryId, MAX_ACCOUNT_STATES);
|
|
4214
4482
|
let retryableAuthChanged = false;
|
|
4483
|
+
const guard = async () => {
|
|
4484
|
+
if (signal.aborted || requestContextChanged()) throw abortError();
|
|
4485
|
+
if (!requiresRequestBoundaryGuard) return;
|
|
4486
|
+
const revalidated = await awaitWithDeadline(
|
|
4487
|
+
resolveUsageAuth(ctx, adapter, void 0, credentialReader, credentialCandidates),
|
|
4488
|
+
signal,
|
|
4489
|
+
Math.max(1, deadlineAt - Date.now()),
|
|
4490
|
+
`revalidating ${providerName} runtime auth`
|
|
4491
|
+
);
|
|
4492
|
+
if (signal.aborted || requestContextChanged()) throw abortError();
|
|
4493
|
+
if (revalidated?.fingerprint !== auth.fingerprint) {
|
|
4494
|
+
if (["deepseek", "minimax", "minimax-cn"].includes(adapter.id)) {
|
|
4495
|
+
retryableAuthChanged = true;
|
|
4496
|
+
throw new Error(`${providerName} runtime credential changed during the usage query.`);
|
|
4497
|
+
}
|
|
4498
|
+
throw abortError();
|
|
4499
|
+
}
|
|
4500
|
+
};
|
|
4501
|
+
let queryFingerprint = adapter.targets ? `${auth.fingerprint}:target:${expectedTargetId ?? "unresolved"}` : auth.fingerprint;
|
|
4502
|
+
let failureKey = `${adapter.id}:${queryFingerprint}`;
|
|
4503
|
+
let queryId;
|
|
4215
4504
|
try {
|
|
4216
|
-
const
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4505
|
+
const previousDiscoveryFailure = failureBackoff.get(failureKey);
|
|
4506
|
+
if (!force && previousDiscoveryFailure && previousDiscoveryFailure.until > Date.now()) {
|
|
4507
|
+
return {
|
|
4508
|
+
state: {
|
|
4509
|
+
providerId: adapter.id,
|
|
4510
|
+
providerName,
|
|
4511
|
+
displayState,
|
|
4512
|
+
status: "query-failed",
|
|
4513
|
+
message: previousDiscoveryFailure.message
|
|
4514
|
+
},
|
|
4515
|
+
fingerprint: auth.fingerprint,
|
|
4516
|
+
rememberedTargetId: expectedTargetId
|
|
4517
|
+
};
|
|
4518
|
+
}
|
|
4519
|
+
const target = await resolveUsageTarget(
|
|
4520
|
+
adapter,
|
|
4521
|
+
auth,
|
|
4522
|
+
expectedTargetId,
|
|
4523
|
+
signal,
|
|
4524
|
+
Math.max(1, deadlineAt - Date.now()),
|
|
4525
|
+
guard
|
|
4526
|
+
);
|
|
4527
|
+
if (target.kind === "selection-required") {
|
|
4528
|
+
if (displayState === "current") {
|
|
4529
|
+
transitionCurrentIdentity(`${adapter.id}:${queryFingerprint}`, adapter.id);
|
|
4234
4530
|
}
|
|
4235
|
-
|
|
4531
|
+
return {
|
|
4532
|
+
state: {
|
|
4533
|
+
providerId: adapter.id,
|
|
4534
|
+
providerName,
|
|
4535
|
+
displayState,
|
|
4536
|
+
status: "selection-required",
|
|
4537
|
+
singularLabel: adapter.targets?.singularLabel ?? "target",
|
|
4538
|
+
pluralLabel: adapter.targets?.pluralLabel ?? "targets",
|
|
4539
|
+
choices: target.choices
|
|
4540
|
+
},
|
|
4541
|
+
fingerprint: auth.fingerprint,
|
|
4542
|
+
rememberedTargetId: expectedTargetId
|
|
4543
|
+
};
|
|
4544
|
+
}
|
|
4545
|
+
queryFingerprint = adapter.targets ? `${auth.fingerprint}:target:${target.targetId ?? "none"}` : auth.fingerprint;
|
|
4546
|
+
failureKey = `${adapter.id}:${queryFingerprint}`;
|
|
4547
|
+
if (displayState === "current") {
|
|
4548
|
+
transitionCurrentIdentity(`${adapter.id}:${queryFingerprint}`, adapter.id);
|
|
4549
|
+
}
|
|
4550
|
+
const cached = !force ? cache.get(adapter.id, queryFingerprint) : void 0;
|
|
4551
|
+
if (cached) {
|
|
4552
|
+
return {
|
|
4553
|
+
state: {
|
|
4554
|
+
providerId: adapter.id,
|
|
4555
|
+
providerName,
|
|
4556
|
+
displayState,
|
|
4557
|
+
status: "ready",
|
|
4558
|
+
report: cached
|
|
4559
|
+
},
|
|
4560
|
+
fingerprint: auth.fingerprint,
|
|
4561
|
+
rememberedTargetId: expectedTargetId
|
|
4562
|
+
};
|
|
4563
|
+
}
|
|
4564
|
+
const previousFailure = failureBackoff.get(failureKey);
|
|
4565
|
+
if (!force && previousFailure && previousFailure.until > Date.now()) {
|
|
4566
|
+
return {
|
|
4567
|
+
state: {
|
|
4568
|
+
providerId: adapter.id,
|
|
4569
|
+
providerName,
|
|
4570
|
+
displayState,
|
|
4571
|
+
status: "query-failed",
|
|
4572
|
+
message: previousFailure.message
|
|
4573
|
+
},
|
|
4574
|
+
fingerprint: auth.fingerprint,
|
|
4575
|
+
rememberedTargetId: expectedTargetId
|
|
4576
|
+
};
|
|
4577
|
+
}
|
|
4578
|
+
failureBackoff.delete(failureKey);
|
|
4579
|
+
querySequence += 1;
|
|
4580
|
+
queryId = querySequence;
|
|
4581
|
+
setBoundedMap(latestQueries, failureKey, queryId, MAX_ACCOUNT_STATES);
|
|
4236
4582
|
const report2 = await queryProviderUsage(
|
|
4237
4583
|
adapter,
|
|
4238
4584
|
auth,
|
|
4239
4585
|
signal,
|
|
4240
|
-
|
|
4241
|
-
guard,
|
|
4242
|
-
|
|
4586
|
+
Math.max(1, deadlineAt - Date.now()),
|
|
4587
|
+
requiresRequestBoundaryGuard ? guard : void 0,
|
|
4588
|
+
target.targetId
|
|
4243
4589
|
);
|
|
4244
|
-
if (
|
|
4590
|
+
if (requiresRequestBoundaryGuard) await guard();
|
|
4591
|
+
const effectiveReport = { ...report2, providerName };
|
|
4245
4592
|
if (latestQueries.get(failureKey) === queryId) {
|
|
4246
|
-
cache.set(adapter.id, queryFingerprint,
|
|
4593
|
+
cache.set(adapter.id, queryFingerprint, effectiveReport);
|
|
4247
4594
|
failureBackoff.delete(failureKey);
|
|
4248
4595
|
}
|
|
4249
4596
|
return {
|
|
4250
4597
|
state: {
|
|
4251
4598
|
providerId: adapter.id,
|
|
4252
|
-
providerName
|
|
4599
|
+
providerName,
|
|
4253
4600
|
displayState,
|
|
4254
4601
|
status: "ready",
|
|
4255
|
-
report:
|
|
4602
|
+
report: effectiveReport
|
|
4256
4603
|
},
|
|
4257
|
-
fingerprint: auth.fingerprint
|
|
4604
|
+
fingerprint: auth.fingerprint,
|
|
4605
|
+
rememberedTargetId: expectedTargetId
|
|
4258
4606
|
};
|
|
4259
4607
|
} catch (error) {
|
|
4260
4608
|
if (isStaleExtensionContextError(error) || isAbortError3(error)) throw error;
|
|
4261
4609
|
if (retryableAuthChanged && authRetry === 0 && !signal.aborted && !requestContextChanged() && Date.now() < deadlineAt) {
|
|
4262
|
-
if (latestQueries.get(failureKey) === queryId)
|
|
4610
|
+
if (queryId !== void 0 && latestQueries.get(failureKey) === queryId) {
|
|
4611
|
+
latestQueries.delete(failureKey);
|
|
4612
|
+
}
|
|
4263
4613
|
return queryAdapterState(
|
|
4264
4614
|
ctx,
|
|
4265
4615
|
adapter,
|
|
@@ -4275,7 +4625,7 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4275
4625
|
for (const [key, failure] of failureBackoff) {
|
|
4276
4626
|
if (failure.until <= now) failureBackoff.delete(key);
|
|
4277
4627
|
}
|
|
4278
|
-
if (latestQueries.get(failureKey) === queryId) {
|
|
4628
|
+
if (queryId === void 0 || latestQueries.get(failureKey) === queryId) {
|
|
4279
4629
|
setBoundedMap(
|
|
4280
4630
|
failureBackoff,
|
|
4281
4631
|
failureKey,
|
|
@@ -4286,15 +4636,52 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4286
4636
|
return {
|
|
4287
4637
|
state: {
|
|
4288
4638
|
providerId: adapter.id,
|
|
4289
|
-
providerName
|
|
4639
|
+
providerName,
|
|
4290
4640
|
displayState,
|
|
4291
4641
|
status: "query-failed",
|
|
4292
4642
|
message
|
|
4293
4643
|
},
|
|
4294
|
-
fingerprint: auth.fingerprint
|
|
4644
|
+
fingerprint: auth.fingerprint,
|
|
4645
|
+
rememberedTargetId: expectedTargetId
|
|
4295
4646
|
};
|
|
4296
4647
|
}
|
|
4297
4648
|
};
|
|
4649
|
+
const loadTargetChoices = async (ctx, adapter, signal) => {
|
|
4650
|
+
if (!adapter.targets) throw new Error("Provider does not support usage targets.");
|
|
4651
|
+
const expectedSessionGeneration = sessionGeneration;
|
|
4652
|
+
const expectedSessionId = ctx.sessionManager.getSessionId();
|
|
4653
|
+
const expectedModel = modelIdentity(ctx.model);
|
|
4654
|
+
const expectedTargetId = settingsRuntime.get().settings.selectedTargets[adapter.id];
|
|
4655
|
+
const deadlineAt = Date.now() + DEFAULT_TIMEOUT_MS;
|
|
4656
|
+
const changed = () => expectedSessionGeneration !== sessionGeneration || ctx.sessionManager.getSessionId() !== expectedSessionId || modelIdentity(ctx.model) !== expectedModel || settingsRuntime.get().settings.selectedTargets[adapter.id] !== expectedTargetId;
|
|
4657
|
+
const auth = await awaitWithDeadline(
|
|
4658
|
+
resolveUsageAuth(ctx, adapter, void 0, credentialReader, credentialCandidates),
|
|
4659
|
+
signal,
|
|
4660
|
+
Math.max(1, deadlineAt - Date.now()),
|
|
4661
|
+
`resolving ${providerDisplayName(ctx, adapter.id)} runtime auth`
|
|
4662
|
+
);
|
|
4663
|
+
if (!auth || signal.aborted || changed()) throw abortError();
|
|
4664
|
+
const guard = async () => {
|
|
4665
|
+
if (signal.aborted || changed()) throw abortError();
|
|
4666
|
+
const revalidated = await awaitWithDeadline(
|
|
4667
|
+
resolveUsageAuth(ctx, adapter, void 0, credentialReader, credentialCandidates),
|
|
4668
|
+
signal,
|
|
4669
|
+
Math.max(1, deadlineAt - Date.now()),
|
|
4670
|
+
`revalidating ${providerDisplayName(ctx, adapter.id)} runtime auth`
|
|
4671
|
+
);
|
|
4672
|
+
if (signal.aborted || changed() || revalidated?.fingerprint !== auth.fingerprint) {
|
|
4673
|
+
throw abortError();
|
|
4674
|
+
}
|
|
4675
|
+
};
|
|
4676
|
+
const choices = await listUsageTargets(
|
|
4677
|
+
adapter,
|
|
4678
|
+
auth,
|
|
4679
|
+
signal,
|
|
4680
|
+
Math.max(1, deadlineAt - Date.now()),
|
|
4681
|
+
guard
|
|
4682
|
+
);
|
|
4683
|
+
return { choices, fingerprint: auth.fingerprint };
|
|
4684
|
+
};
|
|
4298
4685
|
const queryCurrentState = async (ctx, model, force, signal) => {
|
|
4299
4686
|
const adapter = adapterForProvider(model?.provider);
|
|
4300
4687
|
if (!adapter) {
|
|
@@ -4378,6 +4765,8 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4378
4765
|
return false;
|
|
4379
4766
|
}
|
|
4380
4767
|
const adapter = adapterForProvider(model?.provider);
|
|
4768
|
+
const selectionStillCurrent = !adapter?.targets || settingsRuntime.get().settings.selectedTargets[adapter.id] === outcome.rememberedTargetId;
|
|
4769
|
+
if (!selectionStillCurrent) return false;
|
|
4381
4770
|
if (outcome.authState === "unavailable") {
|
|
4382
4771
|
if (!adapter) return false;
|
|
4383
4772
|
try {
|
|
@@ -4387,7 +4776,7 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4387
4776
|
DEFAULT_TIMEOUT_MS,
|
|
4388
4777
|
`revalidating ${adapter.displayName} runtime auth`
|
|
4389
4778
|
);
|
|
4390
|
-
return generation === statusGeneration && modelIdentity(ctx.model) === modelIdentity(model) && auth === void 0;
|
|
4779
|
+
return generation === statusGeneration && modelIdentity(ctx.model) === modelIdentity(model) && (!adapter.targets || settingsRuntime.get().settings.selectedTargets[adapter.id] === outcome.rememberedTargetId) && auth === void 0;
|
|
4391
4780
|
} catch (error) {
|
|
4392
4781
|
if (isAbortError3(error) || isStaleExtensionContextError(error)) throw error;
|
|
4393
4782
|
return false;
|
|
@@ -4402,7 +4791,7 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4402
4791
|
DEFAULT_TIMEOUT_MS,
|
|
4403
4792
|
`revalidating ${adapter.displayName} runtime auth`
|
|
4404
4793
|
);
|
|
4405
|
-
return generation === statusGeneration && modelIdentity(ctx.model) === modelIdentity(model) && auth?.fingerprint === outcome.fingerprint;
|
|
4794
|
+
return generation === statusGeneration && modelIdentity(ctx.model) === modelIdentity(model) && (!adapter.targets || settingsRuntime.get().settings.selectedTargets[adapter.id] === outcome.rememberedTargetId) && auth?.fingerprint === outcome.fingerprint;
|
|
4406
4795
|
} catch (error) {
|
|
4407
4796
|
if (isAbortError3(error) || isStaleExtensionContextError(error)) throw error;
|
|
4408
4797
|
return false;
|
|
@@ -4458,6 +4847,88 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4458
4847
|
let redemptionId;
|
|
4459
4848
|
let resetOutcome;
|
|
4460
4849
|
let resetFailure;
|
|
4850
|
+
const actionableTargetState = () => {
|
|
4851
|
+
if (visibleStates.length !== 1) return void 0;
|
|
4852
|
+
const state = visibleStates[0];
|
|
4853
|
+
return state && (state.status === "ready" || state.status === "selection-required") && adapterForProvider(state.providerId)?.targets ? state : void 0;
|
|
4854
|
+
};
|
|
4855
|
+
const promptForTarget = async (state, stateFingerprint) => {
|
|
4856
|
+
const adapter = adapterForProvider(state.providerId);
|
|
4857
|
+
if (!adapter?.targets) return false;
|
|
4858
|
+
const expectedRememberedTargetId = settingsRuntime.get().settings.selectedTargets[adapter.id];
|
|
4859
|
+
const snapshot = state.status === "selection-required" && stateFingerprint ? { choices: state.choices, fingerprint: stateFingerprint } : await runMenuOperation(
|
|
4860
|
+
ctx,
|
|
4861
|
+
`Loading ${adapter.targets.pluralLabel}\u2026`,
|
|
4862
|
+
controller.signal,
|
|
4863
|
+
(signal) => loadTargetChoices(ctx, adapter, signal)
|
|
4864
|
+
);
|
|
4865
|
+
if (!snapshot || controller.signal.aborted || statusGeneration !== menuGeneration) {
|
|
4866
|
+
return false;
|
|
4867
|
+
}
|
|
4868
|
+
const selectOptions = createUsageTargetSelectOptions(snapshot.choices);
|
|
4869
|
+
const selected = await ctx.ui.select(
|
|
4870
|
+
`Select ${adapter.targets.singularLabel} for ${providerDisplayName(ctx, adapter.id)}`,
|
|
4871
|
+
[...selectOptions.options],
|
|
4872
|
+
{ signal: controller.signal }
|
|
4873
|
+
);
|
|
4874
|
+
if (selected === void 0 || controller.signal.aborted || statusGeneration !== menuGeneration || settingsRuntime.get().settings.selectedTargets[adapter.id] !== expectedRememberedTargetId) {
|
|
4875
|
+
return false;
|
|
4876
|
+
}
|
|
4877
|
+
const targetId = selectOptions.targetIdFor(selected);
|
|
4878
|
+
if (!targetId) return false;
|
|
4879
|
+
const revalidated = await runMenuOperation(
|
|
4880
|
+
ctx,
|
|
4881
|
+
`Revalidating ${adapter.targets.singularLabel}\u2026`,
|
|
4882
|
+
controller.signal,
|
|
4883
|
+
(signal) => loadTargetChoices(ctx, adapter, signal)
|
|
4884
|
+
);
|
|
4885
|
+
if (!revalidated || controller.signal.aborted || statusGeneration !== menuGeneration || settingsRuntime.get().settings.selectedTargets[adapter.id] !== expectedRememberedTargetId) {
|
|
4886
|
+
return false;
|
|
4887
|
+
}
|
|
4888
|
+
if (revalidated.fingerprint !== snapshot.fingerprint || !revalidated.choices.some((choice) => choice.id === targetId)) {
|
|
4889
|
+
ctx.ui.notify(
|
|
4890
|
+
`${providerDisplayName(ctx, adapter.id)} ${adapter.targets.pluralLabel} changed; choose again.`,
|
|
4891
|
+
"warning"
|
|
4892
|
+
);
|
|
4893
|
+
return false;
|
|
4894
|
+
}
|
|
4895
|
+
let saved;
|
|
4896
|
+
try {
|
|
4897
|
+
saved = await runMenuOperation(
|
|
4898
|
+
ctx,
|
|
4899
|
+
`Saving ${adapter.targets.singularLabel}\u2026`,
|
|
4900
|
+
controller.signal,
|
|
4901
|
+
(signal) => settingsRuntime.updateSelectedTarget(adapter.id, targetId, signal, async () => {
|
|
4902
|
+
let published;
|
|
4903
|
+
try {
|
|
4904
|
+
published = await loadTargetChoices(ctx, adapter, signal);
|
|
4905
|
+
} catch (error) {
|
|
4906
|
+
if (signal.aborted || controller.signal.aborted || statusGeneration !== menuGeneration || isStaleExtensionContextError(error)) {
|
|
4907
|
+
throw error;
|
|
4908
|
+
}
|
|
4909
|
+
throw new UsageTargetSelectionChangedError();
|
|
4910
|
+
}
|
|
4911
|
+
if (published.fingerprint !== snapshot.fingerprint || !published.choices.some((choice) => choice.id === targetId)) {
|
|
4912
|
+
throw new UsageTargetSelectionChangedError();
|
|
4913
|
+
}
|
|
4914
|
+
})
|
|
4915
|
+
);
|
|
4916
|
+
} catch (error) {
|
|
4917
|
+
if (error instanceof UsageTargetSelectionChangedError) {
|
|
4918
|
+
ctx.ui.notify(
|
|
4919
|
+
`${providerDisplayName(ctx, adapter.id)} ${adapter.targets.pluralLabel} changed; choose again.`,
|
|
4920
|
+
"warning"
|
|
4921
|
+
);
|
|
4922
|
+
return false;
|
|
4923
|
+
}
|
|
4924
|
+
throw error;
|
|
4925
|
+
}
|
|
4926
|
+
if (!saved || controller.signal.aborted || statusGeneration !== menuGeneration) {
|
|
4927
|
+
return false;
|
|
4928
|
+
}
|
|
4929
|
+
invalidateProviderState(adapter.id);
|
|
4930
|
+
return true;
|
|
4931
|
+
};
|
|
4461
4932
|
const { defineMenu, runMenu } = await import("@narumitw/pi-tui-kit");
|
|
4462
4933
|
if (controller.signal.aborted || statusGeneration !== menuGeneration) return;
|
|
4463
4934
|
const menu = defineMenu({
|
|
@@ -4465,6 +4936,8 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4465
4936
|
screens: {
|
|
4466
4937
|
main: () => {
|
|
4467
4938
|
const fastAvailability = fastRuntime.availability(ctx.model);
|
|
4939
|
+
const targetState = actionableTargetState();
|
|
4940
|
+
const targetAdapter = adapterForProvider(targetState?.providerId);
|
|
4468
4941
|
const fastLines = fastAvailability.kind === "available" ? [`Fast mode: ${fastAvailability.enabled ? "On" : "Off"}`, FAST_USAGE_WARNING] : fastAvailability.kind === "unavailable" ? [`Fast mode: Unavailable \xB7 ${fastAvailability.reason}`] : [];
|
|
4469
4942
|
return {
|
|
4470
4943
|
kind: "actions",
|
|
@@ -4473,6 +4946,13 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4473
4946
|
items: [
|
|
4474
4947
|
{ id: "refresh", label: REFRESH_CURRENT, action: "refresh" },
|
|
4475
4948
|
{ id: "settings", label: SETTINGS, action: "settings" },
|
|
4949
|
+
...targetState && targetAdapter?.targets ? [
|
|
4950
|
+
{
|
|
4951
|
+
id: "target",
|
|
4952
|
+
label: `${targetState.status === "selection-required" ? "Select" : "Change"} ${targetAdapter.targets.singularLabel}\u2026`,
|
|
4953
|
+
action: "target"
|
|
4954
|
+
}
|
|
4955
|
+
] : [],
|
|
4476
4956
|
...fastAvailability.kind === "available" ? [
|
|
4477
4957
|
{
|
|
4478
4958
|
id: "toggle-fast",
|
|
@@ -4503,7 +4983,7 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4503
4983
|
title: "Select a configured provider",
|
|
4504
4984
|
items: configuredAdapters(ctx).filter((adapter) => adapter.id !== ctx.model?.provider).map((adapter) => ({
|
|
4505
4985
|
id: adapter.id,
|
|
4506
|
-
label: adapter.
|
|
4986
|
+
label: providerDisplayName(ctx, adapter.id),
|
|
4507
4987
|
action: "provider"
|
|
4508
4988
|
})),
|
|
4509
4989
|
hint: "back"
|
|
@@ -4565,6 +5045,58 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4565
5045
|
})
|
|
4566
5046
|
},
|
|
4567
5047
|
actions: {
|
|
5048
|
+
target: async () => {
|
|
5049
|
+
const targetState = actionableTargetState();
|
|
5050
|
+
if (!targetState) return { kind: "rejected" };
|
|
5051
|
+
try {
|
|
5052
|
+
const stateFingerprint = targetState === current.state ? current.fingerprint : void 0;
|
|
5053
|
+
if (!await promptForTarget(targetState, stateFingerprint)) {
|
|
5054
|
+
return { kind: "stay" };
|
|
5055
|
+
}
|
|
5056
|
+
const adapter = adapterForProvider(targetState.providerId);
|
|
5057
|
+
if (!adapter) return { kind: "rejected" };
|
|
5058
|
+
if (targetState.providerId === ctx.model?.provider) {
|
|
5059
|
+
const refreshed = await queryStableCurrent(
|
|
5060
|
+
ctx,
|
|
5061
|
+
true,
|
|
5062
|
+
controller,
|
|
5063
|
+
`Checking ${providerDisplayName(ctx, adapter.id)} usage\u2026`
|
|
5064
|
+
);
|
|
5065
|
+
if (!refreshed) return { kind: "stay" };
|
|
5066
|
+
stableCurrent = refreshed;
|
|
5067
|
+
current = refreshed.outcome;
|
|
5068
|
+
visibleStates = [current.state];
|
|
5069
|
+
publishStableCurrent(ctx, refreshed);
|
|
5070
|
+
return { kind: "stay" };
|
|
5071
|
+
}
|
|
5072
|
+
const outcome = await runMenuOperation(
|
|
5073
|
+
ctx,
|
|
5074
|
+
`Checking ${providerDisplayName(ctx, adapter.id)} usage\u2026`,
|
|
5075
|
+
controller.signal,
|
|
5076
|
+
(signal) => queryAdapterState(ctx, adapter, "configured", true, signal)
|
|
5077
|
+
);
|
|
5078
|
+
if (!outcome) return { kind: "stay" };
|
|
5079
|
+
const revalidated = await queryStableCurrent(
|
|
5080
|
+
ctx,
|
|
5081
|
+
false,
|
|
5082
|
+
controller,
|
|
5083
|
+
"Revalidating current usage\u2026"
|
|
5084
|
+
);
|
|
5085
|
+
if (!revalidated) return { kind: "stay" };
|
|
5086
|
+
stableCurrent = revalidated;
|
|
5087
|
+
current = revalidated.outcome;
|
|
5088
|
+
visibleStates = [
|
|
5089
|
+
outcome.state.providerId === current.state.providerId ? current.state : { ...outcome.state, displayState: "configured" }
|
|
5090
|
+
];
|
|
5091
|
+
return { kind: "stay" };
|
|
5092
|
+
} catch (error) {
|
|
5093
|
+
if (isAbortError3(error) || isStaleExtensionContextError(error)) {
|
|
5094
|
+
return { kind: "stay" };
|
|
5095
|
+
}
|
|
5096
|
+
ctx.ui.notify(`Could not select target: ${errorMessage(error)}`, "error");
|
|
5097
|
+
return { kind: "stay" };
|
|
5098
|
+
}
|
|
5099
|
+
},
|
|
4568
5100
|
settings: async () => {
|
|
4569
5101
|
await showUsageSettings(
|
|
4570
5102
|
ctx,
|
|
@@ -4773,13 +5305,25 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4773
5305
|
(candidate) => candidate.id === itemId && candidate.id !== ctx.model?.provider
|
|
4774
5306
|
);
|
|
4775
5307
|
if (!adapter) return { kind: "back" };
|
|
4776
|
-
|
|
5308
|
+
let outcome = await runMenuOperation(
|
|
4777
5309
|
ctx,
|
|
4778
|
-
`Checking ${adapter.
|
|
5310
|
+
`Checking ${providerDisplayName(ctx, adapter.id)} usage\u2026`,
|
|
4779
5311
|
controller.signal,
|
|
4780
5312
|
(signal) => queryAdapterState(ctx, adapter, "configured", false, signal)
|
|
4781
5313
|
);
|
|
4782
5314
|
if (!outcome) return { kind: "back" };
|
|
5315
|
+
if (outcome.state.status === "selection-required") {
|
|
5316
|
+
if (!await promptForTarget(outcome.state, outcome.fingerprint)) {
|
|
5317
|
+
return { kind: "back" };
|
|
5318
|
+
}
|
|
5319
|
+
outcome = await runMenuOperation(
|
|
5320
|
+
ctx,
|
|
5321
|
+
`Checking ${providerDisplayName(ctx, adapter.id)} usage\u2026`,
|
|
5322
|
+
controller.signal,
|
|
5323
|
+
(signal) => queryAdapterState(ctx, adapter, "configured", true, signal)
|
|
5324
|
+
);
|
|
5325
|
+
if (!outcome) return { kind: "back" };
|
|
5326
|
+
}
|
|
4783
5327
|
const revalidated = await queryStableCurrent(
|
|
4784
5328
|
ctx,
|
|
4785
5329
|
false,
|
|
@@ -4822,7 +5366,7 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4822
5366
|
const adapter = adapters[index];
|
|
4823
5367
|
return {
|
|
4824
5368
|
providerId: adapter.id,
|
|
4825
|
-
providerName: adapter.
|
|
5369
|
+
providerName: providerDisplayName(ctx, adapter.id),
|
|
4826
5370
|
displayState: "configured",
|
|
4827
5371
|
status: "query-failed",
|
|
4828
5372
|
message: errorMessage(result.reason)
|
|
@@ -4936,15 +5480,19 @@ export {
|
|
|
4936
5480
|
codexFastStatusLabel,
|
|
4937
5481
|
consumeCodexResetCredit,
|
|
4938
5482
|
correctCodexFastMessageCost,
|
|
5483
|
+
createFireworksAdapter,
|
|
4939
5484
|
createUsageSettingsRuntime,
|
|
5485
|
+
createUsageTargetSelectOptions,
|
|
4940
5486
|
usageExtension as default,
|
|
4941
5487
|
errorMessage,
|
|
4942
5488
|
fingerprintResolvedAuth,
|
|
4943
5489
|
formatProviderStates,
|
|
4944
5490
|
formatUsageReport,
|
|
4945
5491
|
formatUsageStatusline,
|
|
5492
|
+
isBoundedTargetId,
|
|
4946
5493
|
isStaleExtensionContextError,
|
|
4947
5494
|
listCodexResetCredits,
|
|
5495
|
+
listUsageTargets,
|
|
4948
5496
|
loadUsageSettings,
|
|
4949
5497
|
miniMaxUsageKind,
|
|
4950
5498
|
normalizeBasetenBillingUsagePayload,
|
|
@@ -4960,14 +5508,17 @@ export {
|
|
|
4960
5508
|
normalizeOpenCodeZenPayload,
|
|
4961
5509
|
normalizeOpenRouterKeyPayload,
|
|
4962
5510
|
normalizeUsageSettings,
|
|
5511
|
+
normalizeUsageTargets,
|
|
4963
5512
|
normalizeVercelAIGatewayCreditsPayload,
|
|
4964
5513
|
normalizeXaiBillingPayload,
|
|
4965
5514
|
normalizeZaiQuotaPayload,
|
|
5515
|
+
normalizeZaiSubscriptionPayload,
|
|
4966
5516
|
providerIsConfigured,
|
|
4967
5517
|
queryProviderUsage,
|
|
4968
5518
|
redactUsageError,
|
|
4969
5519
|
resolveCodexResetAuth,
|
|
4970
5520
|
resolveUsageAuth,
|
|
5521
|
+
resolveUsageTarget,
|
|
4971
5522
|
rewriteCodexFastPayload,
|
|
4972
5523
|
runWithConcurrency,
|
|
4973
5524
|
sanitizeDisplayText,
|