tokenflexing 1.1.0 → 1.2.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.
- package/README.md +155 -86
- package/package.json +1 -1
- package/src/cli.js +119 -16
package/README.md
CHANGED
|
@@ -1,138 +1,207 @@
|
|
|
1
|
-
#
|
|
1
|
+
# tokenflexing
|
|
2
2
|
|
|
3
|
-
Show off your AI token usage. Universal CLI for Claude Code, Codex, Cursor, and
|
|
3
|
+
> Show off your AI token usage. Universal CLI for Claude Code, Codex, Cursor, OpenCode, and 20+ other AI tools.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/tokenflexing)
|
|
6
|
+
[](https://www.npmjs.com/package/tokenflexing)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
[](#license)
|
|
4
9
|
|
|
5
10
|
```bash
|
|
6
|
-
npx
|
|
11
|
+
npx tokenflexing@latest scan
|
|
7
12
|
```
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
`tokenflexing` is a pure-local scanner that finds every AI coding tool on your machine, extracts token counts and cost where the data is parseable, and surfaces the rest as "detected but no telemetry yet". It never reads your prompts or model completions — only the usage/token blocks already written to disk by the tools themselves.
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
It also ships an [MCP server](https://modelcontextprotocol.io), a `login`/`sync` pair for pushing snapshots to your [tokenflexing.com](https://tokenflexing.com) profile, and an `--install` flag that wires a daily auto-refresh into `launchd` (macOS) or Task Scheduler (Windows).
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
|---|---|
|
|
15
|
-
| `tokenflex` / `tokenflex stats` | Today / 7-day / 30-day / all-time dollar + token spend per source, per model |
|
|
16
|
-
| `tokenflex scan` | Universal device inventory — measured, detected, and not-found in one view |
|
|
17
|
-
| `tokenflex flex` | Shareable ASCII card — drop in tweets, HN comments, or Slack |
|
|
18
|
-
| `tokenflex daemon` | Preview the daily auto-refresh LaunchAgent (macOS) or Task Scheduler entry (Windows) |
|
|
19
|
-
| `tokenflex daemon --install` | Install the daemon (runs `tokenflex scan` at 6:00 AM daily) |
|
|
20
|
-
| `tokenflex daemon --uninstall` | Remove the daemon |
|
|
21
|
-
| `tokenflex mcp` | Start MCP server — Claude Code, Cursor, and any MCP-capable agent can call tokenflex tools natively |
|
|
22
|
-
| `tokenflex install-hooks --apply` | Register tokenflex as an MCP server in Claude Code or Cursor settings |
|
|
23
|
-
| `tokenflex --help` | Usage |
|
|
24
|
-
|
|
25
|
-
## What `tokenflex scan` detects
|
|
26
|
-
|
|
27
|
-
| Tool | Status |
|
|
28
|
-
|---|---|
|
|
29
|
-
| **Claude Code** | ✅ Token counts + cost extracted (`~/.claude/projects/**/*.jsonl`) |
|
|
30
|
-
| **Codex CLI** | ✅ Token counts + cost extracted (`~/.codex/sessions/**/*.jsonl`) |
|
|
31
|
-
| **Cursor** | 🔍 Detected — SQLite `state.vscdb` reader planned |
|
|
32
|
-
| **Claude Desktop** | 🔍 Detected — Electron, no token events exposed |
|
|
33
|
-
| **ChatGPT Desktop** | 🔍 Detected — Electron, conversation data only |
|
|
34
|
-
| **Windsurf (Codeium)** | 🔍 Detected — structured token log not exposed yet |
|
|
35
|
-
| **Continue.dev** | 🔍 Detected — telemetry parser planned |
|
|
36
|
-
| **Aider** | 🔍 Detected — cost-line parser planned |
|
|
37
|
-
| **Cline** | 🔍 Detected — `ui_messages.json` task parser planned |
|
|
38
|
-
| **Roo Code** | 🔍 Detected — `ui_messages.json` task parser planned |
|
|
39
|
-
| **Kilo Code** | 🔍 Detected — `ui_messages.json` task parser planned |
|
|
40
|
-
| **Zed AI** | 🔍 Detected — SQLite `threads.db` reader planned |
|
|
41
|
-
| **Gemini CLI** | 🔍 Detected — `~/.gemini/tmp/*.jsonl` parser planned |
|
|
42
|
-
| **OpenCode** | 🔍 Detected — `opencode/storage/message/*.json` parser planned |
|
|
43
|
-
| **Amp** | 🔍 Detected — `amp/threads/T-*.json` parser planned |
|
|
44
|
-
| **Antigravity (Google)** | 🔍 Detected — session cache format TBD |
|
|
45
|
-
| **GitHub Copilot** | 🔍 Detected — OTEL trace extraction planned |
|
|
46
|
-
| **Goose (Block)** | 🔍 Detected — SQLite `sessions.db` reader planned |
|
|
47
|
-
| **Kiro (Amazon)** | 🔍 Detected — `~/.kiro/sessions/cli/*.json` parser planned |
|
|
48
|
-
| **Mux** | 🔍 Detected — `session-usage.json` parser planned |
|
|
49
|
-
| **OpenClaw** | 🔍 Detected — JSONL agent sessions parser planned |
|
|
50
|
-
| **Crush** | 🔍 Detected — `projects.json` parser planned |
|
|
51
|
-
| **Kimi (Moonshot)** | 🔍 Detected — `wire.jsonl` parser planned |
|
|
52
|
-
| **Hermes** | 🔍 Detected — SQLite `state.db` reader planned |
|
|
53
|
-
|
|
54
|
-
macOS, Windows, and Linux (XDG) paths covered.
|
|
18
|
+
---
|
|
55
19
|
|
|
56
|
-
##
|
|
20
|
+
## Contents
|
|
21
|
+
|
|
22
|
+
- [Install](#install)
|
|
23
|
+
- [Commands](#commands)
|
|
24
|
+
- [What `scan` detects](#what-scan-detects)
|
|
25
|
+
- [Cloud sync](#cloud-sync)
|
|
26
|
+
- [MCP server](#mcp-server)
|
|
27
|
+
- [Daily auto-refresh](#daily-auto-refresh)
|
|
28
|
+
- [Privacy](#privacy)
|
|
29
|
+
- [Why local files?](#why-local-files)
|
|
30
|
+
- [Changelog](#changelog)
|
|
31
|
+
- [License](#license)
|
|
57
32
|
|
|
58
|
-
|
|
33
|
+
---
|
|
59
34
|
|
|
60
35
|
## Install
|
|
61
36
|
|
|
62
37
|
```bash
|
|
63
|
-
# one-shot
|
|
64
|
-
npx
|
|
38
|
+
# one-shot, no install
|
|
39
|
+
npx tokenflexing@latest scan
|
|
65
40
|
|
|
66
41
|
# or install globally
|
|
67
|
-
npm install -g
|
|
68
|
-
|
|
42
|
+
npm install -g tokenflexing
|
|
43
|
+
tokenflexing scan
|
|
69
44
|
```
|
|
70
45
|
|
|
71
|
-
Requires Node ≥ 18.
|
|
46
|
+
Requires **Node ≥ 18**. Works on macOS, Windows, and Linux (XDG paths).
|
|
72
47
|
|
|
73
|
-
|
|
48
|
+
---
|
|
74
49
|
|
|
75
|
-
|
|
50
|
+
## Commands
|
|
76
51
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
52
|
+
| Command | What it does |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `tokenflexing` *(default)* | Today / 7-day / 30-day / all-time dollar + token totals. |
|
|
55
|
+
| `tokenflexing stats` | Same as above, with a per-model breakdown for every source. |
|
|
56
|
+
| `tokenflexing scan` | Full device inventory — measured, detected, and not-found tools side by side. |
|
|
57
|
+
| `tokenflexing flex` | Shareable ASCII card for X, HN, or Slack. |
|
|
58
|
+
| `tokenflexing login` | Browser-based pairing with your tokenflexing.com account. |
|
|
59
|
+
| `tokenflexing login --token <tok>` | Skip the browser flow — paste a token from tokenflexing.com/settings. |
|
|
60
|
+
| `tokenflexing sync` | Push current local stats to your public profile. |
|
|
61
|
+
| `tokenflexing mcp` | Start the MCP stdio server. |
|
|
62
|
+
| `tokenflexing install-hooks --apply` | Register `tokenflexing` as an MCP server in Claude Code (default) or Cursor (`--client cursor`). |
|
|
63
|
+
| `tokenflexing daemon` | Preview the daily-refresh LaunchAgent / Task Scheduler entry. |
|
|
64
|
+
| `tokenflexing daemon --install` | Install it (runs `tokenflexing scan` at 6:00 AM daily via `npx`). |
|
|
65
|
+
| `tokenflexing daemon --uninstall` | Remove the daemon. |
|
|
66
|
+
| `tokenflexing --version` | Print the installed version. |
|
|
67
|
+
| `tokenflexing --help` | Full usage. |
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## What `scan` detects
|
|
72
|
+
|
|
73
|
+
### Fully measured (token counts + cost extracted)
|
|
74
|
+
|
|
75
|
+
| Tool | Source |
|
|
76
|
+
|---|---|
|
|
77
|
+
| **Claude Code** | `~/.claude/projects/**/*.jsonl` |
|
|
78
|
+
| **Codex CLI** | `~/.codex/sessions/**/*.jsonl` |
|
|
79
|
+
| **OpenCode** | `~/.local/share/opencode/opencode.db` (when sessions exist) |
|
|
80
|
+
|
|
81
|
+
### Detected (presence + path, telemetry parser in progress)
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
npx tokenflex@latest install-hooks --apply
|
|
83
|
+
Cursor · Claude Desktop · ChatGPT Desktop · Windsurf · Continue.dev · Aider · Cline · Roo Code · Kilo Code · Zed AI · Gemini CLI · Amp · Antigravity (Google) · GitHub Copilot · Goose (Block) · Kiro (Amazon) · Mux · OpenClaw · Crush · Kimi (Moonshot) · Hermes
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
npx tokenflex@latest install-hooks --client cursor --apply
|
|
85
|
+
Cursor is intentionally turn-count only because Cursor is subscription-based and stores no per-message token data locally.
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Cloud sync
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# one-time browser login — opens https://tokenflexing.com/cli-auth
|
|
93
|
+
npx tokenflexing@latest login
|
|
94
|
+
|
|
95
|
+
# push your local stats to your public profile + the global leaderboard
|
|
96
|
+
npx tokenflexing@latest sync
|
|
89
97
|
```
|
|
90
98
|
|
|
91
|
-
|
|
99
|
+
The token is stored at `~/.config/tokenflexing/token` (or `%APPDATA%/tokenflexing/token` on Windows). You only log in once; the token persists across reboots and only needs to be re-issued if you revoke it from [tokenflexing.com/settings](https://tokenflexing.com/settings).
|
|
92
100
|
|
|
93
|
-
|
|
94
|
-
|---|---|
|
|
95
|
-
| `get_my_stats` | Token spend by period and model |
|
|
96
|
-
| `get_device_scan` | Which AI tools are installed + measured |
|
|
97
|
-
| `get_flex_card` | Shareable one-liner for X / HN / Slack |
|
|
101
|
+
`sync` reads the same data that `scan` produces and POSTs a snapshot per source to `/api/sync/device`. The web app stores it in the `device_stats` table keyed by `(user_id, source)`, and the dashboard surfaces rows from every device you've paired — so usage from your laptop, desktop, and CI box all roll up into one profile.
|
|
98
102
|
|
|
99
|
-
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## MCP server
|
|
106
|
+
|
|
107
|
+
Make your AI agent self-aware about how much it costs to run. Register `tokenflexing` as an MCP server in Claude Code, Cursor, or any other [MCP-capable](https://modelcontextprotocol.io) client:
|
|
100
108
|
|
|
101
109
|
```bash
|
|
102
|
-
|
|
110
|
+
# preview what will be written (no changes yet)
|
|
111
|
+
npx tokenflexing@latest install-hooks
|
|
112
|
+
|
|
113
|
+
# Claude Code: writes to ~/.claude/settings.json
|
|
114
|
+
npx tokenflexing@latest install-hooks --apply
|
|
115
|
+
|
|
116
|
+
# Cursor: writes to ~/.cursor/mcp.json
|
|
117
|
+
npx tokenflexing@latest install-hooks --client cursor --apply
|
|
118
|
+
|
|
119
|
+
# project-local: writes to .claude/settings.json in the current directory
|
|
120
|
+
npx tokenflexing@latest install-hooks --apply --project
|
|
103
121
|
```
|
|
104
122
|
|
|
105
|
-
|
|
123
|
+
After restarting your editor, your agent has three new tools:
|
|
124
|
+
|
|
125
|
+
| Tool | Use it when the user asks |
|
|
126
|
+
|---|---|
|
|
127
|
+
| `get_my_stats` | "How much did I burn on AI this week?" |
|
|
128
|
+
| `get_device_scan` | "Which AI tools do I have installed?" |
|
|
129
|
+
| `get_flex_card` | "Show me a card I can post." |
|
|
130
|
+
|
|
131
|
+
Manual MCP config (if you prefer editing JSON):
|
|
106
132
|
|
|
107
133
|
```json
|
|
108
134
|
{
|
|
109
135
|
"mcpServers": {
|
|
110
|
-
"
|
|
136
|
+
"tokenflexing": {
|
|
137
|
+
"command": "npx",
|
|
138
|
+
"args": ["-y", "tokenflexing@latest", "mcp"],
|
|
139
|
+
"type": "stdio"
|
|
140
|
+
}
|
|
111
141
|
}
|
|
112
142
|
}
|
|
113
143
|
```
|
|
114
144
|
|
|
145
|
+
Or run the server directly for custom integrations:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npx tokenflexing@latest mcp
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
115
153
|
## Daily auto-refresh
|
|
116
154
|
|
|
117
155
|
```bash
|
|
118
|
-
# macOS: installs a LaunchAgent
|
|
119
|
-
|
|
156
|
+
# macOS: installs a LaunchAgent at ~/Library/LaunchAgents/com.tokenflexing.sync.plist
|
|
157
|
+
tokenflexing daemon --install
|
|
120
158
|
|
|
121
|
-
# Windows: creates a Task Scheduler entry
|
|
122
|
-
|
|
159
|
+
# Windows: creates a Task Scheduler entry "Tokenflexing Sync"
|
|
160
|
+
tokenflexing daemon --install
|
|
123
161
|
|
|
124
|
-
#
|
|
125
|
-
|
|
162
|
+
# remove it on either platform
|
|
163
|
+
tokenflexing daemon --uninstall
|
|
126
164
|
```
|
|
127
165
|
|
|
128
|
-
|
|
166
|
+
The scheduled job invokes `npx tokenflexing@latest scan` rather than a hard-coded local path, so it keeps working across upgrades, across `node` versions, and on machines that don't have the package installed globally. Logs land at `/tmp/tokenflexing-sync.log` on macOS / Linux.
|
|
167
|
+
|
|
168
|
+
---
|
|
129
169
|
|
|
130
170
|
## Privacy
|
|
131
171
|
|
|
132
|
-
- No network calls
|
|
133
|
-
-
|
|
134
|
-
-
|
|
172
|
+
- **No network calls** during `scan`, `stats`, or `flex` — pure local read.
|
|
173
|
+
- `login` and `sync` only contact `tokenflexing.com`.
|
|
174
|
+
- Only **usage blocks** (token counts, model ids, timestamps) are parsed — never message content, never prompts, never completions.
|
|
175
|
+
- The MCP server returns only the same aggregated numbers; it never reads your conversation history.
|
|
176
|
+
- The daemon logs to `/tmp/tokenflexing-sync.log` and nowhere else.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Why local files?
|
|
181
|
+
|
|
182
|
+
Provider Admin APIs (Anthropic, OpenAI) require **organization admin** keys that individual developers don't have. From the Anthropic docs verbatim: *"The Admin API is unavailable for individual accounts."* OpenAI: *"Only Organization Owners can create and use Admin API keys."*
|
|
183
|
+
|
|
184
|
+
Local session JSONL and SQLite files contain per-turn usage data for **anyone** running Claude Code, Codex CLI, or OpenCode — on any plan, with no org key required. That's why `scan` parses files first and only falls back to API connectors when you explicitly add one.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Changelog
|
|
189
|
+
|
|
190
|
+
### 1.1.0 — 2026-05-17
|
|
191
|
+
|
|
192
|
+
- Renamed package from `tokenflex` to `tokenflexing` (the original name was taken on npm).
|
|
193
|
+
- **Fixed daemon path bug**: the LaunchAgent / Task Scheduler plist used to embed the developer's local file path resolved from `import.meta.url`, which never existed for end users. The plist now invokes `npx tokenflexing@latest scan`, derived from the same directory as `process.execPath` so it works on any host.
|
|
194
|
+
- Cloud sync (`login` + `sync`) promoted from "ships in v0.3" to live.
|
|
195
|
+
- OpenCode SQLite reader added; Cursor detection reports turn counts (Cursor is subscription-based).
|
|
196
|
+
- Config directory moved from `~/.config/tokenflex` to `~/.config/tokenflexing`.
|
|
197
|
+
- All help text, log messages, and MCP entry names updated to the new binary.
|
|
198
|
+
|
|
199
|
+
### 0.3.0 — 2026-05-16
|
|
200
|
+
|
|
201
|
+
Initial preview release as `tokenflex` (now superseded by `tokenflexing@1.1.0`).
|
|
202
|
+
|
|
203
|
+
---
|
|
135
204
|
|
|
136
205
|
## License
|
|
137
206
|
|
|
138
|
-
MIT
|
|
207
|
+
MIT © 2026 Khadin Akbar
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -11,8 +11,9 @@ import {
|
|
|
11
11
|
import { join, dirname } from "node:path";
|
|
12
12
|
import { homedir, userInfo } from "node:os";
|
|
13
13
|
import { execFileSync } from "node:child_process";
|
|
14
|
-
//
|
|
15
|
-
|
|
14
|
+
// commandLogin currently uses browser-based polling rather than interactive
|
|
15
|
+
// stdin, so node:readline is intentionally not imported. Re-add if/when we
|
|
16
|
+
// reintroduce an interactive `--token` prompt fallback.
|
|
16
17
|
|
|
17
18
|
/* ───── Platform ───────────────────────────────────────────────── */
|
|
18
19
|
|
|
@@ -459,7 +460,7 @@ function commandMcp() {
|
|
|
459
460
|
function handle(req) {
|
|
460
461
|
const { method, id, params } = req;
|
|
461
462
|
if (method === "initialize") {
|
|
462
|
-
send({ jsonrpc: "2.0", id, result: { protocolVersion: "2024-11-05", capabilities: { tools: {} }, serverInfo: { name: "tokenflexing", version: "1.
|
|
463
|
+
send({ jsonrpc: "2.0", id, result: { protocolVersion: "2024-11-05", capabilities: { tools: {} }, serverInfo: { name: "tokenflexing", version: "1.2.0" } } });
|
|
463
464
|
} else if (method === "notifications/initialized" || method === "ping") {
|
|
464
465
|
if (id !== undefined) send({ jsonrpc: "2.0", id, result: {} });
|
|
465
466
|
} else if (method === "tools/list") {
|
|
@@ -919,23 +920,39 @@ async function commandSync() {
|
|
|
919
920
|
console.log(`${c.bold}tokenflexing sync${c.reset}`);
|
|
920
921
|
console.log("");
|
|
921
922
|
|
|
922
|
-
// Collect
|
|
923
|
-
|
|
923
|
+
// Collect every detected source (measured + detection-only). Detection-only
|
|
924
|
+
// rows ship with zero counts and `detected: true` so the dashboard knows
|
|
925
|
+
// the tool is installed even though we can't measure its tokens yet.
|
|
926
|
+
const sources = collectAllSources();
|
|
924
927
|
if (!sources.length) {
|
|
925
|
-
console.log(`${c.faint}Nothing to sync — no
|
|
928
|
+
console.log(`${c.faint}Nothing to sync — no AI tools detected on this device.${c.reset}`);
|
|
926
929
|
console.log("");
|
|
927
930
|
return;
|
|
928
931
|
}
|
|
929
932
|
|
|
930
|
-
const scans = sources.map((s) =>
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
933
|
+
const scans = sources.map((s) => {
|
|
934
|
+
if (s.detected) {
|
|
935
|
+
return {
|
|
936
|
+
source: s.id,
|
|
937
|
+
detected: true,
|
|
938
|
+
events: 0,
|
|
939
|
+
input: 0,
|
|
940
|
+
output: 0,
|
|
941
|
+
cached: 0,
|
|
942
|
+
cost: 0,
|
|
943
|
+
models: {},
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
return {
|
|
947
|
+
source: s.id,
|
|
948
|
+
events: s.stats.events,
|
|
949
|
+
input: s.stats.input,
|
|
950
|
+
output: s.stats.output,
|
|
951
|
+
cached: s.stats.cached,
|
|
952
|
+
cost: s.stats.cost,
|
|
953
|
+
models: Object.fromEntries(s.stats.models),
|
|
954
|
+
};
|
|
955
|
+
});
|
|
939
956
|
|
|
940
957
|
let res;
|
|
941
958
|
try {
|
|
@@ -967,12 +984,97 @@ function collectSources() {
|
|
|
967
984
|
.map((r) => ({ id: r.id, name: r.name, stats: r.stats }));
|
|
968
985
|
}
|
|
969
986
|
|
|
970
|
-
|
|
987
|
+
/**
|
|
988
|
+
* Return every source the scan touched: measured (with token data) AND
|
|
989
|
+
* detection-only (presence confirmed but no usage parser yet). Detection-only
|
|
990
|
+
* entries get zero counts and a `detected: true` flag so the web dashboard can
|
|
991
|
+
* surface them as "installed, telemetry coming" rows instead of silently
|
|
992
|
+
* dropping them.
|
|
993
|
+
*/
|
|
994
|
+
function collectAllSources() {
|
|
995
|
+
return runScan()
|
|
996
|
+
.filter((r) => r.found)
|
|
997
|
+
.map((r) => ({
|
|
998
|
+
id: r.id,
|
|
999
|
+
name: r.name,
|
|
1000
|
+
detected: !r.hasData,
|
|
1001
|
+
stats: r.hasData ? r.stats : null,
|
|
1002
|
+
}));
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* `tokenflexing connect` — the one-command onboarding flow.
|
|
1007
|
+
* Bundles scan + login (if needed) + sync + a friendly nudge to install the
|
|
1008
|
+
* daily-refresh daemon. Targeted at first-time users who don't want to read
|
|
1009
|
+
* the full command reference.
|
|
1010
|
+
*/
|
|
1011
|
+
async function commandConnect() {
|
|
1012
|
+
console.log("");
|
|
1013
|
+
console.log(`${c.bold}tokenflexing connect${c.reset} ${c.faint}— one-command setup${c.reset}`);
|
|
1014
|
+
console.log("");
|
|
1015
|
+
|
|
1016
|
+
// ── Step 1: scan ────────────────────────────────────────────────
|
|
1017
|
+
console.log(`${c.faint}Step 1/3 · Scanning this machine for AI tools…${c.reset}`);
|
|
1018
|
+
const scan = runScan();
|
|
1019
|
+
const measured = scan.filter((r) => r.hasData);
|
|
1020
|
+
const detected = scan.filter((r) => r.found && !r.hasData);
|
|
1021
|
+
|
|
1022
|
+
if (!measured.length && !detected.length) {
|
|
1023
|
+
console.log("");
|
|
1024
|
+
console.log(`${c.faint}No AI tools detected on this device.${c.reset}`);
|
|
1025
|
+
console.log(`${c.faint}Install Claude Code, Codex CLI, Cursor, OpenCode, Antigravity,${c.reset}`);
|
|
1026
|
+
console.log(`${c.faint}Windsurf, Aider, or any of the 20+ supported tools and re-run.${c.reset}`);
|
|
1027
|
+
console.log("");
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
console.log(`${c.green}✓${c.reset} Found ${c.bold}${measured.length}${c.reset} measured · ${c.bold}${detected.length}${c.reset} detected`);
|
|
1032
|
+
for (const m of measured) {
|
|
1033
|
+
const cost = m.stats?.cost ?? 0;
|
|
1034
|
+
console.log(` ${c.green}●${c.reset} ${pad(m.name, 22)} ${c.faint}${fmtUsd(cost)} all-time${c.reset}`);
|
|
1035
|
+
}
|
|
1036
|
+
for (const d of detected) {
|
|
1037
|
+
console.log(` ${c.yellow}◎${c.reset} ${pad(d.name, 22)} ${c.faint}detected · telemetry parser pending${c.reset}`);
|
|
1038
|
+
}
|
|
1039
|
+
console.log("");
|
|
1040
|
+
|
|
1041
|
+
// ── Step 2: login if needed ─────────────────────────────────────
|
|
1042
|
+
let token = loadToken();
|
|
1043
|
+
if (!token) {
|
|
1044
|
+
console.log(`${c.faint}Step 2/3 · Pairing this device with tokenflexing.com…${c.reset}`);
|
|
1045
|
+
await commandLogin([]);
|
|
1046
|
+
token = loadToken();
|
|
1047
|
+
if (!token) {
|
|
1048
|
+
console.log(`${c.faint}Connect aborted — could not pair this device.${c.reset}`);
|
|
1049
|
+
console.log("");
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
} else {
|
|
1053
|
+
console.log(`${c.faint}Step 2/3 · Already paired with tokenflexing.com${c.reset} ${c.green}✓${c.reset}`);
|
|
1054
|
+
console.log("");
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// ── Step 3: sync ────────────────────────────────────────────────
|
|
1058
|
+
console.log(`${c.faint}Step 3/3 · Pushing stats to your profile…${c.reset}`);
|
|
1059
|
+
await commandSync();
|
|
1060
|
+
|
|
1061
|
+
// ── Daemon nudge ────────────────────────────────────────────────
|
|
1062
|
+
console.log(`${c.bold}You're connected.${c.reset}`);
|
|
1063
|
+
console.log("");
|
|
1064
|
+
console.log(`Optional next steps:`);
|
|
1065
|
+
console.log(` ${c.green}tokenflexing daemon --install${c.reset} ${c.faint}refresh daily at 6 AM${c.reset}`);
|
|
1066
|
+
console.log(` ${c.green}tokenflexing install-hooks --apply${c.reset} ${c.faint}wire MCP into Claude Code${c.reset}`);
|
|
1067
|
+
console.log(` ${c.faint}View your profile: ${SITE}/dashboard${c.reset}`);
|
|
1068
|
+
console.log("");
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
function commandVersion() { console.log("1.2.0"); }
|
|
971
1072
|
|
|
972
1073
|
function commandHelp() {
|
|
973
1074
|
console.log("");
|
|
974
1075
|
console.log(`${c.bold}tokenflexing${c.reset}${c.faint} — show off your AI token usage${c.reset}`);
|
|
975
1076
|
console.log("");
|
|
1077
|
+
console.log(` ${c.green}tokenflexing connect${c.reset} ${c.bold}one-command setup${c.reset} — scan + login + sync`);
|
|
976
1078
|
console.log(` ${c.green}tokenflexing${c.reset} show stats (default)`);
|
|
977
1079
|
console.log(` ${c.green}tokenflexing stats${c.reset} breakdown per source + model`);
|
|
978
1080
|
console.log(` ${c.green}tokenflexing scan${c.reset} detect ALL AI tools on this device`);
|
|
@@ -1009,6 +1111,7 @@ export async function main(argv) {
|
|
|
1009
1111
|
case undefined: return commandStats();
|
|
1010
1112
|
case "scan": return commandScan();
|
|
1011
1113
|
case "flex": return commandFlex();
|
|
1114
|
+
case "connect": return commandConnect();
|
|
1012
1115
|
case "daemon": return commandDaemon(argv.slice(1));
|
|
1013
1116
|
case "mcp": return commandMcp();
|
|
1014
1117
|
case "install-hooks": return commandInstallHooks(argv.slice(1));
|