tokentracker-cli 1.1.13 → 1.2.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.
Files changed (126) hide show
  1. package/dashboard/dist/assets/{AchievementBadge-DHCKPAgO.js → AchievementBadge-DPlXNqoK.js} +1 -1
  2. package/dashboard/dist/assets/{AchievementsPage-BqlGMD7S.js → AchievementsPage-B49wjdK_.js} +2 -2
  3. package/dashboard/dist/assets/{AchievementsSection-CTTTR9CN.js → AchievementsSection-DoYMz2Rr.js} +1 -1
  4. package/dashboard/dist/assets/{BadgeDetailModal-e0Iz8ct4.js → BadgeDetailModal-DUCS6K1n.js} +1 -1
  5. package/dashboard/dist/assets/{BotAnimated-BLYZyT1O.js → BotAnimated-C21PdYSf.js} +1 -1
  6. package/dashboard/dist/assets/{Card-EHkn9ktD.js → Card-BU2CqxfU.js} +1 -1
  7. package/dashboard/dist/assets/{CommandPalette-CuSVstX9.js → CommandPalette-O3pOLyJo.js} +1 -1
  8. package/dashboard/dist/assets/CommunityStatsModal-Bt82neQ7.js +1 -0
  9. package/dashboard/dist/assets/{ConfirmModal-pIUQJ7QM.js → ConfirmModal-zUljpLNe.js} +1 -1
  10. package/dashboard/dist/assets/{Controls-CX4Di57j.js → Controls-D56v-YJw.js} +1 -1
  11. package/dashboard/dist/assets/DashboardPage-CKEuFzuc.js +1 -0
  12. package/dashboard/dist/assets/DevicePage-CCIyAYH3.js +1 -0
  13. package/dashboard/dist/assets/{DialogClose-Dn-nxOSp.js → DialogClose-C-QK97qp.js} +1 -1
  14. package/dashboard/dist/assets/DialogDescription-COXweCuc.js +1 -0
  15. package/dashboard/dist/assets/{DialogTitle-DuAY9sfZ.js → DialogTitle-6q0jyME6.js} +1 -1
  16. package/dashboard/dist/assets/{FadeIn-25jNO9JV.js → FadeIn-BUm1z22V.js} +1 -1
  17. package/dashboard/dist/assets/{HeaderGithubStar-0-ne32-S.js → HeaderGithubStar-CxdmloMy.js} +1 -1
  18. package/dashboard/dist/assets/{HoverTooltip-C2Qop8B3.js → HoverTooltip-CrMdGYZj.js} +1 -1
  19. package/dashboard/dist/assets/{IpCheckPage-Q9lwiFdt.js → IpCheckPage-w6UUK_1K.js} +1 -1
  20. package/dashboard/dist/assets/LandingPage-DLDE7wdR.js +2 -0
  21. package/dashboard/dist/assets/{LeaderboardAvatar-CiQrgneF.js → LeaderboardAvatar-BqD4dEvQ.js} +1 -1
  22. package/dashboard/dist/assets/LeaderboardPage-kgHSryiT.js +2 -0
  23. package/dashboard/dist/assets/{LeaderboardProfileModal-CF-MI8rL.js → LeaderboardProfileModal-zLdeOiA3.js} +4 -4
  24. package/dashboard/dist/assets/{LeaderboardProfilePage-DRbLb-fO.js → LeaderboardProfilePage-Fljho5RP.js} +2 -2
  25. package/dashboard/dist/assets/{LimitsPage-lvYidBkz.js → LimitsPage-Do_uJqdQ.js} +2 -2
  26. package/dashboard/dist/assets/{LocalOnlyNotice-CAQTnlaI.js → LocalOnlyNotice-RIj2XT_X.js} +1 -1
  27. package/dashboard/dist/assets/{LoginCard-Ci2Rrsfl.js → LoginCard-Dx6MmGjU.js} +1 -1
  28. package/dashboard/dist/assets/{LoginModal-DhyQPl-V.js → LoginModal-BWaZVRnT.js} +1 -1
  29. package/dashboard/dist/assets/{LoginPage-B6q8P6hE.js → LoginPage-BNkU-riS.js} +1 -1
  30. package/dashboard/dist/assets/{PetPage-82Cldgg6.js → PetPage-CyEgU2Vm.js} +1 -1
  31. package/dashboard/dist/assets/PopoverPopup-CmUIY8lF.js +1 -0
  32. package/dashboard/dist/assets/{ProviderIcon-aLwclDsD.js → ProviderIcon-BSI-1Cy8.js} +1 -1
  33. package/dashboard/dist/assets/{ResetPasswordPage-BlRl_aJr.js → ResetPasswordPage-CUXKkLb4.js} +1 -1
  34. package/dashboard/dist/assets/Select-B6CM0ico.js +1 -0
  35. package/dashboard/dist/assets/SelectItemText-CZ7puxuB.js +1 -0
  36. package/dashboard/dist/assets/{ServiceStatusPage-Pd6dgJua.js → ServiceStatusPage-B4lIs2fc.js} +1 -1
  37. package/dashboard/dist/assets/SessionsPage-BngTWN7Q.css +1 -0
  38. package/dashboard/dist/assets/SessionsPage-D99UfBgy.js +3 -0
  39. package/dashboard/dist/assets/SettingsPage-DT3rld91.js +2 -0
  40. package/dashboard/dist/assets/SkillsPage-Dv47kVCB.js +1 -0
  41. package/dashboard/dist/assets/{TokenGalaxy-BN71jQQ8.js → TokenGalaxy-Bk3kGSGi.js} +2 -2
  42. package/dashboard/dist/assets/TrendMonitor-mB1x3c_o.js +19 -0
  43. package/dashboard/dist/assets/WidgetsPage-BhUArowe.js +1 -0
  44. package/dashboard/dist/assets/{WrappedPage-Cq8DrzLm.js → WrappedPage-DHRmP4Ey.js} +1 -1
  45. package/dashboard/dist/assets/__vite-browser-external-BW1rxvYq.js +45 -0
  46. package/dashboard/dist/assets/__vite-browser-external-gKkiQad5.css +1 -0
  47. package/dashboard/dist/assets/{agent-logos-X7FQq7rz.js → agent-logos-D7f7AMF-.js} +1 -1
  48. package/dashboard/dist/assets/{arrow-up-right-CPxuidzy.js → arrow-up-right-c39fdiDQ.js} +1 -1
  49. package/dashboard/dist/assets/{check-D2K19ObI.js → check-8LQ7IvkH.js} +1 -1
  50. package/dashboard/dist/assets/{composite-BN1UKLj0.js → composite-BgLSXWKA.js} +1 -1
  51. package/dashboard/dist/assets/{download-CpW-TB2h.js → download-x3Gmv-wu.js} +1 -1
  52. package/dashboard/dist/assets/{external-link-ChBchK6G.js → external-link-daw-XTjZ.js} +1 -1
  53. package/dashboard/dist/assets/{flame-f7119ZMd.js → flame-B9o0_EF8.js} +1 -1
  54. package/dashboard/dist/assets/{format-tokens-COrRnk53.js → format-tokens-o9atQGtV.js} +1 -1
  55. package/dashboard/dist/assets/host-mode-GPY2DxE4.js +1 -0
  56. package/dashboard/dist/assets/{icons-Dn9vA9Qu.js → icons-CmM2z-Jt.js} +1 -1
  57. package/dashboard/dist/assets/{index-BCNOKYSO.js → index-Bfgblk6o.js} +1 -1
  58. package/dashboard/dist/assets/{index-CA-94DFW.js → index-CPcJP0uH.js} +1 -1
  59. package/dashboard/dist/assets/{index-CElyhNzh.js → index-CtQOew7C.js} +1 -1
  60. package/dashboard/dist/assets/{info-BHsPBczD.js → info-BaK8Hoi2.js} +1 -1
  61. package/dashboard/dist/assets/{link-2-BHfEWv22.js → link-2-CV3uvMH3.js} +1 -1
  62. package/dashboard/dist/assets/{loader-circle-DTIT1Uk1.js → loader-circle-DYpa-xTZ.js} +1 -1
  63. package/dashboard/dist/assets/{modifiers.esm-CPBla3OK.js → modifiers.esm-Ds4Uh7JO.js} +1 -1
  64. package/dashboard/dist/assets/{monitor-smartphone-CbsYB-S2.js → monitor-smartphone-BbeYHvY-.js} +1 -1
  65. package/dashboard/dist/assets/{plus-CZiNoMsy.js → plus-C9qGVLFv.js} +1 -1
  66. package/dashboard/dist/assets/provider-display-BWYNyitK.js +42 -0
  67. package/dashboard/dist/assets/{react-CZdcuJ28.js → react-CO1HovF5.js} +1 -1
  68. package/dashboard/dist/assets/{search-q2ARMGca.js → search-DBnhkzG_.js} +1 -1
  69. package/dashboard/dist/assets/{skills-api-CIyqyk-w.js → skills-api-BSkbxC8-.js} +1 -1
  70. package/dashboard/dist/assets/{sortable.esm-BztIEZTk.js → sortable.esm-SVl6OixH.js} +1 -1
  71. package/dashboard/dist/assets/{store-DvdXwk-7.js → store-DrEEdO_u.js} +1 -1
  72. package/dashboard/dist/assets/terminal-kUMdPZhq.js +1 -0
  73. package/dashboard/dist/assets/{trash-D0ptaWPk.js → trash-Dd_vbrYo.js} +1 -1
  74. package/dashboard/dist/assets/{use-community-stats-DYibzhAl.js → use-community-stats-Bmlmv4H-.js} +1 -1
  75. package/dashboard/dist/assets/{use-latest-request-guard-viIhH7H5.js → use-latest-request-guard-CqZwzrCW.js} +1 -1
  76. package/dashboard/dist/assets/{use-limits-display-prefs-CbXApWU1.js → use-limits-display-prefs-CDC0MZRY.js} +1 -1
  77. package/dashboard/dist/assets/{use-ordered-list-BBP8hr8y.js → use-ordered-list-DvBI7mUU.js} +1 -1
  78. package/dashboard/dist/assets/{use-pet-settings-CIrPZ9pB.js → use-pet-settings-C_FrqySO.js} +2 -2
  79. package/dashboard/dist/assets/use-reduced-motion-BEOENqBa.js +1 -0
  80. package/dashboard/dist/assets/{use-session-efficiency-pref-2Vb3tJrw.js → use-session-efficiency-pref-D8cAb4A9.js} +1 -1
  81. package/dashboard/dist/assets/{use-transform-CECRFELz.js → use-transform-uSKYTYaW.js} +1 -1
  82. package/dashboard/dist/assets/{use-usage-limits-DEnbjOVG.js → use-usage-limits-DFgOKTtv.js} +1 -1
  83. package/dashboard/dist/assets/{useCurrency-Dh9pUzDO.js → useCurrency-t1iy_pC3.js} +1 -1
  84. package/dashboard/dist/assets/useTokenFormat-0bPjtaaM.js +1 -0
  85. package/dashboard/dist/assets/{zap-BdeAxq64.js → zap-DFM6AsYo.js} +1 -1
  86. package/dashboard/dist/index.html +2 -2
  87. package/dashboard/dist/ip-check.html +2 -2
  88. package/dashboard/dist/leaderboard.html +2 -2
  89. package/dashboard/dist/legal.css +1 -0
  90. package/dashboard/dist/pricing.html +35 -0
  91. package/dashboard/dist/privacy.html +278 -0
  92. package/dashboard/dist/share.html +2 -2
  93. package/dashboard/dist/terms.html +41 -0
  94. package/package.json +1 -1
  95. package/src/lib/codex-rollout-parser.js +5 -0
  96. package/src/lib/local-api.js +5 -1
  97. package/src/lib/pricing/index.js +45 -0
  98. package/src/lib/pricing/seed-snapshot.json +1 -1
  99. package/src/lib/rollout.js +1 -0
  100. package/src/lib/session-analytics.js +74 -18
  101. package/src/lib/session-performance.js +214 -0
  102. package/dashboard/dist/assets/CommunityStatsModal-Cb1vefLE.js +0 -1
  103. package/dashboard/dist/assets/DashboardPage-D1Ox1y3E.js +0 -1
  104. package/dashboard/dist/assets/DevicePage-Q57gBE8A.js +0 -1
  105. package/dashboard/dist/assets/DialogDescription-GXslTq2z.js +0 -1
  106. package/dashboard/dist/assets/LandingPage-iX6E8Flr.js +0 -2
  107. package/dashboard/dist/assets/LeaderboardPage-BIhIA28A.js +0 -2
  108. package/dashboard/dist/assets/PopoverPopup-Btcu7vU0.js +0 -1
  109. package/dashboard/dist/assets/SegmentedControl-2O_XVL8u.js +0 -1
  110. package/dashboard/dist/assets/Select-D5uuRQwh.js +0 -1
  111. package/dashboard/dist/assets/SelectItemText-Bq_ZdWU0.js +0 -1
  112. package/dashboard/dist/assets/SessionsPage-DjHudcn6.js +0 -2
  113. package/dashboard/dist/assets/SettingsPage-DOLhZiBE.js +0 -2
  114. package/dashboard/dist/assets/SkillsPage-D9-ZorB5.js +0 -1
  115. package/dashboard/dist/assets/ToolbarRootContext-B0cqyD1i.js +0 -1
  116. package/dashboard/dist/assets/TrendMonitor-DWxtbzyQ.js +0 -19
  117. package/dashboard/dist/assets/WidgetsPage-BwmJ2Z9d.js +0 -1
  118. package/dashboard/dist/assets/__vite-browser-external-CPMSmJgP.js +0 -45
  119. package/dashboard/dist/assets/__vite-browser-external-DrXtuPP9.css +0 -1
  120. package/dashboard/dist/assets/copy-CSN17qFv.js +0 -1
  121. package/dashboard/dist/assets/host-mode-Bc_iTzHk.js +0 -1
  122. package/dashboard/dist/assets/maximize-2-BmtIBzLu.js +0 -1
  123. package/dashboard/dist/assets/provider-display-CXKRt9q1.js +0 -42
  124. package/dashboard/dist/assets/terminal-CFQOW2Ey.js +0 -1
  125. package/dashboard/dist/assets/use-reduced-motion-Dpq-cKyM.js +0 -1
  126. package/dashboard/dist/assets/useTokenFormat-CoGcVhPP.js +0 -1
