agent-usage-all-in-one 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/README.zh-CN.md +7 -1
- package/dist/cli.js +867 -98
- 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/55QxuuK3.js +1 -0
- package/dist/web/_app/immutable/chunks/CIxAYfti.js +1 -0
- package/dist/web/_app/immutable/entry/app.zHTQ4fOQ.js +2 -0
- package/dist/web/_app/immutable/entry/start.gNCaHF0d.js +1 -0
- package/dist/web/_app/immutable/nodes/0.f03zL-fK.js +1 -0
- package/dist/web/_app/immutable/nodes/1.DjE9OLQ8.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 +1 -1
- package/static/brand/agent-usage-showcase.jpg +0 -0
- package/static/brands/README.md +4 -4
- package/dist/web/_app/immutable/assets/2.BiUr1b86.css +0 -1
- package/dist/web/_app/immutable/chunks/BW9s3L2z.js +0 -3
- package/dist/web/_app/immutable/chunks/CwKqhBmI.js +0 -1
- package/dist/web/_app/immutable/entry/app.CJoUfLXD.js +0 -2
- package/dist/web/_app/immutable/entry/start.DEFna-Tq.js +0 -1
- package/dist/web/_app/immutable/nodes/0.DTcMU-UW.js +0 -1
- package/dist/web/_app/immutable/nodes/1.BRZSpfvp.js +0 -1
- package/dist/web/_app/immutable/nodes/2.BKHXaqdm.js +0 -43
- package/dist/web/brand/agent-usage-banner.svg +0 -27
- package/static/brand/agent-usage-banner.svg +0 -27
package/dist/cli.js
CHANGED
|
@@ -749,16 +749,15 @@ function priceObservation(providerId, observation, catalog, calculatedAt) {
|
|
|
749
749
|
ratePerMillion: entry.cacheWriteRatesPerMillion.oneHour
|
|
750
750
|
}
|
|
751
751
|
].filter((item) => item.tokens > 0) : null;
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
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) {
|
|
756
755
|
billableTokens.push({ tokenKind: "cache-write", tokens: cacheWriteTokens });
|
|
757
756
|
}
|
|
758
757
|
const nonZero = billableTokens.filter((item) => item.tokens > 0);
|
|
759
758
|
if (nonZero.some((item) => entry.ratesPerMillion[item.tokenKind] === null)) {
|
|
760
759
|
return unavailable(
|
|
761
|
-
normalized.cacheWriteTokens > 0 ? "pricing-tier-ambiguous" : "token-kinds-incomplete"
|
|
760
|
+
normalized.cacheWriteTokens > 0 && !cacheWriteFree ? "pricing-tier-ambiguous" : "token-kinds-incomplete"
|
|
762
761
|
);
|
|
763
762
|
}
|
|
764
763
|
const lineItems = nonZero.map((item) => {
|
|
@@ -777,6 +776,14 @@ function priceObservation(providerId, observation, catalog, calculatedAt) {
|
|
|
777
776
|
}))
|
|
778
777
|
);
|
|
779
778
|
}
|
|
779
|
+
if (cacheWriteFree) {
|
|
780
|
+
lineItems.push({
|
|
781
|
+
tokenKind: "cache-write",
|
|
782
|
+
tokens: cacheWriteTokens,
|
|
783
|
+
ratePerMillion: 0,
|
|
784
|
+
amount: 0
|
|
785
|
+
});
|
|
786
|
+
}
|
|
780
787
|
const pricedTokens = lineItems.reduce((total, item) => total + item.tokens, 0);
|
|
781
788
|
if (pricedTokens !== normalized.recordedTokens) return unavailable("token-kinds-incomplete");
|
|
782
789
|
const amount = preciseMoney(lineItems.reduce((total, item) => total + item.amount, 0));
|
|
@@ -1184,6 +1191,382 @@ var init_retail_pricing = __esm({
|
|
|
1184
1191
|
}
|
|
1185
1192
|
});
|
|
1186
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
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
});
|
|
1569
|
+
|
|
1187
1570
|
// src/core/usage-application.ts
|
|
1188
1571
|
function safeConnectorFailure(error) {
|
|
1189
1572
|
if (error instanceof Error && "code" in error && typeof error.code === "string" && "recovery" in error && typeof error.recovery === "string") {
|
|
@@ -1289,6 +1672,12 @@ async function withTimeout(promise, timeoutMs, message) {
|
|
|
1289
1672
|
function notificationLevel(value) {
|
|
1290
1673
|
return value === "5" ? 2 : value === "20" ? 1 : 0;
|
|
1291
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
|
+
}
|
|
1292
1681
|
var UsageApplication;
|
|
1293
1682
|
var init_usage_application = __esm({
|
|
1294
1683
|
"src/core/usage-application.ts"() {
|
|
@@ -1297,6 +1686,7 @@ var init_usage_application = __esm({
|
|
|
1297
1686
|
init_redaction();
|
|
1298
1687
|
init_usage_export();
|
|
1299
1688
|
init_retail_pricing();
|
|
1689
|
+
init_plan_pricing();
|
|
1300
1690
|
UsageApplication = class {
|
|
1301
1691
|
#repository;
|
|
1302
1692
|
#connectors;
|
|
@@ -1310,10 +1700,13 @@ var init_usage_application = __esm({
|
|
|
1310
1700
|
#notifier;
|
|
1311
1701
|
#startAtLoginManager;
|
|
1312
1702
|
#priceCatalog;
|
|
1703
|
+
#planCatalog;
|
|
1313
1704
|
#refreshPromise = null;
|
|
1314
1705
|
#refreshMode = null;
|
|
1315
1706
|
#backgroundPromise = null;
|
|
1316
1707
|
#queuedHardRebuildPromise = null;
|
|
1708
|
+
#queuedUserProcessingPromise = null;
|
|
1709
|
+
#userInitiatedProcessing = false;
|
|
1317
1710
|
#databaseWriteQueue = Promise.resolve();
|
|
1318
1711
|
#processingStatus;
|
|
1319
1712
|
constructor(options) {
|
|
@@ -1329,6 +1722,7 @@ var init_usage_application = __esm({
|
|
|
1329
1722
|
this.#notifier = options.notifier;
|
|
1330
1723
|
this.#startAtLoginManager = options.startAtLoginManager;
|
|
1331
1724
|
this.#priceCatalog = options.priceCatalog === void 0 ? OFFICIAL_PRICING_CATALOG : options.priceCatalog;
|
|
1725
|
+
this.#planCatalog = options.planCatalog ?? SUBSCRIPTION_PLAN_CATALOG;
|
|
1332
1726
|
this.#processingStatus = createProcessingStatus(this.#clock().toISOString(), false);
|
|
1333
1727
|
}
|
|
1334
1728
|
async #backfillRetailCosts(mode) {
|
|
@@ -1375,40 +1769,50 @@ var init_usage_application = __esm({
|
|
|
1375
1769
|
getProcessingStatus() {
|
|
1376
1770
|
return structuredClone(this.#processingStatus);
|
|
1377
1771
|
}
|
|
1378
|
-
startBackgroundProcessing() {
|
|
1379
|
-
return this.#startProcessing("incremental");
|
|
1772
|
+
startBackgroundProcessing(options = {}) {
|
|
1773
|
+
return this.#startProcessing("incremental", options.userInitiated === true);
|
|
1380
1774
|
}
|
|
1381
1775
|
startHardRebuild() {
|
|
1382
|
-
return this.#startProcessing("hard-rebuild");
|
|
1776
|
+
return this.#startProcessing("hard-rebuild", true);
|
|
1383
1777
|
}
|
|
1384
|
-
#startProcessing(mode) {
|
|
1778
|
+
#startProcessing(mode, userInitiated) {
|
|
1385
1779
|
if (this.#backgroundPromise) {
|
|
1386
1780
|
if (mode === "hard-rebuild" && !this.#processingStatus.hardRebuild) {
|
|
1387
1781
|
this.#queuedHardRebuildPromise ??= this.#backgroundPromise.then(
|
|
1388
|
-
() => this.#startProcessing("hard-rebuild"),
|
|
1389
|
-
() => this.#startProcessing("hard-rebuild")
|
|
1782
|
+
() => this.#startProcessing("hard-rebuild", true),
|
|
1783
|
+
() => this.#startProcessing("hard-rebuild", true)
|
|
1390
1784
|
).finally(() => {
|
|
1391
1785
|
this.#queuedHardRebuildPromise = null;
|
|
1392
1786
|
});
|
|
1393
1787
|
return this.#queuedHardRebuildPromise;
|
|
1394
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
|
+
}
|
|
1395
1798
|
return this.#backgroundPromise;
|
|
1396
1799
|
}
|
|
1800
|
+
this.#userInitiatedProcessing = userInitiated || mode === "hard-rebuild";
|
|
1397
1801
|
this.#processingStatus = createProcessingStatus(
|
|
1398
1802
|
this.#clock().toISOString(),
|
|
1399
1803
|
mode === "hard-rebuild"
|
|
1400
1804
|
);
|
|
1401
|
-
this.#backgroundPromise = this.#runProcessing(mode).finally(
|
|
1402
|
-
|
|
1403
|
-
|
|
1805
|
+
this.#backgroundPromise = this.#runProcessing(mode, this.#userInitiatedProcessing).finally(
|
|
1806
|
+
() => {
|
|
1807
|
+
this.#backgroundPromise = null;
|
|
1808
|
+
this.#userInitiatedProcessing = false;
|
|
1809
|
+
}
|
|
1810
|
+
);
|
|
1404
1811
|
return this.#backgroundPromise;
|
|
1405
1812
|
}
|
|
1406
|
-
async #runProcessing(mode) {
|
|
1813
|
+
async #runProcessing(mode, userInitiated) {
|
|
1407
1814
|
await this.#runModule("discovery", () => this.discoverConnectors().then(() => void 0));
|
|
1408
|
-
await this.#runModule(
|
|
1409
|
-
"usage",
|
|
1410
|
-
() => this.refresh({ userInitiated: mode === "hard-rebuild", mode })
|
|
1411
|
-
);
|
|
1815
|
+
await this.#runModule("usage", () => this.refresh({ userInitiated, mode }));
|
|
1412
1816
|
await this.#runModule(
|
|
1413
1817
|
"pricing",
|
|
1414
1818
|
() => this.#queueDatabaseWrite(async () => {
|
|
@@ -1619,6 +2023,83 @@ var init_usage_application = __esm({
|
|
|
1619
2023
|
connectors: this.#repository.getConnectorRuntimeStates()
|
|
1620
2024
|
};
|
|
1621
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
|
+
}
|
|
1622
2103
|
async #sendNotificationTransitions() {
|
|
1623
2104
|
if (!this.#notifier) return;
|
|
1624
2105
|
const overview = this.#repository.getOverview(this.#clock());
|
|
@@ -1767,6 +2248,14 @@ var init_usage_application = __esm({
|
|
|
1767
2248
|
async getOverview(query = {}) {
|
|
1768
2249
|
return this.#repository.getOverview(this.#clock(), query);
|
|
1769
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
|
+
}
|
|
1770
2259
|
async exportUsage(request) {
|
|
1771
2260
|
const overview = this.#repository.getOverview(this.#clock(), request);
|
|
1772
2261
|
const accountIdentifiers = request.includeAccountIdentifiers ? this.#repository.getProviderAccountIdentifiers() : {};
|
|
@@ -2082,7 +2571,7 @@ var init_claude_code_connector = __esm({
|
|
|
2082
2571
|
usage: history.usage,
|
|
2083
2572
|
...history.usage.length > 0 && history.complete ? {
|
|
2084
2573
|
usageReconciliation: {
|
|
2085
|
-
|
|
2574
|
+
authoritativeIdPrefixes: ["claude-transcript:"],
|
|
2086
2575
|
retiredIdPrefixes: ["claude-otel:"]
|
|
2087
2576
|
}
|
|
2088
2577
|
} : {},
|
|
@@ -2637,22 +3126,50 @@ var init_claude_usage_screen_client = __esm({
|
|
|
2637
3126
|
|
|
2638
3127
|
// src/connectors/codex/codex-connector.ts
|
|
2639
3128
|
import { z as z2 } from "zod";
|
|
2640
|
-
function
|
|
2641
|
-
return
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
3129
|
+
function settledAccountDays(response, nowMs) {
|
|
3130
|
+
return (response?.dailyUsageBuckets ?? []).map((bucket) => ({
|
|
3131
|
+
day: bucket.startDate,
|
|
3132
|
+
startMs: Date.parse(`${bucket.startDate}T00:00:00.000Z`),
|
|
3133
|
+
tokens: bucket.tokens
|
|
3134
|
+
})).filter((day) => Number.isFinite(day.startMs) && day.startMs + DAY_MS <= nowMs);
|
|
3135
|
+
}
|
|
3136
|
+
function reconcileAccountDays(days, localUsage) {
|
|
3137
|
+
const reconciledDays = new Set(days.map((day) => day.day));
|
|
3138
|
+
const localTotals = /* @__PURE__ */ new Map();
|
|
3139
|
+
for (const observation of localUsage) {
|
|
3140
|
+
const day = observation.observedAt.slice(0, 10);
|
|
3141
|
+
if (!reconciledDays.has(day)) continue;
|
|
3142
|
+
const recorded = normalizeTokenObservation(observation).recordedTokens;
|
|
3143
|
+
localTotals.set(day, (localTotals.get(day) ?? 0) + recorded);
|
|
3144
|
+
}
|
|
3145
|
+
return days.flatMap((day) => {
|
|
3146
|
+
const localTotal = localTotals.get(day.day) ?? 0;
|
|
3147
|
+
if (localTotal > day.tokens) return [];
|
|
3148
|
+
const observedAt = new Date(day.startMs).toISOString();
|
|
3149
|
+
const accountObservation = {
|
|
3150
|
+
id: `codex:daily:${day.day}`,
|
|
3151
|
+
billingDomainId: "subscription",
|
|
3152
|
+
model: null,
|
|
3153
|
+
observedAt,
|
|
3154
|
+
sourceReportedTotalTokens: day.tokens,
|
|
3155
|
+
inputTokens: 0,
|
|
3156
|
+
outputTokens: 0,
|
|
3157
|
+
cacheReadTokens: 0,
|
|
3158
|
+
cacheWriteTokens: 0,
|
|
3159
|
+
modelAttribution: "unclassified",
|
|
3160
|
+
timePrecision: "day",
|
|
3161
|
+
usageScope: "account-wide",
|
|
3162
|
+
authority: "official-account"
|
|
3163
|
+
};
|
|
3164
|
+
if (localTotal === 0) return [accountObservation];
|
|
2649
3165
|
return [
|
|
3166
|
+
accountObservation,
|
|
2650
3167
|
{
|
|
2651
|
-
id: `codex-transcript:account-remainder:${day}`,
|
|
3168
|
+
id: `codex-transcript:account-remainder:${day.day}`,
|
|
2652
3169
|
billingDomainId: "subscription",
|
|
2653
3170
|
model: null,
|
|
2654
|
-
observedAt
|
|
2655
|
-
reconciledRemainderTokens:
|
|
3171
|
+
observedAt,
|
|
3172
|
+
reconciledRemainderTokens: day.tokens - localTotal,
|
|
2656
3173
|
inputTokens: 0,
|
|
2657
3174
|
outputTokens: 0,
|
|
2658
3175
|
cacheReadTokens: 0,
|
|
@@ -2709,23 +3226,6 @@ function mapQuotaBuckets(response) {
|
|
|
2709
3226
|
}
|
|
2710
3227
|
return buckets;
|
|
2711
3228
|
}
|
|
2712
|
-
function mapTokenUsage(response) {
|
|
2713
|
-
return (response?.dailyUsageBuckets ?? []).map((bucket) => ({
|
|
2714
|
-
id: `codex:daily:${bucket.startDate}`,
|
|
2715
|
-
billingDomainId: "subscription",
|
|
2716
|
-
model: null,
|
|
2717
|
-
observedAt: `${bucket.startDate}T00:00:00.000Z`,
|
|
2718
|
-
sourceReportedTotalTokens: bucket.tokens,
|
|
2719
|
-
inputTokens: 0,
|
|
2720
|
-
outputTokens: 0,
|
|
2721
|
-
cacheReadTokens: 0,
|
|
2722
|
-
cacheWriteTokens: 0,
|
|
2723
|
-
modelAttribution: "unclassified",
|
|
2724
|
-
timePrecision: "day",
|
|
2725
|
-
usageScope: "account-wide",
|
|
2726
|
-
authority: "official-account"
|
|
2727
|
-
}));
|
|
2728
|
-
}
|
|
2729
3229
|
function formatWindowLabel(durationMinutes, fallback) {
|
|
2730
3230
|
if (durationMinutes === 300) return "5 hour";
|
|
2731
3231
|
if (durationMinutes === 10080) return "Week";
|
|
@@ -2734,7 +3234,7 @@ function formatWindowLabel(durationMinutes, fallback) {
|
|
|
2734
3234
|
if (durationMinutes % 60 === 0) return `${durationMinutes / 60} hours`;
|
|
2735
3235
|
return `${durationMinutes} minutes`;
|
|
2736
3236
|
}
|
|
2737
|
-
var nullableNumeric, numeric, rateLimitWindowSchema, rateLimitSnapshotSchema, codexRateLimitsSchema, codexTokenUsageSchema, CodexConnector;
|
|
3237
|
+
var nullableNumeric, numeric, rateLimitWindowSchema, rateLimitSnapshotSchema, codexRateLimitsSchema, codexTokenUsageSchema, CodexConnector, DAY_MS;
|
|
2738
3238
|
var init_codex_connector = __esm({
|
|
2739
3239
|
"src/connectors/codex/codex-connector.ts"() {
|
|
2740
3240
|
"use strict";
|
|
@@ -2784,7 +3284,8 @@ var init_codex_connector = __esm({
|
|
|
2784
3284
|
this.#historyClient = historyClient;
|
|
2785
3285
|
}
|
|
2786
3286
|
async collect(options = { mode: "incremental" }) {
|
|
2787
|
-
const
|
|
3287
|
+
const now = this.#clock();
|
|
3288
|
+
const observedAt = now.toISOString();
|
|
2788
3289
|
const warnings = [];
|
|
2789
3290
|
let payload = null;
|
|
2790
3291
|
try {
|
|
@@ -2794,18 +3295,18 @@ var init_codex_connector = __esm({
|
|
|
2794
3295
|
}
|
|
2795
3296
|
const history = this.#historyClient ? await this.#historyClient.readUsage(options) : { usage: [], costs: [], complete: true };
|
|
2796
3297
|
const hasLocalHistory = history.usage.length > 0;
|
|
2797
|
-
const
|
|
2798
|
-
const canReconcile = history.complete &&
|
|
2799
|
-
const
|
|
3298
|
+
const settledDays = settledAccountDays(payload?.tokenUsage ?? null, now.getTime());
|
|
3299
|
+
const canReconcile = history.complete && settledDays.length > 0;
|
|
3300
|
+
const accountUsage = canReconcile ? reconcileAccountDays(settledDays, history.usage) : [];
|
|
2800
3301
|
if (!history.complete) warnings.push(incompleteTranscriptFailure2());
|
|
2801
3302
|
return {
|
|
2802
3303
|
provider: { id: "codex", displayName: "Codex" },
|
|
2803
3304
|
billingDomains: [{ id: "subscription", displayName: "Codex subscription" }],
|
|
2804
3305
|
quotaBuckets: payload ? mapQuotaBuckets(payload.rateLimits) : [],
|
|
2805
|
-
usage: [...accountUsage, ...history.usage
|
|
3306
|
+
usage: [...accountUsage, ...history.usage],
|
|
2806
3307
|
...hasLocalHistory && canReconcile ? {
|
|
2807
3308
|
usageReconciliation: {
|
|
2808
|
-
|
|
3309
|
+
authoritativeIdPrefixes: ["codex-transcript:", "codex:daily:"],
|
|
2809
3310
|
retiredIdPrefixes: []
|
|
2810
3311
|
}
|
|
2811
3312
|
} : {},
|
|
@@ -2815,6 +3316,7 @@ var init_codex_connector = __esm({
|
|
|
2815
3316
|
};
|
|
2816
3317
|
}
|
|
2817
3318
|
};
|
|
3319
|
+
DAY_MS = 24 * 60 * 60 * 1e3;
|
|
2818
3320
|
}
|
|
2819
3321
|
});
|
|
2820
3322
|
|
|
@@ -3128,7 +3630,7 @@ var init_opencode_local_connector = __esm({
|
|
|
3128
3630
|
quotaBuckets: [],
|
|
3129
3631
|
usage: requests.map(mapLocalUsage),
|
|
3130
3632
|
usageReconciliation: {
|
|
3131
|
-
|
|
3633
|
+
authoritativeIdPrefixes: ["opencode-local-request:"],
|
|
3132
3634
|
retiredIdPrefixes: []
|
|
3133
3635
|
},
|
|
3134
3636
|
costs: requests.flatMap(
|
|
@@ -3554,7 +4056,7 @@ var init_opencode_go_connector = __esm({
|
|
|
3554
4056
|
usage: [],
|
|
3555
4057
|
...localResult.status === "fulfilled" ? {
|
|
3556
4058
|
usageReconciliation: {
|
|
3557
|
-
|
|
4059
|
+
authoritativeIdPrefixes: ["opencode-request:"],
|
|
3558
4060
|
retiredIdPrefixes: ["opencode-session:"]
|
|
3559
4061
|
}
|
|
3560
4062
|
} : {},
|
|
@@ -3703,7 +4205,7 @@ var init_grok_build_connector = __esm({
|
|
|
3703
4205
|
usage: history.usage,
|
|
3704
4206
|
...history.usage.length > 0 && history.complete ? {
|
|
3705
4207
|
usageReconciliation: {
|
|
3706
|
-
|
|
4208
|
+
authoritativeIdPrefixes: ["grok-transcript:"],
|
|
3707
4209
|
retiredIdPrefixes: ["grok-otel:", "grok-headless:"]
|
|
3708
4210
|
}
|
|
3709
4211
|
} : {},
|
|
@@ -5041,6 +5543,25 @@ async function startLocalServer(options) {
|
|
|
5041
5543
|
sendOtlpProtobuf(response, 200);
|
|
5042
5544
|
return;
|
|
5043
5545
|
}
|
|
5546
|
+
if (request.method === "GET" && requestUrl.pathname === "/api/overview/providers") {
|
|
5547
|
+
sendJson(response, 200, await options.application.getAgentProviderIndex());
|
|
5548
|
+
return;
|
|
5549
|
+
}
|
|
5550
|
+
if (request.method === "GET" && requestUrl.pathname.startsWith("/api/overview/providers/")) {
|
|
5551
|
+
const providerId = decodeURIComponent(
|
|
5552
|
+
requestUrl.pathname.slice("/api/overview/providers/".length)
|
|
5553
|
+
);
|
|
5554
|
+
const provider = await options.application.getProviderOverview(
|
|
5555
|
+
providerId,
|
|
5556
|
+
parseUsageQuery(requestUrl)
|
|
5557
|
+
);
|
|
5558
|
+
if (!provider) {
|
|
5559
|
+
sendJson(response, 404, { error: "provider-not-found" });
|
|
5560
|
+
return;
|
|
5561
|
+
}
|
|
5562
|
+
sendJson(response, 200, provider);
|
|
5563
|
+
return;
|
|
5564
|
+
}
|
|
5044
5565
|
if (request.method === "GET" && requestUrl.pathname === "/api/overview") {
|
|
5045
5566
|
const query = parseUsageQuery(requestUrl);
|
|
5046
5567
|
sendJson(response, 200, await options.application.getOverview(query));
|
|
@@ -5051,6 +5572,13 @@ async function startLocalServer(options) {
|
|
|
5051
5572
|
sendJson(response, 403, { error: "invalid-origin" });
|
|
5052
5573
|
return;
|
|
5053
5574
|
}
|
|
5575
|
+
if (requestUrl.searchParams.get("background") === "true") {
|
|
5576
|
+
void options.application.startBackgroundProcessing({
|
|
5577
|
+
userInitiated: requestUrl.searchParams.get("mode") !== "automatic"
|
|
5578
|
+
});
|
|
5579
|
+
sendJson(response, 202, { accepted: true });
|
|
5580
|
+
return;
|
|
5581
|
+
}
|
|
5054
5582
|
await options.application.refresh({
|
|
5055
5583
|
userInitiated: requestUrl.searchParams.get("mode") !== "automatic"
|
|
5056
5584
|
});
|
|
@@ -5103,6 +5631,19 @@ async function startLocalServer(options) {
|
|
|
5103
5631
|
sendJson(response, 200, await options.application.clearData(input));
|
|
5104
5632
|
return;
|
|
5105
5633
|
}
|
|
5634
|
+
if (request.method === "GET" && requestUrl.pathname === "/api/plans") {
|
|
5635
|
+
sendJson(response, 200, await options.application.getPlanSettings());
|
|
5636
|
+
return;
|
|
5637
|
+
}
|
|
5638
|
+
if (request.method === "PATCH" && requestUrl.pathname === "/api/plans") {
|
|
5639
|
+
if (!validMutationOrigin(authentication, request, origin)) {
|
|
5640
|
+
sendJson(response, 403, { error: "invalid-origin" });
|
|
5641
|
+
return;
|
|
5642
|
+
}
|
|
5643
|
+
const input = planSubscriptionSchema.parse(await readJsonBody(request));
|
|
5644
|
+
sendJson(response, 200, await options.application.updatePlanSubscription(input));
|
|
5645
|
+
return;
|
|
5646
|
+
}
|
|
5106
5647
|
if (request.method === "GET" && requestUrl.pathname === "/api/monitoring") {
|
|
5107
5648
|
sendJson(response, 200, await options.application.getMonitoringStatus());
|
|
5108
5649
|
return;
|
|
@@ -5301,7 +5842,7 @@ function sendHtml(response, status, body) {
|
|
|
5301
5842
|
response.writeHead(status, { "content-type": "text/html; charset=utf-8" });
|
|
5302
5843
|
response.end(body);
|
|
5303
5844
|
}
|
|
5304
|
-
var SESSION_COOKIE, connectorActionSchema, usageQuerySchema, monitoringSettingsSchema, clearDataSchema, hardRebuildSchema;
|
|
5845
|
+
var SESSION_COOKIE, connectorActionSchema, usageQuerySchema, monitoringSettingsSchema, planSubscriptionSchema, clearDataSchema, hardRebuildSchema;
|
|
5305
5846
|
var init_local_server = __esm({
|
|
5306
5847
|
"src/server/local-server.ts"() {
|
|
5307
5848
|
"use strict";
|
|
@@ -5324,6 +5865,17 @@ var init_local_server = __esm({
|
|
|
5324
5865
|
notificationsEnabled: z9.boolean().optional(),
|
|
5325
5866
|
startAtLogin: z9.boolean().optional()
|
|
5326
5867
|
}).refine((value) => Object.keys(value).length > 0, "At least one setting is required");
|
|
5868
|
+
planSubscriptionSchema = z9.object({
|
|
5869
|
+
providerId: z9.string().min(1),
|
|
5870
|
+
billingDomainId: z9.string().min(1),
|
|
5871
|
+
plan: z9.object({
|
|
5872
|
+
planId: z9.string().min(1).nullable(),
|
|
5873
|
+
amount: z9.number().positive().finite().optional(),
|
|
5874
|
+
currency: z9.string().length(3).optional(),
|
|
5875
|
+
billingPeriod: z9.enum(["monthly", "annual"]).optional(),
|
|
5876
|
+
anchorDate: z9.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional()
|
|
5877
|
+
}).nullable()
|
|
5878
|
+
});
|
|
5327
5879
|
clearDataSchema = z9.object({ deleteProductSecrets: z9.boolean().default(false) });
|
|
5328
5880
|
hardRebuildSchema = z9.object({ confirmExpensiveOperation: z9.literal(true) });
|
|
5329
5881
|
}
|
|
@@ -5613,8 +6165,9 @@ function isForkedCodexSession(payload) {
|
|
|
5613
6165
|
const spawn7 = asObject(subagent?.thread_spawn);
|
|
5614
6166
|
return string(spawn7?.parent_thread_id) !== null;
|
|
5615
6167
|
}
|
|
5616
|
-
async function listTranscriptFiles(
|
|
6168
|
+
async function listTranscriptFiles(roots, cutoff) {
|
|
5617
6169
|
const files = [];
|
|
6170
|
+
const visited = /* @__PURE__ */ new Set();
|
|
5618
6171
|
let complete = true;
|
|
5619
6172
|
const walk = async (directory, isRoot = false) => {
|
|
5620
6173
|
let entries;
|
|
@@ -5629,6 +6182,8 @@ async function listTranscriptFiles(root, cutoff) {
|
|
|
5629
6182
|
if (entry.isDirectory()) {
|
|
5630
6183
|
await walk(path);
|
|
5631
6184
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
6185
|
+
if (visited.has(path)) continue;
|
|
6186
|
+
visited.add(path);
|
|
5632
6187
|
try {
|
|
5633
6188
|
const metadata = await stat(path);
|
|
5634
6189
|
if (metadata.mtimeMs >= cutoff) {
|
|
@@ -5640,7 +6195,7 @@ async function listTranscriptFiles(root, cutoff) {
|
|
|
5640
6195
|
}
|
|
5641
6196
|
}
|
|
5642
6197
|
};
|
|
5643
|
-
await walk(root, true);
|
|
6198
|
+
for (const root of roots) await walk(root, true);
|
|
5644
6199
|
return { files, complete };
|
|
5645
6200
|
}
|
|
5646
6201
|
function isMissingPath(error) {
|
|
@@ -5733,7 +6288,7 @@ var init_local_transcript_usage_client = __esm({
|
|
|
5733
6288
|
init_token_normalization();
|
|
5734
6289
|
LocalTranscriptUsageClient = class {
|
|
5735
6290
|
#provider;
|
|
5736
|
-
#
|
|
6291
|
+
#roots;
|
|
5737
6292
|
#clock;
|
|
5738
6293
|
#lookbackDays;
|
|
5739
6294
|
#cachePath;
|
|
@@ -5741,7 +6296,7 @@ var init_local_transcript_usage_client = __esm({
|
|
|
5741
6296
|
#cacheLoaded = false;
|
|
5742
6297
|
constructor(options) {
|
|
5743
6298
|
this.#provider = options.provider;
|
|
5744
|
-
this.#
|
|
6299
|
+
this.#roots = options.roots;
|
|
5745
6300
|
this.#clock = options.clock ?? (() => /* @__PURE__ */ new Date());
|
|
5746
6301
|
this.#lookbackDays = options.lookbackDays ?? 90;
|
|
5747
6302
|
this.#cachePath = options.cachePath;
|
|
@@ -5750,7 +6305,7 @@ var init_local_transcript_usage_client = __esm({
|
|
|
5750
6305
|
await this.#loadCache();
|
|
5751
6306
|
if (options.mode === "hard-rebuild") this.#fileCache.clear();
|
|
5752
6307
|
const cutoff = this.#clock().getTime() - this.#lookbackDays * 24 * 60 * 60 * 1e3;
|
|
5753
|
-
const discovered = await listTranscriptFiles(this.#
|
|
6308
|
+
const discovered = await listTranscriptFiles(this.#roots, cutoff);
|
|
5754
6309
|
const files = discovered.files;
|
|
5755
6310
|
let complete = discovered.complete;
|
|
5756
6311
|
const usage = [];
|
|
@@ -5943,10 +6498,7 @@ async function runDatabaseWorker(workerData) {
|
|
|
5943
6498
|
}
|
|
5944
6499
|
function additiveUsagePredicate(alias = "") {
|
|
5945
6500
|
const prefix = alias ? `${alias}.` : "";
|
|
5946
|
-
const
|
|
5947
|
-
FROM usage_observations
|
|
5948
|
-
WHERE provider_id = 'codex' AND id LIKE 'codex:daily:%')`;
|
|
5949
|
-
const codexRemainderDays = `(SELECT billing_domain_id, date(observed_at)
|
|
6501
|
+
const codexReconciledRemainderIds = `(SELECT billing_domain_id, id
|
|
5950
6502
|
FROM usage_observations
|
|
5951
6503
|
WHERE provider_id = 'codex' AND id LIKE 'codex-transcript:account-remainder:%')`;
|
|
5952
6504
|
return `NOT (
|
|
@@ -5960,18 +6512,11 @@ function additiveUsagePredicate(alias = "") {
|
|
|
5960
6512
|
)
|
|
5961
6513
|
OR (
|
|
5962
6514
|
${prefix}provider_id = 'codex'
|
|
6515
|
+
AND ${prefix}id LIKE 'codex:daily:%'
|
|
5963
6516
|
AND (
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
)
|
|
5968
|
-
OR (
|
|
5969
|
-
${prefix}id LIKE 'codex-transcript:%'
|
|
5970
|
-
AND ${prefix}id NOT LIKE 'codex-transcript:account-remainder:%'
|
|
5971
|
-
AND (${prefix}billing_domain_id, date(${prefix}observed_at)) IN ${codexOfficialDays}
|
|
5972
|
-
AND (${prefix}billing_domain_id, date(${prefix}observed_at)) NOT IN ${codexRemainderDays}
|
|
5973
|
-
)
|
|
5974
|
-
)
|
|
6517
|
+
${prefix}billing_domain_id,
|
|
6518
|
+
'codex-transcript:account-remainder:' || substr(${prefix}id, 13)
|
|
6519
|
+
) IN ${codexReconciledRemainderIds}
|
|
5975
6520
|
)
|
|
5976
6521
|
)`;
|
|
5977
6522
|
}
|
|
@@ -6253,7 +6798,7 @@ function buildGlobalSummary(providers, riskSummary, now, query) {
|
|
|
6253
6798
|
tokenEvidence,
|
|
6254
6799
|
apiRetailEquivalent: {
|
|
6255
6800
|
status: sawRetailEquivalent ? "available" : "unavailable",
|
|
6256
|
-
amount: sawRetailEquivalent ?
|
|
6801
|
+
amount: sawRetailEquivalent ? preciseAmount2(retailAmount) : null,
|
|
6257
6802
|
currency: "USD",
|
|
6258
6803
|
pricingCoverage: tokenEvidence.recordedTokens === 0 ? null : retailPricedTokens / tokenEvidence.recordedTokens
|
|
6259
6804
|
},
|
|
@@ -6263,7 +6808,7 @@ function buildGlobalSummary(providers, riskSummary, now, query) {
|
|
|
6263
6808
|
contributions
|
|
6264
6809
|
};
|
|
6265
6810
|
}
|
|
6266
|
-
function buildTokenMoneyWorkbench(providers, now, query) {
|
|
6811
|
+
function buildTokenMoneyWorkbench(providers, now, query, planSubscriptions, planBillingPeriods) {
|
|
6267
6812
|
const normalized = normalizeUsageQuery(now, query);
|
|
6268
6813
|
const allHistories = allDomainHistories(providers);
|
|
6269
6814
|
const headlineHistories = allHistories.filter(({ includedInHeadline }) => includedInHeadline);
|
|
@@ -6454,9 +6999,43 @@ function buildTokenMoneyWorkbench(providers, now, query) {
|
|
|
6454
6999
|
observationCount > 0 ? recordedTokens : null,
|
|
6455
7000
|
retailEquivalent,
|
|
6456
7001
|
reportedEstimate
|
|
6457
|
-
)
|
|
7002
|
+
),
|
|
7003
|
+
planValue: buildWorkbenchPlanValue({
|
|
7004
|
+
domains: planValueDomains(allHistories, comparisonCurrency),
|
|
7005
|
+
subscriptions: planSubscriptions,
|
|
7006
|
+
comparisonCurrency,
|
|
7007
|
+
start: normalized.start.toISOString(),
|
|
7008
|
+
end: normalized.end.toISOString(),
|
|
7009
|
+
rates: uniqueExchangeRates(allHistories.flatMap(({ history }) => history.exchangeRates)),
|
|
7010
|
+
billingPeriods: planBillingPeriods
|
|
7011
|
+
})
|
|
6458
7012
|
};
|
|
6459
7013
|
}
|
|
7014
|
+
function planValueDomains(histories, comparisonCurrency) {
|
|
7015
|
+
return histories.map(({ provider, domain, history, includedInHeadline }) => {
|
|
7016
|
+
const domainTokens = history.tokenEvidence.recordedTokens;
|
|
7017
|
+
const metric = (purpose) => buildWorkbenchMetric(
|
|
7018
|
+
history.costs,
|
|
7019
|
+
purpose,
|
|
7020
|
+
comparisonCurrency,
|
|
7021
|
+
domainTokens,
|
|
7022
|
+
history.exchangeRates
|
|
7023
|
+
);
|
|
7024
|
+
return {
|
|
7025
|
+
providerId: provider.id,
|
|
7026
|
+
providerDisplayName: provider.displayName,
|
|
7027
|
+
billingDomainId: domain.id,
|
|
7028
|
+
billingDomainDisplayName: domain.displayName,
|
|
7029
|
+
includedInHeadline,
|
|
7030
|
+
recordedTokens: domainTokens,
|
|
7031
|
+
observationCount: history.tokenEvidence.observationCount,
|
|
7032
|
+
retailEquivalent: metric("retail-equivalent"),
|
|
7033
|
+
actualCost: metric("actual"),
|
|
7034
|
+
authorities: history.authorities ?? [],
|
|
7035
|
+
lastObservedAt: history.lastObservedAt ?? null
|
|
7036
|
+
};
|
|
7037
|
+
});
|
|
7038
|
+
}
|
|
6460
7039
|
function allDomainHistories(providers) {
|
|
6461
7040
|
return providers.flatMap(
|
|
6462
7041
|
(provider) => provider.billingDomains.map((domain) => ({
|
|
@@ -6512,11 +7091,11 @@ function buildWorkbenchMetric(costs, purpose, comparisonCurrency, recordedTokens
|
|
|
6512
7091
|
return {
|
|
6513
7092
|
purpose,
|
|
6514
7093
|
status,
|
|
6515
|
-
amount: status === "available" ?
|
|
7094
|
+
amount: status === "available" ? preciseAmount2(convertedAmount) : null,
|
|
6516
7095
|
comparisonCurrency,
|
|
6517
7096
|
nativeAmounts: [...native.entries()].map(([currency, amount]) => ({
|
|
6518
7097
|
currency,
|
|
6519
|
-
amount: amount.complete ?
|
|
7098
|
+
amount: amount.complete ? preciseAmount2(amount.amount) : null,
|
|
6520
7099
|
records: amount.records,
|
|
6521
7100
|
knownRecords: amount.knownRecords
|
|
6522
7101
|
})).sort((left, right) => left.currency.localeCompare(right.currency)),
|
|
@@ -7108,7 +7687,7 @@ function round(value) {
|
|
|
7108
7687
|
function pricingInputsChanged(existing, observation) {
|
|
7109
7688
|
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;
|
|
7110
7689
|
}
|
|
7111
|
-
function
|
|
7690
|
+
function preciseAmount2(value) {
|
|
7112
7691
|
return Number(value.toFixed(12));
|
|
7113
7692
|
}
|
|
7114
7693
|
function tokenAuthority(authorities) {
|
|
@@ -7144,6 +7723,7 @@ var init_sqlite_usage_repository = __esm({
|
|
|
7144
7723
|
"src/server/sqlite-usage-repository.ts"() {
|
|
7145
7724
|
"use strict";
|
|
7146
7725
|
init_token_normalization();
|
|
7726
|
+
init_plan_pricing();
|
|
7147
7727
|
FRESHNESS_WINDOW_MS = 15 * 60 * 1e3;
|
|
7148
7728
|
QUERY_INDEXES_SQL = `
|
|
7149
7729
|
CREATE INDEX IF NOT EXISTS usage_observed_at_idx
|
|
@@ -7321,7 +7901,7 @@ var init_sqlite_usage_repository = __esm({
|
|
|
7321
7901
|
const usageReconciliation = snapshot2.usageReconciliation;
|
|
7322
7902
|
if (usageReconciliation) {
|
|
7323
7903
|
const prefixes = [
|
|
7324
|
-
usageReconciliation.
|
|
7904
|
+
...usageReconciliation.authoritativeIdPrefixes,
|
|
7325
7905
|
...usageReconciliation.retiredIdPrefixes
|
|
7326
7906
|
];
|
|
7327
7907
|
if (prefixes.some((prefix) => prefix.length === 0)) {
|
|
@@ -7356,11 +7936,12 @@ var init_sqlite_usage_repository = __esm({
|
|
|
7356
7936
|
for (const prefix of usageReconciliation.retiredIdPrefixes) {
|
|
7357
7937
|
deleteStoredUsage(prefix, /* @__PURE__ */ new Set());
|
|
7358
7938
|
}
|
|
7359
|
-
const authoritativePrefix
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7939
|
+
for (const authoritativePrefix of usageReconciliation.authoritativeIdPrefixes) {
|
|
7940
|
+
const incomingAuthoritativeIds = new Set(
|
|
7941
|
+
snapshot2.usage.map((observation) => observation.id).filter((id) => id.startsWith(authoritativePrefix))
|
|
7942
|
+
);
|
|
7943
|
+
deleteStoredUsage(authoritativePrefix, incomingAuthoritativeIds);
|
|
7944
|
+
}
|
|
7364
7945
|
}
|
|
7365
7946
|
const usageStatement = this.#database.prepare(
|
|
7366
7947
|
`INSERT INTO usage_observations (
|
|
@@ -7704,14 +8285,38 @@ var init_sqlite_usage_repository = __esm({
|
|
|
7704
8285
|
(provider) => this.#getProviderOverview(provider, now, query)
|
|
7705
8286
|
);
|
|
7706
8287
|
const riskSummary = buildRiskSummary(overviews);
|
|
8288
|
+
const planSubscriptions = this.getPlanSubscriptions();
|
|
7707
8289
|
return {
|
|
7708
8290
|
generatedAt: now.toISOString(),
|
|
7709
8291
|
globalSummary: buildGlobalSummary(overviews, riskSummary, now, query),
|
|
7710
|
-
workbench: buildTokenMoneyWorkbench(
|
|
8292
|
+
workbench: buildTokenMoneyWorkbench(
|
|
8293
|
+
overviews,
|
|
8294
|
+
now,
|
|
8295
|
+
query,
|
|
8296
|
+
planSubscriptions,
|
|
8297
|
+
this.#planBillingPeriodSummaries(planSubscriptions, now, query)
|
|
8298
|
+
),
|
|
7711
8299
|
providers: overviews,
|
|
7712
8300
|
riskSummary
|
|
7713
8301
|
};
|
|
7714
8302
|
}
|
|
8303
|
+
getAgentProviderIndex(now) {
|
|
8304
|
+
const providers = this.#database.prepare("SELECT id, display_name FROM providers ORDER BY id").all();
|
|
8305
|
+
return {
|
|
8306
|
+
generatedAt: now.toISOString(),
|
|
8307
|
+
providers: providers.filter(
|
|
8308
|
+
(provider) => provider.id !== "opencode" && (!this.#hideDemoProvider || provider.id !== "demo")
|
|
8309
|
+
).map((provider) => ({ id: provider.id, displayName: provider.display_name }))
|
|
8310
|
+
};
|
|
8311
|
+
}
|
|
8312
|
+
getProviderOverview(now, providerId, query = {}) {
|
|
8313
|
+
if (this.#hideDemoProvider && providerId === "demo") return null;
|
|
8314
|
+
const provider = this.#database.prepare(
|
|
8315
|
+
`SELECT id, display_name, last_success_at, last_error, last_error_code, last_recovery
|
|
8316
|
+
FROM providers WHERE id = ?`
|
|
8317
|
+
).get(providerId);
|
|
8318
|
+
return provider ? this.#getProviderOverview(provider, now, query) : null;
|
|
8319
|
+
}
|
|
7715
8320
|
saveExchangeRateSnapshot(snapshot2) {
|
|
7716
8321
|
this.#database.prepare(
|
|
7717
8322
|
`INSERT INTO exchange_rate_snapshots (
|
|
@@ -7787,6 +8392,57 @@ var init_sqlite_usage_repository = __esm({
|
|
|
7787
8392
|
settings.startAtLogin ? 1 : 0
|
|
7788
8393
|
);
|
|
7789
8394
|
}
|
|
8395
|
+
getPlanSubscriptions() {
|
|
8396
|
+
const rows = this.#database.prepare(
|
|
8397
|
+
`SELECT provider_id, billing_domain_id, plan_id, display_name, amount, currency,
|
|
8398
|
+
billing_period, anchor_date, price_source, updated_at
|
|
8399
|
+
FROM plan_subscriptions
|
|
8400
|
+
ORDER BY provider_id, billing_domain_id`
|
|
8401
|
+
).all();
|
|
8402
|
+
return rows.map((row) => ({
|
|
8403
|
+
providerId: row.provider_id,
|
|
8404
|
+
billingDomainId: row.billing_domain_id,
|
|
8405
|
+
planId: row.plan_id,
|
|
8406
|
+
displayName: row.display_name,
|
|
8407
|
+
amount: row.amount,
|
|
8408
|
+
currency: row.currency,
|
|
8409
|
+
billingPeriod: row.billing_period,
|
|
8410
|
+
anchorDate: row.anchor_date,
|
|
8411
|
+
priceSource: row.price_source,
|
|
8412
|
+
updatedAt: row.updated_at
|
|
8413
|
+
}));
|
|
8414
|
+
}
|
|
8415
|
+
savePlanSubscription(subscription) {
|
|
8416
|
+
this.#database.prepare(
|
|
8417
|
+
`INSERT INTO plan_subscriptions (
|
|
8418
|
+
provider_id, billing_domain_id, plan_id, display_name, amount, currency,
|
|
8419
|
+
billing_period, anchor_date, price_source, updated_at
|
|
8420
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8421
|
+
ON CONFLICT(provider_id, billing_domain_id) DO UPDATE SET
|
|
8422
|
+
plan_id = excluded.plan_id,
|
|
8423
|
+
display_name = excluded.display_name,
|
|
8424
|
+
amount = excluded.amount,
|
|
8425
|
+
currency = excluded.currency,
|
|
8426
|
+
billing_period = excluded.billing_period,
|
|
8427
|
+
anchor_date = excluded.anchor_date,
|
|
8428
|
+
price_source = excluded.price_source,
|
|
8429
|
+
updated_at = excluded.updated_at`
|
|
8430
|
+
).run(
|
|
8431
|
+
subscription.providerId,
|
|
8432
|
+
subscription.billingDomainId,
|
|
8433
|
+
subscription.planId,
|
|
8434
|
+
subscription.displayName,
|
|
8435
|
+
subscription.amount,
|
|
8436
|
+
subscription.currency,
|
|
8437
|
+
subscription.billingPeriod,
|
|
8438
|
+
subscription.anchorDate,
|
|
8439
|
+
subscription.priceSource,
|
|
8440
|
+
subscription.updatedAt
|
|
8441
|
+
);
|
|
8442
|
+
}
|
|
8443
|
+
deletePlanSubscription(providerId, billingDomainId) {
|
|
8444
|
+
this.#database.prepare("DELETE FROM plan_subscriptions WHERE provider_id = ? AND billing_domain_id = ?").run(providerId, billingDomainId);
|
|
8445
|
+
}
|
|
7790
8446
|
getNotificationState(key) {
|
|
7791
8447
|
const row = this.#database.prepare("SELECT value FROM notification_state WHERE key = ?").get(key);
|
|
7792
8448
|
return row?.value ?? null;
|
|
@@ -8245,6 +8901,101 @@ var init_sqlite_usage_repository = __esm({
|
|
|
8245
8901
|
coverageByDomain
|
|
8246
8902
|
};
|
|
8247
8903
|
}
|
|
8904
|
+
/**
|
|
8905
|
+
* Cycle-to-date evidence for every subscription that declares a renewal date.
|
|
8906
|
+
* Each Provider is measured over its own billing period, which is why this
|
|
8907
|
+
* cannot reuse the one rolling window the rest of the workbench shares.
|
|
8908
|
+
*/
|
|
8909
|
+
#planBillingPeriodSummaries(subscriptions, now, query) {
|
|
8910
|
+
const normalized = normalizeUsageQuery(now, query);
|
|
8911
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
8912
|
+
for (const subscription of subscriptions) {
|
|
8913
|
+
if (!subscription.anchorDate) continue;
|
|
8914
|
+
const period = billingPeriodContaining(
|
|
8915
|
+
subscription.anchorDate,
|
|
8916
|
+
subscription.billingPeriod,
|
|
8917
|
+
now
|
|
8918
|
+
);
|
|
8919
|
+
if (!period) continue;
|
|
8920
|
+
const observedThrough = new Date(Math.min(now.getTime(), period.end.getTime()));
|
|
8921
|
+
summaries.set(`${subscription.providerId}:${subscription.billingDomainId}`, {
|
|
8922
|
+
start: period.start.toISOString(),
|
|
8923
|
+
end: period.end.toISOString(),
|
|
8924
|
+
observedThrough: observedThrough.toISOString(),
|
|
8925
|
+
...this.#rangeUsageSummary(
|
|
8926
|
+
subscription.providerId,
|
|
8927
|
+
subscription.billingDomainId,
|
|
8928
|
+
period.start,
|
|
8929
|
+
observedThrough,
|
|
8930
|
+
normalized.comparisonCurrency
|
|
8931
|
+
)
|
|
8932
|
+
});
|
|
8933
|
+
}
|
|
8934
|
+
return summaries;
|
|
8935
|
+
}
|
|
8936
|
+
#rangeUsageSummary(providerId, billingDomainId, start, end, comparisonCurrency) {
|
|
8937
|
+
const startIso = start.toISOString();
|
|
8938
|
+
const endIso = end.toISOString();
|
|
8939
|
+
const usage = this.#database.prepare(
|
|
8940
|
+
`SELECT id, model, observed_at, authority, total_tokens, input_tokens, output_tokens,
|
|
8941
|
+
reasoning_tokens, cache_read_tokens, cache_write_tokens, cache_write_5m_tokens,
|
|
8942
|
+
cache_write_1h_tokens, source_reported_total_tokens,
|
|
8943
|
+
unclassified_tokens, total_derivation, model_attribution, time_precision,
|
|
8944
|
+
usage_scope, aggregation_temporality, reasoning_semantics,
|
|
8945
|
+
cache_read_semantics, cache_write_semantics
|
|
8946
|
+
FROM usage_observations
|
|
8947
|
+
WHERE provider_id = ? AND billing_domain_id = ?
|
|
8948
|
+
AND observed_at >= ? AND observed_at < ?
|
|
8949
|
+
AND ${additiveUsagePredicate()}
|
|
8950
|
+
ORDER BY observed_at, id`
|
|
8951
|
+
).all(providerId, billingDomainId, startIso, endIso);
|
|
8952
|
+
const costs = this.#database.prepare(
|
|
8953
|
+
`SELECT id, source_id, billing_domain_id, observed_at, kind, amount, currency, authority,
|
|
8954
|
+
price_snapshot_id, price_snapshot_version, price_snapshot_source,
|
|
8955
|
+
price_snapshot_canonical_model, price_snapshot_effective_at,
|
|
8956
|
+
price_snapshot_effective_until, price_snapshot_currency,
|
|
8957
|
+
price_snapshot_rates_json, price_snapshot_source_url,
|
|
8958
|
+
price_snapshot_context_tier, model, usage_observation_id, priced_tokens,
|
|
8959
|
+
line_items_json, calculated_at
|
|
8960
|
+
FROM cost_records
|
|
8961
|
+
WHERE provider_id = ? AND billing_domain_id = ?
|
|
8962
|
+
AND observed_at >= ? AND observed_at < ?
|
|
8963
|
+
ORDER BY observed_at, id`
|
|
8964
|
+
).all(providerId, billingDomainId, startIso, endIso);
|
|
8965
|
+
const rateRows = this.#database.prepare(
|
|
8966
|
+
`SELECT id, base_currency, quote_currency, rate, observed_at, source
|
|
8967
|
+
FROM exchange_rate_snapshots WHERE observed_at < ? ORDER BY observed_at DESC, id`
|
|
8968
|
+
).all(endIso);
|
|
8969
|
+
const rateByCurrency = /* @__PURE__ */ new Map();
|
|
8970
|
+
for (const row of rateRows) {
|
|
8971
|
+
if (row.quote_currency === comparisonCurrency && !rateByCurrency.has(row.base_currency)) {
|
|
8972
|
+
rateByCurrency.set(row.base_currency, row);
|
|
8973
|
+
}
|
|
8974
|
+
}
|
|
8975
|
+
const evidence = emptyTokenEvidence();
|
|
8976
|
+
for (const row of usage) addTokenEvidence(evidence, row);
|
|
8977
|
+
const finished = finishTokenEvidence(evidence);
|
|
8978
|
+
const usedRates = /* @__PURE__ */ new Map();
|
|
8979
|
+
const historyCosts = summarizeHistoryCosts(
|
|
8980
|
+
costs,
|
|
8981
|
+
finished.recordedTokens,
|
|
8982
|
+
rateByCurrency,
|
|
8983
|
+
comparisonCurrency,
|
|
8984
|
+
end,
|
|
8985
|
+
usedRates
|
|
8986
|
+
);
|
|
8987
|
+
return {
|
|
8988
|
+
recordedTokens: finished.recordedTokens,
|
|
8989
|
+
observationCount: finished.observationCount,
|
|
8990
|
+
retailEquivalent: buildWorkbenchMetric(
|
|
8991
|
+
historyCosts,
|
|
8992
|
+
"retail-equivalent",
|
|
8993
|
+
comparisonCurrency,
|
|
8994
|
+
finished.recordedTokens,
|
|
8995
|
+
[...usedRates.values()]
|
|
8996
|
+
)
|
|
8997
|
+
};
|
|
8998
|
+
}
|
|
8248
8999
|
#getBillingHistory(providerId, billingDomainId, now, query) {
|
|
8249
9000
|
const normalized = normalizeUsageQuery(now, query);
|
|
8250
9001
|
const start = normalized.start.toISOString();
|
|
@@ -8653,6 +9404,19 @@ var init_sqlite_usage_repository = __esm({
|
|
|
8653
9404
|
key TEXT PRIMARY KEY,
|
|
8654
9405
|
value TEXT NOT NULL
|
|
8655
9406
|
);
|
|
9407
|
+
CREATE TABLE IF NOT EXISTS plan_subscriptions (
|
|
9408
|
+
provider_id TEXT NOT NULL,
|
|
9409
|
+
billing_domain_id TEXT NOT NULL,
|
|
9410
|
+
plan_id TEXT,
|
|
9411
|
+
display_name TEXT NOT NULL,
|
|
9412
|
+
amount REAL NOT NULL,
|
|
9413
|
+
currency TEXT NOT NULL,
|
|
9414
|
+
billing_period TEXT NOT NULL,
|
|
9415
|
+
anchor_date TEXT,
|
|
9416
|
+
price_source TEXT NOT NULL,
|
|
9417
|
+
updated_at TEXT NOT NULL,
|
|
9418
|
+
PRIMARY KEY (provider_id, billing_domain_id)
|
|
9419
|
+
);
|
|
8656
9420
|
`);
|
|
8657
9421
|
const usageColumns = this.#database.prepare("PRAGMA table_info(usage_observations)").all();
|
|
8658
9422
|
if (!usageColumns.some((column) => column.name === "total_tokens")) {
|
|
@@ -8821,6 +9585,10 @@ var init_sqlite_usage_repository = __esm({
|
|
|
8821
9585
|
this.#database.exec(`ALTER TABLE quota_buckets ADD COLUMN ${name} ${type}`);
|
|
8822
9586
|
}
|
|
8823
9587
|
}
|
|
9588
|
+
const planColumns = this.#database.prepare("PRAGMA table_info(plan_subscriptions)").all();
|
|
9589
|
+
if (planColumns.length > 0 && !planColumns.some((column) => column.name === "anchor_date")) {
|
|
9590
|
+
this.#database.exec("ALTER TABLE plan_subscriptions ADD COLUMN anchor_date TEXT");
|
|
9591
|
+
}
|
|
8824
9592
|
}
|
|
8825
9593
|
};
|
|
8826
9594
|
}
|
|
@@ -8957,18 +9725,19 @@ async function runDaemon(home) {
|
|
|
8957
9725
|
function localTranscriptClient(provider, applicationHome) {
|
|
8958
9726
|
return new LocalTranscriptUsageClient({
|
|
8959
9727
|
provider,
|
|
8960
|
-
|
|
9728
|
+
roots: localTranscriptRoots(provider),
|
|
8961
9729
|
cachePath: join8(applicationHome, "cache", `${provider}-transcripts.json`)
|
|
8962
9730
|
});
|
|
8963
9731
|
}
|
|
8964
|
-
function
|
|
9732
|
+
function localTranscriptRoots(provider) {
|
|
8965
9733
|
if (provider === "codex") {
|
|
8966
|
-
|
|
9734
|
+
const home = resolveHomeOverride(process.env.CODEX_HOME, ".codex");
|
|
9735
|
+
return [join8(home, "sessions"), join8(home, "archived_sessions")];
|
|
8967
9736
|
}
|
|
8968
9737
|
if (provider === "claude-code") {
|
|
8969
|
-
return join8(resolveHomeOverride(process.env.CLAUDE_CONFIG_DIR, ".claude"), "projects");
|
|
9738
|
+
return [join8(resolveHomeOverride(process.env.CLAUDE_CONFIG_DIR, ".claude"), "projects")];
|
|
8970
9739
|
}
|
|
8971
|
-
return join8(resolveHomeOverride(process.env.GROK_HOME, ".grok"), "sessions");
|
|
9740
|
+
return [join8(resolveHomeOverride(process.env.GROK_HOME, ".grok"), "sessions")];
|
|
8972
9741
|
}
|
|
8973
9742
|
function resolveHomeOverride(value, fallback) {
|
|
8974
9743
|
const configured = value?.trim();
|