agent-usage-all-in-one 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BUNDLED_DEPENDENCIES.json +4 -1
- package/BUNDLED_LICENSES.md +283 -0
- package/README.md +24 -12
- package/README.zh-CN.md +17 -10
- package/THIRD_PARTY_LICENSES.md +3 -1
- package/dist/cli.js +1125 -119
- package/dist/cli.js.map +1 -1
- package/dist/web/_app/immutable/assets/2.BWrFQDDF.css +1 -0
- package/dist/web/_app/immutable/chunks/3vuWay1s.js +3 -0
- package/dist/web/_app/immutable/chunks/CIxAYfti.js +1 -0
- package/dist/web/_app/immutable/chunks/DrMah8OK.js +1 -0
- package/dist/web/_app/immutable/entry/app.BJJ5ybn9.js +2 -0
- package/dist/web/_app/immutable/entry/start.C8pSj-JI.js +1 -0
- package/dist/web/_app/immutable/nodes/0.f03zL-fK.js +1 -0
- package/dist/web/_app/immutable/nodes/1.zxn5xzLP.js +1 -0
- package/dist/web/_app/immutable/nodes/2.CeZkCUZN.js +27 -0
- package/dist/web/_app/version.json +1 -1
- package/dist/web/brand/agent-usage-showcase.jpg +0 -0
- package/dist/web/brands/README.md +4 -4
- package/dist/web/index.html +30 -8
- package/docs/open-source.md +1 -1
- package/package.json +2 -1
- package/static/brand/agent-usage-showcase.jpg +0 -0
- package/static/brands/README.md +4 -4
- package/dist/web/_app/immutable/assets/2.Bb7EkOVL.css +0 -1
- package/dist/web/_app/immutable/chunks/Cui19qu3.js +0 -1
- package/dist/web/_app/immutable/chunks/DQaovcqE.js +0 -3
- package/dist/web/_app/immutable/entry/app.CpOB18I2.js +0 -2
- package/dist/web/_app/immutable/entry/start.Csw0voA2.js +0 -1
- package/dist/web/_app/immutable/nodes/0.Di1qFV6d.js +0 -1
- package/dist/web/_app/immutable/nodes/1.CVs4QxxY.js +0 -1
- package/dist/web/_app/immutable/nodes/2.ByyWXaMo.js +0 -31
- package/dist/web/brand/agent-usage-banner.svg +0 -27
- package/static/brand/agent-usage-banner.svg +0 -27
package/dist/cli.js
CHANGED
|
@@ -643,6 +643,41 @@ function xaiEntry(options) {
|
|
|
643
643
|
source: XAI_PRICING_SOURCE
|
|
644
644
|
};
|
|
645
645
|
}
|
|
646
|
+
function openCodeLocalHistoryEntries(entries) {
|
|
647
|
+
return entries.flatMap((entry) => {
|
|
648
|
+
const modelPrefix = openCodeLocalModelPrefix(entry);
|
|
649
|
+
if (!modelPrefix) return [];
|
|
650
|
+
const localModel = (model) => model.includes("/") ? model : `${modelPrefix}/${model}`;
|
|
651
|
+
const canonicalModel = localModel(entry.canonicalModel);
|
|
652
|
+
return [
|
|
653
|
+
{
|
|
654
|
+
...entry,
|
|
655
|
+
id: `opencode-local-${entry.id}`,
|
|
656
|
+
providerId: "opencode",
|
|
657
|
+
billingDomainId: "local-history",
|
|
658
|
+
canonicalModel,
|
|
659
|
+
aliases: [
|
|
660
|
+
...new Set(
|
|
661
|
+
[entry.canonicalModel, ...entry.aliases].map(localModel).filter((model) => model !== canonicalModel)
|
|
662
|
+
)
|
|
663
|
+
]
|
|
664
|
+
}
|
|
665
|
+
];
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
function openCodeLocalModelPrefix(entry) {
|
|
669
|
+
if (entry.providerId === "claude-code" && entry.billingDomainId === "subscription") {
|
|
670
|
+
return "anthropic";
|
|
671
|
+
}
|
|
672
|
+
if (entry.providerId === "codex" && entry.billingDomainId === "subscription") {
|
|
673
|
+
return "openai";
|
|
674
|
+
}
|
|
675
|
+
if (entry.providerId === "grok" && entry.billingDomainId === "xai-api") return "xai";
|
|
676
|
+
if (entry.providerId === "opencode-go" && entry.billingDomainId === "go-subscription") {
|
|
677
|
+
return "opencode-go";
|
|
678
|
+
}
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
646
681
|
function deriveRetailEquivalentCosts(snapshot2, catalog = ANTHROPIC_PRICING_CATALOG, calculatedAt = snapshot2.observedAt) {
|
|
647
682
|
const costs = [];
|
|
648
683
|
const decisions = [];
|
|
@@ -714,16 +749,15 @@ function priceObservation(providerId, observation, catalog, calculatedAt) {
|
|
|
714
749
|
ratePerMillion: entry.cacheWriteRatesPerMillion.oneHour
|
|
715
750
|
}
|
|
716
751
|
].filter((item) => item.tokens > 0) : null;
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
if (cacheWriteTokens > 0 && !exactCacheWriteItems) {
|
|
752
|
+
const cacheWriteFree = cacheWriteTokens > 0 && !exactCacheWriteItems && entry.ratesPerMillion["cache-write"] === null && !entry.cacheWriteRatesPerMillion;
|
|
753
|
+
const cacheWriteBillable = cacheWriteTokens > 0 && !exactCacheWriteItems && !cacheWriteFree;
|
|
754
|
+
if (cacheWriteBillable) {
|
|
721
755
|
billableTokens.push({ tokenKind: "cache-write", tokens: cacheWriteTokens });
|
|
722
756
|
}
|
|
723
757
|
const nonZero = billableTokens.filter((item) => item.tokens > 0);
|
|
724
758
|
if (nonZero.some((item) => entry.ratesPerMillion[item.tokenKind] === null)) {
|
|
725
759
|
return unavailable(
|
|
726
|
-
normalized.cacheWriteTokens > 0 ? "pricing-tier-ambiguous" : "token-kinds-incomplete"
|
|
760
|
+
normalized.cacheWriteTokens > 0 && !cacheWriteFree ? "pricing-tier-ambiguous" : "token-kinds-incomplete"
|
|
727
761
|
);
|
|
728
762
|
}
|
|
729
763
|
const lineItems = nonZero.map((item) => {
|
|
@@ -742,6 +776,14 @@ function priceObservation(providerId, observation, catalog, calculatedAt) {
|
|
|
742
776
|
}))
|
|
743
777
|
);
|
|
744
778
|
}
|
|
779
|
+
if (cacheWriteFree) {
|
|
780
|
+
lineItems.push({
|
|
781
|
+
tokenKind: "cache-write",
|
|
782
|
+
tokens: cacheWriteTokens,
|
|
783
|
+
ratePerMillion: 0,
|
|
784
|
+
amount: 0
|
|
785
|
+
});
|
|
786
|
+
}
|
|
745
787
|
const pricedTokens = lineItems.reduce((total, item) => total + item.tokens, 0);
|
|
746
788
|
if (pricedTokens !== normalized.recordedTokens) return unavailable("token-kinds-incomplete");
|
|
747
789
|
const amount = preciseMoney(lineItems.reduce((total, item) => total + item.amount, 0));
|
|
@@ -806,7 +848,7 @@ function matchContext(rule, observation) {
|
|
|
806
848
|
function preciseMoney(value) {
|
|
807
849
|
return Number(value.toFixed(12));
|
|
808
850
|
}
|
|
809
|
-
var ANTHROPIC_PRICING_CATALOG, OPENCODE_GO_SOURCE, XAI_PRICING_SOURCE, OPENAI_GPT_56_SOURCE, OPEN_CODE_GO_SOURCE_PATH, openCodeHistory, OPEN_CODE_BASE_HISTORY, OPEN_CODE_PRICE_HISTORY, OPEN_CODE_FLAT_MODELS, OPEN_CODE_CONTEXT_MODELS, DEEPSEEK_GO_MODELS, WEEKDAY_PEAK_RANGES, OFFICIAL_PRICING_CATALOG;
|
|
851
|
+
var ANTHROPIC_PRICING_CATALOG, OPENCODE_GO_SOURCE, XAI_PRICING_SOURCE, OPENAI_GPT_56_SOURCE, OPEN_CODE_GO_SOURCE_PATH, openCodeHistory, OPEN_CODE_BASE_HISTORY, OPEN_CODE_PRICE_HISTORY, OPEN_CODE_FLAT_MODELS, OPEN_CODE_CONTEXT_MODELS, DEEPSEEK_GO_MODELS, WEEKDAY_PEAK_RANGES, DIRECT_OFFICIAL_PRICING_CATALOG, OFFICIAL_PRICING_CATALOG;
|
|
810
852
|
var init_retail_pricing = __esm({
|
|
811
853
|
"src/core/retail-pricing.ts"() {
|
|
812
854
|
"use strict";
|
|
@@ -1004,7 +1046,7 @@ var init_retail_pricing = __esm({
|
|
|
1004
1046
|
{ startHour: 1, endHour: 4 },
|
|
1005
1047
|
{ startHour: 6, endHour: 10 }
|
|
1006
1048
|
];
|
|
1007
|
-
|
|
1049
|
+
DIRECT_OFFICIAL_PRICING_CATALOG = {
|
|
1008
1050
|
version: "2026-08-28-grok-4.6-build",
|
|
1009
1051
|
entries: [
|
|
1010
1052
|
...ANTHROPIC_PRICING_CATALOG.entries,
|
|
@@ -1139,6 +1181,389 @@ var init_retail_pricing = __esm({
|
|
|
1139
1181
|
})
|
|
1140
1182
|
]
|
|
1141
1183
|
};
|
|
1184
|
+
OFFICIAL_PRICING_CATALOG = {
|
|
1185
|
+
version: "2026-08-29-opencode-local-history",
|
|
1186
|
+
entries: [
|
|
1187
|
+
...DIRECT_OFFICIAL_PRICING_CATALOG.entries,
|
|
1188
|
+
...openCodeLocalHistoryEntries(DIRECT_OFFICIAL_PRICING_CATALOG.entries)
|
|
1189
|
+
]
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1194
|
+
// src/core/plan-pricing.ts
|
|
1195
|
+
function buildBillingPeriod(subscription, summary, comparisonCurrency, rates) {
|
|
1196
|
+
if (!summary) return null;
|
|
1197
|
+
const start = new Date(summary.start).getTime();
|
|
1198
|
+
const end = new Date(summary.end).getTime();
|
|
1199
|
+
const observedThrough = new Date(summary.observedThrough).getTime();
|
|
1200
|
+
const totalDays = (end - start) / MILLISECONDS_PER_DAY;
|
|
1201
|
+
const elapsedDays = Math.max(0, Math.min(observedThrough, end) - start) / MILLISECONDS_PER_DAY;
|
|
1202
|
+
const periodCost = planMoneyAmount(
|
|
1203
|
+
preciseAmount(subscription.amount),
|
|
1204
|
+
subscription.currency,
|
|
1205
|
+
comparisonCurrency,
|
|
1206
|
+
// The period's own rate evidence first, so a short rolling window cannot
|
|
1207
|
+
// leave a cycle amount unconverted.
|
|
1208
|
+
[...summary.retailEquivalent.exchangeRates, ...rates],
|
|
1209
|
+
summary.observedThrough
|
|
1210
|
+
);
|
|
1211
|
+
const retailAmount = summary.retailEquivalent.status === "available" ? summary.retailEquivalent.amount : null;
|
|
1212
|
+
const breakEvenRatio = periodCost.amount !== null && periodCost.amount > 0 && retailAmount !== null ? preciseAmount(retailAmount / periodCost.amount) : null;
|
|
1213
|
+
return {
|
|
1214
|
+
start: summary.start,
|
|
1215
|
+
end: summary.end,
|
|
1216
|
+
elapsedDays: preciseAmount(elapsedDays),
|
|
1217
|
+
totalDays: preciseAmount(totalDays),
|
|
1218
|
+
progress: totalDays > 0 ? preciseAmount(Math.min(1, elapsedDays / totalDays)) : 0,
|
|
1219
|
+
periodCost,
|
|
1220
|
+
recordedTokens: summary.observationCount > 0 ? summary.recordedTokens : null,
|
|
1221
|
+
retailEquivalent: summary.retailEquivalent,
|
|
1222
|
+
breakEvenRatio,
|
|
1223
|
+
ratioBound: ratioBoundFor(breakEvenRatio, summary.retailEquivalent.pricingCoverage)
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
function planCatalogEntry(catalog, id) {
|
|
1227
|
+
return catalog.entries.find((entry) => entry.id === id) ?? null;
|
|
1228
|
+
}
|
|
1229
|
+
function planCatalogEntriesForDomain(catalog, providerId, billingDomainId) {
|
|
1230
|
+
return catalog.entries.filter(
|
|
1231
|
+
(entry) => entry.providerId === providerId && entry.billingDomainId === billingDomainId
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
function addMonths(date, months) {
|
|
1235
|
+
const day = date.getUTCDate();
|
|
1236
|
+
const shifted = new Date(
|
|
1237
|
+
Date.UTC(
|
|
1238
|
+
date.getUTCFullYear(),
|
|
1239
|
+
date.getUTCMonth() + months,
|
|
1240
|
+
1,
|
|
1241
|
+
date.getUTCHours(),
|
|
1242
|
+
date.getUTCMinutes(),
|
|
1243
|
+
date.getUTCSeconds(),
|
|
1244
|
+
date.getUTCMilliseconds()
|
|
1245
|
+
)
|
|
1246
|
+
);
|
|
1247
|
+
const lastDayOfMonth = new Date(
|
|
1248
|
+
Date.UTC(shifted.getUTCFullYear(), shifted.getUTCMonth() + 1, 0)
|
|
1249
|
+
).getUTCDate();
|
|
1250
|
+
shifted.setUTCDate(Math.min(day, lastDayOfMonth));
|
|
1251
|
+
return shifted;
|
|
1252
|
+
}
|
|
1253
|
+
function billingPeriodContaining(anchorDate, billingPeriod, at) {
|
|
1254
|
+
const anchor = new Date(anchorDate.length === 10 ? `${anchorDate}T00:00:00.000Z` : anchorDate);
|
|
1255
|
+
if (Number.isNaN(anchor.getTime())) return null;
|
|
1256
|
+
const step = billingPeriod === "monthly" ? 1 : 12;
|
|
1257
|
+
const monthsApart = (at.getUTCFullYear() - anchor.getUTCFullYear()) * 12 + (at.getUTCMonth() - anchor.getUTCMonth());
|
|
1258
|
+
let index = Math.floor(monthsApart / step);
|
|
1259
|
+
let start = addMonths(anchor, index * step);
|
|
1260
|
+
while (start.getTime() > at.getTime()) {
|
|
1261
|
+
index -= 1;
|
|
1262
|
+
start = addMonths(anchor, index * step);
|
|
1263
|
+
}
|
|
1264
|
+
let end = addMonths(anchor, (index + 1) * step);
|
|
1265
|
+
while (end.getTime() <= at.getTime()) {
|
|
1266
|
+
index += 1;
|
|
1267
|
+
start = end;
|
|
1268
|
+
end = addMonths(anchor, (index + 1) * step);
|
|
1269
|
+
}
|
|
1270
|
+
return { start, end };
|
|
1271
|
+
}
|
|
1272
|
+
function windowDays(start, end) {
|
|
1273
|
+
const span = new Date(end).getTime() - new Date(start).getTime();
|
|
1274
|
+
return span > 0 ? span / MILLISECONDS_PER_DAY : 0;
|
|
1275
|
+
}
|
|
1276
|
+
function proratePlanPrice(amount, billingPeriod, days) {
|
|
1277
|
+
return amount * days / PLAN_PERIOD_DAYS[billingPeriod];
|
|
1278
|
+
}
|
|
1279
|
+
function convertPlanAmount(amount, currency, comparisonCurrency, rates, end) {
|
|
1280
|
+
if (currency.toUpperCase() === comparisonCurrency.toUpperCase()) {
|
|
1281
|
+
return { amount, reason: null, rate: null };
|
|
1282
|
+
}
|
|
1283
|
+
const rate = rates.find(
|
|
1284
|
+
(candidate) => candidate.baseCurrency.toUpperCase() === currency.toUpperCase() && candidate.quoteCurrency.toUpperCase() === comparisonCurrency.toUpperCase()
|
|
1285
|
+
);
|
|
1286
|
+
if (!rate) return { amount: null, reason: "missing-rate", rate: null };
|
|
1287
|
+
if (new Date(end).getTime() - new Date(rate.observedAt).getTime() > STALE_RATE_MILLISECONDS) {
|
|
1288
|
+
return { amount: null, reason: "stale-rate", rate };
|
|
1289
|
+
}
|
|
1290
|
+
return { amount: amount * rate.rate, reason: null, rate };
|
|
1291
|
+
}
|
|
1292
|
+
function preciseAmount(value) {
|
|
1293
|
+
return Number(value.toPrecision(12));
|
|
1294
|
+
}
|
|
1295
|
+
function planMoneyAmount(nativeAmount, nativeCurrency, comparisonCurrency, rates, end) {
|
|
1296
|
+
const converted = convertPlanAmount(nativeAmount, nativeCurrency, comparisonCurrency, rates, end);
|
|
1297
|
+
return {
|
|
1298
|
+
status: converted.amount === null ? "unavailable" : "available",
|
|
1299
|
+
amount: converted.amount === null ? null : preciseAmount(converted.amount),
|
|
1300
|
+
nativeAmount,
|
|
1301
|
+
nativeCurrency,
|
|
1302
|
+
comparisonCurrency,
|
|
1303
|
+
conversionUnavailableReason: converted.reason,
|
|
1304
|
+
exchangeRates: converted.rate ? [converted.rate] : []
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
function ratioBoundFor(ratio, pricingCoverage) {
|
|
1308
|
+
if (ratio === null) return "unavailable";
|
|
1309
|
+
return pricingCoverage !== null && pricingCoverage < 0.999 ? "lower" : "exact";
|
|
1310
|
+
}
|
|
1311
|
+
function unitPricePerMillion(amount, tokens) {
|
|
1312
|
+
if (amount === null || tokens <= 0) return null;
|
|
1313
|
+
return preciseAmount(amount / (tokens / 1e6));
|
|
1314
|
+
}
|
|
1315
|
+
function buildWorkbenchPlanValue(options) {
|
|
1316
|
+
const { comparisonCurrency, start, end, rates } = options;
|
|
1317
|
+
const days = windowDays(start, end);
|
|
1318
|
+
const subscriptionByDomain = new Map(
|
|
1319
|
+
options.subscriptions.map((subscription) => [
|
|
1320
|
+
`${subscription.providerId}:${subscription.billingDomainId}`,
|
|
1321
|
+
subscription
|
|
1322
|
+
])
|
|
1323
|
+
);
|
|
1324
|
+
const entries = [];
|
|
1325
|
+
const metered = [];
|
|
1326
|
+
const unconfigured = [];
|
|
1327
|
+
for (const domain of options.domains) {
|
|
1328
|
+
const subscription = subscriptionByDomain.get(`${domain.providerId}:${domain.billingDomainId}`);
|
|
1329
|
+
if (!subscription) {
|
|
1330
|
+
if (domain.actualCost.records > 0) {
|
|
1331
|
+
metered.push({
|
|
1332
|
+
providerId: domain.providerId,
|
|
1333
|
+
providerDisplayName: domain.providerDisplayName,
|
|
1334
|
+
billingDomainId: domain.billingDomainId,
|
|
1335
|
+
billingDomainDisplayName: domain.billingDomainDisplayName,
|
|
1336
|
+
recordedTokens: domain.observationCount > 0 ? domain.recordedTokens : null,
|
|
1337
|
+
actualCost: domain.actualCost,
|
|
1338
|
+
retailEquivalent: domain.retailEquivalent
|
|
1339
|
+
});
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
if (domain.observationCount > 0) {
|
|
1343
|
+
unconfigured.push({
|
|
1344
|
+
providerId: domain.providerId,
|
|
1345
|
+
providerDisplayName: domain.providerDisplayName,
|
|
1346
|
+
billingDomainId: domain.billingDomainId,
|
|
1347
|
+
billingDomainDisplayName: domain.billingDomainDisplayName,
|
|
1348
|
+
recordedTokens: domain.recordedTokens
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
const windowPlanCost = planMoneyAmount(
|
|
1354
|
+
preciseAmount(proratePlanPrice(subscription.amount, subscription.billingPeriod, days)),
|
|
1355
|
+
subscription.currency,
|
|
1356
|
+
comparisonCurrency,
|
|
1357
|
+
rates,
|
|
1358
|
+
end
|
|
1359
|
+
);
|
|
1360
|
+
const retail = domain.retailEquivalent;
|
|
1361
|
+
const retailAmount = retail.status === "available" ? retail.amount : null;
|
|
1362
|
+
const valueRatio = windowPlanCost.amount !== null && windowPlanCost.amount > 0 && retailAmount !== null ? preciseAmount(retailAmount / windowPlanCost.amount) : null;
|
|
1363
|
+
const ratioBound = ratioBoundFor(valueRatio, retail.pricingCoverage);
|
|
1364
|
+
const billingPeriod = buildBillingPeriod(
|
|
1365
|
+
subscription,
|
|
1366
|
+
options.billingPeriods?.get(`${domain.providerId}:${domain.billingDomainId}`),
|
|
1367
|
+
comparisonCurrency,
|
|
1368
|
+
rates
|
|
1369
|
+
);
|
|
1370
|
+
entries.push({
|
|
1371
|
+
providerId: domain.providerId,
|
|
1372
|
+
providerDisplayName: domain.providerDisplayName,
|
|
1373
|
+
billingDomainId: domain.billingDomainId,
|
|
1374
|
+
billingDomainDisplayName: domain.billingDomainDisplayName,
|
|
1375
|
+
includedInHeadline: domain.includedInHeadline,
|
|
1376
|
+
plan: {
|
|
1377
|
+
planId: subscription.planId,
|
|
1378
|
+
displayName: subscription.displayName,
|
|
1379
|
+
amount: subscription.amount,
|
|
1380
|
+
currency: subscription.currency,
|
|
1381
|
+
billingPeriod: subscription.billingPeriod,
|
|
1382
|
+
anchorDate: subscription.anchorDate,
|
|
1383
|
+
priceSource: subscription.priceSource,
|
|
1384
|
+
updatedAt: subscription.updatedAt
|
|
1385
|
+
},
|
|
1386
|
+
windowDays: preciseAmount(days),
|
|
1387
|
+
windowPlanCost,
|
|
1388
|
+
billingPeriod,
|
|
1389
|
+
recordedTokens: domain.observationCount > 0 ? domain.recordedTokens : null,
|
|
1390
|
+
retailEquivalent: retail,
|
|
1391
|
+
valueRatio,
|
|
1392
|
+
ratioBound,
|
|
1393
|
+
status: valueRatio === null ? "unavailable" : ratioBound === "lower" ? "partial" : "available",
|
|
1394
|
+
effectiveUnitPrice: unitPricePerMillion(windowPlanCost.amount, domain.recordedTokens),
|
|
1395
|
+
retailUnitPrice: unitPricePerMillion(retailAmount, retail.pricedTokens),
|
|
1396
|
+
pricingCoverage: retail.pricingCoverage,
|
|
1397
|
+
authorities: domain.authorities,
|
|
1398
|
+
lastObservedAt: domain.lastObservedAt
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
const rank = (entry) => entry.valueRatio ?? -1;
|
|
1402
|
+
entries.sort(
|
|
1403
|
+
(left, right) => rank(right) - rank(left) || `${left.providerId}:${left.billingDomainId}`.localeCompare(
|
|
1404
|
+
`${right.providerId}:${right.billingDomainId}`
|
|
1405
|
+
)
|
|
1406
|
+
);
|
|
1407
|
+
metered.sort(
|
|
1408
|
+
(left, right) => `${left.providerId}:${left.billingDomainId}`.localeCompare(
|
|
1409
|
+
`${right.providerId}:${right.billingDomainId}`
|
|
1410
|
+
)
|
|
1411
|
+
);
|
|
1412
|
+
unconfigured.sort(
|
|
1413
|
+
(left, right) => right.recordedTokens - left.recordedTokens || `${left.providerId}:${left.billingDomainId}`.localeCompare(
|
|
1414
|
+
`${right.providerId}:${right.billingDomainId}`
|
|
1415
|
+
)
|
|
1416
|
+
);
|
|
1417
|
+
return {
|
|
1418
|
+
windowDays: preciseAmount(days),
|
|
1419
|
+
comparisonCurrency,
|
|
1420
|
+
catalogVersion: SUBSCRIPTION_PLAN_CATALOG.version,
|
|
1421
|
+
entries,
|
|
1422
|
+
meteredDomains: metered,
|
|
1423
|
+
unconfiguredDomains: unconfigured
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
var PLAN_PERIOD_DAYS, MILLISECONDS_PER_DAY, STALE_RATE_MILLISECONDS, SUBSCRIPTION_PLAN_CATALOG;
|
|
1427
|
+
var init_plan_pricing = __esm({
|
|
1428
|
+
"src/core/plan-pricing.ts"() {
|
|
1429
|
+
"use strict";
|
|
1430
|
+
PLAN_PERIOD_DAYS = {
|
|
1431
|
+
monthly: 365.25 / 12,
|
|
1432
|
+
annual: 365.25
|
|
1433
|
+
};
|
|
1434
|
+
MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
1435
|
+
STALE_RATE_MILLISECONDS = 7 * MILLISECONDS_PER_DAY;
|
|
1436
|
+
SUBSCRIPTION_PLAN_CATALOG = {
|
|
1437
|
+
version: "2026-08-30",
|
|
1438
|
+
entries: [
|
|
1439
|
+
{
|
|
1440
|
+
id: "claude-pro-monthly",
|
|
1441
|
+
providerId: "claude-code",
|
|
1442
|
+
billingDomainId: "subscription",
|
|
1443
|
+
displayName: "Claude Pro",
|
|
1444
|
+
amount: 20,
|
|
1445
|
+
currency: "USD",
|
|
1446
|
+
billingPeriod: "monthly",
|
|
1447
|
+
source: {
|
|
1448
|
+
title: "Claude plans and pricing",
|
|
1449
|
+
url: "https://claude.com/pricing",
|
|
1450
|
+
retrievedAt: "2026-08-30"
|
|
1451
|
+
}
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
id: "claude-pro-annual",
|
|
1455
|
+
providerId: "claude-code",
|
|
1456
|
+
billingDomainId: "subscription",
|
|
1457
|
+
displayName: "Claude Pro (annual billing)",
|
|
1458
|
+
amount: 17 * 12,
|
|
1459
|
+
currency: "USD",
|
|
1460
|
+
billingPeriod: "annual",
|
|
1461
|
+
source: {
|
|
1462
|
+
title: "Claude plans and pricing",
|
|
1463
|
+
url: "https://claude.com/pricing",
|
|
1464
|
+
retrievedAt: "2026-08-30"
|
|
1465
|
+
}
|
|
1466
|
+
},
|
|
1467
|
+
{
|
|
1468
|
+
id: "claude-max-5x",
|
|
1469
|
+
providerId: "claude-code",
|
|
1470
|
+
billingDomainId: "subscription",
|
|
1471
|
+
displayName: "Claude Max 5x",
|
|
1472
|
+
amount: 100,
|
|
1473
|
+
currency: "USD",
|
|
1474
|
+
billingPeriod: "monthly",
|
|
1475
|
+
source: {
|
|
1476
|
+
title: "What is the Max plan?",
|
|
1477
|
+
url: "https://support.claude.com/en/articles/11049741-what-is-the-max-plan",
|
|
1478
|
+
retrievedAt: "2026-08-30"
|
|
1479
|
+
}
|
|
1480
|
+
},
|
|
1481
|
+
{
|
|
1482
|
+
id: "claude-max-20x",
|
|
1483
|
+
providerId: "claude-code",
|
|
1484
|
+
billingDomainId: "subscription",
|
|
1485
|
+
displayName: "Claude Max 20x",
|
|
1486
|
+
amount: 200,
|
|
1487
|
+
currency: "USD",
|
|
1488
|
+
billingPeriod: "monthly",
|
|
1489
|
+
source: {
|
|
1490
|
+
title: "What is the Max plan?",
|
|
1491
|
+
url: "https://support.claude.com/en/articles/11049741-what-is-the-max-plan",
|
|
1492
|
+
retrievedAt: "2026-08-30"
|
|
1493
|
+
}
|
|
1494
|
+
},
|
|
1495
|
+
{
|
|
1496
|
+
id: "chatgpt-plus",
|
|
1497
|
+
providerId: "codex",
|
|
1498
|
+
billingDomainId: "subscription",
|
|
1499
|
+
displayName: "ChatGPT Plus",
|
|
1500
|
+
amount: 20,
|
|
1501
|
+
currency: "USD",
|
|
1502
|
+
billingPeriod: "monthly",
|
|
1503
|
+
source: {
|
|
1504
|
+
title: "ChatGPT pricing",
|
|
1505
|
+
url: "https://chatgpt.com/pricing/",
|
|
1506
|
+
retrievedAt: "2026-08-30"
|
|
1507
|
+
}
|
|
1508
|
+
},
|
|
1509
|
+
{
|
|
1510
|
+
id: "chatgpt-pro-100",
|
|
1511
|
+
providerId: "codex",
|
|
1512
|
+
billingDomainId: "subscription",
|
|
1513
|
+
displayName: "ChatGPT Pro (5x)",
|
|
1514
|
+
amount: 100,
|
|
1515
|
+
currency: "USD",
|
|
1516
|
+
billingPeriod: "monthly",
|
|
1517
|
+
source: {
|
|
1518
|
+
title: "About ChatGPT Pro tiers",
|
|
1519
|
+
url: "https://help.openai.com/en/articles/9793128-about-chatgpt-pro-tiers",
|
|
1520
|
+
retrievedAt: "2026-08-30"
|
|
1521
|
+
}
|
|
1522
|
+
},
|
|
1523
|
+
{
|
|
1524
|
+
id: "chatgpt-pro-200",
|
|
1525
|
+
providerId: "codex",
|
|
1526
|
+
billingDomainId: "subscription",
|
|
1527
|
+
displayName: "ChatGPT Pro (20x)",
|
|
1528
|
+
amount: 200,
|
|
1529
|
+
currency: "USD",
|
|
1530
|
+
billingPeriod: "monthly",
|
|
1531
|
+
source: {
|
|
1532
|
+
title: "About ChatGPT Pro tiers",
|
|
1533
|
+
url: "https://help.openai.com/en/articles/9793128-about-chatgpt-pro-tiers",
|
|
1534
|
+
retrievedAt: "2026-08-30"
|
|
1535
|
+
}
|
|
1536
|
+
},
|
|
1537
|
+
{
|
|
1538
|
+
id: "chatgpt-business-seat",
|
|
1539
|
+
providerId: "codex",
|
|
1540
|
+
billingDomainId: "subscription",
|
|
1541
|
+
displayName: "ChatGPT Business (one seat)",
|
|
1542
|
+
amount: 25,
|
|
1543
|
+
currency: "USD",
|
|
1544
|
+
billingPeriod: "monthly",
|
|
1545
|
+
source: {
|
|
1546
|
+
title: "ChatGPT Business pricing",
|
|
1547
|
+
url: "https://openai.com/business/chatgpt-pricing/",
|
|
1548
|
+
retrievedAt: "2026-08-30"
|
|
1549
|
+
}
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
id: "opencode-go-monthly",
|
|
1553
|
+
providerId: "opencode-go",
|
|
1554
|
+
billingDomainId: "go-subscription",
|
|
1555
|
+
displayName: "OpenCode Go",
|
|
1556
|
+
amount: 10,
|
|
1557
|
+
currency: "USD",
|
|
1558
|
+
billingPeriod: "monthly",
|
|
1559
|
+
source: {
|
|
1560
|
+
title: "OpenCode Go",
|
|
1561
|
+
url: "https://opencode.ai/go",
|
|
1562
|
+
retrievedAt: "2026-08-30"
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
]
|
|
1566
|
+
};
|
|
1142
1567
|
}
|
|
1143
1568
|
});
|
|
1144
1569
|
|
|
@@ -1247,6 +1672,12 @@ async function withTimeout(promise, timeoutMs, message) {
|
|
|
1247
1672
|
function notificationLevel(value) {
|
|
1248
1673
|
return value === "5" ? 2 : value === "20" ? 1 : 0;
|
|
1249
1674
|
}
|
|
1675
|
+
function pickAgentProviderIndex(overview) {
|
|
1676
|
+
return {
|
|
1677
|
+
generatedAt: overview.generatedAt,
|
|
1678
|
+
providers: overview.providers.filter((provider) => provider.id !== "opencode").map(({ id, displayName }) => ({ id, displayName }))
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1250
1681
|
var UsageApplication;
|
|
1251
1682
|
var init_usage_application = __esm({
|
|
1252
1683
|
"src/core/usage-application.ts"() {
|
|
@@ -1255,6 +1686,7 @@ var init_usage_application = __esm({
|
|
|
1255
1686
|
init_redaction();
|
|
1256
1687
|
init_usage_export();
|
|
1257
1688
|
init_retail_pricing();
|
|
1689
|
+
init_plan_pricing();
|
|
1258
1690
|
UsageApplication = class {
|
|
1259
1691
|
#repository;
|
|
1260
1692
|
#connectors;
|
|
@@ -1268,10 +1700,13 @@ var init_usage_application = __esm({
|
|
|
1268
1700
|
#notifier;
|
|
1269
1701
|
#startAtLoginManager;
|
|
1270
1702
|
#priceCatalog;
|
|
1703
|
+
#planCatalog;
|
|
1271
1704
|
#refreshPromise = null;
|
|
1272
1705
|
#refreshMode = null;
|
|
1273
1706
|
#backgroundPromise = null;
|
|
1274
1707
|
#queuedHardRebuildPromise = null;
|
|
1708
|
+
#queuedUserProcessingPromise = null;
|
|
1709
|
+
#userInitiatedProcessing = false;
|
|
1275
1710
|
#databaseWriteQueue = Promise.resolve();
|
|
1276
1711
|
#processingStatus;
|
|
1277
1712
|
constructor(options) {
|
|
@@ -1287,6 +1722,7 @@ var init_usage_application = __esm({
|
|
|
1287
1722
|
this.#notifier = options.notifier;
|
|
1288
1723
|
this.#startAtLoginManager = options.startAtLoginManager;
|
|
1289
1724
|
this.#priceCatalog = options.priceCatalog === void 0 ? OFFICIAL_PRICING_CATALOG : options.priceCatalog;
|
|
1725
|
+
this.#planCatalog = options.planCatalog ?? SUBSCRIPTION_PLAN_CATALOG;
|
|
1290
1726
|
this.#processingStatus = createProcessingStatus(this.#clock().toISOString(), false);
|
|
1291
1727
|
}
|
|
1292
1728
|
async #backfillRetailCosts(mode) {
|
|
@@ -1333,40 +1769,50 @@ var init_usage_application = __esm({
|
|
|
1333
1769
|
getProcessingStatus() {
|
|
1334
1770
|
return structuredClone(this.#processingStatus);
|
|
1335
1771
|
}
|
|
1336
|
-
startBackgroundProcessing() {
|
|
1337
|
-
return this.#startProcessing("incremental");
|
|
1772
|
+
startBackgroundProcessing(options = {}) {
|
|
1773
|
+
return this.#startProcessing("incremental", options.userInitiated === true);
|
|
1338
1774
|
}
|
|
1339
1775
|
startHardRebuild() {
|
|
1340
|
-
return this.#startProcessing("hard-rebuild");
|
|
1776
|
+
return this.#startProcessing("hard-rebuild", true);
|
|
1341
1777
|
}
|
|
1342
|
-
#startProcessing(mode) {
|
|
1778
|
+
#startProcessing(mode, userInitiated) {
|
|
1343
1779
|
if (this.#backgroundPromise) {
|
|
1344
1780
|
if (mode === "hard-rebuild" && !this.#processingStatus.hardRebuild) {
|
|
1345
1781
|
this.#queuedHardRebuildPromise ??= this.#backgroundPromise.then(
|
|
1346
|
-
() => this.#startProcessing("hard-rebuild"),
|
|
1347
|
-
() => this.#startProcessing("hard-rebuild")
|
|
1782
|
+
() => this.#startProcessing("hard-rebuild", true),
|
|
1783
|
+
() => this.#startProcessing("hard-rebuild", true)
|
|
1348
1784
|
).finally(() => {
|
|
1349
1785
|
this.#queuedHardRebuildPromise = null;
|
|
1350
1786
|
});
|
|
1351
1787
|
return this.#queuedHardRebuildPromise;
|
|
1352
1788
|
}
|
|
1789
|
+
if (userInitiated && !this.#userInitiatedProcessing) {
|
|
1790
|
+
this.#queuedUserProcessingPromise ??= this.#backgroundPromise.then(
|
|
1791
|
+
() => this.#startProcessing(mode, true),
|
|
1792
|
+
() => this.#startProcessing(mode, true)
|
|
1793
|
+
).finally(() => {
|
|
1794
|
+
this.#queuedUserProcessingPromise = null;
|
|
1795
|
+
});
|
|
1796
|
+
return this.#queuedUserProcessingPromise;
|
|
1797
|
+
}
|
|
1353
1798
|
return this.#backgroundPromise;
|
|
1354
1799
|
}
|
|
1800
|
+
this.#userInitiatedProcessing = userInitiated || mode === "hard-rebuild";
|
|
1355
1801
|
this.#processingStatus = createProcessingStatus(
|
|
1356
1802
|
this.#clock().toISOString(),
|
|
1357
1803
|
mode === "hard-rebuild"
|
|
1358
1804
|
);
|
|
1359
|
-
this.#backgroundPromise = this.#runProcessing(mode).finally(
|
|
1360
|
-
|
|
1361
|
-
|
|
1805
|
+
this.#backgroundPromise = this.#runProcessing(mode, this.#userInitiatedProcessing).finally(
|
|
1806
|
+
() => {
|
|
1807
|
+
this.#backgroundPromise = null;
|
|
1808
|
+
this.#userInitiatedProcessing = false;
|
|
1809
|
+
}
|
|
1810
|
+
);
|
|
1362
1811
|
return this.#backgroundPromise;
|
|
1363
1812
|
}
|
|
1364
|
-
async #runProcessing(mode) {
|
|
1813
|
+
async #runProcessing(mode, userInitiated) {
|
|
1365
1814
|
await this.#runModule("discovery", () => this.discoverConnectors().then(() => void 0));
|
|
1366
|
-
await this.#runModule(
|
|
1367
|
-
"usage",
|
|
1368
|
-
() => this.refresh({ userInitiated: mode === "hard-rebuild", mode })
|
|
1369
|
-
);
|
|
1815
|
+
await this.#runModule("usage", () => this.refresh({ userInitiated, mode }));
|
|
1370
1816
|
await this.#runModule(
|
|
1371
1817
|
"pricing",
|
|
1372
1818
|
() => this.#queueDatabaseWrite(async () => {
|
|
@@ -1577,6 +2023,83 @@ var init_usage_application = __esm({
|
|
|
1577
2023
|
connectors: this.#repository.getConnectorRuntimeStates()
|
|
1578
2024
|
};
|
|
1579
2025
|
}
|
|
2026
|
+
async getPlanSettings() {
|
|
2027
|
+
return {
|
|
2028
|
+
catalogVersion: this.#planCatalog.version,
|
|
2029
|
+
domains: this.#planEligibleDomains(),
|
|
2030
|
+
subscriptions: this.#repository.getPlanSubscriptions()
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
async updatePlanSubscription(input) {
|
|
2034
|
+
const domain = this.#planEligibleDomains().find(
|
|
2035
|
+
(candidate) => candidate.providerId === input.providerId && candidate.billingDomainId === input.billingDomainId
|
|
2036
|
+
);
|
|
2037
|
+
if (!domain) throw new Error("Unknown subscription billing domain");
|
|
2038
|
+
if (input.plan === null) {
|
|
2039
|
+
this.#repository.deletePlanSubscription(input.providerId, input.billingDomainId);
|
|
2040
|
+
return this.getPlanSettings();
|
|
2041
|
+
}
|
|
2042
|
+
const preset = input.plan.planId ? planCatalogEntry(this.#planCatalog, input.plan.planId) : null;
|
|
2043
|
+
if (input.plan.planId && !preset) throw new Error("Unknown plan preset");
|
|
2044
|
+
if (preset && (preset.providerId !== input.providerId || preset.billingDomainId !== input.billingDomainId)) {
|
|
2045
|
+
throw new Error("Plan preset belongs to another billing domain");
|
|
2046
|
+
}
|
|
2047
|
+
const amount = input.plan.amount ?? preset?.amount;
|
|
2048
|
+
if (amount === void 0) throw new Error("A plan price is required");
|
|
2049
|
+
if (!Number.isFinite(amount) || amount <= 0) {
|
|
2050
|
+
throw new Error("A plan price must be a positive amount");
|
|
2051
|
+
}
|
|
2052
|
+
const currency = (input.plan.currency ?? preset?.currency ?? "USD").toUpperCase();
|
|
2053
|
+
if (!/^[A-Z]{3}$/.test(currency)) throw new Error("A plan currency must be a 3-letter code");
|
|
2054
|
+
const billingPeriod = input.plan.billingPeriod ?? preset?.billingPeriod ?? "monthly";
|
|
2055
|
+
const anchorDate = input.plan.anchorDate ?? null;
|
|
2056
|
+
if (anchorDate !== null && Number.isNaN((/* @__PURE__ */ new Date(`${anchorDate}T00:00:00.000Z`)).getTime())) {
|
|
2057
|
+
throw new Error("A renewal date must be a calendar date");
|
|
2058
|
+
}
|
|
2059
|
+
const overridesPreset = preset !== null && (amount !== preset.amount || currency !== preset.currency.toUpperCase() || billingPeriod !== preset.billingPeriod);
|
|
2060
|
+
this.#repository.savePlanSubscription({
|
|
2061
|
+
providerId: input.providerId,
|
|
2062
|
+
billingDomainId: input.billingDomainId,
|
|
2063
|
+
planId: preset?.id ?? null,
|
|
2064
|
+
displayName: preset?.displayName ?? "",
|
|
2065
|
+
amount,
|
|
2066
|
+
currency,
|
|
2067
|
+
billingPeriod,
|
|
2068
|
+
anchorDate,
|
|
2069
|
+
priceSource: preset && !overridesPreset ? "catalog-preset" : "user-entered",
|
|
2070
|
+
updatedAt: this.#clock().toISOString()
|
|
2071
|
+
});
|
|
2072
|
+
return this.getPlanSettings();
|
|
2073
|
+
}
|
|
2074
|
+
/**
|
|
2075
|
+
* A billing domain can carry a plan price when its connector does not report
|
|
2076
|
+
* an actual metered charge. Metered domains keep their own billed amounts and
|
|
2077
|
+
* never receive a declared subscription price.
|
|
2078
|
+
*/
|
|
2079
|
+
#planEligibleDomains() {
|
|
2080
|
+
const domains = /* @__PURE__ */ new Map();
|
|
2081
|
+
for (const definition of this.#connectorDefinitions) {
|
|
2082
|
+
if (definition.expectedCoverage?.includes("actual-cost")) continue;
|
|
2083
|
+
const key = `${definition.target.provider.id}:${definition.target.billingDomain.id}`;
|
|
2084
|
+
if (domains.has(key)) continue;
|
|
2085
|
+
domains.set(key, {
|
|
2086
|
+
providerId: definition.target.provider.id,
|
|
2087
|
+
providerDisplayName: definition.target.provider.displayName,
|
|
2088
|
+
billingDomainId: definition.target.billingDomain.id,
|
|
2089
|
+
billingDomainDisplayName: definition.target.billingDomain.displayName,
|
|
2090
|
+
presets: planCatalogEntriesForDomain(
|
|
2091
|
+
this.#planCatalog,
|
|
2092
|
+
definition.target.provider.id,
|
|
2093
|
+
definition.target.billingDomain.id
|
|
2094
|
+
)
|
|
2095
|
+
});
|
|
2096
|
+
}
|
|
2097
|
+
return [...domains.values()].sort(
|
|
2098
|
+
(left, right) => `${left.providerId}:${left.billingDomainId}`.localeCompare(
|
|
2099
|
+
`${right.providerId}:${right.billingDomainId}`
|
|
2100
|
+
)
|
|
2101
|
+
);
|
|
2102
|
+
}
|
|
1580
2103
|
async #sendNotificationTransitions() {
|
|
1581
2104
|
if (!this.#notifier) return;
|
|
1582
2105
|
const overview = this.#repository.getOverview(this.#clock());
|
|
@@ -1725,6 +2248,14 @@ var init_usage_application = __esm({
|
|
|
1725
2248
|
async getOverview(query = {}) {
|
|
1726
2249
|
return this.#repository.getOverview(this.#clock(), query);
|
|
1727
2250
|
}
|
|
2251
|
+
async getAgentProviderIndex() {
|
|
2252
|
+
const now = this.#clock();
|
|
2253
|
+
return this.#repository.getAgentProviderIndex?.(now) ?? pickAgentProviderIndex(this.#repository.getOverview(now));
|
|
2254
|
+
}
|
|
2255
|
+
async getProviderOverview(providerId, query = {}) {
|
|
2256
|
+
const now = this.#clock();
|
|
2257
|
+
return this.#repository.getProviderOverview?.(now, providerId, query) ?? this.#repository.getOverview(now, query).providers.find((provider) => provider.id === providerId) ?? null;
|
|
2258
|
+
}
|
|
1728
2259
|
async exportUsage(request) {
|
|
1729
2260
|
const overview = this.#repository.getOverview(this.#clock(), request);
|
|
1730
2261
|
const accountIdentifiers = request.includeAccountIdentifiers ? this.#repository.getProviderAccountIdentifiers() : {};
|
|
@@ -1933,9 +2464,9 @@ var init_catalog = __esm({
|
|
|
1933
2464
|
},
|
|
1934
2465
|
{
|
|
1935
2466
|
id: "opencode-go",
|
|
1936
|
-
displayName: "OpenCode
|
|
2467
|
+
displayName: "OpenCode",
|
|
1937
2468
|
command: "opencode",
|
|
1938
|
-
permissionDescription: "Read
|
|
2469
|
+
permissionDescription: "Read local usage and optional Go account quota through official OpenCode surfaces.",
|
|
1939
2470
|
credentialOwner: "official-client",
|
|
1940
2471
|
experimental: false,
|
|
1941
2472
|
expectedCoverage: ["quota", "tokens", "history"],
|
|
@@ -1990,6 +2521,7 @@ function mapQuota(quota) {
|
|
|
1990
2521
|
billingDomainId: "subscription",
|
|
1991
2522
|
label: quota.label,
|
|
1992
2523
|
usedPercent: quota.usedPercent,
|
|
2524
|
+
windowDurationMinutes: quota.windowDurationMinutes,
|
|
1993
2525
|
resetsAt: quota.resetsAt,
|
|
1994
2526
|
authority: "official-client",
|
|
1995
2527
|
scope: "account-wide"
|
|
@@ -2294,6 +2826,7 @@ function parseClaudeUsageScreen(text, now) {
|
|
|
2294
2826
|
id,
|
|
2295
2827
|
label: displayLabel(heading),
|
|
2296
2828
|
usedPercent: Number(match[1]),
|
|
2829
|
+
windowDurationMinutes: windowDurationMinutes(heading),
|
|
2297
2830
|
resetsAt: resetLine ? parseReset(resetLine, now) : null
|
|
2298
2831
|
});
|
|
2299
2832
|
}
|
|
@@ -2326,6 +2859,11 @@ function displayLabel(heading) {
|
|
|
2326
2859
|
const weekly = heading.match(/^Weekly\s*[·—-]\s*(.+)$/i);
|
|
2327
2860
|
return weekly ? `Week \xB7 ${weekly[1]}` : heading;
|
|
2328
2861
|
}
|
|
2862
|
+
function windowDurationMinutes(heading) {
|
|
2863
|
+
if (/^(5[- ]hour limit|current session)$/i.test(heading)) return 300;
|
|
2864
|
+
if (/^(Current week\s*\(.+\)|Weekly\s*[·—-]\s*.+)$/i.test(heading)) return 10080;
|
|
2865
|
+
return null;
|
|
2866
|
+
}
|
|
2329
2867
|
function parseReset(line, now) {
|
|
2330
2868
|
const relative = line.match(/Resets\s+in\s+(?:(\d+)d\s*)?(?:(\d+)h\s*)?(?:(\d+)m)?/i);
|
|
2331
2869
|
if (relative) {
|
|
@@ -2652,6 +3190,7 @@ function mapQuotaBuckets(response) {
|
|
|
2652
3190
|
billingDomainId: "subscription",
|
|
2653
3191
|
label: multipleLimits ? `${baseLabel} \xB7 ${windowLabel}` : windowLabel,
|
|
2654
3192
|
usedPercent: window.usedPercent,
|
|
3193
|
+
windowDurationMinutes: window.windowDurationMins,
|
|
2655
3194
|
resetsAt: window.resetsAt === null ? null : new Date(window.resetsAt * 1e3).toISOString(),
|
|
2656
3195
|
authority: "official-account"
|
|
2657
3196
|
});
|
|
@@ -2989,6 +3528,119 @@ var init_stdio_codex_account_client = __esm({
|
|
|
2989
3528
|
}
|
|
2990
3529
|
});
|
|
2991
3530
|
|
|
3531
|
+
// src/connectors/opencode-local/opencode-local-connector.ts
|
|
3532
|
+
function mapLocalUsage(request) {
|
|
3533
|
+
return {
|
|
3534
|
+
id: `opencode-local-request:${request.id}`,
|
|
3535
|
+
billingDomainId: "local-history",
|
|
3536
|
+
model: request.model,
|
|
3537
|
+
observedAt: new Date(request.observedAtMs).toISOString(),
|
|
3538
|
+
inputTokens: request.inputTokens,
|
|
3539
|
+
outputTokens: request.outputTokens,
|
|
3540
|
+
reasoningTokens: request.reasoningTokens,
|
|
3541
|
+
cacheReadTokens: request.cacheReadTokens,
|
|
3542
|
+
cacheWriteTokens: request.cacheWriteTokens,
|
|
3543
|
+
tokenSemantics: {
|
|
3544
|
+
reasoning: "separate",
|
|
3545
|
+
cacheRead: "separate",
|
|
3546
|
+
cacheWrite: "separate"
|
|
3547
|
+
},
|
|
3548
|
+
modelAttribution: "known",
|
|
3549
|
+
timePrecision: "event",
|
|
3550
|
+
usageScope: "this-mac",
|
|
3551
|
+
aggregationTemporality: "delta",
|
|
3552
|
+
authority: "local-observation"
|
|
3553
|
+
};
|
|
3554
|
+
}
|
|
3555
|
+
function mapLocalCost(request, cost) {
|
|
3556
|
+
const usageObservationId = `opencode-local-request:${request.id}`;
|
|
3557
|
+
return {
|
|
3558
|
+
id: `opencode-local-request-cost:${request.id}`,
|
|
3559
|
+
sourceId: usageObservationId,
|
|
3560
|
+
billingDomainId: "local-history",
|
|
3561
|
+
observedAt: new Date(request.observedAtMs).toISOString(),
|
|
3562
|
+
kind: "reported-estimate",
|
|
3563
|
+
amount: cost,
|
|
3564
|
+
currency: "USD",
|
|
3565
|
+
authority: "local-observation",
|
|
3566
|
+
model: request.model,
|
|
3567
|
+
usageObservationId,
|
|
3568
|
+
priceSnapshot: {
|
|
3569
|
+
id: "opencode-local-message-reported-cost-v1",
|
|
3570
|
+
version: "2026-08-29",
|
|
3571
|
+
source: "OpenCode local message history reported cost",
|
|
3572
|
+
canonicalModel: request.model,
|
|
3573
|
+
effectiveAt: "2026-08-29T00:00:00.000Z",
|
|
3574
|
+
effectiveUntil: null,
|
|
3575
|
+
currency: "USD",
|
|
3576
|
+
ratesPerMillion: {
|
|
3577
|
+
input: null,
|
|
3578
|
+
output: null,
|
|
3579
|
+
reasoning: null,
|
|
3580
|
+
"cache-read": null,
|
|
3581
|
+
"cache-write": null
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
};
|
|
3585
|
+
}
|
|
3586
|
+
function safeFailure3(error) {
|
|
3587
|
+
if (error instanceof Error && "code" in error && typeof error.code === "string" && "recovery" in error && typeof error.recovery === "string") {
|
|
3588
|
+
return { code: error.code, message: error.message, recovery: error.recovery };
|
|
3589
|
+
}
|
|
3590
|
+
return {
|
|
3591
|
+
code: "opencode-local-refresh-failed",
|
|
3592
|
+
message: "OpenCode local history coverage is incomplete.",
|
|
3593
|
+
recovery: "Run agent-usage doctor, then retry refresh."
|
|
3594
|
+
};
|
|
3595
|
+
}
|
|
3596
|
+
var OpenCodeLocalConnector;
|
|
3597
|
+
var init_opencode_local_connector = __esm({
|
|
3598
|
+
"src/connectors/opencode-local/opencode-local-connector.ts"() {
|
|
3599
|
+
"use strict";
|
|
3600
|
+
OpenCodeLocalConnector = class {
|
|
3601
|
+
id = "opencode";
|
|
3602
|
+
displayName = "OpenCode";
|
|
3603
|
+
consentId = "opencode-go";
|
|
3604
|
+
#localHistoryClient;
|
|
3605
|
+
#clock;
|
|
3606
|
+
constructor(options) {
|
|
3607
|
+
this.#localHistoryClient = options.localHistoryClient;
|
|
3608
|
+
this.#clock = options.clock ?? (() => /* @__PURE__ */ new Date());
|
|
3609
|
+
}
|
|
3610
|
+
async collect() {
|
|
3611
|
+
const observedAt = this.#clock().toISOString();
|
|
3612
|
+
try {
|
|
3613
|
+
const requests = await this.#localHistoryClient.readHistory();
|
|
3614
|
+
return {
|
|
3615
|
+
provider: { id: this.id, displayName: this.displayName },
|
|
3616
|
+
billingDomains: [{ id: "local-history", displayName: "Local history" }],
|
|
3617
|
+
quotaBuckets: [],
|
|
3618
|
+
usage: requests.map(mapLocalUsage),
|
|
3619
|
+
usageReconciliation: {
|
|
3620
|
+
authoritativeIdPrefix: "opencode-local-request:",
|
|
3621
|
+
retiredIdPrefixes: []
|
|
3622
|
+
},
|
|
3623
|
+
costs: requests.flatMap(
|
|
3624
|
+
(request) => request.cost === null ? [] : [mapLocalCost(request, request.cost)]
|
|
3625
|
+
),
|
|
3626
|
+
observedAt
|
|
3627
|
+
};
|
|
3628
|
+
} catch (error) {
|
|
3629
|
+
return {
|
|
3630
|
+
provider: { id: this.id, displayName: this.displayName },
|
|
3631
|
+
billingDomains: [{ id: "local-history", displayName: "Local history" }],
|
|
3632
|
+
quotaBuckets: [],
|
|
3633
|
+
usage: [],
|
|
3634
|
+
costs: [],
|
|
3635
|
+
warnings: [safeFailure3(error)],
|
|
3636
|
+
observedAt
|
|
3637
|
+
};
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
};
|
|
3641
|
+
}
|
|
3642
|
+
});
|
|
3643
|
+
|
|
2992
3644
|
// src/connectors/opencode-go/local-opencode-history-client.ts
|
|
2993
3645
|
import { execFile as execFileCallback } from "child_process";
|
|
2994
3646
|
import { createHash } from "crypto";
|
|
@@ -3077,12 +3729,27 @@ var init_local_opencode_history_client = __esm({
|
|
|
3077
3729
|
#command;
|
|
3078
3730
|
#execFile;
|
|
3079
3731
|
#readDatabase;
|
|
3732
|
+
#activeRead = null;
|
|
3080
3733
|
constructor(options = {}) {
|
|
3081
3734
|
this.#command = options.command ?? "opencode";
|
|
3082
3735
|
this.#execFile = options.execFile ?? executeOpenCode;
|
|
3083
3736
|
this.#readDatabase = options.readDatabase ?? readDatabase;
|
|
3084
3737
|
}
|
|
3085
|
-
|
|
3738
|
+
readHistory() {
|
|
3739
|
+
if (this.#activeRead) return this.#activeRead;
|
|
3740
|
+
const activeRead = this.#readHistory();
|
|
3741
|
+
this.#activeRead = activeRead;
|
|
3742
|
+
activeRead.then(
|
|
3743
|
+
() => {
|
|
3744
|
+
if (this.#activeRead === activeRead) this.#activeRead = null;
|
|
3745
|
+
},
|
|
3746
|
+
() => {
|
|
3747
|
+
if (this.#activeRead === activeRead) this.#activeRead = null;
|
|
3748
|
+
}
|
|
3749
|
+
);
|
|
3750
|
+
return activeRead;
|
|
3751
|
+
}
|
|
3752
|
+
async #readHistory() {
|
|
3086
3753
|
let databasePath;
|
|
3087
3754
|
try {
|
|
3088
3755
|
const { stdout } = await this.#execFile(this.#command, ["db", "path"]);
|
|
@@ -3113,8 +3780,9 @@ var init_local_opencode_history_client = __esm({
|
|
|
3113
3780
|
{ cause: error }
|
|
3114
3781
|
);
|
|
3115
3782
|
}
|
|
3116
|
-
return completedAssistantRows.filter(
|
|
3783
|
+
return completedAssistantRows.filter(hasCategorizedTokens).map((row) => ({
|
|
3117
3784
|
id: `v2:${createHash("sha256").update(row.sourceId).digest("hex")}`,
|
|
3785
|
+
providerId: row.providerId,
|
|
3118
3786
|
model: `${row.providerId}/${row.modelId}`,
|
|
3119
3787
|
cost: row.cost,
|
|
3120
3788
|
inputTokens: row.inputTokens,
|
|
@@ -3317,6 +3985,7 @@ function mapQuota2(response) {
|
|
|
3317
3985
|
billingDomainId: "go-subscription",
|
|
3318
3986
|
label: LIMITS[id].label,
|
|
3319
3987
|
usedPercent: response.usage[id].percent,
|
|
3988
|
+
windowDurationMinutes: LIMITS[id].windowDurationMinutes,
|
|
3320
3989
|
resetsAt: response.usage[id].resetsAt,
|
|
3321
3990
|
authority: "official-account",
|
|
3322
3991
|
scope: "account-wide",
|
|
@@ -3326,61 +3995,7 @@ function mapQuota2(response) {
|
|
|
3326
3995
|
fallbackStatus: "unknown"
|
|
3327
3996
|
}));
|
|
3328
3997
|
}
|
|
3329
|
-
function
|
|
3330
|
-
return {
|
|
3331
|
-
id: `opencode-request:${request.id}`,
|
|
3332
|
-
billingDomainId: "go-subscription",
|
|
3333
|
-
model: request.model,
|
|
3334
|
-
observedAt: new Date(request.observedAtMs).toISOString(),
|
|
3335
|
-
inputTokens: request.inputTokens,
|
|
3336
|
-
outputTokens: request.outputTokens,
|
|
3337
|
-
reasoningTokens: request.reasoningTokens,
|
|
3338
|
-
cacheReadTokens: request.cacheReadTokens,
|
|
3339
|
-
cacheWriteTokens: request.cacheWriteTokens,
|
|
3340
|
-
tokenSemantics: {
|
|
3341
|
-
reasoning: "separate",
|
|
3342
|
-
cacheRead: "separate",
|
|
3343
|
-
cacheWrite: "separate"
|
|
3344
|
-
},
|
|
3345
|
-
modelAttribution: "known",
|
|
3346
|
-
timePrecision: "event",
|
|
3347
|
-
usageScope: "this-mac",
|
|
3348
|
-
aggregationTemporality: "delta",
|
|
3349
|
-
authority: "local-observation"
|
|
3350
|
-
};
|
|
3351
|
-
}
|
|
3352
|
-
function mapLocalCost(request, cost) {
|
|
3353
|
-
const usageObservationId = `opencode-request:${request.id}`;
|
|
3354
|
-
return {
|
|
3355
|
-
id: `opencode-request-cost:${request.id}`,
|
|
3356
|
-
sourceId: usageObservationId,
|
|
3357
|
-
billingDomainId: "go-subscription",
|
|
3358
|
-
observedAt: new Date(request.observedAtMs).toISOString(),
|
|
3359
|
-
kind: "reported-estimate",
|
|
3360
|
-
amount: cost,
|
|
3361
|
-
currency: "USD",
|
|
3362
|
-
authority: "local-observation",
|
|
3363
|
-
model: request.model,
|
|
3364
|
-
usageObservationId,
|
|
3365
|
-
priceSnapshot: {
|
|
3366
|
-
id: "opencode-message-reported-cost-v2",
|
|
3367
|
-
version: "2026-08-28",
|
|
3368
|
-
source: "OpenCode local message history reported cost",
|
|
3369
|
-
canonicalModel: request.model,
|
|
3370
|
-
effectiveAt: "2026-08-28T00:00:00.000Z",
|
|
3371
|
-
effectiveUntil: null,
|
|
3372
|
-
currency: "USD",
|
|
3373
|
-
ratesPerMillion: {
|
|
3374
|
-
input: null,
|
|
3375
|
-
output: null,
|
|
3376
|
-
reasoning: null,
|
|
3377
|
-
"cache-read": null,
|
|
3378
|
-
"cache-write": null
|
|
3379
|
-
}
|
|
3380
|
-
}
|
|
3381
|
-
};
|
|
3382
|
-
}
|
|
3383
|
-
function safeFailure3(error) {
|
|
3998
|
+
function safeFailure4(error) {
|
|
3384
3999
|
if (error instanceof Error && "code" in error && typeof error.code === "string" && "recovery" in error && typeof error.recovery === "string") {
|
|
3385
4000
|
return { code: error.code, message: error.message, recovery: error.recovery };
|
|
3386
4001
|
}
|
|
@@ -3395,9 +4010,9 @@ var init_opencode_go_connector = __esm({
|
|
|
3395
4010
|
"src/connectors/opencode-go/opencode-go-connector.ts"() {
|
|
3396
4011
|
"use strict";
|
|
3397
4012
|
LIMITS = {
|
|
3398
|
-
rolling: { label: "5 hour", amount: 12 },
|
|
3399
|
-
weekly: { label: "Week", amount: 30 },
|
|
3400
|
-
monthly: { label: "Month", amount: 60 }
|
|
4013
|
+
rolling: { label: "5 hour", amount: 12, windowDurationMinutes: 300 },
|
|
4014
|
+
weekly: { label: "Week", amount: 30, windowDurationMinutes: 10080 },
|
|
4015
|
+
monthly: { label: "Month", amount: 60, windowDurationMinutes: 43200 }
|
|
3401
4016
|
};
|
|
3402
4017
|
OpenCodeGoConnector = class {
|
|
3403
4018
|
id = "opencode-go";
|
|
@@ -3418,24 +4033,21 @@ var init_opencode_go_connector = __esm({
|
|
|
3418
4033
|
this.#localHistoryClient.readHistory()
|
|
3419
4034
|
]);
|
|
3420
4035
|
const warnings = [];
|
|
3421
|
-
if (accountResult.status === "rejected") warnings.push(
|
|
3422
|
-
if (localResult.status === "rejected") warnings.push(
|
|
4036
|
+
if (accountResult.status === "rejected") warnings.push(safeFailure4(accountResult.reason));
|
|
4037
|
+
if (localResult.status === "rejected") warnings.push(safeFailure4(localResult.reason));
|
|
3423
4038
|
const account = accountResult.status === "fulfilled" ? accountResult.value : null;
|
|
3424
|
-
const local = localResult.status === "fulfilled" ? localResult.value : [];
|
|
3425
4039
|
return {
|
|
3426
4040
|
provider: { id: this.id, displayName: this.displayName },
|
|
3427
4041
|
billingDomains: [{ id: "go-subscription", displayName: "OpenCode Go subscription" }],
|
|
3428
4042
|
quotaBuckets: account ? mapQuota2(account) : [],
|
|
3429
|
-
usage:
|
|
4043
|
+
usage: [],
|
|
3430
4044
|
...localResult.status === "fulfilled" ? {
|
|
3431
4045
|
usageReconciliation: {
|
|
3432
4046
|
authoritativeIdPrefix: "opencode-request:",
|
|
3433
4047
|
retiredIdPrefixes: ["opencode-session:"]
|
|
3434
4048
|
}
|
|
3435
4049
|
} : {},
|
|
3436
|
-
costs:
|
|
3437
|
-
(request) => request.cost === null ? [] : [mapLocalCost(request, request.cost)]
|
|
3438
|
-
),
|
|
4050
|
+
costs: [],
|
|
3439
4051
|
warnings,
|
|
3440
4052
|
observedAt
|
|
3441
4053
|
};
|
|
@@ -3473,6 +4085,11 @@ function mapBillingQuota(billing) {
|
|
|
3473
4085
|
billingDomainId: "grok-build-subscription",
|
|
3474
4086
|
label: period.label,
|
|
3475
4087
|
usedPercent,
|
|
4088
|
+
windowDurationMinutes: nativeWindowDurationMinutes(
|
|
4089
|
+
config.currentPeriod?.start ?? config.billingPeriodStart,
|
|
4090
|
+
resetsAt,
|
|
4091
|
+
period.id
|
|
4092
|
+
),
|
|
3476
4093
|
resetsAt,
|
|
3477
4094
|
authority: "official-client",
|
|
3478
4095
|
scope: "account-wide",
|
|
@@ -3480,6 +4097,16 @@ function mapBillingQuota(billing) {
|
|
|
3480
4097
|
}
|
|
3481
4098
|
];
|
|
3482
4099
|
}
|
|
4100
|
+
function nativeWindowDurationMinutes(startsAt, resetsAt, period) {
|
|
4101
|
+
const start = startsAt ? Date.parse(startsAt) : Number.NaN;
|
|
4102
|
+
const end = resetsAt ? Date.parse(resetsAt) : Number.NaN;
|
|
4103
|
+
if (Number.isFinite(start) && Number.isFinite(end) && end > start) {
|
|
4104
|
+
return Math.round((end - start) / 6e4);
|
|
4105
|
+
}
|
|
4106
|
+
if (period === "weekly") return 10080;
|
|
4107
|
+
if (period === "monthly") return 43200;
|
|
4108
|
+
return null;
|
|
4109
|
+
}
|
|
3483
4110
|
function nativePeriod(type, fallback) {
|
|
3484
4111
|
if (type === "USAGE_PERIOD_TYPE_WEEKLY") return { id: "weekly", label: "Weekly limit" };
|
|
3485
4112
|
if (type === "USAGE_PERIOD_TYPE_MONTHLY" || fallback === "monthly") {
|
|
@@ -3491,7 +4118,7 @@ function derivePercent(used, limit) {
|
|
|
3491
4118
|
if (used === void 0 || limit === void 0 || limit <= 0) return null;
|
|
3492
4119
|
return Math.min(100, Math.max(0, used / limit * 100));
|
|
3493
4120
|
}
|
|
3494
|
-
function
|
|
4121
|
+
function safeFailure5(error) {
|
|
3495
4122
|
if (error instanceof Error && "code" in error && typeof error.code === "string" && "recovery" in error && typeof error.recovery === "string") {
|
|
3496
4123
|
return { code: error.code, message: error.message, recovery: error.recovery };
|
|
3497
4124
|
}
|
|
@@ -3554,7 +4181,7 @@ var init_grok_build_connector = __esm({
|
|
|
3554
4181
|
});
|
|
3555
4182
|
}
|
|
3556
4183
|
} catch (error) {
|
|
3557
|
-
warnings.push(
|
|
4184
|
+
warnings.push(safeFailure5(error));
|
|
3558
4185
|
}
|
|
3559
4186
|
const history = this.#historyClient ? await this.#historyClient.readUsage(options) : { usage: [], costs: [], complete: true };
|
|
3560
4187
|
if (!history.complete) warnings.push(incompleteTranscriptFailure3());
|
|
@@ -3698,6 +4325,50 @@ import { open } from "fs/promises";
|
|
|
3698
4325
|
import { homedir as homedir2 } from "os";
|
|
3699
4326
|
import { join as join3 } from "path";
|
|
3700
4327
|
import { z as z7 } from "zod";
|
|
4328
|
+
function billingIsFresh(billing, now) {
|
|
4329
|
+
if (!billing.sourceObservedAt) return false;
|
|
4330
|
+
const observedAt = Date.parse(billing.sourceObservedAt);
|
|
4331
|
+
if (!Number.isFinite(observedAt)) return false;
|
|
4332
|
+
return now.getTime() - observedAt <= GROK_BILLING_FRESHNESS_MS;
|
|
4333
|
+
}
|
|
4334
|
+
async function refreshBillingLogViaDashboard(command, timeoutMs) {
|
|
4335
|
+
if (process.platform !== "darwin") return;
|
|
4336
|
+
const refreshTimeoutMs = Math.min(timeoutMs, 6e3);
|
|
4337
|
+
const expectScript2 = [
|
|
4338
|
+
`set timeout ${Math.max(1, Math.ceil(refreshTimeoutMs / 1e3))}`,
|
|
4339
|
+
"log_user 0",
|
|
4340
|
+
"set command $env(AGENT_USAGE_GROK_REFRESH_COMMAND)",
|
|
4341
|
+
"spawn -noecho $command dashboard",
|
|
4342
|
+
`after ${Math.min(2e3, Math.max(250, refreshTimeoutMs - 1e3))}`,
|
|
4343
|
+
'send "\\021\\021"',
|
|
4344
|
+
"expect eof"
|
|
4345
|
+
].join("\n");
|
|
4346
|
+
await new Promise((resolve5) => {
|
|
4347
|
+
const dashboard = spawn3("/usr/bin/expect", ["-c", expectScript2], {
|
|
4348
|
+
env: {
|
|
4349
|
+
...process.env,
|
|
4350
|
+
AGENT_USAGE_GROK_REFRESH_COMMAND: command,
|
|
4351
|
+
TERM: process.env.TERM ?? "xterm-256color"
|
|
4352
|
+
},
|
|
4353
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
4354
|
+
});
|
|
4355
|
+
dashboard.stdout.resume();
|
|
4356
|
+
dashboard.stderr.resume();
|
|
4357
|
+
let settled = false;
|
|
4358
|
+
const stopTimer = setTimeout(() => {
|
|
4359
|
+
dashboard.kill("SIGTERM");
|
|
4360
|
+
finish();
|
|
4361
|
+
}, refreshTimeoutMs);
|
|
4362
|
+
const finish = () => {
|
|
4363
|
+
if (settled) return;
|
|
4364
|
+
settled = true;
|
|
4365
|
+
clearTimeout(stopTimer);
|
|
4366
|
+
resolve5();
|
|
4367
|
+
};
|
|
4368
|
+
dashboard.once("error", finish);
|
|
4369
|
+
dashboard.once("exit", finish);
|
|
4370
|
+
});
|
|
4371
|
+
}
|
|
3701
4372
|
function parseLatestBillingLog(text) {
|
|
3702
4373
|
const lines = text.split(/\r?\n/);
|
|
3703
4374
|
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
@@ -3748,7 +4419,7 @@ function unavailableError3(cause) {
|
|
|
3748
4419
|
{ cause }
|
|
3749
4420
|
);
|
|
3750
4421
|
}
|
|
3751
|
-
var GrokBillingAdapterError, StdioGrokBillingClient, JsonRpcError, JsonLinePeer2;
|
|
4422
|
+
var GrokBillingAdapterError, StdioGrokBillingClient, GROK_BILLING_FRESHNESS_MS, JsonRpcError, JsonLinePeer2;
|
|
3752
4423
|
var init_stdio_grok_billing_client = __esm({
|
|
3753
4424
|
"src/connectors/grok-build/stdio-grok-billing-client.ts"() {
|
|
3754
4425
|
"use strict";
|
|
@@ -3768,6 +4439,8 @@ var init_stdio_grok_billing_client = __esm({
|
|
|
3768
4439
|
#timeoutMs;
|
|
3769
4440
|
#spawnProcess;
|
|
3770
4441
|
#readUnifiedLog;
|
|
4442
|
+
#refreshBillingLog;
|
|
4443
|
+
#clock;
|
|
3771
4444
|
constructor(options = {}) {
|
|
3772
4445
|
this.#command = options.command ?? "grok";
|
|
3773
4446
|
this.#timeoutMs = options.timeoutMs ?? 8e3;
|
|
@@ -3777,13 +4450,15 @@ var init_stdio_grok_billing_client = __esm({
|
|
|
3777
4450
|
this.#readUnifiedLog = options.readUnifiedLog ?? (() => readLogTail(
|
|
3778
4451
|
join3(process.env.GROK_HOME ?? join3(homedir2(), ".grok"), "logs", "unified.jsonl")
|
|
3779
4452
|
));
|
|
4453
|
+
this.#refreshBillingLog = options.refreshBillingLog ?? (options.readUnifiedLog ? async () => void 0 : () => refreshBillingLogViaDashboard(this.#command, this.#timeoutMs));
|
|
4454
|
+
this.#clock = options.clock ?? (() => /* @__PURE__ */ new Date());
|
|
3780
4455
|
}
|
|
3781
4456
|
async readBilling() {
|
|
3782
4457
|
let process2;
|
|
3783
4458
|
try {
|
|
3784
4459
|
process2 = this.#spawnProcess(this.#command, ["agent", "--no-leader", "stdio"]);
|
|
3785
4460
|
} catch (error) {
|
|
3786
|
-
const fallback = await this.#
|
|
4461
|
+
const fallback = await this.#readFreshLogFallback();
|
|
3787
4462
|
if (fallback) return fallback;
|
|
3788
4463
|
throw unavailableError3(error);
|
|
3789
4464
|
}
|
|
@@ -3815,7 +4490,7 @@ var init_stdio_grok_billing_client = __esm({
|
|
|
3815
4490
|
}
|
|
3816
4491
|
return billing.data;
|
|
3817
4492
|
} catch (error) {
|
|
3818
|
-
const fallback = await this.#
|
|
4493
|
+
const fallback = await this.#readFreshLogFallback();
|
|
3819
4494
|
if (fallback) return fallback;
|
|
3820
4495
|
if (error instanceof GrokBillingAdapterError) throw error;
|
|
3821
4496
|
if (error instanceof JsonRpcError && error.code === -32601) {
|
|
@@ -3846,7 +4521,18 @@ var init_stdio_grok_billing_client = __esm({
|
|
|
3846
4521
|
return null;
|
|
3847
4522
|
}
|
|
3848
4523
|
}
|
|
4524
|
+
async #readFreshLogFallback() {
|
|
4525
|
+
const cached = await this.#readLogFallback();
|
|
4526
|
+
if (cached && billingIsFresh(cached, this.#clock())) return cached;
|
|
4527
|
+
try {
|
|
4528
|
+
await this.#refreshBillingLog();
|
|
4529
|
+
} catch {
|
|
4530
|
+
return cached;
|
|
4531
|
+
}
|
|
4532
|
+
return await this.#readLogFallback() ?? cached;
|
|
4533
|
+
}
|
|
3849
4534
|
};
|
|
4535
|
+
GROK_BILLING_FRESHNESS_MS = 5 * 60 * 1e3;
|
|
3850
4536
|
JsonRpcError = class extends Error {
|
|
3851
4537
|
code;
|
|
3852
4538
|
constructor(body) {
|
|
@@ -4133,7 +4819,7 @@ function endpointFailure(error, label) {
|
|
|
4133
4819
|
recovery: "Check network access and xAI status, then retry."
|
|
4134
4820
|
};
|
|
4135
4821
|
}
|
|
4136
|
-
function
|
|
4822
|
+
function safeFailure6(error) {
|
|
4137
4823
|
if (error instanceof Error && "code" in error && typeof error.code === "string" && "recovery" in error && typeof error.recovery === "string") {
|
|
4138
4824
|
return { code: error.code, message: error.message, recovery: error.recovery };
|
|
4139
4825
|
}
|
|
@@ -4388,7 +5074,7 @@ var init_xai_api_connector = __esm({
|
|
|
4388
5074
|
costs: [],
|
|
4389
5075
|
balances: [],
|
|
4390
5076
|
invoices: [],
|
|
4391
|
-
warnings: [
|
|
5077
|
+
warnings: [safeFailure6(error)],
|
|
4392
5078
|
observedAt
|
|
4393
5079
|
};
|
|
4394
5080
|
}
|
|
@@ -4844,6 +5530,25 @@ async function startLocalServer(options) {
|
|
|
4844
5530
|
sendOtlpProtobuf(response, 200);
|
|
4845
5531
|
return;
|
|
4846
5532
|
}
|
|
5533
|
+
if (request.method === "GET" && requestUrl.pathname === "/api/overview/providers") {
|
|
5534
|
+
sendJson(response, 200, await options.application.getAgentProviderIndex());
|
|
5535
|
+
return;
|
|
5536
|
+
}
|
|
5537
|
+
if (request.method === "GET" && requestUrl.pathname.startsWith("/api/overview/providers/")) {
|
|
5538
|
+
const providerId = decodeURIComponent(
|
|
5539
|
+
requestUrl.pathname.slice("/api/overview/providers/".length)
|
|
5540
|
+
);
|
|
5541
|
+
const provider = await options.application.getProviderOverview(
|
|
5542
|
+
providerId,
|
|
5543
|
+
parseUsageQuery(requestUrl)
|
|
5544
|
+
);
|
|
5545
|
+
if (!provider) {
|
|
5546
|
+
sendJson(response, 404, { error: "provider-not-found" });
|
|
5547
|
+
return;
|
|
5548
|
+
}
|
|
5549
|
+
sendJson(response, 200, provider);
|
|
5550
|
+
return;
|
|
5551
|
+
}
|
|
4847
5552
|
if (request.method === "GET" && requestUrl.pathname === "/api/overview") {
|
|
4848
5553
|
const query = parseUsageQuery(requestUrl);
|
|
4849
5554
|
sendJson(response, 200, await options.application.getOverview(query));
|
|
@@ -4854,6 +5559,13 @@ async function startLocalServer(options) {
|
|
|
4854
5559
|
sendJson(response, 403, { error: "invalid-origin" });
|
|
4855
5560
|
return;
|
|
4856
5561
|
}
|
|
5562
|
+
if (requestUrl.searchParams.get("background") === "true") {
|
|
5563
|
+
void options.application.startBackgroundProcessing({
|
|
5564
|
+
userInitiated: requestUrl.searchParams.get("mode") !== "automatic"
|
|
5565
|
+
});
|
|
5566
|
+
sendJson(response, 202, { accepted: true });
|
|
5567
|
+
return;
|
|
5568
|
+
}
|
|
4857
5569
|
await options.application.refresh({
|
|
4858
5570
|
userInitiated: requestUrl.searchParams.get("mode") !== "automatic"
|
|
4859
5571
|
});
|
|
@@ -4906,6 +5618,19 @@ async function startLocalServer(options) {
|
|
|
4906
5618
|
sendJson(response, 200, await options.application.clearData(input));
|
|
4907
5619
|
return;
|
|
4908
5620
|
}
|
|
5621
|
+
if (request.method === "GET" && requestUrl.pathname === "/api/plans") {
|
|
5622
|
+
sendJson(response, 200, await options.application.getPlanSettings());
|
|
5623
|
+
return;
|
|
5624
|
+
}
|
|
5625
|
+
if (request.method === "PATCH" && requestUrl.pathname === "/api/plans") {
|
|
5626
|
+
if (!validMutationOrigin(authentication, request, origin)) {
|
|
5627
|
+
sendJson(response, 403, { error: "invalid-origin" });
|
|
5628
|
+
return;
|
|
5629
|
+
}
|
|
5630
|
+
const input = planSubscriptionSchema.parse(await readJsonBody(request));
|
|
5631
|
+
sendJson(response, 200, await options.application.updatePlanSubscription(input));
|
|
5632
|
+
return;
|
|
5633
|
+
}
|
|
4909
5634
|
if (request.method === "GET" && requestUrl.pathname === "/api/monitoring") {
|
|
4910
5635
|
sendJson(response, 200, await options.application.getMonitoringStatus());
|
|
4911
5636
|
return;
|
|
@@ -5104,7 +5829,7 @@ function sendHtml(response, status, body) {
|
|
|
5104
5829
|
response.writeHead(status, { "content-type": "text/html; charset=utf-8" });
|
|
5105
5830
|
response.end(body);
|
|
5106
5831
|
}
|
|
5107
|
-
var SESSION_COOKIE, connectorActionSchema, usageQuerySchema, monitoringSettingsSchema, clearDataSchema, hardRebuildSchema;
|
|
5832
|
+
var SESSION_COOKIE, connectorActionSchema, usageQuerySchema, monitoringSettingsSchema, planSubscriptionSchema, clearDataSchema, hardRebuildSchema;
|
|
5108
5833
|
var init_local_server = __esm({
|
|
5109
5834
|
"src/server/local-server.ts"() {
|
|
5110
5835
|
"use strict";
|
|
@@ -5127,6 +5852,17 @@ var init_local_server = __esm({
|
|
|
5127
5852
|
notificationsEnabled: z9.boolean().optional(),
|
|
5128
5853
|
startAtLogin: z9.boolean().optional()
|
|
5129
5854
|
}).refine((value) => Object.keys(value).length > 0, "At least one setting is required");
|
|
5855
|
+
planSubscriptionSchema = z9.object({
|
|
5856
|
+
providerId: z9.string().min(1),
|
|
5857
|
+
billingDomainId: z9.string().min(1),
|
|
5858
|
+
plan: z9.object({
|
|
5859
|
+
planId: z9.string().min(1).nullable(),
|
|
5860
|
+
amount: z9.number().positive().finite().optional(),
|
|
5861
|
+
currency: z9.string().length(3).optional(),
|
|
5862
|
+
billingPeriod: z9.enum(["monthly", "annual"]).optional(),
|
|
5863
|
+
anchorDate: z9.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional()
|
|
5864
|
+
}).nullable()
|
|
5865
|
+
});
|
|
5130
5866
|
clearDataSchema = z9.object({ deleteProductSecrets: z9.boolean().default(false) });
|
|
5131
5867
|
hardRebuildSchema = z9.object({ confirmExpensiveOperation: z9.literal(true) });
|
|
5132
5868
|
}
|
|
@@ -5677,6 +6413,16 @@ async function runDatabaseWorker(workerData) {
|
|
|
5677
6413
|
if (workerData.operation === 'indexes') {
|
|
5678
6414
|
database.exec(workerData.indexesSql);
|
|
5679
6415
|
parentPort.postMessage({ ok: true, data: null });
|
|
6416
|
+
} else if (workerData.operation === 'delete-demo-data') {
|
|
6417
|
+
database.exec('BEGIN IMMEDIATE');
|
|
6418
|
+
try {
|
|
6419
|
+
database.exec(workerData.deleteDemoDataSql);
|
|
6420
|
+
database.exec('COMMIT');
|
|
6421
|
+
parentPort.postMessage({ ok: true, data: null });
|
|
6422
|
+
} catch (error) {
|
|
6423
|
+
database.exec('ROLLBACK');
|
|
6424
|
+
throw error;
|
|
6425
|
+
}
|
|
5680
6426
|
} else if (workerData.operation === 'delete-derived-costs') {
|
|
5681
6427
|
database.prepare(workerData.deleteDerivedCostsSql).run();
|
|
5682
6428
|
parentPort.postMessage({ ok: true, data: null });
|
|
@@ -5844,6 +6590,7 @@ function mapQuotaRow(row) {
|
|
|
5844
6590
|
billingDomainId: row.billing_domain_id,
|
|
5845
6591
|
label: row.label,
|
|
5846
6592
|
usedPercent: row.used_percent,
|
|
6593
|
+
windowDurationMinutes: row.window_duration_minutes,
|
|
5847
6594
|
resetsAt: row.resets_at,
|
|
5848
6595
|
authority: row.authority,
|
|
5849
6596
|
observedAt: row.observed_at,
|
|
@@ -6045,7 +6792,7 @@ function buildGlobalSummary(providers, riskSummary, now, query) {
|
|
|
6045
6792
|
tokenEvidence,
|
|
6046
6793
|
apiRetailEquivalent: {
|
|
6047
6794
|
status: sawRetailEquivalent ? "available" : "unavailable",
|
|
6048
|
-
amount: sawRetailEquivalent ?
|
|
6795
|
+
amount: sawRetailEquivalent ? preciseAmount2(retailAmount) : null,
|
|
6049
6796
|
currency: "USD",
|
|
6050
6797
|
pricingCoverage: tokenEvidence.recordedTokens === 0 ? null : retailPricedTokens / tokenEvidence.recordedTokens
|
|
6051
6798
|
},
|
|
@@ -6055,7 +6802,7 @@ function buildGlobalSummary(providers, riskSummary, now, query) {
|
|
|
6055
6802
|
contributions
|
|
6056
6803
|
};
|
|
6057
6804
|
}
|
|
6058
|
-
function buildTokenMoneyWorkbench(providers, now, query) {
|
|
6805
|
+
function buildTokenMoneyWorkbench(providers, now, query, planSubscriptions, planBillingPeriods) {
|
|
6059
6806
|
const normalized = normalizeUsageQuery(now, query);
|
|
6060
6807
|
const allHistories = allDomainHistories(providers);
|
|
6061
6808
|
const headlineHistories = allHistories.filter(({ includedInHeadline }) => includedInHeadline);
|
|
@@ -6246,9 +6993,43 @@ function buildTokenMoneyWorkbench(providers, now, query) {
|
|
|
6246
6993
|
observationCount > 0 ? recordedTokens : null,
|
|
6247
6994
|
retailEquivalent,
|
|
6248
6995
|
reportedEstimate
|
|
6249
|
-
)
|
|
6996
|
+
),
|
|
6997
|
+
planValue: buildWorkbenchPlanValue({
|
|
6998
|
+
domains: planValueDomains(allHistories, comparisonCurrency),
|
|
6999
|
+
subscriptions: planSubscriptions,
|
|
7000
|
+
comparisonCurrency,
|
|
7001
|
+
start: normalized.start.toISOString(),
|
|
7002
|
+
end: normalized.end.toISOString(),
|
|
7003
|
+
rates: uniqueExchangeRates(allHistories.flatMap(({ history }) => history.exchangeRates)),
|
|
7004
|
+
billingPeriods: planBillingPeriods
|
|
7005
|
+
})
|
|
6250
7006
|
};
|
|
6251
7007
|
}
|
|
7008
|
+
function planValueDomains(histories, comparisonCurrency) {
|
|
7009
|
+
return histories.map(({ provider, domain, history, includedInHeadline }) => {
|
|
7010
|
+
const domainTokens = history.tokenEvidence.recordedTokens;
|
|
7011
|
+
const metric = (purpose) => buildWorkbenchMetric(
|
|
7012
|
+
history.costs,
|
|
7013
|
+
purpose,
|
|
7014
|
+
comparisonCurrency,
|
|
7015
|
+
domainTokens,
|
|
7016
|
+
history.exchangeRates
|
|
7017
|
+
);
|
|
7018
|
+
return {
|
|
7019
|
+
providerId: provider.id,
|
|
7020
|
+
providerDisplayName: provider.displayName,
|
|
7021
|
+
billingDomainId: domain.id,
|
|
7022
|
+
billingDomainDisplayName: domain.displayName,
|
|
7023
|
+
includedInHeadline,
|
|
7024
|
+
recordedTokens: domainTokens,
|
|
7025
|
+
observationCount: history.tokenEvidence.observationCount,
|
|
7026
|
+
retailEquivalent: metric("retail-equivalent"),
|
|
7027
|
+
actualCost: metric("actual"),
|
|
7028
|
+
authorities: history.authorities ?? [],
|
|
7029
|
+
lastObservedAt: history.lastObservedAt ?? null
|
|
7030
|
+
};
|
|
7031
|
+
});
|
|
7032
|
+
}
|
|
6252
7033
|
function allDomainHistories(providers) {
|
|
6253
7034
|
return providers.flatMap(
|
|
6254
7035
|
(provider) => provider.billingDomains.map((domain) => ({
|
|
@@ -6304,11 +7085,11 @@ function buildWorkbenchMetric(costs, purpose, comparisonCurrency, recordedTokens
|
|
|
6304
7085
|
return {
|
|
6305
7086
|
purpose,
|
|
6306
7087
|
status,
|
|
6307
|
-
amount: status === "available" ?
|
|
7088
|
+
amount: status === "available" ? preciseAmount2(convertedAmount) : null,
|
|
6308
7089
|
comparisonCurrency,
|
|
6309
7090
|
nativeAmounts: [...native.entries()].map(([currency, amount]) => ({
|
|
6310
7091
|
currency,
|
|
6311
|
-
amount: amount.complete ?
|
|
7092
|
+
amount: amount.complete ? preciseAmount2(amount.amount) : null,
|
|
6312
7093
|
records: amount.records,
|
|
6313
7094
|
knownRecords: amount.knownRecords
|
|
6314
7095
|
})).sort((left, right) => left.currency.localeCompare(right.currency)),
|
|
@@ -6477,9 +7258,9 @@ function buildWorkbenchModelRanking(histories, buckets, comparisonCurrency, reco
|
|
|
6477
7258
|
)
|
|
6478
7259
|
);
|
|
6479
7260
|
return {
|
|
6480
|
-
byTokens: byTokens.
|
|
6481
|
-
byCost: byCost.
|
|
6482
|
-
byRetailEquivalent: byRetailEquivalent.
|
|
7261
|
+
byTokens: byTokens.map((entry) => entry.id),
|
|
7262
|
+
byCost: byCost.map((entry) => entry.id),
|
|
7263
|
+
byRetailEquivalent: byRetailEquivalent.map((entry) => entry.id),
|
|
6483
7264
|
entries: byTokens,
|
|
6484
7265
|
unclassified
|
|
6485
7266
|
};
|
|
@@ -6900,7 +7681,7 @@ function round(value) {
|
|
|
6900
7681
|
function pricingInputsChanged(existing, observation) {
|
|
6901
7682
|
return existing.billing_domain_id !== observation.billingDomainId || existing.model !== (observation.model?.trim() || "__unclassified__") || existing.observed_at !== observation.observedAt || Number(existing.total_tokens) !== observation.recordedTokens || Number(existing.input_tokens) !== observation.inputTokens || Number(existing.output_tokens) !== observation.outputTokens || Number(existing.reasoning_tokens) !== observation.reasoningTokens || Number(existing.cache_read_tokens) !== observation.cacheReadTokens || Number(existing.cache_write_tokens) !== observation.cacheWriteTokens || (existing.cache_write_5m_tokens === null ? null : Number(existing.cache_write_5m_tokens)) !== (observation.cacheWriteTokenBreakdown?.fiveMinute ?? null) || (existing.cache_write_1h_tokens === null ? null : Number(existing.cache_write_1h_tokens)) !== (observation.cacheWriteTokenBreakdown?.oneHour ?? null) || Number(existing.unclassified_tokens) !== observation.unclassifiedTokens || existing.reasoning_semantics !== observation.tokenSemantics.reasoning || existing.cache_read_semantics !== observation.tokenSemantics.cacheRead || existing.cache_write_semantics !== observation.tokenSemantics.cacheWrite || existing.model_attribution !== observation.modelAttribution || existing.time_precision !== observation.timePrecision || existing.aggregation_temporality !== observation.aggregationTemporality;
|
|
6902
7683
|
}
|
|
6903
|
-
function
|
|
7684
|
+
function preciseAmount2(value) {
|
|
6904
7685
|
return Number(value.toFixed(12));
|
|
6905
7686
|
}
|
|
6906
7687
|
function tokenAuthority(authorities) {
|
|
@@ -6931,11 +7712,12 @@ function freshnessStatus(lastSuccessAt, now) {
|
|
|
6931
7712
|
if (!lastSuccessAt) return "unavailable";
|
|
6932
7713
|
return now.getTime() - new Date(lastSuccessAt).getTime() <= FRESHNESS_WINDOW_MS ? "fresh" : "stale";
|
|
6933
7714
|
}
|
|
6934
|
-
var FRESHNESS_WINDOW_MS, QUERY_INDEXES_SQL, DELETE_DERIVED_RETAIL_COSTS_SQL, DatabaseSync2, RETENTION_AGGREGATE_SQL, SqliteUsageRepository;
|
|
7715
|
+
var FRESHNESS_WINDOW_MS, QUERY_INDEXES_SQL, DELETE_DERIVED_RETAIL_COSTS_SQL, DELETE_DEMO_PROVIDER_DATA_SQL, DatabaseSync2, RETENTION_AGGREGATE_SQL, SqliteUsageRepository;
|
|
6935
7716
|
var init_sqlite_usage_repository = __esm({
|
|
6936
7717
|
"src/server/sqlite-usage-repository.ts"() {
|
|
6937
7718
|
"use strict";
|
|
6938
7719
|
init_token_normalization();
|
|
7720
|
+
init_plan_pricing();
|
|
6939
7721
|
FRESHNESS_WINDOW_MS = 15 * 60 * 1e3;
|
|
6940
7722
|
QUERY_INDEXES_SQL = `
|
|
6941
7723
|
CREATE INDEX IF NOT EXISTS usage_observed_at_idx
|
|
@@ -6964,6 +7746,12 @@ var init_sqlite_usage_repository = __esm({
|
|
|
6964
7746
|
WHERE usage.provider_id = cost_records.provider_id
|
|
6965
7747
|
AND usage.id = cost_records.usage_observation_id
|
|
6966
7748
|
)
|
|
7749
|
+
`;
|
|
7750
|
+
DELETE_DEMO_PROVIDER_DATA_SQL = `
|
|
7751
|
+
DELETE FROM connector_diagnostics WHERE provider_id = 'demo';
|
|
7752
|
+
DELETE FROM connector_runtime WHERE id = 'demo';
|
|
7753
|
+
DELETE FROM connector_settings WHERE id = 'demo';
|
|
7754
|
+
DELETE FROM providers WHERE id = 'demo';
|
|
6967
7755
|
`;
|
|
6968
7756
|
({ DatabaseSync: DatabaseSync2 } = createRequire2(import.meta.url)(
|
|
6969
7757
|
"node:sqlite"
|
|
@@ -6992,8 +7780,10 @@ var init_sqlite_usage_repository = __esm({
|
|
|
6992
7780
|
SqliteUsageRepository = class {
|
|
6993
7781
|
#database;
|
|
6994
7782
|
#databasePath;
|
|
6995
|
-
|
|
7783
|
+
#hideDemoProvider;
|
|
7784
|
+
constructor(databasePath, options = {}) {
|
|
6996
7785
|
this.#databasePath = databasePath;
|
|
7786
|
+
this.#hideDemoProvider = options.hideDemoProvider ?? false;
|
|
6997
7787
|
mkdirSync(dirname3(databasePath), { recursive: true });
|
|
6998
7788
|
this.#database = new DatabaseSync2(databasePath);
|
|
6999
7789
|
this.#database.exec("PRAGMA journal_mode = WAL; PRAGMA foreign_keys = ON;");
|
|
@@ -7041,13 +7831,15 @@ var init_sqlite_usage_repository = __esm({
|
|
|
7041
7831
|
}
|
|
7042
7832
|
const quotaStatement = this.#database.prepare(
|
|
7043
7833
|
`INSERT INTO quota_buckets (
|
|
7044
|
-
provider_id, id, billing_domain_id, label, used_percent,
|
|
7834
|
+
provider_id, id, billing_domain_id, label, used_percent, window_duration_minutes,
|
|
7835
|
+
resets_at, authority, observed_at,
|
|
7045
7836
|
scope, status, limit_amount, limit_currency, fallback_status
|
|
7046
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
7837
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
7047
7838
|
ON CONFLICT(provider_id, id) DO UPDATE SET
|
|
7048
7839
|
billing_domain_id = excluded.billing_domain_id,
|
|
7049
7840
|
label = excluded.label,
|
|
7050
7841
|
used_percent = excluded.used_percent,
|
|
7842
|
+
window_duration_minutes = excluded.window_duration_minutes,
|
|
7051
7843
|
resets_at = excluded.resets_at,
|
|
7052
7844
|
authority = excluded.authority,
|
|
7053
7845
|
observed_at = excluded.observed_at,
|
|
@@ -7064,6 +7856,7 @@ var init_sqlite_usage_repository = __esm({
|
|
|
7064
7856
|
bucket.billingDomainId,
|
|
7065
7857
|
bucket.label,
|
|
7066
7858
|
bucket.usedPercent,
|
|
7859
|
+
bucket.windowDurationMinutes ?? null,
|
|
7067
7860
|
bucket.resetsAt,
|
|
7068
7861
|
bucket.authority,
|
|
7069
7862
|
snapshot2.observedAt,
|
|
@@ -7480,16 +8273,43 @@ var init_sqlite_usage_repository = __esm({
|
|
|
7480
8273
|
`SELECT id, display_name, last_success_at, last_error, last_error_code, last_recovery
|
|
7481
8274
|
FROM providers ORDER BY id`
|
|
7482
8275
|
).all();
|
|
7483
|
-
const
|
|
8276
|
+
const visibleProviders = this.#hideDemoProvider ? providers.filter((provider) => provider.id !== "demo") : providers;
|
|
8277
|
+
const overviews = visibleProviders.map(
|
|
8278
|
+
(provider) => this.#getProviderOverview(provider, now, query)
|
|
8279
|
+
);
|
|
7484
8280
|
const riskSummary = buildRiskSummary(overviews);
|
|
8281
|
+
const planSubscriptions = this.getPlanSubscriptions();
|
|
7485
8282
|
return {
|
|
7486
8283
|
generatedAt: now.toISOString(),
|
|
7487
8284
|
globalSummary: buildGlobalSummary(overviews, riskSummary, now, query),
|
|
7488
|
-
workbench: buildTokenMoneyWorkbench(
|
|
8285
|
+
workbench: buildTokenMoneyWorkbench(
|
|
8286
|
+
overviews,
|
|
8287
|
+
now,
|
|
8288
|
+
query,
|
|
8289
|
+
planSubscriptions,
|
|
8290
|
+
this.#planBillingPeriodSummaries(planSubscriptions, now, query)
|
|
8291
|
+
),
|
|
7489
8292
|
providers: overviews,
|
|
7490
8293
|
riskSummary
|
|
7491
8294
|
};
|
|
7492
8295
|
}
|
|
8296
|
+
getAgentProviderIndex(now) {
|
|
8297
|
+
const providers = this.#database.prepare("SELECT id, display_name FROM providers ORDER BY id").all();
|
|
8298
|
+
return {
|
|
8299
|
+
generatedAt: now.toISOString(),
|
|
8300
|
+
providers: providers.filter(
|
|
8301
|
+
(provider) => provider.id !== "opencode" && (!this.#hideDemoProvider || provider.id !== "demo")
|
|
8302
|
+
).map((provider) => ({ id: provider.id, displayName: provider.display_name }))
|
|
8303
|
+
};
|
|
8304
|
+
}
|
|
8305
|
+
getProviderOverview(now, providerId, query = {}) {
|
|
8306
|
+
if (this.#hideDemoProvider && providerId === "demo") return null;
|
|
8307
|
+
const provider = this.#database.prepare(
|
|
8308
|
+
`SELECT id, display_name, last_success_at, last_error, last_error_code, last_recovery
|
|
8309
|
+
FROM providers WHERE id = ?`
|
|
8310
|
+
).get(providerId);
|
|
8311
|
+
return provider ? this.#getProviderOverview(provider, now, query) : null;
|
|
8312
|
+
}
|
|
7493
8313
|
saveExchangeRateSnapshot(snapshot2) {
|
|
7494
8314
|
this.#database.prepare(
|
|
7495
8315
|
`INSERT INTO exchange_rate_snapshots (
|
|
@@ -7565,6 +8385,57 @@ var init_sqlite_usage_repository = __esm({
|
|
|
7565
8385
|
settings.startAtLogin ? 1 : 0
|
|
7566
8386
|
);
|
|
7567
8387
|
}
|
|
8388
|
+
getPlanSubscriptions() {
|
|
8389
|
+
const rows = this.#database.prepare(
|
|
8390
|
+
`SELECT provider_id, billing_domain_id, plan_id, display_name, amount, currency,
|
|
8391
|
+
billing_period, anchor_date, price_source, updated_at
|
|
8392
|
+
FROM plan_subscriptions
|
|
8393
|
+
ORDER BY provider_id, billing_domain_id`
|
|
8394
|
+
).all();
|
|
8395
|
+
return rows.map((row) => ({
|
|
8396
|
+
providerId: row.provider_id,
|
|
8397
|
+
billingDomainId: row.billing_domain_id,
|
|
8398
|
+
planId: row.plan_id,
|
|
8399
|
+
displayName: row.display_name,
|
|
8400
|
+
amount: row.amount,
|
|
8401
|
+
currency: row.currency,
|
|
8402
|
+
billingPeriod: row.billing_period,
|
|
8403
|
+
anchorDate: row.anchor_date,
|
|
8404
|
+
priceSource: row.price_source,
|
|
8405
|
+
updatedAt: row.updated_at
|
|
8406
|
+
}));
|
|
8407
|
+
}
|
|
8408
|
+
savePlanSubscription(subscription) {
|
|
8409
|
+
this.#database.prepare(
|
|
8410
|
+
`INSERT INTO plan_subscriptions (
|
|
8411
|
+
provider_id, billing_domain_id, plan_id, display_name, amount, currency,
|
|
8412
|
+
billing_period, anchor_date, price_source, updated_at
|
|
8413
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8414
|
+
ON CONFLICT(provider_id, billing_domain_id) DO UPDATE SET
|
|
8415
|
+
plan_id = excluded.plan_id,
|
|
8416
|
+
display_name = excluded.display_name,
|
|
8417
|
+
amount = excluded.amount,
|
|
8418
|
+
currency = excluded.currency,
|
|
8419
|
+
billing_period = excluded.billing_period,
|
|
8420
|
+
anchor_date = excluded.anchor_date,
|
|
8421
|
+
price_source = excluded.price_source,
|
|
8422
|
+
updated_at = excluded.updated_at`
|
|
8423
|
+
).run(
|
|
8424
|
+
subscription.providerId,
|
|
8425
|
+
subscription.billingDomainId,
|
|
8426
|
+
subscription.planId,
|
|
8427
|
+
subscription.displayName,
|
|
8428
|
+
subscription.amount,
|
|
8429
|
+
subscription.currency,
|
|
8430
|
+
subscription.billingPeriod,
|
|
8431
|
+
subscription.anchorDate,
|
|
8432
|
+
subscription.priceSource,
|
|
8433
|
+
subscription.updatedAt
|
|
8434
|
+
);
|
|
8435
|
+
}
|
|
8436
|
+
deletePlanSubscription(providerId, billingDomainId) {
|
|
8437
|
+
this.#database.prepare("DELETE FROM plan_subscriptions WHERE provider_id = ? AND billing_domain_id = ?").run(providerId, billingDomainId);
|
|
8438
|
+
}
|
|
7568
8439
|
getNotificationState(key) {
|
|
7569
8440
|
const row = this.#database.prepare("SELECT value FROM notification_state WHERE key = ?").get(key);
|
|
7570
8441
|
return row?.value ?? null;
|
|
@@ -7720,6 +8591,13 @@ var init_sqlite_usage_repository = __esm({
|
|
|
7720
8591
|
throw error;
|
|
7721
8592
|
}
|
|
7722
8593
|
}
|
|
8594
|
+
async deleteDemoProviderDataAsync() {
|
|
8595
|
+
await runDatabaseWorker({
|
|
8596
|
+
databasePath: this.#databasePath,
|
|
8597
|
+
operation: "delete-demo-data",
|
|
8598
|
+
deleteDemoDataSql: DELETE_DEMO_PROVIDER_DATA_SQL
|
|
8599
|
+
});
|
|
8600
|
+
}
|
|
7723
8601
|
deleteDerivedRetailCosts() {
|
|
7724
8602
|
this.#database.prepare(DELETE_DERIVED_RETAIL_COSTS_SQL).run();
|
|
7725
8603
|
}
|
|
@@ -7823,7 +8701,8 @@ var init_sqlite_usage_repository = __esm({
|
|
|
7823
8701
|
}
|
|
7824
8702
|
#getBillingDomainOverview(providerId, domain, now, query, forecasts, forecastCoverage, degradedDiagnostic, legacyProviderFailure = null) {
|
|
7825
8703
|
const quotaRows = this.#database.prepare(
|
|
7826
|
-
`SELECT id, billing_domain_id, label, used_percent,
|
|
8704
|
+
`SELECT id, billing_domain_id, label, used_percent, window_duration_minutes, resets_at,
|
|
8705
|
+
authority, observed_at,
|
|
7827
8706
|
scope, status, limit_amount, limit_currency, fallback_status
|
|
7828
8707
|
FROM quota_buckets WHERE provider_id = ? AND billing_domain_id = ? ORDER BY id`
|
|
7829
8708
|
).all(providerId, domain.id);
|
|
@@ -8015,6 +8894,101 @@ var init_sqlite_usage_repository = __esm({
|
|
|
8015
8894
|
coverageByDomain
|
|
8016
8895
|
};
|
|
8017
8896
|
}
|
|
8897
|
+
/**
|
|
8898
|
+
* Cycle-to-date evidence for every subscription that declares a renewal date.
|
|
8899
|
+
* Each Provider is measured over its own billing period, which is why this
|
|
8900
|
+
* cannot reuse the one rolling window the rest of the workbench shares.
|
|
8901
|
+
*/
|
|
8902
|
+
#planBillingPeriodSummaries(subscriptions, now, query) {
|
|
8903
|
+
const normalized = normalizeUsageQuery(now, query);
|
|
8904
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
8905
|
+
for (const subscription of subscriptions) {
|
|
8906
|
+
if (!subscription.anchorDate) continue;
|
|
8907
|
+
const period = billingPeriodContaining(
|
|
8908
|
+
subscription.anchorDate,
|
|
8909
|
+
subscription.billingPeriod,
|
|
8910
|
+
now
|
|
8911
|
+
);
|
|
8912
|
+
if (!period) continue;
|
|
8913
|
+
const observedThrough = new Date(Math.min(now.getTime(), period.end.getTime()));
|
|
8914
|
+
summaries.set(`${subscription.providerId}:${subscription.billingDomainId}`, {
|
|
8915
|
+
start: period.start.toISOString(),
|
|
8916
|
+
end: period.end.toISOString(),
|
|
8917
|
+
observedThrough: observedThrough.toISOString(),
|
|
8918
|
+
...this.#rangeUsageSummary(
|
|
8919
|
+
subscription.providerId,
|
|
8920
|
+
subscription.billingDomainId,
|
|
8921
|
+
period.start,
|
|
8922
|
+
observedThrough,
|
|
8923
|
+
normalized.comparisonCurrency
|
|
8924
|
+
)
|
|
8925
|
+
});
|
|
8926
|
+
}
|
|
8927
|
+
return summaries;
|
|
8928
|
+
}
|
|
8929
|
+
#rangeUsageSummary(providerId, billingDomainId, start, end, comparisonCurrency) {
|
|
8930
|
+
const startIso = start.toISOString();
|
|
8931
|
+
const endIso = end.toISOString();
|
|
8932
|
+
const usage = this.#database.prepare(
|
|
8933
|
+
`SELECT id, model, observed_at, authority, total_tokens, input_tokens, output_tokens,
|
|
8934
|
+
reasoning_tokens, cache_read_tokens, cache_write_tokens, cache_write_5m_tokens,
|
|
8935
|
+
cache_write_1h_tokens, source_reported_total_tokens,
|
|
8936
|
+
unclassified_tokens, total_derivation, model_attribution, time_precision,
|
|
8937
|
+
usage_scope, aggregation_temporality, reasoning_semantics,
|
|
8938
|
+
cache_read_semantics, cache_write_semantics
|
|
8939
|
+
FROM usage_observations
|
|
8940
|
+
WHERE provider_id = ? AND billing_domain_id = ?
|
|
8941
|
+
AND observed_at >= ? AND observed_at < ?
|
|
8942
|
+
AND ${additiveUsagePredicate()}
|
|
8943
|
+
ORDER BY observed_at, id`
|
|
8944
|
+
).all(providerId, billingDomainId, startIso, endIso);
|
|
8945
|
+
const costs = this.#database.prepare(
|
|
8946
|
+
`SELECT id, source_id, billing_domain_id, observed_at, kind, amount, currency, authority,
|
|
8947
|
+
price_snapshot_id, price_snapshot_version, price_snapshot_source,
|
|
8948
|
+
price_snapshot_canonical_model, price_snapshot_effective_at,
|
|
8949
|
+
price_snapshot_effective_until, price_snapshot_currency,
|
|
8950
|
+
price_snapshot_rates_json, price_snapshot_source_url,
|
|
8951
|
+
price_snapshot_context_tier, model, usage_observation_id, priced_tokens,
|
|
8952
|
+
line_items_json, calculated_at
|
|
8953
|
+
FROM cost_records
|
|
8954
|
+
WHERE provider_id = ? AND billing_domain_id = ?
|
|
8955
|
+
AND observed_at >= ? AND observed_at < ?
|
|
8956
|
+
ORDER BY observed_at, id`
|
|
8957
|
+
).all(providerId, billingDomainId, startIso, endIso);
|
|
8958
|
+
const rateRows = this.#database.prepare(
|
|
8959
|
+
`SELECT id, base_currency, quote_currency, rate, observed_at, source
|
|
8960
|
+
FROM exchange_rate_snapshots WHERE observed_at < ? ORDER BY observed_at DESC, id`
|
|
8961
|
+
).all(endIso);
|
|
8962
|
+
const rateByCurrency = /* @__PURE__ */ new Map();
|
|
8963
|
+
for (const row of rateRows) {
|
|
8964
|
+
if (row.quote_currency === comparisonCurrency && !rateByCurrency.has(row.base_currency)) {
|
|
8965
|
+
rateByCurrency.set(row.base_currency, row);
|
|
8966
|
+
}
|
|
8967
|
+
}
|
|
8968
|
+
const evidence = emptyTokenEvidence();
|
|
8969
|
+
for (const row of usage) addTokenEvidence(evidence, row);
|
|
8970
|
+
const finished = finishTokenEvidence(evidence);
|
|
8971
|
+
const usedRates = /* @__PURE__ */ new Map();
|
|
8972
|
+
const historyCosts = summarizeHistoryCosts(
|
|
8973
|
+
costs,
|
|
8974
|
+
finished.recordedTokens,
|
|
8975
|
+
rateByCurrency,
|
|
8976
|
+
comparisonCurrency,
|
|
8977
|
+
end,
|
|
8978
|
+
usedRates
|
|
8979
|
+
);
|
|
8980
|
+
return {
|
|
8981
|
+
recordedTokens: finished.recordedTokens,
|
|
8982
|
+
observationCount: finished.observationCount,
|
|
8983
|
+
retailEquivalent: buildWorkbenchMetric(
|
|
8984
|
+
historyCosts,
|
|
8985
|
+
"retail-equivalent",
|
|
8986
|
+
comparisonCurrency,
|
|
8987
|
+
finished.recordedTokens,
|
|
8988
|
+
[...usedRates.values()]
|
|
8989
|
+
)
|
|
8990
|
+
};
|
|
8991
|
+
}
|
|
8018
8992
|
#getBillingHistory(providerId, billingDomainId, now, query) {
|
|
8019
8993
|
const normalized = normalizeUsageQuery(now, query);
|
|
8020
8994
|
const start = normalized.start.toISOString();
|
|
@@ -8238,6 +9212,7 @@ var init_sqlite_usage_repository = __esm({
|
|
|
8238
9212
|
billing_domain_id TEXT NOT NULL,
|
|
8239
9213
|
label TEXT NOT NULL,
|
|
8240
9214
|
used_percent REAL,
|
|
9215
|
+
window_duration_minutes INTEGER,
|
|
8241
9216
|
resets_at TEXT,
|
|
8242
9217
|
authority TEXT NOT NULL,
|
|
8243
9218
|
observed_at TEXT NOT NULL,
|
|
@@ -8422,6 +9397,19 @@ var init_sqlite_usage_repository = __esm({
|
|
|
8422
9397
|
key TEXT PRIMARY KEY,
|
|
8423
9398
|
value TEXT NOT NULL
|
|
8424
9399
|
);
|
|
9400
|
+
CREATE TABLE IF NOT EXISTS plan_subscriptions (
|
|
9401
|
+
provider_id TEXT NOT NULL,
|
|
9402
|
+
billing_domain_id TEXT NOT NULL,
|
|
9403
|
+
plan_id TEXT,
|
|
9404
|
+
display_name TEXT NOT NULL,
|
|
9405
|
+
amount REAL NOT NULL,
|
|
9406
|
+
currency TEXT NOT NULL,
|
|
9407
|
+
billing_period TEXT NOT NULL,
|
|
9408
|
+
anchor_date TEXT,
|
|
9409
|
+
price_source TEXT NOT NULL,
|
|
9410
|
+
updated_at TEXT NOT NULL,
|
|
9411
|
+
PRIMARY KEY (provider_id, billing_domain_id)
|
|
9412
|
+
);
|
|
8425
9413
|
`);
|
|
8426
9414
|
const usageColumns = this.#database.prepare("PRAGMA table_info(usage_observations)").all();
|
|
8427
9415
|
if (!usageColumns.some((column) => column.name === "total_tokens")) {
|
|
@@ -8581,6 +9569,7 @@ var init_sqlite_usage_repository = __esm({
|
|
|
8581
9569
|
for (const [name, type] of [
|
|
8582
9570
|
["scope", "TEXT"],
|
|
8583
9571
|
["status", "TEXT"],
|
|
9572
|
+
["window_duration_minutes", "INTEGER"],
|
|
8584
9573
|
["limit_amount", "REAL"],
|
|
8585
9574
|
["limit_currency", "TEXT"],
|
|
8586
9575
|
["fallback_status", "TEXT"]
|
|
@@ -8589,6 +9578,10 @@ var init_sqlite_usage_repository = __esm({
|
|
|
8589
9578
|
this.#database.exec(`ALTER TABLE quota_buckets ADD COLUMN ${name} ${type}`);
|
|
8590
9579
|
}
|
|
8591
9580
|
}
|
|
9581
|
+
const planColumns = this.#database.prepare("PRAGMA table_info(plan_subscriptions)").all();
|
|
9582
|
+
if (planColumns.length > 0 && !planColumns.some((column) => column.name === "anchor_date")) {
|
|
9583
|
+
this.#database.exec("ALTER TABLE plan_subscriptions ADD COLUMN anchor_date TEXT");
|
|
9584
|
+
}
|
|
8592
9585
|
}
|
|
8593
9586
|
};
|
|
8594
9587
|
}
|
|
@@ -8606,17 +9599,21 @@ import { isAbsolute as isAbsolute3, join as join8, resolve as resolve3 } from "p
|
|
|
8606
9599
|
import { fileURLToPath } from "url";
|
|
8607
9600
|
async function runDaemon(home) {
|
|
8608
9601
|
await mkdir4(home, { recursive: true, mode: 448 });
|
|
8609
|
-
const
|
|
9602
|
+
const demoEnabled = process.env.AGENT_USAGE_DEMO === "1";
|
|
9603
|
+
const repository = new SqliteUsageRepository(join8(home, "usage.sqlite"), {
|
|
9604
|
+
hideDemoProvider: !demoEnabled
|
|
9605
|
+
});
|
|
8610
9606
|
const keychainService = process.env.AGENT_USAGE_KEYCHAIN_SERVICE;
|
|
8611
9607
|
const launchAgentLabel = process.env.AGENT_USAGE_LAUNCH_AGENT_LABEL;
|
|
8612
9608
|
const nodeImport = process.env.AGENT_USAGE_NODE_IMPORT;
|
|
8613
9609
|
const secretStore = new MacOsKeychainSecretStore(void 0, {
|
|
8614
9610
|
service: keychainService
|
|
8615
9611
|
});
|
|
9612
|
+
const openCodeLocalHistoryClient = new CliOpenCodeLocalHistoryClient();
|
|
8616
9613
|
const application = new UsageApplication({
|
|
8617
9614
|
repository,
|
|
8618
9615
|
connectors: [
|
|
8619
|
-
...
|
|
9616
|
+
...demoEnabled ? [createDemoConnector()] : [],
|
|
8620
9617
|
new CodexConnector(
|
|
8621
9618
|
new StdioCodexAccountClient(),
|
|
8622
9619
|
void 0,
|
|
@@ -8630,8 +9627,9 @@ async function runDaemon(home) {
|
|
|
8630
9627
|
accountClient: new OfficialOpenCodeGoClient({
|
|
8631
9628
|
authReader: new OpenCodeAuthFileReader()
|
|
8632
9629
|
}),
|
|
8633
|
-
localHistoryClient:
|
|
9630
|
+
localHistoryClient: openCodeLocalHistoryClient
|
|
8634
9631
|
}),
|
|
9632
|
+
new OpenCodeLocalConnector({ localHistoryClient: openCodeLocalHistoryClient }),
|
|
8635
9633
|
new GrokBuildConnector({
|
|
8636
9634
|
billingClient: new StdioGrokBillingClient(),
|
|
8637
9635
|
historyClient: localTranscriptClient("grok", home)
|
|
@@ -8655,11 +9653,11 @@ async function runDaemon(home) {
|
|
|
8655
9653
|
...keychainService ? { AGENT_USAGE_KEYCHAIN_SERVICE: keychainService } : {},
|
|
8656
9654
|
...launchAgentLabel ? { AGENT_USAGE_LAUNCH_AGENT_LABEL: launchAgentLabel } : {},
|
|
8657
9655
|
...nodeImport ? { AGENT_USAGE_NODE_IMPORT: nodeImport } : {},
|
|
8658
|
-
...
|
|
9656
|
+
...demoEnabled ? { AGENT_USAGE_DEMO: "1" } : {}
|
|
8659
9657
|
}
|
|
8660
9658
|
}),
|
|
8661
9659
|
connectorPolicies: Object.fromEntries(
|
|
8662
|
-
["codex", "claude-code", "opencode-go", "grok", "xai-api"].map((id) => [
|
|
9660
|
+
["codex", "claude-code", "opencode-go", "opencode", "grok", "xai-api"].map((id) => [
|
|
8663
9661
|
id,
|
|
8664
9662
|
{ minimumIntervalMs: 5 * 60 * 1e3, timeoutMs: id === "claude-code" ? 25e3 : 2e4 }
|
|
8665
9663
|
])
|
|
@@ -8687,6 +9685,13 @@ async function runDaemon(home) {
|
|
|
8687
9685
|
origin: server.origin,
|
|
8688
9686
|
apiToken: server.apiToken
|
|
8689
9687
|
});
|
|
9688
|
+
if (!demoEnabled) {
|
|
9689
|
+
void repository.deleteDemoProviderDataAsync().catch(() => {
|
|
9690
|
+
process.stderr.write(
|
|
9691
|
+
"Agent Usage: stale demo data remains hidden and will be cleaned on a later startup.\n"
|
|
9692
|
+
);
|
|
9693
|
+
});
|
|
9694
|
+
}
|
|
8690
9695
|
process.stderr.write(`Agent Usage: web service ready at ${server.origin}
|
|
8691
9696
|
`);
|
|
8692
9697
|
process.stderr.write("Agent Usage: updating cached usage in the background\u2026\n");
|
|
@@ -8788,6 +9793,7 @@ var init_runtime = __esm({
|
|
|
8788
9793
|
init_claude_usage_screen_client();
|
|
8789
9794
|
init_codex_connector();
|
|
8790
9795
|
init_stdio_codex_account_client();
|
|
9796
|
+
init_opencode_local_connector();
|
|
8791
9797
|
init_local_opencode_history_client();
|
|
8792
9798
|
init_official_opencode_go_client();
|
|
8793
9799
|
init_opencode_auth_reader();
|