@@ -0,0 +1,278 @@
1
+ <!doctype html>
2
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Privacy Policy | TokenTracker</title><link rel="stylesheet" href="/legal.css"></head><body><header><a href="/">TokenTracker</a></header><main><h1>Privacy Policy</h1>
3
+ <p><em>Last updated: 2026-10-08 · Applies to the <code>tokentracker-cli</code> npm package, the macOS app, the Windows app, the Linux app, and <a href="https://www.tokentracker.cc">www.tokentracker.cc</a>.</em></p>
4
+ <p>TokenTracker reads the local logs that AI coding tools already write to your disk, and turns them into token counts and cost estimates. It is local-first: the dashboard, the parsers and the database all run on your machine.</p>
5
+ <p>This document lists <strong>every</strong> network request the software can make, what each one sends, and how to switch it off. If you find a request that is not listed here, that is a bug — please <a href="https://github.com/xiufengsun/TokenTracker/issues">open an issue</a>.</p>
6
+ <hr />
7
+ <h2>1. What never leaves your machine</h2>
8
+ <p>TokenTracker's parsers extract numbers and timestamps only. TRAE Work CN is a narrow exception to the local-only source model, and it is off by default: only when you explicitly set <code>TOKENTRACKER_TRAE_CN_USAGE=1</code>, during an eligible non-background sync, when local TRAE Work CN auth exists, does TokenTracker transmit the existing sign-in authorization from the locally signed-in app to TRAE's internal API for a read-only usage request. Without that variable nothing is ever sent. It is not an unconditional or default generic network request. That authorization is never persisted or logged.</p>
9
+ <p><strong>Never retained as usage data or uploaded to TokenTracker:</strong></p>
10
+ <ul>
11
+ <li><strong>Prompts, responses, and conversation bodies</strong> — TokenTracker may parse local tool files containing conversation records to extract usage metrics, but prompt and response texts are never persisted, collected, or transmitted</li>
12
+ <li><strong>File contents</strong> from your projects</li>
13
+ <li><strong>Commit messages and diffs</strong> — Git attribution runs <code>git log</code> locally, uses the subject line only to detect reverts, and keeps nothing</li>
14
+ <li><strong>API keys, cookies, and session tokens</strong> belonging to your AI providers are never persisted or logged by TokenTracker</li>
15
+ </ul>
16
+ <p><strong>Recorded locally, never uploaded:</strong></p>
17
+ <ul>
18
+ <li><strong>File paths, project names, and repository names.</strong> The Projects view needs to know which repo a session belonged to, so <code>project.queue.jsonl</code> stores a project key and git remote URL, and <code>session.queue.jsonl</code> stores each session's working directory. Both files stay on your machine — they are excluded from cloud sync (see §4) and the Projects view is computed entirely locally. Set <code>TOKENTRACKER_DISABLE_GIT_ATTRIBUTION=1</code> to stop deriving them at all.</li>
19
+ </ul>
20
+ <p>You can verify this in <a href="https://github.com/xiufengsun/TokenTracker/blob/main/src/lib/rollout.js"><code>src/lib/rollout.js</code></a>: every <code>parse*Incremental</code> function emits only the queue row shapes described below.</p>
21
+ <hr />
22
+ <h2>2. What is stored locally</h2>
23
+ <p>Everything lives under <code>~/.tokentracker/</code> (<code>%USERPROFILE%\.tokentracker\</code> on Windows):</p>
24
+ <table>
25
+ <thead>
26
+ <tr>
27
+ <th>Path</th>
28
+ <th>Contents</th>
29
+ </tr>
30
+ </thead>
31
+ <tbody>
32
+ <tr>
33
+ <td><code>tracker/queue.jsonl</code></td>
34
+ <td>Append-only hourly buckets: source, model, token counts, timestamp</td>
35
+ </tr>
36
+ <tr>
37
+ <td><code>tracker/project.queue.jsonl</code></td>
38
+ <td>The same hourly buckets, split per project: git remote URL and <code>owner/repo</code> key. Never uploaded</td>
39
+ </tr>
40
+ <tr>
41
+ <td><code>tracker/session.queue.jsonl</code></td>
42
+ <td>Per-session token totals and timing for the Sessions view, plus each session's working directory. Never uploaded</td>
43
+ </tr>
44
+ <tr>
45
+ <td><code>tracker/cursors.json</code></td>
46
+ <td>Read offsets so parsing stays incremental</td>
47
+ </tr>
48
+ <tr>
49
+ <td><code>tracker/config.json</code></td>
50
+ <td>Your preferences</td>
51
+ </tr>
52
+ <tr>
53
+ <td><code>tracker/*-usage-limits-cache.json</code></td>
54
+ <td>Last successful quota reading per provider, so a timeout shows stale bars instead of an error</td>
55
+ </tr>
56
+ <tr>
57
+ <td><code>pets/</code>, <code>skills/</code>, <code>cache/</code></td>
58
+ <td>Desktop pet assets, skill index, misc caches</td>
59
+ </tr>
60
+ </tbody>
61
+ </table>
62
+ <p>To erase everything TokenTracker knows about you, delete that directory. <code>tokentracker uninstall</code> additionally removes the hooks it installed into your AI tools.</p>
63
+ <hr />
64
+ <h2>3. Network requests</h2>
65
+ <h3>3.1 Enabled by default</h3>
66
+ <table>
67
+ <thead>
68
+ <tr>
69
+ <th>Request</th>
70
+ <th>Destination</th>
71
+ <th>What is sent</th>
72
+ <th>Frequency</th>
73
+ </tr>
74
+ </thead>
75
+ <tbody>
76
+ <tr>
77
+ <td><strong>Anonymous heartbeat</strong></td>
78
+ <td><code>srctyff5.us-east.insforge.app</code></td>
79
+ <td>A one-way hash of the machine id, plus the app version, OS platform, and app shell (<code>cli</code> / <code>macos</code> / <code>windows</code> / <code>linux</code>) as separate plain fields. Nothing else.</td>
80
+ <td>At most once per day</td>
81
+ </tr>
82
+ <tr>
83
+ <td><strong>Dashboard analytics</strong></td>
84
+ <td><code>us.i.posthog.com</code> (PostHog)</td>
85
+ <td>Pageviews and explicitly instrumented feature events, plus which shell you use. Autocapture and session recording are <strong>off</strong>; browser Do-Not-Track is respected.</td>
86
+ <td>While the dashboard is open</td>
87
+ </tr>
88
+ <tr>
89
+ <td><strong>Provider quota reads</strong></td>
90
+ <td>The provider's own API (<code>api.anthropic.com</code>, <code>chatgpt.com</code>, <code>cursor.com</code>, <code>api.github.com</code>, <code>api.kimi.com</code>, <code>api.z.ai</code>, <code>qoder.com</code>, <code>qoder.com.cn</code>, <code>openapi.qoder.sh</code>, <code>openapi.qoder.com.cn</code>, <code>cloudcode-pa.googleapis.com</code>, …)</td>
91
+ <td>Whatever that provider's own endpoint requires, authenticated with the credentials <strong>that provider already stored on your machine</strong>. These requests go directly from your machine to the provider — they never pass through our servers, and we never see the response.</td>
92
+ <td>While quota bars are visible</td>
93
+ </tr>
94
+ <tr>
95
+ <td><strong>TRAE Work CN usage read</strong></td>
96
+ <td>TRAE's internal API</td>
97
+ <td>Transmits the existing sign-in authorization from the locally signed-in TRAE Work CN app to TRAE; reads usage metadata only. TokenTracker never persists or logs the auth token or prompt/response content.</td>
98
+ <td>Off unless you set <code>TOKENTRACKER_TRAE_CN_USAGE=1</code>; then during eligible non-background sync when local TRAE Work CN auth exists</td>
99
+ </tr>
100
+ <tr>
101
+ <td><strong>GitHub star count</strong></td>
102
+ <td><code>api.github.com</code></td>
103
+ <td>Nothing but the request itself (public repo metadata)</td>
104
+ <td>On dashboard load</td>
105
+ </tr>
106
+ <tr>
107
+ <td><strong>Update check</strong></td>
108
+ <td><code>api.github.com</code></td>
109
+ <td>Nothing but the request itself</td>
110
+ <td>Windows: once at launch. macOS: only when you click "Check for Updates"</td>
111
+ </tr>
112
+ <tr>
113
+ <td><strong>Pricing data refresh</strong></td>
114
+ <td><code>raw.githubusercontent.com</code></td>
115
+ <td>Nothing but the request itself (public model pricing JSON from BerriAI/litellm)</td>
116
+ <td>At most once every 24 hours when the local pricing cache is missing or stale</td>
117
+ </tr>
118
+ </tbody>
119
+ </table>
120
+ <p>Both telemetry items are disabled together by a single switch:</p>
121
+ <pre><code class="language-bash">export TOKENTRACKER_NO_TELEMETRY=1 # or DO_NOT_TRACK=1
122
+ </code></pre>
123
+ <p>You can also set <code>"telemetry": false</code> in <code>~/.tokentracker/tracker/config.json</code>. On localhost and inside the desktop apps, the dashboard asks the local server for this preference before initialising analytics — and if the answer cannot be confirmed, analytics stays <strong>off</strong> (fail-closed).</p>
124
+ <p>Audit: <a href="https://github.com/xiufengsun/TokenTracker/blob/main/src/lib/telemetry.js"><code>src/lib/telemetry.js</code></a>, <a href="https://github.com/xiufengsun/TokenTracker/blob/main/dashboard/src/lib/analytics.js"><code>dashboard/src/lib/analytics.js</code></a>, <a href="https://github.com/xiufengsun/TokenTracker/blob/main/src/lib/pricing/litellm-fetcher.js"><code>src/lib/pricing/litellm-fetcher.js</code></a>.</p>
125
+ <h3>3.2 Only after you opt in or click something</h3>
126
+ <table>
127
+ <thead>
128
+ <tr>
129
+ <th>Request</th>
130
+ <th>Destination</th>
131
+ <th>What is sent</th>
132
+ <th>Trigger</th>
133
+ </tr>
134
+ </thead>
135
+ <tbody>
136
+ <tr>
137
+ <td><strong>Devin quota read</strong></td>
138
+ <td><code>server.codeium.com</code> (Devin's official <code>GetPlanStatus</code> RPC)</td>
139
+ <td>An empty JSON body, authenticated with the Devin CLI session token already stored on your machine. The token is never persisted or logged by TokenTracker.</td>
140
+ <td>Off by default — only while the Devin provider switch in Settings → Usage &amp; Limits → Providers is on, and only on a locally authenticated request</td>
141
+ </tr>
142
+ <tr>
143
+ <td><strong>Cloud sync / leaderboard</strong></td>
144
+ <td><code>srctyff5.us-east.insforge.app</code></td>
145
+ <td>Hourly buckets only — see §4</td>
146
+ <td>Signing in to a TokenTracker account</td>
147
+ </tr>
148
+ <tr>
149
+ <td><strong>Exchange rates</strong></td>
150
+ <td><code>open.er-api.com</code></td>
151
+ <td>Nothing but the request itself</td>
152
+ <td>Selecting a non-USD display currency</td>
153
+ </tr>
154
+ <tr>
155
+ <td><strong>Desktop pet download</strong></td>
156
+ <td><code>codex-pets.net</code></td>
157
+ <td>The pet id you chose</td>
158
+ <td>Importing a pet from a link</td>
159
+ </tr>
160
+ <tr>
161
+ <td><strong>IP check page</strong></td>
162
+ <td><code>ip.net.coffee</code>, <code>claude.ai</code>, <code>1.1.1.1</code></td>
163
+ <td>Your IP address is, by design, what these endpoints observe — that page exists to tell you how providers see your network</td>
164
+ <td>Opening the IP Check page</td>
165
+ </tr>
166
+ <tr>
167
+ <td><strong>Service status page</strong></td>
168
+ <td>Provider status pages (<code>status.claude.com</code>, <code>status.openai.com</code>, <code>status.cursor.com</code>, …)</td>
169
+ <td>Nothing but the request itself</td>
170
+ <td>Opening the Service Status page</td>
171
+ </tr>
172
+ <tr>
173
+ <td><strong>Share card fonts</strong></td>
174
+ <td><code>fonts.googleapis.com</code></td>
175
+ <td>Standard web-font request; Google can see your IP address</td>
176
+ <td>Generating a share image</td>
177
+ </tr>
178
+ </tbody>
179
+ </table>
180
+ <h3>3.3 Never</h3>
181
+ <ul>
182
+ <li>No request contains prompt text, responses, file contents, paths, or project names.</li>
183
+ <li>We operate no ad network, no data broker integration, and no cross-site tracking.</li>
184
+ <li>We do not sell or rent your data. Service providers listed in this policy process data only for the purposes described here.</li>
185
+ </ul>
186
+ <hr />
187
+ <h2>4. Cloud account and leaderboard</h2>
188
+ <p>Signing in is <strong>entirely optional</strong>. TokenTracker is fully functional without an account; the leaderboard, cross-device aggregation, badges and public profiles are the only features that require one.</p>
189
+ <p><strong>Sent when signed in:</strong></p>
190
+ <ul>
191
+ <li>Hourly usage buckets, each containing exactly: <code>hour_start</code>, <code>source</code>, <code>model</code>, <code>input_tokens</code>, <code>output_tokens</code>, <code>cached_input_tokens</code>, <code>cache_creation_input_tokens</code>, <code>reasoning_output_tokens</code>, <code>total_tokens</code>, <code>conversation_count</code></li>
192
+ <li>A machine id at device-registration time, so usage from several computers can be merged into one account without double-counting</li>
193
+ <li>The email address and display name from your OAuth provider (GitHub or Google)</li>
194
+ </ul>
195
+ <p><strong>Not sent, ever:</strong></p>
196
+ <ul>
197
+ <li>Prompts, responses, file contents, project or repository names, file paths</li>
198
+ <li>Local session records — <code>session.queue.jsonl</code> stays on your machine</li>
199
+ <li>Per-project breakdowns — <code>project.queue.jsonl</code> is never uploaded</li>
200
+ <li>Any provider credential</li>
201
+ </ul>
202
+ <p><strong>Public visibility:</strong> your profile appears on the public leaderboard only while <code>Settings → Account → Public profile</code> is on. Turning it off removes you from the leaderboard and turns badges into a "private" placeholder.</p>
203
+ <p><strong>Deleting cloud data:</strong> contact us privately at <a href="mailto:rynnsun0509@gmail.com">rynnsun0509@gmail.com</a> and we will remove the account and its rows. Deleting <code>~/.tokentracker/</code> removes the local copy immediately.</p>
204
+ <hr />
205
+ <h2>5. Third parties</h2>
206
+ <table>
207
+ <thead>
208
+ <tr>
209
+ <th>Service</th>
210
+ <th>Role</th>
211
+ <th>Their policy</th>
212
+ </tr>
213
+ </thead>
214
+ <tbody>
215
+ <tr>
216
+ <td>InsForge</td>
217
+ <td>Backend for accounts, cloud sync, leaderboard, heartbeat</td>
218
+ <td><a href="https://github.com/InsForge">github.com/InsForge</a></td>
219
+ </tr>
220
+ <tr>
221
+ <td>PostHog</td>
222
+ <td>Anonymous product analytics</td>
223
+ <td><a href="https://posthog.com/privacy">posthog.com/privacy</a></td>
224
+ </tr>
225
+ <tr>
226
+ <td>Vercel</td>
227
+ <td>Hosting for www.tokentracker.cc</td>
228
+ <td><a href="https://vercel.com/legal/privacy-policy">vercel.com/legal/privacy-policy</a></td>
229
+ </tr>
230
+ <tr>
231
+ <td>GitHub</td>
232
+ <td>Source hosting, releases, OAuth, star counts, upstream pricing data (<code>raw.githubusercontent.com</code>)</td>
233
+ <td><a href="https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement">GitHub Privacy Statement</a></td>
234
+ </tr>
235
+ <tr>
236
+ <td>Google</td>
237
+ <td>OAuth sign-in, fonts on share cards</td>
238
+ <td><a href="https://policies.google.com/privacy">policies.google.com/privacy</a></td>
239
+ </tr>
240
+ </tbody>
241
+ </table>
242
+ <p>AI providers whose quota endpoints TokenTracker reads (Anthropic, OpenAI, Cursor, GitHub Copilot, Google, Moonshot, Z.ai, Qoder, Devin, …) are governed by their own policies. TokenTracker acts on your behalf with credentials already on your machine; it does not create any new relationship with them.</p>
243
+ <hr />
244
+ <h2>6. Turning things off</h2>
245
+ <table>
246
+ <thead>
247
+ <tr>
248
+ <th>Variable</th>
249
+ <th>Effect</th>
250
+ </tr>
251
+ </thead>
252
+ <tbody>
253
+ <tr>
254
+ <td><code>TOKENTRACKER_NO_TELEMETRY=1</code></td>
255
+ <td>Disables the daily heartbeat <strong>and</strong> dashboard analytics</td>
256
+ </tr>
257
+ <tr>
258
+ <td><code>DO_NOT_TRACK=1</code></td>
259
+ <td>Same as above (respects the standard)</td>
260
+ </tr>
261
+ <tr>
262
+ <td><code>TOKENTRACKER_DISABLE_GIT_ATTRIBUTION=1</code></td>
263
+ <td>Stops TokenTracker running <code>git log</code> inside your project directories</td>
264
+ </tr>
265
+ </tbody>
266
+ </table>
267
+ <p>Signing out removes cloud sync. Not signing in means it never starts.</p>
268
+ <hr />
269
+ <h2>7. Children</h2>
270
+ <p>TokenTracker is a developer tool and is not directed at children under 13. We do not knowingly collect personal information from children.</p>
271
+ <h2>8. Changes</h2>
272
+ <p>Material changes to this policy will be noted in the release notes and in the <code>Last updated</code> date above. The full history is in <a href="https://github.com/xiufengsun/TokenTracker/commits/main/dashboard/public/privacy.html">this file's Git log</a>.</p>
273
+ <h2>9. Contact</h2>
274
+ <p>Questions, corrections, or deletion requests: <a href="mailto:rynnsun0509@gmail.com">rynnsun0509@gmail.com</a>. Do not publish private account or payment information in GitHub issues.</p>
275
+ <h2>Planned Cloud billing</h2>
276
+ <p>Paid Cloud has not launched. For the planned billing service, Waffo Pancake will operate hosted checkout and handle payment details under its own terms and privacy policy. TokenTracker will receive the order/payment reference, billing email, purchased plan, amount/currency, payment status, subscription term and refund status necessary to deliver access and handle billing. Full payment card numbers and security codes are not stored by TokenTracker. Only the usage metrics described above will be synchronized.</p>
277
+ <p>The planned membership provides access to 90 days of hourly details and 24 months of daily summaries. After membership expires, personal history has a 30-day read/export window. These are access windows, not a promise to delete all stored usage on the expiry date. Aggregated community statistics and compressed usage metrics may be retained to preserve and correct lifetime statistics. Necessary billing records may be retained to meet legal/accounting obligations and resolve payment disputes. Contact the private support address for access, correction or deletion requests; legally required records may be retained. Local data is unaffected by Cloud expiry.</p>
278
+ <p>TokenTracker is operated by Sun Xiufeng, an individual developer in China. Account and usage storage uses the existing InsForge backend, including infrastructure in the United States; other providers can process data in their operating regions. Contact <a href="mailto:rynnsun0509@gmail.com">rynnsun0509@gmail.com</a> for privacy and security requests. This billing supplement was added on 8 October 2026 and describes the planned billing service. Paid Cloud is not yet open.</p></main><footer><a href="/pricing.html">Cloud pricing</a><a href="/terms.html">Terms of Service</a><a href="/privacy.html">Privacy Policy</a><a href="mailto:rynnsun0509@gmail.com">Support: rynnsun0509@gmail.com</a></footer></body></html>
@@ -75,8 +75,8 @@
75
75
  "description": "Shareable Token Tracker dashboard snapshot."
76
76
  }
77
77
  </script>
78
- <script type="module" crossorigin src="/assets/__vite-browser-external-CPMSmJgP.js"></script>
79
- <link rel="stylesheet" crossorigin href="/assets/__vite-browser-external-DrXtuPP9.css">
78
+ <script type="module" crossorigin src="/assets/__vite-browser-external-BW1rxvYq.js"></script>
79
+ <link rel="stylesheet" crossorigin href="/assets/__vite-browser-external-gKkiQad5.css">
80
80
  </head>
81
81
  <body>
82
82
  <main class="aeo-seed-content" aria-label="Token Tracker share page summary">
@@ -0,0 +1,41 @@
1
+ <!doctype html>
2
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Terms of Service | TokenTracker</title><link rel="stylesheet" href="/legal.css"></head><body><header><a href="/">TokenTracker</a></header><main><h1>Terms of Service</h1>
3
+ <p>Version 1.0. Effective 8 October 2026.</p>
4
+ <h2>1. Operator and scope</h2>
5
+ <p>TokenTracker is operated by Sun Xiufeng, an individual developer in China. These terms cover the TokenTracker website and officially hosted TokenTracker Cloud service at https://www.tokentracker.cc. Contact rynnsun0509@gmail.com for support, billing, refunds, privacy and security requests.</p>
6
+ <p>The open-source local software remains governed by its MIT license. Buying Cloud is not required to use the local software. You must be at least 18 and authorized to enter into the subscription agreement. If you do not agree to the applicable terms, do not purchase or use the hosted service.</p>
7
+ <h2>2. The service</h2>
8
+ <p>TokenTracker analyzes usage metrics from AI coding tools. Local token collection, cost estimates, provider limits, local charts, exports and desktop features remain free. TokenTracker does not sell AI model tokens or generate AI content for customers.</p>
9
+ <p>The planned Cloud service provides hosted cross-device usage analytics, a personal web dashboard, history and exports, and device management for up to five registered synchronization devices. The proposed history window is 90 days of hourly details and 24 months of daily summaries. Cloud synchronizes usage metrics, not prompts, responses, source code, AI provider credentials, or local project and session records.</p>
10
+ <p>Paid Cloud is not yet open. These billing provisions describe the planned service and apply to purchases only after the service opens and the customer accepts the terms at checkout. Purchasing eligibility and availability will be shown on the website. Access begins only after the server verifies a successful payment.</p>
11
+ <h2>3. Accounts</h2>
12
+ <p>Keep your account and billing email accurate. Protect your credentials and contact support if an account is compromised. You are responsible for authorized activity on your account. If you act for an organization, you must have authority to do so.</p>
13
+ <h2>4. Plans, pricing and taxes</h2>
14
+ <p>The confirmed base price for the planned Cloud service is USD 4.99 per month or USD 39.99 per year. The annual amount is charged once per year, not monthly. Recurring and fixed-term access have the same base prices. See <a href="/pricing.html">Cloud pricing</a>.</p>
15
+ <p>Prices exclude applicable taxes. Waffo Pancake is the intended Merchant of Record and hosted payment provider, subject to its approval and applicable terms. Its checkout displays available payment methods, taxes and the final total before payment. TokenTracker does not store full payment card numbers or security codes.</p>
16
+ <h2>5. Recurring and fixed-term billing</h2>
17
+ <p>A recurring plan renews monthly or yearly until canceled. The customer must expressly authorize recurring payments at checkout. We will not charge an existing free account merely because paid Cloud launches.</p>
18
+ <p>A fixed-term purchase is a single payment for the displayed term and does not authorize automatic renewal. Buying another fixed term extends access according to the term shown before purchase. Do not start overlapping subscriptions; manage the current plan first.</p>
19
+ <p>A seven-day no-card trial, where offered, begins only when the user activates it and does not automatically turn into a paid subscription. Changes to prices or material billing terms will be communicated before they apply to a future purchase or renewal. Paid terms already purchased retain their agreed price and duration.</p>
20
+ <h2>6. Cancellation and expiry</h2>
21
+ <p>After launch, recurring customers can stop renewal through account billing or Waffo's customer portal at https://pancake.waffo.ai/consumer/portal/login, using the email entered at checkout. Support can assist at rynnsun0509@gmail.com. Cancellation stops future renewal charges while access continues until the paid term ends. Cancellation alone does not request a refund.</p>
22
+ <p>After a trial or paid term ends, new personal Cloud uploads stop. Previously retained personal Cloud history remains readable and exportable for 30 days, subject to the plan's history windows. Local use and free community leaderboard participation continue. Pausing a device does not delete its local data. See the <a href="/privacy.html">Privacy Policy</a> for retention and deletion requests.</p>
23
+ <h2>7. Refund policy</h2>
24
+ <p>The following policy applies to Cloud purchases after paid Cloud opens:</p>
25
+ <ul>
26
+ <li>First-time Cloud buyers may request a full refund within seven days of their initial payment, for either a monthly or annual term.</li>
27
+ <li>Confirmed duplicate charges or incorrect charges are refundable.</li>
28
+ <li>If we cannot deliver purchased access, or an outage within our control lasts at least 72 hours, we will offer a proportional refund for the affected paid period.</li>
29
+ <li>Outside these cases, used subscription periods are not ordinarily refundable. Statutory cancellation, withdrawal, refund and other consumer rights remain available wherever applicable.</li>
30
+ </ul>
31
+ <p>Request a refund at rynnsun0509@gmail.com using your account email, order reference and reason. Do not send card numbers, passwords or provider credentials. Handling times are acknowledgment within two business days and processing eligible refunds within five to ten business days; bank posting time varies. A full refund ends the refunded entitlement. Partial refunds follow the confirmed resolution.</p>
32
+ <h2>8. Acceptable use and intellectual property</h2>
33
+ <p>Use the hosted service lawfully. Do not compromise accounts, bypass access controls, submit fabricated metrics, distribute malware through the service, infringe others' rights, or abuse the infrastructure. We may suspend access for material breaches, fraud or legal obligations. If we discontinue a paid service for reasons unrelated to a customer's breach, we will refund the unused prepaid portion.</p>
34
+ <p>You retain rights in your usage data. You permit us to process synchronized metrics only to provide and maintain the service, including community visibility that you choose. The MIT license continues to govern the open-source software.</p>
35
+ <h2>9. Privacy, estimates and availability</h2>
36
+ <p>The <a href="/privacy.html">Privacy Policy</a> describes data processing and third parties. Estimated AI costs are calculated from usage and model prices; they are not provider invoices or guarantees of billing accuracy. Third-party provider availability and upstream format changes can affect results. Keep your own local copies and exports.</p>
37
+ <h2>10. Liability and consumer rights</h2>
38
+ <p>To the extent permitted by law, the hosted service is provided as available without a guarantee of uninterrupted operation or error-free estimates. We do not exclude rights or liabilities that applicable law does not allow us to exclude. Where lawful, liability for the hosted service is limited to the amount paid for Cloud in the twelve months preceding the claim, and excludes indirect losses.</p>
39
+ <h2>11. Changes, disputes and contact</h2>
40
+ <p>Material updates to these terms will be announced at least fourteen days before taking effect. Changes do not remove accrued consumer rights. Chinese law governs the operator's service obligations, subject to mandatory protections in a customer's applicable jurisdiction. Contact rynnsun0509@gmail.com to resolve a dispute; these terms do not require mandatory arbitration or waive rights to a competent court.</p>
41
+ <p>Support, billing, refund, cancellation, privacy and security contact: <a href="mailto:rynnsun0509@gmail.com">rynnsun0509@gmail.com</a>. Account deletion requests are also accepted by this private email channel. Do not post private account or payment information in public GitHub issues.</p></main><footer><a href="/pricing.html">Cloud pricing</a><a href="/terms.html">Terms of Service</a><a href="/privacy.html">Privacy Policy</a><a href="mailto:rynnsun0509@gmail.com">Support: rynnsun0509@gmail.com</a></footer></body></html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokentracker-cli",
3
- "version": "1.1.13",
3
+ "version": "1.2.1",
4
4
  "description": "Local-first AI coding token usage and cost tracker for 43 tools, with a dashboard, desktop pet, widgets, achievements, and native macOS/Windows apps.",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
@@ -570,6 +570,7 @@ async function parseCodexRolloutFile(filePath, {
570
570
  collectBreakdowns = true,
571
571
  collectModelUsage = false,
572
572
  onObject = null,
573
+ onUsage = null,
573
574
  } = {}) {
574
575
  const filePaths = (Array.isArray(filePath) ? filePath : [filePath]).filter(Boolean);
575
576
  const primaryFilePath = filePaths[0] || String(filePath || "");
@@ -1085,16 +1086,20 @@ async function parseCodexRolloutFile(filePath, {
1085
1086
  }
1086
1087
  const rawDelta = consumeUsageDelta(usageDeltaState, lastUsage, totalUsage);
1087
1088
  const delta = rawDelta ? normalizeUsage(rawDelta) : null;
1089
+ // Timing consumers receive canonical, deduplicated usage below. A null
1090
+ // delta is a snapshot, not another model response.
1088
1091
  if (compaction) countCompactionRecord(compaction);
1089
1092
  const isNewResumeEvent = noteTokenEvent(ts, lastUsage, totalUsage);
1090
1093
  if (inRequestedRange) {
1091
1094
  const eventSessionId = sessionId || rolloutSessionIdFromPath(primaryFilePath) || primaryFilePath;
1092
1095
  const eventKey = `${eventSessionId}:${ts}:${usageEventSignature(lastUsage, totalUsage)}`;
1093
1096
  if (!isNewResumeEvent || (seenTokenEvents && seenTokenEvents.has(eventKey))) {
1097
+ if (typeof onUsage === "function") onUsage({ timestamp: ts, delta: null, rawUsage: lastUsage, model: attributedModel });
1094
1098
  attributeTurn(null);
1095
1099
  } else {
1096
1100
  if (seenTokenEvents && delta?.total_tokens > 0) seenTokenEvents.add(eventKey);
1097
1101
  recordModelUsage(delta, lastUsage || rawDelta);
1102
+ if (typeof onUsage === "function") onUsage({ timestamp: ts, delta, rawUsage: lastUsage || rawDelta, model: attributedModel });
1098
1103
  attributeTurn(delta);
1099
1104
  }
1100
1105
  } else {
@@ -56,6 +56,7 @@ const {
56
56
  computeRowCost,
57
57
  ensurePricingLoaded,
58
58
  getPricingRevision,
59
+ getModelPricingInfo,
59
60
  } = require("./pricing");
60
61
 
61
62
  const {
@@ -2539,8 +2540,11 @@ function createLocalApiHandler({ queuePath, serverVersion = null }) {
2539
2540
  sa.totals.cache_creation_input_tokens += row.cache_creation_input_tokens || 0;
2540
2541
  sa.totals.reasoning_output_tokens += row.reasoning_output_tokens || 0;
2541
2542
  if (!sa.models.has(mdl))
2542
- sa.models.set(mdl, { model: mdl, model_id: mdl, totals: { total_tokens: 0, billable_total_tokens: 0, input_tokens: 0, output_tokens: 0, cached_input_tokens: 0, cache_creation_input_tokens: 0, reasoning_output_tokens: 0, total_cost_usd: "0" } });
2543
+ sa.models.set(mdl, { model: mdl, model_id: mdl, pricing: getModelPricingInfo(mdl, { source: src }), totals: { total_tokens: 0, billable_total_tokens: 0, input_tokens: 0, output_tokens: 0, cached_input_tokens: 0, cache_creation_input_tokens: 0, reasoning_output_tokens: 0, total_cost_usd: "0" } });
2543
2544
  const ma = sa.models.get(mdl);
2545
+ const reportedCost = ["grok", "cline"].includes(src) && Number(row.total_cost_usd) > 0;
2546
+ const costSource = reportedCost ? "provider_reported" : "model_pricing";
2547
+ ma.cost_source = ma.cost_source && ma.cost_source !== costSource ? "mixed" : costSource;
2544
2548
  ma.totals.total_tokens += row.total_tokens || 0;
2545
2549
  ma.totals.billable_total_tokens += row.billable_total_tokens ?? row.total_tokens ?? 0;
2546
2550
  ma.totals.input_tokens += row.input_tokens || 0;
@@ -131,6 +131,50 @@ function getModelPricing(model, opts = {}) {
131
131
  return ZERO_PRICING;
132
132
  }
133
133
 
134
+ // Callers hit this once per queue row, and a miss walks the whole LiteLLM map,
135
+ // so results are memoized per pricing revision. Copies go out so a caller that
136
+ // mutates its object cannot poison the cache.
137
+ const pricingInfoCache = { revision: -1, entries: new Map() };
138
+
139
+ // Preserve matcher provenance: a known zero rate is free, a missing model is
140
+ // unpriced. Both return zero dollars, but they mean different things in the UI.
141
+ function getModelPricingInfo(model, opts = {}) {
142
+ const source = typeof opts === "string" ? opts : opts.source;
143
+ const sourceKey = typeof source === "string" ? source.toLowerCase() : null;
144
+ if (pricingInfoCache.revision !== state.revision) {
145
+ pricingInfoCache.revision = state.revision;
146
+ pricingInfoCache.entries.clear();
147
+ }
148
+ const cacheKey = `${sourceKey || ""}\0${model || ""}`;
149
+ let info = pricingInfoCache.entries.get(cacheKey);
150
+ if (!info) {
151
+ info = computeModelPricingInfo(model, opts, sourceKey);
152
+ pricingInfoCache.entries.set(cacheKey, info);
153
+ }
154
+ return { ...info };
155
+ }
156
+
157
+ function computeModelPricingInfo(model, opts, sourceKey) {
158
+ if (LOCAL_INFERENCE_SOURCES.has(sourceKey) || PI_SUBSCRIPTION_SOURCES.has(sourceKey)) {
159
+ return { status: "free", source: LOCAL_INFERENCE_SOURCES.has(sourceKey) ? "local_inference" : "subscription", ...ZERO_PRICING };
160
+ }
161
+ const result = lookupPricing(model, {
162
+ curated: curatedOverrides,
163
+ litellm: state.litellmPerMillionMap,
164
+ source: sourceKey,
165
+ });
166
+ const rates = getModelPricing(model, opts);
167
+ return {
168
+ status: !result.hit ? "unpriced" : [rates.input, rates.output, rates.cache_read, rates.cache_write]
169
+ .some((value) => Number(value) > 0) ? "priced" : "free",
170
+ source: result.hit ? result.source : null,
171
+ input: Number(rates.input) || 0,
172
+ output: Number(rates.output) || 0,
173
+ cache_read: Number(rates.cache_read) || 0,
174
+ cache_write: Number(rates.cache_write) || 0,
175
+ };
176
+ }
177
+
134
178
  function isDeepSeekTimePricedModel(model) {
135
179
  const lower = String(model || "").toLowerCase();
136
180
  return DEEPSEEK_TIME_PRICED_MODELS.some((name) => lower.includes(name));
@@ -322,6 +366,7 @@ module.exports = {
322
366
  ensurePricingLoaded,
323
367
  getPricingRevision,
324
368
  getModelPricing,
369
+ getModelPricingInfo,
325
370
  getRowPricing,
326
371
  computeRowCost,
327
372
  resetPricingForTests,