tokentracker-cli 0.63.0 → 0.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dashboard/dist/assets/{ActivityHeatmap-CkJIcWzw.js → ActivityHeatmap-DjMtMPit.js} +1 -1
  2. package/dashboard/dist/assets/{Card-yLBo3TPK.js → Card-CD9YpCAv.js} +1 -1
  3. package/dashboard/dist/assets/{DashboardPage-kR7KrvS7.js → DashboardPage-Djhg-yJL.js} +1 -1
  4. package/dashboard/dist/assets/{DevicePage-jh6HO_Sr.js → DevicePage-DA74Sryo.js} +1 -1
  5. package/dashboard/dist/assets/{DialogTitle-D9Ik_gvc.js → DialogTitle-DDp9MHUJ.js} +1 -1
  6. package/dashboard/dist/assets/{FadeIn-C4EDHmX4.js → FadeIn-CBspmJ4G.js} +1 -1
  7. package/dashboard/dist/assets/{HeaderGithubStar-CJ1KBbIb.js → HeaderGithubStar-BDgq5Gx4.js} +1 -1
  8. package/dashboard/dist/assets/{IpCheckPage-vMPzCKhT.js → IpCheckPage-L1GKKx8L.js} +1 -1
  9. package/dashboard/dist/assets/{LandingPage-CPXGM_d3.js → LandingPage-BxI6zHR6.js} +1 -1
  10. package/dashboard/dist/assets/{LeaderboardAvatar-DF8a3ppM.js → LeaderboardAvatar-DHKVlkOF.js} +1 -1
  11. package/dashboard/dist/assets/{LeaderboardPage-D4SbAwT8.js → LeaderboardPage-C8JB9MuB.js} +3 -3
  12. package/dashboard/dist/assets/{LeaderboardProfileModal-BG9JVFBq.js → LeaderboardProfileModal-CpciUHf2.js} +1 -1
  13. package/dashboard/dist/assets/{LeaderboardProfilePage-BMd6sNT9.js → LeaderboardProfilePage-c2Pg5hum.js} +1 -1
  14. package/dashboard/dist/assets/{LimitsPage-DukMAnn1.js → LimitsPage-DkqHHv2V.js} +1 -1
  15. package/dashboard/dist/assets/{LocalOnlyNotice-DrmW8xfr.js → LocalOnlyNotice-CVAJM9pw.js} +1 -1
  16. package/dashboard/dist/assets/{LoginPage-3i9PG3UX.js → LoginPage-L9n2F1t8.js} +1 -1
  17. package/dashboard/dist/assets/{PopoverPopup-BhGTdXgG.js → PopoverPopup-B883Co46.js} +1 -1
  18. package/dashboard/dist/assets/{ResetPasswordPage-ClHd4Qcj.js → ResetPasswordPage-dcb-XQ_8.js} +1 -1
  19. package/dashboard/dist/assets/{Select-DFu3_H6P.js → Select-Dn_eH-Cb.js} +1 -1
  20. package/dashboard/dist/assets/{SelectItemText-fc4n0xEf.js → SelectItemText-BBymhOah.js} +1 -1
  21. package/dashboard/dist/assets/{SettingsPage-DYuS-mL2.js → SettingsPage-DpP5jrlC.js} +1 -1
  22. package/dashboard/dist/assets/{SkillsPage-Bq4GiK2C.js → SkillsPage-BKBEMpDa.js} +1 -1
  23. package/dashboard/dist/assets/{WidgetsPage-DtVlysKw.js → WidgetsPage-ByTWGppP.js} +1 -1
  24. package/dashboard/dist/assets/{WrappedPage-BoTb94i8.js → WrappedPage-BzBYot91.js} +1 -1
  25. package/dashboard/dist/assets/{agent-logos-CWH1ZjEy.js → agent-logos-7x6sZdXn.js} +1 -1
  26. package/dashboard/dist/assets/{arrow-up-right-Dz-ejaM3.js → arrow-up-right-NtrFTgFM.js} +1 -1
  27. package/dashboard/dist/assets/{download-BwyTu0h1.js → download-DBI1zmF1.js} +1 -1
  28. package/dashboard/dist/assets/{info-r1_Ohszh.js → info-WyXjj_mj.js} +1 -1
  29. package/dashboard/dist/assets/{main-B8THj1x9.js → main-sjN9-3PX.js} +2 -2
  30. package/dashboard/dist/assets/{pencil-DWftrgh9.js → pencil-zlgm1deI.js} +1 -1
  31. package/dashboard/dist/assets/{use-limits-display-prefs-DhH4ucKB.js → use-limits-display-prefs-D_3UqeaO.js} +1 -1
  32. package/dashboard/dist/assets/{use-native-settings-CE-ExJHY.js → use-native-settings-BIZTMAe_.js} +1 -1
  33. package/dashboard/dist/assets/{use-usage-limits-BWqgtMqj.js → use-usage-limits-DyrBGYy1.js} +1 -1
  34. package/dashboard/dist/assets/{useCurrency-Cg2lR7Uw.js → useCurrency-BJEllMZE.js} +1 -1
  35. package/dashboard/dist/assets/{useScrollLock-DVYU0_fs.js → useScrollLock-Bim1sAXp.js} +1 -1
  36. package/dashboard/dist/index.html +1 -1
  37. package/dashboard/dist/share.html +1 -1
  38. package/package.json +4 -2
  39. package/src/lib/opencode-go-limits.js +253 -20
  40. package/src/lib/pricing/seed-snapshot.json +1 -1
  41. package/src/lib/rollout.js +44 -6
  42. package/src/lib/skills-manager.js +2 -2
  43. package/src/lib/sqlite-reader.js +59 -0
  44. package/src/lib/usage-limits.js +4 -4
