nous-token 0.1.0 → 0.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/PROTOCOL.md CHANGED
@@ -79,11 +79,11 @@ Users identify themselves with an Ethereum wallet address. The wallet is the per
79
79
 
80
80
  ### How it works
81
81
 
82
- 1. User runs `npx nous-token setup`, enters wallet address + API key
83
- 2. CLI computes `user_hash = SHA-256(api_key)[0:16]` locally, sends `{api_key, wallet}` to `/api/link`
84
- 3. Gateway computes the same hash, verifies it exists in records, links all records to the wallet
85
- 4. Future API calls include `?wallet=0x...` in the base URL (set by CLI) or `X-Nous-Wallet` header (set by plugin)
86
- 5. Gateway stores wallet with each new record and backfills old records for the same hash
82
+ 1. User runs `npx nous-token setup`, enters wallet address
83
+ 2. CLI configures AI tools with base URL: `https://gateway.nousai.cc/{provider}/w/{wallet}/...`
84
+ 3. First API call through the gateway: gateway hashes the auth token, records usage with wallet
85
+ 4. Wallet binding is automatic no separate linking step, no API key transmission
86
+ 5. Old records for the same auth token hash are backfilled to the wallet
87
87
 
88
88
  ### Binding rules
89
89
 
@@ -161,7 +161,6 @@ Both options use self-to-self transactions with data as calldata. No smart contr
161
161
  | `/api/wallet/{address}` | GET | Usage for a wallet (all linked hashes aggregated) |
162
162
  | `/api/user/{hash}` | GET | Single user aggregated stats |
163
163
  | `/api/user/{hash}/receipts` | GET | Signed receipts (paginated) |
164
- | `/api/link` | POST | Link hash to wallet (`{api_key, wallet}`) — first bind wins |
165
164
  | `/api/records` | GET | Raw records for sentinel verification |
166
165
  | `/api/chain` | GET | Merkle root and MMR state |
167
166
  | `/api/sign` | POST | Signed summary (optional aggregation) |
package/README.md CHANGED
@@ -12,11 +12,9 @@ One command to track all your AI spending across every provider. Your data is si
12
12
  npx nous-token setup
13
13
  ```
14
14
 
15
- The CLI asks two things:
16
- 1. **Wallet address** (0x...) — your permanent identity across API keys
17
- 2. **API key** — used locally to compute your hash, never sent anywhere
15
+ The CLI asks one thing: your **wallet address** (0x...) — your permanent identity.
18
16
 
19
- Then it auto-detects your AI tools (Claude Code, Cursor, Python SDKs, etc.) and routes them through the gateway.
17
+ Then it auto-detects your AI tools (Claude Code, Cursor, Python SDKs, etc.) and routes them through the gateway. Works with both API keys and subscription plans (Claude Max, etc.).
20
18
 
21
19
  ## How It Works
22
20
 
@@ -56,9 +54,10 @@ Works with any OpenAI-compatible API out of the box:
56
54
  Your wallet address is your permanent identity. API keys expire and rotate — your wallet doesn't.
57
55
 
58
56
  - Run `npx nous-token setup` with your wallet address
59
- - All usage across any API key links to your wallet
60
- - Switch keys, switch machines — your history follows you
61
- - First bind is permanent (prevents rebind with leaked keys)
57
+ - All usage across any API key or OAuth token links to your wallet
58
+ - Switch keys, switch machines, switch plans — your history follows you
59
+ - First bind is permanent per auth token (prevents rebind with leaked keys)
60
+ - Works with API keys and subscription plans (Claude Max, ChatGPT Plus, etc.)
62
61
 
63
62
  ## Privacy by Structure
64
63
 
@@ -66,7 +65,7 @@ Not by promise — by code. [Audit the source](gateway/src/index.ts).
66
65
 
67
66
  | Data | What happens |
68
67
  |------|-------------|
69
- | API Key | Hashed (SHA-256) for identity. Never stored. |
68
+ | Auth Token | Hashed (SHA-256) for identity. Never stored. Works with API keys and OAuth tokens. |
70
69
  | Prompts | `request.body` piped directly to provider. Never read. |
71
70
  | Responses (streaming) | Tee'd. Only last 4KB buffered to extract `.usage`. |
72
71
  | Responses (non-streaming) | In V8 isolate memory. Only `.usage` accessed. GC'd after request. |
@@ -107,7 +106,6 @@ Gateway: `https://gateway.nousai.cc`
107
106
  | `/api/wallet/{address}` | GET | Usage for a wallet (all linked hashes) |
108
107
  | `/api/user/{hash}` | GET | Single user stats |
