@narumitw/pi-usage 0.60.0 → 0.60.2
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 +27 -19
- package/dist/index.ts +804 -322
- package/dist/index.ts.map +4 -4
- package/package.json +1 -1
- package/src/core.ts +28 -2
- package/src/format.ts +37 -23
- package/src/index.ts +13 -0
- package/src/providers/fireworks.ts +112 -0
- package/src/providers/minimax.ts +21 -0
- package/src/query.ts +110 -150
- package/src/settings.ts +155 -8
- package/src/types.ts +39 -2
- package/src/usage-settings-ui.ts +88 -183
- package/src/usage-targets.ts +143 -0
- package/src/usage.ts +419 -107
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;
|
|
@@ -1145,6 +1254,26 @@ function normalizeWindow(row, fields) {
|
|
|
1145
1254
|
}
|
|
1146
1255
|
const total = nonnegativeInteger(row[fields.totalField], fields.totalField);
|
|
1147
1256
|
const count = nonnegativeInteger(row[fields.countField], fields.countField);
|
|
1257
|
+
if (total === 0) {
|
|
1258
|
+
if (count !== 0) {
|
|
1259
|
+
throw new Error(`MiniMax Token Plan ${fields.label} counts were inconsistent.`);
|
|
1260
|
+
}
|
|
1261
|
+
if (percent === void 0) {
|
|
1262
|
+
throw new Error(`MiniMax Token Plan ${fields.label} returned no quota and no percent.`);
|
|
1263
|
+
}
|
|
1264
|
+
return {
|
|
1265
|
+
id: fields.id,
|
|
1266
|
+
label: fields.label,
|
|
1267
|
+
groupId: fields.groupId,
|
|
1268
|
+
groupLabel: fields.groupLabel,
|
|
1269
|
+
remaining: percent,
|
|
1270
|
+
used: 100 - percent,
|
|
1271
|
+
limit: 0,
|
|
1272
|
+
unit: "percent",
|
|
1273
|
+
windowMinutes,
|
|
1274
|
+
resetsAt
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1148
1277
|
const resolved = resolveQuotaCounts(count, total, percent);
|
|
1149
1278
|
if (!resolved) throw new Error(`MiniMax Token Plan ${fields.label} counts were inconsistent.`);
|
|
1150
1279
|
return {
|
|
@@ -1787,14 +1916,104 @@ function clampPercent3(value) {
|
|
|
1787
1916
|
return Math.min(100, Math.max(0, value));
|
|
1788
1917
|
}
|
|
1789
1918
|
|
|
1919
|
+
// src/usage-targets.ts
|
|
1920
|
+
var MAX_TARGETS = 1e3;
|
|
1921
|
+
var MAX_TARGET_ID_CHARS = 256;
|
|
1922
|
+
var MAX_TARGET_LABEL_CHARS = 120;
|
|
1923
|
+
var MAX_TARGET_DESCRIPTION_CHARS = 180;
|
|
1924
|
+
async function resolveUsageTarget(adapter, auth, rememberedTargetId, signal, timeoutMs, guard) {
|
|
1925
|
+
if (!adapter.targets) return { kind: "selected" };
|
|
1926
|
+
if (rememberedTargetId !== void 0 && !isBoundedTargetId(rememberedTargetId)) {
|
|
1927
|
+
throw new Error(`The remembered ${adapter.targets.singularLabel} identifier was invalid.`);
|
|
1928
|
+
}
|
|
1929
|
+
const choices = await listUsageTargets(adapter, auth, signal, timeoutMs, guard);
|
|
1930
|
+
if (rememberedTargetId) {
|
|
1931
|
+
return choices.some((choice) => choice.id === rememberedTargetId) ? { kind: "selected", targetId: rememberedTargetId } : { kind: "selection-required", choices };
|
|
1932
|
+
}
|
|
1933
|
+
if (choices.length === 1) return { kind: "selected", targetId: choices[0]?.id };
|
|
1934
|
+
return { kind: "selection-required", choices };
|
|
1935
|
+
}
|
|
1936
|
+
async function listUsageTargets(adapter, auth, signal, timeoutMs, guard) {
|
|
1937
|
+
if (!adapter.targets) return [];
|
|
1938
|
+
const startedAt = Date.now();
|
|
1939
|
+
await guard();
|
|
1940
|
+
const listed = await adapter.targets.list(
|
|
1941
|
+
auth,
|
|
1942
|
+
signal,
|
|
1943
|
+
remainingTargetTimeout(timeoutMs, startedAt),
|
|
1944
|
+
guard
|
|
1945
|
+
);
|
|
1946
|
+
await guard();
|
|
1947
|
+
remainingTargetTimeout(timeoutMs, startedAt);
|
|
1948
|
+
const choices = normalizeUsageTargets(listed);
|
|
1949
|
+
if (choices.length === 0) {
|
|
1950
|
+
throw new Error(`${adapter.targets.pluralLabel} discovery returned no choices.`);
|
|
1951
|
+
}
|
|
1952
|
+
return choices;
|
|
1953
|
+
}
|
|
1954
|
+
function normalizeUsageTargets(targets) {
|
|
1955
|
+
if (!Array.isArray(targets)) throw new Error("Target discovery did not return a choices array.");
|
|
1956
|
+
if (targets.length > MAX_TARGETS) {
|
|
1957
|
+
throw new Error(`Target discovery exceeded ${MAX_TARGETS} choices.`);
|
|
1958
|
+
}
|
|
1959
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1960
|
+
return targets.map((target) => {
|
|
1961
|
+
if (!target || typeof target !== "object" || Array.isArray(target)) {
|
|
1962
|
+
throw new Error("Target discovery returned an invalid choice.");
|
|
1963
|
+
}
|
|
1964
|
+
const { id, label, description } = target;
|
|
1965
|
+
if (!isBoundedTargetId(id)) throw new Error("Target discovery returned an invalid ID.");
|
|
1966
|
+
if (seen.has(id)) throw new Error(`Target discovery repeated ${id}.`);
|
|
1967
|
+
seen.add(id);
|
|
1968
|
+
if (typeof label !== "string") {
|
|
1969
|
+
throw new Error("Target discovery returned an invalid display label.");
|
|
1970
|
+
}
|
|
1971
|
+
if (description !== void 0 && typeof description !== "string") {
|
|
1972
|
+
throw new Error("Target discovery returned an invalid description.");
|
|
1973
|
+
}
|
|
1974
|
+
const safeLabel2 = sanitizeDisplayText(label, MAX_TARGET_LABEL_CHARS);
|
|
1975
|
+
if (!safeLabel2) throw new Error("Target discovery returned an empty display label.");
|
|
1976
|
+
const safeDescription = description ? sanitizeDisplayText(description, MAX_TARGET_DESCRIPTION_CHARS) : void 0;
|
|
1977
|
+
return { id, label: safeLabel2, ...safeDescription ? { description: safeDescription } : {} };
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
function createUsageTargetSelectOptions(targets) {
|
|
1981
|
+
const normalized = normalizeUsageTargets(targets);
|
|
1982
|
+
const ids = /* @__PURE__ */ new Map();
|
|
1983
|
+
const options = normalized.map((target) => {
|
|
1984
|
+
const base = target.description ? `${target.label} \u2014 ${target.description}` : target.label;
|
|
1985
|
+
let option = base;
|
|
1986
|
+
if (ids.has(option)) {
|
|
1987
|
+
const safeId = sanitizeDisplayText(target.id, 80) || "target";
|
|
1988
|
+
option = `${base} \xB7 ${safeId}`;
|
|
1989
|
+
let duplicate = 2;
|
|
1990
|
+
while (ids.has(option)) {
|
|
1991
|
+
option = `${base} \xB7 ${safeId} (${duplicate})`;
|
|
1992
|
+
duplicate += 1;
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
ids.set(option, target.id);
|
|
1996
|
+
return option;
|
|
1997
|
+
});
|
|
1998
|
+
return { options, targetIdFor: (option) => ids.get(option) };
|
|
1999
|
+
}
|
|
2000
|
+
function remainingTargetTimeout(timeoutMs, startedAt) {
|
|
2001
|
+
const remaining = timeoutMs - (Date.now() - startedAt);
|
|
2002
|
+
if (remaining <= 0) throw new Error("Timed out while discovering provider targets.");
|
|
2003
|
+
return remaining;
|
|
2004
|
+
}
|
|
2005
|
+
function isBoundedTargetId(value) {
|
|
2006
|
+
return typeof value === "string" && value.length > 0 && value.length <= MAX_TARGET_ID_CHARS && ![...value].some((character) => {
|
|
2007
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
2008
|
+
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
|
|
1790
2012
|
// src/query.ts
|
|
1791
2013
|
var BASETEN_BILLING_USAGE_URL = "https://api.baseten.co/v1/billing/usage_summary";
|
|
1792
2014
|
var BASETEN_USAGE_WINDOW_DAYS = 30;
|
|
1793
2015
|
var CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
1794
2016
|
var DEEPSEEK_BALANCE_URL = "https://api.deepseek.com/user/balance";
|
|
1795
|
-
var FIREWORKS_BILLING_SUMMARY_ORIGIN = "https://api.fireworks.ai";
|
|
1796
|
-
var FIREWORKS_SPEND_WINDOW_DAYS = 30;
|
|
1797
|
-
var FIREWORKS_MAX_ACCOUNT_PAGES = 5;
|
|
1798
2017
|
var GITHUB_COPILOT_USAGE_URL = "https://api.github.com/copilot_internal/user";
|
|
1799
2018
|
var OPENROUTER_KEY_URL = "https://openrouter.ai/api/v1/key";
|
|
1800
2019
|
var VERCEL_AI_GATEWAY_CREDITS_URL = "https://ai-gateway.vercel.sh/v1/credits";
|
|
@@ -1833,7 +2052,7 @@ var SUPPORTED_ADAPTERS = [
|
|
|
1833
2052
|
basetenBillingUsageUrl(windowAt),
|
|
1834
2053
|
auth,
|
|
1835
2054
|
signal,
|
|
1836
|
-
|
|
2055
|
+
remainingTimeout2(timeoutMs, startedAt, "fetching Baseten billing usage"),
|
|
1837
2056
|
"Baseten billing usage endpoint",
|
|
1838
2057
|
{ redirect: "error" }
|
|
1839
2058
|
);
|
|
@@ -1926,7 +2145,7 @@ var SUPPORTED_ADAPTERS = [
|
|
|
1926
2145
|
VERCEL_AI_GATEWAY_CREDITS_URL,
|
|
1927
2146
|
auth,
|
|
1928
2147
|
signal,
|
|
1929
|
-
|
|
2148
|
+
remainingTimeout2(timeoutMs, startedAt, "fetching Vercel AI Gateway credits"),
|
|
1930
2149
|
"Vercel AI Gateway credits endpoint",
|
|
1931
2150
|
{ redirect: "error" }
|
|
1932
2151
|
);
|
|
@@ -1934,35 +2153,7 @@ var SUPPORTED_ADAPTERS = [
|
|
|
1934
2153
|
return normalizeVercelAIGatewayCreditsPayload(payload, Date.now());
|
|
1935
2154
|
}
|
|
1936
2155
|
},
|
|
1937
|
-
|
|
1938
|
-
id: "fireworks",
|
|
1939
|
-
displayName: "Fireworks",
|
|
1940
|
-
semantics: { kind: "api-key", label: "Fireworks API spend" },
|
|
1941
|
-
async query(auth, signal, timeoutMs, guard, settings) {
|
|
1942
|
-
if (!guard) throw new Error("Fireworks API spend requires request-boundary revalidation.");
|
|
1943
|
-
const startedAt = Date.now();
|
|
1944
|
-
await guard();
|
|
1945
|
-
const accountId = await resolveFireworksAccountId(
|
|
1946
|
-
auth,
|
|
1947
|
-
signal,
|
|
1948
|
-
remainingTimeout(timeoutMs, startedAt, "resolving the Fireworks account"),
|
|
1949
|
-
guard,
|
|
1950
|
-
settings?.fireworksAccountId
|
|
1951
|
-
);
|
|
1952
|
-
await guard();
|
|
1953
|
-
const billingWindowAt = Date.now();
|
|
1954
|
-
const payload = await fetchProviderJson(
|
|
1955
|
-
fireworksBillingSummaryUrl(accountId, billingWindowAt),
|
|
1956
|
-
auth,
|
|
1957
|
-
signal,
|
|
1958
|
-
remainingTimeout(timeoutMs, startedAt, "fetching Fireworks rated spend"),
|
|
1959
|
-
"Fireworks billing summary endpoint",
|
|
1960
|
-
{ redirect: "error" }
|
|
1961
|
-
);
|
|
1962
|
-
await guard();
|
|
1963
|
-
return normalizeFireworksBillingSummaryPayload(payload, accountId, Date.now());
|
|
1964
|
-
}
|
|
1965
|
-
},
|
|
2156
|
+
createFireworksAdapter(fetchProviderJson),
|
|
1966
2157
|
{
|
|
1967
2158
|
id: "opencode-go",
|
|
1968
2159
|
displayName: "OpenCode Go",
|
|
@@ -2063,7 +2254,7 @@ var XAI_ADAPTER = {
|
|
|
2063
2254
|
XAI_USER_URL,
|
|
2064
2255
|
clientAuth,
|
|
2065
2256
|
signal,
|
|
2066
|
-
|
|
2257
|
+
remainingTimeout2(timeoutMs, startedAt),
|
|
2067
2258
|
"xAI consumer identity endpoint",
|
|
2068
2259
|
{ redirect: "error", userAgent: false }
|
|
2069
2260
|
);
|
|
@@ -2079,7 +2270,7 @@ var XAI_ADAPTER = {
|
|
|
2079
2270
|
XAI_BILLING_URL,
|
|
2080
2271
|
billingAuth,
|
|
2081
2272
|
signal,
|
|
2082
|
-
|
|
2273
|
+
remainingTimeout2(timeoutMs, startedAt),
|
|
2083
2274
|
"xAI consumer billing endpoint",
|
|
2084
2275
|
{ redirect: "error", userAgent: false }
|
|
2085
2276
|
);
|
|
@@ -2107,6 +2298,12 @@ async function resolveUsageAuth(ctx, adapter, salt = AUTH_FINGERPRINT_SALT, cred
|
|
|
2107
2298
|
);
|
|
2108
2299
|
if (!model) return void 0;
|
|
2109
2300
|
const registry = ctx.modelRegistry;
|
|
2301
|
+
const provider = registry.getProvider?.(adapter.id);
|
|
2302
|
+
if (provider?.baseUrl && !hasOfficialUrlOrigin(provider.baseUrl, adapter.id)) {
|
|
2303
|
+
throw new Error(
|
|
2304
|
+
`${adapter.displayName} usage cannot send an overridden provider credential to the official usage endpoint.`
|
|
2305
|
+
);
|
|
2306
|
+
}
|
|
2110
2307
|
let modelAuth;
|
|
2111
2308
|
const currentModel = ctx.model?.provider === adapter.id ? ctx.model : void 0;
|
|
2112
2309
|
const resolveCurrentModelAuth = async () => {
|
|
@@ -2129,25 +2326,64 @@ async function resolveUsageAuth(ctx, adapter, salt = AUTH_FINGERPRINT_SALT, cred
|
|
|
2129
2326
|
);
|
|
2130
2327
|
}
|
|
2131
2328
|
if (resolveSelectedAuthLast) modelAuth = await resolveCurrentModelAuth();
|
|
2329
|
+
if (modelAuth?.baseUrl && !hasOfficialUrlOrigin(modelAuth.baseUrl, adapter.id)) {
|
|
2330
|
+
throw new Error(
|
|
2331
|
+
`${adapter.displayName} usage cannot send model-resolved proxy credentials to the official usage endpoint.`
|
|
2332
|
+
);
|
|
2333
|
+
}
|
|
2132
2334
|
const auth = modelAuth ?? providerResult?.auth;
|
|
2133
2335
|
if (!auth) return void 0;
|
|
2336
|
+
const finalize = (resolved) => {
|
|
2337
|
+
const preservedAuth = { ...providerResult?.auth ?? auth };
|
|
2338
|
+
const env = providerResult?.env ?? modelAuth?.env;
|
|
2339
|
+
const source = providerResult?.source;
|
|
2340
|
+
const effectiveBaseUrl = modelAuth?.baseUrl ?? providerResult?.auth.baseUrl ?? provider?.baseUrl ?? model.baseUrl;
|
|
2341
|
+
const redactionInputs = [
|
|
2342
|
+
preservedAuth.apiKey,
|
|
2343
|
+
...Object.values(preservedAuth.headers ?? {}),
|
|
2344
|
+
...Object.values(env ?? {}),
|
|
2345
|
+
modelAuth?.apiKey,
|
|
2346
|
+
...Object.values(modelAuth?.headers ?? {})
|
|
2347
|
+
].filter((value) => typeof value === "string" && value.length > 0);
|
|
2348
|
+
return {
|
|
2349
|
+
...resolved,
|
|
2350
|
+
auth: preservedAuth,
|
|
2351
|
+
...env ? { env: { ...env } } : {},
|
|
2352
|
+
...source ? { source } : {},
|
|
2353
|
+
effectiveBaseUrl,
|
|
2354
|
+
secrets: [.../* @__PURE__ */ new Set([...resolved.secrets, ...redactionInputs])],
|
|
2355
|
+
fingerprint: fingerprintResolvedAuth(
|
|
2356
|
+
{
|
|
2357
|
+
apiKey: resolved.apiKey,
|
|
2358
|
+
headers: resolved.headers,
|
|
2359
|
+
baseUrl: effectiveBaseUrl,
|
|
2360
|
+
env,
|
|
2361
|
+
source,
|
|
2362
|
+
providerAuth: preservedAuth
|
|
2363
|
+
},
|
|
2364
|
+
salt
|
|
2365
|
+
)
|
|
2366
|
+
};
|
|
2367
|
+
};
|
|
2134
2368
|
if (adapter.id === "github-copilot") {
|
|
2135
2369
|
const offered = candidateReader ? candidateReader(ctx, adapter.id) : fallbackOAuthCredentialCandidates(adapter.id, credentialReader);
|
|
2136
2370
|
if (!offered.ok) {
|
|
2137
2371
|
throw new Error("GitHub Copilot OAuth credential discovery failed closed.");
|
|
2138
2372
|
}
|
|
2139
|
-
return
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2373
|
+
return finalize(
|
|
2374
|
+
resolveGitHubCopilotUsageAuth(
|
|
2375
|
+
auth,
|
|
2376
|
+
model,
|
|
2377
|
+
salt,
|
|
2378
|
+
offered.candidates,
|
|
2379
|
+
offered.offeredCount === 0
|
|
2380
|
+
)
|
|
2145
2381
|
);
|
|
2146
2382
|
}
|
|
2147
2383
|
if (adapter.id === "xai") {
|
|
2148
2384
|
const offered = candidateReader ? candidateReader(ctx, adapter.id) : fallbackOAuthCredentialCandidates(adapter.id, credentialReader);
|
|
2149
2385
|
if (!offered.ok) throw new Error("xAI OAuth credential discovery failed closed.");
|
|
2150
|
-
return resolveXaiUsageAuth(auth, model, salt, offered.candidates);
|
|
2386
|
+
return finalize(resolveXaiUsageAuth(auth, model, salt, offered.candidates));
|
|
2151
2387
|
}
|
|
2152
2388
|
if (adapter.id === "deepseek") {
|
|
2153
2389
|
const resolvedAuthorization = authorizationFrom(auth);
|
|
@@ -2155,10 +2391,10 @@ async function resolveUsageAuth(ctx, adapter, salt = AUTH_FINGERPRINT_SALT, cred
|
|
|
2155
2391
|
if (!access) throw new Error("DeepSeek API balance requires Bearer authentication.");
|
|
2156
2392
|
const authorization2 = `Bearer ${access}`;
|
|
2157
2393
|
const headers2 = { Authorization: authorization2 };
|
|
2158
|
-
return {
|
|
2394
|
+
return finalize({
|
|
2159
2395
|
apiKey: access,
|
|
2160
2396
|
headers: headers2,
|
|
2161
|
-
fingerprint:
|
|
2397
|
+
fingerprint: "",
|
|
2162
2398
|
secrets: [
|
|
2163
2399
|
access,
|
|
2164
2400
|
auth.apiKey,
|
|
@@ -2167,7 +2403,7 @@ async function resolveUsageAuth(ctx, adapter, salt = AUTH_FINGERPRINT_SALT, cred
|
|
|
2167
2403
|
authorization2
|
|
2168
2404
|
].filter((value) => Boolean(value)),
|
|
2169
2405
|
model
|
|
2170
|
-
};
|
|
2406
|
+
});
|
|
2171
2407
|
}
|
|
2172
2408
|
const authorization = authorizationFrom(auth);
|
|
2173
2409
|
if (!authorization) return void 0;
|
|
@@ -2175,17 +2411,41 @@ async function resolveUsageAuth(ctx, adapter, salt = AUTH_FINGERPRINT_SALT, cred
|
|
|
2175
2411
|
const secrets = [auth.apiKey, headerValue(auth.headers, "Authorization"), authorization].filter(
|
|
2176
2412
|
(value) => Boolean(value)
|
|
2177
2413
|
);
|
|
2178
|
-
return {
|
|
2414
|
+
return finalize({
|
|
2179
2415
|
apiKey: auth.apiKey,
|
|
2180
2416
|
headers,
|
|
2181
|
-
fingerprint:
|
|
2417
|
+
fingerprint: "",
|
|
2182
2418
|
secrets,
|
|
2183
2419
|
model
|
|
2184
|
-
};
|
|
2420
|
+
});
|
|
2185
2421
|
}
|
|
2186
|
-
async function queryProviderUsage(adapter, auth, signal, timeoutMs, guard,
|
|
2422
|
+
async function queryProviderUsage(adapter, auth, signal, timeoutMs, guard, targetOrSettings) {
|
|
2423
|
+
const startedAt = Date.now();
|
|
2424
|
+
let targetId = typeof targetOrSettings === "string" ? targetOrSettings : adapter.id === "fireworks" ? targetOrSettings?.fireworksAccountId : void 0;
|
|
2425
|
+
let resolvedLegacyFireworksTarget = false;
|
|
2187
2426
|
try {
|
|
2188
|
-
|
|
2427
|
+
if (adapter.id === "fireworks" && typeof targetOrSettings !== "string" && adapter.targets && guard) {
|
|
2428
|
+
const target = await resolveUsageTarget(
|
|
2429
|
+
adapter,
|
|
2430
|
+
auth,
|
|
2431
|
+
targetId,
|
|
2432
|
+
signal,
|
|
2433
|
+
remainingTimeout2(timeoutMs, startedAt, "resolving the Fireworks account"),
|
|
2434
|
+
guard
|
|
2435
|
+
);
|
|
2436
|
+
if (target.kind === "selection-required") {
|
|
2437
|
+
throw new Error("Fireworks account selection is required.");
|
|
2438
|
+
}
|
|
2439
|
+
targetId = target.targetId;
|
|
2440
|
+
resolvedLegacyFireworksTarget = true;
|
|
2441
|
+
}
|
|
2442
|
+
return await adapter.query(
|
|
2443
|
+
auth,
|
|
2444
|
+
signal,
|
|
2445
|
+
resolvedLegacyFireworksTarget ? remainingTimeout2(timeoutMs, startedAt, `querying ${adapter.displayName} usage`) : timeoutMs,
|
|
2446
|
+
guard,
|
|
2447
|
+
targetId
|
|
2448
|
+
);
|
|
2189
2449
|
} catch (error) {
|
|
2190
2450
|
if (isStaleExtensionContextError(error) || isAbortError(error)) throw error;
|
|
2191
2451
|
throw new Error(redactUsageError(errorMessage(error), auth.secrets));
|
|
@@ -2484,7 +2744,7 @@ function hasOfficialUrlOrigin(value, providerId) {
|
|
|
2484
2744
|
}
|
|
2485
2745
|
if (providerId === "openai-codex") return url.origin === "https://chatgpt.com";
|
|
2486
2746
|
if (providerId === "deepseek") return url.origin === "https://api.deepseek.com";
|
|
2487
|
-
if (providerId === "fireworks") return url.origin ===
|
|
2747
|
+
if (providerId === "fireworks") return url.origin === "https://api.fireworks.ai";
|
|
2488
2748
|
if (providerId === "openrouter") return url.origin === "https://openrouter.ai";
|
|
2489
2749
|
if (providerId === "vercel-ai-gateway") return url.origin === "https://ai-gateway.vercel.sh";
|
|
2490
2750
|
if (providerId === "opencode-go") return url.origin === "https://opencode.ai";
|
|
@@ -2540,7 +2800,7 @@ async function queryMiniMaxUsage(providerId, auth, signal, timeoutMs, guard) {
|
|
|
2540
2800
|
`${MINIMAX_API_ROOTS[providerId]}${path}`,
|
|
2541
2801
|
auth,
|
|
2542
2802
|
signal,
|
|
2543
|
-
|
|
2803
|
+
remainingTimeout2(timeoutMs, startedAt, "fetching MiniMax usage"),
|
|
2544
2804
|
"MiniMax usage endpoint",
|
|
2545
2805
|
{ redirect: "error" }
|
|
2546
2806
|
);
|
|
@@ -2555,94 +2815,18 @@ async function queryMoonshotBalance(providerId, auth, signal, timeoutMs, guard)
|
|
|
2555
2815
|
MOONSHOT_BALANCE_URLS[providerId],
|
|
2556
2816
|
auth,
|
|
2557
2817
|
signal,
|
|
2558
|
-
|
|
2818
|
+
remainingTimeout2(timeoutMs, startedAt, "fetching Moonshot AI balance"),
|
|
2559
2819
|
"Moonshot AI balance endpoint",
|
|
2560
2820
|
{ redirect: "error" }
|
|
2561
2821
|
);
|
|
2562
2822
|
await guard();
|
|
2563
2823
|
return normalizeMoonshotBalancePayload(providerId, payload, Date.now());
|
|
2564
2824
|
}
|
|
2565
|
-
function
|
|
2825
|
+
function remainingTimeout2(timeoutMs, startedAt, description = "fetching xAI consumer usage") {
|
|
2566
2826
|
const remaining = timeoutMs - (Date.now() - startedAt);
|
|
2567
2827
|
if (remaining <= 0) throw new Error(`Timed out while ${description}.`);
|
|
2568
2828
|
return remaining;
|
|
2569
2829
|
}
|
|
2570
|
-
async function resolveFireworksAccountId(auth, signal, timeoutMs, guard, configuredAccountId) {
|
|
2571
|
-
if (configuredAccountId !== void 0 && !isFireworksAccountId(configuredAccountId)) {
|
|
2572
|
-
throw new Error("The Fireworks account setting was not a safe account slug.");
|
|
2573
|
-
}
|
|
2574
|
-
const startedAt = Date.now();
|
|
2575
|
-
const accounts = [];
|
|
2576
|
-
let pageToken;
|
|
2577
|
-
for (let page = 0; page < FIREWORKS_MAX_ACCOUNT_PAGES; page += 1) {
|
|
2578
|
-
await guard();
|
|
2579
|
-
const payload = await fetchProviderJson(
|
|
2580
|
-
fireworksAccountsUrl(pageToken),
|
|
2581
|
-
auth,
|
|
2582
|
-
signal,
|
|
2583
|
-
remainingTimeout(timeoutMs, startedAt, "fetching Fireworks accounts"),
|
|
2584
|
-
"Fireworks accounts endpoint",
|
|
2585
|
-
{ redirect: "error" }
|
|
2586
|
-
);
|
|
2587
|
-
for (const accountId of normalizeFireworksAccountsPayload(
|
|
2588
|
-
payload
|
|
2589
|
-
)) {
|
|
2590
|
-
if (accounts.includes(accountId)) {
|
|
2591
|
-
throw new Error(`Fireworks accounts listing repeated ${accountId}.`);
|
|
2592
|
-
}
|
|
2593
|
-
accounts.push(accountId);
|
|
2594
|
-
if (configuredAccountId === accountId) return accountId;
|
|
2595
|
-
}
|
|
2596
|
-
pageToken = fireworksNextPageToken(payload.nextPageToken);
|
|
2597
|
-
if (!pageToken) break;
|
|
2598
|
-
}
|
|
2599
|
-
if (pageToken) {
|
|
2600
|
-
throw new Error(
|
|
2601
|
-
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.`
|
|
2602
|
-
);
|
|
2603
|
-
}
|
|
2604
|
-
if (accounts.length === 0) {
|
|
2605
|
-
throw new Error("Fireworks account discovery returned no accounts for this API key.");
|
|
2606
|
-
}
|
|
2607
|
-
if (configuredAccountId) {
|
|
2608
|
-
throw new Error(
|
|
2609
|
-
"The configured Fireworks account does not match an account visible to this API key."
|
|
2610
|
-
);
|
|
2611
|
-
}
|
|
2612
|
-
if (accounts.length === 1) return accounts[0];
|
|
2613
|
-
const preview = accounts.slice(0, 8).join(", ");
|
|
2614
|
-
const suffix = accounts.length > 8 ? ` \u2026and ${accounts.length - 8} more` : "";
|
|
2615
|
-
throw new Error(
|
|
2616
|
-
`The Fireworks key can see ${accounts.length} accounts (${preview}${suffix}); set fireworksAccountId in pi-usage.json to one of them.`
|
|
2617
|
-
);
|
|
2618
|
-
}
|
|
2619
|
-
function fireworksAccountsUrl(pageToken) {
|
|
2620
|
-
const url = new URL("/v1/accounts", FIREWORKS_BILLING_SUMMARY_ORIGIN);
|
|
2621
|
-
url.searchParams.set("pageSize", "200");
|
|
2622
|
-
if (pageToken !== void 0) url.searchParams.set("pageToken", pageToken);
|
|
2623
|
-
return url.toString();
|
|
2624
|
-
}
|
|
2625
|
-
function fireworksNextPageToken(value) {
|
|
2626
|
-
if (value === void 0 || value === null) return void 0;
|
|
2627
|
-
if (typeof value !== "string" || !value || value.length > 512) {
|
|
2628
|
-
throw new Error("Fireworks accounts listing returned an invalid page token.");
|
|
2629
|
-
}
|
|
2630
|
-
return value;
|
|
2631
|
-
}
|
|
2632
|
-
function fireworksBillingSummaryUrl(accountId, startedAt) {
|
|
2633
|
-
const dayMs = 24 * 60 * 60 * 1e3;
|
|
2634
|
-
const dayFloor = (time) => `${new Date(time).toISOString().slice(0, 10)}T00:00:00Z`;
|
|
2635
|
-
const url = new URL(
|
|
2636
|
-
`/v1/accounts/${accountId}/billing/summary`,
|
|
2637
|
-
FIREWORKS_BILLING_SUMMARY_ORIGIN
|
|
2638
|
-
);
|
|
2639
|
-
url.searchParams.set(
|
|
2640
|
-
"startTime",
|
|
2641
|
-
dayFloor(startedAt - (FIREWORKS_SPEND_WINDOW_DAYS - 1) * dayMs)
|
|
2642
|
-
);
|
|
2643
|
-
url.searchParams.set("endTime", dayFloor(startedAt + dayMs));
|
|
2644
|
-
return url.toString();
|
|
2645
|
-
}
|
|
2646
2830
|
function zaiOrigin(baseUrl) {
|
|
2647
2831
|
const base = baseUrl?.trim();
|
|
2648
2832
|
if (!base) throw new Error("Z.AI model base URL is unavailable.");
|
|
@@ -2665,7 +2849,7 @@ async function queryZaiUsage(providerId, providerName, auth, signal, timeoutMs,
|
|
|
2665
2849
|
zaiMonitorUrl(auth.model.baseUrl),
|
|
2666
2850
|
zaiMonitorAuth(auth),
|
|
2667
2851
|
signal,
|
|
2668
|
-
|
|
2852
|
+
remainingTimeout2(timeoutMs, startedAt, `fetching ${providerName} quota`),
|
|
2669
2853
|
`${providerName} quota endpoint`
|
|
2670
2854
|
);
|
|
2671
2855
|
await guard();
|
|
@@ -3016,6 +3200,10 @@ function formatProviderStates(states) {
|
|
|
3016
3200
|
return states.map((state) => {
|
|
3017
3201
|
if (state.status === "ready") return formatUsageReport(state.report, state.displayState);
|
|
3018
3202
|
const label = state.displayState === "current" ? "Current" : "Configured";
|
|
3203
|
+
if (state.status === "selection-required") {
|
|
3204
|
+
return `${state.providerName} \xB7 ${label}
|
|
3205
|
+
Selection required: choose this provider's ${state.singularLabel} by viewing it individually.`;
|
|
3206
|
+
}
|
|
3019
3207
|
const status = state.status === "auth-unavailable" ? "Authentication unavailable" : state.status === "unsupported" ? "Unsupported" : "Query failed";
|
|
3020
3208
|
return `${state.providerName} \xB7 ${label}
|
|
3021
3209
|
${status}: ${state.message}`;
|
|
@@ -3250,7 +3438,7 @@ function formatMiniMaxReport(lines, report2) {
|
|
|
3250
3438
|
if (bucket.groupId !== previousGroup) lines.push(`${bucket.groupLabel ?? "Token Plan"}:`);
|
|
3251
3439
|
previousGroup = bucket.groupId;
|
|
3252
3440
|
const reset = bucket.resetsAt ? ` (resets ${formatReset(bucket.resetsAt)})` : "";
|
|
3253
|
-
const value = bucket.period === "unlimited" ? "unlimited" : bucket.limit && bucket.remaining !== void 0 ? `${bucket.remaining} of ${bucket.limit} left \xB7 ${percentRemaining(bucket)}%${reset}` : "unavailable";
|
|
3441
|
+
const value = bucket.period === "unlimited" ? "unlimited" : bucket.unit === "percent" && bucket.remaining !== void 0 ? `${bucket.remaining}% remaining${reset}` : bucket.limit !== void 0 && bucket.remaining !== void 0 ? `${bucket.remaining} of ${bucket.limit} left \xB7 ${percentRemaining(bucket)}%${reset}` : "unavailable";
|
|
3254
3442
|
lines.push(`${`${bucket.label}:`.padEnd(VALUE_COLUMN)}${value}`);
|
|
3255
3443
|
}
|
|
3256
3444
|
}
|
|
@@ -3271,7 +3459,11 @@ function formatMiniMaxStatusline(report2, model) {
|
|
|
3271
3459
|
parts.push(`unlimited ${window}`);
|
|
3272
3460
|
continue;
|
|
3273
3461
|
}
|
|
3274
|
-
if (
|
|
3462
|
+
if (bucket.unit === "percent" && bucket.remaining !== void 0) {
|
|
3463
|
+
parts.push(`${bucket.remaining}% ${window}`);
|
|
3464
|
+
continue;
|
|
3465
|
+
}
|
|
3466
|
+
if (bucket.limit === void 0 || bucket.remaining === void 0) continue;
|
|
3275
3467
|
parts.push(`${percentRemaining(bucket)}% ${window}`);
|
|
3276
3468
|
}
|
|
3277
3469
|
return parts.length > 1 ? parts.join(" ") : void 0;
|
|
@@ -3283,22 +3475,26 @@ function selectMiniMaxGroup(report2, model) {
|
|
|
3283
3475
|
)
|
|
3284
3476
|
];
|
|
3285
3477
|
if (groups.length <= 1) return groups[0];
|
|
3286
|
-
if (model
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
const
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
(
|
|
3300
|
-
|
|
3301
|
-
|
|
3478
|
+
if (model && model.provider !== report2.providerId) return void 0;
|
|
3479
|
+
if (model) {
|
|
3480
|
+
const modelKeys = [model.id, model.name].map(normalizeMiniMaxModelKey).filter((key) => key !== void 0);
|
|
3481
|
+
const candidates = groups.map((group) => {
|
|
3482
|
+
const bucket = report2.buckets.find((candidate) => candidate.groupId === group);
|
|
3483
|
+
const patterns = [bucket?.groupLabel, ...bucket?.modelKeys ?? [], group].map(normalizeMiniMaxModelKey).filter((key) => key !== void 0);
|
|
3484
|
+
return { group, patterns };
|
|
3485
|
+
});
|
|
3486
|
+
const exact = candidates.find(
|
|
3487
|
+
({ patterns }) => patterns.some((pattern) => !pattern.includes("*") && modelKeys.includes(pattern))
|
|
3488
|
+
);
|
|
3489
|
+
if (exact) return exact.group;
|
|
3490
|
+
const wildcard = candidates.find(
|
|
3491
|
+
({ patterns }) => patterns.some(
|
|
3492
|
+
(pattern) => pattern.includes("*") && modelKeys.some((key) => wildcardKeyMatches(pattern, key))
|
|
3493
|
+
)
|
|
3494
|
+
);
|
|
3495
|
+
if (wildcard) return wildcard.group;
|
|
3496
|
+
}
|
|
3497
|
+
return groups.find((group) => group === "general");
|
|
3302
3498
|
}
|
|
3303
3499
|
function normalizeMiniMaxModelKey(value) {
|
|
3304
3500
|
const key = value?.toLowerCase().replace(/[^a-z0-9*]+/gu, "");
|
|
@@ -3552,7 +3748,8 @@ var USAGE_SETTINGS_FILE = "pi-usage.json";
|
|
|
3552
3748
|
var MAX_USAGE_SETTINGS_BYTES = 64 * 1024;
|
|
3553
3749
|
var DEFAULT_USAGE_SETTINGS = Object.freeze({
|
|
3554
3750
|
codexFastMode: false,
|
|
3555
|
-
codexStatusResetCountdown: true
|
|
3751
|
+
codexStatusResetCountdown: true,
|
|
3752
|
+
selectedTargets: Object.freeze({})
|
|
3556
3753
|
});
|
|
3557
3754
|
function usageSettingsPath() {
|
|
3558
3755
|
return join(getAgentDir(), USAGE_SETTINGS_FILE);
|
|
@@ -3568,10 +3765,16 @@ function normalizeUsageSettings(value) {
|
|
|
3568
3765
|
if (Object.hasOwn(value, "fireworksAccountId") && !isFireworksAccountId(value.fireworksAccountId)) {
|
|
3569
3766
|
return void 0;
|
|
3570
3767
|
}
|
|
3768
|
+
const selectedTargets = normalizeSelectedTargets(value.selectedTargets);
|
|
3769
|
+
if (Object.hasOwn(value, "selectedTargets") && !selectedTargets) return void 0;
|
|
3770
|
+
const effectiveTargets = { ...selectedTargets ?? {} };
|
|
3771
|
+
if (!effectiveTargets.fireworks && isFireworksAccountId(value.fireworksAccountId)) {
|
|
3772
|
+
effectiveTargets.fireworks = value.fireworksAccountId;
|
|
3773
|
+
}
|
|
3571
3774
|
return {
|
|
3572
3775
|
codexFastMode: typeof value.codexFastMode === "boolean" ? value.codexFastMode : DEFAULT_USAGE_SETTINGS.codexFastMode,
|
|
3573
3776
|
codexStatusResetCountdown: typeof value.codexStatusResetCountdown === "boolean" ? value.codexStatusResetCountdown : DEFAULT_USAGE_SETTINGS.codexStatusResetCountdown,
|
|
3574
|
-
|
|
3777
|
+
selectedTargets: effectiveTargets
|
|
3575
3778
|
};
|
|
3576
3779
|
}
|
|
3577
3780
|
async function loadUsageSettings(path = usageSettingsPath(), signal) {
|
|
@@ -3647,19 +3850,84 @@ function createUsageSettingsRuntime(options = {}) {
|
|
|
3647
3850
|
state = saved;
|
|
3648
3851
|
return structuredClone(state);
|
|
3649
3852
|
}),
|
|
3853
|
+
updateSelectedTarget: (providerId, targetId, signal, checkPublishedSelection) => enqueue(async () => {
|
|
3854
|
+
const transaction = await saveUsageTargetSelection(
|
|
3855
|
+
path,
|
|
3856
|
+
providerId,
|
|
3857
|
+
targetId,
|
|
3858
|
+
operations,
|
|
3859
|
+
signal
|
|
3860
|
+
);
|
|
3861
|
+
try {
|
|
3862
|
+
await checkPublishedSelection?.();
|
|
3863
|
+
throwIfAborted(signal);
|
|
3864
|
+
} catch (error) {
|
|
3865
|
+
try {
|
|
3866
|
+
await restoreUsageSettingsState(
|
|
3867
|
+
path,
|
|
3868
|
+
transaction.saved,
|
|
3869
|
+
transaction.previous,
|
|
3870
|
+
operations
|
|
3871
|
+
);
|
|
3872
|
+
state = transaction.previous;
|
|
3873
|
+
} catch (rollbackError) {
|
|
3874
|
+
state = await loadUsageSettings(path);
|
|
3875
|
+
throw new AggregateError(
|
|
3876
|
+
[error, rollbackError],
|
|
3877
|
+
"Target selection changed after publication and pi-usage.json rollback failed"
|
|
3878
|
+
);
|
|
3879
|
+
}
|
|
3880
|
+
throw error;
|
|
3881
|
+
}
|
|
3882
|
+
state = transaction.saved;
|
|
3883
|
+
return structuredClone(state);
|
|
3884
|
+
}),
|
|
3650
3885
|
flush: () => queue
|
|
3651
3886
|
};
|
|
3652
3887
|
}
|
|
3653
3888
|
async function saveUsageSettingsPatch(path, patch, operations, signal) {
|
|
3889
|
+
return saveUsageSettingsDocument(
|
|
3890
|
+
path,
|
|
3891
|
+
(document) => {
|
|
3892
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
3893
|
+
if (value === void 0) delete document[key];
|
|
3894
|
+
else document[key] = value;
|
|
3895
|
+
}
|
|
3896
|
+
},
|
|
3897
|
+
operations,
|
|
3898
|
+
signal
|
|
3899
|
+
);
|
|
3900
|
+
}
|
|
3901
|
+
async function saveUsageTargetSelection(path, providerId, targetId, operations, signal) {
|
|
3902
|
+
if (!isProviderId(providerId) || !isBoundedTargetId(targetId)) {
|
|
3903
|
+
throw new Error("Refusing to save an invalid usage target selection");
|
|
3904
|
+
}
|
|
3905
|
+
const previous = await loadUsageSettings(path, signal);
|
|
3906
|
+
const saved = await saveUsageSettingsDocument(
|
|
3907
|
+
path,
|
|
3908
|
+
(document) => {
|
|
3909
|
+
document.selectedTargets = {
|
|
3910
|
+
...normalizeSelectedTargets(document.selectedTargets) ?? {},
|
|
3911
|
+
[providerId]: targetId
|
|
3912
|
+
};
|
|
3913
|
+
if (providerId === "fireworks") delete document.fireworksAccountId;
|
|
3914
|
+
},
|
|
3915
|
+
operations,
|
|
3916
|
+
signal,
|
|
3917
|
+
previous
|
|
3918
|
+
);
|
|
3919
|
+
return { saved, previous };
|
|
3920
|
+
}
|
|
3921
|
+
async function saveUsageSettingsDocument(path, mutate, operations, signal, expected) {
|
|
3654
3922
|
const latest = await loadUsageSettings(path, signal);
|
|
3655
3923
|
if (latest.kind === "invalid") {
|
|
3656
3924
|
throw new Error("Cannot overwrite an invalid pi-usage.json; repair it and reload first");
|
|
3657
3925
|
}
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
if (value === void 0) delete document[key];
|
|
3661
|
-
else document[key] = value;
|
|
3926
|
+
if (expected && !sameUsageSettingsDocument(latest, expected)) {
|
|
3927
|
+
throw new Error("pi-usage.json changed while saving; retry the action");
|
|
3662
3928
|
}
|
|
3929
|
+
const document = { ...latest.document };
|
|
3930
|
+
mutate(document);
|
|
3663
3931
|
const settings = normalizeUsageSettings(document);
|
|
3664
3932
|
if (!settings) throw new Error("Refusing to save invalid pi-usage settings");
|
|
3665
3933
|
const directory = dirname(path);
|
|
@@ -3686,6 +3954,32 @@ async function saveUsageSettingsPatch(path, patch, operations, signal) {
|
|
|
3686
3954
|
}
|
|
3687
3955
|
return { kind: "loaded", path, settings, document };
|
|
3688
3956
|
}
|
|
3957
|
+
async function restoreUsageSettingsState(path, published, previous, operations) {
|
|
3958
|
+
if (previous.kind === "missing") {
|
|
3959
|
+
const current = await loadUsageSettings(path);
|
|
3960
|
+
if (!sameUsageSettingsDocument(current, published)) {
|
|
3961
|
+
throw new Error("pi-usage.json changed before target selection rollback");
|
|
3962
|
+
}
|
|
3963
|
+
await rm(path);
|
|
3964
|
+
return;
|
|
3965
|
+
}
|
|
3966
|
+
if (previous.kind !== "loaded" || !previous.document) {
|
|
3967
|
+
throw new Error("Cannot restore invalid prior pi-usage.json settings");
|
|
3968
|
+
}
|
|
3969
|
+
await saveUsageSettingsDocument(
|
|
3970
|
+
path,
|
|
3971
|
+
(document) => {
|
|
3972
|
+
for (const key of Object.keys(document)) delete document[key];
|
|
3973
|
+
Object.assign(document, previous.document);
|
|
3974
|
+
},
|
|
3975
|
+
operations,
|
|
3976
|
+
void 0,
|
|
3977
|
+
published
|
|
3978
|
+
);
|
|
3979
|
+
}
|
|
3980
|
+
function sameUsageSettingsDocument(left, right) {
|
|
3981
|
+
return left.kind === right.kind && JSON.stringify(left.document) === JSON.stringify(right.document);
|
|
3982
|
+
}
|
|
3689
3983
|
async function chmodPrivate(path) {
|
|
3690
3984
|
await chmod(path, 384);
|
|
3691
3985
|
}
|
|
@@ -3698,6 +3992,19 @@ function isRecord3(value) {
|
|
|
3698
3992
|
function isNodeError(error) {
|
|
3699
3993
|
return error instanceof Error && "code" in error;
|
|
3700
3994
|
}
|
|
3995
|
+
function normalizeSelectedTargets(value) {
|
|
3996
|
+
if (value === void 0) return {};
|
|
3997
|
+
if (!isRecord3(value)) return void 0;
|
|
3998
|
+
const targets = {};
|
|
3999
|
+
for (const [providerId, targetId] of Object.entries(value)) {
|
|
4000
|
+
if (!isProviderId(providerId) || !isBoundedTargetId(targetId)) return void 0;
|
|
4001
|
+
targets[providerId] = targetId;
|
|
4002
|
+
}
|
|
4003
|
+
return targets;
|
|
4004
|
+
}
|
|
4005
|
+
function isProviderId(value) {
|
|
4006
|
+
return /^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$/u.test(value);
|
|
4007
|
+
}
|
|
3701
4008
|
|
|
3702
4009
|
// src/usage.ts
|
|
3703
4010
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
@@ -3908,8 +4215,6 @@ import {
|
|
|
3908
4215
|
SettingsList,
|
|
3909
4216
|
Text
|
|
3910
4217
|
} from "@earendil-works/pi-tui";
|
|
3911
|
-
var AUTO = "Auto";
|
|
3912
|
-
var EDIT = "Edit\u2026";
|
|
3913
4218
|
var OFF = "Off";
|
|
3914
4219
|
var ON = "On";
|
|
3915
4220
|
async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent, onApplied) {
|
|
@@ -3917,31 +4222,13 @@ async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent,
|
|
|
3917
4222
|
if (ctx.hasUI) ctx.ui.notify(`Edit settings manually: ${settingsRuntime.get().path}`, "info");
|
|
3918
4223
|
return false;
|
|
3919
4224
|
}
|
|
3920
|
-
|
|
3921
|
-
while (!parentSignal.aborted && isCurrent()) {
|
|
3922
|
-
const result = await showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, onApplied);
|
|
3923
|
-
if (!result) return changed;
|
|
3924
|
-
changed ||= result.changed;
|
|
3925
|
-
if (!result.editFireworksAccount) return changed;
|
|
3926
|
-
changed ||= await editFireworksAccount(
|
|
3927
|
-
ctx,
|
|
3928
|
-
settingsRuntime,
|
|
3929
|
-
parentSignal,
|
|
3930
|
-
isCurrent,
|
|
3931
|
-
onApplied
|
|
3932
|
-
);
|
|
3933
|
-
}
|
|
3934
|
-
return changed;
|
|
3935
|
-
}
|
|
3936
|
-
async function showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, onApplied) {
|
|
3937
|
-
return ctx.ui.custom((tui, theme, _keybindings, done) => {
|
|
4225
|
+
return await ctx.ui.custom((tui, theme, _keybindings, done) => {
|
|
3938
4226
|
const localController = new AbortController();
|
|
3939
4227
|
const signal = AbortSignal.any([parentSignal, localController.signal]);
|
|
3940
4228
|
let changed = false;
|
|
3941
4229
|
let closing = false;
|
|
3942
4230
|
let saveQueue = Promise.resolve();
|
|
3943
4231
|
const state = settingsRuntime.get();
|
|
3944
|
-
const fireworksValue = state.settings.fireworksAccountId ?? AUTO;
|
|
3945
4232
|
const items = [
|
|
3946
4233
|
{
|
|
3947
4234
|
id: "codexFastMode",
|
|
@@ -3956,13 +4243,6 @@ async function showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, o
|
|
|
3956
4243
|
description: "Show time remaining until each Codex usage limit resets.",
|
|
3957
4244
|
currentValue: state.settings.codexStatusResetCountdown ? ON : OFF,
|
|
3958
4245
|
values: [OFF, ON]
|
|
3959
|
-
},
|
|
3960
|
-
{
|
|
3961
|
-
id: "fireworksAccountId",
|
|
3962
|
-
label: "Fireworks account",
|
|
3963
|
-
description: "Select Edit to enter a visible account slug, or submit blank to clear it.",
|
|
3964
|
-
currentValue: fireworksValue,
|
|
3965
|
-
values: state.settings.fireworksAccountId ? [state.settings.fireworksAccountId, EDIT] : [AUTO, EDIT]
|
|
3966
4246
|
}
|
|
3967
4247
|
];
|
|
3968
4248
|
const container = new Container();
|
|
@@ -3972,13 +4252,13 @@ async function showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, o
|
|
|
3972
4252
|
if (closing) return;
|
|
3973
4253
|
closing = true;
|
|
3974
4254
|
localController.abort();
|
|
3975
|
-
done(
|
|
4255
|
+
done(changed);
|
|
3976
4256
|
};
|
|
3977
4257
|
const queueUpdate = (id, requested, display) => {
|
|
3978
4258
|
saveQueue = saveQueue.then(async () => {
|
|
3979
4259
|
const previous = settingsRuntime.get().settings[id];
|
|
3980
4260
|
if (settingsRuntime.get().kind === "invalid") {
|
|
3981
|
-
settingsList.updateValue(id,
|
|
4261
|
+
settingsList.updateValue(id, previous ? ON : OFF);
|
|
3982
4262
|
if (!signal.aborted && isCurrent()) {
|
|
3983
4263
|
ctx.ui.notify("Repair pi-usage.json and reload before changing settings.", "error");
|
|
3984
4264
|
tui.requestRender();
|
|
@@ -3989,7 +4269,7 @@ async function showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, o
|
|
|
3989
4269
|
await settingsRuntime.update({ [id]: requested }, signal);
|
|
3990
4270
|
} catch (error) {
|
|
3991
4271
|
if (signal.aborted || !isCurrent()) return;
|
|
3992
|
-
settingsList.updateValue(id,
|
|
4272
|
+
settingsList.updateValue(id, previous ? ON : OFF);
|
|
3993
4273
|
ctx.ui.notify(`Could not save pi-usage.json: ${errorMessage(error)}`, "error");
|
|
3994
4274
|
tui.requestRender();
|
|
3995
4275
|
return;
|
|
@@ -4009,18 +4289,7 @@ async function showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, o
|
|
|
4009
4289
|
getSettingsListTheme(),
|
|
4010
4290
|
(id, value) => {
|
|
4011
4291
|
if (closing || signal.aborted || !isCurrent()) return;
|
|
4012
|
-
|
|
4013
|
-
if (value === EDIT) {
|
|
4014
|
-
saveQueue = saveQueue.then(() => {
|
|
4015
|
-
if (closing || signal.aborted || !isCurrent()) return;
|
|
4016
|
-
closing = true;
|
|
4017
|
-
done({ changed, editFireworksAccount: true });
|
|
4018
|
-
});
|
|
4019
|
-
}
|
|
4020
|
-
return;
|
|
4021
|
-
}
|
|
4022
|
-
const settingId = id;
|
|
4023
|
-
queueUpdate(settingId, value !== OFF, value);
|
|
4292
|
+
queueUpdate(id, value !== OFF, value);
|
|
4024
4293
|
},
|
|
4025
4294
|
cancel
|
|
4026
4295
|
);
|
|
@@ -4040,43 +4309,7 @@ async function showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, o
|
|
|
4040
4309
|
parentSignal.removeEventListener("abort", cancel);
|
|
4041
4310
|
}
|
|
4042
4311
|
};
|
|
4043
|
-
});
|
|
4044
|
-
}
|
|
4045
|
-
async function editFireworksAccount(ctx, settingsRuntime, signal, isCurrent, onApplied) {
|
|
4046
|
-
while (!signal.aborted && isCurrent()) {
|
|
4047
|
-
const state = settingsRuntime.get();
|
|
4048
|
-
if (state.kind === "invalid") {
|
|
4049
|
-
ctx.ui.notify("Repair pi-usage.json and reload before changing settings.", "error");
|
|
4050
|
-
return false;
|
|
4051
|
-
}
|
|
4052
|
-
const entered = await ctx.ui.input(
|
|
4053
|
-
"Fireworks account slug \xB7 submit blank for Auto",
|
|
4054
|
-
state.settings.fireworksAccountId ?? "Example: acme",
|
|
4055
|
-
{ signal }
|
|
4056
|
-
);
|
|
4057
|
-
if (signal.aborted || !isCurrent() || entered === void 0) return false;
|
|
4058
|
-
const normalized = entered.trim();
|
|
4059
|
-
const requested = normalized || void 0;
|
|
4060
|
-
if (requested !== void 0 && !isFireworksAccountId(requested)) {
|
|
4061
|
-
ctx.ui.notify("Enter a URL-safe Fireworks account slug.", "warning");
|
|
4062
|
-
continue;
|
|
4063
|
-
}
|
|
4064
|
-
if (requested === state.settings.fireworksAccountId) return false;
|
|
4065
|
-
try {
|
|
4066
|
-
await settingsRuntime.update({ fireworksAccountId: requested }, signal);
|
|
4067
|
-
} catch (error) {
|
|
4068
|
-
if (signal.aborted || !isCurrent()) return false;
|
|
4069
|
-
ctx.ui.notify(`Could not save pi-usage.json: ${errorMessage(error)}`, "error");
|
|
4070
|
-
return false;
|
|
4071
|
-
}
|
|
4072
|
-
onApplied("fireworksAccountId");
|
|
4073
|
-
return true;
|
|
4074
|
-
}
|
|
4075
|
-
return false;
|
|
4076
|
-
}
|
|
4077
|
-
function displaySetting(id, value) {
|
|
4078
|
-
if (id === "fireworksAccountId") return typeof value === "string" ? value : AUTO;
|
|
4079
|
-
return value ? ON : OFF;
|
|
4312
|
+
}) ?? false;
|
|
4080
4313
|
}
|
|
4081
4314
|
|
|
4082
4315
|
// src/usage.ts
|
|
@@ -4093,6 +4326,9 @@ var VIEW_ALL = "View all configured providers\u2026";
|
|
|
4093
4326
|
var CLOSE = "Close";
|
|
4094
4327
|
var SETTINGS = "Settings";
|
|
4095
4328
|
var REDEEM_CODEX_RESET = "Redeem usage limit reset\u2026";
|
|
4329
|
+
var UsageTargetSelectionChangedError = class extends Error {
|
|
4330
|
+
name = "UsageTargetSelectionChangedError";
|
|
4331
|
+
};
|
|
4096
4332
|
function usageExtension(pi, dependencies = {}) {
|
|
4097
4333
|
const credentialReader = dependencies.credentialReader;
|
|
4098
4334
|
const credentialCandidates = createOAuthCredentialCandidateReader(pi, credentialReader);
|
|
@@ -4162,10 +4398,8 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4162
4398
|
return;
|
|
4163
4399
|
}
|
|
4164
4400
|
if (outcome.state.status !== "ready") {
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
outcome.state.status === "auth-unavailable" ? "auth unavailable" : "usage error"
|
|
4168
|
-
)) {
|
|
4401
|
+
const chip = outcome.state.status === "auth-unavailable" ? "auth unavailable" : outcome.state.status === "selection-required" ? "selection required" : `usage err: ${outcome.state.message.slice(0, 50)}`;
|
|
4402
|
+
if (safeSetStatus(ctx, chip)) {
|
|
4169
4403
|
if (shouldSchedule && sessionActive) scheduleStatusRefresh(ctx, model);
|
|
4170
4404
|
}
|
|
4171
4405
|
return;
|
|
@@ -4217,8 +4451,8 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4217
4451
|
const expectedSessionGeneration = sessionGeneration;
|
|
4218
4452
|
const expectedSessionId = ctx.sessionManager.getSessionId();
|
|
4219
4453
|
const expectedModelIdentity = modelIdentity(ctx.model);
|
|
4220
|
-
const
|
|
4221
|
-
const
|
|
4454
|
+
const expectedTargetId = adapter.targets ? settingsRuntime.get().settings.selectedTargets[adapter.id] : void 0;
|
|
4455
|
+
const providerName = providerDisplayName(ctx, adapter.id);
|
|
4222
4456
|
let auth;
|
|
4223
4457
|
try {
|
|
4224
4458
|
auth = await awaitWithDeadline(
|
|
@@ -4235,17 +4469,16 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4235
4469
|
return {
|
|
4236
4470
|
state: {
|
|
4237
4471
|
providerId: adapter.id,
|
|
4238
|
-
providerName
|
|
4472
|
+
providerName,
|
|
4239
4473
|
displayState,
|
|
4240
4474
|
status: isTimeoutError(error) ? "query-failed" : "auth-unavailable",
|
|
4241
4475
|
message: errorMessage(error)
|
|
4242
4476
|
}
|
|
4243
4477
|
};
|
|
4244
4478
|
}
|
|
4245
|
-
const requiresRequestBoundaryGuard = [
|
|
4479
|
+
const requiresRequestBoundaryGuard = adapter.targets !== void 0 || [
|
|
4246
4480
|
"baseten",
|
|
4247
4481
|
"deepseek",
|
|
4248
|
-
"fireworks",
|
|
4249
4482
|
"minimax",
|
|
4250
4483
|
"minimax-cn",
|
|
4251
4484
|
"moonshotai",
|
|
@@ -4255,7 +4488,7 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4255
4488
|
"zai",
|
|
4256
4489
|
"zai-coding-cn"
|
|
4257
4490
|
].includes(adapter.id);
|
|
4258
|
-
const requestContextChanged = () => expectedSessionGeneration !== sessionGeneration || ctx.sessionManager.getSessionId() !== expectedSessionId || modelIdentity(ctx.model) !== expectedModelIdentity || adapter.
|
|
4491
|
+
const requestContextChanged = () => expectedSessionGeneration !== sessionGeneration || ctx.sessionManager.getSessionId() !== expectedSessionId || modelIdentity(ctx.model) !== expectedModelIdentity || adapter.targets !== void 0 && settingsRuntime.get().settings.selectedTargets[adapter.id] !== expectedTargetId;
|
|
4259
4492
|
if (requiresRequestBoundaryGuard && requestContextChanged()) throw abortError();
|
|
4260
4493
|
if (!auth) {
|
|
4261
4494
|
if (displayState === "current") {
|
|
@@ -4264,98 +4497,145 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4264
4497
|
return {
|
|
4265
4498
|
state: {
|
|
4266
4499
|
providerId: adapter.id,
|
|
4267
|
-
providerName
|
|
4500
|
+
providerName,
|
|
4268
4501
|
displayState,
|
|
4269
4502
|
status: "auth-unavailable",
|
|
4270
|
-
message: `No runtime credential is configured for ${
|
|
4503
|
+
message: `No runtime credential is configured for ${providerName}.`
|
|
4271
4504
|
},
|
|
4272
4505
|
authState: "unavailable"
|
|
4273
4506
|
};
|
|
4274
4507
|
}
|
|
4275
|
-
const queryFingerprint = adapter.id === "fireworks" ? `${auth.fingerprint}:account:${expectedFireworksAccountId ?? "auto"}` : auth.fingerprint;
|
|
4276
|
-
if (displayState === "current") {
|
|
4277
|
-
transitionCurrentIdentity(`${adapter.id}:${queryFingerprint}`, adapter.id);
|
|
4278
|
-
}
|
|
4279
|
-
const cached = !force ? cache.get(adapter.id, queryFingerprint) : void 0;
|
|
4280
|
-
if (cached) {
|
|
4281
|
-
return {
|
|
4282
|
-
state: {
|
|
4283
|
-
providerId: adapter.id,
|
|
4284
|
-
providerName: adapter.displayName,
|
|
4285
|
-
displayState,
|
|
4286
|
-
status: "ready",
|
|
4287
|
-
report: cached
|
|
4288
|
-
},
|
|
4289
|
-
fingerprint: auth.fingerprint
|
|
4290
|
-
};
|
|
4291
|
-
}
|
|
4292
|
-
const failureKey = `${adapter.id}:${queryFingerprint}`;
|
|
4293
|
-
const previousFailure = failureBackoff.get(failureKey);
|
|
4294
|
-
if (!force && previousFailure && previousFailure.until > Date.now()) {
|
|
4295
|
-
return {
|
|
4296
|
-
state: {
|
|
4297
|
-
providerId: adapter.id,
|
|
4298
|
-
providerName: adapter.displayName,
|
|
4299
|
-
displayState,
|
|
4300
|
-
status: "query-failed",
|
|
4301
|
-
message: previousFailure.message
|
|
4302
|
-
},
|
|
4303
|
-
fingerprint: auth.fingerprint
|
|
4304
|
-
};
|
|
4305
|
-
}
|
|
4306
|
-
failureBackoff.delete(failureKey);
|
|
4307
|
-
querySequence += 1;
|
|
4308
|
-
const queryId = querySequence;
|
|
4309
|
-
setBoundedMap(latestQueries, failureKey, queryId, MAX_ACCOUNT_STATES);
|
|
4310
4508
|
let retryableAuthChanged = false;
|
|
4509
|
+
const guard = async () => {
|
|
4510
|
+
if (signal.aborted || requestContextChanged()) throw abortError();
|
|
4511
|
+
if (!requiresRequestBoundaryGuard) return;
|
|
4512
|
+
const revalidated = await awaitWithDeadline(
|
|
4513
|
+
resolveUsageAuth(ctx, adapter, void 0, credentialReader, credentialCandidates),
|
|
4514
|
+
signal,
|
|
4515
|
+
Math.max(1, deadlineAt - Date.now()),
|
|
4516
|
+
`revalidating ${providerName} runtime auth`
|
|
4517
|
+
);
|
|
4518
|
+
if (signal.aborted || requestContextChanged()) throw abortError();
|
|
4519
|
+
if (revalidated?.fingerprint !== auth.fingerprint) {
|
|
4520
|
+
if (["deepseek", "minimax", "minimax-cn"].includes(adapter.id)) {
|
|
4521
|
+
retryableAuthChanged = true;
|
|
4522
|
+
throw new Error(`${providerName} runtime credential changed during the usage query.`);
|
|
4523
|
+
}
|
|
4524
|
+
throw abortError();
|
|
4525
|
+
}
|
|
4526
|
+
};
|
|
4527
|
+
let queryFingerprint = adapter.targets ? `${auth.fingerprint}:target:${expectedTargetId ?? "unresolved"}` : auth.fingerprint;
|
|
4528
|
+
let failureKey = `${adapter.id}:${queryFingerprint}`;
|
|
4529
|
+
let queryId;
|
|
4311
4530
|
try {
|
|
4312
|
-
const
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4531
|
+
const previousDiscoveryFailure = failureBackoff.get(failureKey);
|
|
4532
|
+
if (!force && previousDiscoveryFailure && previousDiscoveryFailure.until > Date.now()) {
|
|
4533
|
+
return {
|
|
4534
|
+
state: {
|
|
4535
|
+
providerId: adapter.id,
|
|
4536
|
+
providerName,
|
|
4537
|
+
displayState,
|
|
4538
|
+
status: "query-failed",
|
|
4539
|
+
message: previousDiscoveryFailure.message
|
|
4540
|
+
},
|
|
4541
|
+
fingerprint: auth.fingerprint,
|
|
4542
|
+
rememberedTargetId: expectedTargetId
|
|
4543
|
+
};
|
|
4544
|
+
}
|
|
4545
|
+
const target = await resolveUsageTarget(
|
|
4546
|
+
adapter,
|
|
4547
|
+
auth,
|
|
4548
|
+
expectedTargetId,
|
|
4549
|
+
signal,
|
|
4550
|
+
Math.max(1, deadlineAt - Date.now()),
|
|
4551
|
+
guard
|
|
4552
|
+
);
|
|
4553
|
+
if (target.kind === "selection-required") {
|
|
4554
|
+
if (displayState === "current") {
|
|
4555
|
+
transitionCurrentIdentity(`${adapter.id}:${queryFingerprint}`, adapter.id);
|
|
4330
4556
|
}
|
|
4331
|
-
|
|
4557
|
+
return {
|
|
4558
|
+
state: {
|
|
4559
|
+
providerId: adapter.id,
|
|
4560
|
+
providerName,
|
|
4561
|
+
displayState,
|
|
4562
|
+
status: "selection-required",
|
|
4563
|
+
singularLabel: adapter.targets?.singularLabel ?? "target",
|
|
4564
|
+
pluralLabel: adapter.targets?.pluralLabel ?? "targets",
|
|
4565
|
+
choices: target.choices
|
|
4566
|
+
},
|
|
4567
|
+
fingerprint: auth.fingerprint,
|
|
4568
|
+
rememberedTargetId: expectedTargetId
|
|
4569
|
+
};
|
|
4570
|
+
}
|
|
4571
|
+
queryFingerprint = adapter.targets ? `${auth.fingerprint}:target:${target.targetId ?? "none"}` : auth.fingerprint;
|
|
4572
|
+
failureKey = `${adapter.id}:${queryFingerprint}`;
|
|
4573
|
+
if (displayState === "current") {
|
|
4574
|
+
transitionCurrentIdentity(`${adapter.id}:${queryFingerprint}`, adapter.id);
|
|
4575
|
+
}
|
|
4576
|
+
const cached = !force ? cache.get(adapter.id, queryFingerprint) : void 0;
|
|
4577
|
+
if (cached) {
|
|
4578
|
+
return {
|
|
4579
|
+
state: {
|
|
4580
|
+
providerId: adapter.id,
|
|
4581
|
+
providerName,
|
|
4582
|
+
displayState,
|
|
4583
|
+
status: "ready",
|
|
4584
|
+
report: cached
|
|
4585
|
+
},
|
|
4586
|
+
fingerprint: auth.fingerprint,
|
|
4587
|
+
rememberedTargetId: expectedTargetId
|
|
4588
|
+
};
|
|
4589
|
+
}
|
|
4590
|
+
const previousFailure = failureBackoff.get(failureKey);
|
|
4591
|
+
if (!force && previousFailure && previousFailure.until > Date.now()) {
|
|
4592
|
+
return {
|
|
4593
|
+
state: {
|
|
4594
|
+
providerId: adapter.id,
|
|
4595
|
+
providerName,
|
|
4596
|
+
displayState,
|
|
4597
|
+
status: "query-failed",
|
|
4598
|
+
message: previousFailure.message
|
|
4599
|
+
},
|
|
4600
|
+
fingerprint: auth.fingerprint,
|
|
4601
|
+
rememberedTargetId: expectedTargetId
|
|
4602
|
+
};
|
|
4603
|
+
}
|
|
4604
|
+
failureBackoff.delete(failureKey);
|
|
4605
|
+
querySequence += 1;
|
|
4606
|
+
queryId = querySequence;
|
|
4607
|
+
setBoundedMap(latestQueries, failureKey, queryId, MAX_ACCOUNT_STATES);
|
|
4332
4608
|
const report2 = await queryProviderUsage(
|
|
4333
4609
|
adapter,
|
|
4334
4610
|
auth,
|
|
4335
4611
|
signal,
|
|
4336
|
-
|
|
4337
|
-
guard,
|
|
4338
|
-
|
|
4612
|
+
Math.max(1, deadlineAt - Date.now()),
|
|
4613
|
+
requiresRequestBoundaryGuard ? guard : void 0,
|
|
4614
|
+
target.targetId
|
|
4339
4615
|
);
|
|
4340
|
-
if (
|
|
4616
|
+
if (requiresRequestBoundaryGuard) await guard();
|
|
4617
|
+
const effectiveReport = { ...report2, providerName };
|
|
4341
4618
|
if (latestQueries.get(failureKey) === queryId) {
|
|
4342
|
-
cache.set(adapter.id, queryFingerprint,
|
|
4619
|
+
cache.set(adapter.id, queryFingerprint, effectiveReport);
|
|
4343
4620
|
failureBackoff.delete(failureKey);
|
|
4344
4621
|
}
|
|
4345
4622
|
return {
|
|
4346
4623
|
state: {
|
|
4347
4624
|
providerId: adapter.id,
|
|
4348
|
-
providerName
|
|
4625
|
+
providerName,
|
|
4349
4626
|
displayState,
|
|
4350
4627
|
status: "ready",
|
|
4351
|
-
report:
|
|
4628
|
+
report: effectiveReport
|
|
4352
4629
|
},
|
|
4353
|
-
fingerprint: auth.fingerprint
|
|
4630
|
+
fingerprint: auth.fingerprint,
|
|
4631
|
+
rememberedTargetId: expectedTargetId
|
|
4354
4632
|
};
|
|
4355
4633
|
} catch (error) {
|
|
4356
4634
|
if (isStaleExtensionContextError(error) || isAbortError3(error)) throw error;
|
|
4357
4635
|
if (retryableAuthChanged && authRetry === 0 && !signal.aborted && !requestContextChanged() && Date.now() < deadlineAt) {
|
|
4358
|
-
if (latestQueries.get(failureKey) === queryId)
|
|
4636
|
+
if (queryId !== void 0 && latestQueries.get(failureKey) === queryId) {
|
|
4637
|
+
latestQueries.delete(failureKey);
|
|
4638
|
+
}
|
|
4359
4639
|
return queryAdapterState(
|
|
4360
4640
|
ctx,
|
|
4361
4641
|
adapter,
|
|
@@ -4371,7 +4651,7 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4371
4651
|
for (const [key, failure] of failureBackoff) {
|
|
4372
4652
|
if (failure.until <= now) failureBackoff.delete(key);
|
|
4373
4653
|
}
|
|
4374
|
-
if (latestQueries.get(failureKey) === queryId) {
|
|
4654
|
+
if (queryId === void 0 || latestQueries.get(failureKey) === queryId) {
|
|
4375
4655
|
setBoundedMap(
|
|
4376
4656
|
failureBackoff,
|
|
4377
4657
|
failureKey,
|
|
@@ -4382,15 +4662,52 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4382
4662
|
return {
|
|
4383
4663
|
state: {
|
|
4384
4664
|
providerId: adapter.id,
|
|
4385
|
-
providerName
|
|
4665
|
+
providerName,
|
|
4386
4666
|
displayState,
|
|
4387
4667
|
status: "query-failed",
|
|
4388
4668
|
message
|
|
4389
4669
|
},
|
|
4390
|
-
fingerprint: auth.fingerprint
|
|
4670
|
+
fingerprint: auth.fingerprint,
|
|
4671
|
+
rememberedTargetId: expectedTargetId
|
|
4391
4672
|
};
|
|
4392
4673
|
}
|
|
4393
4674
|
};
|
|
4675
|
+
const loadTargetChoices = async (ctx, adapter, signal) => {
|
|
4676
|
+
if (!adapter.targets) throw new Error("Provider does not support usage targets.");
|
|
4677
|
+
const expectedSessionGeneration = sessionGeneration;
|
|
4678
|
+
const expectedSessionId = ctx.sessionManager.getSessionId();
|
|
4679
|
+
const expectedModel = modelIdentity(ctx.model);
|
|
4680
|
+
const expectedTargetId = settingsRuntime.get().settings.selectedTargets[adapter.id];
|
|
4681
|
+
const deadlineAt = Date.now() + DEFAULT_TIMEOUT_MS;
|
|
4682
|
+
const changed = () => expectedSessionGeneration !== sessionGeneration || ctx.sessionManager.getSessionId() !== expectedSessionId || modelIdentity(ctx.model) !== expectedModel || settingsRuntime.get().settings.selectedTargets[adapter.id] !== expectedTargetId;
|
|
4683
|
+
const auth = await awaitWithDeadline(
|
|
4684
|
+
resolveUsageAuth(ctx, adapter, void 0, credentialReader, credentialCandidates),
|
|
4685
|
+
signal,
|
|
4686
|
+
Math.max(1, deadlineAt - Date.now()),
|
|
4687
|
+
`resolving ${providerDisplayName(ctx, adapter.id)} runtime auth`
|
|
4688
|
+
);
|
|
4689
|
+
if (!auth || signal.aborted || changed()) throw abortError();
|
|
4690
|
+
const guard = async () => {
|
|
4691
|
+
if (signal.aborted || changed()) throw abortError();
|
|
4692
|
+
const revalidated = await awaitWithDeadline(
|
|
4693
|
+
resolveUsageAuth(ctx, adapter, void 0, credentialReader, credentialCandidates),
|
|
4694
|
+
signal,
|
|
4695
|
+
Math.max(1, deadlineAt - Date.now()),
|
|
4696
|
+
`revalidating ${providerDisplayName(ctx, adapter.id)} runtime auth`
|
|
4697
|
+
);
|
|
4698
|
+
if (signal.aborted || changed() || revalidated?.fingerprint !== auth.fingerprint) {
|
|
4699
|
+
throw abortError();
|
|
4700
|
+
}
|
|
4701
|
+
};
|
|
4702
|
+
const choices = await listUsageTargets(
|
|
4703
|
+
adapter,
|
|
4704
|
+
auth,
|
|
4705
|
+
signal,
|
|
4706
|
+
Math.max(1, deadlineAt - Date.now()),
|
|
4707
|
+
guard
|
|
4708
|
+
);
|
|
4709
|
+
return { choices, fingerprint: auth.fingerprint };
|
|
4710
|
+
};
|
|
4394
4711
|
const queryCurrentState = async (ctx, model, force, signal) => {
|
|
4395
4712
|
const adapter = adapterForProvider(model?.provider);
|
|
4396
4713
|
if (!adapter) {
|
|
@@ -4446,7 +4763,9 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4446
4763
|
const startStatusRefresh = (ctx, model, force) => {
|
|
4447
4764
|
void refreshCurrentStatus(ctx, model, force).catch((error) => {
|
|
4448
4765
|
if (isStaleExtensionContextError(error) || isAbortError3(error)) return;
|
|
4449
|
-
|
|
4766
|
+
const message = errorMessage(error);
|
|
4767
|
+
console.error("[pi-usage] refresh failed:", message);
|
|
4768
|
+
safeSetStatus(ctx, `usage err: ${message.slice(0, 50)}`);
|
|
4450
4769
|
});
|
|
4451
4770
|
};
|
|
4452
4771
|
const runMenuOperation = async (ctx, label, parentSignal, operation, cancellable = true) => {
|
|
@@ -4474,6 +4793,8 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4474
4793
|
return false;
|
|
4475
4794
|
}
|
|
4476
4795
|
const adapter = adapterForProvider(model?.provider);
|
|
4796
|
+
const selectionStillCurrent = !adapter?.targets || settingsRuntime.get().settings.selectedTargets[adapter.id] === outcome.rememberedTargetId;
|
|
4797
|
+
if (!selectionStillCurrent) return false;
|
|
4477
4798
|
if (outcome.authState === "unavailable") {
|
|
4478
4799
|
if (!adapter) return false;
|
|
4479
4800
|
try {
|
|
@@ -4483,7 +4804,7 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4483
4804
|
DEFAULT_TIMEOUT_MS,
|
|
4484
4805
|
`revalidating ${adapter.displayName} runtime auth`
|
|
4485
4806
|
);
|
|
4486
|
-
return generation === statusGeneration && modelIdentity(ctx.model) === modelIdentity(model) && auth === void 0;
|
|
4807
|
+
return generation === statusGeneration && modelIdentity(ctx.model) === modelIdentity(model) && (!adapter.targets || settingsRuntime.get().settings.selectedTargets[adapter.id] === outcome.rememberedTargetId) && auth === void 0;
|
|
4487
4808
|
} catch (error) {
|
|
4488
4809
|
if (isAbortError3(error) || isStaleExtensionContextError(error)) throw error;
|
|
4489
4810
|
return false;
|
|
@@ -4498,7 +4819,7 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4498
4819
|
DEFAULT_TIMEOUT_MS,
|
|
4499
4820
|
`revalidating ${adapter.displayName} runtime auth`
|
|
4500
4821
|
);
|
|
4501
|
-
return generation === statusGeneration && modelIdentity(ctx.model) === modelIdentity(model) && auth?.fingerprint === outcome.fingerprint;
|
|
4822
|
+
return generation === statusGeneration && modelIdentity(ctx.model) === modelIdentity(model) && (!adapter.targets || settingsRuntime.get().settings.selectedTargets[adapter.id] === outcome.rememberedTargetId) && auth?.fingerprint === outcome.fingerprint;
|
|
4502
4823
|
} catch (error) {
|
|
4503
4824
|
if (isAbortError3(error) || isStaleExtensionContextError(error)) throw error;
|
|
4504
4825
|
return false;
|
|
@@ -4554,6 +4875,88 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4554
4875
|
let redemptionId;
|
|
4555
4876
|
let resetOutcome;
|
|
4556
4877
|
let resetFailure;
|
|
4878
|
+
const actionableTargetState = () => {
|
|
4879
|
+
if (visibleStates.length !== 1) return void 0;
|
|
4880
|
+
const state = visibleStates[0];
|
|
4881
|
+
return state && (state.status === "ready" || state.status === "selection-required") && adapterForProvider(state.providerId)?.targets ? state : void 0;
|
|
4882
|
+
};
|
|
4883
|
+
const promptForTarget = async (state, stateFingerprint) => {
|
|
4884
|
+
const adapter = adapterForProvider(state.providerId);
|
|
4885
|
+
if (!adapter?.targets) return false;
|
|
4886
|
+
const expectedRememberedTargetId = settingsRuntime.get().settings.selectedTargets[adapter.id];
|
|
4887
|
+
const snapshot = state.status === "selection-required" && stateFingerprint ? { choices: state.choices, fingerprint: stateFingerprint } : await runMenuOperation(
|
|
4888
|
+
ctx,
|
|
4889
|
+
`Loading ${adapter.targets.pluralLabel}\u2026`,
|
|
4890
|
+
controller.signal,
|
|
4891
|
+
(signal) => loadTargetChoices(ctx, adapter, signal)
|
|
4892
|
+
);
|
|
4893
|
+
if (!snapshot || controller.signal.aborted || statusGeneration !== menuGeneration) {
|
|
4894
|
+
return false;
|
|
4895
|
+
}
|
|
4896
|
+
const selectOptions = createUsageTargetSelectOptions(snapshot.choices);
|
|
4897
|
+
const selected = await ctx.ui.select(
|
|
4898
|
+
`Select ${adapter.targets.singularLabel} for ${providerDisplayName(ctx, adapter.id)}`,
|
|
4899
|
+
[...selectOptions.options],
|
|
4900
|
+
{ signal: controller.signal }
|
|
4901
|
+
);
|
|
4902
|
+
if (selected === void 0 || controller.signal.aborted || statusGeneration !== menuGeneration || settingsRuntime.get().settings.selectedTargets[adapter.id] !== expectedRememberedTargetId) {
|
|
4903
|
+
return false;
|
|
4904
|
+
}
|
|
4905
|
+
const targetId = selectOptions.targetIdFor(selected);
|
|
4906
|
+
if (!targetId) return false;
|
|
4907
|
+
const revalidated = await runMenuOperation(
|
|
4908
|
+
ctx,
|
|
4909
|
+
`Revalidating ${adapter.targets.singularLabel}\u2026`,
|
|
4910
|
+
controller.signal,
|
|
4911
|
+
(signal) => loadTargetChoices(ctx, adapter, signal)
|
|
4912
|
+
);
|
|
4913
|
+
if (!revalidated || controller.signal.aborted || statusGeneration !== menuGeneration || settingsRuntime.get().settings.selectedTargets[adapter.id] !== expectedRememberedTargetId) {
|
|
4914
|
+
return false;
|
|
4915
|
+
}
|
|
4916
|
+
if (revalidated.fingerprint !== snapshot.fingerprint || !revalidated.choices.some((choice) => choice.id === targetId)) {
|
|
4917
|
+
ctx.ui.notify(
|
|
4918
|
+
`${providerDisplayName(ctx, adapter.id)} ${adapter.targets.pluralLabel} changed; choose again.`,
|
|
4919
|
+
"warning"
|
|
4920
|
+
);
|
|
4921
|
+
return false;
|
|
4922
|
+
}
|
|
4923
|
+
let saved;
|
|
4924
|
+
try {
|
|
4925
|
+
saved = await runMenuOperation(
|
|
4926
|
+
ctx,
|
|
4927
|
+
`Saving ${adapter.targets.singularLabel}\u2026`,
|
|
4928
|
+
controller.signal,
|
|
4929
|
+
(signal) => settingsRuntime.updateSelectedTarget(adapter.id, targetId, signal, async () => {
|
|
4930
|
+
let published;
|
|
4931
|
+
try {
|
|
4932
|
+
published = await loadTargetChoices(ctx, adapter, signal);
|
|
4933
|
+
} catch (error) {
|
|
4934
|
+
if (signal.aborted || controller.signal.aborted || statusGeneration !== menuGeneration || isStaleExtensionContextError(error)) {
|
|
4935
|
+
throw error;
|
|
4936
|
+
}
|
|
4937
|
+
throw new UsageTargetSelectionChangedError();
|
|
4938
|
+
}
|
|
4939
|
+
if (published.fingerprint !== snapshot.fingerprint || !published.choices.some((choice) => choice.id === targetId)) {
|
|
4940
|
+
throw new UsageTargetSelectionChangedError();
|
|
4941
|
+
}
|
|
4942
|
+
})
|
|
4943
|
+
);
|
|
4944
|
+
} catch (error) {
|
|
4945
|
+
if (error instanceof UsageTargetSelectionChangedError) {
|
|
4946
|
+
ctx.ui.notify(
|
|
4947
|
+
`${providerDisplayName(ctx, adapter.id)} ${adapter.targets.pluralLabel} changed; choose again.`,
|
|
4948
|
+
"warning"
|
|
4949
|
+
);
|
|
4950
|
+
return false;
|
|
4951
|
+
}
|
|
4952
|
+
throw error;
|
|
4953
|
+
}
|
|
4954
|
+
if (!saved || controller.signal.aborted || statusGeneration !== menuGeneration) {
|
|
4955
|
+
return false;
|
|
4956
|
+
}
|
|
4957
|
+
invalidateProviderState(adapter.id);
|
|
4958
|
+
return true;
|
|
4959
|
+
};
|
|
4557
4960
|
const { defineMenu, runMenu } = await import("@narumitw/pi-tui-kit");
|
|
4558
4961
|
if (controller.signal.aborted || statusGeneration !== menuGeneration) return;
|
|
4559
4962
|
const menu = defineMenu({
|
|
@@ -4561,6 +4964,8 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4561
4964
|
screens: {
|
|
4562
4965
|
main: () => {
|
|
4563
4966
|
const fastAvailability = fastRuntime.availability(ctx.model);
|
|
4967
|
+
const targetState = actionableTargetState();
|
|
4968
|
+
const targetAdapter = adapterForProvider(targetState?.providerId);
|
|
4564
4969
|
const fastLines = fastAvailability.kind === "available" ? [`Fast mode: ${fastAvailability.enabled ? "On" : "Off"}`, FAST_USAGE_WARNING] : fastAvailability.kind === "unavailable" ? [`Fast mode: Unavailable \xB7 ${fastAvailability.reason}`] : [];
|
|
4565
4970
|
return {
|
|
4566
4971
|
kind: "actions",
|
|
@@ -4569,6 +4974,13 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4569
4974
|
items: [
|
|
4570
4975
|
{ id: "refresh", label: REFRESH_CURRENT, action: "refresh" },
|
|
4571
4976
|
{ id: "settings", label: SETTINGS, action: "settings" },
|
|
4977
|
+
...targetState && targetAdapter?.targets ? [
|
|
4978
|
+
{
|
|
4979
|
+
id: "target",
|
|
4980
|
+
label: `${targetState.status === "selection-required" ? "Select" : "Change"} ${targetAdapter.targets.singularLabel}\u2026`,
|
|
4981
|
+
action: "target"
|
|
4982
|
+
}
|
|
4983
|
+
] : [],
|
|
4572
4984
|
...fastAvailability.kind === "available" ? [
|
|
4573
4985
|
{
|
|
4574
4986
|
id: "toggle-fast",
|
|
@@ -4599,7 +5011,7 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4599
5011
|
title: "Select a configured provider",
|
|
4600
5012
|
items: configuredAdapters(ctx).filter((adapter) => adapter.id !== ctx.model?.provider).map((adapter) => ({
|
|
4601
5013
|
id: adapter.id,
|
|
4602
|
-
label: adapter.
|
|
5014
|
+
label: providerDisplayName(ctx, adapter.id),
|
|
4603
5015
|
action: "provider"
|
|
4604
5016
|
})),
|
|
4605
5017
|
hint: "back"
|
|
@@ -4661,6 +5073,58 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4661
5073
|
})
|
|
4662
5074
|
},
|
|
4663
5075
|
actions: {
|
|
5076
|
+
target: async () => {
|
|
5077
|
+
const targetState = actionableTargetState();
|
|
5078
|
+
if (!targetState) return { kind: "rejected" };
|
|
5079
|
+
try {
|
|
5080
|
+
const stateFingerprint = targetState === current.state ? current.fingerprint : void 0;
|
|
5081
|
+
if (!await promptForTarget(targetState, stateFingerprint)) {
|
|
5082
|
+
return { kind: "stay" };
|
|
5083
|
+
}
|
|
5084
|
+
const adapter = adapterForProvider(targetState.providerId);
|
|
5085
|
+
if (!adapter) return { kind: "rejected" };
|
|
5086
|
+
if (targetState.providerId === ctx.model?.provider) {
|
|
5087
|
+
const refreshed = await queryStableCurrent(
|
|
5088
|
+
ctx,
|
|
5089
|
+
true,
|
|
5090
|
+
controller,
|
|
5091
|
+
`Checking ${providerDisplayName(ctx, adapter.id)} usage\u2026`
|
|
5092
|
+
);
|
|
5093
|
+
if (!refreshed) return { kind: "stay" };
|
|
5094
|
+
stableCurrent = refreshed;
|
|
5095
|
+
current = refreshed.outcome;
|
|
5096
|
+
visibleStates = [current.state];
|
|
5097
|
+
publishStableCurrent(ctx, refreshed);
|
|
5098
|
+
return { kind: "stay" };
|
|
5099
|
+
}
|
|
5100
|
+
const outcome = await runMenuOperation(
|
|
5101
|
+
ctx,
|
|
5102
|
+
`Checking ${providerDisplayName(ctx, adapter.id)} usage\u2026`,
|
|
5103
|
+
controller.signal,
|
|
5104
|
+
(signal) => queryAdapterState(ctx, adapter, "configured", true, signal)
|
|
5105
|
+
);
|
|
5106
|
+
if (!outcome) return { kind: "stay" };
|
|
5107
|
+
const revalidated = await queryStableCurrent(
|
|
5108
|
+
ctx,
|
|
5109
|
+
false,
|
|
5110
|
+
controller,
|
|
5111
|
+
"Revalidating current usage\u2026"
|
|
5112
|
+
);
|
|
5113
|
+
if (!revalidated) return { kind: "stay" };
|
|
5114
|
+
stableCurrent = revalidated;
|
|
5115
|
+
current = revalidated.outcome;
|
|
5116
|
+
visibleStates = [
|
|
5117
|
+
outcome.state.providerId === current.state.providerId ? current.state : { ...outcome.state, displayState: "configured" }
|
|
5118
|
+
];
|
|
5119
|
+
return { kind: "stay" };
|
|
5120
|
+
} catch (error) {
|
|
5121
|
+
if (isAbortError3(error) || isStaleExtensionContextError(error)) {
|
|
5122
|
+
return { kind: "stay" };
|
|
5123
|
+
}
|
|
5124
|
+
ctx.ui.notify(`Could not select target: ${errorMessage(error)}`, "error");
|
|
5125
|
+
return { kind: "stay" };
|
|
5126
|
+
}
|
|
5127
|
+
},
|
|
4664
5128
|
settings: async () => {
|
|
4665
5129
|
await showUsageSettings(
|
|
4666
5130
|
ctx,
|
|
@@ -4869,13 +5333,25 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4869
5333
|
(candidate) => candidate.id === itemId && candidate.id !== ctx.model?.provider
|
|
4870
5334
|
);
|
|
4871
5335
|
if (!adapter) return { kind: "back" };
|
|
4872
|
-
|
|
5336
|
+
let outcome = await runMenuOperation(
|
|
4873
5337
|
ctx,
|
|
4874
|
-
`Checking ${adapter.
|
|
5338
|
+
`Checking ${providerDisplayName(ctx, adapter.id)} usage\u2026`,
|
|
4875
5339
|
controller.signal,
|
|
4876
5340
|
(signal) => queryAdapterState(ctx, adapter, "configured", false, signal)
|
|
4877
5341
|
);
|
|
4878
5342
|
if (!outcome) return { kind: "back" };
|
|
5343
|
+
if (outcome.state.status === "selection-required") {
|
|
5344
|
+
if (!await promptForTarget(outcome.state, outcome.fingerprint)) {
|
|
5345
|
+
return { kind: "back" };
|
|
5346
|
+
}
|
|
5347
|
+
outcome = await runMenuOperation(
|
|
5348
|
+
ctx,
|
|
5349
|
+
`Checking ${providerDisplayName(ctx, adapter.id)} usage\u2026`,
|
|
5350
|
+
controller.signal,
|
|
5351
|
+
(signal) => queryAdapterState(ctx, adapter, "configured", true, signal)
|
|
5352
|
+
);
|
|
5353
|
+
if (!outcome) return { kind: "back" };
|
|
5354
|
+
}
|
|
4879
5355
|
const revalidated = await queryStableCurrent(
|
|
4880
5356
|
ctx,
|
|
4881
5357
|
false,
|
|
@@ -4918,7 +5394,7 @@ function usageExtension(pi, dependencies = {}) {
|
|
|
4918
5394
|
const adapter = adapters[index];
|
|
4919
5395
|
return {
|
|
4920
5396
|
providerId: adapter.id,
|
|
4921
|
-
providerName: adapter.
|
|
5397
|
+
providerName: providerDisplayName(ctx, adapter.id),
|
|
4922
5398
|
displayState: "configured",
|
|
4923
5399
|
status: "query-failed",
|
|
4924
5400
|
message: errorMessage(result.reason)
|
|
@@ -5032,15 +5508,19 @@ export {
|
|
|
5032
5508
|
codexFastStatusLabel,
|
|
5033
5509
|
consumeCodexResetCredit,
|
|
5034
5510
|
correctCodexFastMessageCost,
|
|
5511
|
+
createFireworksAdapter,
|
|
5035
5512
|
createUsageSettingsRuntime,
|
|
5513
|
+
createUsageTargetSelectOptions,
|
|
5036
5514
|
usageExtension as default,
|
|
5037
5515
|
errorMessage,
|
|
5038
5516
|
fingerprintResolvedAuth,
|
|
5039
5517
|
formatProviderStates,
|
|
5040
5518
|
formatUsageReport,
|
|
5041
5519
|
formatUsageStatusline,
|
|
5520
|
+
isBoundedTargetId,
|
|
5042
5521
|
isStaleExtensionContextError,
|
|
5043
5522
|
listCodexResetCredits,
|
|
5523
|
+
listUsageTargets,
|
|
5044
5524
|
loadUsageSettings,
|
|
5045
5525
|
miniMaxUsageKind,
|
|
5046
5526
|
normalizeBasetenBillingUsagePayload,
|
|
@@ -5056,6 +5536,7 @@ export {
|
|
|
5056
5536
|
normalizeOpenCodeZenPayload,
|
|
5057
5537
|
normalizeOpenRouterKeyPayload,
|
|
5058
5538
|
normalizeUsageSettings,
|
|
5539
|
+
normalizeUsageTargets,
|
|
5059
5540
|
normalizeVercelAIGatewayCreditsPayload,
|
|
5060
5541
|
normalizeXaiBillingPayload,
|
|
5061
5542
|
normalizeZaiQuotaPayload,
|
|
@@ -5065,6 +5546,7 @@ export {
|
|
|
5065
5546
|
redactUsageError,
|
|
5066
5547
|
resolveCodexResetAuth,
|
|
5067
5548
|
resolveUsageAuth,
|
|
5549
|
+
resolveUsageTarget,
|
|
5068
5550
|
rewriteCodexFastPayload,
|
|
5069
5551
|
runWithConcurrency,
|
|
5070
5552
|
sanitizeDisplayText,
|