tokmon 0.20.1 → 0.20.3

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.
@@ -18,7 +18,7 @@ import {
18
18
  systemTimezone,
19
19
  time,
20
20
  tokens
21
- } from "./chunk-5BW4H7WW.js";
21
+ } from "./chunk-ANCKHLJV.js";
22
22
  import {
23
23
  COLOR_PALETTE,
24
24
  DEFAULTS,
@@ -3194,7 +3194,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, wsToken = n
3194
3194
  if (webStartingRef.current) return;
3195
3195
  webStartingRef.current = true;
3196
3196
  try {
3197
- const { startWebServer } = await import("./server-RL2JDFQY.js");
3197
+ const { startWebServer } = await import("./server-R6Z6W4CN.js");
3198
3198
  const ctrl = await startWebServer({ config: cfg, log: false });
3199
3199
  webRef.current = ctrl;
3200
3200
  openUrl(ctrl.url);
@@ -2554,22 +2554,187 @@ var antigravityProvider = {
2554
2554
  };
2555
2555
 
2556
2556
  // src/providers/gemini/billing.ts
2557
- import { access as access8, readFile as readFile6 } from "fs/promises";
2557
+ import { access as access8, readFile as readFile6, readdir as readdir9 } from "fs/promises";
2558
+ import { join as join14 } from "path";
2559
+ import { homedir as homedir12 } from "os";
2560
+
2561
+ // src/providers/gemini/usage.ts
2562
+ import { readdir as readdir8, stat as fsStat5 } from "fs/promises";
2563
+ import { createReadStream as createReadStream5 } from "fs";
2564
+ import { createInterface as createInterface5 } from "readline";
2558
2565
  import { join as join13 } from "path";
2559
2566
  import { homedir as homedir11 } from "os";
2567
+ var PRICING3 = {
2568
+ "gemini-3.1-pro-preview": { in: 2e-6, out: 12e-6, cr: 2e-7 },
2569
+ "gemini-3.1-pro": { in: 2e-6, out: 12e-6, cr: 2e-7 },
2570
+ "gemini-3-pro-preview": { in: 2e-6, out: 12e-6, cr: 2e-7 },
2571
+ "gemini-3-pro": { in: 2e-6, out: 12e-6, cr: 2e-7 },
2572
+ "gemini-3.5-flash": { in: 15e-7, out: 9e-6, cr: 15e-8 },
2573
+ "gemini-3-flash-preview": { in: 15e-7, out: 9e-6, cr: 15e-8 },
2574
+ "gemini-3-flash": { in: 15e-7, out: 9e-6, cr: 15e-8 },
2575
+ "gemini-2.5-flash-lite": { in: 1e-7, out: 4e-7, cr: 1e-8 },
2576
+ "gemini-3.1-flash-lite": { in: 1e-7, out: 4e-7, cr: 1e-8 },
2577
+ "gemini-2.5-flash": { in: 3e-7, out: 25e-7, cr: 3e-8 },
2578
+ "gemini-2.5-pro": { in: 125e-8, out: 1e-5, cr: 125e-9 },
2579
+ "gemini-2.0-flash": { in: 1e-7, out: 4e-7, cr: 25e-9 }
2580
+ };
2581
+ var PRICE_KEYS3 = Object.keys(PRICING3).sort((a, b) => b.length - a.length);
2582
+ var ZERO_PRICE3 = { in: 0, out: 0, cr: 0 };
2583
+ function geminiTmpDir(homeDir) {
2584
+ return join13(homeDir ?? homedir11(), ".gemini", "tmp");
2585
+ }
2586
+ function priceFor3(model) {
2587
+ const m = model.toLowerCase().trim();
2588
+ for (const key of PRICE_KEYS3) {
2589
+ if (!m.startsWith(key)) continue;
2590
+ const rest = m.slice(key.length);
2591
+ if (rest === "" || rest[0] === "-") return PRICING3[key];
2592
+ }
2593
+ return ZERO_PRICE3;
2594
+ }
2595
+ function shortModel2(model) {
2596
+ return model.replace(/(-preview|-customtools)+$/, "");
2597
+ }
2598
+ function isGeminiSessionFile(path) {
2599
+ return /(^|[\\/])chats[\\/]session-.*\.jsonl$/.test(path) || /(^|[\\/])chats[\\/]session-.*\.json$/.test(path);
2600
+ }
2601
+ async function parseFile4(path) {
2602
+ const entries = [];
2603
+ const rl = createInterface5({ input: createReadStream5(path), crlfDelay: Infinity });
2604
+ for await (const line of rl) {
2605
+ try {
2606
+ const obj = JSON.parse(line.charCodeAt(0) === 65279 ? line.slice(1) : line);
2607
+ if (obj.sessionId && obj.kind || obj.$set || obj.$rewindTo) continue;
2608
+ if (obj.type !== "gemini" || !obj.tokens) continue;
2609
+ const ts = Date.parse(obj.timestamp ?? "");
2610
+ if (!Number.isFinite(ts)) continue;
2611
+ const t = obj.tokens;
2612
+ const input = Math.max(0, safeNum(t.input) + safeNum(t.tool) - safeNum(t.cached));
2613
+ const output = safeNum(t.output) + safeNum(t.thoughts);
2614
+ const cacheRead = safeNum(t.cached);
2615
+ if (input + output + cacheRead === 0) continue;
2616
+ const model = typeof obj.model === "string" && obj.model ? obj.model : "unknown";
2617
+ const p = priceFor3(model);
2618
+ entries.push({
2619
+ id: typeof obj.id === "string" ? obj.id : void 0,
2620
+ ts,
2621
+ model: shortModel2(model),
2622
+ cost: input * p.in + cacheRead * p.cr + output * p.out,
2623
+ input,
2624
+ output,
2625
+ cacheCreate: 0,
2626
+ cacheRead,
2627
+ cacheSavings: cacheRead * (p.in - p.cr)
2628
+ });
2629
+ } catch {
2630
+ }
2631
+ }
2632
+ return entries;
2633
+ }
2634
+ async function loadEntries5(since, homeDir) {
2635
+ const files = [];
2636
+ const seen = /* @__PURE__ */ new Set();
2637
+ const seenIno = /* @__PURE__ */ new Set();
2638
+ let listing;
2639
+ try {
2640
+ listing = await readdir8(geminiTmpDir(homeDir), { recursive: true });
2641
+ } catch {
2642
+ return [];
2643
+ }
2644
+ for (const f of listing) {
2645
+ if (!isGeminiSessionFile(f)) continue;
2646
+ const path = join13(geminiTmpDir(homeDir), f);
2647
+ if (seen.has(path)) continue;
2648
+ seen.add(path);
2649
+ try {
2650
+ const s = await fsStat5(path);
2651
+ if (s.mtimeMs < since) continue;
2652
+ if (s.ino && process.platform !== "win32") {
2653
+ const idn = `${s.dev}:${s.ino}`;
2654
+ if (seenIno.has(idn)) continue;
2655
+ seenIno.add(idn);
2656
+ }
2657
+ files.push({ path, mtimeMs: s.mtimeMs, size: s.size });
2658
+ } catch {
2659
+ }
2660
+ }
2661
+ return loadCachedEntries(files, parseFile4, since);
2662
+ }
2663
+ async function geminiDashboard(tz, homeDir) {
2664
+ const entries = await loadEntries5(dashboardSince(tz), homeDir);
2665
+ return summarize(entries, tz);
2666
+ }
2667
+ async function geminiTable(tz, homeDir) {
2668
+ const entries = await loadEntries5(tableSince(tz), homeDir);
2669
+ return tabulate(entries, tz);
2670
+ }
2671
+
2672
+ // src/providers/gemini/billing.ts
2560
2673
  function geminiCredsPath(homeDir) {
2561
- return join13(homeDir ?? homedir11(), ".gemini", "oauth_creds.json");
2674
+ return join14(homeDir ?? homedir12(), ".gemini", "oauth_creds.json");
2675
+ }
2676
+ function geminiDir(homeDir) {
2677
+ return join14(homeDir ?? homedir12(), ".gemini");
2678
+ }
2679
+ function authTypeFromSettings(settings) {
2680
+ const raw = settings?.security?.auth?.selectedType ?? settings?.selectedAuthType;
2681
+ if (typeof raw !== "string") return "none";
2682
+ const value = raw.trim().toLowerCase();
2683
+ if (!value) return "none";
2684
+ if (value.includes("vertex") || value.includes("use_vertex_ai")) return "vertex";
2685
+ if (value.includes("gemini-api-key") || value.includes("api-key") || value.includes("use_gemini")) return "api-key";
2686
+ return "none";
2562
2687
  }
2563
- function hasGeminiApiKey() {
2688
+ async function authMethodFromSettings(homeDir) {
2689
+ try {
2690
+ const raw = await readFile6(join14(geminiDir(homeDir), "settings.json"), "utf8");
2691
+ return authTypeFromSettings(JSON.parse(raw));
2692
+ } catch {
2693
+ return "none";
2694
+ }
2695
+ }
2696
+ async function hasGeminiApiKeyFile(homeDir) {
2697
+ try {
2698
+ await access8(join14(geminiDir(homeDir), "api_key"));
2699
+ return true;
2700
+ } catch {
2701
+ }
2702
+ try {
2703
+ const env = await readFile6(join14(geminiDir(homeDir), ".env"), "utf8");
2704
+ return /^\s*GEMINI_API_KEY\s*=/m.test(env);
2705
+ } catch {
2706
+ return false;
2707
+ }
2708
+ }
2709
+ function hasGeminiApiKeyEnv() {
2564
2710
  return ["GEMINI_API_KEY", "GOOGLE_API_KEY", "GOOGLE_GENAI_API_KEY"].some((name) => typeof process.env[name] === "string" && process.env[name].trim() !== "");
2565
2711
  }
2712
+ async function noOAuthAuthMessage(homeDir) {
2713
+ const settingsMethod = await authMethodFromSettings(homeDir);
2714
+ if (settingsMethod === "api-key") return "API key auth \u2014 quota needs Google login (run gemini)";
2715
+ if (settingsMethod === "vertex") return "Vertex AI auth \u2014 quota needs Google login (run gemini)";
2716
+ if (await hasGeminiApiKeyFile(homeDir) || hasGeminiApiKeyEnv()) {
2717
+ return "API key auth \u2014 quota needs Google login (run gemini)";
2718
+ }
2719
+ return "Not signed in \u2014 run gemini and log in with Google";
2720
+ }
2566
2721
  async function detectGemini(homeDir) {
2722
+ let oauthOk = false;
2567
2723
  try {
2568
2724
  await access8(geminiCredsPath(homeDir));
2569
- return true;
2725
+ oauthOk = true;
2726
+ } catch {
2727
+ }
2728
+ return oauthOk || await hasGeminiChatSessions(homeDir);
2729
+ }
2730
+ async function hasGeminiChatSessions(homeDir) {
2731
+ let listing;
2732
+ try {
2733
+ listing = await readdir9(geminiTmpDir(homeDir), { recursive: true });
2570
2734
  } catch {
2571
2735
  return false;
2572
2736
  }
2737
+ return listing.some((path) => /(^|[\\/])chats[\\/]session-.*\.jsonl$/.test(path));
2573
2738
  }
2574
2739
  function decodeBase64UrlJson2(segment) {
2575
2740
  try {
@@ -2603,7 +2768,7 @@ async function geminiBilling(account) {
2603
2768
  return {
2604
2769
  plan: null,
2605
2770
  metrics: [],
2606
- error: hasGeminiApiKey() ? "API key auth \u2014 quota needs Google login (run gemini)" : "Not signed in \u2014 run gemini and log in with Google",
2771
+ error: await noOAuthAuthMessage(account.homeDir),
2607
2772
  ...identity
2608
2773
  };
2609
2774
  }
@@ -2624,34 +2789,36 @@ var geminiProvider = {
2624
2789
  id: "gemini",
2625
2790
  name: "Gemini",
2626
2791
  color: "greenBright",
2627
- hasUsage: false,
2792
+ hasUsage: true,
2628
2793
  hasBilling: true,
2629
2794
  detect: (homeDir) => detectGemini(homeDir),
2795
+ fetchSummary: (account, tz) => geminiDashboard(tz, account.homeDir),
2796
+ fetchTable: (account, tz) => geminiTable(tz, account.homeDir),
2630
2797
  fetchBilling: (account) => geminiBilling(account)
2631
2798
  };
2632
2799
 
2633
2800
  // src/providers/detect.ts
2634
2801
  import { accessSync, constants } from "fs";
2635
- import { join as join14, delimiter, isAbsolute as isAbsolute2 } from "path";
2636
- import { homedir as homedir12 } from "os";
2802
+ import { join as join15, delimiter, isAbsolute as isAbsolute2 } from "path";
2803
+ import { homedir as homedir13 } from "os";
2637
2804
  function searchDirs() {
2638
- const home = homedir12();
2805
+ const home = homedir13();
2639
2806
  const fromEnv = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
2640
2807
  const extra = process.platform === "win32" ? [
2641
- process.env.APPDATA && join14(process.env.APPDATA, "npm"),
2642
- process.env.LOCALAPPDATA && join14(process.env.LOCALAPPDATA, "pnpm"),
2643
- join14(home, "scoop", "shims")
2808
+ process.env.APPDATA && join15(process.env.APPDATA, "npm"),
2809
+ process.env.LOCALAPPDATA && join15(process.env.LOCALAPPDATA, "pnpm"),
2810
+ join15(home, "scoop", "shims")
2644
2811
  ] : [
2645
2812
  "/opt/homebrew/bin",
2646
2813
  "/usr/local/bin",
2647
2814
  "/usr/bin",
2648
2815
  "/bin",
2649
2816
  "/opt/local/bin",
2650
- join14(home, ".local", "bin"),
2651
- join14(home, "bin"),
2652
- join14(home, ".npm-global", "bin"),
2653
- join14(home, ".bun", "bin"),
2654
- join14(home, ".local", "share", "pnpm")
2817
+ join15(home, ".local", "bin"),
2818
+ join15(home, "bin"),
2819
+ join15(home, ".npm-global", "bin"),
2820
+ join15(home, ".bun", "bin"),
2821
+ join15(home, ".local", "share", "pnpm")
2655
2822
  ];
2656
2823
  return [.../* @__PURE__ */ new Set([...fromEnv, ...extra.filter((d) => !!d)])];
2657
2824
  }
@@ -2668,7 +2835,7 @@ function onPath(names) {
2668
2835
  for (const dir of searchDirs()) {
2669
2836
  for (const n of names) {
2670
2837
  for (const e of exts) {
2671
- if (isExec(join14(dir, n + e))) return true;
2838
+ if (isExec(join15(dir, n + e))) return true;
2672
2839
  }
2673
2840
  }
2674
2841
  }
@@ -2678,7 +2845,7 @@ function anyExists(paths) {
2678
2845
  return paths.some((p) => !!p && isExec(p));
2679
2846
  }
2680
2847
  function installSignals(id) {
2681
- const home = homedir12();
2848
+ const home = homedir13();
2682
2849
  const pf = process.env.ProgramFiles;
2683
2850
  const pf86 = process.env["ProgramFiles(x86)"];
2684
2851
  const lad = process.env.LOCALAPPDATA;
@@ -2686,8 +2853,8 @@ function installSignals(id) {
2686
2853
  case "claude":
2687
2854
  return onPath(["claude"]) || anyExists([
2688
2855
  "/Applications/Claude.app",
2689
- join14(home, "Applications", "Claude.app"),
2690
- lad && join14(lad, "Programs", "claude", "Claude.exe")
2856
+ join15(home, "Applications", "Claude.app"),
2857
+ lad && join15(lad, "Programs", "claude", "Claude.exe")
2691
2858
  ]);
2692
2859
  case "codex": {
2693
2860
  const bin = process.env.CODEX_BIN;
@@ -2697,10 +2864,10 @@ function installSignals(id) {
2697
2864
  case "cursor":
2698
2865
  return onPath(["cursor", "cursor-agent"]) || anyExists([
2699
2866
  "/Applications/Cursor.app",
2700
- join14(home, "Applications", "Cursor.app"),
2701
- lad && join14(lad, "Programs", "cursor", "Cursor.exe"),
2702
- pf && join14(pf, "Cursor", "Cursor.exe"),
2703
- pf86 && join14(pf86, "Cursor", "Cursor.exe"),
2867
+ join15(home, "Applications", "Cursor.app"),
2868
+ lad && join15(lad, "Programs", "cursor", "Cursor.exe"),
2869
+ pf && join15(pf, "Cursor", "Cursor.exe"),
2870
+ pf86 && join15(pf86, "Cursor", "Cursor.exe"),
2704
2871
  "/opt/Cursor/cursor",
2705
2872
  "/usr/share/cursor/cursor",
2706
2873
  "/usr/bin/cursor"
@@ -2714,8 +2881,8 @@ function installSignals(id) {
2714
2881
  case "antigravity":
2715
2882
  return onPath(["antigravity"]) || anyExists([
2716
2883
  "/Applications/Antigravity.app",
2717
- join14(home, "Applications", "Antigravity.app"),
2718
- lad && join14(lad, "Programs", "Antigravity", "Antigravity.exe")
2884
+ join15(home, "Applications", "Antigravity.app"),
2885
+ lad && join15(lad, "Programs", "Antigravity", "Antigravity.exe")
2719
2886
  ]);
2720
2887
  case "gemini":
2721
2888
  return onPath(["gemini"]);
@@ -2752,10 +2919,10 @@ async function detectProviders() {
2752
2919
 
2753
2920
  // src/accounts.ts
2754
2921
  import { existsSync, readdirSync, readFileSync, statSync } from "fs";
2755
- import { homedir as homedir13 } from "os";
2756
- import { basename, join as join15, resolve } from "path";
2922
+ import { homedir as homedir14 } from "os";
2923
+ import { basename, join as join16, resolve } from "path";
2757
2924
  function accountKey(providerId, homeDir) {
2758
- return `${providerId}:${homeDir ? resolve(expandHome(homeDir)) : homedir13()}`;
2925
+ return `${providerId}:${homeDir ? resolve(expandHome(homeDir)) : homedir14()}`;
2759
2926
  }
2760
2927
  function uniqueId(base, used) {
2761
2928
  let id = slugify(base) || "account";
@@ -2776,7 +2943,7 @@ function uniqueId(base, used) {
2776
2943
  }
2777
2944
  function readClaudeIdentity2(homeDir) {
2778
2945
  try {
2779
- const parsed = JSON.parse(readFileSync(join15(homeDir, ".claude.json"), "utf-8"));
2946
+ const parsed = JSON.parse(readFileSync(join16(homeDir, ".claude.json"), "utf-8"));
2780
2947
  const oauth = parsed?.oauthAccount;
2781
2948
  return {
2782
2949
  email: typeof oauth?.emailAddress === "string" && oauth.emailAddress.trim() ? oauth.emailAddress.trim() : void 0,
@@ -2787,10 +2954,10 @@ function readClaudeIdentity2(homeDir) {
2787
2954
  }
2788
2955
  }
2789
2956
  function hasClaudeState(homeDir) {
2790
- return existsSync(join15(homeDir, ".claude.json")) || existsSync(join15(homeDir, ".claude", ".credentials.json")) || existsSync(join15(homeDir, ".claude", "projects")) || existsSync(join15(homeDir, ".config", "claude", ".credentials.json")) || existsSync(join15(homeDir, ".config", "claude", "projects"));
2957
+ return existsSync(join16(homeDir, ".claude.json")) || existsSync(join16(homeDir, ".claude", ".credentials.json")) || existsSync(join16(homeDir, ".claude", "projects")) || existsSync(join16(homeDir, ".config", "claude", ".credentials.json")) || existsSync(join16(homeDir, ".config", "claude", "projects"));
2791
2958
  }
2792
2959
  function candidateAlternateHomes(prefix) {
2793
- const home = homedir13();
2960
+ const home = homedir14();
2794
2961
  let entries;
2795
2962
  try {
2796
2963
  entries = readdirSync(home);
@@ -2801,7 +2968,7 @@ function candidateAlternateHomes(prefix) {
2801
2968
  const pattern = new RegExp(`^\\.${prefix}[_-]`);
2802
2969
  for (const name of entries) {
2803
2970
  if (!pattern.test(name)) continue;
2804
- const path = join15(home, name);
2971
+ const path = join16(home, name);
2805
2972
  try {
2806
2973
  if (!statSync(path).isDirectory()) continue;
2807
2974
  out.push(path);
@@ -2827,7 +2994,7 @@ function decodeBase64UrlJson3(segment) {
2827
2994
  }
2828
2995
  }
2829
2996
  function codexAuthPaths(homeDir) {
2830
- return [join15(homeDir, ".codex", "auth.json"), join15(homeDir, "auth.json")];
2997
+ return [join16(homeDir, ".codex", "auth.json"), join16(homeDir, "auth.json")];
2831
2998
  }
2832
2999
  function readCodexIdentity(homeDir) {
2833
3000
  for (const path of codexAuthPaths(homeDir)) {
@@ -8,7 +8,7 @@ import {
8
8
  detectProviders,
9
9
  fetchPeak,
10
10
  resolveTimezone
11
- } from "./chunk-5BW4H7WW.js";
11
+ } from "./chunk-ANCKHLJV.js";
12
12
  import {
13
13
  cacheDir,
14
14
  expandHome,
package/dist/cli.js CHANGED
@@ -14,12 +14,12 @@ process.emitWarning = ((warning, ...rest) => {
14
14
  var args = process.argv.slice(2);
15
15
  var subcommand = args[0]?.toLowerCase();
16
16
  if (subcommand === "__daemon") {
17
- const { runDaemon } = await import("./daemon-OBQJO6D4.js");
17
+ const { runDaemon } = await import("./daemon-BUYFTSED.js");
18
18
  await runDaemon(args.slice(1), { foreground: false });
19
19
  process.exit(typeof process.exitCode === "number" ? process.exitCode : 0);
20
20
  }
21
21
  if (subcommand === "serve" || subcommand === "web") {
22
- const { runDaemon } = await import("./daemon-OBQJO6D4.js");
22
+ const { runDaemon } = await import("./daemon-BUYFTSED.js");
23
23
  await runDaemon(args.slice(1), { foreground: true });
24
24
  process.exit(typeof process.exitCode === "number" ? process.exitCode : 0);
25
25
  }
@@ -68,5 +68,5 @@ setGlyphs(resolveGlyphs({
68
68
  }));
69
69
  var daemon = await attachOrSpawn();
70
70
  var mode = daemon.kind === "spawned" ? "connected" : "degraded";
71
- var { bootstrapInk } = await import("./bootstrap-ink-AO3QA5BH.js");
71
+ var { bootstrapInk } = await import("./bootstrap-ink-F3JFQ3ON.js");
72
72
  await bootstrapInk({ interval, config, daemon, mode });
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  appVersion,
4
4
  startWebServer
5
- } from "./chunk-DZY72PPB.js";
5
+ } from "./chunk-M3RGQIOW.js";
6
6
  import {
7
7
  flushDisk
8
- } from "./chunk-5BW4H7WW.js";
8
+ } from "./chunk-ANCKHLJV.js";
9
9
  import {
10
10
  cacheDir,
11
11
  loadConfig
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startWebServer
4
- } from "./chunk-DZY72PPB.js";
5
- import "./chunk-5BW4H7WW.js";
4
+ } from "./chunk-M3RGQIOW.js";
5
+ import "./chunk-ANCKHLJV.js";
6
6
  import "./chunk-XQEJ4WQ5.js";
7
7
  export {
8
8
  startWebServer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokmon",
3
- "version": "0.20.1",
3
+ "version": "0.20.3",
4
4
  "description": "Terminal + web dashboard for Claude Code, Codex, Cursor, opencode, pi, Copilot, Gemini & more — usage, limits, and costs",
5
5
  "type": "module",
6
6
  "bin": {