rex-claude 4.0.0 → 6.0.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.
Files changed (38) hide show
  1. package/dist/agents-JIZXXASP.js +853 -0
  2. package/dist/app-3VWDSH5F.js +248 -0
  3. package/dist/audio-US2J627E.js +196 -0
  4. package/dist/audit-ZVTGE4L4.js +8 -0
  5. package/dist/call-AQZ3Z5SE.js +143 -0
  6. package/dist/chunk-5ND7JYY3.js +62 -0
  7. package/dist/chunk-6SRV2I2H.js +56 -0
  8. package/dist/{setup-AO3MW46W.js → chunk-A7ZLQUOX.js} +93 -16
  9. package/dist/chunk-E5UYN3W7.js +105 -0
  10. package/dist/chunk-HAHJD3QH.js +147 -0
  11. package/dist/{init-DLFEGD6O.js → chunk-KR7ISYZH.js} +328 -29
  12. package/dist/chunk-LTOM55UV.js +154 -0
  13. package/dist/chunk-PDX44BCA.js +11 -0
  14. package/dist/chunk-PPGYFMU5.js +67 -0
  15. package/dist/{chunk-7AGI43F5.js → chunk-WBMVBMWB.js} +4 -2
  16. package/dist/{context-FN5O5YBI.js → context-XNCG2M5Q.js} +2 -1
  17. package/dist/daemon-5KNSNFTD.js +208 -0
  18. package/dist/gateway-YLP66MCQ.js +2273 -0
  19. package/dist/hammerspoon/rex-call-watcher.lua +186 -0
  20. package/dist/index.js +309 -15
  21. package/dist/init-RDZFIBLA.js +30 -0
  22. package/dist/install-63JBDPRU.js +41 -0
  23. package/dist/{llm-YRORUH7E.js → llm-RALIPIMI.js} +2 -1
  24. package/dist/mcp_registry-DX4GGSP6.js +514 -0
  25. package/dist/migrate-GDO37TI5.js +87 -0
  26. package/dist/{optimize-UKMAGQQE.js → optimize-5TE5RKZV.js} +2 -1
  27. package/dist/paths-4SECM6E6.js +38 -0
  28. package/dist/preload-I3MYBVNU.js +78 -0
  29. package/dist/projects-V6TSLO7E.js +17 -0
  30. package/dist/{prune-2PPIVDXK.js → prune-B7F5B5OF.js} +2 -1
  31. package/dist/recategorize-YXYIMQLZ.js +155 -0
  32. package/dist/router-2JD34COX.js +12 -0
  33. package/dist/self-improve-YK7RCYF4.js +197 -0
  34. package/dist/setup-KNDTVFO6.js +8 -0
  35. package/dist/skills-AIWFY5NH.js +374 -0
  36. package/dist/voice-RITC3EVC.js +248 -0
  37. package/package.json +12 -3
  38. package/dist/gateway-EKMU5D7J.js +0 -784