@@ -147,9 +147,26 @@ async function parseRolloutIncremental({
147
147
  const key = filePath;
148
148
  const prev = cursors.files[key] || null;
149
149
  const inode = st.ino || 0;
150
- const startOffset = prev && prev.inode === inode ? prev.offset || 0 : 0;
151
- const lastTotal = prev && prev.inode === inode ? prev.lastTotal || null : null;
152
- const lastModel = prev && prev.inode === inode ? prev.lastModel || null : null;
150
+ const sameInode = prev && prev.inode === inode;
151
+ const prevOffset = sameInode ? prev.offset || 0 : 0;
152
+ const truncated = sameInode && prevOffset > st.size;
153
+ const startOffset = sameInode && !truncated ? prevOffset : 0;
154
+ const lastTotal = sameInode && !truncated ? prev.lastTotal || null : null;
155
+ const lastModel = sameInode && !truncated ? prev.lastModel || null : null;
156
+
157
+ if (!projectEnabled && sameInode && !truncated && startOffset >= st.size) {
158
+ if (cb) {
159
+ cb({
160
+ index: idx + 1,
161
+ total: totalFiles,
162
+ filePath,
163
+ filesProcessed,
164
+ eventsAggregated,
165
+ bucketsQueued: touchedBuckets.size,
166
+ });
167
+ }
168
+ continue;
169
+ }
153
170
 
154
171
  const projectContext = projectEnabled
155
172
  ? await resolveProjectContextForFile({
@@ -165,6 +182,7 @@ async function parseRolloutIncremental({
165
182
 
166
183
  const result = await parseRolloutFile({
167
184
  filePath,
185
+ fileStat: st,
168
186
  startOffset,
169
187
  lastTotal,
170
188
  lastModel,
@@ -267,7 +285,24 @@ async function parseClaudeIncremental({
267
285
  const key = filePath;
268
286
  const prev = cursors.files[key] || null;
269
287
  const inode = st.ino || 0;
270
- const startOffset = prev && prev.inode === inode ? prev.offset || 0 : 0;
288
+ const sameInode = prev && prev.inode === inode;
289
+ const prevOffset = sameInode ? prev.offset || 0 : 0;
290
+ const truncated = sameInode && prevOffset > st.size;
291
+ const startOffset = sameInode && !truncated ? prevOffset : 0;
292
+
293
+ if (!projectEnabled && sameInode && !truncated && startOffset >= st.size) {
294
+ if (cb) {
295
+ cb({
296
+ index: idx + 1,
297
+ total: totalFiles,
298
+ filePath,
299
+ filesProcessed,
300
+ eventsAggregated,
301
+ bucketsQueued: touchedBuckets.size,
302
+ });
303
+ }
304
+ continue;
305
+ }
271
306
 
272
307
  const projectContext = projectEnabled
273
308
  ? await resolveProjectContextForFile({
@@ -283,6 +318,7 @@ async function parseClaudeIncremental({
283
318
 
284
319
  const result = await parseClaudeFile({
285
320
  filePath,
321
+ fileStat: st,
286
322
  startOffset,
287
323
  hourlyState,
288
324
  touchedBuckets,
@@ -770,6 +806,7 @@ function codexSessionIdFromPath(filePath) {
770
806
 
771
807
  async function parseRolloutFile({
772
808
  filePath,
809
+ fileStat,
773
810
  startOffset,
774
811
  lastTotal,
775
812
  lastModel,
@@ -786,7 +823,7 @@ async function parseRolloutFile({
786
823
  seenCodexEvents,
787
824
  sessionId,
788
825
  }) {
789
- const st = await fs.stat(filePath);
826
+ const st = fileStat || (await fs.stat(filePath));
790
827
  const endOffset = st.size;
791
828
  if (startOffset >= endOffset) {
792
829
  return { endOffset, lastTotal, lastModel, eventsAggregated: 0 };
@@ -926,6 +963,7 @@ async function parseRolloutFile({
926
963
 
927
964
  async function parseClaudeFile({
928
965
  filePath,
966
+ fileStat,
929
967
  startOffset,
930
968
  hourlyState,
931
969
  touchedBuckets,
@@ -936,7 +974,7 @@ async function parseClaudeFile({
936
974
  projectKey,
937
975
  seenMessageHashes,
938
976
  }) {
939
- const st = await fs.stat(filePath).catch(() => null);
977
+ const st = fileStat || (await fs.stat(filePath).catch(() => null));
940
978
  if (!st || !st.isFile()) return { endOffset: startOffset, eventsAggregated: 0 };
941
979
 
942
980
  const endOffset = st.size;
@@ -577,8 +577,8 @@ function targetSkillPath(baseDir, directory) {
577
577
  const targetPath = path.resolve(root, safe);
578
578
  if (!pathStrictlyWithin(root, targetPath)) return null;
579
579
  try {
580
- const rootStat = fs.lstatSync(root);
581
- if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) return null;
580
+ const rootStat = fs.statSync(root);
581
+ if (!rootStat.isDirectory()) return null;
582
582
  } catch (e) {
583
583
  if (e?.code !== "ENOENT") return null;
584
584
  }
@@ -1,5 +1,6 @@
1
1
  const cp = require("node:child_process");
2
2
  const fssync = require("node:fs");
3
+ const { promisify } = require("node:util");
3
4
 
4
5
  const warnedSqliteReadFailures = new Set();
5
6
 
@@ -116,6 +117,63 @@ function readSqliteJsonRows(dbPath, sql, options = {}) {
116
117
  return [];
117
118
  }
118
119
 
120
+ async function readSqliteRowsWithCliAsync(dbPath, sql, { execFile, timeout, maxBuffer }) {
121
+ const { stdout } = await execFile("sqlite3", ["-json", dbPath, sql], {
122
+ encoding: "utf8",
123
+ maxBuffer,
124
+ timeout,
125
+ });
126
+ if (!stdout || !stdout.trim()) return [];
127
+ const rows = JSON.parse(stdout);
128
+ return Array.isArray(rows) ? rows : [];
129
+ }
130
+
131
+ // Async twin of readSqliteJsonRows for hot paths that must not block the event
132
+ // loop (e.g. the usage-limits poll — see the "limits 路径 spawnSync 冻结全端点"
133
+ // lesson). The CLI path runs via async execFile; the node:sqlite fallback is
134
+ // synchronous (no async API exists), but it only runs when the sqlite3 CLI is
135
+ // absent — uncommon on macOS/Linux, where the async path keeps the loop free.
136
+ async function readSqliteJsonRowsAsync(dbPath, sql, options = {}) {
137
+ if (!dbPath || !sql) return [];
138
+ try {
139
+ if (!fssync.existsSync(dbPath)) return [];
140
+ } catch (_e) {
141
+ return [];
142
+ }
143
+ const execFile = options.execFile || promisify(cp.execFile);
144
+ const requireFn = options.requireFn || require;
145
+ const env = options.env || process.env;
146
+ const timeout = Number.isFinite(options.timeout) ? options.timeout : 30_000;
147
+ const maxBuffer = Number.isFinite(options.maxBuffer) ? options.maxBuffer : 50 * 1024 * 1024;
148
+ const label = options.label || "local";
149
+
150
+ let cliError = null;
151
+ try {
152
+ return await readSqliteRowsWithCliAsync(dbPath, sql, { execFile, timeout, maxBuffer });
153
+ } catch (err) {
154
+ cliError = err;
155
+ }
156
+
157
+ let nodeSqliteError = null;
158
+ try {
159
+ return readSqliteRowsWithNode(dbPath, sql, { requireFn });
160
+ } catch (err) {
161
+ nodeSqliteError = err;
162
+ }
163
+
164
+ if (isSqliteCliUnavailable(cliError) && isNodeSqliteUnavailable(nodeSqliteError)) {
165
+ warnSqliteUnavailable({
166
+ dbPath,
167
+ label,
168
+ cliError,
169
+ nodeSqliteError,
170
+ env,
171
+ stderr: options.stderr,
172
+ });
173
+ }
174
+ return [];
175
+ }
176
+
119
177
  function readSqliteFirstValue(dbPath, sql, column, options = {}) {
120
178
  const rows = readSqliteJsonRows(dbPath, sql, options);
121
179
  const row = rows[0];
@@ -131,6 +189,7 @@ function resetSqliteReaderWarningsForTests() {
131
189
 
132
190
  module.exports = {
133
191
  readSqliteJsonRows,
192
+ readSqliteJsonRowsAsync,
134
193
  readSqliteFirstValue,
135
194
  resetSqliteReaderWarningsForTests,
136
195
  };
@@ -2477,10 +2477,10 @@ async function fetchUsageLimitsUncached({
2477
2477
  .catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
2478
2478
  withProviderTimeout(fetchZcodeLimits({ home, env, fetchImpl: providerFetch }), "ZCode", providerTimeoutMs)
2479
2479
  .catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
2480
- // OpenCode Go scrapes the workspace dashboard (no public REST API yet,
2481
- // tracked at anomalyco/opencode#16017). Auth is via two env vars, see
2482
- // src/lib/opencode-go-limits.js for the exact endpoint + cookie shape.
2483
- withProviderTimeout(fetchOpencodeGoLimits({ env, fetchImpl: providerFetch }), "OpenCode Go", providerTimeoutMs)
2480
+ // OpenCode Go: local opencode.db cost-vs-dollar-cap estimate by default
2481
+ // (auth-free, zero-config), upgraded to the exact server-side scrape when an
2482
+ // OPENCODE_GO_AUTH_COOKIE is set. See src/lib/opencode-go-limits.js.
2483
+ withProviderTimeout(fetchOpencodeGoLimits({ home, env, fetchImpl: providerFetch }), "OpenCode Go", providerTimeoutMs)
2484
2484
  .catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
2485
2485
  ]);
2486
2486