renote-server 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,465 @@
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 () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.LocalTerminalConnection = exports.localTerminalManager = exports.ZellijTerminalConnection = void 0;
37
+ const pty = __importStar(require("node-pty"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const fs = __importStar(require("fs"));
41
+ const child_process_1 = require("child_process");
42
+ const logger_1 = require("../utils/logger");
43
+ /**
44
+ * Find the full path to a command
45
+ */
46
+ function findCommand(cmd) {
47
+ const home = process.env.HOME || '';
48
+ const additionalPaths = [
49
+ path.join(home, '.local', 'bin'),
50
+ path.join(home, '.npm-global', 'bin'),
51
+ path.join(home, 'bin'),
52
+ '/usr/local/bin',
53
+ '/opt/homebrew/bin',
54
+ ];
55
+ for (const dir of additionalPaths) {
56
+ const fullPath = path.join(dir, cmd);
57
+ if (fs.existsSync(fullPath)) {
58
+ return fullPath;
59
+ }
60
+ }
61
+ return null;
62
+ }
63
+ /**
64
+ * Get extended PATH
65
+ */
66
+ function getExtendedPath() {
67
+ const home = process.env.HOME || '';
68
+ const currentPath = process.env.PATH || '';
69
+ const additionalPaths = [
70
+ path.join(home, '.local', 'bin'),
71
+ path.join(home, '.npm-global', 'bin'),
72
+ path.join(home, 'bin'),
73
+ '/opt/homebrew/bin',
74
+ '/usr/local/bin',
75
+ ];
76
+ return [...additionalPaths, currentPath].join(':');
77
+ }
78
+ /**
79
+ * Check if zellij is available
80
+ */
81
+ function isZellijAvailable() {
82
+ try {
83
+ (0, child_process_1.execSync)('which zellij', { stdio: 'ignore' });
84
+ return true;
85
+ }
86
+ catch {
87
+ return false;
88
+ }
89
+ }
90
+ /**
91
+ * List existing zellij sessions
92
+ */
93
+ function listZellijSessions() {
94
+ try {
95
+ const output = (0, child_process_1.execSync)('zellij list-sessions -s 2>/dev/null || true', {
96
+ encoding: 'utf-8',
97
+ env: { ...process.env, PATH: getExtendedPath() },
98
+ });
99
+ return output.trim().split('\n').filter(s => s.length > 0);
100
+ }
101
+ catch {
102
+ return [];
103
+ }
104
+ }
105
+ /**
106
+ * Kill a zellij session
107
+ */
108
+ function killZellijSession(sessionName) {
109
+ try {
110
+ (0, child_process_1.execSync)(`zellij kill-session ${sessionName} 2>/dev/null || true`, {
111
+ env: { ...process.env, PATH: getExtendedPath() },
112
+ });
113
+ return true;
114
+ }
115
+ catch {
116
+ return false;
117
+ }
118
+ }
119
+ /**
120
+ * Manages zellij-backed terminal sessions for a client
121
+ */
122
+ class ZellijTerminalConnection {
123
+ constructor(clientId) {
124
+ this.sessions = new Map();
125
+ this.dataCallbacks = new Map();
126
+ this.closeCallbacks = new Map();
127
+ this.clientId = clientId;
128
+ this.zellijAvailable = isZellijAvailable();
129
+ if (this.zellijAvailable) {
130
+ logger_1.logger.info('Zellij is available, using zellij-backed sessions');
131
+ }
132
+ else {
133
+ logger_1.logger.warn('Zellij not found, falling back to plain PTY');
134
+ }
135
+ }
136
+ /**
137
+ * Generate a unique zellij session name
138
+ */
139
+ generateSessionName(sessionId, type) {
140
+ // Use a prefix to identify our sessions
141
+ return `renote-${type}-${sessionId.replace(/[^a-zA-Z0-9]/g, '-')}`;
142
+ }
143
+ /**
144
+ * Check if a session exists
145
+ */
146
+ hasTerminal(sessionId) {
147
+ return this.sessions.has(sessionId);
148
+ }
149
+ /**
150
+ * Rebind callbacks for reconnection
151
+ */
152
+ rebindCallbacks(sessionId, onData, onClose) {
153
+ const session = this.sessions.get(sessionId);
154
+ if (!session) {
155
+ return false;
156
+ }
157
+ this.dataCallbacks.set(sessionId, onData);
158
+ this.closeCallbacks.set(sessionId, onClose);
159
+ // If PTY process exists and is running, we're good
160
+ if (session.ptyProcess) {
161
+ logger_1.logger.info(`Rebound callbacks for session ${sessionId}`);
162
+ return true;
163
+ }
164
+ // PTY was closed but zellij session might still exist - reattach
165
+ if (this.zellijAvailable) {
166
+ const zellijName = this.generateSessionName(sessionId, session.type);
167
+ const existingSessions = listZellijSessions();
168
+ if (existingSessions.includes(zellijName)) {
169
+ logger_1.logger.info(`Reattaching to existing zellij session ${zellijName}`);
170
+ return this.attachToZellijSession(sessionId, session.type, onData, onClose);
171
+ }
172
+ }
173
+ return false;
174
+ }
175
+ /**
176
+ * Attach to an existing or new zellij session
177
+ */
178
+ attachToZellijSession(sessionId, type, onData, onClose, options) {
179
+ const zellijName = this.generateSessionName(sessionId, type);
180
+ const cols = options?.cols || 80;
181
+ const rows = options?.rows || 24;
182
+ const cwd = options?.cwd || process.env.HOME || process.cwd();
183
+ try {
184
+ // zellij attach -c will create if not exists
185
+ const ptyProcess = pty.spawn('zellij', ['attach', '-c', zellijName], {
186
+ name: 'xterm-256color',
187
+ cols,
188
+ rows,
189
+ cwd,
190
+ env: {
191
+ ...process.env,
192
+ PATH: getExtendedPath(),
193
+ TERM: 'xterm-256color',
194
+ COLORTERM: 'truecolor',
195
+ },
196
+ });
197
+ this.setupPtyHandlers(sessionId, ptyProcess, onData, onClose);
198
+ const session = this.sessions.get(sessionId);
199
+ if (session) {
200
+ session.ptyProcess = ptyProcess;
201
+ }
202
+ else {
203
+ this.sessions.set(sessionId, {
204
+ name: zellijName,
205
+ type,
206
+ createdAt: Date.now(),
207
+ ptyProcess,
208
+ });
209
+ }
210
+ logger_1.logger.info(`Attached to zellij session ${zellijName} (${cols}x${rows})`);
211
+ return true;
212
+ }
213
+ catch (error) {
214
+ logger_1.logger.error(`Failed to attach to zellij session ${zellijName}:`, error);
215
+ return false;
216
+ }
217
+ }
218
+ /**
219
+ * Setup PTY event handlers
220
+ */
221
+ setupPtyHandlers(sessionId, ptyProcess, onData, onClose) {
222
+ this.dataCallbacks.set(sessionId, onData);
223
+ this.closeCallbacks.set(sessionId, onClose);
224
+ ptyProcess.onData((data) => {
225
+ const callback = this.dataCallbacks.get(sessionId);
226
+ if (callback) {
227
+ callback(data);
228
+ }
229
+ });
230
+ ptyProcess.onExit(({ exitCode, signal }) => {
231
+ logger_1.logger.info(`PTY for session ${sessionId} exited with code ${exitCode}, signal ${signal}`);
232
+ const session = this.sessions.get(sessionId);
233
+ if (session) {
234
+ session.ptyProcess = null;
235
+ }
236
+ // Note: Don't remove the session - zellij session is still running
237
+ // Only call close callback if client needs to know
238
+ const callback = this.closeCallbacks.get(sessionId);
239
+ if (callback) {
240
+ callback();
241
+ }
242
+ });
243
+ }
244
+ /**
245
+ * Start a new terminal session
246
+ */
247
+ startTerminal(sessionId, onData, onClose, options = { type: 'shell' }) {
248
+ // Check if session already exists
249
+ if (this.sessions.has(sessionId)) {
250
+ return this.rebindCallbacks(sessionId, onData, onClose);
251
+ }
252
+ const type = options.type;
253
+ if (this.zellijAvailable) {
254
+ // Create zellij session
255
+ const zellijName = this.generateSessionName(sessionId, type);
256
+ this.sessions.set(sessionId, {
257
+ name: zellijName,
258
+ type,
259
+ createdAt: Date.now(),
260
+ ptyProcess: null,
261
+ });
262
+ // Attach to zellij session
263
+ const attached = this.attachToZellijSession(sessionId, type, onData, onClose, options);
264
+ if (!attached) {
265
+ this.sessions.delete(sessionId);
266
+ return false;
267
+ }
268
+ // If claude type, run claude command inside zellij
269
+ if (type === 'claude') {
270
+ setTimeout(() => {
271
+ const claudePath = findCommand('claude') || 'claude';
272
+ const args = options.claudeArgs?.join(' ') || '';
273
+ this.writeToTerminal(sessionId, `${claudePath} ${args}\n`);
274
+ }, 500);
275
+ }
276
+ return true;
277
+ }
278
+ else {
279
+ // Fallback to plain PTY
280
+ return this.startPlainPty(sessionId, onData, onClose, options);
281
+ }
282
+ }
283
+ /**
284
+ * Fallback: start plain PTY without zellij
285
+ */
286
+ startPlainPty(sessionId, onData, onClose, options) {
287
+ const cols = options.cols || 80;
288
+ const rows = options.rows || 24;
289
+ const cwd = options.cwd || process.env.HOME || process.cwd();
290
+ let command;
291
+ let args;
292
+ if (options.type === 'claude') {
293
+ const claudePath = findCommand('claude');
294
+ command = claudePath || 'claude';
295
+ args = options.claudeArgs || [];
296
+ }
297
+ else {
298
+ command = process.env.SHELL || (os.platform() === 'win32' ? 'powershell.exe' : 'bash');
299
+ args = [];
300
+ }
301
+ try {
302
+ const ptyProcess = pty.spawn(command, args, {
303
+ name: 'xterm-256color',
304
+ cols,
305
+ rows,
306
+ cwd,
307
+ env: {
308
+ ...process.env,
309
+ PATH: getExtendedPath(),
310
+ TERM: 'xterm-256color',
311
+ COLORTERM: 'truecolor',
312
+ },
313
+ });
314
+ this.sessions.set(sessionId, {
315
+ name: sessionId,
316
+ type: options.type,
317
+ createdAt: Date.now(),
318
+ ptyProcess,
319
+ });
320
+ this.setupPtyHandlers(sessionId, ptyProcess, onData, onClose);
321
+ logger_1.logger.info(`Started plain PTY ${options.type} session ${sessionId} (${cols}x${rows})`);
322
+ return true;
323
+ }
324
+ catch (error) {
325
+ logger_1.logger.error(`Failed to start plain PTY ${sessionId}:`, error);
326
+ return false;
327
+ }
328
+ }
329
+ /**
330
+ * Write to terminal
331
+ */
332
+ writeToTerminal(sessionId, data) {
333
+ const session = this.sessions.get(sessionId);
334
+ if (!session || !session.ptyProcess) {
335
+ logger_1.logger.warn(`Terminal ${sessionId} not found or not attached`);
336
+ return false;
337
+ }
338
+ session.ptyProcess.write(data);
339
+ return true;
340
+ }
341
+ /**
342
+ * Resize terminal
343
+ */
344
+ resizeTerminal(sessionId, cols, rows) {
345
+ const session = this.sessions.get(sessionId);
346
+ if (!session || !session.ptyProcess) {
347
+ logger_1.logger.warn(`Terminal ${sessionId} not found for resize`);
348
+ return false;
349
+ }
350
+ session.ptyProcess.resize(cols, rows);
351
+ logger_1.logger.debug(`Resized terminal ${sessionId} to ${cols}x${rows}`);
352
+ return true;
353
+ }
354
+ /**
355
+ * Close terminal (detach from zellij, but don't kill the session)
356
+ */
357
+ closeTerminal(sessionId, killSession = false) {
358
+ const session = this.sessions.get(sessionId);
359
+ if (!session) {
360
+ logger_1.logger.warn(`Terminal ${sessionId} not found for close`);
361
+ return false;
362
+ }
363
+ // Kill the PTY (detach from zellij)
364
+ if (session.ptyProcess) {
365
+ session.ptyProcess.kill();
366
+ session.ptyProcess = null;
367
+ }
368
+ // Optionally kill the zellij session too
369
+ if (killSession && this.zellijAvailable) {
370
+ killZellijSession(session.name);
371
+ logger_1.logger.info(`Killed zellij session ${session.name}`);
372
+ }
373
+ this.sessions.delete(sessionId);
374
+ this.dataCallbacks.delete(sessionId);
375
+ this.closeCallbacks.delete(sessionId);
376
+ logger_1.logger.info(`Closed terminal ${sessionId}${killSession ? ' (session killed)' : ' (session preserved)'}`);
377
+ return true;
378
+ }
379
+ /**
380
+ * Get list of active terminals
381
+ */
382
+ getActiveTerminals() {
383
+ return Array.from(this.sessions.keys());
384
+ }
385
+ /**
386
+ * Get terminal info
387
+ */
388
+ getTerminalInfo(sessionId) {
389
+ const session = this.sessions.get(sessionId);
390
+ if (!session)
391
+ return null;
392
+ return {
393
+ type: session.type,
394
+ createdAt: session.createdAt,
395
+ zellijSession: this.zellijAvailable ? session.name : undefined,
396
+ };
397
+ }
398
+ /**
399
+ * Close all terminals
400
+ */
401
+ closeAll(killSessions = false) {
402
+ for (const [sessionId] of this.sessions) {
403
+ this.closeTerminal(sessionId, killSessions);
404
+ }
405
+ }
406
+ /**
407
+ * List all zellij sessions managed by this server
408
+ */
409
+ static listManagedSessions() {
410
+ return listZellijSessions().filter(s => s.startsWith('renote-'));
411
+ }
412
+ /**
413
+ * Kill a zellij session by sessionId (without needing a connection)
414
+ * Tries both shell and claude session names
415
+ */
416
+ static killSessionById(sessionId) {
417
+ const sanitized = sessionId.replace(/[^a-zA-Z0-9]/g, '-');
418
+ const shellName = `renote-shell-${sanitized}`;
419
+ const claudeName = `renote-claude-${sanitized}`;
420
+ let killed = false;
421
+ const existingSessions = listZellijSessions();
422
+ if (existingSessions.includes(shellName)) {
423
+ killed = killZellijSession(shellName) || killed;
424
+ }
425
+ if (existingSessions.includes(claudeName)) {
426
+ killed = killZellijSession(claudeName) || killed;
427
+ }
428
+ return killed;
429
+ }
430
+ }
431
+ exports.ZellijTerminalConnection = ZellijTerminalConnection;
432
+ exports.LocalTerminalConnection = ZellijTerminalConnection;
433
+ /**
434
+ * Manager for all client connections
435
+ */
436
+ class ZellijTerminalManager {
437
+ constructor() {
438
+ this.connections = new Map();
439
+ }
440
+ getConnection(clientId) {
441
+ return this.connections.get(clientId);
442
+ }
443
+ getOrCreateConnection(clientId) {
444
+ let connection = this.connections.get(clientId);
445
+ if (!connection) {
446
+ connection = new ZellijTerminalConnection(clientId);
447
+ this.connections.set(clientId, connection);
448
+ logger_1.logger.info(`Created terminal connection for client ${clientId}`);
449
+ }
450
+ return connection;
451
+ }
452
+ removeConnection(clientId, killSessions = false) {
453
+ const connection = this.connections.get(clientId);
454
+ if (connection) {
455
+ connection.closeAll(killSessions);
456
+ this.connections.delete(clientId);
457
+ logger_1.logger.info(`Removed terminal connection for client ${clientId}`);
458
+ }
459
+ }
460
+ getConnectionCount() {
461
+ return this.connections.size;
462
+ }
463
+ }
464
+ // Export with the same interface for compatibility
465
+ exports.localTerminalManager = new ZellijTerminalManager();
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.terminalWebSocketHandler = exports.TerminalWebSocketHandler = void 0;
7
+ const ws_1 = __importDefault(require("ws"));
8
+ const url_1 = require("url");
9
+ const localTerminalManager_1 = require("./localTerminalManager");
10
+ const auth_1 = require("../websocket/auth");
11
+ const logger_1 = require("../utils/logger");
12
+ class TerminalWebSocketHandler {
13
+ constructor() {
14
+ // Map from WebSocket to sessionId for cleanup
15
+ this.wsToSession = new Map();
16
+ this.authManager = new auth_1.AuthManager();
17
+ }
18
+ /**
19
+ * Check if a request should be handled by this handler
20
+ */
21
+ shouldHandle(request) {
22
+ try {
23
+ const url = new url_1.URL(request.url || '', `http://${request.headers.host}`);
24
+ return url.pathname === '/terminal';
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ /**
31
+ * Handle a new terminal WebSocket connection
32
+ */
33
+ handleConnection(ws, request) {
34
+ try {
35
+ const url = new url_1.URL(request.url || '', `http://${request.headers.host}`);
36
+ const token = url.searchParams.get('token') || '';
37
+ const sessionId = url.searchParams.get('sessionId');
38
+ const type = (url.searchParams.get('type') || 'shell');
39
+ const cols = parseInt(url.searchParams.get('cols') || '80', 10);
40
+ const rows = parseInt(url.searchParams.get('rows') || '24', 10);
41
+ // Validate token
42
+ if (!this.authManager.validateToken(token)) {
43
+ logger_1.logger.warn('Terminal WebSocket: invalid token');
44
+ ws.close(4001, 'Invalid token');
45
+ return;
46
+ }
47
+ if (!sessionId) {
48
+ logger_1.logger.warn('Terminal WebSocket: missing sessionId');
49
+ ws.close(4002, 'Missing sessionId');
50
+ return;
51
+ }
52
+ // Generate a unique client ID for this connection
53
+ const clientId = this.authManager.generateClientId();
54
+ this.wsToSession.set(ws, { clientId, sessionId });
55
+ logger_1.logger.info(`Terminal WebSocket connected: clientId=${clientId}, sessionId=${sessionId}, type=${type}, ${cols}x${rows}`);
56
+ const connection = localTerminalManager_1.localTerminalManager.getOrCreateConnection(clientId);
57
+ const options = { type, cols, rows };
58
+ const success = connection.startTerminal(sessionId, (data) => {
59
+ // Send terminal output as text frame
60
+ if (ws.readyState === ws_1.default.OPEN) {
61
+ ws.send(data);
62
+ }
63
+ }, () => {
64
+ // Terminal closed
65
+ if (ws.readyState === ws_1.default.OPEN) {
66
+ ws.close(1000, 'Terminal closed');
67
+ }
68
+ }, options);
69
+ if (!success) {
70
+ logger_1.logger.error(`Failed to start terminal: sessionId=${sessionId}`);
71
+ ws.close(4003, 'Failed to start terminal');
72
+ return;
73
+ }
74
+ // Handle incoming messages
75
+ ws.on('message', (data, isBinary) => {
76
+ if (isBinary) {
77
+ // Binary frame = control message
78
+ this.handleControlMessage(ws, clientId, sessionId, data);
79
+ }
80
+ else {
81
+ // Text frame = terminal input
82
+ connection.writeToTerminal(sessionId, data.toString());
83
+ }
84
+ });
85
+ ws.on('close', () => {
86
+ logger_1.logger.info(`Terminal WebSocket closed: clientId=${clientId}, sessionId=${sessionId}`);
87
+ // Detach but don't kill the zellij session
88
+ connection.closeTerminal(sessionId, false);
89
+ localTerminalManager_1.localTerminalManager.removeConnection(clientId, false);
90
+ this.wsToSession.delete(ws);
91
+ });
92
+ ws.on('error', (error) => {
93
+ logger_1.logger.error(`Terminal WebSocket error: ${error.message}`);
94
+ });
95
+ }
96
+ catch (error) {
97
+ logger_1.logger.error('Terminal WebSocket connection error:', error);
98
+ ws.close(4000, 'Connection error');
99
+ }
100
+ }
101
+ handleControlMessage(ws, clientId, sessionId, data) {
102
+ try {
103
+ const message = JSON.parse(data.toString());
104
+ switch (message.type) {
105
+ case 'resize':
106
+ if (message.cols && message.rows) {
107
+ const connection = localTerminalManager_1.localTerminalManager.getConnection(clientId);
108
+ if (connection) {
109
+ connection.resizeTerminal(sessionId, message.cols, message.rows);
110
+ logger_1.logger.debug(`Terminal resized: ${sessionId} -> ${message.cols}x${message.rows}`);
111
+ }
112
+ }
113
+ break;
114
+ case 'ping':
115
+ // Respond with pong as binary frame
116
+ ws.send(Buffer.from(JSON.stringify({ type: 'pong' })));
117
+ break;
118
+ default:
119
+ logger_1.logger.warn(`Unknown control message type: ${message.type}`);
120
+ }
121
+ }
122
+ catch (error) {
123
+ logger_1.logger.error('Failed to parse control message:', error);
124
+ }
125
+ }
126
+ }
127
+ exports.TerminalWebSocketHandler = TerminalWebSocketHandler;
128
+ exports.terminalWebSocketHandler = new TerminalWebSocketHandler();
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logger = void 0;
4
+ const config_1 = require("../config");
5
+ var LogLevel;
6
+ (function (LogLevel) {
7
+ LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
8
+ LogLevel[LogLevel["INFO"] = 1] = "INFO";
9
+ LogLevel[LogLevel["WARN"] = 2] = "WARN";
10
+ LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
11
+ })(LogLevel || (LogLevel = {}));
12
+ const LEVEL_MAP = {
13
+ debug: LogLevel.DEBUG,
14
+ info: LogLevel.INFO,
15
+ warn: LogLevel.WARN,
16
+ error: LogLevel.ERROR,
17
+ };
18
+ class Logger {
19
+ constructor() {
20
+ this.level = LEVEL_MAP[config_1.CONFIG.logLevel] || LogLevel.INFO;
21
+ }
22
+ log(level, message, ...args) {
23
+ if (level >= this.level) {
24
+ const timestamp = new Date().toISOString();
25
+ const levelName = LogLevel[level];
26
+ console.log(`[${timestamp}] [${levelName}]`, message, ...args);
27
+ }
28
+ }
29
+ debug(message, ...args) {
30
+ this.log(LogLevel.DEBUG, message, ...args);
31
+ }
32
+ info(message, ...args) {
33
+ this.log(LogLevel.INFO, message, ...args);
34
+ }
35
+ warn(message, ...args) {
36
+ this.log(LogLevel.WARN, message, ...args);
37
+ }
38
+ error(message, ...args) {
39
+ this.log(LogLevel.ERROR, message, ...args);
40
+ }
41
+ }
42
+ exports.logger = new Logger();
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthManager = void 0;
4
+ const config_1 = require("../config");
5
+ const logger_1 = require("../utils/logger");
6
+ class AuthManager {
7
+ validateToken(token) {
8
+ if (!config_1.CONFIG.authToken) {
9
+ logger_1.logger.warn('No AUTH_TOKEN configured, accepting all connections');
10
+ return true;
11
+ }
12
+ return token === config_1.CONFIG.authToken;
13
+ }
14
+ generateClientId() {
15
+ return `client_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
16
+ }
17
+ }
18
+ exports.AuthManager = AuthManager;