tokmon 0.14.0 → 0.14.2

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 (2) hide show
  1. package/dist/cli.js +234 -75
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -609,7 +609,7 @@ function glyphs() {
609
609
 
610
610
  // src/app.tsx
611
611
  import { useState as useState3, useEffect as useEffect3, useCallback, useRef as useRef2, useMemo } from "react";
612
- import { Box as Box7, Text as Text7, useInput, useStdout, useApp } from "ink";
612
+ import { Box as Box7, Text as Text7, Transform, useInput, useStdout, useApp } from "ink";
613
613
  import { useMouse } from "@zenobius/ink-mouse";
614
614
 
615
615
  // src/http.ts
@@ -1987,11 +1987,15 @@ var copilotProvider = {
1987
1987
  };
1988
1988
 
1989
1989
  // src/providers/antigravity/billing.ts
1990
- import { access as access7, readdir as readdir6 } from "fs/promises";
1991
- import { join as join12 } from "path";
1992
- import { homedir as homedir10 } from "os";
1990
+ import { access as access7, readdir as readdir7 } from "fs/promises";
1991
+ import { join as join13 } from "path";
1992
+ import { homedir as homedir11 } from "os";
1993
1993
 
1994
1994
  // src/providers/cloud-code.ts
1995
+ import { readFile as readFile6, readdir as readdir6, realpath, stat } from "fs/promises";
1996
+ import { spawnSync } from "child_process";
1997
+ import { homedir as homedir10 } from "os";
1998
+ import { dirname, join as join12 } from "path";
1995
1999
  var CLOUD_CODE_URLS = [
1996
2000
  "https://daily-cloudcode-pa.googleapis.com",
1997
2001
  "https://cloudcode-pa.googleapis.com"
@@ -2000,8 +2004,9 @@ var LOAD_CODE_ASSIST_PATH = "/v1internal:loadCodeAssist";
2000
2004
  var FETCH_MODELS_PATH = "/v1internal:fetchAvailableModels";
2001
2005
  var RETRIEVE_QUOTA_PATH = "/v1internal:retrieveUserQuota";
2002
2006
  var GOOGLE_OAUTH_URL = "https://oauth2.googleapis.com/token";
2003
- var GOOGLE_CLIENT_ID = process.env.TOKMON_GOOGLE_CLIENT_ID ?? "";
2004
- var GOOGLE_CLIENT_SECRET = process.env.TOKMON_GOOGLE_CLIENT_SECRET ?? "";
2007
+ var GOOGLE_OAUTH_CLIENT_REGEX = /OAUTH_CLIENT_ID\s*=\s*["']([0-9]{6,}-[a-z0-9]+\.apps\.googleusercontent\.com)["']\s*;?\s*(?:var|const|let)?\s*OAUTH_CLIENT_SECRET\s*=\s*["'](GOCSPX-[A-Za-z0-9_-]+)["']/s;
2008
+ var MAX_BUNDLE_READ = 32 * 1024 * 1024;
2009
+ var cachedClient;
2005
2010
  var OAUTH_TOKEN_KEY = "antigravityUnifiedStateSync.oauthToken";
2006
2011
  var OAUTH_TOKEN_SENTINEL = "oauthTokenInfoSentinelKey";
2007
2012
  var CC_MODEL_BLACKLIST = {
@@ -2112,11 +2117,90 @@ function redact(token) {
2112
2117
  if (!token) return "none";
2113
2118
  return `...${token.slice(-4)}`;
2114
2119
  }
2120
+ function geminiBundleCandidates() {
2121
+ const candidates = [];
2122
+ const addBundle = (nodeModulesRoot) => {
2123
+ if (!nodeModulesRoot) return;
2124
+ candidates.push(join12(nodeModulesRoot, "@google", "gemini-cli", "bundle"));
2125
+ };
2126
+ try {
2127
+ const which = spawnSync("command", ["-v", "gemini"], { encoding: "utf8", timeout: 5e3 });
2128
+ const resolved = typeof which.stdout === "string" ? which.stdout.trim().split("\n")[0]?.trim() : "";
2129
+ if (resolved) candidates.push(resolved);
2130
+ } catch {
2131
+ }
2132
+ const home = homedir10();
2133
+ addBundle("/opt/homebrew/lib/node_modules");
2134
+ addBundle("/usr/local/lib/node_modules");
2135
+ addBundle(join12(home, ".local", "share", "node_modules"));
2136
+ addBundle(join12(home, ".bun", "install", "global", "node_modules"));
2137
+ try {
2138
+ const prefix = spawnSync("npm", ["config", "get", "prefix"], { encoding: "utf8", timeout: 5e3 });
2139
+ const root = typeof prefix.stdout === "string" ? prefix.stdout.trim() : "";
2140
+ if (root && root !== "undefined") addBundle(join12(root, "lib", "node_modules"));
2141
+ } catch {
2142
+ }
2143
+ return [...new Set(candidates.filter(Boolean))];
2144
+ }
2145
+ async function resolveBundleDir(candidate) {
2146
+ try {
2147
+ if (candidate.endsWith(`${join12("@google", "gemini-cli", "bundle")}`)) {
2148
+ return candidate;
2149
+ }
2150
+ const real = await realpath(candidate);
2151
+ return dirname(real);
2152
+ } catch {
2153
+ return null;
2154
+ }
2155
+ }
2156
+ async function scanBundleDir(dir) {
2157
+ let entries;
2158
+ try {
2159
+ entries = await readdir6(dir);
2160
+ } catch {
2161
+ return null;
2162
+ }
2163
+ const targets = entries.filter((name) => name === "gemini.js" || name.startsWith("chunk-") && name.endsWith(".js"));
2164
+ for (const name of targets) {
2165
+ const filePath = join12(dir, name);
2166
+ try {
2167
+ const info = await stat(filePath);
2168
+ if (!info.isFile() || info.size > MAX_BUNDLE_READ) continue;
2169
+ const contents = await readFile6(filePath, "utf8");
2170
+ if (!contents.includes("OAUTH_CLIENT_SECRET")) continue;
2171
+ const match = GOOGLE_OAUTH_CLIENT_REGEX.exec(contents);
2172
+ if (match) return { clientId: match[1], clientSecret: match[2] };
2173
+ } catch {
2174
+ }
2175
+ }
2176
+ return null;
2177
+ }
2178
+ async function discoverGoogleOAuthClient() {
2179
+ try {
2180
+ for (const candidate of geminiBundleCandidates()) {
2181
+ const dir = await resolveBundleDir(candidate);
2182
+ if (!dir) continue;
2183
+ const found = await scanBundleDir(dir);
2184
+ if (found) return found;
2185
+ }
2186
+ } catch {
2187
+ }
2188
+ return null;
2189
+ }
2190
+ async function resolveGoogleClient() {
2191
+ const envId = process.env.TOKMON_GOOGLE_CLIENT_ID?.trim();
2192
+ const envSecret = process.env.TOKMON_GOOGLE_CLIENT_SECRET?.trim();
2193
+ if (envId && envSecret) return { clientId: envId, clientSecret: envSecret };
2194
+ if (cachedClient === void 0) cachedClient = await discoverGoogleOAuthClient();
2195
+ return cachedClient;
2196
+ }
2115
2197
  async function refreshAccessToken(refreshToken) {
2116
- if (!refreshToken || !GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET) return null;
2198
+ if (!refreshToken) return null;
2199
+ const client = await resolveGoogleClient();
2200
+ if (!client) return null;
2117
2201
  const body = new URLSearchParams({
2118
- client_id: GOOGLE_CLIENT_ID,
2119
- client_secret: GOOGLE_CLIENT_SECRET,
2202
+ client_id: client.clientId,
2203
+ client_secret: client.clientSecret,
2120
2204
  refresh_token: refreshToken,
2121
2205
  grant_type: "refresh_token"
2122
2206
  });
@@ -2289,33 +2373,33 @@ async function firstExisting(paths) {
2289
2373
  return paths[0];
2290
2374
  }
2291
2375
  async function antigravityStateDb(homeDir) {
2292
- const base = homeDir ?? homedir10();
2376
+ const base = homeDir ?? homedir11();
2293
2377
  const tail = ["User", "globalStorage", "state.vscdb"];
2294
2378
  if (process.platform === "darwin") {
2295
- const support = join12(base, "Library", "Application Support");
2379
+ const support = join13(base, "Library", "Application Support");
2296
2380
  const exact = [
2297
- join12(support, "Antigravity IDE", ...tail),
2298
- join12(support, "Antigravity", ...tail)
2381
+ join13(support, "Antigravity IDE", ...tail),
2382
+ join13(support, "Antigravity", ...tail)
2299
2383
  ];
2300
2384
  try {
2301
- const entries = await readdir6(support, { withFileTypes: true });
2302
- const matches = entries.filter((e) => e.isDirectory() && e.name.includes("Antigravity")).map((e) => join12(support, e.name, ...tail));
2385
+ const entries = await readdir7(support, { withFileTypes: true });
2386
+ const matches = entries.filter((e) => e.isDirectory() && e.name.includes("Antigravity")).map((e) => join13(support, e.name, ...tail));
2303
2387
  return firstExisting([...exact, ...matches]);
2304
2388
  } catch {
2305
2389
  return firstExisting(exact);
2306
2390
  }
2307
2391
  }
2308
2392
  if (process.platform === "win32") {
2309
- const roaming = homeDir ? join12(homeDir, "AppData", "Roaming") : envDir("APPDATA") ?? join12(base, "AppData", "Roaming");
2393
+ const roaming = homeDir ? join13(homeDir, "AppData", "Roaming") : envDir("APPDATA") ?? join13(base, "AppData", "Roaming");
2310
2394
  return firstExisting([
2311
- join12(roaming, "Antigravity IDE", ...tail),
2312
- join12(roaming, "Antigravity", ...tail)
2395
+ join13(roaming, "Antigravity IDE", ...tail),
2396
+ join13(roaming, "Antigravity", ...tail)
2313
2397
  ]);
2314
2398
  }
2315
- const cfg = homeDir ? join12(homeDir, ".config") : envDir("XDG_CONFIG_HOME") ?? join12(base, ".config");
2399
+ const cfg = homeDir ? join13(homeDir, ".config") : envDir("XDG_CONFIG_HOME") ?? join13(base, ".config");
2316
2400
  return firstExisting([
2317
- join12(cfg, "Antigravity IDE", ...tail),
2318
- join12(cfg, "Antigravity", ...tail)
2401
+ join13(cfg, "Antigravity IDE", ...tail),
2402
+ join13(cfg, "Antigravity", ...tail)
2319
2403
  ]);
2320
2404
  }
2321
2405
  async function detectAntigravity(homeDir) {
@@ -2346,11 +2430,11 @@ var antigravityProvider = {
2346
2430
  };
2347
2431
 
2348
2432
  // src/providers/gemini/billing.ts
2349
- import { access as access8, readFile as readFile6 } from "fs/promises";
2350
- import { join as join13 } from "path";
2351
- import { homedir as homedir11 } from "os";
2433
+ import { access as access8, readFile as readFile7 } from "fs/promises";
2434
+ import { join as join14 } from "path";
2435
+ import { homedir as homedir12 } from "os";
2352
2436
  function geminiCredsPath(homeDir) {
2353
- return join13(homeDir ?? homedir11(), ".gemini", "oauth_creds.json");
2437
+ return join14(homeDir ?? homedir12(), ".gemini", "oauth_creds.json");
2354
2438
  }
2355
2439
  async function detectGemini(homeDir) {
2356
2440
  try {
@@ -2362,7 +2446,7 @@ async function detectGemini(homeDir) {
2362
2446
  }
2363
2447
  async function readGeminiCreds(path) {
2364
2448
  try {
2365
- return JSON.parse(await readFile6(path, "utf8"));
2449
+ return JSON.parse(await readFile7(path, "utf8"));
2366
2450
  } catch {
2367
2451
  return null;
2368
2452
  }
@@ -2398,26 +2482,26 @@ var geminiProvider = {
2398
2482
 
2399
2483
  // src/providers/detect.ts
2400
2484
  import { accessSync, constants } from "fs";
2401
- import { join as join14, delimiter, isAbsolute as isAbsolute3 } from "path";
2402
- import { homedir as homedir12 } from "os";
2485
+ import { join as join15, delimiter, isAbsolute as isAbsolute3 } from "path";
2486
+ import { homedir as homedir13 } from "os";
2403
2487
  function searchDirs() {
2404
- const home = homedir12();
2488
+ const home = homedir13();
2405
2489
  const fromEnv = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
2406
2490
  const extra = process.platform === "win32" ? [
2407
- process.env.APPDATA && join14(process.env.APPDATA, "npm"),
2408
- process.env.LOCALAPPDATA && join14(process.env.LOCALAPPDATA, "pnpm"),
2409
- join14(home, "scoop", "shims")
2491
+ process.env.APPDATA && join15(process.env.APPDATA, "npm"),
2492
+ process.env.LOCALAPPDATA && join15(process.env.LOCALAPPDATA, "pnpm"),
2493
+ join15(home, "scoop", "shims")
2410
2494
  ] : [
2411
2495
  "/opt/homebrew/bin",
2412
2496
  "/usr/local/bin",
2413
2497
  "/usr/bin",
2414
2498
  "/bin",
2415
2499
  "/opt/local/bin",
2416
- join14(home, ".local", "bin"),
2417
- join14(home, "bin"),
2418
- join14(home, ".npm-global", "bin"),
2419
- join14(home, ".bun", "bin"),
2420
- join14(home, ".local", "share", "pnpm")
2500
+ join15(home, ".local", "bin"),
2501
+ join15(home, "bin"),
2502
+ join15(home, ".npm-global", "bin"),
2503
+ join15(home, ".bun", "bin"),
2504
+ join15(home, ".local", "share", "pnpm")
2421
2505
  ];
2422
2506
  return [.../* @__PURE__ */ new Set([...fromEnv, ...extra.filter((d) => !!d)])];
2423
2507
  }
@@ -2434,7 +2518,7 @@ function onPath(names) {
2434
2518
  for (const dir of searchDirs()) {
2435
2519
  for (const n of names) {
2436
2520
  for (const e of exts) {
2437
- if (isExec(join14(dir, n + e))) return true;
2521
+ if (isExec(join15(dir, n + e))) return true;
2438
2522
  }
2439
2523
  }
2440
2524
  }
@@ -2444,7 +2528,7 @@ function anyExists(paths) {
2444
2528
  return paths.some((p) => !!p && isExec(p));
2445
2529
  }
2446
2530
  function installSignals(id) {
2447
- const home = homedir12();
2531
+ const home = homedir13();
2448
2532
  const pf = process.env.ProgramFiles;
2449
2533
  const pf86 = process.env["ProgramFiles(x86)"];
2450
2534
  const lad = process.env.LOCALAPPDATA;
@@ -2452,8 +2536,8 @@ function installSignals(id) {
2452
2536
  case "claude":
2453
2537
  return onPath(["claude"]) || anyExists([
2454
2538
  "/Applications/Claude.app",
2455
- join14(home, "Applications", "Claude.app"),
2456
- lad && join14(lad, "Programs", "claude", "Claude.exe")
2539
+ join15(home, "Applications", "Claude.app"),
2540
+ lad && join15(lad, "Programs", "claude", "Claude.exe")
2457
2541
  ]);
2458
2542
  case "codex": {
2459
2543
  const bin = process.env.CODEX_BIN;
@@ -2463,10 +2547,10 @@ function installSignals(id) {
2463
2547
  case "cursor":
2464
2548
  return onPath(["cursor", "cursor-agent"]) || anyExists([
2465
2549
  "/Applications/Cursor.app",
2466
- join14(home, "Applications", "Cursor.app"),
2467
- lad && join14(lad, "Programs", "cursor", "Cursor.exe"),
2468
- pf && join14(pf, "Cursor", "Cursor.exe"),
2469
- pf86 && join14(pf86, "Cursor", "Cursor.exe"),
2550
+ join15(home, "Applications", "Cursor.app"),
2551
+ lad && join15(lad, "Programs", "cursor", "Cursor.exe"),
2552
+ pf && join15(pf, "Cursor", "Cursor.exe"),
2553
+ pf86 && join15(pf86, "Cursor", "Cursor.exe"),
2470
2554
  "/opt/Cursor/cursor",
2471
2555
  "/usr/share/cursor/cursor",
2472
2556
  "/usr/bin/cursor"
@@ -2480,8 +2564,8 @@ function installSignals(id) {
2480
2564
  case "antigravity":
2481
2565
  return onPath(["antigravity"]) || anyExists([
2482
2566
  "/Applications/Antigravity.app",
2483
- join14(home, "Applications", "Antigravity.app"),
2484
- lad && join14(lad, "Programs", "Antigravity", "Antigravity.exe")
2567
+ join15(home, "Applications", "Antigravity.app"),
2568
+ lad && join15(lad, "Programs", "Antigravity", "Antigravity.exe")
2485
2569
  ]);
2486
2570
  case "gemini":
2487
2571
  return onPath(["gemini"]);
@@ -2550,14 +2634,14 @@ function accountsByProvider(accounts) {
2550
2634
  }
2551
2635
 
2552
2636
  // src/snapshot.ts
2553
- import { readFile as readFile7, writeFile as writeFile3, mkdir as mkdir3, rename as rename3 } from "fs/promises";
2554
- import { join as join15 } from "path";
2637
+ import { readFile as readFile8, writeFile as writeFile3, mkdir as mkdir3, rename as rename3 } from "fs/promises";
2638
+ import { join as join16 } from "path";
2555
2639
  function snapshotFile() {
2556
- return join15(cacheDir(), "dashboard-snapshot.json");
2640
+ return join16(cacheDir(), "dashboard-snapshot.json");
2557
2641
  }
2558
2642
  async function loadSnapshot() {
2559
2643
  try {
2560
- const obj = JSON.parse(await readFile7(snapshotFile(), "utf-8"));
2644
+ const obj = JSON.parse(await readFile8(snapshotFile(), "utf-8"));
2561
2645
  return obj && typeof obj === "object" ? obj : {};
2562
2646
  } catch {
2563
2647
  return {};
@@ -2573,7 +2657,7 @@ function saveSnapshot(stats) {
2573
2657
  try {
2574
2658
  const dir = cacheDir();
2575
2659
  await mkdir3(dir, { recursive: true });
2576
- const tmp = join15(dir, `dashboard-snapshot.json.${process.pid}.tmp`);
2660
+ const tmp = join16(dir, `dashboard-snapshot.json.${process.pid}.tmp`);
2577
2661
  await writeFile3(tmp, JSON.stringify(obj));
2578
2662
  await rename3(tmp, snapshotFile());
2579
2663
  } catch {
@@ -2930,6 +3014,30 @@ function aggregate(list) {
2930
3014
  }
2931
3015
  return z;
2932
3016
  }
3017
+ function TotalsRow({ groups, stats, cols }) {
3018
+ const zero = () => ({ cost: 0, tokens: 0, cacheRead: 0, cacheSavings: 0 });
3019
+ const t = zero(), w = zero(), m = zero();
3020
+ for (const g of groups) {
3021
+ if (!PROVIDERS[g.provider].hasUsage) continue;
3022
+ for (const a of g.accounts) {
3023
+ const d = stats.get(a.id)?.dashboard;
3024
+ if (!d) continue;
3025
+ t.cost += d.today.cost;
3026
+ t.tokens += d.today.tokens;
3027
+ w.cost += d.week.cost;
3028
+ w.tokens += d.week.tokens;
3029
+ m.cost += d.month.cost;
3030
+ m.tokens += d.month.tokens;
3031
+ }
3032
+ }
3033
+ const inner = cols - 4;
3034
+ const dot = glyphs().middot;
3035
+ const full = `${glyphs().dotAll} Today ${currency(t.cost)} (${tokens(t.tokens)} tok) ${dot} Week ${currency(w.cost)} (${tokens(w.tokens)} tok) ${dot} Month ${currency(m.cost)} (${tokens(m.tokens)} tok)`;
3036
+ const noTok = `${glyphs().dotAll} Today ${currency(t.cost)} ${dot} Week ${currency(w.cost)} ${dot} Month ${currency(m.cost)}`;
3037
+ const tight = `${glyphs().dotAll} ${currency(t.cost)} ${dot} ${currency(w.cost)} ${dot} ${currency(m.cost)}`;
3038
+ const text = full.length <= inner ? full : noTok.length <= inner ? noTok : tight;
3039
+ return /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: text }) });
3040
+ }
2933
3041
 
2934
3042
  // src/ui/table.tsx
2935
3043
  import { Box as Box3, Text as Text3 } from "ink";
@@ -3775,9 +3883,31 @@ var CURSOR_SORTS = [
3775
3883
  { label: "model", dir: null }
3776
3884
  ];
3777
3885
  var IS_TTY = process.stdin.isTTY === true;
3886
+ var REPO_URL = "https://github.com/DavidIlie/tokmon";
3887
+ function detectHyperlinks(env, isTTY) {
3888
+ const force = env.FORCE_HYPERLINK;
3889
+ if (force != null && force !== "") return force !== "0" && force.toLowerCase() !== "false";
3890
+ if (!isTTY || env.TERM === "dumb" || env.NO_HYPERLINK) return false;
3891
+ if (env.WT_SESSION || env.ConEmuANSI === "ON" || env.KITTY_WINDOW_ID || env.TERM === "xterm-kitty") return true;
3892
+ if (env.KONSOLE_VERSION || env.TERMINAL_EMULATOR === "JetBrains-JediTerm") return true;
3893
+ if (env.VTE_VERSION && Number(env.VTE_VERSION) >= 5e3) return true;
3894
+ const tp = env.TERM_PROGRAM;
3895
+ if (tp) {
3896
+ const [maj, min] = (env.TERM_PROGRAM_VERSION ?? "").split(".").map((n) => Number(n) || 0);
3897
+ if (tp === "iTerm.app") return maj > 3 || maj === 3 && min >= 1;
3898
+ if (tp === "vscode" || tp === "WezTerm" || tp === "ghostty" || tp === "Hyper" || tp === "Tabby" || tp === "rio") return true;
3899
+ }
3900
+ return false;
3901
+ }
3902
+ var HYPERLINKS = detectHyperlinks(process.env, process.stdout.isTTY === true);
3903
+ function osc8(text, url) {
3904
+ if (!HYPERLINKS) return text;
3905
+ return `\x1B]8;;${url}\x07${text}\x1B]8;;\x07`;
3906
+ }
3778
3907
  var DEBOUNCE_MS = 300;
3779
3908
  var LOADER_GRACE_MS = 600;
3780
3909
  var LOADER_MAX_MS = 8e3;
3910
+ var LOADER_MIN_VISIBLE_MS = 700;
3781
3911
  var DEFAULT_CONFIG = {
3782
3912
  interval: 2,
3783
3913
  billingInterval: 5,
@@ -3792,8 +3922,13 @@ var DEFAULT_CONFIG = {
3792
3922
  ascii: "auto",
3793
3923
  knownProviders: []
3794
3924
  };
3795
- function App({ interval: cliInterval }) {
3796
- const [config2, setConfig] = useState3(null);
3925
+ function applyStartup(c, cliInterval) {
3926
+ if (cliInterval) c = { ...c, interval: cliInterval / 1e3 };
3927
+ if (c.defaultFocus === "all") c = { ...c, activeAccountId: null };
3928
+ return c;
3929
+ }
3930
+ function App({ interval: cliInterval, initialConfig }) {
3931
+ const [config2, setConfig] = useState3(() => initialConfig ? applyStartup(initialConfig, cliInterval) : null);
3797
3932
  const [detected, setDetected] = useState3([]);
3798
3933
  const [stats, setStats] = useState3(/* @__PURE__ */ new Map());
3799
3934
  const [peak, setPeak] = useState3(null);
@@ -3820,7 +3955,9 @@ function App({ interval: cliInterval }) {
3820
3955
  const [dashPage, setDashPage] = useState3(0);
3821
3956
  const [debouncePassed, setDebouncePassed] = useState3(false);
3822
3957
  const [graceHold, setGraceHold] = useState3(false);
3958
+ const [loaderShownAt, setLoaderShownAt] = useState3(null);
3823
3959
  const loaderDone = useRef2(false);
3960
+ const prevShowPicker = useRef2(false);
3824
3961
  const { stdout } = useStdout();
3825
3962
  const { exit } = useApp();
3826
3963
  const rows = stdout?.rows ?? 24;
@@ -3858,7 +3995,7 @@ function App({ interval: cliInterval }) {
3858
3995
  /*"focus "*/
3859
3996
  ))) : 0;
3860
3997
  const headerRows = cols < 70 ? 2 : 1;
3861
- const CHROME = 2 + headerRows + 3 + (hasStrip ? 1 + stripLines : 0) + 2;
3998
+ const CHROME = 2 + headerRows + 3 + (hasStrip ? 1 + stripLines : 0) + 2 + 2;
3862
3999
  const gridBudget = Math.max(1, rows - CHROME);
3863
4000
  const dashLayout = chooseLayout(
3864
4001
  Math.max(56, cols - 4),
@@ -3878,7 +4015,8 @@ function App({ interval: cliInterval }) {
3878
4015
  const needsOnboarding = configReady && !cfg.onboarded;
3879
4016
  const newProviders = configReady && cfg.onboarded ? PROVIDER_ORDER.filter((p) => !cfg.knownProviders.includes(p) && detected.includes(p)) : [];
3880
4017
  const showPicker = needsOnboarding || newProviders.length > 0;
3881
- const showLoader = configReady && !showPicker && !showSettings && !TOO_SMALL && accounts.length > 0 && (!allReady || graceHold) && debouncePassed && !loaderDone.current;
4018
+ const minVisibleHold = loaderShownAt !== null && Date.now() - loaderShownAt < LOADER_MIN_VISIBLE_MS;
4019
+ const showLoader = configReady && !showPicker && !showSettings && !TOO_SMALL && accounts.length > 0 && (!allReady || graceHold || minVisibleHold) && (debouncePassed || loaderShownAt !== null) && !loaderDone.current;
3882
4020
  const pickerProviders = needsOnboarding ? PROVIDER_ORDER : newProviders;
3883
4021
  const onboardEnabled = onboardSel ?? detected;
3884
4022
  const onboardItems = pickerProviders.map((pid) => ({
@@ -3889,15 +4027,24 @@ function App({ interval: cliInterval }) {
3889
4027
  enabled: onboardEnabled.includes(pid)
3890
4028
  }));
3891
4029
  useEffect3(() => {
3892
- loadConfig().then((c) => {
3893
- if (cliInterval) c = { ...c, interval: cliInterval / 1e3 };
3894
- if (c.defaultFocus === "all") c = { ...c, activeAccountId: null };
3895
- setConfig(c);
3896
- });
4030
+ const wasPicker = prevShowPicker.current;
4031
+ prevShowPicker.current = showPicker;
4032
+ if (wasPicker && !showPicker) {
4033
+ loaderDone.current = false;
4034
+ setDebouncePassed(false);
4035
+ setGraceHold(false);
4036
+ setLoaderShownAt(null);
4037
+ }
4038
+ }, [showPicker]);
4039
+ useEffect3(() => {
4040
+ if (showLoader && loaderShownAt === null) setLoaderShownAt(Date.now());
4041
+ }, [showLoader, loaderShownAt]);
4042
+ useEffect3(() => {
4043
+ if (!initialConfig) loadConfig().then((c) => setConfig(applyStartup(c, cliInterval)));
3897
4044
  detectProviders().then(setDetected);
3898
4045
  }, []);
3899
4046
  useEffect3(() => {
3900
- if (seededRef.current || !configReady || accounts.length === 0) return;
4047
+ if (seededRef.current || !configReady || showPicker || accounts.length === 0) return;
3901
4048
  seededRef.current = true;
3902
4049
  loadSnapshot().then((snap) => {
3903
4050
  setStats((prev) => {
@@ -3910,7 +4057,7 @@ function App({ interval: cliInterval }) {
3910
4057
  return next;
3911
4058
  });
3912
4059
  });
3913
- }, [configReady, accountsKey]);
4060
+ }, [configReady, showPicker, accountsKey]);
3914
4061
  useEffect3(() => {
3915
4062
  if (stats.size === 0) return;
3916
4063
  const t = setTimeout(() => saveSnapshot(stats), 500);
@@ -3931,19 +4078,21 @@ function App({ interval: cliInterval }) {
3931
4078
  }, [configReady, showPicker, accountsKey]);
3932
4079
  useEffect3(() => {
3933
4080
  if (!allReady || loaderDone.current) return;
3934
- if (!debouncePassed) {
4081
+ if (loaderShownAt === null) {
3935
4082
  loaderDone.current = true;
3936
4083
  return;
3937
4084
  }
3938
4085
  setGraceHold(true);
4086
+ const minRemaining = Math.max(0, LOADER_MIN_VISIBLE_MS - (Date.now() - loaderShownAt));
4087
+ const hold = Math.max(LOADER_GRACE_MS, minRemaining);
3939
4088
  const t = setTimeout(() => {
3940
4089
  loaderDone.current = true;
3941
4090
  setGraceHold(false);
3942
- }, LOADER_GRACE_MS);
4091
+ }, hold);
3943
4092
  return () => clearTimeout(t);
3944
4093
  }, [allReady]);
3945
4094
  useEffect3(() => {
3946
- if (!configReady) return;
4095
+ if (!configReady || showPicker) return;
3947
4096
  let active2 = true;
3948
4097
  let timer;
3949
4098
  const load = async () => {
@@ -3970,9 +4119,9 @@ function App({ interval: cliInterval }) {
3970
4119
  active2 = false;
3971
4120
  clearTimeout(timer);
3972
4121
  };
3973
- }, [interval2, tz, configReady, accountsKey]);
4122
+ }, [interval2, tz, configReady, showPicker, accountsKey]);
3974
4123
  useEffect3(() => {
3975
- if (!configReady) return;
4124
+ if (!configReady || showPicker) return;
3976
4125
  let active2 = true;
3977
4126
  let timer;
3978
4127
  const load = async () => {
@@ -3998,7 +4147,7 @@ function App({ interval: cliInterval }) {
3998
4147
  active2 = false;
3999
4148
  clearTimeout(timer);
4000
4149
  };
4001
- }, [billingMs, configReady, accountsKey]);
4150
+ }, [billingMs, configReady, showPicker, accountsKey]);
4002
4151
  const tableKey = `${effTableProvider}|${tableAccounts.map((a) => `${a.id}:${a.homeDir ?? ""}`).join(",")}|${tz}`;
4003
4152
  useEffect3(() => {
4004
4153
  setTable(null);
@@ -4636,7 +4785,8 @@ function App({ interval: cliInterval }) {
4636
4785
  }
4637
4786
  }
4638
4787
  )
4639
- ] })
4788
+ ] }),
4789
+ /* @__PURE__ */ jsx7(TotalsRow, { groups, stats, cols })
4640
4790
  ] }),
4641
4791
  tab === 1 && /* @__PURE__ */ jsxs7(Fragment4, { children: [
4642
4792
  tableProvs.length > 0 && /* @__PURE__ */ jsx7(TableProviderBar, { providers: tableProvs, active: effTableProvider, onSelect: (p) => {
@@ -4775,9 +4925,9 @@ function Footer({ hasAccounts, paginated, cols }) {
4775
4925
  const showPage = paginated && inner >= BASE2 + (showJump ? JUMP : 0) + PAGE;
4776
4926
  return /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexWrap: "nowrap", children: [
4777
4927
  /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "by " }),
4778
- /* @__PURE__ */ jsx7(Text7, { children: "David Ilie" }),
4928
+ /* @__PURE__ */ jsx7(Transform, { transform: (s) => osc8(s, REPO_URL), children: /* @__PURE__ */ jsx7(Text7, { children: "David Ilie" }) }),
4779
4929
  /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " (" }),
4780
- /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: "davidilie.com" }),
4930
+ /* @__PURE__ */ jsx7(Transform, { transform: (s) => osc8(s, "https://davidilie.com"), children: /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: "davidilie.com" }) }),
4781
4931
  /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
4782
4932
  ") ",
4783
4933
  glyphs().middot,
@@ -4873,8 +5023,16 @@ for (let i = 0; i < args.length; i++) {
4873
5023
  }
4874
5024
  }
4875
5025
  var config = await loadConfig();
4876
- if (config.clearScreen && process.stdout.isTTY) {
4877
- process.stdout.write("\x1B[2J\x1B[H");
5026
+ var altScreen = config.clearScreen && process.stdout.isTTY === true;
5027
+ var leaveAltScreen = () => {
5028
+ try {
5029
+ process.stdout.write("\x1B[?1049l");
5030
+ } catch {
5031
+ }
5032
+ };
5033
+ if (altScreen) {
5034
+ process.stdout.write("\x1B[?1049h\x1B[H");
5035
+ process.once("exit", leaveAltScreen);
4878
5036
  }
4879
5037
  setGlyphs(resolveGlyphs({
4880
5038
  flag: asciiFlag,
@@ -4883,6 +5041,7 @@ setGlyphs(resolveGlyphs({
4883
5041
  isTTY: !!process.stdout.isTTY,
4884
5042
  platform: process.platform
4885
5043
  }));
4886
- var { waitUntilExit } = render(/* @__PURE__ */ jsx8(MouseProvider, { children: /* @__PURE__ */ jsx8(App, { interval }) }));
5044
+ var { waitUntilExit } = render(/* @__PURE__ */ jsx8(MouseProvider, { children: /* @__PURE__ */ jsx8(App, { interval, initialConfig: config }) }));
4887
5045
  await waitUntilExit();
4888
5046
  await flushDisk();
5047
+ if (altScreen) leaveAltScreen();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokmon",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "Terminal dashboard for Claude Code, Codex, and Cursor usage, limits, and costs",
5
5
  "type": "module",
6
6
  "bin": {