@@ -1,784 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/gateway.ts
4
- import { homedir } from "os";
5
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
6
- import { join } from "path";
7
- import { execSync } from "child_process";
8
- var OLLAMA_URL = process.env.OLLAMA_URL || "http://localhost:11434";
9
- var STATE_FILE = join(homedir(), ".rex-memory", "gateway-state.json");
10
- var LOG_FILE = join(homedir(), ".claude", "rex-gateway-commands.log");
11
- function loadConfig() {
12
- const defaults = {
13
- macTailscaleIp: "100.112.24.122",
14
- macAddress: "52:f1:cf:b2:a5:32",
15
- vpsTailscaleIp: "100.86.167.118",
16
- pollTimeout: 30,
17
- maxOutputLength: 4e3
18
- };
19
- try {
20
- const settingsPath = join(homedir(), ".claude", "settings.json");
21
- const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
22
- const gw = settings.env || {};
23
- return {
24
- macTailscaleIp: gw.REX_MAC_TAILSCALE_IP || defaults.macTailscaleIp,
25
- macAddress: gw.REX_MAC_ADDRESS || defaults.macAddress,
26
- vpsTailscaleIp: gw.REX_VPS_TAILSCALE_IP || defaults.vpsTailscaleIp,
27
- pollTimeout: parseInt(gw.REX_POLL_TIMEOUT || "") || defaults.pollTimeout,
28
- maxOutputLength: parseInt(gw.REX_MAX_OUTPUT || "") || defaults.maxOutputLength
29
- };
30
- } catch {
31
- return defaults;
32
- }
33
- }
34
- function loadState() {
35
- try {
36
- if (existsSync(STATE_FILE)) {
37
- return JSON.parse(readFileSync(STATE_FILE, "utf-8"));
38
- }
39
- } catch {
40
- }
41
- return { mode: "qwen", lastActivity: (/* @__PURE__ */ new Date()).toISOString(), sessionsCount: 0 };
42
- }
43
- function saveState(state2) {
44
- try {
45
- const dir = join(homedir(), ".rex-memory");
46
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
47
- writeFileSync(STATE_FILE, JSON.stringify(state2, null, 2));
48
- } catch {
49
- }
50
- }
51
- var state = loadState();
52
- var config = loadConfig();
53
- function logCommand(from, command, result) {
54
- try {
55
- const ts = (/* @__PURE__ */ new Date()).toISOString();
56
- const entry = `[${ts}] @${from}: ${command} -> ${result.slice(0, 200)}
57
- `;
58
- const dir = join(homedir(), ".claude");
59
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
60
- writeFileSync(LOG_FILE, entry, { flag: "a" });
61
- } catch {
62
- }
63
- }
64
- var COLORS = {
65
- reset: "\x1B[0m",
66
- green: "\x1B[32m",
67
- yellow: "\x1B[33m",
68
- red: "\x1B[31m",
69
- dim: "\x1B[2m",
70
- bold: "\x1B[1m",
71
- cyan: "\x1B[36m"
72
- };
73
- function getCredentials() {
74
- const settingsPath = join(homedir(), ".claude", "settings.json");
75
- try {
76
- const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
77
- const token2 = settings.env?.REX_TELEGRAM_BOT_TOKEN;
78
- const chatId2 = settings.env?.REX_TELEGRAM_CHAT_ID;
79
- if (token2 && chatId2) return { token: token2, chatId: chatId2 };
80
- } catch {
81
- }
82
- const token = process.env.REX_TELEGRAM_BOT_TOKEN;
83
- const chatId = process.env.REX_TELEGRAM_CHAT_ID;
84
- if (token && chatId) return { token, chatId };
85
- return null;
86
- }
87
- async function tg(token, method, body) {
88
- try {
89
- const res = await fetch(`https://api.telegram.org/bot${token}/${method}`, {
90
- method: "POST",
91
- headers: { "Content-Type": "application/json" },
92
- body: JSON.stringify(body)
93
- });
94
- return await res.json();
95
- } catch {
96
- return null;
97
- }
98
- }
99
- async function send(token, chatId, text, keyboard) {
100
- const body = { chat_id: chatId, text, parse_mode: "Markdown" };
101
- if (keyboard) {
102
- body.reply_markup = { inline_keyboard: keyboard };
103
- }
104
- return tg(token, "sendMessage", body);
105
- }
106
- async function editMessage(token, chatId, messageId, text, keyboard) {
107
- const body = { chat_id: chatId, message_id: messageId, text, parse_mode: "Markdown" };
108
- if (keyboard) {
109
- body.reply_markup = { inline_keyboard: keyboard };
110
- }
111
- return tg(token, "editMessageText", body);
112
- }
113
- async function answerCallback(token, callbackId, text) {
114
- return tg(token, "answerCallbackQuery", { callback_query_id: callbackId, text });
115
- }
116
- function isAuthorized(msgChatId, authorizedChatId) {
117
- return String(msgChatId) === String(authorizedChatId);
118
- }
119
- function mainMenu() {
120
- return [
121
- [
122
- { text: "\u{1F4CA} Status", callback_data: "status" },
123
- { text: "\u{1FA7A} Doctor", callback_data: "doctor" },
124
- { text: "\u{1F50D} Memory", callback_data: "memory_menu" }
125
- ],
126
- [
127
- { text: "\u{1F5A5} Git", callback_data: "git" },
128
- { text: "\u26A1 Optimize", callback_data: "optimize" },
129
- { text: "\u{1F4E5} Ingest", callback_data: "ingest" }
130
- ],
131
- [
132
- { text: `\u{1F916} Mode: ${state.mode === "qwen" ? "Qwen (local)" : "Claude"}`, callback_data: "switch_mode" },
133
- { text: "\u{1F9F9} Prune", callback_data: "prune" }
134
- ],
135
- [
136
- { text: "\u{1F4A4} Wake Mac", callback_data: "wake_mac" },
137
- { text: "\u{1F50C} Mac Status", callback_data: "mac_status" }
138
- ],
139
- [
140
- { text: "\u{1F4CB} Sessions", callback_data: "sessions" },
141
- { text: "\u{1F4DD} Logs", callback_data: "logs" }
142
- ]
143
- ];
144
- }
145
- function backButton() {
146
- return [[{ text: "\u25C0\uFE0F Menu", callback_data: "menu" }]];
147
- }
148
- function claudeMenu() {
149
- return [
150
- [
151
- { text: "\u{1F4AC} New Session", callback_data: "claude_new" },
152
- { text: "\u{1F4C2} Continue Last", callback_data: "claude_continue" }
153
- ],
154
- [
155
- { text: "\u{1F4CB} List Sessions", callback_data: "claude_sessions" },
156
- { text: "\u{1F504} Resume #", callback_data: "claude_resume" }
157
- ],
158
- [{ text: "\u25C0\uFE0F Menu", callback_data: "menu" }]
159
- ];
160
- }
161
- function run(cmd, timeout = 3e4) {
162
- try {
163
- return execSync(cmd, { timeout, encoding: "utf-8" }).trim();
164
- } catch (e) {
165
- return e.stderr?.trim() || e.message || "Command failed";
166
- }
167
- }
168
- function strip(text) {
169
- return text.replace(/\x1b\[[0-9;]*m/g, "");
170
- }
171
- function truncate(text, max) {
172
- const limit = max || config.maxOutputLength;
173
- if (text.length <= limit) return text;
174
- return text.slice(0, limit) + "\n\n... (truncated)";
175
- }
176
- async function wakeMac() {
177
- try {
178
- const mac = config.macAddress.replace(/:/g, "");
179
- const pyCmd = `python3 -c "
180
- import socket, struct
181
- mac = bytes.fromhex('${mac}')
182
- pkt = b'\\xff'*6 + mac*16
183
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
184
- s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
185
- s.sendto(pkt, ('255.255.255.255', 9))
186
- s.sendto(pkt, ('${config.macTailscaleIp}', 9))
187
- s.close()
188
- print('Magic packet sent')
189
- "`;
190
- const out = run(pyCmd, 5e3);
191
- if (out.includes("Magic packet sent")) return "\u2705 Magic packet sent to Mac";
192
- } catch {
193
- }
194
- try {
195
- const ping = run(`ping -c 1 -W 2 ${config.macTailscaleIp} 2>/dev/null`, 5e3);
196
- if (ping.includes("1 packets received") || ping.includes("1 received")) {
197
- return "\u2705 Mac is already awake (responds to ping)";
198
- }
199
- } catch {
200
- }
201
- return "\u26A0\uFE0F Magic packet sent \u2014 Mac may take 30s to wake";
202
- }
203
- async function checkMacStatus() {
204
- try {
205
- const ping = run(`ping -c 1 -W 3 ${config.macTailscaleIp} 2>/dev/null`, 5e3);
206
- const online = ping.includes("1 packets received") || ping.includes("1 received");
207
- if (online) {
208
- const ts = run("tailscale status 2>/dev/null | head -5", 5e3);
209
- return { online: true, details: ts };
210
- }
211
- return { online: false, details: "Mac not responding to ping" };
212
- } catch {
213
- return { online: false, details: "Ping failed" };
214
- }
215
- }
216
- async function askLLM(prompt) {
217
- if (state.mode === "qwen") {
218
- return askQwen(prompt);
219
- } else {
220
- return askClaude(prompt);
221
- }
222
- }
223
- async function askQwen(prompt) {
224
- try {
225
- const check = await fetch(`${OLLAMA_URL}/api/tags`);
226
- if (!check.ok) return "\u26A0\uFE0F Ollama not running. /wake to wake Mac first.";
227
- } catch {
228
- return "\u26A0\uFE0F Ollama not running. Wake Mac first.";
229
- }
230
- const out = run(`rex llm "${prompt.replace(/"/g, '\\"').replace(/`/g, "\\`")}"`, 6e4);
231
- if (!out || out.includes("rex-claude") || out.includes("Commands:")) {
232
- return "\u26A0\uFE0F LLM returned no useful response";
233
- }
234
- return truncate(out);
235
- }
236
- async function askClaude(prompt) {
237
- try {
238
- const escapedPrompt = prompt.replace(/"/g, '\\"').replace(/`/g, "\\`");
239
- const out = run(`claude -p "${escapedPrompt}" 2>/dev/null`, 12e4);
240
- if (out) return truncate(out);
241
- return "\u26A0\uFE0F Claude CLI not available or returned empty";
242
- } catch {
243
- return "\u26A0\uFE0F Claude CLI error";
244
- }
245
- }
246
- async function claudeSession(prompt, resume) {
247
- try {
248
- const flag = resume ? "--continue" : "";
249
- const escapedPrompt = prompt.replace(/"/g, '\\"').replace(/`/g, "\\`");
250
- const out = run(`claude ${flag} -p "${escapedPrompt}" 2>/dev/null`, 18e4);
251
- state.sessionsCount++;
252
- saveState(state);
253
- if (out) return truncate(out);
254
- return "\u26A0\uFE0F No response from Claude session";
255
- } catch {
256
- return "\u26A0\uFE0F Claude session error";
257
- }
258
- }
259
- async function handleCallback(token, chatId, messageId, callbackId, data, from) {
260
- await answerCallback(token, callbackId);
261
- logCommand(from, `[btn] ${data}`, "ok");
262
- switch (data) {
263
- case "menu":
264
- await editMessage(
265
- token,
266
- chatId,
267
- messageId,
268
- "\u{1F996} *REX Gateway v3*\nChoisis une action :",
269
- mainMenu()
270
- );
271
- break;
272
- case "status": {
273
- const out = strip(run("rex status"));
274
- await editMessage(
275
- token,
276
- chatId,
277
- messageId,
278
- `\u{1F4CA} *Status*
279
- ${out}`,
280
- backButton()
281
- );
282
- break;
283
- }
284
- case "doctor": {
285
- await editMessage(token, chatId, messageId, "\u{1FA7A} _Running diagnostics..._");
286
- const out = truncate(strip(run("rex doctor")));
287
- await editMessage(
288
- token,
289
- chatId,
290
- messageId,
291
- `\u{1FA7A} *Doctor*
292
- \`\`\`
293
- ${out}
294
- \`\`\``,
295
- backButton()
296
- );
297
- break;
298
- }
299
- case "git": {
300
- const branch = run('git branch --show-current 2>/dev/null || echo "n/a"');
301
- const status = run("git status --short 2>/dev/null | head -15");
302
- const lastCommit = run('git log -1 --format="%s" 2>/dev/null || echo "n/a"');
303
- await editMessage(
304
- token,
305
- chatId,
306
- messageId,
307
- `\u{1F5A5} *Git*
308
- Branch: \`${branch}\`
309
- Last: ${lastCommit}
310
- \`\`\`
311
- ${status || "Clean"}
312
- \`\`\``,
313
- backButton()
314
- );
315
- break;
316
- }
317
- case "optimize": {
318
- await editMessage(token, chatId, messageId, "\u26A1 _Analyzing CLAUDE.md..._");
319
- const out = truncate(strip(run("rex optimize", 6e4)), 3500);
320
- await editMessage(
321
- token,
322
- chatId,
323
- messageId,
324
- `\u26A1 *Optimize*
325
- \`\`\`
326
- ${out}
327
- \`\`\``,
328
- [[
329
- { text: "\u{1F527} Apply", callback_data: "optimize_apply" },
330
- { text: "\u25C0\uFE0F Menu", callback_data: "menu" }
331
- ]]
332
- );
333
- break;
334
- }
335
- case "optimize_apply": {
336
- await editMessage(token, chatId, messageId, "\u{1F527} _Applying optimizations..._");
337
- const out = truncate(strip(run("rex optimize --apply", 12e4)), 3500);
338
- await editMessage(
339
- token,
340
- chatId,
341
- messageId,
342
- `\u{1F527} *Applied*
343
- \`\`\`
344
- ${out}
345
- \`\`\``,
346
- backButton()
347
- );
348
- break;
349
- }
350
- case "ingest": {
351
- await editMessage(token, chatId, messageId, "\u{1F4E5} _Ingesting sessions..._");
352
- const out = truncate(strip(run("rex ingest", 12e4)), 3500);
353
- await editMessage(
354
- token,
355
- chatId,
356
- messageId,
357
- `\u{1F4E5} *Ingest*
358
- \`\`\`
359
- ${out}
360
- \`\`\``,
361
- backButton()
362
- );
363
- break;
364
- }
365
- case "prune": {
366
- await editMessage(token, chatId, messageId, "\u{1F9F9} _Pruning old memories..._");
367
- const out = truncate(strip(run("rex prune", 6e4)));
368
- await editMessage(
369
- token,
370
- chatId,
371
- messageId,
372
- `\u{1F9F9} *Prune*
373
- \`\`\`
374
- ${out}
375
- \`\`\``,
376
- backButton()
377
- );
378
- break;
379
- }
380
- case "memory_menu":
381
- await editMessage(
382
- token,
383
- chatId,
384
- messageId,
385
- "\u{1F50D} *Memory*\nEnvoie ta recherche en texte ou :",
386
- [
387
- [
388
- { text: "\u{1F4E5} Ingest Now", callback_data: "ingest" },
389
- { text: "\u{1F9F9} Prune", callback_data: "prune" }
390
- ],
391
- [
392
- { text: "\u{1F4CA} Stats", callback_data: "memory_stats" },
393
- { text: "\u25C0\uFE0F Menu", callback_data: "menu" }
394
- ]
395
- ]
396
- );
397
- break;
398
- case "memory_stats": {
399
- const out = run("rex prune --stats", 1e4);
400
- await editMessage(
401
- token,
402
- chatId,
403
- messageId,
404
- `\u{1F4CA} *Memory Stats*
405
- \`\`\`
406
- ${strip(out)}
407
- \`\`\``,
408
- backButton()
409
- );
410
- break;
411
- }
412
- case "switch_mode":
413
- state.mode = state.mode === "qwen" ? "claude" : "qwen";
414
- state.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
415
- saveState(state);
416
- await editMessage(
417
- token,
418
- chatId,
419
- messageId,
420
- `\u{1F916} Mode switched to *${state.mode === "qwen" ? "Qwen (local LLM)" : "Claude (CLI)"}*`,
421
- mainMenu()
422
- );
423
- break;
424
- case "wake_mac": {
425
- await editMessage(token, chatId, messageId, "\u{1F4A4} _Sending wake signal..._");
426
- const result = await wakeMac();
427
- await new Promise((r) => setTimeout(r, 3e3));
428
- const status = await checkMacStatus();
429
- await editMessage(
430
- token,
431
- chatId,
432
- messageId,
433
- `\u{1F4A4} *Wake Mac*
434
- ${result}
435
-
436
- \u{1F50C} ${status.online ? "\u{1F7E2} Online" : "\u{1F534} Offline"}
437
- \`${status.details}\``,
438
- [[
439
- { text: "\u{1F504} Check Again", callback_data: "mac_status" },
440
- { text: "\u25C0\uFE0F Menu", callback_data: "menu" }
441
- ]]
442
- );
443
- break;
444
- }
445
- case "mac_status": {
446
- await editMessage(token, chatId, messageId, "\u{1F50C} _Checking Mac..._");
447
- const status = await checkMacStatus();
448
- await editMessage(
449
- token,
450
- chatId,
451
- messageId,
452
- `\u{1F50C} *Mac Status*
453
- ${status.online ? "\u{1F7E2} Online" : "\u{1F534} Offline"}
454
- \`\`\`
455
- ${status.details}
456
- \`\`\``,
457
- [[
458
- { text: "\u{1F4A4} Wake", callback_data: "wake_mac" },
459
- { text: "\u{1F504} Refresh", callback_data: "mac_status" },
460
- { text: "\u25C0\uFE0F Menu", callback_data: "menu" }
461
- ]]
462
- );
463
- break;
464
- }
465
- case "sessions": {
466
- await editMessage(
467
- token,
468
- chatId,
469
- messageId,
470
- `\u{1F4CB} *Claude Sessions*
471
- Mode: *${state.mode}*
472
- Sessions: ${state.sessionsCount}
473
- Last: ${state.lastActivity}`,
474
- claudeMenu()
475
- );
476
- break;
477
- }
478
- case "claude_new":
479
- await editMessage(
480
- token,
481
- chatId,
482
- messageId,
483
- "\u{1F4AC} *New Claude Session*\nEnvoie ta question/tache en texte. Claude va la traiter en mode session.",
484
- backButton()
485
- );
486
- break;
487
- case "claude_continue": {
488
- await editMessage(token, chatId, messageId, "\u{1F4C2} _Continuing last session..._");
489
- const out = await claudeSession("Continue the previous task. What was I working on?", true);
490
- await editMessage(
491
- token,
492
- chatId,
493
- messageId,
494
- `\u{1F4C2} *Session Continued*
495
- ${out}`,
496
- [[
497
- { text: "\u{1F4AC} Reply", callback_data: "claude_new" },
498
- { text: "\u25C0\uFE0F Menu", callback_data: "menu" }
499
- ]]
500
- );
501
- break;
502
- }
503
- case "claude_sessions": {
504
- const sessions = run("ls -lt ~/.claude/projects/ 2>/dev/null | head -10");
505
- await editMessage(
506
- token,
507
- chatId,
508
- messageId,
509
- `\u{1F4CB} *Recent Sessions*
510
- \`\`\`
511
- ${strip(sessions)}
512
- \`\`\``,
513
- claudeMenu()
514
- );
515
- break;
516
- }
517
- case "claude_resume": {
518
- await editMessage(
519
- token,
520
- chatId,
521
- messageId,
522
- "\u{1F504} *Resume Session*\nEnvoie le chemin du projet pour reprendre la session.",
523
- backButton()
524
- );
525
- break;
526
- }
527
- case "logs": {
528
- let logs = "No logs yet";
529
- try {
530
- if (existsSync(LOG_FILE)) {
531
- logs = run(`tail -20 "${LOG_FILE}"`);
532
- }
533
- } catch {
534
- }
535
- await editMessage(
536
- token,
537
- chatId,
538
- messageId,
539
- `\u{1F4DD} *Recent Logs*
540
- \`\`\`
541
- ${truncate(strip(logs), 3e3)}
542
- \`\`\``,
543
- backButton()
544
- );
545
- break;
546
- }
547
- }
548
- }
549
- var BLOCKED_COMMANDS = [
550
- "rm -rf",
551
- "rm -r /",
552
- "mkfs",
553
- "dd if=",
554
- ":(){",
555
- "chmod -R 777",
556
- "git push --force main",
557
- "git push --force master",
558
- "sudo rm",
559
- "sudo chmod",
560
- "eval ",
561
- "curl | sh",
562
- "curl | bash",
563
- "wget | sh",
564
- "> /dev/sd",
565
- "shutdown",
566
- "reboot",
567
- "init 0"
568
- ];
569
- async function handleText(token, chatId, text, from) {
570
- const cmd = text.trim().toLowerCase();
571
- if (cmd === "/start" || cmd === "/menu" || cmd === "/help" || cmd === "/h") {
572
- await send(token, chatId, "\u{1F996} *REX Gateway v3*\nChoisis une action :", mainMenu());
573
- logCommand(from, cmd, "menu");
574
- return;
575
- }
576
- if (cmd === "/status" || cmd === "/s") {
577
- const out = strip(run("rex status"));
578
- await send(token, chatId, `\u{1F4CA} ${out}`, backButton());
579
- logCommand(from, "/status", out);
580
- return;
581
- }
582
- if (cmd === "/wake" || cmd === "/w") {
583
- await send(token, chatId, "\u{1F4A4} _Sending wake signal..._");
584
- const result = await wakeMac();
585
- await send(token, chatId, result, backButton());
586
- logCommand(from, "/wake", result);
587
- return;
588
- }
589
- if (cmd === "/doctor" || cmd === "/d") {
590
- await send(token, chatId, "\u{1FA7A} _Running diagnostics..._");
591
- const out = truncate(strip(run("rex doctor")));
592
- await send(token, chatId, `\u{1FA7A}
593
- \`\`\`
594
- ${out}
595
- \`\`\``, backButton());
596
- logCommand(from, "/doctor", "done");
597
- return;
598
- }
599
- if (cmd === "/ingest" || cmd === "/i") {
600
- await send(token, chatId, "\u{1F4E5} _Ingesting..._");
601
- const out = truncate(strip(run("rex ingest", 12e4)));
602
- await send(token, chatId, `\u{1F4E5}
603
- \`\`\`
604
- ${out}
605
- \`\`\``, backButton());
606
- logCommand(from, "/ingest", "done");
607
- return;
608
- }
609
- if (cmd === "/prune") {
610
- await send(token, chatId, "\u{1F9F9} _Pruning..._");
611
- const out = truncate(strip(run("rex prune", 6e4)));
612
- await send(token, chatId, `\u{1F9F9}
613
- \`\`\`
614
- ${out}
615
- \`\`\``, backButton());
616
- logCommand(from, "/prune", "done");
617
- return;
618
- }
619
- if (cmd.startsWith("/search ") || cmd.startsWith("/q ")) {
620
- const query = text.replace(/^\/(search|q)\s+/i, "");
621
- if (!query) {
622
- await send(token, chatId, "Usage: /search <query>");
623
- return;
624
- }
625
- const out = run(`rex search ${query}`);
626
- await send(
627
- token,
628
- chatId,
629
- out ? `\u{1F50D} *Search:* ${query}
630
- \`\`\`
631
- ${truncate(out, 3e3)}
632
- \`\`\`` : "No results",
633
- backButton()
634
- );
635
- logCommand(from, `/search ${query}`, out ? "found" : "empty");
636
- return;
637
- }
638
- if (cmd.startsWith("/sh ") || cmd.startsWith("/run ")) {
639
- const shellCmd = text.replace(/^\/(sh|run)\s+/i, "");
640
- if (BLOCKED_COMMANDS.some((b) => shellCmd.toLowerCase().includes(b))) {
641
- await send(token, chatId, "\u{1F6AB} Blocked: dangerous command");
642
- logCommand(from, `/sh ${shellCmd}`, "BLOCKED");
643
- return;
644
- }
645
- const out = run(shellCmd);
646
- await send(token, chatId, `\`$ ${shellCmd}\`
647
- \`\`\`
648
- ${truncate(out, 3500)}
649
- \`\`\``, backButton());
650
- logCommand(from, `/sh ${shellCmd}`, out.slice(0, 100));
651
- return;
652
- }
653
- if (cmd.startsWith("/mode")) {
654
- state.mode = state.mode === "qwen" ? "claude" : "qwen";
655
- state.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
656
- saveState(state);
657
- await send(
658
- token,
659
- chatId,
660
- `\u{1F916} Switched to *${state.mode === "qwen" ? "Qwen (local)" : "Claude"}*`,
661
- mainMenu()
662
- );
663
- logCommand(from, "/mode", state.mode);
664
- return;
665
- }
666
- if (cmd === "/claude" || cmd === "/c") {
667
- await send(token, chatId, "\u{1F916} *Claude Remote*\nGere tes sessions Claude a distance :", claudeMenu());
668
- return;
669
- }
670
- if (cmd.startsWith("/claude ") || cmd.startsWith("/c ")) {
671
- const prompt = text.replace(/^\/(claude|c)\s+/i, "");
672
- await send(token, chatId, "\u{1F916} _Claude is thinking..._");
673
- const out = await claudeSession(prompt);
674
- await send(token, chatId, out, [
675
- [
676
- { text: "\u{1F4AC} Continue", callback_data: "claude_continue" },
677
- { text: "\u25C0\uFE0F Menu", callback_data: "menu" }
678
- ]
679
- ]);
680
- logCommand(from, `/claude ${prompt.slice(0, 50)}`, out.slice(0, 100));
681
- return;
682
- }
683
- if (text.length > 2) {
684
- const modeLabel = state.mode === "qwen" ? "\u{1F9E0} Qwen" : "\u{1F916} Claude";
685
- await send(token, chatId, `${modeLabel} _thinking..._`);
686
- state.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
687
- saveState(state);
688
- let response;
689
- if (state.mode === "claude") {
690
- response = await claudeSession(text);
691
- } else {
692
- response = await askLLM(text);
693
- }
694
- await send(token, chatId, response, [
695
- [
696
- { text: `Mode: ${state.mode}`, callback_data: "switch_mode" },
697
- { text: state.mode === "claude" ? "\u{1F4AC} Continue" : "\u25C0\uFE0F Menu", callback_data: state.mode === "claude" ? "claude_continue" : "menu" }
698
- ]
699
- ]);
700
- logCommand(from, text.slice(0, 80), response.slice(0, 100));
701
- return;
702
- }
703
- await send(token, chatId, "\u{1F996} *REX*\nEnvoie un message ou appuie sur Menu :", mainMenu());
704
- }
705
- async function gateway() {
706
- const creds = getCredentials();
707
- if (!creds) {
708
- console.error(`${COLORS.red}No Telegram credentials found.${COLORS.reset}`);
709
- console.error(`Run ${COLORS.cyan}rex setup${COLORS.reset} to configure Telegram gateway.`);
710
- process.exit(1);
711
- }
712
- const { token, chatId } = creds;
713
- config = loadConfig();
714
- state = loadState();
715
- console.log(`${COLORS.bold}REX Gateway v3${COLORS.reset} \u2014 Interactive Telegram bot`);
716
- console.log(`${COLORS.dim}Chat: ${chatId} | Mode: ${state.mode} | Sessions: ${state.sessionsCount}${COLORS.reset}`);
717
- console.log(`${COLORS.dim}Auth: restricted to chat_id ${chatId}${COLORS.reset}`);
718
- console.log(`${COLORS.dim}Ctrl+C to stop${COLORS.reset}
719
- `);
720
- let offset = 0;
721
- try {
722
- const flush = await fetch(`https://api.telegram.org/bot${token}/getUpdates?offset=-1`);
723
- const flushData = await flush.json();
724
- if (flushData.result?.length) {
725
- offset = flushData.result[flushData.result.length - 1].update_id + 1;
726
- }
727
- } catch {
728
- }
729
- await send(token, chatId, `\u{1F7E2} *REX Gateway v3* started
730
- Mode: ${state.mode} | Sessions: ${state.sessionsCount}`, mainMenu());
731
- process.on("SIGINT", async () => {
732
- console.log(`
733
- ${COLORS.dim}Shutting down...${COLORS.reset}`);
734
- state.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
735
- saveState(state);
736
- await send(token, chatId, "\u{1F534} *REX Gateway* stopped");
737
- process.exit(0);
738
- });
739
- process.on("SIGTERM", async () => {
740
- state.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
741
- saveState(state);
742
- await send(token, chatId, "\u{1F534} *REX Gateway* stopped (SIGTERM)");
743
- process.exit(0);
744
- });
745
- while (true) {
746
- try {
747
- const res = await fetch(
748
- `https://api.telegram.org/bot${token}/getUpdates?offset=${offset}&timeout=${config.pollTimeout}&allowed_updates=["message","callback_query"]`
749
- );
750
- const data = await res.json();
751
- if (!data.ok || !data.result?.length) continue;
752
- for (const update of data.result) {
753
- offset = update.update_id + 1;
754
- if (update.callback_query) {
755
- const cb = update.callback_query;
756
- const cbChatId = String(cb.message?.chat?.id);
757
- if (!isAuthorized(cbChatId, chatId)) {
758
- await answerCallback(token, cb.id, "\u{1F6AB} Unauthorized");
759
- continue;
760
- }
761
- const from2 = cb.from?.username ?? "?";
762
- console.log(`${COLORS.cyan}@${from2}${COLORS.reset} [btn] ${cb.data}`);
763
- await handleCallback(token, chatId, cb.message.message_id, cb.id, cb.data, from2);
764
- continue;
765
- }
766
- const msg = update.message;
767
- if (!msg?.text) continue;
768
- if (!isAuthorized(msg.chat.id, chatId)) {
769
- await send(token, String(msg.chat.id), "\u{1F6AB} Unauthorized. This REX instance is private.");
770
- continue;
771
- }
772
- const from = msg.from?.username ?? "?";
773
- console.log(`${COLORS.cyan}@${from}${COLORS.reset}: ${msg.text}`);
774
- await handleText(token, chatId, msg.text, from);
775
- }
776
- } catch (err) {
777
- console.error(`${COLORS.red}Poll error:${COLORS.reset} ${err.message}`);
778
- await new Promise((r) => setTimeout(r, 5e3));
779
- }
780
- }
781
- }
782
- export {
783
- gateway
784
- };