@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.
- package/LICENSE +21 -0
- package/README.md +188 -0
- package/dist/adapters/aider.d.ts +5 -0
- package/dist/adapters/aider.js +29 -0
- package/dist/adapters/cline.d.ts +5 -0
- package/dist/adapters/cline.js +32 -0
- package/dist/adapters/continue.d.ts +5 -0
- package/dist/adapters/continue.js +45 -0
- package/dist/adapters/cursor.d.ts +5 -0
- package/dist/adapters/cursor.js +23 -0
- package/dist/adapters/env.d.ts +5 -0
- package/dist/adapters/env.js +25 -0
- package/dist/adapters/index.d.ts +6 -0
- package/dist/adapters/index.js +6 -0
- package/dist/adapters/openclaw.d.ts +2 -0
- package/dist/adapters/openclaw.js +167 -0
- package/dist/cashu-store.d.ts +52 -0
- package/dist/cashu-store.js +201 -0
- package/dist/commands/audit.d.ts +6 -0
- package/dist/commands/audit.js +340 -0
- package/dist/commands/balance.d.ts +5 -0
- package/dist/commands/balance.js +29 -0
- package/dist/commands/config.d.ts +5 -0
- package/dist/commands/config.js +62 -0
- package/dist/commands/connect.d.ts +1 -0
- package/dist/commands/connect.js +43 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +178 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.js +50 -0
- package/dist/commands/mint.d.ts +5 -0
- package/dist/commands/mint.js +168 -0
- package/dist/commands/recover.d.ts +1 -0
- package/dist/commands/recover.js +61 -0
- package/dist/commands/service.d.ts +7 -0
- package/dist/commands/service.js +378 -0
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +128 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.js +87 -0
- package/dist/config.d.ts +83 -0
- package/dist/config.js +224 -0
- package/dist/connectors/cursor.d.ts +2 -0
- package/dist/connectors/cursor.js +28 -0
- package/dist/connectors/env.d.ts +2 -0
- package/dist/connectors/env.js +38 -0
- package/dist/connectors/index.d.ts +26 -0
- package/dist/connectors/index.js +30 -0
- package/dist/connectors/interface.d.ts +20 -0
- package/dist/connectors/interface.js +1 -0
- package/dist/connectors/openclaw.d.ts +2 -0
- package/dist/connectors/openclaw.js +202 -0
- package/dist/gate-discovery.d.ts +49 -0
- package/dist/gate-discovery.js +142 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +177 -0
- package/dist/proxy.d.ts +11 -0
- package/dist/proxy.js +352 -0
- 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,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,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,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,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,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,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
|
+
}
|