tokentracker-cli 0.87.6 → 0.87.8

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.
Files changed (115) hide show
  1. package/LICENSE +1 -1
  2. package/README.de.md +32 -32
  3. package/README.ja.md +31 -31
  4. package/README.ko.md +31 -31
  5. package/README.md +37 -36
  6. package/README.zh-CN.md +34 -33
  7. package/dashboard/dist/assets/{AchievementBadge-BRh6I-jK.js → AchievementBadge-CpxX02OK.js} +1 -1
  8. package/dashboard/dist/assets/{AchievementsPage-BsAJOqK8.js → AchievementsPage-CSms8IjI.js} +2 -2
  9. package/dashboard/dist/assets/{AchievementsSection-DebeB3vT.js → AchievementsSection-Sm43tnkh.js} +1 -1
  10. package/dashboard/dist/assets/{BadgeDetailModal-DniVJwrw.js → BadgeDetailModal-cSNqMLsK.js} +1 -1
  11. package/dashboard/dist/assets/{Card-DvKbmZEl.js → Card-wNwNRVSb.js} +1 -1
  12. package/dashboard/dist/assets/{ClawdAnimated-DCyXpvgK.js → ClawdAnimated-S8sIS2Ca.js} +1 -1
  13. package/dashboard/dist/assets/{CommandPalette-BadO9q-D.js → CommandPalette-DVeiwbPM.js} +1 -1
  14. package/dashboard/dist/assets/{CommunityStatsModal-DzBeGuLk.js → CommunityStatsModal-g5P4j3um.js} +1 -1
  15. package/dashboard/dist/assets/{ConfirmModal-BLppThU-.js → ConfirmModal-2P2D5iIp.js} +1 -1
  16. package/dashboard/dist/assets/{Controls-BBVOO6-V.js → Controls-CCaotoQP.js} +1 -1
  17. package/dashboard/dist/assets/{DashboardPage-DgV2-5pv.js → DashboardPage-DxPGTJpe.js} +1 -1
  18. package/dashboard/dist/assets/{DevicePage-BZ8Z3veC.js → DevicePage-_nLwNash.js} +1 -1
  19. package/dashboard/dist/assets/{DialogClose-Be7JwC-H.js → DialogClose-D0giFwO3.js} +1 -1
  20. package/dashboard/dist/assets/DialogDescription-8veCvaUb.js +1 -0
  21. package/dashboard/dist/assets/{DialogTitle-Bgr5ftrn.js → DialogTitle-JS_04eUC.js} +1 -1
  22. package/dashboard/dist/assets/{FadeIn-Cc5Grbxc.js → FadeIn-tFzTWDkC.js} +1 -1
  23. package/dashboard/dist/assets/HeaderGithubStar-B2EUJUTH.js +1 -0
  24. package/dashboard/dist/assets/{HoverTooltip-BwRvWZqo.js → HoverTooltip-BJzHksPT.js} +1 -1
  25. package/dashboard/dist/assets/{IpCheckPage-Ce4dWLI1.js → IpCheckPage-B8mA0mut.js} +1 -1
  26. package/dashboard/dist/assets/{LandingPage-CuDfsomP.js → LandingPage-DmJZDKr9.js} +2 -2
  27. package/dashboard/dist/assets/{LeaderboardAvatar-D50UCqhG.js → LeaderboardAvatar-CDeRQUCg.js} +1 -1
  28. package/dashboard/dist/assets/{LeaderboardPage-fXYjgVOH.js → LeaderboardPage-Dkgm89kY.js} +2 -2
  29. package/dashboard/dist/assets/{LeaderboardProfileModal-DiIM1-8L.js → LeaderboardProfileModal-B6_kuR1z.js} +3 -3
  30. package/dashboard/dist/assets/{LeaderboardProfilePage-UzcVaHbK.js → LeaderboardProfilePage-BRs8sJBV.js} +2 -2
  31. package/dashboard/dist/assets/LimitsPage-DZ1s5NWV.js +4 -0
  32. package/dashboard/dist/assets/LocalOnlyNotice-Cu_hSnux.js +1 -0
  33. package/dashboard/dist/assets/{LoginCard-e4xbbFLq.js → LoginCard-yDZbGsHR.js} +1 -1
  34. package/dashboard/dist/assets/{LoginModal-CLxWc3FD.js → LoginModal-DEvzCUfQ.js} +1 -1
  35. package/dashboard/dist/assets/{LoginPage-sf34MK6Q.js → LoginPage-DBySF9do.js} +1 -1
  36. package/dashboard/dist/assets/{PetPage-BA_BAFKG.js → PetPage-DnzH9t-r.js} +1 -1
  37. package/dashboard/dist/assets/{PopoverPopup-BwFAXu-Z.js → PopoverPopup-DgFsEMax.js} +1 -1
  38. package/dashboard/dist/assets/{ProviderIcon-MRSlzNzV.js → ProviderIcon-BEixe-xP.js} +1 -1
  39. package/dashboard/dist/assets/{ResetPasswordPage-DJAq2p4v.js → ResetPasswordPage-excKqsCL.js} +1 -1
  40. package/dashboard/dist/assets/{SegmentedControl-LWEqN03-.js → SegmentedControl-DDFkH64o.js} +1 -1
  41. package/dashboard/dist/assets/{Select-tkoxDrUN.js → Select-ChEPIuJf.js} +1 -1
  42. package/dashboard/dist/assets/{SelectItemText-bBnvuF48.js → SelectItemText--b7ISnJm.js} +1 -1
  43. package/dashboard/dist/assets/{ServiceStatusPage-DDGNQfQ1.js → ServiceStatusPage-DtAY-Qnn.js} +1 -1
  44. package/dashboard/dist/assets/{SessionsPage-x7-I3BLn.js → SessionsPage-CKFglvuj.js} +1 -1
  45. package/dashboard/dist/assets/{SettingsPage-ColZX8Yi.js → SettingsPage-s0605qWg.js} +1 -1
  46. package/dashboard/dist/assets/{SkillsPage-DELWPebQ.js → SkillsPage-DTsbCucq.js} +1 -1
  47. package/dashboard/dist/assets/{TokenGalaxy-Bz8Cto8i.js → TokenGalaxy-C_M7q8S4.js} +1 -1
  48. package/dashboard/dist/assets/{ToolbarRootContext-BvOVtmfA.js → ToolbarRootContext-CXQwpea-.js} +1 -1
  49. package/dashboard/dist/assets/{TrendMonitor-DuNGP2Rf.js → TrendMonitor-1Yhlplf0.js} +1 -1
  50. package/dashboard/dist/assets/{WidgetsPage-CRGlX9WP.js → WidgetsPage-DC6pmtld.js} +1 -1
  51. package/dashboard/dist/assets/{WrappedPage-DQ3hLeL1.js → WrappedPage-DixoqR13.js} +1 -1
  52. package/dashboard/dist/assets/{__vite-browser-external-CGdlUam8.js → __vite-browser-external-5aLUeBkM.js} +6 -4
  53. package/dashboard/dist/assets/{agent-logos-FfNeyajM.js → agent-logos-DILTbPLg.js} +1 -1
  54. package/dashboard/dist/assets/{arrow-up-right-BEQHH_0c.js → arrow-up-right-DgIDgjVW.js} +1 -1
  55. package/dashboard/dist/assets/{check-Bl_xgq6c.js → check-Bz6l09VH.js} +1 -1
  56. package/dashboard/dist/assets/{chevron-right-YO2OTiWy.js → chevron-right-EvGQXbI3.js} +1 -1
  57. package/dashboard/dist/assets/{copy-DtRgnD2D.js → copy-DbsB0OVy.js} +1 -1
  58. package/dashboard/dist/assets/{download-BiCrU4Wh.js → download-R8nUvXYh.js} +1 -1
  59. package/dashboard/dist/assets/{external-link-CVogXP9K.js → external-link-x0oNzGeq.js} +1 -1
  60. package/dashboard/dist/assets/{flame-GFlQvi3d.js → flame-Bo4IdG6k.js} +1 -1
  61. package/dashboard/dist/assets/{format-tokens-BZrnF3-R.js → format-tokens-BSGC1-Gz.js} +1 -1
  62. package/dashboard/dist/assets/{icons-CzzsWeR-.js → icons-D0Okncmo.js} +1 -1
  63. package/dashboard/dist/assets/{index-mrU077Y8.js → index-BbOkC6wf.js} +1 -1
  64. package/dashboard/dist/assets/{index-CyOwnTlp.js → index-Bvvmei-I.js} +1 -1
  65. package/dashboard/dist/assets/{index-B1S00F5t.js → index-D6F8eNr_.js} +1 -1
  66. package/dashboard/dist/assets/{info-CL-k0W8L.js → info-DXXKNpLG.js} +1 -1
  67. package/dashboard/dist/assets/{limits-providers-DGKlD0uY.js → limits-providers-DYa4fdsE.js} +1 -1
  68. package/dashboard/dist/assets/{link-2-DOsRG9tT.js → link-2-C9mxEnT6.js} +1 -1
  69. package/dashboard/dist/assets/{loader-circle-Da-W_cCG.js → loader-circle-Y8V8Mq9L.js} +1 -1
  70. package/dashboard/dist/assets/{maximize-2-CPp2Asek.js → maximize-2-QuP69Xcg.js} +1 -1
  71. package/dashboard/dist/assets/{modifiers.esm-B0cihd2s.js → modifiers.esm-B3SleRKs.js} +1 -1
  72. package/dashboard/dist/assets/{monitor-smartphone-CLMr7OcT.js → monitor-smartphone-B2LslYi7.js} +1 -1
  73. package/dashboard/dist/assets/{provider-display-h1GGY5sy.js → provider-display-DzBwrDZp.js} +2 -2
  74. package/dashboard/dist/assets/{react-BXj1W2tW.js → react-BooKG84W.js} +1 -1
  75. package/dashboard/dist/assets/{search-DTwQkbLa.js → search-zdYuHmL-.js} +1 -1
  76. package/dashboard/dist/assets/{skills-api-B7_r_5CR.js → skills-api-C2IkKfwE.js} +1 -1
  77. package/dashboard/dist/assets/{sortable.esm-Ba4_NOAf.js → sortable.esm-BHvl5vG5.js} +1 -1
  78. package/dashboard/dist/assets/{store-CZZj0fmU.js → store-BBP4jx0R.js} +1 -1
  79. package/dashboard/dist/assets/{terminal-Dd0uhXcN.js → terminal-C0nhFNQ0.js} +1 -1
  80. package/dashboard/dist/assets/{trash-2-CyBqbulx.js → trash-2-DNVSqbxA.js} +1 -1
  81. package/dashboard/dist/assets/{use-community-stats-D657285K.js → use-community-stats-BK070d1s.js} +1 -1
  82. package/dashboard/dist/assets/{use-limits-display-prefs-z0wBtxb1.js → use-limits-display-prefs-BXOUxblO.js} +1 -1
  83. package/dashboard/dist/assets/{use-ordered-list-CiIuzdVF.js → use-ordered-list-0SVHZHaW.js} +1 -1
  84. package/dashboard/dist/assets/use-reduced-motion-abZbtoVS.js +1 -0
  85. package/dashboard/dist/assets/{use-session-efficiency-pref-BiHWvNJQ.js → use-session-efficiency-pref-eaUpwuLK.js} +1 -1
  86. package/dashboard/dist/assets/{use-transform-DcbCiXLq.js → use-transform-Bhar2U4w.js} +1 -1
  87. package/dashboard/dist/assets/{use-usage-limits-tUq-_GSd.js → use-usage-limits-BGGCMVzi.js} +1 -1
  88. package/dashboard/dist/assets/{useCurrency-BlOEzUvV.js → useCurrency-gIJVmq3w.js} +1 -1
  89. package/dashboard/dist/assets/{useScrollLock-CFKQBf15.js → useScrollLock-BhFzeRtR.js} +1 -1
  90. package/dashboard/dist/assets/{useTokenFormat-ggxahHK0.js → useTokenFormat-BZ8WT-A6.js} +1 -1
  91. package/dashboard/dist/assets/{zap-De7YrxAq.js → zap-Dvc_Jt1t.js} +1 -1
  92. package/dashboard/dist/brand-logos/omp.svg +33 -0
  93. package/dashboard/dist/brand-logos/pi.svg +16 -0
  94. package/dashboard/dist/index.html +7 -7
  95. package/dashboard/dist/ip-check.html +1 -1
  96. package/dashboard/dist/leaderboard.html +1 -1
  97. package/dashboard/dist/llms.txt +4 -4
  98. package/dashboard/dist/share.html +2 -2
  99. package/package.json +3 -3
  100. package/src/commands/init.js +49 -8
  101. package/src/commands/status.js +12 -5
  102. package/src/commands/sync.js +1 -0
  103. package/src/commands/uninstall.js +36 -1
  104. package/src/lib/grok-hook.js +11 -2
  105. package/src/lib/omp-hook.js +589 -0
  106. package/src/lib/opencode-go-limits.js +59 -30
  107. package/src/lib/pricing/seed-snapshot.json +1 -1
  108. package/src/lib/rollout.js +283 -68
  109. package/src/lib/star-cta.js +1 -1
  110. package/src/lib/usage-limits.js +3 -3
  111. package/dashboard/dist/assets/DialogDescription-fYPOWV-6.js +0 -1
  112. package/dashboard/dist/assets/HeaderGithubStar-BN1WZPnH.js +0 -1
  113. package/dashboard/dist/assets/LimitsPage-Clg8P5-M.js +0 -4
  114. package/dashboard/dist/assets/LocalOnlyNotice-rdNf4GvF.js +0 -1
  115. package/dashboard/dist/assets/use-reduced-motion-DXlYG36v.js +0 -1
