mobilecoder-mcp 2.1.2 → 2.1.3

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/dist/agent.js CHANGED
@@ -1,323 +1,351 @@
1
- import { io } from 'socket.io-client';
2
- import os from 'os';
3
- import { createRequire } from 'module';
4
- import crypto from 'crypto';
5
- import chalk from 'chalk';
6
- import ora from 'ora';
7
- import fs from 'fs';
8
- import path from 'path';
9
- const require = createRequire(import.meta.url);
10
- const pty = require('node-pty');
11
- // 🚨 BURAYI KENDİ DOMAIN ADRESİNLE DEĞİŞTİR!
12
- const RELAY_SERVER_URL = 'https://api.mobilecoder.xyz';
13
- // Security Constants
14
- const PBKDF2_ITERATIONS = 100000;
15
- const KEY_LENGTH = 32; // 256 bits
16
- const SALT = 'mobilecoder-salt-v2';
17
- const KEY_ROTATION_INTERVAL = 600000; // 10 minutes
18
- const MESSAGE_MAX_AGE = 300000; // 5 minutes
19
- export class ZKRelayAgent {
20
- socket;
21
- ptyProcess;
22
- secretCode;
23
- roomId;
24
- // 🔐 Güçlendirilmiş Güvenlik Değişkenleri
25
- masterKey;
26
- sessionKey;
27
- sessionCounter = 0;
28
- seenNonces = new Set();
29
- nonceCleanupInterval = null;
30
- keyRotationInterval = null;
31
- spinner = ora('Initializing Secure Tunnel...');
32
- constructor() {
33
- this.setupSecurity();
34
- this.initPTY();
35
- this.connectToRelay();
36
- this.startSecurityMaintenance();
37
- }
38
- // 1. Güvenlik Katmanı: PBKDF2 ile Güçlendirilmiş Anahtar Türetme
39
- setupSecurity() {
40
- // 6 Haneli rastgele kod (Kullanıcının göreceği)
41
- const randomNum = Math.floor(100000 + Math.random() * 900000);
42
- this.secretCode = randomNum.toString();
43
- // Oda ID'si: Kodun Hash'i (Sunucu sadece bunu görür, kodu göremez)
44
- this.roomId = crypto.createHash('sha256').update(this.secretCode).digest('hex');
45
- // 🔐 PBKDF2 ile Master Key Türetme (Brute-force'a karşı savunma)
46
- this.masterKey = crypto.pbkdf2Sync(this.secretCode, SALT, PBKDF2_ITERATIONS, KEY_LENGTH, 'sha256');
47
- // Session Key (PFS için rotasyonlu)
48
- this.sessionKey = this.deriveSessionKey(0);
49
- }
50
- // 🔐 Session Key Türetme (Perfect Forward Secrecy)
51
- deriveSessionKey(counter) {
52
- return crypto.pbkdf2Sync(this.masterKey, `session-${counter}`, 10000, KEY_LENGTH, 'sha256');
53
- }
54
- // 🔄 Key Rotation (Her 10 dakikada bir)
55
- rotateKey() {
56
- this.sessionCounter++;
57
- this.sessionKey = this.deriveSessionKey(this.sessionCounter);
58
- console.log(chalk.cyan(`🔄 Session key rotated to #${this.sessionCounter}`));
59
- // Notify peer about key rotation
60
- this.sendSecurePayload('key_rotation', { counter: this.sessionCounter });
61
- }
62
- // 🧹 Güvenlik Bakımı
63
- startSecurityMaintenance() {
64
- // Nonce temizliği (5 dakikadan eski nonce'ları sil)
65
- this.nonceCleanupInterval = setInterval(() => {
66
- this.seenNonces.clear();
67
- }, MESSAGE_MAX_AGE);
68
- // Key rotation
69
- this.keyRotationInterval = setInterval(() => {
70
- this.rotateKey();
71
- }, KEY_ROTATION_INTERVAL);
72
- }
73
- // 2. Terminal (PTY) Başlatma
74
- initPTY() {
75
- const shell = os.platform() === 'win32' ? 'powershell.exe' : (process.env.SHELL || 'bash');
76
- this.ptyProcess = pty.spawn(shell, [], {
77
- name: 'xterm-256color',
78
- cols: 80,
79
- rows: 24,
80
- cwd: process.cwd(),
81
- env: process.env
82
- });
83
- // Terminalden çıkan veriyi şifrele ve yolla
84
- this.ptyProcess.onData((data) => {
85
- this.sendSecurePayload('term_data', data);
86
- });
87
- }
88
- // 3. Relay Sunucusuna Bağlanma
89
- connectToRelay() {
90
- this.spinner.start('Connecting to MobileCoder Relay Network...');
91
- this.socket = io(RELAY_SERVER_URL, {
92
- transports: ['websocket'], // Hız için sadece websocket
93
- reconnection: true,
94
- reconnectionAttempts: Infinity
95
- });
96
- this.socket.on('connect', () => {
97
- this.spinner.succeed(chalk.green('Connected to Relay Node!'));
98
- // Odaya Katıl (Sadece Hash ID gönderilir)
99
- this.socket.emit('join_room', { code: this.roomId, type: 'agent' });
100
- this.printBanner();
101
- });
102
- this.socket.on('disconnect', () => {
103
- this.spinner.warn(chalk.yellow('Connection lost. Reconnecting...'));
104
- });
105
- // Mobilden biri bağlandığında
106
- this.socket.on('peer_joined', () => {
107
- console.log(chalk.green('\n📱 Mobile Client Connected via Encrypted Tunnel!'));
108
- this.sendEnvInfo();
109
- });
110
- // Mobilden şifreli veri geldiğinde
111
- this.socket.on('relay_data', (message) => {
112
- this.handleIncomingMessage(message);
113
- });
114
- // Also listen for connect_error
115
- this.socket.on('connect_error', (err) => {
116
- // console.error(err);
117
- });
118
- }
119
- // 🔐 AES-256-CBC ile Şifreleme (Nonce tabanlı)
120
- encrypt(data) {
121
- const nonce = crypto.randomBytes(16);
122
- const cipher = crypto.createCipheriv('aes-256-cbc', this.sessionKey, nonce);
123
- const encrypted = Buffer.concat([
124
- cipher.update(data, 'utf8'),
125
- cipher.final()
126
- ]);
127
- return {
128
- nonce: nonce.toString('base64'),
129
- data: encrypted.toString('base64'),
130
- timestamp: Date.now()
131
- };
132
- }
133
- // 🔐 AES-256-CBC ile Şifre Çözme (Replay Attack Korumalı)
134
- decrypt(packet) {
135
- // Timestamp kontrolü (5 dakikadan eski mesajları reddet)
136
- if (Date.now() - packet.timestamp > MESSAGE_MAX_AGE) {
137
- throw new Error('Message too old - potential replay attack');
138
- }
139
- // Nonce uniqueness kontrolü (Replay attack önleme)
140
- if (this.seenNonces.has(packet.nonce)) {
141
- throw new Error('Replay attack detected - duplicate nonce');
142
- }
143
- this.seenNonces.add(packet.nonce);
144
- const decipher = crypto.createDecipheriv('aes-256-cbc', this.sessionKey, Buffer.from(packet.nonce, 'base64'));
145
- return Buffer.concat([
146
- decipher.update(Buffer.from(packet.data, 'base64')),
147
- decipher.final()
148
- ]).toString('utf8');
149
- }
150
- // Gelen veriyi çöz ve işle
151
- handleIncomingMessage(encryptedPayload) {
152
- try {
153
- let decryptedText;
154
- // Yeni format: { nonce, data, timestamp }
155
- if (typeof encryptedPayload === 'object' && encryptedPayload.nonce) {
156
- decryptedText = this.decrypt(encryptedPayload);
157
- }
158
- // Legacy format: { e: ciphertext } (CryptoJS uyumluluğu)
159
- else if (typeof encryptedPayload === 'object' && encryptedPayload.e) {
160
- // Legacy CryptoJS decrypt fallback
161
- const CryptoJS = require('crypto-js');
162
- const bytes = CryptoJS.AES.decrypt(encryptedPayload.e, this.secretCode);
163
- decryptedText = bytes.toString(CryptoJS.enc.Utf8);
164
- }
165
- else {
166
- return;
167
- }
168
- if (!decryptedText)
169
- return;
170
- // Veri JSON mu? (Resize komutu vs olabilir)
171
- try {
172
- const cmd = JSON.parse(decryptedText);
173
- // Handle key rotation from peer
174
- if (cmd.type === 'key_rotation') {
175
- this.sessionCounter = cmd.counter;
176
- this.sessionKey = this.deriveSessionKey(this.sessionCounter);
177
- console.log(chalk.cyan(`🔄 Key synced to session #${this.sessionCounter}`));
178
- return;
179
- }
180
- // 1. Terminal Input (Keystrokes)
181
- if (cmd.type === 'input') {
182
- this.ptyProcess.write(cmd.data);
183
- return;
184
- }
185
- // 2. Terminal Resize
186
- if (cmd.type === 'resize') {
187
- this.ptyProcess.resize(cmd.cols, cmd.rows);
188
- return;
189
- }
190
- // 3. Chat Commands (e.g. "ls -la")
191
- if (cmd.type === 'command') {
192
- this.ptyProcess.write(cmd.text + '\r');
193
- return;
194
- }
195
- // 4. CLI Tool/Button Commands (e.g. "git status")
196
- if (cmd.type === 'cli_command') {
197
- this.ptyProcess.write(cmd.command + '\r');
198
- return;
199
- }
200
- // 5. Tool Calls (File System Access)
201
- if (cmd.type === 'tool_call') {
202
- this.handleToolCall(cmd.tool, cmd.data, cmd.id);
203
- return;
204
- }
205
- }
206
- catch (e) {
207
- // JSON değilse saf terminal girdisidir
208
- this.ptyProcess.write(decryptedText);
209
- }
210
- }
211
- catch (error) {
212
- // Şifre çözülemezse (Yanlış anahtar vb.) sessizce yut
213
- console.error(chalk.red('Decryption failed:'), error.message);
214
- }
215
- }
216
- async handleToolCall(tool, args, id) {
217
- try {
218
- let result = null;
219
- if (tool === 'list_directory') {
220
- const dirPath = args.path ? path.resolve(args.path) : process.cwd();
221
- const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
222
- const files = entries.map(entry => ({
223
- name: entry.name,
224
- type: entry.isDirectory() ? 'directory' : 'file',
225
- size: 0 // Simplification for speed
226
- }));
227
- result = { files };
228
- }
229
- else if (tool === 'read_file') {
230
- const filePath = path.resolve(args.path);
231
- const content = await fs.promises.readFile(filePath, 'utf-8');
232
- result = { content };
233
- }
234
- else {
235
- throw new Error(`Unknown tool: ${tool}`);
236
- }
237
- this.sendSecurePayload('tool_result', {
238
- id,
239
- result
240
- });
241
- }
242
- catch (error) {
243
- this.sendSecurePayload('tool_result', {
244
- id,
245
- error: error.message,
246
- type: 'error'
247
- });
248
- }
249
- }
250
- // Veriyi şifrele ve gönder
251
- sendSecurePayload(type, data) {
252
- let payloadStr = '';
253
- if (type === 'meta' || type === 'tool_result' || type === 'key_rotation') {
254
- // Standardize format
255
- if (type === 'tool_result') {
256
- if (data.type === 'error') {
257
- payloadStr = JSON.stringify({ ...data });
258
- }
259
- else {
260
- payloadStr = JSON.stringify({ type: 'result', ...data });
261
- }
262
- }
263
- else if (type === 'key_rotation') {
264
- payloadStr = JSON.stringify({ type: 'key_rotation', ...data });
265
- }
266
- else {
267
- payloadStr = JSON.stringify(data);
268
- }
269
- }
270
- else if (type === 'term_data') {
271
- // Wrap output in { type: 'output', data: ... }
272
- if (typeof data === 'string') {
273
- payloadStr = JSON.stringify({ type: 'output', data: data });
274
- }
275
- else {
276
- payloadStr = JSON.stringify(data);
277
- }
278
- }
279
- // Fallback if empty (shouldn't happen with above logic)
280
- if (!payloadStr)
281
- payloadStr = JSON.stringify(data);
282
- // 🔐 Yeni şifreleme formatı ile gönder
283
- const encrypted = this.encrypt(payloadStr);
284
- this.socket.emit('relay_data', {
285
- room: this.roomId,
286
- payload: encrypted
287
- });
288
- }
289
- sendEnvInfo() {
290
- const info = {
291
- type: 'host_info',
292
- username: os.userInfo().username,
293
- hostname: os.hostname(),
294
- platform: os.platform(),
295
- cwd: process.cwd()
296
- };
297
- this.sendSecurePayload('meta', info);
298
- }
299
- printBanner() {
300
- console.log('\n' + chalk.bgBlue.bold(' MOBILECODER ZK-RELAY v2.1.0 '));
301
- console.log(chalk.gray('🔐 PBKDF2-SHA256 + AES-256-CBC + Nonce-Based Replay Protection'));
302
- console.log(chalk.gray('🔄 Perfect Forward Secrecy with 10-min Key Rotation\n'));
303
- console.log(chalk.yellow('┌──────────────────────────────────────┐'));
304
- console.log(chalk.yellow('│ 🔑 CONNECTION CODE (ENTER ON APP) │'));
305
- console.log(chalk.yellow('│ │'));
306
- console.log(chalk.yellow(`│ ${chalk.white.bold.bgBlack(` ${this.secretCode.slice(0, 3)} - ${this.secretCode.slice(3)} `)} │`));
307
- console.log(chalk.yellow('│ │'));
308
- console.log(chalk.yellow('└──────────────────────────────────────┘'));
309
- console.log(chalk.cyan('\nWaiting for mobile connection...'));
310
- }
311
- // Cleanup
312
- destroy() {
313
- if (this.nonceCleanupInterval)
314
- clearInterval(this.nonceCleanupInterval);
315
- if (this.keyRotationInterval)
316
- clearInterval(this.keyRotationInterval);
317
- if (this.socket)
318
- this.socket.disconnect();
319
- if (this.ptyProcess)
320
- this.ptyProcess.kill();
321
- }
322
- }
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.ZKRelayAgent = void 0;
30
+ const socket_io_client_1 = require("socket.io-client");
31
+ const os = __importStar(require("os"));
32
+ const pty = __importStar(require("node-pty"));
33
+ const crypto = __importStar(require("crypto"));
34
+ const chalk_1 = __importDefault(require("chalk"));
35
+ const ora_1 = __importDefault(require("ora"));
36
+ const fs = __importStar(require("fs"));
37
+ const path = __importStar(require("path"));
38
+ // 🚨 BURAYI KENDİ DOMAIN ADRESİNLE DEĞİŞTİR!
39
+ const RELAY_SERVER_URL = 'https://api.mobilecoder.xyz';
40
+ // Security Constants
41
+ const PBKDF2_ITERATIONS = 100000;
42
+ const KEY_LENGTH = 32; // 256 bits
43
+ const SALT = 'mobilecoder-salt-v2';
44
+ const KEY_ROTATION_INTERVAL = 600000; // 10 minutes
45
+ const MESSAGE_MAX_AGE = 300000; // 5 minutes
46
+ class ZKRelayAgent {
47
+ socket;
48
+ ptyProcess;
49
+ secretCode;
50
+ roomId;
51
+ // 🔐 Güçlendirilmiş Güvenlik Değişkenleri
52
+ masterKey;
53
+ sessionKey;
54
+ sessionCounter = 0;
55
+ seenNonces = new Set();
56
+ nonceCleanupInterval = null;
57
+ keyRotationInterval = null;
58
+ spinner = (0, ora_1.default)('Initializing Secure Tunnel...');
59
+ constructor() {
60
+ this.setupSecurity();
61
+ this.initPTY();
62
+ this.connectToRelay();
63
+ this.startSecurityMaintenance();
64
+ }
65
+ // 1. Güvenlik Katmanı: PBKDF2 ile Güçlendirilmiş Anahtar Türetme
66
+ setupSecurity() {
67
+ // 6 Haneli rastgele kod (Kullanıcının göreceği)
68
+ const randomNum = Math.floor(100000 + Math.random() * 900000);
69
+ this.secretCode = randomNum.toString();
70
+ // Oda ID'si: Kodun Hash'i (Sunucu sadece bunu görür, kodu göremez)
71
+ this.roomId = crypto.createHash('sha256').update(this.secretCode).digest('hex');
72
+ // 🔐 PBKDF2 ile Master Key Türetme (Brute-force'a karşı savunma)
73
+ this.masterKey = crypto.pbkdf2Sync(this.secretCode, SALT, PBKDF2_ITERATIONS, KEY_LENGTH, 'sha256');
74
+ // Session Key (PFS için rotasyonlu)
75
+ this.sessionKey = this.deriveSessionKey(0);
76
+ }
77
+ // 🔐 Session Key Türetme (Perfect Forward Secrecy)
78
+ deriveSessionKey(counter) {
79
+ return crypto.pbkdf2Sync(this.masterKey, `session-${counter}`, 10000, KEY_LENGTH, 'sha256');
80
+ }
81
+ // 🔄 Key Rotation (Her 10 dakikada bir)
82
+ rotateKey() {
83
+ this.sessionCounter++;
84
+ this.sessionKey = this.deriveSessionKey(this.sessionCounter);
85
+ console.log(chalk_1.default.cyan(`🔄 Session key rotated to #${this.sessionCounter}`));
86
+ // Notify peer about key rotation
87
+ this.sendSecurePayload('key_rotation', { counter: this.sessionCounter });
88
+ }
89
+ // 🧹 Güvenlik Bakımı
90
+ startSecurityMaintenance() {
91
+ // Nonce temizliği (5 dakikadan eski nonce'ları sil)
92
+ this.nonceCleanupInterval = setInterval(() => {
93
+ this.seenNonces.clear();
94
+ }, MESSAGE_MAX_AGE);
95
+ // Key rotation
96
+ this.keyRotationInterval = setInterval(() => {
97
+ this.rotateKey();
98
+ }, KEY_ROTATION_INTERVAL);
99
+ }
100
+ // 2. Terminal (PTY) Başlatma
101
+ initPTY() {
102
+ const shell = os.platform() === 'win32' ? 'powershell.exe' : (process.env.SHELL || 'bash');
103
+ this.ptyProcess = pty.spawn(shell, [], {
104
+ name: 'xterm-256color',
105
+ cols: 80,
106
+ rows: 24,
107
+ cwd: process.cwd(),
108
+ env: process.env
109
+ });
110
+ // Terminalden çıkan veriyi şifrele ve yolla
111
+ this.ptyProcess.onData((data) => {
112
+ this.sendSecurePayload('term_data', data);
113
+ });
114
+ }
115
+ // 3. Relay Sunucusuna Bağlanma
116
+ connectToRelay() {
117
+ this.spinner.start('Connecting to MobileCoder Relay Network...');
118
+ this.socket = (0, socket_io_client_1.io)(RELAY_SERVER_URL, {
119
+ transports: ['websocket'],
120
+ reconnection: true,
121
+ reconnectionAttempts: Infinity
122
+ });
123
+ this.socket.on('connect', () => {
124
+ this.spinner.succeed(chalk_1.default.green('Connected to Relay Node!'));
125
+ // Odaya Katıl (Sadece Hash ID gönderilir)
126
+ this.socket.emit('join_room', { code: this.roomId, type: 'agent' });
127
+ this.printBanner();
128
+ });
129
+ this.socket.on('disconnect', () => {
130
+ this.spinner.warn(chalk_1.default.yellow('Connection lost. Reconnecting...'));
131
+ });
132
+ // Mobilden biri bağlandığında
133
+ this.socket.on('peer_joined', () => {
134
+ console.log(chalk_1.default.green('\n📱 Mobile Client Connected via Encrypted Tunnel!'));
135
+ this.sendEnvInfo();
136
+ });
137
+ // Mobilden şifreli veri geldiğinde
138
+ this.socket.on('relay_data', (message) => {
139
+ this.handleIncomingMessage(message);
140
+ });
141
+ // Also listen for connect_error
142
+ this.socket.on('connect_error', (err) => {
143
+ // console.error(err);
144
+ });
145
+ }
146
+ // 🔐 AES-256-CBC ile Şifreleme (Nonce tabanlı)
147
+ encrypt(data) {
148
+ const nonce = crypto.randomBytes(16);
149
+ const cipher = crypto.createCipheriv('aes-256-cbc', this.sessionKey, nonce);
150
+ const encrypted = Buffer.concat([
151
+ cipher.update(data, 'utf8'),
152
+ cipher.final()
153
+ ]);
154
+ return {
155
+ nonce: nonce.toString('base64'),
156
+ data: encrypted.toString('base64'),
157
+ timestamp: Date.now()
158
+ };
159
+ }
160
+ // 🔐 AES-256-CBC ile Şifre Çözme (Replay Attack Korumalı)
161
+ decrypt(packet) {
162
+ // Timestamp kontrolü (5 dakikadan eski mesajları reddet)
163
+ if (Date.now() - packet.timestamp > MESSAGE_MAX_AGE) {
164
+ throw new Error('Message too old - potential replay attack');
165
+ }
166
+ // Nonce uniqueness kontrolü (Replay attack önleme)
167
+ if (this.seenNonces.has(packet.nonce)) {
168
+ throw new Error('Replay attack detected - duplicate nonce');
169
+ }
170
+ this.seenNonces.add(packet.nonce);
171
+ const decipher = crypto.createDecipheriv('aes-256-cbc', this.sessionKey, Buffer.from(packet.nonce, 'base64'));
172
+ return Buffer.concat([
173
+ decipher.update(Buffer.from(packet.data, 'base64')),
174
+ decipher.final()
175
+ ]).toString('utf8');
176
+ }
177
+ // Gelen veriyi çöz ve işle
178
+ handleIncomingMessage(encryptedPayload) {
179
+ try {
180
+ let decryptedText;
181
+ // Yeni format: { nonce, data, timestamp }
182
+ if (typeof encryptedPayload === 'object' && encryptedPayload.nonce) {
183
+ decryptedText = this.decrypt(encryptedPayload);
184
+ }
185
+ // Legacy format: { e: ciphertext } (CryptoJS uyumluluğu)
186
+ else if (typeof encryptedPayload === 'object' && encryptedPayload.e) {
187
+ // Legacy CryptoJS decrypt fallback
188
+ const CryptoJS = require('crypto-js');
189
+ const bytes = CryptoJS.AES.decrypt(encryptedPayload.e, this.secretCode);
190
+ decryptedText = bytes.toString(CryptoJS.enc.Utf8);
191
+ }
192
+ else {
193
+ return;
194
+ }
195
+ if (!decryptedText)
196
+ return;
197
+ // Veri JSON mu? (Resize komutu vs olabilir)
198
+ try {
199
+ const cmd = JSON.parse(decryptedText);
200
+ // Handle key rotation from peer
201
+ if (cmd.type === 'key_rotation') {
202
+ this.sessionCounter = cmd.counter;
203
+ this.sessionKey = this.deriveSessionKey(this.sessionCounter);
204
+ console.log(chalk_1.default.cyan(`🔄 Key synced to session #${this.sessionCounter}`));
205
+ return;
206
+ }
207
+ // 1. Terminal Input (Keystrokes)
208
+ if (cmd.type === 'input') {
209
+ this.ptyProcess.write(cmd.data);
210
+ return;
211
+ }
212
+ // 2. Terminal Resize
213
+ if (cmd.type === 'resize') {
214
+ this.ptyProcess.resize(cmd.cols, cmd.rows);
215
+ return;
216
+ }
217
+ // 3. Chat Commands (e.g. "ls -la")
218
+ if (cmd.type === 'command') {
219
+ this.ptyProcess.write(cmd.text + '\r');
220
+ return;
221
+ }
222
+ // 4. CLI Tool/Button Commands (e.g. "git status")
223
+ if (cmd.type === 'cli_command') {
224
+ this.ptyProcess.write(cmd.command + '\r');
225
+ return;
226
+ }
227
+ // 5. Tool Calls (File System Access)
228
+ if (cmd.type === 'tool_call') {
229
+ this.handleToolCall(cmd.tool, cmd.data, cmd.id);
230
+ return;
231
+ }
232
+ }
233
+ catch (e) {
234
+ // JSON değilse saf terminal girdisidir
235
+ this.ptyProcess.write(decryptedText);
236
+ }
237
+ }
238
+ catch (error) {
239
+ // Şifre çözülemezse (Yanlış anahtar vb.) sessizce yut
240
+ console.error(chalk_1.default.red('Decryption failed:'), error.message);
241
+ }
242
+ }
243
+ async handleToolCall(tool, args, id) {
244
+ try {
245
+ let result = null;
246
+ if (tool === 'list_directory') {
247
+ const dirPath = args.path ? path.resolve(args.path) : process.cwd();
248
+ const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
249
+ const files = entries.map(entry => ({
250
+ name: entry.name,
251
+ type: entry.isDirectory() ? 'directory' : 'file',
252
+ size: 0 // Simplification for speed
253
+ }));
254
+ result = { files };
255
+ }
256
+ else if (tool === 'read_file') {
257
+ const filePath = path.resolve(args.path);
258
+ const content = await fs.promises.readFile(filePath, 'utf-8');
259
+ result = { content };
260
+ }
261
+ else {
262
+ throw new Error(`Unknown tool: ${tool}`);
263
+ }
264
+ this.sendSecurePayload('tool_result', {
265
+ id,
266
+ result
267
+ });
268
+ }
269
+ catch (error) {
270
+ this.sendSecurePayload('tool_result', {
271
+ id,
272
+ error: error.message,
273
+ type: 'error'
274
+ });
275
+ }
276
+ }
277
+ // Veriyi şifrele ve gönder
278
+ sendSecurePayload(type, data) {
279
+ let payloadStr = '';
280
+ if (type === 'meta' || type === 'tool_result' || type === 'key_rotation') {
281
+ // Standardize format
282
+ if (type === 'tool_result') {
283
+ if (data.type === 'error') {
284
+ payloadStr = JSON.stringify({ ...data });
285
+ }
286
+ else {
287
+ payloadStr = JSON.stringify({ type: 'result', ...data });
288
+ }
289
+ }
290
+ else if (type === 'key_rotation') {
291
+ payloadStr = JSON.stringify({ type: 'key_rotation', ...data });
292
+ }
293
+ else {
294
+ payloadStr = JSON.stringify(data);
295
+ }
296
+ }
297
+ else if (type === 'term_data') {
298
+ // Wrap output in { type: 'output', data: ... }
299
+ if (typeof data === 'string') {
300
+ payloadStr = JSON.stringify({ type: 'output', data: data });
301
+ }
302
+ else {
303
+ payloadStr = JSON.stringify(data);
304
+ }
305
+ }
306
+ // Fallback if empty (shouldn't happen with above logic)
307
+ if (!payloadStr)
308
+ payloadStr = JSON.stringify(data);
309
+ // 🔐 Yeni şifreleme formatı ile gönder
310
+ const encrypted = this.encrypt(payloadStr);
311
+ this.socket.emit('relay_data', {
312
+ room: this.roomId,
313
+ payload: encrypted
314
+ });
315
+ }
316
+ sendEnvInfo() {
317
+ const info = {
318
+ type: 'host_info',
319
+ username: os.userInfo().username,
320
+ hostname: os.hostname(),
321
+ platform: os.platform(),
322
+ cwd: process.cwd()
323
+ };
324
+ this.sendSecurePayload('meta', info);
325
+ }
326
+ printBanner() {
327
+ console.log('\n' + chalk_1.default.bgBlue.bold(' MOBILECODER ZK-RELAY v2.1.0 '));
328
+ console.log(chalk_1.default.gray('🔐 PBKDF2-SHA256 + AES-256-CBC + Nonce-Based Replay Protection'));
329
+ console.log(chalk_1.default.gray('🔄 Perfect Forward Secrecy with 10-min Key Rotation\n'));
330
+ console.log(chalk_1.default.yellow('┌──────────────────────────────────────┐'));
331
+ console.log(chalk_1.default.yellow('│ 🔑 CONNECTION CODE (ENTER ON APP) │'));
332
+ console.log(chalk_1.default.yellow('│ │'));
333
+ console.log(chalk_1.default.yellow(`│ ${chalk_1.default.white.bold.bgBlack(` ${this.secretCode.slice(0, 3)} - ${this.secretCode.slice(3)} `)} │`));
334
+ console.log(chalk_1.default.yellow('│ │'));
335
+ console.log(chalk_1.default.yellow('└──────────────────────────────────────┘'));
336
+ console.log(chalk_1.default.cyan('\nWaiting for mobile connection...'));
337
+ }
338
+ // Cleanup
339
+ destroy() {
340
+ if (this.nonceCleanupInterval)
341
+ clearInterval(this.nonceCleanupInterval);
342
+ if (this.keyRotationInterval)
343
+ clearInterval(this.keyRotationInterval);
344
+ if (this.socket)
345
+ this.socket.disconnect();
346
+ if (this.ptyProcess)
347
+ this.ptyProcess.kill();
348
+ }
349
+ }
350
+ exports.ZKRelayAgent = ZKRelayAgent;
323
351
  //# sourceMappingURL=agent.js.map