@token2chat/t2c 0.2.0-beta.1

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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +188 -0
  3. package/dist/adapters/aider.d.ts +5 -0
  4. package/dist/adapters/aider.js +29 -0
  5. package/dist/adapters/cline.d.ts +5 -0
  6. package/dist/adapters/cline.js +32 -0
  7. package/dist/adapters/continue.d.ts +5 -0
  8. package/dist/adapters/continue.js +45 -0
  9. package/dist/adapters/cursor.d.ts +5 -0
  10. package/dist/adapters/cursor.js +23 -0
  11. package/dist/adapters/env.d.ts +5 -0
  12. package/dist/adapters/env.js +25 -0
  13. package/dist/adapters/index.d.ts +6 -0
  14. package/dist/adapters/index.js +6 -0
  15. package/dist/adapters/openclaw.d.ts +2 -0
  16. package/dist/adapters/openclaw.js +167 -0
  17. package/dist/cashu-store.d.ts +52 -0
  18. package/dist/cashu-store.js +201 -0
  19. package/dist/commands/audit.d.ts +6 -0
  20. package/dist/commands/audit.js +340 -0
  21. package/dist/commands/balance.d.ts +5 -0
  22. package/dist/commands/balance.js +29 -0
  23. package/dist/commands/config.d.ts +5 -0
  24. package/dist/commands/config.js +62 -0
  25. package/dist/commands/connect.d.ts +1 -0
  26. package/dist/commands/connect.js +43 -0
  27. package/dist/commands/doctor.d.ts +1 -0
  28. package/dist/commands/doctor.js +178 -0
  29. package/dist/commands/init.d.ts +3 -0
  30. package/dist/commands/init.js +50 -0
  31. package/dist/commands/mint.d.ts +5 -0
  32. package/dist/commands/mint.js +168 -0
  33. package/dist/commands/recover.d.ts +1 -0
  34. package/dist/commands/recover.js +61 -0
  35. package/dist/commands/service.d.ts +7 -0
  36. package/dist/commands/service.js +378 -0
  37. package/dist/commands/setup.d.ts +1 -0
  38. package/dist/commands/setup.js +128 -0
  39. package/dist/commands/status.d.ts +5 -0
  40. package/dist/commands/status.js +87 -0
  41. package/dist/config.d.ts +83 -0
  42. package/dist/config.js +224 -0
  43. package/dist/connectors/cursor.d.ts +2 -0
  44. package/dist/connectors/cursor.js +28 -0
  45. package/dist/connectors/env.d.ts +2 -0
  46. package/dist/connectors/env.js +38 -0
  47. package/dist/connectors/index.d.ts +26 -0
  48. package/dist/connectors/index.js +30 -0
  49. package/dist/connectors/interface.d.ts +20 -0
  50. package/dist/connectors/interface.js +1 -0
  51. package/dist/connectors/openclaw.d.ts +2 -0
  52. package/dist/connectors/openclaw.js +202 -0
  53. package/dist/gate-discovery.d.ts +49 -0
  54. package/dist/gate-discovery.js +142 -0
  55. package/dist/index.d.ts +2 -0
  56. package/dist/index.js +177 -0
  57. package/dist/proxy.d.ts +11 -0
  58. package/dist/proxy.js +352 -0
  59. package/package.json +84 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Token2Chat
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # t2c - Token2Chat CLI
2
+
3
+ Pay-per-request LLM access via Cashu ecash.
4
+
5
+ ## Installation
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
33
+
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.
39
+
40
+ ```bash
41
+ t2c status # Pretty output
42
+ t2c status --json # JSON output
43
+ ```
44
+
45
+ ### `t2c service`
46
+ Manage the local proxy service.
47
+
48
+ ```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
+ ```
61
+
62
+ ### `t2c mint`
63
+ Add funds to your wallet.
64
+
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
+ ```
70
+
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
81
+ ```
82
+
83
+ ## Architecture
84
+
85
+ ```
86
+ ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
87
+ │ AI Tool │────▶│ t2c proxy │────▶│ Gate │
88
+ │ (OpenClaw, │ │ :10402 (ecash) │ │ │
89
+ │ Cursor, │ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
90
+ │ etc.) │
91
+ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
92
+ ```
93
+
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
100
+
101
+ ## Configuration
102
+
103
+ Config is stored in `~/.t2c/config.json`:
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
+ ```
114
+
115
+ ## System Service
116
+
117
+ ### macOS (launchd)
118
+
119
+ ```bash
120
+ # Install
121
+ t2c service install
122
+
123
+ # Load manually
124
+ launchctl load ~/Library/LaunchAgents/com.token2chat.proxy.plist
125
+
126
+ # Uninstall
127
+ t2c service uninstall
128
+ ```
129
+
130
+ ### Linux (systemd)
131
+
132
+ ```bash
133
+ # Install
134
+ t2c service install
135
+
136
+ # Enable and start
137
+ systemctl --user daemon-reload
138
+ systemctl --user enable t2c-proxy.service
139
+ systemctl --user start t2c-proxy.service
140
+
141
+ # Uninstall
142
+ t2c service uninstall
143
+ ```
144
+
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
164
+
165
+ ## Development
166
+
167
+ ```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
178
+
179
+ # Run with coverage
180
+ npm run test:coverage
181
+
182
+ # Link for local testing
183
+ npm link
184
+ ```
185
+
186
+ ## License
187
+
188
+ MIT
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Aider adapter - Generate config for Aider AI coding assistant
3
+ */
4
+ import type { T2CConfig, AdapterConfigOptions } from "../config.js";
5
+ export declare function aiderAdapter(t2cConfig: T2CConfig, opts: AdapterConfigOptions): Promise<void>;
@@ -0,0 +1,29 @@
1
+ export async function aiderAdapter(t2cConfig, opts) {
2
+ const baseUrl = `http://127.0.0.1:${t2cConfig.proxyPort}/v1`;
3
+ const apiKey = opts.proxySecret ?? "t2c-local";
4
+ if (opts.json) {
5
+ console.log(JSON.stringify({
6
+ OPENAI_API_KEY: apiKey,
7
+ OPENAI_API_BASE: baseUrl,
8
+ }, null, 2));
9
+ return;
10
+ }
11
+ console.log("\nšŸŽŸļø Aider Configuration\n");
12
+ console.log("Option 1: Environment variables\n");
13
+ console.log(` export OPENAI_API_KEY=${apiKey}`);
14
+ console.log(` export OPENAI_API_BASE=${baseUrl}\n`);
15
+ console.log(" Then run:");
16
+ console.log(" aider --model openai/anthropic/claude-sonnet-4\n");
17
+ console.log("Option 2: Command line arguments\n");
18
+ console.log(` aider --openai-api-key ${apiKey} \\`);
19
+ console.log(` --openai-api-base ${baseUrl} \\`);
20
+ console.log(" --model openai/anthropic/claude-sonnet-4\n");
21
+ console.log("Option 3: .aider.conf.yml (project root)\n");
22
+ console.log("```yaml");
23
+ console.log(`openai-api-key: ${apiKey}`);
24
+ console.log(`openai-api-base: ${baseUrl}`);
25
+ console.log("model: openai/anthropic/claude-sonnet-4");
26
+ console.log("```\n");
27
+ console.log("Note: Aider uses 'openai/' prefix for OpenAI-compatible endpoints.");
28
+ console.log("Available models: openai/anthropic/claude-opus-4, openai/openai/gpt-4o, etc.\n");
29
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Cline adapter - Generate config for Cline VS Code extension
3
+ */
4
+ import type { T2CConfig, AdapterConfigOptions } from "../config.js";
5
+ export declare function clineAdapter(t2cConfig: T2CConfig, opts: AdapterConfigOptions): Promise<void>;
@@ -0,0 +1,32 @@
1
+ export async function clineAdapter(t2cConfig, opts) {
2
+ const baseUrl = `http://127.0.0.1:${t2cConfig.proxyPort}/v1`;
3
+ const apiKey = opts.proxySecret ?? "t2c-local";
4
+ if (opts.json) {
5
+ console.log(JSON.stringify({
6
+ "cline.apiProvider": "openai-compatible",
7
+ "cline.openAiCompatibleApiBaseUrl": baseUrl,
8
+ "cline.openAiCompatibleApiKey": apiKey,
9
+ "cline.openAiCompatibleModelId": "anthropic/claude-sonnet-4",
10
+ }, null, 2));
11
+ return;
12
+ }
13
+ console.log("\nšŸŽŸļø Cline Configuration\n");
14
+ console.log("Add to your VS Code settings.json:\n");
15
+ console.log(" ~/.vscode/settings.json (global)");
16
+ console.log(" .vscode/settings.json (workspace)\n");
17
+ console.log("```json");
18
+ console.log("{");
19
+ console.log(' "cline.apiProvider": "openai-compatible",');
20
+ console.log(` "cline.openAiCompatibleApiBaseUrl": "${baseUrl}",`);
21
+ console.log(` "cline.openAiCompatibleApiKey": "${apiKey}",`);
22
+ console.log(' "cline.openAiCompatibleModelId": "anthropic/claude-sonnet-4"');
23
+ console.log("}");
24
+ console.log("```\n");
25
+ console.log("Or configure via Cline extension settings UI:\n");
26
+ console.log(" 1. Open Cline settings (click gear icon)");
27
+ console.log(' 2. Set API Provider to "OpenAI Compatible"');
28
+ console.log(` 3. Base URL: ${baseUrl}`);
29
+ console.log(` 4. API Key: ${apiKey}`);
30
+ console.log(" 5. Model ID: anthropic/claude-sonnet-4\n");
31
+ console.log("Available models: anthropic/claude-opus-4, openai/gpt-4o, etc.\n");
32
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Continue adapter - Generate config for Continue.dev VS Code extension
3
+ */
4
+ import type { T2CConfig, AdapterConfigOptions } from "../config.js";
5
+ export declare function continueAdapter(t2cConfig: T2CConfig, opts: AdapterConfigOptions): Promise<void>;
@@ -0,0 +1,45 @@
1
+ export async function continueAdapter(t2cConfig, opts) {
2
+ const baseUrl = `http://127.0.0.1:${t2cConfig.proxyPort}/v1`;
3
+ const apiKey = opts.proxySecret ?? "t2c-local";
4
+ const modelConfig = {
5
+ title: "Token2Chat",
6
+ provider: "openai",
7
+ model: "anthropic/claude-sonnet-4",
8
+ apiBase: baseUrl,
9
+ apiKey,
10
+ };
11
+ if (opts.json) {
12
+ console.log(JSON.stringify({
13
+ models: [modelConfig],
14
+ }, null, 2));
15
+ return;
16
+ }
17
+ console.log("\nšŸŽŸļø Continue Configuration\n");
18
+ console.log("Add to ~/.continue/config.json:\n");
19
+ console.log("```json");
20
+ console.log("{");
21
+ console.log(' "models": [');
22
+ console.log(" {");
23
+ console.log(' "title": "Token2Chat",');
24
+ console.log(' "provider": "openai",');
25
+ console.log(' "model": "anthropic/claude-sonnet-4",');
26
+ console.log(` "apiBase": "${baseUrl}",`);
27
+ console.log(` "apiKey": "${apiKey}"`);
28
+ console.log(" }");
29
+ console.log(" ]");
30
+ console.log("}");
31
+ console.log("```\n");
32
+ console.log("For chat + autocomplete, add to tabAutocompleteModel as well:\n");
33
+ console.log("```json");
34
+ console.log("{");
35
+ console.log(' "tabAutocompleteModel": {');
36
+ console.log(' "title": "Token2Chat Autocomplete",');
37
+ console.log(' "provider": "openai",');
38
+ console.log(' "model": "anthropic/claude-sonnet-4",');
39
+ console.log(` "apiBase": "${baseUrl}",`);
40
+ console.log(' "apiKey": "t2c-local"');
41
+ console.log(" }");
42
+ console.log("}");
43
+ console.log("```\n");
44
+ console.log("Available models: anthropic/claude-opus-4, openai/gpt-4o, etc.\n");
45
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Cursor adapter - Generate config for Cursor IDE
3
+ */
4
+ import type { T2CConfig, AdapterConfigOptions } from "../config.js";
5
+ export declare function cursorAdapter(t2cConfig: T2CConfig, opts: AdapterConfigOptions): Promise<void>;
@@ -0,0 +1,23 @@
1
+ export async function cursorAdapter(t2cConfig, opts) {
2
+ const baseUrl = `http://127.0.0.1:${t2cConfig.proxyPort}/v1`;
3
+ const apiKey = opts.proxySecret ?? "t2c-local";
4
+ if (opts.json) {
5
+ console.log(JSON.stringify({
6
+ openai: {
7
+ baseUrl,
8
+ apiKey,
9
+ },
10
+ }, null, 2));
11
+ return;
12
+ }
13
+ console.log("\nšŸŽŸļø Cursor Configuration\n");
14
+ console.log("In Cursor Settings (Cmd/Ctrl + ,), configure:\n");
15
+ console.log(" 1. OpenAI Base URL:");
16
+ console.log(` ${baseUrl}\n`);
17
+ console.log(" 2. OpenAI API Key:");
18
+ console.log(` ${apiKey}\n`);
19
+ console.log(" 3. Model:");
20
+ console.log(" anthropic/claude-sonnet-4 (or any OpenRouter model)\n");
21
+ console.log("Note: Cursor uses OpenRouter-style model IDs (with /).");
22
+ console.log("Available models: anthropic/claude-opus-4, openai/gpt-4o, etc.\n");
23
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Environment adapter - Generate env vars for generic OpenAI-compatible tools
3
+ */
4
+ import type { T2CConfig, AdapterConfigOptions } from "../config.js";
5
+ export declare function envAdapter(t2cConfig: T2CConfig, opts: AdapterConfigOptions): Promise<void>;
@@ -0,0 +1,25 @@
1
+ export async function envAdapter(t2cConfig, opts) {
2
+ const baseUrl = `http://127.0.0.1:${t2cConfig.proxyPort}/v1`;
3
+ const apiKey = opts.proxySecret ?? "t2c-local";
4
+ if (opts.json) {
5
+ console.log(JSON.stringify({
6
+ OPENAI_API_KEY: apiKey,
7
+ OPENAI_BASE_URL: baseUrl,
8
+ }, null, 2));
9
+ return;
10
+ }
11
+ console.log("\nšŸŽŸļø Environment Variables\n");
12
+ console.log("For any OpenAI-compatible tool, set these environment variables:\n");
13
+ console.log(` export OPENAI_API_KEY=${apiKey}`);
14
+ console.log(` export OPENAI_BASE_URL=${baseUrl}\n`);
15
+ console.log("Or in a .env file:\n");
16
+ console.log(` OPENAI_API_KEY=${apiKey}`);
17
+ console.log(` OPENAI_BASE_URL=${baseUrl}\n`);
18
+ console.log("Model format: provider/model (e.g., anthropic/claude-sonnet-4)\n");
19
+ console.log("These work with tools like:");
20
+ console.log(" - LangChain");
21
+ console.log(" - LlamaIndex");
22
+ console.log(" - Continue.dev");
23
+ console.log(" - Aider");
24
+ console.log(" - Any OpenAI SDK-based application\n");
25
+ }
@@ -0,0 +1,6 @@
1
+ export { openclawAdapter } from "./openclaw.js";
2
+ export { cursorAdapter } from "./cursor.js";
3
+ export { envAdapter } from "./env.js";
4
+ export { clineAdapter } from "./cline.js";
5
+ export { continueAdapter } from "./continue.js";
6
+ export { aiderAdapter } from "./aider.js";
@@ -0,0 +1,6 @@
1
+ export { openclawAdapter } from "./openclaw.js";
2
+ export { cursorAdapter } from "./cursor.js";
3
+ export { envAdapter } from "./env.js";
4
+ export { clineAdapter } from "./cline.js";
5
+ export { continueAdapter } from "./continue.js";
6
+ export { aiderAdapter } from "./aider.js";
@@ -0,0 +1,2 @@
1
+ import type { T2CConfig, AdapterConfigOptions } from "../config.js";
2
+ export declare function openclawAdapter(t2cConfig: T2CConfig, opts: AdapterConfigOptions): Promise<void>;
@@ -0,0 +1,167 @@
1
+ /**
2
+ * OpenClaw adapter - Generate config for OpenClaw
3
+ */
4
+ import fs from "node:fs/promises";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ // Popular models available through the Gate
8
+ const GATE_MODELS = [
9
+ // OpenAI
10
+ { id: "openai-gpt-4o-mini", name: "GPT-4o Mini", contextWindow: 128_000 },
11
+ { id: "openai-gpt-4o", name: "GPT-4o", contextWindow: 128_000 },
12
+ { id: "openai-gpt-5.2-pro", name: "GPT-5.2 Pro", contextWindow: 400_000 },
13
+ { id: "openai-o3-pro", name: "OpenAI o3 Pro", contextWindow: 200_000 },
14
+ // Anthropic
15
+ { id: "anthropic-claude-opus-4.5", name: "Claude Opus 4.5", contextWindow: 200_000 },
16
+ { id: "anthropic-claude-sonnet-4.5", name: "Claude Sonnet 4.5", contextWindow: 200_000 },
17
+ { id: "anthropic-claude-opus-4", name: "Claude Opus 4", contextWindow: 200_000 },
18
+ { id: "anthropic-claude-sonnet-4", name: "Claude Sonnet 4", contextWindow: 200_000 },
19
+ // Google
20
+ { id: "google-gemini-3-flash-preview", name: "Gemini 3 Flash", contextWindow: 1_000_000 },
21
+ { id: "google-gemini-2.5-pro-preview", name: "Gemini 2.5 Pro", contextWindow: 1_000_000 },
22
+ // Others
23
+ { id: "deepseek-deepseek-r1", name: "DeepSeek R1", contextWindow: 64_000 },
24
+ { id: "qwen-qwen3-coder-next", name: "Qwen3 Coder", contextWindow: 256_000 },
25
+ { id: "moonshotai-kimi-k2.5", name: "Kimi K2.5", contextWindow: 256_000 },
26
+ ];
27
+ function generateOpenClawConfig(t2cConfig, apiKey) {
28
+ return {
29
+ models: {
30
+ mode: "merge",
31
+ providers: {
32
+ token2chat: {
33
+ baseUrl: `http://127.0.0.1:${t2cConfig.proxyPort}/v1`,
34
+ apiKey,
35
+ api: "openai-completions",
36
+ authHeader: false,
37
+ models: GATE_MODELS.map((m) => ({
38
+ id: m.id,
39
+ name: m.name,
40
+ reasoning: false,
41
+ input: ["text"],
42
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
43
+ contextWindow: m.contextWindow,
44
+ maxTokens: 16_384,
45
+ })),
46
+ },
47
+ },
48
+ },
49
+ };
50
+ }
51
+ async function loadOpenClawConfig() {
52
+ const configPath = path.join(os.homedir(), ".openclaw", "openclaw.json");
53
+ try {
54
+ const raw = await fs.readFile(configPath, "utf-8");
55
+ return { config: JSON.parse(raw), path: configPath };
56
+ }
57
+ catch (e) {
58
+ // Distinguish between file not found and parse error
59
+ if (e.code === "ENOENT") {
60
+ return null;
61
+ }
62
+ // If file exists but is invalid JSON, throw with helpful message
63
+ throw new Error(`Failed to parse ${configPath}: ${e.message}`);
64
+ }
65
+ }
66
+ async function saveOpenClawConfig(config, configPath) {
67
+ // Create backup before modifying
68
+ const backupPath = `${configPath}.backup.${Date.now()}`;
69
+ try {
70
+ const existing = await fs.readFile(configPath, "utf-8");
71
+ await fs.writeFile(backupPath, existing);
72
+ }
73
+ catch {
74
+ // File may not exist yet
75
+ }
76
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2));
77
+ }
78
+ function mergeDeep(target, source) {
79
+ const result = { ...target };
80
+ for (const key of Object.keys(source)) {
81
+ if (source[key] &&
82
+ typeof source[key] === "object" &&
83
+ !Array.isArray(source[key]) &&
84
+ target[key] &&
85
+ typeof target[key] === "object" &&
86
+ !Array.isArray(target[key])) {
87
+ result[key] = mergeDeep(target[key], source[key]);
88
+ }
89
+ else {
90
+ result[key] = source[key];
91
+ }
92
+ }
93
+ return result;
94
+ }
95
+ export async function openclawAdapter(t2cConfig, opts) {
96
+ const apiKey = opts.proxySecret ?? "t2c-local";
97
+ const patch = generateOpenClawConfig(t2cConfig, apiKey);
98
+ if (opts.json) {
99
+ console.log(JSON.stringify(patch, null, 2));
100
+ return;
101
+ }
102
+ if (opts.apply) {
103
+ let existing;
104
+ try {
105
+ existing = await loadOpenClawConfig();
106
+ }
107
+ catch (e) {
108
+ console.error(`Error reading OpenClaw config: ${e.message}`);
109
+ console.error("\nPossible fixes:");
110
+ console.error(" 1. Fix the JSON syntax error in openclaw.json");
111
+ console.error(" 2. Run 't2c config openclaw' (without --apply) to see the config to add manually");
112
+ process.exit(1);
113
+ }
114
+ if (!existing) {
115
+ console.error("OpenClaw config not found at ~/.openclaw/openclaw.json");
116
+ console.error("\nTo set up OpenClaw first:");
117
+ console.error(" openclaw onboard\n");
118
+ console.error("Or create the config manually and run this command again.");
119
+ process.exit(1);
120
+ }
121
+ // Check if token2chat provider already exists
122
+ const existingT2C = existing.config.models?.providers?.token2chat;
123
+ if (existingT2C) {
124
+ console.log("Token2Chat provider already configured in OpenClaw.");
125
+ console.log("Updating configuration...\n");
126
+ }
127
+ const merged = mergeDeep(existing.config, patch);
128
+ try {
129
+ await saveOpenClawConfig(merged, existing.path);
130
+ }
131
+ catch (e) {
132
+ console.error(`Failed to save config: ${e.message}`);
133
+ process.exit(1);
134
+ }
135
+ console.log("āœ… OpenClaw config updated: ~/.openclaw/openclaw.json\n");
136
+ // Verify the saved config is valid JSON
137
+ try {
138
+ const verify = await fs.readFile(existing.path, "utf-8");
139
+ JSON.parse(verify);
140
+ }
141
+ catch {
142
+ console.error("āš ļø Warning: Config may be corrupted. Check ~/.openclaw/openclaw.json");
143
+ console.error(" A backup was created before modification.");
144
+ }
145
+ console.log("Token2Chat provider added. To use it:\n");
146
+ console.log(" 1. Restart the gateway:");
147
+ console.log(" openclaw gateway restart\n");
148
+ console.log(" 2. Set as default model (optional):");
149
+ console.log(" openclaw models set token2chat/anthropic-claude-sonnet-4\n");
150
+ console.log(" 3. Or add to fallbacks in openclaw.json:");
151
+ console.log(' agents.defaults.model.fallbacks: ["token2chat/anthropic-claude-sonnet-4"]\n');
152
+ return;
153
+ }
154
+ // Default: show instructions
155
+ console.log("\nšŸŽŸļø OpenClaw Configuration\n");
156
+ console.log("Add this to your ~/.openclaw/openclaw.json:\n");
157
+ console.log("```json");
158
+ console.log(JSON.stringify(patch, null, 2));
159
+ console.log("```\n");
160
+ console.log("Or run with --apply to merge automatically:");
161
+ console.log(" t2c config openclaw --apply\n");
162
+ console.log("After updating config:\n");
163
+ console.log(" 1. Restart the gateway:");
164
+ console.log(" openclaw gateway restart\n");
165
+ console.log(" 2. Set as default or fallback model:");
166
+ console.log(" openclaw models set token2chat/anthropic-claude-sonnet-4\n");
167
+ }
@@ -0,0 +1,52 @@
1
+ import { type Proof } from "@cashu/cashu-ts";
2
+ export interface CashuStoreData {
3
+ mint: string;
4
+ unit: string;
5
+ proofs: Proof[];
6
+ }
7
+ export declare class CashuStore {
8
+ private path;
9
+ private data;
10
+ private wallet;
11
+ private mutex;
12
+ constructor(path: string, data: CashuStoreData);
13
+ get mint(): string;
14
+ get balance(): number;
15
+ get proofCount(): number;
16
+ /** Load from file, or create new empty store */
17
+ static load(path: string, mint?: string): Promise<CashuStore>;
18
+ /** Persist to file */
19
+ save(): Promise<void>;
20
+ /** Return a deep copy of the store data */
21
+ exportData(): CashuStoreData;
22
+ /** Returns true if balance is below threshold */
23
+ needsFunding(threshold: number): boolean;
24
+ /**
25
+ * Select proofs totalling at least `amount` sat,
26
+ * encode as a Cashu V4 token, and remove them from the store.
27
+ */
28
+ selectAndEncode(amount: number): Promise<string>;
29
+ /**
30
+ * Import proofs directly into the store.
31
+ * No mint swap — caller is responsible for proof validity.
32
+ */
33
+ importProofs(proofs: Proof[]): Promise<number>;
34
+ /** Get or init CashuWallet (lazy, connects to mint) */
35
+ private getCashuWallet;
36
+ /**
37
+ * Receive an encoded Cashu token — swap at mint for fresh proofs.
38
+ * Used for change/refund tokens from the Gate.
39
+ */
40
+ receiveToken(encodedToken: string): Promise<number>;
41
+ /**
42
+ * Create a mint quote (Lightning invoice) for funding.
43
+ */
44
+ createMintQuote(amount: number): Promise<{
45
+ quote: string;
46
+ request: string;
47
+ }>;
48
+ /**
49
+ * Check and mint tokens from a paid quote.
50
+ */
51
+ mintFromQuote(quoteId: string, amount: number): Promise<number>;
52
+ }