@@ -5,32 +5,23 @@ const { readSqliteJsonRowsAsync } = require("./sqlite-reader");
5
5
 
6
6
  // OpenCode Go usage limits.
7
7
  //
8
- // Two data sources, in priority order:
8
+ // Two data sources are available:
9
9
  //
10
- // 1. Local opencode.db (auth-free, the default / zero-config path). The
11
- // `opencode` CLI records every Go turn's USD `cost` in its SQLite
12
- // `message` table. OpenCode Go's limits are themselves dollar caps
13
- // ($12/5h, $30/week, $60/month https://opencode.ai/docs/go), so summing
14
- // local cost per window ÷ the dollar cap is dimensionally exact, not a
15
- // heuristic. Its only blind spot is usage that did NOT go through the
16
- // local CLI (e.g. the web console, or another machine). This is what
17
- // token-monitor (Javis603/token-monitor) falls back to, and the only
18
- // source that survives opencode's OAuth changes (see #225).
10
+ // 1. Web scrape of the workspace dashboard
11
+ // (https://opencode.ai/workspace/<id>/go) for the server's authoritative
12
+ // rolling (5h) / weekly / monthly usagePercent and subscription state.
13
+ // The cookie is sent as `Cookie: auth=<OPENCODE_GO_AUTH_COOKIE>` per
14
+ // slkiser/opencode-quota#41.
19
15
  //
