@token2chat/t2c 0.2.0-beta.1 → 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/README.md CHANGED
@@ -1,188 +1,161 @@
1
- # t2c - Token2Chat CLI
1
+ # t2c Pay for AI with Ecash
2
2
 
3
- Pay-per-request LLM access via Cashu ecash.
3
+ > CLI + local proxy that makes any AI tool work with token2chat. No accounts, no API keys.
4
4
 
5
- ## Installation
5
+ t2c manages a local Cashu ecash wallet and runs a proxy that auto-attaches payment tokens to your LLM requests. Point your AI tool at `localhost:10402` and it just works.
6
6
 
7
- ```bash
8
- npm install -g @token2chat/cli
9
- ```
10
-
11
- ## Quick Start
12
-
13
- ```bash
14
- # 1. Run setup wizard
15
- t2c setup
16
-
17
- # 2. Start the proxy service
18
- t2c service start
19
-
20
- # 3. Add funds to your wallet
21
- t2c mint
22
-
23
- # 4. Configure your AI tool
24
- t2c config openclaw --apply # For OpenClaw (auto-merge)
25
- t2c config cursor # For Cursor
26
- t2c config env # For other tools
27
-
28
- # 5. (Optional) Install as system service for auto-start
29
- t2c service install
30
- ```
31
-
32
- ## Commands
7
+ Built by an autonomous AI agent.
33
8
 
34
- ### `t2c setup`
35
- Interactive setup wizard. Configures Gate URL, Mint URL, proxy port, and wallet path.
36
-
37
- ### `t2c status`
38
- Show service status and wallet balance.
9
+ ## Install
39
10
 
40
11
  ```bash
41
- t2c status # Pretty output
42
- t2c status --json # JSON output
12
+ npm install -g @token2chat/t2c
43
13
  ```
44
14
 
45
- ### `t2c service`
46
- Manage the local proxy service.
15
+ ## Quick Start
47
16
 
48
17
  ```bash
49
- t2c service start # Start as daemon
50
- t2c service start -f # Run in foreground
51
- t2c service stop # Stop the service
52
- t2c service restart # Restart the service
53
- t2c service status # Show detailed service status
54
- t2c service logs # Show logs
55
- t2c service logs -f # Follow logs
56
-
57
- # System service management (auto-start on login)
58
- t2c service install # Install as launchd (macOS) or systemd (Linux)
59
- t2c service uninstall # Uninstall system service
60
- ```
18
+ # 1. Initialize wallet and config
19
+ t2c init
61
20
 
62
- ### `t2c mint`
63
- Add funds to your wallet.
21
+ # 2. Fund your wallet via Lightning
22
+ t2c mint 5000 # creates invoice for 5000 sats
23
+ t2c mint --check <quote> # check payment & mint ecash
64
24
 
65
- ```bash
66
- t2c mint # Show deposit address
67
- t2c mint --check # Check for pending deposits
68
- t2c mint 1000 # Create Lightning invoice for 1000 sat
69
- ```
25
+ # 3. Connect your AI tool
26
+ t2c connect cursor # or: openclaw, cline, aider, continue, env
27
+ t2c service start # start the local proxy
70
28
 
71
- ### `t2c config`
72
- Generate configuration for AI tools.
73
-
74
- ```bash
75
- t2c config list # List supported tools
76
- t2c config openclaw # Show OpenClaw config
77
- t2c config openclaw --apply # Apply to openclaw.json (creates backup)
78
- t2c config openclaw --json # Output raw JSON
79
- t2c config cursor # Show Cursor config
80
- t2c config env # Show environment variables
29
+ # 4. Use AI as normal — t2c handles payment automatically
81
30
  ```
82
31
 
83
- ## Architecture
32
+ ## How It Works
84
33
 
