tokentracker-cli 0.17.2 → 0.18.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 (38) hide show
  1. package/README.md +2 -1
  2. package/README.zh-CN.md +4 -3
  3. package/dashboard/dist/assets/{Card-CgMq55sY.js → Card-8ZPdKuRR.js} +1 -1
  4. package/dashboard/dist/assets/DashboardPage-OVP6u_7i.js +1 -0
  5. package/dashboard/dist/assets/{FadeIn-DLhKBgqY.js → FadeIn-BxnaPv7O.js} +1 -1
  6. package/dashboard/dist/assets/{HeaderGithubStar-C9HsEaL7.js → HeaderGithubStar-8z6DrTLD.js} +1 -1
  7. package/dashboard/dist/assets/{IpCheckPage-XRIx0rfG.js → IpCheckPage-yagKgpi7.js} +1 -1
  8. package/dashboard/dist/assets/{LandingPage-DXlrkwEx.js → LandingPage-Ca72J5F0.js} +1 -1
  9. package/dashboard/dist/assets/{LeaderboardPage-CIqzV9cT.js → LeaderboardPage-CSmW4lBz.js} +1 -1
  10. package/dashboard/dist/assets/{LeaderboardProfilePage-IUgRwHTr.js → LeaderboardProfilePage-BOihURRE.js} +1 -1
  11. package/dashboard/dist/assets/{LimitsPage-OrcWqg7m.js → LimitsPage-Bq4zB2w9.js} +1 -1
  12. package/dashboard/dist/assets/{LoginPage-BuSNuEnm.js → LoginPage-CoB1ZkE6.js} +1 -1
  13. package/dashboard/dist/assets/{PopoverPopup-D6I_CIFY.js → PopoverPopup-D7d5-v70.js} +1 -1
  14. package/dashboard/dist/assets/{ProviderIcon-Bp-wDvoj.js → ProviderIcon-DzvUcjPu.js} +1 -1
  15. package/dashboard/dist/assets/{SettingsPage-DiyJd4az.js → SettingsPage-BeyW1iTj.js} +1 -1
  16. package/dashboard/dist/assets/{SkillsPage-d8zU8wsf.js → SkillsPage-B6auz1NO.js} +1 -1
  17. package/dashboard/dist/assets/{WidgetsPage-DJsxeeBY.js → WidgetsPage-C9t8qw0F.js} +1 -1
  18. package/dashboard/dist/assets/{chevron-down-CFtbeaPZ.js → chevron-down-C8RgL-uJ.js} +1 -1
  19. package/dashboard/dist/assets/{download-HjhL2_eA.js → download-C90EEqc8.js} +1 -1
  20. package/dashboard/dist/assets/{leaderboard-columns-CrSoLL2s.js → leaderboard-columns-BgqTAms5.js} +1 -1
  21. package/dashboard/dist/assets/{main-CgJNueY2.js → main-DJcfmlDf.js} +2 -2
  22. package/dashboard/dist/assets/{use-limits-display-prefs-BW2QZr21.js → use-limits-display-prefs-BUBBOUIF.js} +1 -1
  23. package/dashboard/dist/assets/{use-native-settings-Bd1d-CVd.js → use-native-settings-CFUEzyoi.js} +1 -1
  24. package/dashboard/dist/assets/{use-reduced-motion-B0JB6MTu.js → use-reduced-motion-NZDZrVKK.js} +1 -1
  25. package/dashboard/dist/assets/{use-usage-limits-OtRz0apS.js → use-usage-limits-CoOOhZrW.js} +1 -1
  26. package/dashboard/dist/index.html +1 -1
  27. package/dashboard/dist/share.html +1 -1
  28. package/package.json +1 -1
  29. package/src/commands/serve.js +5 -1
  30. package/src/commands/sync.js +30 -0
  31. package/src/lib/local-api.js +84 -0
  32. package/src/lib/pricing/curated-overrides.json +2 -1
  33. package/src/lib/pricing/index.js +12 -4
  34. package/src/lib/pricing/matcher.js +45 -10
  35. package/src/lib/pricing/seed-snapshot.json +1 -1
  36. package/src/lib/rollout.js +374 -0
  37. package/src/lib/usage-limits.js +2 -1
  38. package/dashboard/dist/assets/DashboardPage-DfBWYjnA.js +0 -1