20
- // 2. Web scrape of the workspace dashboard
21
- // (https://opencode.ai/workspace/<id>/go) for the server's EXACT
22
- // rolling (5h) / weekly / monthly usagePercent. Used only when a cookie
23
- // is configured AND the scrape succeeds it carries the precise
24
- // server-side number but is fragile: opencode moved auth to OAuth
25
- // (auth.opencode.ai), so a bare `auth` cookie now often 302s to the login
26
- // page (the #225 saga). The cookie is sent verbatim as
27
- // `Cookie: auth=<OPENCODE_GO_AUTH_COOKIE>` per slkiser/opencode-quota#41.
16
+ // 2. Local opencode.db cost aggregation, explicitly enabled with
17
+ // TOKENTRACKER_OPENCODE_GO_LOCAL_ESTIMATE=1. The `opencode` CLI records
18
+ // every Go turn's USD `cost` in SQLite, so cost ÷ Go's dollar caps is a
19
+ // useful local estimate. It cannot prove that the account still has an
20
+ // active Go subscription, because the database contains historical turns
21
+ // but no current entitlement state.
28
22
  //
29
- // The opencode web console has no public REST API for quota
30
- // (anomalyco/opencode#16017, #16513) and the CLI's `sk-` API key authenticates
31
- // only the inference gateway (/zen/go/v1), not the usage windows — so the local
32
- // DB is the only accurate auth-free option. The returned shape is identical for
33
- // both sources; `source` is `'local'` or `'web'`.
23
+ // The returned shape is shared by both sources. `source` is `'web'` for
24
+ // authoritative dashboard data or `'local-estimate'` for the opt-in estimate.
34
25
 
