@john2026/cluadex 1.1.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 +282 -0
- package/bin/cluadex +32 -0
- package/bin/cluadex-telegram +188 -0
- package/bin/import-specifier.mjs +7 -0
- package/bin/import-specifier.test.mjs +13 -0
- package/dist/cli.mjs +537535 -0
- package/package.json +153 -0
package/README.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# cluadex
|
|
2
|
+
|
|
3
|
+
<img width="1700" height="460" alt="github-header-banner (3)" src="https://github.com/user-attachments/assets/33a60056-a756-4030-8737-14076d69c3d4" />
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
cluadex is a fork of the Claude Code source that adds a full OpenAI-compatible provider shim, NVIDIA AI (NIM) support, a smart multi-provider router, a Telegram gateway, and local inference via Ollama and Atomic Chat. Every Claude Code tool works — bash, file ops, grep, glob, agents, MCP, tasks — powered by whatever model you choose.
|
|
7
|
+
|
|
8
|
+
**Author:** karen john
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g @john2026/cluadex
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
cluadex
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
On first run, if no provider is configured, Claudex will prompt you to set one up via `/provider`. No Anthropic account required.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Fastest Start
|
|
27
|
+
|
|
28
|
+
Pick a provider, set three env vars, run.
|
|
29
|
+
|
|
30
|
+
### NVIDIA AI — free key, best models
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
export CLAUDE_CODE_USE_NVIDIA=1
|
|
34
|
+
export NVIDIA_API_KEY=nvapi-your-key
|
|
35
|
+
export NVIDIA_MODEL=moonshotai/kimi-k2-instruct
|
|
36
|
+
cluadex
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Free key at [build.nvidia.com](https://build.nvidia.com/).
|
|
40
|
+
|
|
41
|
+
### OpenAI
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
export CLAUDE_CODE_USE_OPENAI=1
|
|
45
|
+
export OPENAI_API_KEY=sk-your-key
|
|
46
|
+
export OPENAI_MODEL=gpt-4o
|
|
47
|
+
cluadex
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Google Gemini — free key
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
export CLAUDE_CODE_USE_GEMINI=1
|
|
54
|
+
export GEMINI_API_KEY=your-key
|
|
55
|
+
export GEMINI_MODEL=gemini-2.0-flash
|
|
56
|
+
cluadex
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Free key at [aistudio.google.com/apikey](https://aistudio.google.com/apikey).
|
|
60
|
+
|
|
61
|
+
### Ollama — fully local, no key
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
ollama pull llama3.1:8b
|
|
65
|
+
export CLAUDE_CODE_USE_OPENAI=1
|
|
66
|
+
export OPENAI_BASE_URL=http://localhost:11434/v1
|
|
67
|
+
export OPENAI_MODEL=llama3.1:8b
|
|
68
|
+
cluadex
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Guides
|
|
74
|
+
|
|
75
|
+
| Audience | Guide |
|
|
76
|
+
|---|---|
|
|
77
|
+
| New to terminals | [Non-Technical Setup](docs/non-technical-setup.md) |
|
|
78
|
+
| Windows | [Windows Quick Start](docs/quick-start-windows.md) |
|
|
79
|
+
| macOS / Linux | [macOS / Linux Quick Start](docs/quick-start-mac-linux.md) |
|
|
80
|
+
| Android (Termux) | [Android Install](ANDROID_INSTALL.md) |
|
|
81
|
+
| Source builds, profiles, diagnostics | [Advanced Setup](docs/advanced-setup.md) |
|
|
82
|
+
| Daily Ollama workflow | [Playbook](PLAYBOOK.md) |
|
|
83
|
+
| Telegram bot | [Telegram Gateway](telegram-gateway/README.md) |
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Supported Providers
|
|
88
|
+
|
|
89
|
+
| Provider | Env flag | Key var |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| NVIDIA AI (NIM) | `CLAUDE_CODE_USE_NVIDIA=1` | `NVIDIA_API_KEY` |
|
|
92
|
+
| OpenAI / any OpenAI-compatible | `CLAUDE_CODE_USE_OPENAI=1` | `OPENAI_API_KEY` |
|
|
93
|
+
| Google Gemini | `CLAUDE_CODE_USE_GEMINI=1` | `GEMINI_API_KEY` |
|
|
94
|
+
| GitHub Models | `CLAUDE_CODE_USE_GITHUB=1` | `GITHUB_TOKEN` |
|
|
95
|
+
| Amazon Bedrock | `CLAUDE_CODE_USE_BEDROCK=1` | AWS credentials |
|
|
96
|
+
| Google Vertex AI | `CLAUDE_CODE_USE_VERTEX=1` | GCP credentials |
|
|
97
|
+
| Microsoft Foundry | `CLAUDE_CODE_USE_FOUNDRY=1` | `ANTHROPIC_FOUNDRY_API_KEY` |
|
|
98
|
+
| Ollama (local) | `CLAUDE_CODE_USE_OPENAI=1` + localhost URL | none |
|
|
99
|
+
| Atomic Chat (Apple Silicon) | `CLAUDE_CODE_USE_OPENAI=1` + 127.0.0.1:1337 | none |
|
|
100
|
+
| Anthropic (default) | none | `ANTHROPIC_API_KEY` |
|
|
101
|
+
|
|
102
|
+
Any OpenAI-compatible endpoint works: DeepSeek, Groq, Mistral, Together AI, OpenRouter, LM Studio, Azure OpenAI, and more.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## NVIDIA AI Models
|
|
107
|
+
|
|
108
|
+
| Model | Best for |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `moonshotai/kimi-k2-instruct` | Reasoning, coding (default) |
|
|
111
|
+
| `nvidia/llama-3.1-nemotron-ultra-253b-v1` | Flagship quality |
|
|
112
|
+
| `meta/llama-3.3-70b-instruct` | Balanced speed/quality |
|
|
113
|
+
| `meta/llama-3.1-8b-instruct` | Fast, lightweight |
|
|
114
|
+
| `deepseek-ai/deepseek-r1` | Deep reasoning |
|
|
115
|
+
| `qwen/qwen3-235b-a22b` | Large MoE |
|
|
116
|
+
| `mistralai/mistral-large-2-instruct` | Instruction following |
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Startup Themes
|
|
121
|
+
|
|
122
|
+
Set `CLAUDEX_THEME` to change the banner color scheme:
|
|
123
|
+
|
|
124
|
+
| Theme | Colors |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `sunset` | warm orange → rust (default) |
|
|
127
|
+
| `ocean` | deep teal → electric cyan |
|
|
128
|
+
| `aurora` | green → violet |
|
|
129
|
+
| `neon` | hot pink → electric blue |
|
|
130
|
+
| `mono` | white → grey |
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
export CLAUDEX_THEME=ocean
|
|
134
|
+
cluadex
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Profile Launcher
|
|
140
|
+
|
|
141
|
+
Save a provider profile once, launch with one command:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# save a profile (also works via /provider inside the CLI)
|
|
145
|
+
bun run profile:init -- --provider nvidia --api-key nvapi-...
|
|
146
|
+
bun run profile:init -- --provider openai --api-key sk-...
|
|
147
|
+
bun run profile:init -- --provider ollama --model llama3.1:8b
|
|
148
|
+
|
|
149
|
+
# launch from saved profile
|
|
150
|
+
bun run dev:profile
|
|
151
|
+
|
|
152
|
+
# provider-specific launchers
|
|
153
|
+
bun run dev:nvidia
|
|
154
|
+
bun run dev:openai
|
|
155
|
+
bun run dev:ollama
|
|
156
|
+
bun run dev:gemini
|
|
157
|
+
bun run dev:codex
|
|
158
|
+
bun run dev:atomic-chat
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Smart Router
|
|
164
|
+
|
|
165
|
+
Benchmarks all configured providers on startup and routes each request to the fastest, cheapest, healthiest option:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
export ROUTER_MODE=smart
|
|
169
|
+
export ROUTER_STRATEGY=balanced # latency | cost | balanced
|
|
170
|
+
cluadex
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Telegram Gateway
|
|
176
|
+
|
|
177
|
+
Use Claudex through a Telegram bot. Each user gets an isolated session.
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# configure (one time)
|
|
181
|
+
cluadex telegram setup --token 123456:ABC --provider nvidia
|
|
182
|
+
|
|
183
|
+
# allow yourself
|
|
184
|
+
cluadex telegram permit 987654321
|
|
185
|
+
|
|
186
|
+
# start the gateway
|
|
187
|
+
cluadex telegram start
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Or manage from inside the CLI:
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
/telegram setup --token 123456:ABC --provider nvidia
|
|
194
|
+
/telegram permit 987654321
|
|
195
|
+
/telegram status
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Full guide: [telegram-gateway/README.md](telegram-gateway/README.md)
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## What Works
|
|
203
|
+
|
|
204
|
+
- All tools: Bash, FileRead, FileWrite, FileEdit, Glob, Grep, WebFetch, WebSearch, Agent, MCP, LSP, NotebookEdit, Tasks
|
|
205
|
+
- Real-time token streaming
|
|
206
|
+
- Multi-step tool chains
|
|
207
|
+
- Base64 and URL image inputs (vision models)
|
|
208
|
+
- Slash commands: /commit, /review, /compact, /diff, /doctor, /provider, /telegram, etc.
|
|
209
|
+
- Sub-agents via AgentTool
|
|
210
|
+
- Persistent memory
|
|
211
|
+
|
|
212
|
+
## What's Different from Upstream
|
|
213
|
+
|
|
214
|
+
- No Anthropic extended thinking (OpenAI models use different reasoning)
|
|
215
|
+
- No prompt caching (Anthropic-specific)
|
|
216
|
+
- No Anthropic beta headers
|
|
217
|
+
- Token output defaults to 32K — models that cap lower are handled gracefully
|
|
218
|
+
- First-run skips Anthropic login if a provider profile is saved
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Web Search
|
|
223
|
+
|
|
224
|
+
`WebSearch` is disabled by default for non-Anthropic providers. Set a [Firecrawl](https://firecrawl.dev) key to enable it:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
export FIRECRAWL_API_KEY=your-key
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Free tier includes 500 credits. With this set, `WebSearch` works for all providers and `WebFetch` handles JS-rendered pages.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## How the Shim Works
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
Claude Code Tool System
|
|
238
|
+
│
|
|
239
|
+
▼
|
|
240
|
+
Anthropic SDK interface (duck-typed)
|
|
241
|
+
│
|
|
242
|
+
▼
|
|
243
|
+
openaiShim.ts ◄── translates formats
|
|
244
|
+
│
|
|
245
|
+
▼
|
|
246
|
+
OpenAI Chat Completions API
|
|
247
|
+
│
|
|
248
|
+
▼
|
|
249
|
+
Any compatible model
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Model Quality Reference
|
|
255
|
+
|
|
256
|
+
| Model | Tool Calling | Code | Speed |
|
|
257
|
+
|---|---|---|---|
|
|
258
|
+
| GPT-4o | Excellent | Excellent | Fast |
|
|
259
|
+
| Kimi K2 (NVIDIA) | Excellent | Excellent | Fast |
|
|
260
|
+
| DeepSeek-V3 | Great | Great | Fast |
|
|
261
|
+
| Gemini 2.0 Flash | Great | Good | Very Fast |
|
|
262
|
+
| Llama 3.3 70B | Good | Good | Medium |
|
|
263
|
+
| Mistral Large | Good | Good | Fast |
|
|
264
|
+
| GPT-4o-mini | Good | Good | Very Fast |
|
|
265
|
+
| Qwen 2.5 72B | Good | Good | Medium |
|
|
266
|
+
| Models < 7B | Limited | Limited | Very Fast |
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## VS Code Extension
|
|
271
|
+
|
|
272
|
+
Install the bundled extension from `vscode-extension/openclaude-vscode` for one-click terminal launch and the `Claudex Terminal Black` theme.
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Origin
|
|
277
|
+
|
|
278
|
+
Fork of the Claude Code source snapshot that became publicly accessible via an npm source map exposure on March 31, 2026. The original source is the property of Anthropic. This project is not affiliated with or endorsed by Anthropic.
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
Educational and research use. Original source subject to Anthropic's terms. Shim additions and new provider integrations are public domain.
|
package/bin/cluadex
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Claudex — Claude Code with any LLM
|
|
5
|
+
*
|
|
6
|
+
* If dist/cli.mjs exists (built), run that.
|
|
7
|
+
* Otherwise, tell the user to build first or use `bun run dev`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync } from 'fs'
|
|
11
|
+
import { join, dirname } from 'path'
|
|
12
|
+
import { fileURLToPath, pathToFileURL } from 'url'
|
|
13
|
+
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
15
|
+
const distPath = join(__dirname, '..', 'dist', 'cli.mjs')
|
|
16
|
+
|
|
17
|
+
if (existsSync(distPath)) {
|
|
18
|
+
await import(pathToFileURL(distPath).href)
|
|
19
|
+
} else {
|
|
20
|
+
console.error(`
|
|
21
|
+
cluadex: dist/cli.mjs not found.
|
|
22
|
+
|
|
23
|
+
Build first:
|
|
24
|
+
bun run build
|
|
25
|
+
|
|
26
|
+
Or run directly with Bun:
|
|
27
|
+
bun run dev
|
|
28
|
+
|
|
29
|
+
See README.md for setup instructions.
|
|
30
|
+
`)
|
|
31
|
+
process.exit(1)
|
|
32
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* claudex telegram — manage the Claudex Telegram gateway
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* claudex telegram setup --token <token> --provider <provider>
|
|
7
|
+
* claudex telegram permit <user-id>
|
|
8
|
+
* claudex telegram revoke <user-id>
|
|
9
|
+
* claudex telegram status
|
|
10
|
+
* claudex telegram start
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync } from 'node:fs'
|
|
14
|
+
import { homedir } from 'node:os'
|
|
15
|
+
import { join, dirname } from 'node:path'
|
|
16
|
+
import { fileURLToPath } from 'node:url'
|
|
17
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
18
|
+
import { spawn } from 'node:child_process'
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
21
|
+
const CONFIG_DIR = join(homedir(), '.claudex')
|
|
22
|
+
const CONFIG_PATH = join(CONFIG_DIR, 'telegram.json')
|
|
23
|
+
|
|
24
|
+
function loadCfg() {
|
|
25
|
+
if (!existsSync(CONFIG_PATH)) return null
|
|
26
|
+
try { return JSON.parse(readFileSync(CONFIG_PATH, 'utf8')) } catch { return null }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function saveCfg(cfg) {
|
|
30
|
+
if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true })
|
|
31
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2), { encoding: 'utf8', mode: 0o600 })
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseArg(name) {
|
|
35
|
+
const args = process.argv.slice(3)
|
|
36
|
+
const eqForm = args.find(a => a.startsWith(`${name}=`))
|
|
37
|
+
if (eqForm) return eqForm.split('=').slice(1).join('=')
|
|
38
|
+
const idx = args.indexOf(name)
|
|
39
|
+
if (idx !== -1 && args[idx + 1] && !args[idx + 1].startsWith('--')) return args[idx + 1]
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const sub = process.argv[2]
|
|
44
|
+
|
|
45
|
+
switch (sub) {
|
|
46
|
+
case 'setup': {
|
|
47
|
+
const token = parseArg('--token')
|
|
48
|
+
const provider = parseArg('--provider') ?? 'openai'
|
|
49
|
+
const timeout = parseInt(parseArg('--timeout') ?? '300000', 10)
|
|
50
|
+
const maxSess = parseInt(parseArg('--max-sessions') ?? '10', 10)
|
|
51
|
+
|
|
52
|
+
if (!token) {
|
|
53
|
+
console.error('Usage: claudex telegram setup --token <bot-token> --provider <openai|nvidia|gemini|ollama>')
|
|
54
|
+
console.error('')
|
|
55
|
+
console.error('Get a bot token from @BotFather on Telegram.')
|
|
56
|
+
process.exit(1)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const cfg = {
|
|
60
|
+
botToken: token,
|
|
61
|
+
allowedIds: loadCfg()?.allowedIds ?? [],
|
|
62
|
+
provider,
|
|
63
|
+
idleTimeoutMs: timeout,
|
|
64
|
+
maxSessions: maxSess,
|
|
65
|
+
createdAt: loadCfg()?.createdAt ?? new Date().toISOString(),
|
|
66
|
+
updatedAt: new Date().toISOString(),
|
|
67
|
+
}
|
|
68
|
+
saveCfg(cfg)
|
|
69
|
+
console.log(`✅ Config saved: ${CONFIG_PATH}`)
|
|
70
|
+
console.log(` Provider: ${provider}`)
|
|
71
|
+
console.log(` Token: ${token.slice(0, 8)}…`)
|
|
72
|
+
console.log(` Timeout: ${Math.round(timeout / 60000)} min`)
|
|
73
|
+
console.log(` Sessions: max ${maxSess}`)
|
|
74
|
+
console.log('')
|
|
75
|
+
console.log('Next steps:')
|
|
76
|
+
console.log(' claudex telegram permit <your-telegram-id>')
|
|
77
|
+
console.log(' claudex telegram start')
|
|
78
|
+
console.log('')
|
|
79
|
+
console.log('Find your Telegram ID by messaging @userinfobot')
|
|
80
|
+
break
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
case 'permit': {
|
|
84
|
+
const idStr = process.argv[3]
|
|
85
|
+
const id = parseInt(idStr, 10)
|
|
86
|
+
if (!idStr || isNaN(id)) {
|
|
87
|
+
console.error('Usage: claudex telegram permit <telegram-user-id>')
|
|
88
|
+
console.error('')
|
|
89
|
+
console.error('Find your ID by messaging @userinfobot on Telegram.')
|
|
90
|
+
process.exit(1)
|
|
91
|
+
}
|
|
92
|
+
const cfg = loadCfg()
|
|
93
|
+
if (!cfg) {
|
|
94
|
+
console.error('❌ No config found. Run: claudex telegram setup --token <token>')
|
|
95
|
+
process.exit(1)
|
|
96
|
+
}
|
|
97
|
+
if (cfg.allowedIds.includes(id)) {
|
|
98
|
+
console.log(`ℹ️ User ${id} is already permitted.`)
|
|
99
|
+
} else {
|
|
100
|
+
cfg.allowedIds.push(id)
|
|
101
|
+
cfg.updatedAt = new Date().toISOString()
|
|
102
|
+
saveCfg(cfg)
|
|
103
|
+
console.log(`✅ Permitted user ${id}`)
|
|
104
|
+
console.log(` Total allowed: ${cfg.allowedIds.length}`)
|
|
105
|
+
}
|
|
106
|
+
break
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
case 'revoke': {
|
|
110
|
+
const idStr = process.argv[3]
|
|
111
|
+
const id = parseInt(idStr, 10)
|
|
112
|
+
if (!idStr || isNaN(id)) {
|
|
113
|
+
console.error('Usage: claudex telegram revoke <telegram-user-id>')
|
|
114
|
+
process.exit(1)
|
|
115
|
+
}
|
|
116
|
+
const cfg = loadCfg()
|
|
117
|
+
if (!cfg) { console.error('❌ No config found.'); process.exit(1) }
|
|
118
|
+
const before = cfg.allowedIds.length
|
|
119
|
+
cfg.allowedIds = cfg.allowedIds.filter(x => x !== id)
|
|
120
|
+
if (cfg.allowedIds.length < before) {
|
|
121
|
+
cfg.updatedAt = new Date().toISOString()
|
|
122
|
+
saveCfg(cfg)
|
|
123
|
+
console.log(`✅ Revoked user ${id}`)
|
|
124
|
+
} else {
|
|
125
|
+
console.log(`ℹ️ User ${id} was not in the allowed list.`)
|
|
126
|
+
}
|
|
127
|
+
break
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
case 'status': {
|
|
131
|
+
const cfg = loadCfg()
|
|
132
|
+
if (!cfg) {
|
|
133
|
+
console.log('❌ No Telegram gateway configured.')
|
|
134
|
+
console.log('')
|
|
135
|
+
console.log('Run: claudex telegram setup --token <token> --provider <provider>')
|
|
136
|
+
} else {
|
|
137
|
+
console.log('📡 Telegram Gateway Config')
|
|
138
|
+
console.log(` Config: ${CONFIG_PATH}`)
|
|
139
|
+
console.log(` Token: ${cfg.botToken.slice(0, 8)}…`)
|
|
140
|
+
console.log(` Provider: ${cfg.provider}`)
|
|
141
|
+
console.log(` Allowed: ${cfg.allowedIds.length === 0 ? 'open (no whitelist)' : cfg.allowedIds.join(', ')}`)
|
|
142
|
+
console.log(` Timeout: ${Math.round(cfg.idleTimeoutMs / 60000)} min`)
|
|
143
|
+
console.log(` Sessions: max ${cfg.maxSessions}`)
|
|
144
|
+
console.log(` Updated: ${cfg.updatedAt}`)
|
|
145
|
+
}
|
|
146
|
+
break
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
case 'start': {
|
|
150
|
+
const cfg = loadCfg()
|
|
151
|
+
if (!cfg) {
|
|
152
|
+
console.error('❌ No config found. Run: claudex telegram setup --token <token>')
|
|
153
|
+
process.exit(1)
|
|
154
|
+
}
|
|
155
|
+
console.log('🚀 Starting Claudex Telegram Gateway…')
|
|
156
|
+
console.log(` Provider: ${cfg.provider}`)
|
|
157
|
+
console.log(` Allowed: ${cfg.allowedIds.length === 0 ? 'open' : cfg.allowedIds.join(', ')}`)
|
|
158
|
+
console.log('')
|
|
159
|
+
|
|
160
|
+
const botPath = join(__dirname, '..', 'telegram-gateway', 'bot.ts')
|
|
161
|
+
const child = spawn('bun', ['run', botPath], {
|
|
162
|
+
stdio: 'inherit',
|
|
163
|
+
env: { ...process.env, TELEGRAM_BOT_TOKEN: cfg.botToken },
|
|
164
|
+
})
|
|
165
|
+
child.on('exit', code => process.exit(code ?? 0))
|
|
166
|
+
break
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
default: {
|
|
170
|
+
console.log('claudex telegram — Telegram gateway management')
|
|
171
|
+
console.log('')
|
|
172
|
+
console.log('Commands:')
|
|
173
|
+
console.log(' claudex telegram setup --token <token> --provider <provider>')
|
|
174
|
+
console.log(' configure the gateway')
|
|
175
|
+
console.log(' claudex telegram permit <id> allow a Telegram user ID')
|
|
176
|
+
console.log(' claudex telegram revoke <id> remove a Telegram user ID')
|
|
177
|
+
console.log(' claudex telegram status show current config')
|
|
178
|
+
console.log(' claudex telegram start start the gateway')
|
|
179
|
+
console.log('')
|
|
180
|
+
console.log('Providers: openai, nvidia, gemini, ollama, codex')
|
|
181
|
+
console.log('')
|
|
182
|
+
console.log('Example:')
|
|
183
|
+
console.log(' claudex telegram setup --token 123456:ABC --provider nvidia')
|
|
184
|
+
console.log(' claudex telegram permit 987654321')
|
|
185
|
+
console.log(' claudex telegram start')
|
|
186
|
+
break
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import test from 'node:test'
|
|
3
|
+
|
|
4
|
+
import { getDistImportSpecifier } from './import-specifier.mjs'
|
|
5
|
+
|
|
6
|
+
test('builds a file URL import specifier for dist/cli.mjs', () => {
|
|
7
|
+
const specifier = getDistImportSpecifier('C:\\repo\\bin')
|
|
8
|
+
|
|
9
|
+
assert.equal(
|
|
10
|
+
specifier,
|
|
11
|
+
'file:///C:/repo/dist/cli.mjs',
|
|
12
|
+
)
|
|
13
|
+
})
|