tokentracker-cli 0.18.1 → 0.20.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 (43) hide show
  1. package/README.md +5 -4
  2. package/README.zh-CN.md +4 -3
  3. package/dashboard/dist/assets/{Card-8ZPdKuRR.js → Card-jA08WeEw.js} +1 -1
  4. package/dashboard/dist/assets/{DashboardPage-OVP6u_7i.js → DashboardPage-chDVOYmG.js} +1 -1
  5. package/dashboard/dist/assets/{FadeIn-BxnaPv7O.js → FadeIn-DqSYXuUL.js} +1 -1
  6. package/dashboard/dist/assets/{HeaderGithubStar-8z6DrTLD.js → HeaderGithubStar-C11rWv0B.js} +1 -1
  7. package/dashboard/dist/assets/{IpCheckPage-yagKgpi7.js → IpCheckPage-CkEZ9yLK.js} +1 -1
  8. package/dashboard/dist/assets/{LandingPage-Ca72J5F0.js → LandingPage-BgckTHRQ.js} +1 -1
  9. package/dashboard/dist/assets/{LeaderboardPage-CSmW4lBz.js → LeaderboardPage-BCNW7UWp.js} +1 -1
  10. package/dashboard/dist/assets/{LeaderboardProfilePage-BOihURRE.js → LeaderboardProfilePage-BLATxMt-.js} +1 -1
  11. package/dashboard/dist/assets/{LimitsPage-Bq4zB2w9.js → LimitsPage-arF--WgR.js} +1 -1
  12. package/dashboard/dist/assets/{LoginPage-CoB1ZkE6.js → LoginPage-DpoFP0va.js} +1 -1
  13. package/dashboard/dist/assets/{PopoverPopup-D7d5-v70.js → PopoverPopup-kdgc2H6C.js} +1 -1
  14. package/dashboard/dist/assets/{ProviderIcon-DzvUcjPu.js → ProviderIcon-DV5r9qqP.js} +1 -1
  15. package/dashboard/dist/assets/{SettingsPage-BeyW1iTj.js → SettingsPage-Bb22ORmU.js} +1 -1
  16. package/dashboard/dist/assets/{SkillsPage-B6auz1NO.js → SkillsPage-xhtBqVKC.js} +1 -1
  17. package/dashboard/dist/assets/{WidgetsPage-C9t8qw0F.js → WidgetsPage-CUoSVDET.js} +1 -1
  18. package/dashboard/dist/assets/{chevron-down-C8RgL-uJ.js → chevron-down-DYb2EChD.js} +1 -1
  19. package/dashboard/dist/assets/{download-C90EEqc8.js → download-C-_8o6dh.js} +1 -1
  20. package/dashboard/dist/assets/{leaderboard-columns-BgqTAms5.js → leaderboard-columns-BgzBlYo7.js} +1 -1
  21. package/dashboard/dist/assets/{main-DJcfmlDf.js → main-11hApDak.js} +6 -4
  22. package/dashboard/dist/assets/{use-limits-display-prefs-BUBBOUIF.js → use-limits-display-prefs-BeGKWUuk.js} +1 -1
  23. package/dashboard/dist/assets/{use-native-settings-CFUEzyoi.js → use-native-settings-nTTHktn0.js} +1 -1
  24. package/dashboard/dist/assets/{use-reduced-motion-NZDZrVKK.js → use-reduced-motion-DU8Gm6j1.js} +1 -1
  25. package/dashboard/dist/assets/{use-usage-limits-CoOOhZrW.js → use-usage-limits-DTPmEB8Y.js} +1 -1
  26. package/dashboard/dist/brand-logos/codebuddy.svg +1 -0
  27. package/dashboard/dist/brand-logos/every-code.svg +1 -0
  28. package/dashboard/dist/brand-logos/grok.svg +1 -0
  29. package/dashboard/dist/brand-logos/hermes.svg +1 -11
  30. package/dashboard/dist/brand-logos/kilo-code.svg +1 -0
  31. package/dashboard/dist/brand-logos/oh-my-pi.svg +1 -0
  32. package/dashboard/dist/index.html +1 -1
  33. package/dashboard/dist/share.html +1 -1
  34. package/package.json +1 -1
  35. package/src/commands/init.js +9 -5
  36. package/src/commands/serve.js +0 -12
  37. package/src/commands/sync.js +370 -7
  38. package/src/lib/grok-hook.js +86 -7
  39. package/src/lib/pricing/curated-overrides.json +1 -1
  40. package/src/lib/pricing/seed-snapshot.json +1 -1
  41. package/src/lib/rollout.js +438 -148
  42. package/src/lib/subscriptions.js +92 -40
  43. package/src/lib/usage-limits.js +1 -1