85
34
  ```
86
- ┌─────────────┐ ┌──────────────────┐ ┌──────────┐
87
- │ AI Tool │────▶│ t2c proxy │────▶│ Gate
88
- (OpenClaw, │ │ :10402 (ecash) │ │ │
89
- Cursor, │ └──────────────────┘ └──────────┘
90
- │ etc.) │
91
- └─────────────┘
35
+ Your AI Tool (Cursor, Cline, OpenClaw, etc.)
36
+
37
+ POST http://127.0.0.1:10402/v1/chat/completions
38
+ Authorization: Bearer <proxy-secret>
39
+
40
+ t2c proxy (local)
41
+ │ 1. Estimate price from request
42
+ │ 2. Select ecash proofs from wallet
43
+ │ 3. Encode as Cashu V4 token
44
+ │ 4. Attach X-Cashu header
45
+ │ 5. Forward to Gate
46
+
47
+ Gate (gate.token2chat.com)
48
+ │ 6. Verify ecash, proxy to LLM
49
+ │ 7. Return response + change token
50
+
51
+ Response flows back to your AI tool
52
+ (t2c reclaims change into wallet)
92
53
  ```
93
54
 
94
- The proxy:
95
- 1. Receives OpenAI-compatible requests from AI tools
96
- 2. Selects ecash tokens from your local wallet
97
- 3. Forwards requests to the Gate with payment
98
- 4. Handles change/refund tokens automatically
99
- 5. Returns the LLM response to your AI tool
55
+ ## CLI Reference
56
+
57
+ ### Wallet
58
+
59
+ | Command | Description |
60
+ |---------|-------------|
61
+ | `t2c init` | Initialize config + wallet |
62
+ | `t2c balance` | Show wallet balance (in USD) |
63
+ | `t2c mint [amount]` | Fund wallet via Lightning invoice |
64
+ | `t2c mint --check <quote>` | Check payment and mint ecash |
65
+ | `t2c recover` | Recover failed change/refund tokens |
66
+ | `t2c audit` | Full fund audit (wallet + mint + gate + anomalies) |
67
+
68
+ ### Proxy Service
69
+
70
+ | Command | Description |
71
+ |---------|-------------|
72
+ | `t2c service start` | Start local proxy (foreground) |
73
+ | `t2c service stop` | Stop proxy |
74
+ | `t2c service restart` | Restart proxy |
75
+ | `t2c service status` | Show proxy status |
76
+ | `t2c service logs` | View proxy logs |
77
+ | `t2c service install` | Install as system service (launchd/systemd) |
78
+ | `t2c service uninstall` | Remove system service |
79
+
80
+ ### Integration
81
+
82
+ | Command | Description |
83
+ |---------|-------------|
84
+ | `t2c connect <app>` | Auto-configure an AI tool |
85
+ | `t2c config <tool>` | Print config snippet for a specific tool |
86
+ | `t2c status` | Service status + balance overview |
87
+ | `t2c monitor` | Live TUI dashboard |
88
+ | `t2c doctor` | Self-diagnostic |
89
+
90
+ ### Supported AI Tools
91
+
92
+ | Tool | Command | What it does |
93
+ |------|---------|-------------|
94
+ | **Cursor** | `t2c connect cursor` | Writes proxy URL + key to Cursor config |
95
+ | **OpenClaw** | `t2c connect openclaw` | Adds provider to OpenClaw config |
96
+ | **Cline** | `t2c connect cline` | Configures VS Code extension |
97
+ | **Continue** | `t2c connect continue` | Configures Continue extension |
98
+ | **Aider** | `t2c connect aider` | Sets environment variables |
99
+ | **Any tool** | `t2c connect env` | Prints env vars to export manually |
100
100
 
101
101
  ## Configuration
102
102
 
103
- Config is stored in `~/.t2c/config.json`:
103
+ Config is stored at `~/.t2c/config.json`.
104
104
 
105
- ```json
106
- {
107
- "gateUrl": "https://gate.token2chat.com",
108
- "mintUrl": "https://mint.token2chat.com",
109
- "walletPath": "~/.t2c/wallet.json",
110
- "proxyPort": 10402,
111
- "lowBalanceThreshold": 1000
112
- }
113
- ```
105
+ | Field | Default | Description |
106
+ |-------|---------|-------------|
107
+ | `gate` | `https://gate.token2chat.com` | Gate URL |
108
+ | `mint` | `https://mint.token2chat.com` | Mint URL |
109
+ | `proxy.port` | `10402` | Local proxy port |
110
+ | `proxy.secret` | (auto-generated) | Bearer token for proxy auth |
111
+ | `wallet` | `~/.t2c/wallet.json` | Wallet file path |
114
112
 
115
- ## System Service
113
+ ### Gate Discovery
116
114
 