109
108
  | `/api/user/{hash}/receipts` | GET | Signed receipts (paginated) |
110
- | `/api/link` | POST | Link hash to wallet (`{api_key, wallet}`) |
111
109
  | `/api/records` | GET | Raw records for sentinel verification |
112
110
  | `/api/chain` | GET | Merkle root and MMR state |
113
111
  | `/api/sign` | POST | Signed summary for on-chain storage |
package/cli/setup.ts CHANGED
@@ -7,11 +7,10 @@
7
7
  // Scans for installed AI tools, configures them to route through
8
8
  // the nous-token gateway for usage tracking.
9
9
 
10
- import { existsSync, readFileSync, writeFileSync, mkdirSync, appendFileSync } from "fs";
10
+ import { existsSync, readFileSync, writeFileSync } from "fs";
11
11
  import { homedir } from "os";
12
12
  import { join } from "path";
13
13
  import { execSync } from "child_process";
14
- import { createHash } from "crypto";
15
14
 
16
15
  const GATEWAY = "https://gateway.nousai.cc";
17
16
  const HOME = homedir();
@@ -26,7 +25,7 @@ function saveWallet(wallet: string): void {
26
25
  }
27
26
 
28
27
  function gatewayUrl(path: string, wallet: string): string {
29
- return wallet ? `${GATEWAY}${path}?wallet=${wallet}` : `${GATEWAY}${path}`;
28
+ return wallet ? `${GATEWAY}${path}/w/${wallet}` : `${GATEWAY}${path}`;
30
29
  }
31
30
 
32
31
  interface Tool {
@@ -181,27 +180,6 @@ function setShellEnv(key: string, value: string, toolName: string): ConfigResult
181
180
  return { success: true, message: `set ${key} in ${rcName}` };
182
181
  }
183
182
 
184
- // ── Find first available API key ──
185
-
186
- function findFirstApiKey(): string | null {
187
- const keys = [
188
- process.env.OPENAI_API_KEY,
189
- process.env.ANTHROPIC_API_KEY,
190
- process.env.DEEPSEEK_API_KEY,
191
- process.env.GEMINI_API_KEY,
192
- process.env.GROQ_API_KEY,
193
- ];
194
- for (const key of keys) {
195
- if (key) return key.replace(/^Bearer\s+/i, "");
196
- }
197
- return null;
198
- }
199
-
200
- function computeUserHash(): string | null {
201
- const key = findFirstApiKey();
202
- if (!key) return null;
203
- return createHash("sha256").update(key).digest("hex").slice(0, 32);
204
- }
205
183
 
206
184
  // ── Prompt helper ──
207
185
 
@@ -242,39 +220,7 @@ if (wallet) {
242
220
  process.exit(0);
243
221
  }
244
222
 
245
- // Step 2: API key — read from env or ask
246
- let apiKey = findFirstApiKey();
247
- if (!apiKey) {
248
- const input = await ask(" API key (sk-...): ");
249
- if (input) {
250
- apiKey = input.replace(/^Bearer\s+/i, "");
251
- }
252
- }
253
-
254
- // Step 3: Link hash → wallet via gateway
255
- if (apiKey) {
256
- const hash = createHash("sha256").update(apiKey).digest("hex").slice(0, 32);
257
- console.log(` \x1b[90mUser hash: ${hash}\x1b[0m`);
258
- try {
259
- const res = await fetch(`${GATEWAY}/api/link`, {
260
- method: "POST",
261
- headers: { "Content-Type": "application/json" },
262
- body: JSON.stringify({ api_key: apiKey, wallet }),
263
- });
264
- const data = await res.json() as { ok?: boolean; error?: string };
265
- if (data.ok) {
266
- console.log(` \x1b[32m✓ Linked to wallet\x1b[0m\n`);
267
- } else {
268
- console.log(` \x1b[90m${data.error || "No existing records to link — they'll link automatically on first use."}\x1b[0m\n`);
269
- }
270
- } catch {
271
- console.log(" \x1b[90mCouldn't reach gateway — linking will happen on first use.\x1b[0m\n");
272
- }
273
- } else {
274
- console.log(" \x1b[90mNo API key found. Wallet will link on first use through the gateway.\x1b[0m\n");
275
- }
276
-
277
- // Step 4: Configure tools
223
+ // Step 2: Configure tools
278
224
  console.log(" Scanning for AI tools...\n");
279
225
 
280
226
  let configured = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nous-token",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Open-source AI usage tracker. One command to track all your AI spending.",
5
5
  "type": "module",
6
6
  "main": "index.ts",