@@ -6131,6 +6131,372 @@ async function parseGrokBuildIncremental({
6131
6131
  };
6132
6132
  }
6133
6133
 
6134
+ function resolveAntigravityBrainDirs(geminiHome) {
6135
+ if (!geminiHome || typeof geminiHome !== "string") return [];
6136
+ return [
6137
+ path.join(geminiHome, "antigravity", "brain"),
6138
+ path.join(geminiHome, "antigravity-ide", "brain"),
6139
+ path.join(geminiHome, "antigravity-cli", "brain"),
6140
+ ];
6141
+ }
6142
+
6143
+ async function listAntigravitySessionFiles(brainDir) {
6144
+ const out = [];
6145
+ if (!brainDir || typeof brainDir !== "string") return out;
6146
+ const entries = await safeReadDir(brainDir).catch(() => []);
6147
+ for (const entry of entries) {
6148
+ if (!entry.isDirectory()) continue;
6149
+ const logsDir = path.join(brainDir, entry.name, ".system_generated", "logs");
6150
+ const transcriptPath = path.join(logsDir, "transcript.jsonl");
6151
+ const st = await fs.stat(transcriptPath).catch(() => null);
6152
+ if (st && st.isFile()) {
6153
+ out.push(transcriptPath);
6154
+ }
6155
+ }
6156
+ out.sort((a, b) => a.localeCompare(b));
6157
+ return out;
6158
+ }
6159
+
6160
+ async function listAntigravityTranscripts(geminiHome) {
6161
+ const dirs = resolveAntigravityBrainDirs(geminiHome);
6162
+ const lists = await Promise.all(dirs.map((dir) => listAntigravitySessionFiles(dir)));
6163
+ return lists.flat();
6164
+ }
6165
+
6166
+ async function parseAntigravityIncremental({
6167
+ sessionFiles,
6168
+ cursors,
6169
+ queuePath,
6170
+ projectQueuePath,
6171
+ onProgress,
6172
+ source,
6173
+ publicRepoResolver,
6174
+ }) {
6175
+ await ensureDir(path.dirname(queuePath));
6176
+ let filesProcessed = 0;
6177
+ let eventsAggregated = 0;
6178
+
6179
+ const cb = typeof onProgress === "function" ? onProgress : null;
6180
+ const files = Array.isArray(sessionFiles) ? sessionFiles : [];
6181
+ const totalFiles = files.length;
6182
+ const hourlyState = normalizeHourlyState(cursors?.hourly);
6183
+ const projectEnabled = typeof projectQueuePath === "string" && projectQueuePath.length > 0;
6184
+ const projectState = projectEnabled ? normalizeProjectState(cursors?.projectHourly) : null;
6185
+ const projectTouchedBuckets = projectEnabled ? new Set() : null;
6186
+ const projectMetaCache = projectEnabled ? new Map() : null;
6187
+ const publicRepoCache = projectEnabled ? new Map() : null;
6188
+ const touchedBuckets = new Set();
6189
+ const defaultSource = normalizeSourceInput(source) || "antigravity";
6190
+
6191
+ if (!cursors.files || typeof cursors.files !== "object") {
6192
+ cursors.files = {};
6193
+ }
6194
+
6195
+ for (let idx = 0; idx < files.length; idx++) {
6196
+ const entry = files[idx];
6197
+ const filePath = typeof entry === "string" ? entry : entry?.path;
6198
+ if (!filePath) continue;
6199
+ const fileSource =
6200
+ typeof entry === "string"
6201
+ ? defaultSource
6202
+ : normalizeSourceInput(entry?.source) || defaultSource;
6203
+ const st = await fs.stat(filePath).catch(() => null);
6204
+ if (!st || !st.isFile()) continue;
6205
+
6206
+ const key = filePath;
6207
+ const prev = cursors.files[key] || null;
6208
+ const inode = st.ino || 0;
6209
+ const size = Number.isFinite(st.size) ? st.size : 0;
6210
+ const mtimeMs = Number.isFinite(st.mtimeMs) ? st.mtimeMs : 0;
6211
+
6212
+ const unchanged =
6213
+ prev && prev.inode === inode && prev.size === size && prev.mtimeMs === mtimeMs;
6214
+ if (unchanged) {
6215
+ filesProcessed += 1;
6216
+ if (cb) {
6217
+ cb({
6218
+ index: idx + 1,
6219
+ total: totalFiles,
6220
+ filePath,
6221
+ filesProcessed,
6222
+ eventsAggregated,
6223
+ bucketsQueued: touchedBuckets.size,
6224
+ });
6225
+ }
6226
+ continue;
6227
+ }
6228
+
6229
+ const sameFile = prev && prev.inode === inode;
6230
+ const lastLine = sameFile ? Number(prev.lastLine || 0) : 0;
6231
+ const initialContextTokens = sameFile ? Number(prev.contextTokens || 0) : 0;
6232
+ const initialModel = sameFile && typeof prev.currentModel === "string" ? prev.currentModel : null;
6233
+
6234
+ const projectContext = projectEnabled
6235
+ ? await resolveProjectContextForFile({
6236
+ filePath,
6237
+ projectMetaCache,
6238
+ publicRepoCache,
6239
+ publicRepoResolver,
6240
+ projectState,
6241
+ })
6242
+ : null;
6243
+ const projectRef = projectContext?.projectRef || null;
6244
+ const projectKey = projectContext?.projectKey || null;
6245
+
6246
+ const result = await parseAntigravityFile({
6247
+ filePath,
6248
+ lastLine,
6249
+ initialContextTokens,
6250
+ initialModel,
6251
+ hourlyState,
6252
+ touchedBuckets,
6253
+ source: fileSource,
6254
+ projectState,
6255
+ projectTouchedBuckets,
6256
+ projectRef,
6257
+ projectKey,
6258
+ });
6259
+
6260
+ cursors.files[key] = {
6261
+ inode,
6262
+ size,
6263
+ mtimeMs,
6264
+ lastLine: result.lastLine,
6265
+ contextTokens: result.contextTokens,
6266
+ currentModel: result.currentModel,
6267
+ updatedAt: new Date().toISOString(),
6268
+ };
6269
+
6270
+ filesProcessed += 1;
6271
+ eventsAggregated += result.eventsAggregated;
6272
+
6273
+ if (cb) {
6274
+ cb({
6275
+ index: idx + 1,
6276
+ total: totalFiles,
6277
+ filePath,
6278
+ filesProcessed,
6279
+ eventsAggregated,
6280
+ bucketsQueued: touchedBuckets.size,
6281
+ });
6282
+ }
6283
+ }
6284
+
6285
+ const bucketsQueued = await enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets });
6286
+ const projectBucketsQueued = projectEnabled
6287
+ ? await enqueueTouchedProjectBuckets({ projectQueuePath, projectState, projectTouchedBuckets })
6288
+ : 0;
6289
+ hourlyState.updatedAt = new Date().toISOString();
6290
+ cursors.hourly = hourlyState;
6291
+ if (projectState) {
6292
+ projectState.updatedAt = new Date().toISOString();
6293
+ cursors.projectHourly = projectState;
6294
+ }
6295
+
6296
+ return { filesProcessed, eventsAggregated, bucketsQueued, projectBucketsQueued };
6297
+ }
6298
+
6299
+ async function parseAntigravityFile({
6300
+ filePath,
6301
+ lastLine,
6302
+ initialContextTokens,
6303
+ initialModel,
6304
+ hourlyState,
6305
+ touchedBuckets,
6306
+ source,
6307
+ projectState,
6308
+ projectTouchedBuckets,
6309
+ projectRef,
6310
+ projectKey,
6311
+ }) {
6312
+ const raw = await fs.readFile(filePath, "utf8").catch(() => "");
6313
+ if (!raw.trim()) {
6314
+ return { lastLine: 0, eventsAggregated: 0, contextTokens: 0, currentModel: null };
6315
+ }
6316
+
6317
+ const lines = raw
6318
+ .split(/\r?\n/)
6319
+ .map((line) => line.trim())
6320
+ .filter(Boolean);
6321
+ let eventsAggregated = 0;
6322
+ // Resume cached context-token total + model so historical lines (i < lastLine)
6323
+ // don't need to be re-tokenized on every sync. Falls back to a full re-walk
6324
+ // when the cached state is missing (legacy cursor) or the file rotated.
6325
+ const canResume =
6326
+ Number.isFinite(lastLine) && lastLine > 0 && lastLine <= lines.length;
6327
+ const cachedTokens = Number.isFinite(initialContextTokens) ? initialContextTokens : 0;
6328
+ const cachedModel = typeof initialModel === "string" ? initialModel : null;
6329
+ const resumed = canResume && (cachedTokens > 0 || cachedModel !== null);
6330
+ const scanStart = resumed ? lastLine : 0;
6331
+ let currentModel = resumed ? cachedModel : null;
6332
+ let contextTokens = resumed ? cachedTokens : 0;
6333
+ let lastCompletedLine = Math.min(Number.isFinite(lastLine) ? lastLine : 0, lines.length);
6334
+
6335
+ for (let i = scanStart; i < lines.length; i++) {
6336
+ const line = lines[i];
6337
+
6338
+ let parsed;
6339
+ try {
6340
+ parsed = JSON.parse(line);
6341
+ } catch (_e) {
6342
+ if (i >= lastLine) break;
6343
+ continue;
6344
+ }
6345
+
6346
+ const isNewEvent = i >= lastLine;
6347
+
6348
+ if (parsed.type === "USER_INPUT" || parsed.type === "USER_SETTINGS_CHANGE") {
6349
+ const content = typeof parsed.content === "string" ? parsed.content : "";
6350
+ const model = parseAntigravityModelSelection(content);
6351
+ if (model) currentModel = model;
6352
+ }
6353
+
6354
+ const eventContextTokens = antigravityContextTokens(parsed);
6355
+
6356
+ if (!isNewEvent) {
6357
+ contextTokens += eventContextTokens;
6358
+ lastCompletedLine = i + 1;
6359
+ continue;
6360
+ }
6361
+
6362
+ const timestamp = parsed.created_at;
6363
+ if (!timestamp) {
6364
+ contextTokens += eventContextTokens;
6365
+ lastCompletedLine = i + 1;
6366
+ continue;
6367
+ }
6368
+
6369
+ const bucketStart = toUtcHalfHourStart(timestamp);
6370
+ if (!bucketStart) {
6371
+ contextTokens += eventContextTokens;
6372
+ lastCompletedLine = i + 1;
6373
+ continue;
6374
+ }
6375
+
6376
+ let model = currentModel || "antigravity-unknown";
6377
+ let delta = initTotals();
6378
+
6379
+ if (parsed.type === "PLANNER_RESPONSE") {
6380
+ const content = typeof parsed.content === "string" ? parsed.content : "";
6381
+ const thinking = typeof parsed.thinking === "string" ? parsed.thinking : "";
6382
+
6383
+ delta.input_tokens = contextTokens;
6384
+ delta.output_tokens =
6385
+ antigravityValueTokens(content) + antigravityValueTokens(parsed.tool_calls);
6386
+ delta.reasoning_output_tokens = antigravityValueTokens(thinking);
6387
+ delta.total_tokens =
6388
+ delta.input_tokens + delta.output_tokens + delta.reasoning_output_tokens;
6389
+ delta.billable_total_tokens = delta.total_tokens;
6390
+ delta.conversation_count = 1;
6391
+ }
6392
+
6393
+ if (delta.total_tokens === 0) {
6394
+ contextTokens += eventContextTokens;
6395
+ lastCompletedLine = i + 1;
6396
+ continue;
6397
+ }
6398
+
6399
+ const bucket = getHourlyBucket(hourlyState, source, model, bucketStart);
6400
+ addTotals(bucket.totals, delta);
6401
+ touchedBuckets.add(bucketKey(source, model, bucketStart));
6402
+
6403
+ if (projectKey && projectState && projectTouchedBuckets) {
6404
+ const projectBucket = getProjectBucket(
6405
+ projectState,
6406
+ projectKey,
6407
+ source,
6408
+ bucketStart,
6409
+ projectRef,
6410
+ );
6411
+ addTotals(projectBucket.totals, delta);
6412
+ projectTouchedBuckets.add(projectBucketKey(projectKey, source, bucketStart));
6413
+ }
6414
+ eventsAggregated += 1;
6415
+ contextTokens += eventContextTokens;
6416
+ lastCompletedLine = i + 1;
6417
+ }
6418
+
6419
+ return {
6420
+ lastLine: lastCompletedLine,
6421
+ eventsAggregated,
6422
+ contextTokens,
6423
+ currentModel,
6424
+ };
6425
+ }
6426
+
6427
+ function parseAntigravityModelSelection(content) {
6428
+ if (typeof content !== "string" || !content) return null;
6429
+ const match = content.match(
6430
+ /changed setting `Model Selection` from .*? to ([^`\n]+?)(?:\s*\([^)]*\))?\.(?:\s+|$)/i,
6431
+ );
6432
+ if (!match) return null;
6433
+ return normalizeAntigravityTranscriptModel(match[1]);
6434
+ }
6435
+
6436
+ function normalizeAntigravityTranscriptModel(modelName) {
6437
+ if (!modelName || typeof modelName !== "string") return null;
6438
+ let slug = modelName
6439
+ .trim()
6440
+ .replace(/\([^)]*\)/g, " ")
6441
+ .replace(/\b(thinking|xhigh|high|medium|low|fast)\b/gi, " ")
6442
+ .toLowerCase()
6443
+ .replace(/[^a-z0-9.]+/g, "-")
6444
+ .replace(/^-+|-+$/g, "")
6445
+ .replace(/-{2,}/g, "-");
6446
+ if (!slug) return null;
6447
+
6448
+ for (const marker of ["gemini", "claude", "gpt"]) {
6449
+ const idx = slug.indexOf(marker);
6450
+ if (idx >= 0) {
6451
+ slug = slug.slice(idx);
6452
+ break;
6453
+ }
6454
+ }
6455
+ if (/^(gemini|claude|gpt)-/.test(slug)) return slug;
6456
+ return `antigravity-${slug}`;
6457
+ }
6458
+
6459
+ function antigravityContextTokens(event) {
6460
+ if (!event || typeof event !== "object") return 0;
6461
+ let tokens = antigravityValueTokens(event.content);
6462
+ if (event.type === "PLANNER_RESPONSE" && event.tool_calls) {
6463
+ tokens += antigravityValueTokens(event.tool_calls);
6464
+ }
6465
+ return tokens;
6466
+ }
6467
+
6468
+ function antigravityValueTokens(value) {
6469
+ if (typeof value === "string") return estimateAntigravityTokens(value);
6470
+ if (value == null) return 0;
6471
+ try {
6472
+ return estimateAntigravityTokens(JSON.stringify(value));
6473
+ } catch (_e) {
6474
+ return 0;
6475
+ }
6476
+ }
6477
+
6478
+ function estimateAntigravityTokens(text) {
6479
+ if (typeof text !== "string" || text.length === 0) return 0;
6480
+ let cjk = 0;
6481
+ let other = 0;
6482
+ for (const ch of text) {
6483
+ if (isCjkCodePoint(ch.codePointAt(0))) {
6484
+ cjk += 1;
6485
+ } else {
6486
+ other += 1;
6487
+ }
6488
+ }
6489
+ return cjk + Math.ceil(other / 4);
6490
+ }
6491
+
6492
+ function isCjkCodePoint(code) {
6493
+ return (
6494
+ (code >= 0x3400 && code <= 0x4dbf) ||
6495
+ (code >= 0x4e00 && code <= 0x9fff) ||
6496
+ (code >= 0x3040 && code <= 0x30ff)
6497
+ );
6498
+ }
6499
+
6134
6500
  module.exports = {
6135
6501
  listRolloutFiles,
6136
6502
  listClaudeProjectFiles,
@@ -6200,4 +6566,12 @@ module.exports = {
6200
6566
  resolveGrokBuildHome,
6201
6567
  resolveGrokBuildSessions,
6202
6568
  parseGrokBuildIncremental,
6569
+
6570
+ // Antigravity (Google Gemini) - Session logs parser
6571
+ resolveAntigravityBrainDirs,
6572
+ listAntigravitySessionFiles,
6573
+ listAntigravityTranscripts,
6574
+ parseAntigravityIncremental,
6575
+ estimateAntigravityTokens,
6576
+ isCjkCodePoint,
6203
6577
  };
@@ -1129,11 +1129,12 @@ function parseProcessLine(line) {
1129
1129
 
1130
1130
  function isAntigravityCommandLine(command) {
1131
1131
  const lower = String(command || "").toLowerCase();
1132
- return lower.includes("language_server_macos")
1132
+ return lower.includes("language_server")
1133
1133
  && (
1134
1134
  (lower.includes("--app_data_dir") && lower.includes("antigravity"))
1135
1135
  || lower.includes("/antigravity/")
1136
1136
  || lower.includes("\\antigravity\\")
1137
+ || lower.includes("--override_ide_name antigravity")
1137
1138
  );
1138
1139
  }
1139
1140