tokentracker-cli 0.88.6 → 0.88.7
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/dashboard/dist/assets/{AchievementBadge-CgVttPJy.js → AchievementBadge-m7oaBLpH.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-2MeyWMmO.js → AchievementsPage-BHHh4VjM.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-hZGNcWfs.js → AchievementsSection-CGLNe_1p.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-DBMexTtI.js → BadgeDetailModal-v1DGF6EM.js} +1 -1
- package/dashboard/dist/assets/{Card-Ca6NJlW2.js → Card-C16SqIR8.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-D5GOjZtF.js → ClawdAnimated-Bj3ptLJv.js} +1 -1
- package/dashboard/dist/assets/{CommandPalette-CPLhHuIS.js → CommandPalette-BaegDLAM.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-BFyZtlgh.js → CommunityStatsModal-CThxVFG0.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-DzpUyVbg.js → ConfirmModal-C0sP3Ktk.js} +1 -1
- package/dashboard/dist/assets/{Controls-CIPVemCR.js → Controls-C8usK6RW.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-h2LfrNwp.js → DashboardPage-DbzbT_cY.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-CORcVYoS.js → DevicePage-Cu4qqRcB.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-BMizP3FN.js → DialogClose-DVo3jJb6.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-zf3KGPP3.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-CAlyWmQy.js → DialogTitle-C1UOMgTZ.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-R8uHDC1n.js → FadeIn-CuQlQrvO.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-D-H9BYBq.js → HeaderGithubStar-BTt2612H.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-C6D0Qf3J.js → HoverTooltip-CHdLolcf.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-BDKN_XOv.js → IpCheckPage-BFz8hr_3.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-CVS7Gi16.js → LandingPage-CTyNSAYl.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-By9ZRxCx.js → LeaderboardAvatar-CBdOXZWk.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-B2FWLDbM.js → LeaderboardPage-DStx51Bj.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-bKfAVxsg.js → LeaderboardProfileModal-CtxgJR07.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-NSZpmCOS.js → LeaderboardProfilePage-DrpsHTdZ.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-DEeKWZ3u.js → LimitsPage-m_itA-7d.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-CJRoGtCr.js → LocalOnlyNotice-A62wFwYT.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-DsotnrVc.js → LoginCard-DFJugNa-.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-CBzSJfD-.js → LoginModal-DF4BOwS6.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-Dr7dP5R6.js → LoginPage-BxFhfitr.js} +1 -1
- package/dashboard/dist/assets/{PetPage-D_FQtbbK.js → PetPage-l4fkRsf1.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-BBIGg4i-.js → PopoverPopup-y-OAHy4v.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-C6NT8xCP.js → ProviderIcon-DwoPHz27.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-BbDZdmyR.js → ResetPasswordPage-CCDj94rl.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-C0YbTO7C.js → SegmentedControl-lEKPzZlV.js} +1 -1
- package/dashboard/dist/assets/{Select-CO90ysTb.js → Select-DoyUBoxn.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-CI5GNKhv.js → SelectItemText-K8Vzn1Dd.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-CV71dVHd.js → ServiceStatusPage-Cqyo7OtW.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-B5jvyh-X.js → SessionsPage-BELC-8aJ.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-DokT0qhX.js → SettingsPage-C6Og4tMS.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-BYAAiPpI.js → SkillsPage-BKKgpXc_.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-m_5kxlC_.js → TokenGalaxy-DoSCQSpY.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-TYR3c0G7.js → ToolbarRootContext-GoQP9LXi.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-DjVbRbLd.js → TrendMonitor-BHE1KkJ7.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-DwImvB2Y.js → WidgetsPage-CHBzNb19.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-CEcqtP_W.js → WrappedPage-CW_kqVoY.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-BOpcJ7Bn.js → __vite-browser-external-BgeLzJ0q.js} +2 -2
- package/dashboard/dist/assets/{agent-logos-Cwc0bFNx.js → agent-logos-BWeWRlms.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-C0nGIX35.js → arrow-up-right-5GGSSEKW.js} +1 -1
- package/dashboard/dist/assets/{check-Cpk8oXcn.js → check-g6Fq4TSB.js} +1 -1
- package/dashboard/dist/assets/{chevron-right-DsKtZU0a.js → chevron-right-DjPuavEP.js} +1 -1
- package/dashboard/dist/assets/{copy-COeB2IQx.js → copy-BAMl60zT.js} +1 -1
- package/dashboard/dist/assets/{download-Dvw5E6xA.js → download-BQPhL4h5.js} +1 -1
- package/dashboard/dist/assets/{external-link-COe8flh4.js → external-link-CU3XKMbZ.js} +1 -1
- package/dashboard/dist/assets/{flame-Dza_-WNE.js → flame-B85YY_rB.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-CKDbxlqC.js → format-tokens-DDxlnhNV.js} +1 -1
- package/dashboard/dist/assets/{icons-Dj3nlViF.js → icons-D1e1TWTe.js} +1 -1
- package/dashboard/dist/assets/{index-CBgWOlov.js → index-BZ2hkHKk.js} +1 -1
- package/dashboard/dist/assets/{index-DvR1xY_C.js → index-CWMkVmon.js} +1 -1
- package/dashboard/dist/assets/{index-DCpYik68.js → index-sQl-P7u5.js} +1 -1
- package/dashboard/dist/assets/{info-B9M6Luq-.js → info-DlSYpCFm.js} +1 -1
- package/dashboard/dist/assets/{limits-providers-CAZ_eIVT.js → limits-providers-DS5EIbJK.js} +1 -1
- package/dashboard/dist/assets/{link-2-PL-NJZyN.js → link-2-DbEVoNk-.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-CQ-akiLL.js → loader-circle-CqmzGIX-.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-9EZoXvlF.js → maximize-2-Ce8X8kHQ.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-Bc1a9Bqm.js → modifiers.esm-BQNRpVcA.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-DR9MHu8R.js → monitor-smartphone-8drugZmh.js} +1 -1
- package/dashboard/dist/assets/{provider-display-DAH8KAyP.js → provider-display-BayTojFu.js} +1 -1
- package/dashboard/dist/assets/{react-gkeEFJtr.js → react-BfgySeLu.js} +1 -1
- package/dashboard/dist/assets/{search-B67Xwb-N.js → search-BkGUxlXI.js} +1 -1
- package/dashboard/dist/assets/{skills-api-aZca3hZV.js → skills-api-D80JxFc1.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-Crm57xTH.js → sortable.esm-SjRQ4Ehw.js} +1 -1
- package/dashboard/dist/assets/{store-DisKKZeI.js → store-B3WkWBgn.js} +1 -1
- package/dashboard/dist/assets/{terminal-DGueqM2Q.js → terminal-CIvEvMau.js} +1 -1
- package/dashboard/dist/assets/{trash-2-b5d9XdRV.js → trash-2-BZclE0mU.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-DOPcq80h.js → use-community-stats-ChaFzJoW.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-DGfPkvee.js → use-limits-display-prefs-2LALdqvT.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-Dyul6pNM.js → use-ordered-list-10Nmxw1I.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-J12KnTgP.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-BPzM7u7L.js → use-session-efficiency-pref-CqPK4jX1.js} +1 -1
- package/dashboard/dist/assets/{use-transform-C5yhWdHM.js → use-transform-s6og1tg9.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-C6S69l9t.js → use-usage-limits-jyRJ_kl9.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-BfJWbenA.js → useCurrency-hZfdhfqK.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-Btq-Jtjh.js → useScrollLock-DFF4vpw2.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-DaXiI8qf.js → useTokenFormat-DaVdoRvh.js} +1 -1
- package/dashboard/dist/assets/{zap-DMOIXlpH.js → zap-B1XMdyCV.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/ip-check.html +1 -1
- package/dashboard/dist/leaderboard.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +1 -1
- package/src/lib/opencode-go-limits.js +122 -13
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/dashboard/dist/assets/DialogDescription-B_xemJjT.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-UUonF8mY.js +0 -1
|
@@ -5,23 +5,27 @@ const { readSqliteJsonRowsAsync } = require("./sqlite-reader");
|
|
|
5
5
|
|
|
6
6
|
// OpenCode Go usage limits.
|
|
7
7
|
//
|
|
8
|
-
//
|
|
8
|
+
// Three data sources are available, in priority order:
|
|
9
9
|
//
|
|
10
|
-
// 1.
|
|
11
|
-
// (https://opencode.ai/
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// slkiser/opencode-quota#41.
|
|
10
|
+
// 1. The official OpenCode Go usage API
|
|
11
|
+
// (GET https://opencode.ai/zen/go/v1/usage) with
|
|
12
|
+
// `Authorization: Bearer <OPENCODE_GO_API_KEY>`. It returns the server's
|
|
13
|
+
// authoritative rolling (5h), weekly, and monthly usage windows.
|
|
15
14
|
//
|
|
16
|
-
// 2.
|
|
15
|
+
// 2. Web scrape of the workspace dashboard
|
|
16
|
+
// (https://opencode.ai/workspace/<id>/go) for existing users configured
|
|
17
|
+
// with OPENCODE_GO_AUTH_COOKIE. This is retained as a compatibility path.
|
|
18
|
+
//
|
|
19
|
+
// 3. Local opencode.db cost aggregation, explicitly enabled with
|
|
17
20
|
// TOKENTRACKER_OPENCODE_GO_LOCAL_ESTIMATE=1. The `opencode` CLI records
|
|
18
21
|
// every Go turn's USD `cost` in SQLite, so cost ÷ Go's dollar caps is a
|
|
19
22
|
// useful local estimate. It cannot prove that the account still has an
|
|
20
23
|
// active Go subscription, because the database contains historical turns
|
|
21
24
|
// but no current entitlement state.
|
|
22
25
|
//
|
|
23
|
-
// The returned shape is shared by
|
|
24
|
-
//
|
|
26
|
+
// The returned shape is shared by all sources. `source` is `'api'` for the
|
|
27
|
+
// official endpoint, `'web'` for the compatibility scrape, or `'local-estimate'`
|
|
28
|
+
// for the opt-in estimate.
|
|
25
29
|
|
|
26
30
|
const SCRAPED_NUMBER_PATTERN = "([0-9]+(?:\\.[0-9]+)?)";
|
|
27
31
|
|
|
@@ -38,11 +42,12 @@ function windowFieldRegex(windowKey, field) {
|
|
|
38
42
|
return new RegExp(`${windowKey}[^}]*?${field}"?\\s*[:=]+\\s*${SCRAPED_NUMBER_PATTERN}`);
|
|
39
43
|
}
|
|
40
44
|
|
|
45
|
+
const GO_USAGE_API_URL = "https://opencode.ai/zen/go/v1/usage";
|
|
41
46
|
const DASHBOARD_URL_PREFIX = "https://opencode.ai/workspace/";
|
|
42
47
|
const DASHBOARD_URL_SUFFIX = "/go";
|
|
43
48
|
const USER_AGENT =
|
|
44
49
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Gecko/20100101 Firefox/148.0";
|
|
45
|
-
const
|
|
50
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;
|
|
46
51
|
const LOCAL_ESTIMATE_ENV = "TOKENTRACKER_OPENCODE_GO_LOCAL_ESTIMATE";
|
|
47
52
|
|
|
48
53
|
function isTruthyEnvValue(value) {
|
|
@@ -78,6 +83,13 @@ function readConfig(env = process.env) {
|
|
|
78
83
|
return { workspaceId, authCookie };
|
|
79
84
|
}
|
|
80
85
|
|
|
86
|
+
function readApiKey(env = process.env) {
|
|
87
|
+
if (!env || typeof env !== "object") return "";
|
|
88
|
+
return typeof env.OPENCODE_GO_API_KEY === "string"
|
|
89
|
+
? env.OPENCODE_GO_API_KEY.trim()
|
|
90
|
+
: "";
|
|
91
|
+
}
|
|
92
|
+
|
|
81
93
|
function clampPercent(value) {
|
|
82
94
|
if (value === null || value === undefined || value === "") return null;
|
|
83
95
|
let n = Number(value);
|
|
@@ -486,6 +498,84 @@ function localGoResult(local) {
|
|
|
486
498
|
};
|
|
487
499
|
}
|
|
488
500
|
|
|
501
|
+
// --- Official usage API (exact server-side usage, API-key-gated) -------------
|
|
502
|
+
|
|
503
|
+
async function fetchOpencodeGoApiLimits({ apiKey, fetchImpl, nowMs, timeoutMs }) {
|
|
504
|
+
let response;
|
|
505
|
+
try {
|
|
506
|
+
response = await withTimeout(fetchImpl, timeoutMs)(GO_USAGE_API_URL, {
|
|
507
|
+
method: "GET",
|
|
508
|
+
headers: {
|
|
509
|
+
Authorization: `Bearer ${apiKey}`,
|
|
510
|
+
Accept: "application/json",
|
|
511
|
+
"User-Agent": USER_AGENT,
|
|
512
|
+
},
|
|
513
|
+
});
|
|
514
|
+
} catch (err) {
|
|
515
|
+
return { configured: true, error: sanitizeMessage(err?.message || err) };
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (response.status === 401) {
|
|
519
|
+
return {
|
|
520
|
+
configured: true,
|
|
521
|
+
error: "OpenCode Go API key is missing, invalid, expired, or not entitled to an OpenCode Go subscription.",
|
|
522
|
+
auth_error: true,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
if (response.status === 403) {
|
|
526
|
+
return {
|
|
527
|
+
configured: true,
|
|
528
|
+
error: "OpenCode Go subscription required for this API key.",
|
|
529
|
+
auth_error: true,
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
if (!response.ok) {
|
|
533
|
+
return { configured: true, error: `OpenCode Go usage API error ${response.status}` };
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
let payload;
|
|
537
|
+
try {
|
|
538
|
+
payload = JSON.parse(await response.text());
|
|
539
|
+
} catch (err) {
|
|
540
|
+
return {
|
|
541
|
+
configured: true,
|
|
542
|
+
error: `Could not parse OpenCode Go usage API response: ${sanitizeMessage(err?.message || err)}`,
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const rolling = buildWindow({
|
|
547
|
+
usagePercent: payload?.rollingUsage?.usagePercent,
|
|
548
|
+
resetInSec: payload?.rollingUsage?.resetInSec,
|
|
549
|
+
nowMs,
|
|
550
|
+
});
|
|
551
|
+
const weekly = buildWindow({
|
|
552
|
+
usagePercent: payload?.weeklyUsage?.usagePercent,
|
|
553
|
+
resetInSec: payload?.weeklyUsage?.resetInSec,
|
|
554
|
+
nowMs,
|
|
555
|
+
});
|
|
556
|
+
const monthly = buildWindow({
|
|
557
|
+
usagePercent: payload?.monthlyUsage?.usagePercent,
|
|
558
|
+
resetInSec: payload?.monthlyUsage?.resetInSec,
|
|
559
|
+
nowMs,
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
if (!rolling && !weekly && !monthly) {
|
|
563
|
+
return {
|
|
564
|
+
configured: true,
|
|
565
|
+
error: "OpenCode Go usage API response did not include any known usage windows.",
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return {
|
|
570
|
+
configured: true,
|
|
571
|
+
error: null,
|
|
572
|
+
subscription_status: "active",
|
|
573
|
+
primary_window: rolling,
|
|
574
|
+
secondary_window: weekly,
|
|
575
|
+
tertiary_window: monthly,
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
|
|
489
579
|
// --- Web scrape (exact server-side usage, cookie-gated) ---------------------
|
|
490
580
|
|
|
491
581
|
async function scrapeOpencodeGoWeb({ cfg, fetchImpl, nowMs, timeoutMs }) {
|
|
@@ -571,14 +661,31 @@ async function fetchOpencodeGoLimits({
|
|
|
571
661
|
env = process.env,
|
|
572
662
|
fetchImpl = fetch,
|
|
573
663
|
nowMs = Date.now(),
|
|
574
|
-
timeoutMs =
|
|
664
|
+
timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS,
|
|
575
665
|
sqliteOptions = {},
|
|
576
666
|
} = {}) {
|
|
667
|
+
const apiKey = readApiKey(env);
|
|
577
668
|
const cfg = readConfig(env);
|
|
578
669
|
const allowLocalEstimate = localEstimateEnabled(env);
|
|
579
670
|
|
|
580
|
-
//
|
|
581
|
-
//
|
|
671
|
+
// Prefer the documented API whenever an API key is explicitly configured.
|
|
672
|
+
// An authentication failure is actionable and must not be hidden by silently
|
|
673
|
+
// falling back to a possibly stale cookie. For a transient API failure, an
|
|
674
|
+
// existing cookie remains a compatibility fallback.
|
|
675
|
+
if (apiKey) {
|
|
676
|
+
const api = await fetchOpencodeGoApiLimits({ apiKey, fetchImpl, nowMs, timeoutMs });
|
|
677
|
+
if (!api?.error && (api.primary_window || api.secondary_window || api.tertiary_window)) {
|
|
678
|
+
return { ...api, source: "api" };
|
|
679
|
+
}
|
|
680
|
+
if (api?.auth_error) {
|
|
681
|
+
const { auth_error, ...result } = api;
|
|
682
|
+
return result;
|
|
683
|
+
}
|
|
684
|
+
if (!cfg) return api || { configured: true, error: "OpenCode Go unavailable" };
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Keep the cookie-backed dashboard path for existing configurations and as a
|
|
688
|
+
// fallback when the official API is temporarily unavailable.
|
|
582
689
|
if (cfg) {
|
|
583
690
|
const web = await scrapeOpencodeGoWeb({ cfg, fetchImpl, nowMs, timeoutMs });
|
|
584
691
|
if (web?.subscription_status === "inactive") return web;
|
|
@@ -602,7 +709,9 @@ async function fetchOpencodeGoLimits({
|
|
|
602
709
|
|
|
603
710
|
module.exports = {
|
|
604
711
|
fetchOpencodeGoLimits,
|
|
712
|
+
fetchOpencodeGoApiLimits,
|
|
605
713
|
readConfig,
|
|
714
|
+
readApiKey,
|
|
606
715
|
localEstimateEnabled,
|
|
607
716
|
detectSubscriptionStatus,
|
|
608
717
|
extractWindows,
|