ai-terminal-remote-bot 1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
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,85 @@
1
+ # ai-terminal-remote-bot
2
+
3
+ Secure remote terminal bot with Claude Code integration. Control your server and use Claude Code interactively from Telegram.
4
+
5
+ ## Features
6
+
7
+ - **Remote Terminal**: Execute commands, navigate filesystem, view files
8
+ - **Claude Code Integration**: Interactive AI sessions with real-time tool usage visibility
9
+ - **Media Support**: Send documents, photos, and audio with automatic handling
10
+ - **Audio Transcription**: Voice messages and audio files are transcribed using Whisper (JS/ONNX, offline, no API keys). First transcription downloads ~40MB model.
11
+ - **Security Hardened**: Command validation, path restrictions, rate limiting, no shell injection
12
+ - **Clean Architecture**: Strategy Pattern, DI, per-user sessions, SOLID principles
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install -g ai-terminal-remote-bot
18
+ ```
19
+
20
+ Or clone and link:
21
+
22
+ ```bash
23
+ git clone <repo>
24
+ cd ai-terminal-remote-bot
25
+ npm install
26
+ npm link
27
+ ```
28
+
29
+ ## Setup
30
+
31
+ ```bash
32
+ ai-terminal-remote-bot # First run triggers setup wizard
33
+ ai-terminal-remote-bot --config # Re-run setup anytime
34
+ ```
35
+
36
+ You'll need:
37
+ 1. A Telegram bot token from [@BotFather](https://t.me/BotFather)
38
+ 2. Your Telegram user ID from [@userinfobot](https://t.me/userinfobot)
39
+
40
+ Config is stored in `~/.ai-terminal-remote-bot/config.json` with `0600` permissions.
41
+
42
+ ## Commands
43
+
44
+ | Command | Description |
45
+ |---------|-------------|
46
+ | `/run <cmd>` | Execute a shell command |
47
+ | `/cd <dir>` | Change directory |
48
+ | `/ls [args]` | List files |
49
+ | `/cat <file>` | View file contents |
50
+ | `/status` | System status (RAM, disk, uptime) |
51
+ | `/procs` | Top processes by CPU |
52
+ | `/kill <PID>` | Kill a process |
53
+ | `/claude [prompt]` | Enter Claude mode / send prompt |
54
+ | `/ask <question>` | One-shot Claude query |
55
+ | `/new` | New Claude conversation |
56
+ | `/exit` | Return to terminal mode |
57
+
58
+ In **terminal mode**, plain text is executed as bash commands.
59
+ In **Claude mode**, plain text is sent to Claude Code.
60
+
61
+ ## Security
62
+
63
+ - Dangerous commands (rm -rf, shutdown, etc.) require confirmation
64
+ - Irrecoverable commands (fork bombs, mkfs, dd) are blocked entirely
65
+ - `/ls`, `/cat`, `/kill` use `execFile()` with args array (no shell)
66
+ - Claude prompts use `spawn('claude', ['-p', prompt])` via argv (no shell interpolation)
67
+ - Path validation against configurable allowed paths
68
+ - Rate limiting: 30 requests/minute sliding window
69
+ - Config file permissions enforced at `0600`
70
+
71
+ ## Architecture
72
+
73
+ ```
74
+ cli.js → config.mjs → setup.mjs OR server.mjs
75
+ ├── platforms/telegram.mjs (Strategy Pattern)
76
+ ├── modules/terminal.mjs (execFile/spawn)
77
+ ├── modules/claude.mjs (spawn with argv)
78
+ ├── modules/media.mjs (safe file handling)
79
+ ├── security.mjs (validation layer)
80
+ └── utils.mjs (pure functions)
81
+ ```
82
+
83
+ ## License
84
+
85
+ MIT
package/cli.js ADDED
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env -S node --disable-warning=DEP0040
2
+
3
+ /**
4
+ * CLI entry point - parses flags, delegates to setup or server.
5
+ * Default behavior: launch bot as background daemon.
6
+ */
7
+
8
+ import { readFileSync, writeFileSync, unlinkSync, existsSync, openSync } from 'fs';
9
+ import { dirname, join } from 'path';
10
+ import { fileURLToPath } from 'url';
11
+ import { spawn } from 'child_process';
12
+ import { loadConfig, configExists, CONFIG_DIR } from './src/config.mjs';
13
+ import { runSetup } from './src/setup.mjs';
14
+ import { startServer } from './src/server.mjs';
15
+ import { initI18n, t } from './src/i18n/index.mjs';
16
+
17
+ const __dirname = dirname(fileURLToPath(import.meta.url));
18
+ const pkg = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8'));
19
+
20
+ const PID_FILE = join(CONFIG_DIR, 'bot.pid');
21
+ const LOG_FILE = join(CONFIG_DIR, 'bot.log');
22
+
23
+ const args = process.argv.slice(2);
24
+
25
+ // --- Helpers ---
26
+
27
+ function readPid() {
28
+ try {
29
+ return parseInt(readFileSync(PID_FILE, 'utf8').trim(), 10);
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ function isRunning(pid) {
36
+ try {
37
+ process.kill(pid, 0);
38
+ return true;
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+
44
+ function stopBot() {
45
+ const pid = readPid();
46
+ if (!pid || !isRunning(pid)) {
47
+ console.log(t('cli.botNotRunning'));
48
+ return false;
49
+ }
50
+ try {
51
+ process.kill(pid, 'SIGTERM');
52
+ try { unlinkSync(PID_FILE); } catch {}
53
+ console.log(t('cli.botStopped'));
54
+ return true;
55
+ } catch (err) {
56
+ console.error(t('cli.botStopFailed', { pid, error: err.message }));
57
+ return false;
58
+ }
59
+ }
60
+
61
+ function startDaemon() {
62
+ const pid = readPid();
63
+ if (pid && isRunning(pid)) {
64
+ console.log(t('cli.botAlreadyRunning', { pid }));
65
+ return;
66
+ }
67
+ // Clean stale PID file
68
+ if (pid) {
69
+ try { unlinkSync(PID_FILE); } catch {}
70
+ }
71
+
72
+ const logFd = openSync(LOG_FILE, 'a');
73
+ const child = spawn(process.execPath, ['--disable-warning=DEP0040', fileURLToPath(import.meta.url), '--daemon'], {
74
+ detached: true,
75
+ stdio: ['ignore', logFd, logFd],
76
+ cwd: __dirname,
77
+ });
78
+
79
+ writeFileSync(PID_FILE, String(child.pid));
80
+ child.unref();
81
+
82
+ console.log(t('cli.botStarted', { pid: child.pid, log: LOG_FILE }));
83
+ }
84
+
85
+ // --- Flag handling ---
86
+
87
+ if (args.includes('--version') || args.includes('-v')) {
88
+ console.log(`ai-terminal-remote-bot v${pkg.version}`);
89
+ process.exit(0);
90
+ }
91
+
92
+ if (args.includes('--help') || args.includes('-h')) {
93
+ console.log(`
94
+ ai-terminal-remote-bot v${pkg.version}
95
+
96
+ Usage:
97
+ ai-terminal-remote-bot Start bot as background daemon
98
+ ai-terminal-remote-bot --config Run setup wizard
99
+ ai-terminal-remote-bot --stop Stop the running bot
100
+ ai-terminal-remote-bot --restart Restart the bot
101
+ ai-terminal-remote-bot --status Check if bot is running
102
+ ai-terminal-remote-bot --fg Run in foreground (for debugging)
103
+ ai-terminal-remote-bot --version Show version
104
+ ai-terminal-remote-bot --help Show this help
105
+ `);
106
+ process.exit(0);
107
+ }
108
+
109
+ if (args.includes('--config')) {
110
+ await runSetup();
111
+ process.exit(0);
112
+ }
113
+
114
+ // Everything below needs config
115
+ if (!configExists()) {
116
+ console.log(t('cli.noConfig'));
117
+ await runSetup();
118
+ process.exit(0);
119
+ }
120
+
121
+ const config = loadConfig();
122
+ initI18n(config.language);
123
+
124
+ // --stop
125
+ if (args.includes('--stop')) {
126
+ stopBot();
127
+ process.exit(0);
128
+ }
129
+
130
+ // --restart
131
+ if (args.includes('--restart')) {
132
+ stopBot();
133
+ startDaemon();
134
+ process.exit(0);
135
+ }
136
+
137
+ // --status
138
+ if (args.includes('--status')) {
139
+ const pid = readPid();
140
+ if (pid && isRunning(pid)) {
141
+ console.log(t('cli.botRunning', { pid }));
142
+ } else {
143
+ if (pid) { try { unlinkSync(PID_FILE); } catch {} }
144
+ console.log(t('cli.botNotRunning'));
145
+ }
146
+ process.exit(0);
147
+ }
148
+
149
+ // --daemon (hidden internal flag — the actual server process)
150
+ if (args.includes('--daemon')) {
151
+ console.log(`ai-terminal-remote-bot v${pkg.version}`);
152
+ console.log(t('cli.startInfo', { platform: config.platform, userId: config.allowedUserId }));
153
+ await startServer(config);
154
+ // should not reach here unless server exits
155
+ process.exit(0);
156
+ }
157
+
158
+ // --fg (foreground mode for debugging)
159
+ if (args.includes('--fg')) {
160
+ console.log(`ai-terminal-remote-bot v${pkg.version}`);
161
+ console.log(t('cli.startInfo', { platform: config.platform, userId: config.allowedUserId }));
162
+ await startServer(config);
163
+ process.exit(0);
164
+ }
165
+
166
+ // Default: start as background daemon
167
+ console.log(`ai-terminal-remote-bot v${pkg.version}`);
168
+ startDaemon();
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "ai-terminal-remote-bot",
3
+ "version": "1.0.0",
4
+ "description": "Secure remote terminal bot with Claude Code integration",
5
+ "type": "module",
6
+ "bin": {
7
+ "ai-terminal-remote-bot": "./cli.js"
8
+ },
9
+ "main": "cli.js",
10
+ "scripts": {
11
+ "start": "node --disable-warning=DEP0040 cli.js",
12
+ "dev": "node --disable-warning=DEP0040 --watch cli.js"
13
+ },
14
+ "keywords": [
15
+ "telegram",
16
+ "bot",
17
+ "terminal",
18
+ "claude",
19
+ "remote"
20
+ ],
21
+ "author": "",
22
+ "license": "MIT",
23
+ "engines": {
24
+ "node": ">=18.0.0"
25
+ },
26
+ "dependencies": {
27
+ "@huggingface/transformers": "^3.4.1",
28
+ "telegraf": "^4.16.3",
29
+ "wavefile": "^11.0.0"
30
+ }
31
+ }
package/src/config.mjs ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Config management - reads/writes ~/.ai-terminal-remote-bot/config.json with 0600 perms.
3
+ */
4
+
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import os from 'os';
8
+
9
+ const CONFIG_DIR = path.join(os.homedir(), '.ai-terminal-remote-bot');
10
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
11
+ const CONFIG_PERMS = 0o600;
12
+ const DIR_PERMS = 0o700;
13
+
14
+ const DEFAULTS = {
15
+ language: 'en',
16
+ platform: 'telegram',
17
+ botToken: '',
18
+ allowedUserId: 0,
19
+ allowedPaths: [],
20
+ mediaDir: '',
21
+ maxOutputLength: 4000,
22
+ commandTimeout: 30000,
23
+ claudeTimeout: 300000,
24
+ };
25
+
26
+ /**
27
+ * Load config from disk. Returns null if not found.
28
+ */
29
+ export function loadConfig() {
30
+ if (!fs.existsSync(CONFIG_FILE)) return null;
31
+
32
+ // Verify permissions
33
+ const stat = fs.statSync(CONFIG_FILE);
34
+ const mode = stat.mode & 0o777;
35
+ if (mode !== CONFIG_PERMS) {
36
+ fs.chmodSync(CONFIG_FILE, CONFIG_PERMS);
37
+ }
38
+
39
+ const raw = fs.readFileSync(CONFIG_FILE, 'utf8');
40
+ const config = JSON.parse(raw);
41
+ return { ...DEFAULTS, ...config };
42
+ }
43
+
44
+ /**
45
+ * Save config to disk with secure permissions.
46
+ */
47
+ export function saveConfig(config) {
48
+ if (!fs.existsSync(CONFIG_DIR)) {
49
+ fs.mkdirSync(CONFIG_DIR, { mode: DIR_PERMS, recursive: true });
50
+ }
51
+
52
+ const merged = { ...DEFAULTS, ...config };
53
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2), { mode: CONFIG_PERMS });
54
+ return merged;
55
+ }
56
+
57
+ /**
58
+ * Check if a valid config exists.
59
+ */
60
+ export function configExists() {
61
+ const config = loadConfig();
62
+ return config && config.botToken && config.allowedUserId;
63
+ }
64
+
65
+ export { CONFIG_DIR, CONFIG_FILE };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * i18n singleton — loads locale JSON, provides t(key, vars) for template strings.
3
+ */
4
+
5
+ import { readFileSync } from 'fs';
6
+ import { dirname, join } from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+
11
+ let strings = {};
12
+ let currentLang = 'en';
13
+
14
+ /**
15
+ * Initialize i18n with the given language. Falls back to 'en' if locale not found.
16
+ * @param {string} lang - 'en' or 'es'
17
+ */
18
+ export function initI18n(lang) {
19
+ currentLang = (lang === 'es') ? 'es' : 'en';
20
+ const localePath = join(__dirname, 'locales', `${currentLang}.json`);
21
+ try {
22
+ strings = JSON.parse(readFileSync(localePath, 'utf8'));
23
+ } catch {
24
+ // Fallback to English
25
+ currentLang = 'en';
26
+ strings = JSON.parse(readFileSync(join(__dirname, 'locales', 'en.json'), 'utf8'));
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Translate a dot-notation key, replacing {{var}} placeholders.
32
+ * @param {string} key - e.g. 'server.rateLimit' or 'setup.botValidated'
33
+ * @param {object} [vars] - e.g. { username: 'bot', firstName: 'Bot' }
34
+ * @returns {string}
35
+ */
36
+ export function t(key, vars) {
37
+ const parts = key.split('.');
38
+ let val = strings;
39
+ for (const p of parts) {
40
+ val = val?.[p];
41
+ }
42
+ if (typeof val !== 'string') return key;
43
+ if (!vars) return val;
44
+ return val.replace(/\{\{(\w+)\}\}/g, (_, name) => vars[name] ?? '');
45
+ }
46
+
47
+ /**
48
+ * Get the current language code.
49
+ * @returns {'en'|'es'}
50
+ */
51
+ export function getLang() {
52
+ return currentLang;
53
+ }
@@ -0,0 +1,114 @@
1
+ {
2
+ "cli": {
3
+ "noConfig": "No configuration found. Starting setup...\n",
4
+ "startInfo": "Platform: {{platform}} | User: {{userId}}\n",
5
+ "botStarted": "🤖 Bot started (PID: {{pid}}). Log: {{log}}",
6
+ "botStopped": "Bot stopped.",
7
+ "botAlreadyRunning": "Bot is already running (PID: {{pid}}).",
8
+ "botNotRunning": "Bot is not running.",
9
+ "botRunning": "🤖 Bot is running (PID: {{pid}}).",
10
+ "botStopFailed": "Could not stop bot (PID: {{pid}}): {{error}}"
11
+ },
12
+ "setup": {
13
+ "existingConfig": "Existing config detected.\n",
14
+ "tokenHint": "📌 Create a bot with @BotFather using /newbot and paste the token here.",
15
+ "tokenPrompt": "Bot Token{{hint}}: ",
16
+ "tokenRequired": "\n❌ Token is required. Aborting.",
17
+ "validatingToken": "\nValidating token...",
18
+ "invalidToken": "❌ Invalid token. Check with @BotFather.",
19
+ "botValidated": "✅ Bot: @{{username}} ({{firstName}})\n",
20
+ "userIdHint": "📌 Send /start to @userinfobot on Telegram to get your numeric ID.",
21
+ "userIdPrompt": "Your Telegram User ID{{hint}}: ",
22
+ "userIdRequired": "\n❌ User ID is required. Send /start to @userinfobot to get it.",
23
+ "pathsHint": "📌 Directories where the bot can operate (cd, read files, run commands).",
24
+ "pathsExamples": " Examples: /home/user,/var/www | Empty = full access",
25
+ "pathsPrompt": "Allowed paths{{hint}}: ",
26
+ "mediaDirHint": "\n📌 Directory where media files from Telegram are saved (images, audio, PDFs).",
27
+ "mediaDirAutoCreate": " This folder is created automatically if it doesn't exist.",
28
+ "mediaDirExample": " Example: {{example}}",
29
+ "mediaDirPrompt": "Media download path [{{default}}]: ",
30
+ "advancedOptions": "\n⚙️ Advanced options (press Enter to keep defaults):",
31
+ "cmdTimeoutPrompt": "Command timeout in minutes [{{default}}]: ",
32
+ "claudeTimeoutPrompt": "Claude timeout in minutes [{{default}}]: ",
33
+ "maxOutputPrompt": "Max output chars [{{default}}]: ",
34
+ "configSaved": "\n✅ Configuration saved.",
35
+ "runAgain": " Run again to start the bot.\n"
36
+ },
37
+ "server": {
38
+ "rateLimit": "⏳ Rate limit. Please wait.",
39
+ "claudeBusy": "⏳ Claude is processing...",
40
+ "claudeProcessing": "🤖 Processing...",
41
+ "startMessage": "🖥️ *Remote Terminal*\n\n*Terminal:* text = bash, /run, /cd, /ls, /cat\n*System:* /status /procs /kill\n*Claude:* /claude — interactive session\n/exit — close | /claude_new — new session\n📂 `{{dir}}` 🔧 {{mode}}",
42
+ "runUsage": "Usage: /run <command>",
43
+ "catUsage": "Usage: /cat <file>",
44
+ "killUsage": "Usage: /kill <PID>",
45
+ "invalidPid": "❌ PID must be a number.",
46
+ "claudeMode": "🤖 *Claude Code Mode*\n📂 `{{dir}}`\n\nType and it's sent to Claude.\nSee what it does in real time.\nKeeps conversation context.\n\n/exit — Terminal | /claude_new — New conversation",
47
+ "askUsage": "Usage: /claude_ask <question>",
48
+ "newConversation": "🔄 New conversation.",
49
+ "terminalMode": "🖥️ Terminal mode.",
50
+ "fileSaved": "✅ `{{dest}}` ({{size}} KB)",
51
+ "claudeFileUploaded": "The user uploaded the file {{dest}} and says: {{caption}}",
52
+ "imageSaved": "🖼️ Image saved: `{{dest}}`",
53
+ "claudeImageWithCaption": "Look at this image I saved at {{dest}} and: {{caption}}",
54
+ "claudeImageAnalyze": "Look at the image I saved at {{dest}} and describe it. If it's a code or error screenshot, analyze it.",
55
+ "audioSaved": "🎤 Audio saved: `{{dest}}`\n\nTranscribing...",
56
+ "transcription": "🎤 *Transcription:*\n\n{{text}}",
57
+ "transcriptionFailed": "⚠️ Could not transcribe: {{error}}\nAudio saved as file.",
58
+ "sessionsTitle": "📋 Recent Claude sessions:",
59
+ "noSessions": "No Claude sessions yet.",
60
+ "sessionSelected": "✅ Session resumed. Send a message to continue.",
61
+ "pathNotAllowed": "⛔ Path not allowed: `{{path}}`",
62
+ "helpMessage": "📋 *Available Commands*\n\n*Terminal:*\nText → runs as bash command\n/run `<cmd>` — run command\n/cd `<dir>` — change directory\n/pwd — current directory\n/ls `[dir]` — list files\n/cat `<file>` — show file\n\n*System:*\n/status — system info\n/procs — running processes\n/kill `<PID>` — kill process\n\n*Claude:*\n/claude — enter Claude mode\n/claude\\_ask `<question>` — quick question\n/claude\\_new — new conversation\n/claude\\_sessions — list past sessions\n/exit — back to terminal\n\n*Other:*\n/mode — current mode\n/start — welcome message\n/help — this help"
63
+ },
64
+ "claude": {
65
+ "timeout": "Timeout (5min)",
66
+ "noResponse": "(no response)",
67
+ "toolRead": "📖 Reading: `{{path}}`",
68
+ "toolEdit": "✏️ Editing: `{{path}}`",
69
+ "toolWrite": "📝 Creating: `{{path}}`",
70
+ "toolGlob": "🔍 Searching: `{{pattern}}`",
71
+ "toolGrep": "🔎 Grep: `{{pattern}}`",
72
+ "toolBash": "⚡ `{{command}}`",
73
+ "toolWebSearch": "🌐 {{query}}",
74
+ "toolWebFetch": "🌐 Fetch: {{url}}",
75
+ "toolDefault": "🔧 {{tool}}",
76
+ "notInstalled": "Claude CLI is not installed. Install it with: npm install -g @anthropic-ai/claude-code"
77
+ },
78
+ "security": {
79
+ "emptyCommand": "Empty command.",
80
+ "blockedCommand": "Blocked command: dangerous pattern detected.",
81
+ "dangerousCommand": "Dangerous command — requires confirmation."
82
+ },
83
+ "telegram": {
84
+ "confirmYes": "Yes",
85
+ "confirmNo": "No",
86
+ "cmdHelp": "Command list",
87
+ "cmdStart": "Welcome message",
88
+ "cmdRun": "Run a command",
89
+ "cmdCd": "Change directory",
90
+ "cmdLs": "List files",
91
+ "cmdCat": "Show file contents",
92
+ "cmdStatus": "System info",
93
+ "cmdProcs": "Running processes",
94
+ "cmdKill": "Kill a process",
95
+ "cmdClaude": "Claude mode",
96
+ "cmdAsk": "Quick question to Claude",
97
+ "cmdNew": "New conversation",
98
+ "cmdSessions": "Claude sessions",
99
+ "cmdExit": "Back to terminal",
100
+ "cmdMode": "Current mode",
101
+ "cancelled": "❌ Cancelled.",
102
+ "selected": "✅ Selected",
103
+ "allowed": "✅ Allowed",
104
+ "denied": "❌ Denied",
105
+ "unauthorized": "⛔ Unauthorized.",
106
+ "botActive": "🤖 Bot active."
107
+ },
108
+ "utils": {
109
+ "truncated": "... (truncated)"
110
+ },
111
+ "transcriber": {
112
+ "ffmpegMissing": "ffmpeg is not installed. Install it with: sudo apt install ffmpeg"
113
+ }
114
+ }
@@ -0,0 +1,114 @@
1
+ {
2
+ "cli": {
3
+ "noConfig": "No se encontró configuración. Iniciando setup...\n",
4
+ "startInfo": "Plataforma: {{platform}} | Usuario: {{userId}}\n",
5
+ "botStarted": "🤖 Bot iniciado (PID: {{pid}}). Log: {{log}}",
6
+ "botStopped": "Bot detenido.",
7
+ "botAlreadyRunning": "El bot ya está corriendo (PID: {{pid}}).",
8
+ "botNotRunning": "El bot no está corriendo.",
9
+ "botRunning": "🤖 El bot está corriendo (PID: {{pid}}).",
10
+ "botStopFailed": "No se pudo detener el bot (PID: {{pid}}): {{error}}"
11
+ },
12
+ "setup": {
13
+ "existingConfig": "Configuración existente detectada.\n",
14
+ "tokenHint": "📌 Crea un bot con @BotFather usando /newbot y pega el token aquí.",
15
+ "tokenPrompt": "Token del Bot{{hint}}: ",
16
+ "tokenRequired": "\n❌ El token es obligatorio. Abortando.",
17
+ "validatingToken": "\nValidando token...",
18
+ "invalidToken": "❌ Token inválido. Verificá con @BotFather.",
19
+ "botValidated": "✅ Bot: @{{username}} ({{firstName}})\n",
20
+ "userIdHint": "📌 Enviá /start a @userinfobot en Telegram para obtener tu ID numérico.",
21
+ "userIdPrompt": "Tu ID de usuario de Telegram{{hint}}: ",
22
+ "userIdRequired": "\n❌ El ID de usuario es obligatorio. Enviá /start a @userinfobot para obtenerlo.",
23
+ "pathsHint": "📌 Directorios donde el bot puede operar (cd, leer archivos, ejecutar comandos).",
24
+ "pathsExamples": " Ejemplos: /home/user,/var/www | Vacío = acceso total",
25
+ "pathsPrompt": "Rutas permitidas{{hint}}: ",
26
+ "mediaDirHint": "\n📌 Directorio donde se guardan los archivos multimedia de Telegram (imágenes, audio, PDFs).",
27
+ "mediaDirAutoCreate": " Esta carpeta se crea automáticamente si no existe.",
28
+ "mediaDirExample": " Ejemplo: {{example}}",
29
+ "mediaDirPrompt": "Ruta de descarga de medios [{{default}}]: ",
30
+ "advancedOptions": "\n⚙️ Opciones avanzadas (presioná Enter para mantener los valores por defecto):",
31
+ "cmdTimeoutPrompt": "Timeout de comandos en minutos [{{default}}]: ",
32
+ "claudeTimeoutPrompt": "Timeout de Claude en minutos [{{default}}]: ",
33
+ "maxOutputPrompt": "Máximo de caracteres de salida [{{default}}]: ",
34
+ "configSaved": "\n✅ Configuración guardada.",
35
+ "runAgain": " Ejecutá de nuevo para iniciar el bot.\n"
36
+ },
37
+ "server": {
38
+ "rateLimit": "⏳ Límite de velocidad. Esperá un momento.",
39
+ "claudeBusy": "⏳ Claude está procesando...",
40
+ "claudeProcessing": "🤖 Procesando...",
41
+ "startMessage": "🖥️ *Terminal Remota*\n\n*Terminal:* texto = bash, /run, /cd, /ls, /cat\n*Sistema:* /status /procs /kill\n*Claude:* /claude — sesión interactiva\n/exit — cerrar | /claude_new — nueva sesión\n📂 `{{dir}}` 🔧 {{mode}}",
42
+ "runUsage": "Uso: /run <comando>",
43
+ "catUsage": "Uso: /cat <archivo>",
44
+ "killUsage": "Uso: /kill <PID>",
45
+ "invalidPid": "❌ El PID debe ser un número.",
46
+ "claudeMode": "🤖 *Modo Claude Code*\n📂 `{{dir}}`\n\nEscribí y se envía a Claude.\nVé lo que hace en tiempo real.\nMantiene el contexto de la conversación.\n\n/exit — Terminal | /claude_new — Nueva conversación",
47
+ "askUsage": "Uso: /claude_ask <pregunta>",
48
+ "newConversation": "🔄 Nueva conversación.",
49
+ "terminalMode": "🖥️ Modo terminal.",
50
+ "fileSaved": "✅ `{{dest}}` ({{size}} KB)",
51
+ "claudeFileUploaded": "El usuario subió el archivo {{dest}} y dice: {{caption}}",
52
+ "imageSaved": "🖼️ Imagen guardada: `{{dest}}`",
53
+ "claudeImageWithCaption": "Mirá esta imagen que guardé en {{dest}} y: {{caption}}",
54
+ "claudeImageAnalyze": "Mirá la imagen que guardé en {{dest}} y describila. Si es un screenshot de código o error, analizalo.",
55
+ "audioSaved": "🎤 Audio guardado: `{{dest}}`\n\nTranscribiendo...",
56
+ "transcription": "🎤 *Transcripción:*\n\n{{text}}",
57
+ "transcriptionFailed": "⚠️ No se pudo transcribir: {{error}}\nAudio guardado como archivo.",
58
+ "sessionsTitle": "📋 Sesiones recientes de Claude:",
59
+ "noSessions": "No hay sesiones de Claude aún.",
60
+ "sessionSelected": "✅ Sesión reanudada. Enviá un mensaje para continuar.",
61
+ "pathNotAllowed": "⛔ Ruta no permitida: `{{path}}`",
62
+ "helpMessage": "📋 *Comandos Disponibles*\n\n*Terminal:*\nTexto → se ejecuta como comando bash\n/run `<cmd>` — ejecutar comando\n/cd `<dir>` — cambiar directorio\n/pwd — directorio actual\n/ls `[dir]` — listar archivos\n/cat `<archivo>` — mostrar archivo\n\n*Sistema:*\n/status — info del sistema\n/procs — procesos activos\n/kill `<PID>` — matar proceso\n\n*Claude:*\n/claude — entrar a modo Claude\n/claude\\_ask `<pregunta>` — pregunta rápida\n/claude\\_new — nueva conversación\n/claude\\_sessions — listar sesiones anteriores\n/exit — volver a terminal\n\n*Otros:*\n/mode — modo actual\n/start — mensaje de bienvenida\n/help — esta ayuda"
63
+ },
64
+ "claude": {
65
+ "timeout": "Timeout (5min)",
66
+ "noResponse": "(sin respuesta)",
67
+ "toolRead": "📖 Leyendo: `{{path}}`",
68
+ "toolEdit": "✏️ Editando: `{{path}}`",
69
+ "toolWrite": "📝 Creando: `{{path}}`",
70
+ "toolGlob": "🔍 Buscando: `{{pattern}}`",
71
+ "toolGrep": "🔎 Grep: `{{pattern}}`",
72
+ "toolBash": "⚡ `{{command}}`",
73
+ "toolWebSearch": "🌐 {{query}}",
74
+ "toolWebFetch": "🌐 Fetch: {{url}}",
75
+ "toolDefault": "🔧 {{tool}}",
76
+ "notInstalled": "Claude CLI no está instalado. Instalalo con: npm install -g @anthropic-ai/claude-code"
77
+ },
78
+ "security": {
79
+ "emptyCommand": "Comando vacío.",
80
+ "blockedCommand": "Comando bloqueado: patrón peligroso detectado.",
81
+ "dangerousCommand": "Comando peligroso — requiere confirmación."
82
+ },
83
+ "telegram": {
84
+ "confirmYes": "Sí",
85
+ "confirmNo": "No",
86
+ "cmdHelp": "Lista de comandos",
87
+ "cmdStart": "Mensaje de bienvenida",
88
+ "cmdRun": "Ejecutar comando",
89
+ "cmdCd": "Cambiar directorio",
90
+ "cmdLs": "Listar archivos",
91
+ "cmdCat": "Mostrar archivo",
92
+ "cmdStatus": "Info del sistema",
93
+ "cmdProcs": "Procesos activos",
94
+ "cmdKill": "Matar proceso",
95
+ "cmdClaude": "Modo Claude",
96
+ "cmdAsk": "Pregunta rápida a Claude",
97
+ "cmdNew": "Nueva conversación",
98
+ "cmdSessions": "Sesiones de Claude",
99
+ "cmdExit": "Volver a terminal",
100
+ "cmdMode": "Modo actual",
101
+ "cancelled": "❌ Cancelado.",
102
+ "selected": "✅ Seleccionada",
103
+ "allowed": "✅ Permitido",
104
+ "denied": "❌ Denegado",
105
+ "unauthorized": "⛔ No autorizado.",
106
+ "botActive": "🤖 Bot activo."
107
+ },
108
+ "utils": {
109
+ "truncated": "... (truncado)"
110
+ },
111
+ "transcriber": {
112
+ "ffmpegMissing": "ffmpeg no está instalado. Instalalo con: sudo apt install ffmpeg"
113
+ }
114
+ }