@@ -10,6 +10,10 @@ const { probeOpenclawSessionPluginState } = require("./openclaw-session-plugin")
10
10
  const OPENAI_AUTH_CLAIM = "https://api.openai.com/auth";
11
11
  const MACOS_SECURITY_BIN = "/usr/bin/security";
12
12
  const CLAUDE_CODE_KEYCHAIN_SERVICES = ["Claude Code-credentials"];
13
+ // On Linux, Claude Code persists the same OAuth payload as a plain JSON file
14
+ // (~/.claude/.credentials.json) instead of the macOS Keychain. The payload
15
+ // shape is identical: { claudeAiOauth: { accessToken, subscriptionType, ... } }
16
+ const CLAUDE_CODE_CREDENTIALS_FILE = ".credentials.json";
13
17
 
14
18
  function normalizeString(value) {
15
19
  if (typeof value !== "string") return null;
@@ -193,25 +197,56 @@ function readMacosKeychainPassword({ service, securityRunner, timeoutMs } = {})
193
197
  return trimmed.length > 0 ? trimmed : null;
194
198
  }
195
199
 
196
- function detectClaudeCodeCredentialsPresence({ platform, securityRunner } = {}) {
197
- if (platform !== "darwin") return null;
198
-
199
- for (const service of CLAUDE_CODE_KEYCHAIN_SERVICES) {
200
- const present = probeMacosKeychainGenericPassword({
201
- service,
202
- securityRunner,
203
- });
204
- if (!present) continue;
200
+ function readClaudeCodeCredentialsLinuxFile({ home, fsReader } = {}) {
201
+ const homeDir = typeof home === "string" && home ? home : os.homedir();
202
+ const credPath = path.join(homeDir, ".claude", CLAUDE_CODE_CREDENTIALS_FILE);
203
+ const reader = typeof fsReader === "function" ? fsReader : fs.readFileSync;
204
+ try {
205
+ return reader(credPath, "utf8");
206
+ } catch (_e) {
207
+ return null;
208
+ }
209
+ }
205
210
 
206
- // Existence-only probe: do not read secrets or infer paid tier.
207
- return {
208
- tool: "claude",
209
- provider: "anthropic",
210
- product: "credentials",
211
- planType: "present",
212
- };
211
+ function detectClaudeCodeCredentialsPresence({ platform, securityRunner, home, fsReader } = {}) {
212
+ if (platform === "darwin") {
213
+ for (const service of CLAUDE_CODE_KEYCHAIN_SERVICES) {
214
+ const present = probeMacosKeychainGenericPassword({
215
+ service,
216
+ securityRunner,
217
+ });
218
+ if (!present) continue;
219
+
220
+ // Existence-only probe: do not read secrets or infer paid tier.
221
+ return {
222
+ tool: "claude",
223
+ provider: "anthropic",
224
+ product: "credentials",
225
+ planType: "present",
226
+ };
227
+ }
228
+ return null;
213
229
  }
214
230
 
231
+ if (platform !== "linux") return null;
232
+
233
+ // Linux: credentials live in ~/.claude/.credentials.json (mode 0600).
234
+ // Existence-only: just check that the file is readable and contains the OAuth key.
235
+ const raw = readClaudeCodeCredentialsLinuxFile({ home, fsReader });
236
+ if (!raw) return null;
237
+ try {
238
+ const payload = JSON.parse(raw);
239
+ if (payload?.claudeAiOauth && typeof payload.claudeAiOauth === "object") {
240
+ return {
241
+ tool: "claude",
242
+ provider: "anthropic",
243
+ product: "credentials",
244
+ planType: "present",
245
+ };
246
+ }
247
+ } catch (_e) {
248
+ // fall through
249
+ }
215
250
  return null;
216
251
  }
217
252
 
@@ -228,16 +263,21 @@ function extractClaudeKeychainSubscription(payload) {
228
263
  return { subscriptionType, rateLimitTier };
229
264
  }
230
265
 
231
- function detectClaudeCodeSubscriptionDetails({ platform, securityRunner } = {}) {
232
- if (platform !== "darwin") return null;
233
-
234
- for (const service of CLAUDE_CODE_KEYCHAIN_SERVICES) {
235
- const raw = readMacosKeychainPassword({
236
- service,
237
- securityRunner,
238
- });
239
- if (!raw) continue;
266
+ function detectClaudeCodeSubscriptionDetails({ platform, securityRunner, home, fsReader } = {}) {
267
+ const rawPayloads = [];
268
+ if (platform === "darwin") {
269
+ for (const service of CLAUDE_CODE_KEYCHAIN_SERVICES) {
270
+ const raw = readMacosKeychainPassword({ service, securityRunner });
271
+ if (raw) rawPayloads.push(raw);
272
+ }
273
+ } else if (platform === "linux") {
274
+ const raw = readClaudeCodeCredentialsLinuxFile({ home, fsReader });
275
+ if (raw) rawPayloads.push(raw);
276
+ } else {
277
+ return null;
278
+ }
240
279
 
280
+ for (const raw of rawPayloads) {
241
281
  let payload;
242
282
  try {
243
283
  payload = JSON.parse(raw);
@@ -277,14 +317,14 @@ async function collectLocalSubscriptions({
277
317
  if (opencode) out.push(opencode);
278
318
 
279
319
  if (probeKeychainDetails) {
280
- const claude = detectClaudeCodeSubscriptionDetails({ platform, securityRunner });
320
+ const claude = detectClaudeCodeSubscriptionDetails({ platform, securityRunner, home });
281
321
  if (claude) out.push(claude);
282
322
  else if (probeKeychain) {
283
- const present = detectClaudeCodeCredentialsPresence({ platform, securityRunner });
323
+ const present = detectClaudeCodeCredentialsPresence({ platform, securityRunner, home });
284
324
  if (present) out.push(present);
285
325
  }
286
326
  } else if (probeKeychain) {
287
- const claude = detectClaudeCodeCredentialsPresence({ platform, securityRunner });
327
+ const claude = detectClaudeCodeCredentialsPresence({ platform, securityRunner, home });
288
328
  if (claude) out.push(claude);
289
329
  }
290
330
 
@@ -314,20 +354,32 @@ async function detectOpenclawSessionIntegration({ home, env }) {
314
354
  };
315
355
  }
316
356
 
317
- function readClaudeCodeAccessToken({ platform, securityRunner } = {}) {
318
- if (platform !== "darwin") return null;
319
-
320
- for (const service of CLAUDE_CODE_KEYCHAIN_SERVICES) {
321
- try {
322
- const raw = readMacosKeychainPassword({ service, securityRunner });
323
- if (!raw) continue;
324
- const payload = JSON.parse(raw);
325
- return normalizeString(payload?.claudeAiOauth?.accessToken);
326
- } catch (_e) {
327
- continue;
357
+ function readClaudeCodeAccessToken({ platform, securityRunner, home, fsReader } = {}) {
358
+ if (platform === "darwin") {
359
+ for (const service of CLAUDE_CODE_KEYCHAIN_SERVICES) {
360
+ try {
361
+ const raw = readMacosKeychainPassword({ service, securityRunner });
362
+ if (!raw) continue;
363
+ const payload = JSON.parse(raw);
364
+ return normalizeString(payload?.claudeAiOauth?.accessToken);
365
+ } catch (_e) {
366
+ continue;
367
+ }
328
368
  }
369
+ return null;
370
+ }
371
+
372
+ if (platform !== "linux") return null;
373
+
374
+ // Linux: Claude Code stores the OAuth payload as a JSON file with mode 0600.
375
+ const raw = readClaudeCodeCredentialsLinuxFile({ home, fsReader });
376
+ if (!raw) return null;
377
+ try {
378
+ const payload = JSON.parse(raw);
379
+ return normalizeString(payload?.claudeAiOauth?.accessToken);
380
+ } catch (_e) {
381
+ return null;
329
382
  }
330
- return null;
331
383
  }
332
384
 
333
385
  async function readCodexAccessToken({ home, env } = {}) {
@@ -1531,7 +1531,7 @@ async function getUsageLimits({
1531
1531
  }
1532
1532
 
1533
1533
  const [claudeToken, codexAuth] = await Promise.all([
1534
- Promise.resolve().then(() => readClaudeCodeAccessToken({ platform, securityRunner })),
1534
+ Promise.resolve().then(() => readClaudeCodeAccessToken({ platform, securityRunner, home })),
1535
1535
  readCodexAuthBundle({ home, env }),
1536
1536
  ]);
1537
1537