35
26
  const SCRAPED_NUMBER_PATTERN = "([0-9]+(?:\\.[0-9]+)?)";
36
27
 
@@ -52,6 +43,26 @@ const DASHBOARD_URL_SUFFIX = "/go";
52
43
  const USER_AGENT =
53
44
  "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Gecko/20100101 Firefox/148.0";
54
45
  const DEFAULT_SCRAPE_TIMEOUT_MS = 10_000;
46
+ const LOCAL_ESTIMATE_ENV = "TOKENTRACKER_OPENCODE_GO_LOCAL_ESTIMATE";
47
+
48
+ function isTruthyEnvValue(value) {
49
+ return ["1", "true", "yes", "on"].includes(String(value ?? "").trim().toLowerCase());
50
+ }
51
+
52
+ function localEstimateEnabled(env = process.env) {
53
+ return Boolean(env && typeof env === "object" && isTruthyEnvValue(env[LOCAL_ESTIMATE_ENV]));
54
+ }
55
+
56
+ // OpenCode's inactive Go workspace renders a Subscribe button and no usage
57
+ // items. Keep this detection deliberately narrow: an unknown/changed page must
58
+ // remain an error, not be guessed as an inactive subscription.
59
+ function detectSubscriptionStatus(html) {
60
+ if (typeof html !== "string") return "unknown";
61
+ const normalized = html.replace(/\\(["'])/g, "$1");
62
+ return /data-slot\s*=\s*["']subscribe-button["']/i.test(normalized)
63
+ ? "inactive"
64
+ : "unknown";
65
+ }
55
66
 
56
67
  function readConfig(env = process.env) {
57
68
  if (!env || typeof env !== "object") return null;
@@ -412,7 +423,8 @@ function buildGoAggregateSql({ sessionStart, weekStart, weekEnd, monthStart, mon
412
423
  }
413
424
 
414
425
  // Returns { source:'local', primary/secondary/tertiary_window } or null when no
415
- // opencode.db / no opencode-go rows are found.
426
+ // opencode.db / no opencode-go rows are found. The caller labels this result as
427
+ // an estimate and only exposes it when explicitly opted in.
416
428
  async function collectOpencodeGoLocal({ home, env = process.env, nowMs = Date.now(), sqliteOptions = {} } = {}) {
417
429
  const paths = discoverOpencodeDbPaths({ home, env });
418
430
  if (paths.length === 0) return null;
@@ -465,7 +477,8 @@ function localGoResult(local) {
465
477
  return {
466
478
  configured: true,
467
479
  error: null,
468
- source: "local",
480
+ source: "local-estimate",
481
+ subscription_status: "unknown",
469
482
  // No `plan_label` — the brand name "OpenCode Go" is the row title.
470
483
  primary_window: local.primary_window || null,
471
484
  secondary_window: local.secondary_window || null,
@@ -527,6 +540,13 @@ async function scrapeOpencodeGoWeb({ cfg, fetchImpl, nowMs, timeoutMs }) {
527
540
 
528
541
  const { rolling, weekly, monthly } = extractWindows(html, nowMs);
529
542
  if (!rolling && !weekly && !monthly) {
543
+ if (detectSubscriptionStatus(html) === "inactive") {
544
+ return {
545
+ configured: true,
546
+ error: null,
547
+ subscription_status: "inactive",
548
+ };
549
+ }
530
550
  return {
531
551
  configured: true,
532
552
  error:
@@ -537,6 +557,7 @@ async function scrapeOpencodeGoWeb({ cfg, fetchImpl, nowMs, timeoutMs }) {
537
557
  return {
538
558
  configured: true,
539
559
  error: null,
560
+ subscription_status: "active",
540
561
  // No `plan_label` — the brand name "OpenCode Go" is the row title, so
541
562
  // appending "Go" again would render "OpenCode Go Go" in the panel.
542
563
  primary_window: rolling,
@@ -554,20 +575,26 @@ async function fetchOpencodeGoLimits({
554
575
  sqliteOptions = {},
555
576
  } = {}) {
556
577
  const cfg = readConfig(env);
578
+ const allowLocalEstimate = localEstimateEnabled(env);
557
579
 
558
- // Cookie configured prefer the exact server-side scrape; fall back to the
559
- // local DB estimate when the scrape can't authenticate/parse (the #225 case).
580
+ // A configured cookie gives us the only authoritative subscription state.
581
+ // Never replace an explicit inactive page with historical local usage.
560
582
  if (cfg) {
561
583
  const web = await scrapeOpencodeGoWeb({ cfg, fetchImpl, nowMs, timeoutMs });
584
+ if (web?.subscription_status === "inactive") return web;
562
585
  if (web && !web.error && (web.primary_window || web.secondary_window || web.tertiary_window)) {
563
586
  return { ...web, source: "web" };
564
587
  }
565
- const local = await collectOpencodeGoLocal({ home, env, nowMs, sqliteOptions });
566
- if (local) return localGoResult(local);
588
+ if (allowLocalEstimate) {
589
+ const local = await collectOpencodeGoLocal({ home, env, nowMs, sqliteOptions });
590
+ if (local) return localGoResult(local);
591
+ }
567
592
  return web || { configured: true, error: "OpenCode Go unavailable" };
568
593
  }
569
594
 
570
- // No cookie the local opencode.db is the zero-config source.
595
+ // Local history cannot establish that the account is currently subscribed.
596
+ // Keep it available for users who explicitly accept that limitation.
597
+ if (!allowLocalEstimate) return { configured: false };
571
598
  const local = await collectOpencodeGoLocal({ home, env, nowMs, sqliteOptions });
572
599
  if (local) return localGoResult(local);
573
600
  return { configured: false };
@@ -576,6 +603,8 @@ async function fetchOpencodeGoLimits({
576
603
  module.exports = {
577
604
  fetchOpencodeGoLimits,
578
605
  readConfig,
606
+ localEstimateEnabled,
607
+ detectSubscriptionStatus,
579
608
  extractWindows,
580
609
  parseWindowUsage,
581
610
  parseDataSlotFormat,