tokens-metric 0.4.4 → 0.4.6

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.
@@ -14,6 +14,9 @@ export function codexHome() {
14
14
  export function codexSessionsDir() {
15
15
  return join(codexHome(), 'sessions');
16
16
  }
17
+ export function isCodexInstalled() {
18
+ return existsSync(codexHome());
19
+ }
17
20
  /**
18
21
  * Detect whether Claude Code is installed, the user is logged in, and best-
19
22
  * effort what plan they're on. All signals are LOCAL and BEST-EFFORT — we
@@ -36,6 +36,10 @@ function isNewer(latest, current) {
36
36
  return (lMin ?? 0) > (cMin ?? 0);
37
37
  return (lPat ?? 0) > (cPat ?? 0);
38
38
  }
39
+ /** Write the cache so the next startup skips the network call and shows no prompt. */
40
+ export function markUpToDate(version) {
41
+ writeCache(version);
42
+ }
39
43
  /**
40
44
  * Checks npm registry for a newer version. Non-blocking — uses a 24h cache
41
45
  * so most startups skip the network call entirely.
package/dist/tui/index.js CHANGED
@@ -6,14 +6,14 @@ import { createInterface } from 'node:readline';
6
6
  import { spawnSync } from 'node:child_process';
7
7
  import { findActiveTranscript, findActiveCodexTranscript, listTranscripts } from '../core/parser.js';
8
8
  import { tailTranscript } from '../core/tailer.js';
9
- import { detectAuth } from '../core/detect.js';
9
+ import { detectAuth, isCodexInstalled } from '../core/detect.js';
10
10
  import { categoryCostUSD, contextWindowSize, estimateCostUSD, fmtNumber, fmtUSD } from '../core/format.js';
11
11
  import { buildHistory, bucketCostUSD, bucketTokens, bucketTopModel, getTodaySessions, } from '../core/history.js';
12
12
  import { totalTokens } from '../core/types.js';
13
13
  import { anonymizePath } from '../core/privacy.js';
14
14
  import { createRequire } from 'node:module';
15
15
  import { HELP_TEXT, parseArgs } from '../core/args.js';
16
- import { checkForUpdate } from '../core/updater.js';
16
+ import { checkForUpdate, markUpToDate } from '../core/updater.js';
17
17
  const require = createRequire(import.meta.url);
18
18
  const pkg = require('../../package.json');
19
19
  import { bar, sparklineCells } from './bars.js';
@@ -29,6 +29,7 @@ const BAR_WIDTH = 20;
29
29
  function App() {
30
30
  const { exit } = useApp();
31
31
  const [auth] = useState(() => detectAuth());
32
+ const [codexDetected] = useState(() => isCodexInstalled());
32
33
  // Claude source
33
34
  const [claudeStats, setClaudeStats] = useState(null);
34
35
  const [claudePath, setClaudePath] = useState(null);
@@ -235,19 +236,19 @@ function App() {
235
236
  const lastTailAt = claudeLastTailAt && codexLastTailAt
236
237
  ? Math.max(claudeLastTailAt, codexLastTailAt)
237
238
  : claudeLastTailAt ?? codexLastTailAt;
238
- return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Header, { auth: auth, sessionsToday: today.sessions, projectsToday: today.projects, lastTailAt: lastTailAt, startedAt: startedAtRef.current, now: now, updateAvailable: updateAvailable }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [claudeStats
239
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Header, { auth: auth, codexDetected: codexDetected, sessionsToday: today.sessions, projectsToday: today.projects, lastTailAt: lastTailAt, startedAt: startedAtRef.current, now: now, updateAvailable: updateAvailable }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [claudeStats
239
240
  ? _jsx(SessionStatusBar, { stats: claudeStats, ratePerSec: claudeRate, now: now, series: claudeSeries })
240
241
  : !codexStats && _jsx(SessionStatusBar, { stats: null, ratePerSec: 0, now: now, series: claudeSeries }), codexStats && (_jsx(Box, { marginTop: claudeStats ? 1 : 0, children: _jsx(SessionStatusBar, { stats: codexStats, ratePerSec: codexRate, now: now, series: codexSeries }) }))] }), _jsx(Box, { marginTop: 1, children: _jsx(TabBar, { focusedTab: focusedTab, openTab: openTab }) }), openTab !== null && (_jsxs(Box, { marginTop: 1, children: [openTab === 1 && (_jsx(BreakdownPanel, { stats: primaryStats, series: primarySeries, ratePerSec: primaryRate })), openTab === 2 && _jsx(HistoryPanel, { history: history }), openTab === 3 && _jsx(TodaySessionsPanel, { sessions: todaySessions, now: now }), openTab === 4 && (_jsx(TranscriptsPanel, { transcripts: transcripts, activePath: claudePath, now: now }))] })), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: [_jsx(Text, { color: "magenta", children: "q" }), " quit \u00B7", ' ', _jsx(Text, { color: "magenta", children: "\u2190\u2192" }), " move \u00B7", ' ', _jsx(Text, { color: "magenta", children: "enter" }), " open/close \u00B7", ' ', _jsx(Text, { color: "magenta", children: "1\u20134" }), " jump \u00B7 pricing is", ' ', _jsx(Text, { italic: true, children: "API-equivalent" }), ", not your real bill on a subscription"] }) })] }));
241
242
  }
242
243
  // ── Header ───────────────────────────────────────────────────────────────────
243
- function Header({ auth, sessionsToday, projectsToday, lastTailAt, startedAt, now, updateAvailable, }) {
244
+ function Header({ auth, codexDetected, sessionsToday, projectsToday, lastTailAt, startedAt, now, updateAvailable, }) {
244
245
  const ok = auth.installed && auth.loggedIn;
245
246
  const dot = ok ? 'green' : auth.installed ? 'yellow' : 'red';
246
247
  const tailAgo = lastTailAt ? `updated ${timeAgo(now - lastTailAt)} ago` : 'waiting…';
247
248
  const tailIsLive = !!lastTailAt && now - lastTailAt < 10_000;
248
249
  const tailColor = !lastTailAt ? 'gray' : tailIsLive ? 'green' : 'yellow';
249
250
  const tailStatusText = !lastTailAt ? '' : tailIsLive ? '● live' : '⚠ stale';
250
- return (_jsxs(Box, { borderStyle: "round", borderColor: "cyan", paddingX: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "cyan", children: "\u258C tokens-metric " }), _jsxs(Text, { dimColor: true, children: ["v", pkg.version, " \u2014 real-time Claude Code usage"] })] }), _jsx(Box, { marginTop: 0, children: _jsxs(Text, { children: [_jsx(Text, { color: dot, children: "\u25CF" }), ' ', auth.installed ? 'Claude Code detected' : 'Claude Code NOT detected', _jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Text, { children: sessionsToday }), _jsx(Text, { dimColor: true, children: ` ${plural(sessionsToday, 'session', 'sessions')} · ` }), _jsx(Text, { children: projectsToday }), _jsx(Text, { dimColor: true, children: ` ${plural(projectsToday, 'project', 'projects')} today` })] }) }), _jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "watching ~/.claude/projects \u00B7 " }), tailStatusText ? _jsxs(Text, { color: tailColor, children: [tailStatusText, " "] }) : null, _jsx(Text, { dimColor: true, children: tailAgo }), _jsx(Text, { dimColor: true, children: ' · uptime ' }), _jsx(Text, { children: timeAgo(now - startedAt) })] }), updateAvailable && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: "\u26A1 Update available: " }), _jsxs(Text, { dimColor: true, children: ["v", pkg.version] }), _jsx(Text, { color: "yellow", children: " \u2192 " }), _jsxs(Text, { color: "yellow", bold: true, children: ["v", updateAvailable] }), _jsx(Text, { dimColor: true, children: " npm install -g tokens-metric" })] }))] }));
251
+ return (_jsxs(Box, { borderStyle: "round", borderColor: "cyan", paddingX: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "cyan", children: "\u258C tokens-metric " }), _jsxs(Text, { dimColor: true, children: ["v", pkg.version, " \u2014 real-time Claude Code usage"] })] }), _jsx(Box, { marginTop: 0, children: _jsxs(Text, { children: [_jsx(Text, { color: dot, children: "\u25CF" }), ' ', auth.installed ? 'Claude Code detected' : 'Claude Code NOT detected', _jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Text, { color: codexDetected ? 'green' : 'red', children: "\u25CF" }), ' ', codexDetected ? 'Codex detected' : 'Codex NOT detected', _jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Text, { children: sessionsToday }), _jsx(Text, { dimColor: true, children: ` ${plural(sessionsToday, 'session', 'sessions')} · ` }), _jsx(Text, { children: projectsToday }), _jsx(Text, { dimColor: true, children: ` ${plural(projectsToday, 'project', 'projects')} today` })] }) }), _jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "watching ~/.claude/projects \u00B7 " }), tailStatusText ? _jsxs(Text, { color: tailColor, children: [tailStatusText, " "] }) : null, _jsx(Text, { dimColor: true, children: tailAgo }), _jsx(Text, { dimColor: true, children: ' · uptime ' }), _jsx(Text, { children: timeAgo(now - startedAt) })] }), updateAvailable && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: "\u26A1 Update available: " }), _jsxs(Text, { dimColor: true, children: ["v", pkg.version] }), _jsx(Text, { color: "yellow", children: " \u2192 " }), _jsxs(Text, { color: "yellow", bold: true, children: ["v", updateAvailable] }), _jsx(Text, { dimColor: true, children: " npm install -g tokens-metric" })] }))] }));
251
252
  }
252
253
  function countToday(transcripts, now) {
253
254
  const startOfDay = new Date(now);
@@ -473,6 +474,7 @@ async function promptForUpdate() {
473
474
  shell: true,
474
475
  });
475
476
  if (result.status === 0) {
477
+ markUpToDate(latest);
476
478
  process.stdout.write(`\n✓ Updated to v${latest} — restarting…\n\n`);
477
479
  spawnSync(process.execPath, process.argv.slice(1), { stdio: 'inherit', shell: false });
478
480
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokens-metric",
3
- "version": "0.4.4",
3
+ "version": "0.4.6",
4
4
  "description": "Real-time token usage meter for Claude Code — statusline + Ink TUI.",
5
5
  "type": "module",
6
6
  "bin": {