117
- ### macOS (launchd)
115
+ t2c automatically discovers available gates from [token2.cash/gates.json](https://token2.cash/gates.json). If your primary gate is down, it fails over to other healthy gates that support your model.
118
116
 
119
- ```bash
120
- # Install
121
- t2c service install
117
+ ## Security
122
118
 
123
- # Load manually
124
- launchctl load ~/Library/LaunchAgents/com.token2chat.proxy.plist
119
+ - **Local proxy auth** — Bearer token with timing-safe comparison
120
+ - **Wallet file** — 0o600 permissions, only readable by owner
121
+ - **Config directory** — 0o700 permissions
122
+ - **Mutex-protected wallet** — no concurrent double-spend from overlapping requests
123
+ - **Failed token persistence** — tokens that fail to process are saved for recovery
124
+ - **No tracking** — ecash is bearer money; the gate never knows who you are
125
125
 
126
- # Uninstall
127
- t2c service uninstall
128
- ```
126
+ ## Wallet
129
127
 
130
- ### Linux (systemd)
128
+ The wallet stores Cashu ecash proofs locally at `~/.t2c/wallet.json`.
131
129
 
132
- ```bash
133
- # Install
134
- t2c service install
130
+ - **Unit**: USD (1 unit = $0.00001, displayed as dollars)
131
+ - **Proof selection**: largest-first greedy (minimizes number of proofs per payment)
132
+ - **Change handling**: automatically reclaimed from gate responses (header or SSE event)
133
+ - **Funding**: Lightning invoices via `t2c mint`
135
134
 
136
- # Enable and start
137
- systemctl --user daemon-reload
138
- systemctl --user enable t2c-proxy.service
139
- systemctl --user start t2c-proxy.service
135
+ ### Balance Display
140
136
 
141
- # Uninstall
142
- t2c service uninstall
137
+ ```bash
138
+ $ t2c balance
139
+ Wallet: $4.52 (452,000 units, 23 proofs)
140
+ Mint: https://mint.token2chat.com
143
141
  ```
144
142
 
145
- ## Supported AI Tools
146
-
147
- | Tool | Command | Notes |
148
- |------|---------|-------|
149
- | OpenClaw | `t2c config openclaw --apply` | Auto-merges token2chat provider |
150
- | Cursor | `t2c config cursor` | OpenAI base URL config |
151
- | Generic | `t2c config env` | OPENAI_API_KEY + OPENAI_BASE_URL |
152
-
153
- Any tool that supports OpenAI-compatible APIs can use Token2Chat.
154
-
155
- ## Error Handling
156
-
157
- The CLI handles various error conditions gracefully:
158
-
159
- - **No config**: Prompts to run `t2c setup`
160
- - **No wallet**: Creates one automatically
161
- - **Corrupted config**: Auto-recovers with backup
162
- - **Network timeout**: Retries with exponential backoff
163
- - **Insufficient balance**: Clear error message with balance info
143
+ ## System Service
164
144
 
165
- ## Development
145
+ Install t2c as a background service that starts on boot:
166
146
 
167
147
  ```bash
168
- # Clone and install
169
- git clone https://github.com/token2chat/cli.git
170
- cd cli
171
- npm install
172
-
173
- # Build
174
- npm run build
175
-
176
- # Run tests
177
- npm test
148
+ # macOS (launchd)
149
+ t2c service install
178
150
 
179
- # Run with coverage
180
- npm run test:coverage
151
+ # Linux (systemd)
152
+ t2c service install
181
153
 
182
- # Link for local testing
183
- npm link
154
+ # Both auto-detect the platform
184
155
  ```
185
156
 
157
+ The proxy runs in the background, automatically paying for LLM requests from any connected AI tool.
158
+
186
159
  ## License
187
160
 
188
161
  MIT
@@ -22,7 +22,7 @@ export declare class CashuStore {
22
22
  /** Returns true if balance is below threshold */
23
23
  needsFunding(threshold: number): boolean;
24
24
  /**
25
- * Select proofs totalling at least `amount` sat,
25
+ * Select proofs totalling at least `amount` units,
26
26
  * encode as a Cashu V4 token, and remove them from the store.
27
27
  */
28
28
  selectAndEncode(amount: number): Promise<string>;
@@ -73,7 +73,7 @@ export class CashuStore {
73
73
  catch {
74
74
  const data = {
75
75
  mint: mint ?? "https://mint.token2chat.com",
76
- unit: "sat",
76
+ unit: "usd",
77
77
  proofs: [],
78
78
  };
79
79
  const store = new CashuStore(path, data);
@@ -102,13 +102,13 @@ export class CashuStore {
102
102
  }
103
103
  // ── Proof selection ────────────────────────────────────────
104
104
  /**
105
- * Select proofs totalling at least `amount` sat,
105
+ * Select proofs totalling at least `amount` units,
106
106
  * encode as a Cashu V4 token, and remove them from the store.
107
107
  */
108
108
  async selectAndEncode(amount) {
109
109
  return this.mutex.lock(async () => {
110
110
  if (this.balance < amount) {
111
- throw new Error(`Insufficient balance: need ${amount} sat, have ${this.balance} sat`);
111
+ throw new Error(`Insufficient balance: need ${amount}, have ${this.balance}`);
112
112
  }
113
113
  const sorted = [...this.data.proofs].sort((a, b) => b.amount - a.amount);
114
114
  const selected = [];
@@ -120,7 +120,7 @@ export class CashuStore {
120
120
  total += p.amount;
121
121
  }
122
122
  if (total < amount) {
123
- throw new Error(`Could not select enough proofs for ${amount} sat`);
123
+ throw new Error(`Could not select enough proofs for ${amount}`);
124
124
  }
125
125
  // Remove selected proofs
126
126
  const selectedSecrets = new Set(selected.map((p) => typeof p.secret === "string" ? p.secret : JSON.stringify(p.secret)));
@@ -1,6 +1,71 @@
1
+ /**
2
+ * t2c audit — Full-chain fund visualization
3
+ *
4
+ * Unified view: local wallet + Mint + Gate + transaction log.
5
+ * Cross-references data and highlights anomalies.
6
+ */
7
+ import { type TransactionRecord, type FailedToken } from "../config.js";
1
8
  interface AuditOptions {
2
9
  json?: boolean;
3
10
  lines?: string;
4
11
  }
12
+ interface MintInfo {
13
+ reachable: boolean;
14
+ name?: string;
15
+ version?: string;
16
+ nuts?: Record<string, unknown>;
17
+ keysetIds?: string[];
18
+ error?: string;
19
+ }
20
+ interface GateInfo {
21
+ reachable: boolean;
22
+ mints?: string[];
23
+ models?: string[];
24
+ pricing?: Record<string, {
25
+ input_per_million?: number;
26
+ output_per_million?: number;
27
+ per_request?: number;
28
+ }>;
29
+ error?: string;
30
+ }
31
+ interface ProofBreakdown {
32
+ [denomination: number]: number;
33
+ }
34
+ export interface Anomaly {
35
+ severity: "error" | "warn" | "info";
36
+ message: string;
37
+ }
38
+ interface DiscoveredGate {
39
+ name: string;
40
+ url: string;
41
+ mint: string;
42
+ models: string[];
43
+ healthy: boolean;
44
+ }
45
+ export interface AuditReport {
46
+ timestamp: number;
47
+ wallet: {
48
+ balance: number;
49
+ proofCount: number;
50
+ proofBreakdown: ProofBreakdown;
51
+ mint: string;
52
+ } | null;
53
+ mint: MintInfo;
54
+ gate: GateInfo;
55
+ discoveredGates: DiscoveredGate[];
56
+ transactions: {
57
+ total: number;
58
+ shown: number;
59
+ totalSpent: number;
60
+ totalChange: number;
61
+ totalRefund: number;
62
+ netCost: number;
63
+ errorCount: number;
64
+ recent: TransactionRecord[];
65
+ };
66
+ failedTokens: FailedToken[];
67
+ anomalies: Anomaly[];
68
+ }
69
+ export declare function detectAnomalies(report: AuditReport): Anomaly[];
5
70
  export declare function auditCommand(opts: AuditOptions): Promise<void>;
6
71
  export {};
@@ -4,7 +4,7 @@
4
4
  * Unified view: local wallet + Mint + Gate + transaction log.
5
5
  * Cross-references data and highlights anomalies.
6
6
  */
7
- import { loadConfig, resolveHome, loadFailedTokens, loadTransactions, } from "../config.js";
7
+ import { loadConfig, resolveHome, loadFailedTokens, loadTransactions, formatUnits, } from "../config.js";
8
8
  import { CashuStore } from "../cashu-store.js";
9
9
  import { GateRegistry } from "../gate-discovery.js";
10
10
  async function fetchMintInfo(mintUrl) {
@@ -52,7 +52,7 @@ async function fetchGateInfo(gateUrl) {
52
52
  return { reachable: false, error: e instanceof Error ? e.message : String(e) };
53
53
  }
54
54
  }
55
- function detectAnomalies(report) {
55
+ export function detectAnomalies(report) {
56
56
  const anomalies = [];
57
57
  // Wallet anomalies
58
58
  if (report.wallet) {
@@ -60,7 +60,7 @@ function detectAnomalies(report) {
60
60
  anomalies.push({ severity: "error", message: "Wallet balance is 0 — cannot make requests" });
61
61
  }
62
62
  else if (report.wallet.balance < 500) {
63
- anomalies.push({ severity: "warn", message: `Low wallet balance: ${report.wallet.balance} sat` });
63
+ anomalies.push({ severity: "warn", message: `Low wallet balance: ${formatUnits(report.wallet.balance)}` });
64
64
  }
65
65
  if (report.wallet.proofCount > 100) {
66
66
  anomalies.push({ severity: "warn", message: `High proof count (${report.wallet.proofCount}) — consider consolidating via swap` });
@@ -101,7 +101,7 @@ function detectAnomalies(report) {
101
101
  if (report.transactions.totalSpent > 0 && lossRate > 50) {
102
102
  anomalies.push({
103
103
  severity: "warn",
104
- message: `High fund loss rate: ${lossRate.toFixed(1)}% of spent sats not returned as change/refund`,
104
+ message: `High fund loss rate: ${lossRate.toFixed(1)}% of spent funds not returned as change/refund`,
105
105
  });
106
106
  }
107
107
  // Failed token anomalies
@@ -118,7 +118,7 @@ function detectAnomalies(report) {
118
118
  if (last.balanceAfter !== report.wallet.balance) {
119
119
  anomalies.push({
120
120
  severity: "info",
121
- message: `Balance drift: last tx recorded ${last.balanceAfter} sat, wallet now ${report.wallet.balance} sat (may indicate manual changes)`,
121
+ message: `Balance drift: last tx recorded ${formatUnits(last.balanceAfter)}, wallet now ${formatUnits(report.wallet.balance)} (may indicate manual changes)`,
122
122
  });
123
123
  }
124
124
  }
@@ -141,7 +141,7 @@ function printReport(report) {
141
141
  // ── Wallet ──
142
142
  console.log("--- Wallet ---");
143
143
  if (report.wallet) {
144
- console.log(` Balance: ${report.wallet.balance.toLocaleString()} sat`);
144
+ console.log(` Balance: ${formatUnits(report.wallet.balance)}`);
145
145
  console.log(` Proofs: ${report.wallet.proofCount}`);
146
146
  console.log(` Mint: ${report.wallet.mint}`);
147
147
  const denominations = Object.entries(report.wallet.proofBreakdown)
@@ -149,7 +149,7 @@ function printReport(report) {
149
149
  if (denominations.length > 0) {
150
150
  console.log(" Breakdown:");
151
151
  for (const [denom, count] of denominations) {
152
- console.log(` ${denom} sat x ${count} = ${Number(denom) * count} sat`);
152
+ console.log(` ${formatUnits(Number(denom))} x ${count} = ${formatUnits(Number(denom) * count)}`);
153
153
  }
154
154
  }
155
155
  }
@@ -188,7 +188,7 @@ function printReport(report) {
188
188
  parts.push(`out:${rule.output_per_million}/M`);
189
189
  if (rule.per_request)
190
190
  parts.push(`req:${rule.per_request}`);
191
- console.log(` ${model}: ${parts.join(", ")} sat`);
191
+ console.log(` ${model}: ${parts.join(", ")}`);
192
192
  }
193
193
  }
194
194
  // ── Discovered Gates ──
@@ -210,10 +210,10 @@ function printReport(report) {
210
210
  }
211
211
  else {
212
212
  console.log(` Total: ${report.transactions.total} requests`);
213
- console.log(` Spent: ${report.transactions.totalSpent.toLocaleString()} sat`);
214
- console.log(` Change: +${report.transactions.totalChange.toLocaleString()} sat`);
215
- console.log(` Refund: +${report.transactions.totalRefund.toLocaleString()} sat`);
216
- console.log(` Net cost: ${report.transactions.netCost.toLocaleString()} sat`);
213
+ console.log(` Spent: ${formatUnits(report.transactions.totalSpent)}`);
214
+ console.log(` Change: +${formatUnits(report.transactions.totalChange)}`);
215
+ console.log(` Refund: +${formatUnits(report.transactions.totalRefund)}`);
216
+ console.log(` Net cost: ${formatUnits(report.transactions.netCost)}`);
217
217
  console.log(` Errors: ${report.transactions.errorCount}`);
218
218
  if (report.transactions.recent.length > 0) {
219
219
  console.log("\n Recent transactions:");
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * t2c balance - Simple balance display
3
3
  */
4
- import { loadConfig, WALLET_PATH } from "../config.js";
4
+ import { loadConfig, WALLET_PATH, formatUnits } from "../config.js";
5
5
  import { CashuStore } from "../cashu-store.js";
6
6
  export async function balanceCommand(opts) {
7
7
  const config = await loadConfig();
@@ -14,7 +14,7 @@ export async function balanceCommand(opts) {
14
14
  }));
15
15
  }
16
16
  else {
17
- console.log(`${wallet.balance} sat`);
17
+ console.log(formatUnits(wallet.balance));
18
18
  }
19
19
  }
20
20
  catch (e) {
@@ -4,7 +4,7 @@
4
4
  import fs from "node:fs/promises";
5
5
  import os from "node:os";
6
6
  import path from "node:path";
7
- import { loadConfig, configExists, checkGateHealth, checkMintHealth, CONFIG_PATH, WALLET_PATH, } from "../config.js";
7
+ import { loadConfig, configExists, checkGateHealth, checkMintHealth, CONFIG_PATH, WALLET_PATH, formatUnits, } from "../config.js";
8
8
  import { CashuStore } from "../cashu-store.js";
9
9
  // Platform-specific service paths
10
10
  const LAUNCHD_PLIST_PATH = path.join(os.homedir(), "Library", "LaunchAgents", "com.token2chat.proxy.plist");
@@ -31,7 +31,7 @@ async function checkWallet(config) {
31
31
  return {
32
32
  name: "Wallet",
33
33
  ok: true,
34
- detail: `${WALLET_PATH} (${wallet.balance} sat)`,
34
+ detail: `${WALLET_PATH} (${formatUnits(wallet.balance)})`,
35
35
  };
36
36
  }
37
37
  catch (e) {
@@ -4,7 +4,7 @@
4
4
  * Uses sensible defaults for gate/mint/port/wallet.
5
5
  * No interactive prompts — just validate connectivity and create wallet.
6
6
  */
7
- import { saveConfig, configExists, resolveHome, loadConfig, checkGateHealth, checkMintHealth, DEFAULT_CONFIG, } from "../config.js";
7
+ import { saveConfig, configExists, resolveHome, loadConfig, checkGateHealth, checkMintHealth, DEFAULT_CONFIG, formatUnits, } from "../config.js";
8
8
  import { CashuStore } from "../cashu-store.js";
9
9
  export async function initCommand(opts) {
10
10
  console.log("\n🎟️ Token2Chat Init\n");
@@ -35,7 +35,7 @@ export async function initCommand(opts) {
35
35
  try {
36
36
  const resolvedPath = resolveHome(config.walletPath);
37
37
  const wallet = await CashuStore.load(resolvedPath, config.mintUrl);
38
- console.log(` Wallet initialized (balance: ${wallet.balance} sat)`);
38
+ console.log(` Wallet initialized (balance: ${formatUnits(wallet.balance)})`);
39
39
  }
40
40
  catch (e) {
41
41
  console.log(` ⚠️ Wallet init failed: ${e}`);
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import fs from "node:fs/promises";
5
5
  import path from "node:path";
6
- import { loadConfig, resolveHome, CONFIG_DIR } from "../config.js";
6
+ import { loadConfig, resolveHome, CONFIG_DIR, formatUnits } from "../config.js";
7
7
  import { CashuStore } from "../cashu-store.js";
8
8
  // Fallback deposit address (Mint's EVM address for USDC/USDT)
9
9
  // This is used if /v1/info endpoint doesn't provide a deposit address
@@ -61,7 +61,7 @@ export async function mintCommand(amount, opts) {
61
61
  }
62
62
  const currentBalance = wallet.balance;
63
63
  console.log("\n🎟️ Token2Chat Funding\n");
64
- console.log(`Current balance: ${currentBalance.toLocaleString()} sat\n`);
64
+ console.log(`Current balance: ${formatUnits(currentBalance)}\n`);
65
65
  if (opts.check) {
66
66
  // Check for pending Lightning quotes and mint paid ones
67
67
  console.log("Checking pending Lightning quotes...\n");
@@ -84,7 +84,7 @@ export async function mintCommand(amount, opts) {
84
84
  }
85
85
  try {
86
86
  const minted = await wallet.mintFromQuote(pq.quote, pq.amount);
87
- console.log(`✅ Minted ${minted.toLocaleString()} sat from quote ${pq.quote.slice(0, 8)}...`);
87
+ console.log(`✅ Minted ${formatUnits(minted)} from quote ${pq.quote.slice(0, 8)}...`);
88
88
  mintedTotal += minted;
89
89
  }
90
90
  catch (e) {
@@ -105,8 +105,8 @@ export async function mintCommand(amount, opts) {
105
105
  // Update pending quotes file
106
106
  await savePendingQuotes({ quotes: stillPending });
107
107
  if (mintedTotal > 0) {
108
- console.log(`\n🎉 Total minted: ${mintedTotal.toLocaleString()} sat`);
109
- console.log(`New balance: ${wallet.balance.toLocaleString()} sat\n`);
108
+ console.log(`\n🎉 Total minted: ${formatUnits(mintedTotal)}`);
109
+ console.log(`New balance: ${formatUnits(wallet.balance)}\n`);
110
110
  }
111
111
  else if (stillPending.length > 0) {
112
112
  console.log(`\n${stillPending.length} quote(s) still awaiting payment.`);
@@ -122,19 +122,19 @@ export async function mintCommand(amount, opts) {
122
122
  }
123
123
  // Lightning funding (if amount specified)
124
124
  if (amount) {
125
- const sats = parseInt(amount, 10);
126
- if (isNaN(sats) || sats <= 0) {
127
- console.error("Invalid amount. Specify sats to fund via Lightning.");
125
+ const units = parseInt(amount, 10);
126
+ if (isNaN(units) || units <= 0) {
127
+ console.error("Invalid amount. Specify units to fund via Lightning.");
128
128
  process.exit(1);
129
129
  }
130
- console.log(`⚡ Creating Lightning invoice for ${sats.toLocaleString()} sat...\n`);
130
+ console.log(`⚡ Creating Lightning invoice for ${formatUnits(units)}...\n`);
131
131
  try {
132
- const quote = await wallet.createMintQuote(sats);
132
+ const quote = await wallet.createMintQuote(units);
133
133
  // Save to pending quotes
134
134
  const pendingData = await loadPendingQuotes();
135
135
  pendingData.quotes.push({
136
136
  quote: quote.quote,
137
- amount: sats,
137
+ amount: units,
138
138
  request: quote.request,
139
139
  createdAt: Date.now(),
140
140
  });
@@ -153,7 +153,7 @@ export async function mintCommand(amount, opts) {
153
153
  // Show deposit instructions (EVM stablecoins)
154
154
  const depositAddress = await fetchDepositAddress(config.mintUrl);
155
155
  console.log("Option 1: Lightning (recommended)\n");
156
- console.log(" t2c mint <amount_sats> Create a Lightning invoice\n");
156
+ console.log(" t2c mint <amount> Create a Lightning invoice\n");
157
157
  console.log("Option 2: EVM Stablecoins\n");
158
158
  console.log(" Send USDC or USDT to:\n");
159
159
  console.log(` ${depositAddress}\n`);
@@ -163,6 +163,6 @@ export async function mintCommand(amount, opts) {
163
163
  }
164
164
  console.log("\n💡 Tips:");
165
165
  console.log(" • Lightning deposits are instant");
166
- console.log(" • EVM deposits: ~1 sat per $0.001 USD");
167
- console.log(" • Minimum deposit: $1 USD or 1000 sat\n");
166
+ console.log(" • EVM deposits: 100,000 units = $1.00 USD");
167
+ console.log(" • Minimum deposit: $1.00 USD\n");
168
168
  }