@imenam/self-register-proxy 1.0.37

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,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,510 @@
1
+ # Self-Register Proxy
2
+
3
+ [![npm version](https://badge.fury.io/js/@imenam%2Fself-register-proxy.svg)](https://www.npmjs.com/package/@imenam/self-register-proxy)
4
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
5
+
6
+ Un reverse proxy Node.js avec API d'auto-enregistrement, permettant à vos applications de s'enregistrer dynamiquement et de recevoir un port automatiquement attribué.
7
+
8
+ ## Cas d'usage
9
+
10
+ - **Conteneur Docker unique** exposant un seul port vers l'extérieur
11
+ - **Microservices dynamiques** qui démarrent/s'arrêtent sans configuration manuelle
12
+ - **Environnement de développement** avec plusieurs applications Node.js
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @imenam/self-register-proxy
18
+ ```
19
+
20
+ Ou pour une installation globale :
21
+
22
+ ```bash
23
+ npm install -g @imenam/self-register-proxy
24
+ ```
25
+
26
+ ## Démarrage rapide
27
+
28
+ ### 1. Démarrer le proxy
29
+
30
+ ```bash
31
+ # Via npx (sans installation globale)
32
+ npx @imenam/self-register-proxy
33
+
34
+ # Ou si installé globalement
35
+ self-register-proxy
36
+ # (alias : dynamic-proxy)
37
+
38
+ # Ou via npm script (développement local)
39
+ npm start
40
+ ```
41
+
42
+ Le proxy démarre par défaut sur le port `3000`.
43
+
44
+ ### Configuration via fichier .env
45
+
46
+ Créez un fichier `.env` à la racine du projet (voir `.env.example`) :
47
+
48
+ ```env
49
+ PROXY_NAME=Mon Proxy Dev
50
+ PROXY_PORT=3000
51
+ ```
52
+
53
+ ### Configuration via variables d'environnement
54
+
55
+ ```bash
56
+ # Windows PowerShell
57
+ $env:PROXY_PORT="3002"; npx @imenam/self-register-proxy
58
+
59
+ # Linux/Mac
60
+ PROXY_PORT=3002 npx @imenam/self-register-proxy
61
+ ```
62
+
63
+ ### 2. Enregistrer une application
64
+
65
+ Votre application doit s'enregistrer au démarrage :
66
+
67
+ ```javascript
68
+ const response = await fetch('http://localhost:3000/proxy/register', {
69
+ method: 'POST',
70
+ headers: { 'Content-Type': 'application/json' },
71
+ body: JSON.stringify({
72
+ path: '/myapp', // Chemin d'accès via le proxy
73
+ name: 'My App' // Nom de l'application (optionnel)
74
+ })
75
+ });
76
+
77
+ const { port } = await response.json();
78
+ // port = 4000 (attribué automatiquement)
79
+
80
+ // Démarrez votre serveur sur ce port
81
+ app.listen(port);
82
+ ```
83
+
84
+ ### 3. Accéder à votre application
85
+
86
+ ```
87
+ http://localhost:3000/myapp/ → votre application sur le port 4000
88
+ ```
89
+
90
+ ## Page d'accueil
91
+
92
+ L'URL racine `/` affiche une page listant tous les serveurs enregistrés avec des liens pour y accéder.
93
+
94
+ Le nom du proxy (configurable via `PROXY_NAME`) est affiché dans le titre et l'en-tête de la page.
95
+
96
+ - **Navigateurs** (`Accept: text/html`) → Page HTML avec interface moderne
97
+ - **Clients API** (`Accept: application/json`) → Réponse JSON
98
+
99
+ ```bash
100
+ # Accès navigateur : ouvrir http://localhost:3000/
101
+
102
+ # Accès API
103
+ curl -H "Accept: application/json" http://localhost:3000/
104
+ ```
105
+
106
+ **Réponse JSON :**
107
+ ```json
108
+ {
109
+ "name": "Proxy Server",
110
+ "status": "healthy",
111
+ "uptime": 3600,
112
+ "count": 2,
113
+ "routes": [
114
+ {
115
+ "path": "/api",
116
+ "port": 4000,
117
+ "name": "API Server",
118
+ "registeredAt": "2026-01-27T10:00:00.000Z",
119
+ "target": "http://localhost:4000"
120
+ }
121
+ ],
122
+ "availablePorts": 998
123
+ }
124
+ ```
125
+
126
+ ## API du Proxy
127
+
128
+ | Endpoint | Méthode | Description |
129
+ |----------|---------|-------------|
130
+ | `/` | GET | Page d'accueil (HTML ou JSON selon Accept header) |
131
+ | `/proxy/register` | POST | Enregistre une nouvelle route |
132
+ | `/proxy/unregister` | DELETE | Supprime une route |
133
+ | `/proxy/routes` | GET | Liste toutes les routes |
134
+ | `/proxy/health` | GET | Health check du proxy |
135
+
136
+ ### POST /proxy/register
137
+
138
+ Enregistre une nouvelle route et attribue un port.
139
+
140
+ **Request:**
141
+ ```json
142
+ {
143
+ "path": "/myapp",
144
+ "name": "My Application",
145
+ "port": 4005
146
+ }
147
+ ```
148
+
149
+ | Champ | Type | Requis | Description |
150
+ |-------|------|--------|-------------|
151
+ | `path` | string | ✅ | Chemin URL pour accéder à l'app |
152
+ | `name` | string | ❌ | Nom descriptif |
153
+ | `port` | number | ❌ | Port spécifique (sinon auto-attribué) |
154
+
155
+ **Response (201):**
156
+ ```json
157
+ {
158
+ "success": true,
159
+ "path": "/myapp",
160
+ "port": 4000,
161
+ "name": "My Application",
162
+ "message": "Route registered. Start your server on port 4000"
163
+ }
164
+ ```
165
+
166
+ **Erreurs:**
167
+ - `400` - Path manquant
168
+ - `409` - Path ou port déjà utilisé
169
+
170
+ ### DELETE /proxy/unregister
171
+
172
+ Supprime une route enregistrée.
173
+
174
+ **Request:**
175
+ ```json
176
+ {
177
+ "path": "/myapp"
178
+ }
179
+ ```
180
+
181
+ **Response (200):**
182
+ ```json
183
+ {
184
+ "success": true,
185
+ "path": "/myapp",
186
+ "freedPort": 4000
187
+ }
188
+ ```
189
+
190
+ ### GET /proxy/routes
191
+
192
+ Liste toutes les routes enregistrées.
193
+
194
+ **Response:**
195
+ ```json
196
+ {
197
+ "count": 2,
198
+ "routes": [
199
+ {
200
+ "path": "/api",
201
+ "port": 4000,
202
+ "name": "API Server",
203
+ "registeredAt": "2026-01-27T10:00:00.000Z",
204
+ "target": "http://localhost:4000"
205
+ },
206
+ {
207
+ "path": "/web",
208
+ "port": 4001,
209
+ "name": "Web App",
210
+ "registeredAt": "2026-01-27T10:01:00.000Z",
211
+ "target": "http://localhost:4001"
212
+ }
213
+ ],
214
+ "availablePorts": 998
215
+ }
216
+ ```
217
+
218
+ ### GET /proxy/health
219
+
220
+ Vérifie l'état du proxy.
221
+
222
+ **Response:**
223
+ ```json
224
+ {
225
+ "name": "Proxy Server",
226
+ "status": "healthy",
227
+ "uptime": 3600,
228
+ "registeredRoutes": 2,
229
+ "usedPorts": 2
230
+ }
231
+ ```
232
+
233
+ ## Intégration dans votre application
234
+
235
+ ### Exemple complet avec Express
236
+
237
+ ```javascript
238
+ const express = require('express');
239
+
240
+ const PROXY_URL = process.env.PROXY_URL || 'http://localhost:3000';
241
+ const APP_PATH = process.env.APP_PATH || '/myapp';
242
+ const APP_NAME = process.env.APP_NAME || 'My App';
243
+
244
+ async function start() {
245
+ // 1. S'enregistrer auprès du proxy
246
+ const res = await fetch(`${PROXY_URL}/proxy/register`, {
247
+ method: 'POST',
248
+ headers: { 'Content-Type': 'application/json' },
249
+ body: JSON.stringify({ path: APP_PATH, name: APP_NAME })
250
+ });
251
+
252
+ const { port } = await res.json();
253
+
254
+ // 2. Créer l'application
255
+ const app = express();
256
+
257
+ app.get('/', (req, res) => {
258
+ res.json({ message: 'Hello!' });
259
+ });
260
+
261
+ // 3. Démarrer sur le port attribué
262
+ const server = app.listen(port, () => {
263
+ console.log(`App accessible via ${PROXY_URL}${APP_PATH}`);
264
+ });
265
+
266
+ // 4. Se désenregistrer à l'arrêt
267
+ process.on('SIGTERM', async () => {
268
+ await fetch(`${PROXY_URL}/proxy/unregister`, {
269
+ method: 'DELETE',
270
+ headers: { 'Content-Type': 'application/json' },
271
+ body: JSON.stringify({ path: APP_PATH })
272
+ });
273
+ server.close();
274
+ });
275
+ }
276
+
277
+ start();
278
+ ```
279
+
280
+ ### Utilisation du ProxyClient
281
+
282
+ Le package inclut un client helper pour faciliter l'enregistrement :
283
+
284
+ ```javascript
285
+ const ProxyClient = require('@imenam/self-register-proxy');
286
+
287
+ const proxy = new ProxyClient('http://localhost:3000');
288
+ const { port } = await proxy.register('/myapp', 'My App');
289
+
290
+ app.listen(port);
291
+
292
+ // Arrêt propre automatique
293
+ proxy.setupGracefulShutdown(server);
294
+ ```
295
+
296
+ Méthodes disponibles :
297
+
298
+ - `register(path, name, port?)` - Enregistre une route
299
+ - `unregister()` - Supprime l'enregistrement
300
+ - `listRoutes()` - Liste toutes les routes
301
+ - `health()` - Vérifie l'état du proxy
302
+ - `setupGracefulShutdown(server)` - Configure l'arrêt propre
303
+ - `setupHealthRoute(app, options?)` - Ajoute la route de health check
304
+
305
+ ## Docker
306
+
307
+ ### Build et lancement
308
+
309
+ ```bash
310
+ # Build
311
+ npm run docker:build
312
+
313
+ # Démarrer (proxy + 2 apps exemples)
314
+ npm run docker:up
315
+
316
+ # Arrêter
317
+ npm run docker:down
318
+ ```
319
+
320
+ ### Accès
321
+
322
+ - Proxy : `http://localhost:8081`
323
+ - App 1 : `http://localhost:8081/app1`
324
+ - App 2 : `http://localhost:8081/app2`
325
+ - Routes : `http://localhost:8081/proxy/routes`
326
+
327
+ ### Ajouter votre propre service
328
+
329
+ Dans `docker-compose.yml` :
330
+
331
+ ```yaml
332
+ services:
333
+ my-service:
334
+ build: ./my-service
335
+ environment:
336
+ - PROXY_URL=http://proxy:3000
337
+ - APP_PATH=/my-service
338
+ - APP_NAME=My Service
339
+ depends_on:
340
+ - proxy
341
+ networks:
342
+ - proxy-network
343
+ ```
344
+
345
+ ## Health Check Polling
346
+
347
+ Le proxy vérifie périodiquement que les serveurs enregistrés sont toujours actifs. Si un serveur ne répond pas correctement, il est automatiquement désenregistré.
348
+
349
+ ### Implémentation requise
350
+
351
+ Chaque application enregistrée **doit** implémenter la route `GET /proxy/health` qui retourne un code **200** :
352
+
353
+ ```javascript
354
+ // Avec Express
355
+ app.get('/proxy/health', (req, res) => {
356
+ res.status(200).json({ status: 'healthy' });
357
+ });
358
+ ```
359
+
360
+ ### Avec ProxyClient
361
+
362
+ Le helper `ProxyClient` fournit une méthode pour ajouter automatiquement cette route :
363
+
364
+ ```javascript
365
+ const ProxyClient = require('@imenam/self-register-proxy');
366
+
367
+ const proxy = new ProxyClient('http://localhost:3000');
368
+ const app = express();
369
+
370
+ // Ajoute automatiquement GET /proxy/health
371
+ proxy.setupHealthRoute(app);
372
+
373
+ // Ou avec un health check personnalisé
374
+ proxy.setupHealthRoute(app, {
375
+ healthCheck: async () => {
376
+ // Vérifier la connexion à la DB, etc.
377
+ const dbOk = await checkDatabase();
378
+ return dbOk;
379
+ }
380
+ });
381
+ ```
382
+
383
+ ### Comportement
384
+
385
+ - **Période de grâce** : les nouveaux serveurs ont **60 secondes** après l'enregistrement avant d'être vérifiés (laisse le temps au serveur de démarrer)
386
+ - Le proxy vérifie tous les serveurs toutes les **30 secondes** (par défaut)
387
+ - Timeout de **5 secondes** par requête de health check
388
+ - Si la réponse n'est pas un code **200**, le serveur est automatiquement désenregistré
389
+ - Les erreurs de connexion (serveur arrêté, timeout) entraînent aussi le désenregistrement
390
+
391
+ ## Configuration MCP (Cursor / Claude Desktop)
392
+
393
+ Pour utiliser ce proxy comme serveur MCP, ajoutez la configuration suivante :
394
+
395
+ ### Cursor
396
+
397
+ Dans votre fichier `.cursor/mcp.json` :
398
+
399
+ ```json
400
+ {
401
+ "mcpServers": {
402
+ "dynamic-proxy": {
403
+ "command": "npx",
404
+ "args": ["@imenam/self-register-proxy"],
405
+ "env": {
406
+ "PROXY_PORT": "3000"
407
+ }
408
+ }
409
+ }
410
+ }
411
+ ```
412
+
413
+ ### Claude Desktop
414
+
415
+ Dans votre fichier de configuration Claude Desktop (`claude_desktop_config.json`) :
416
+
417
+ ```json
418
+ {
419
+ "mcpServers": {
420
+ "dynamic-proxy": {
421
+ "command": "npx",
422
+ "args": ["@imenam/self-register-proxy"],
423
+ "env": {
424
+ "PROXY_PORT": "3000"
425
+ }
426
+ }
427
+ }
428
+ }
429
+ ```
430
+
431
+ ### Avec variables d'environnement personnalisées
432
+
433
+ ```json
434
+ {
435
+ "mcpServers": {
436
+ "dynamic-proxy": {
437
+ "command": "npx",
438
+ "args": ["@imenam/self-register-proxy"],
439
+ "env": {
440
+ "PROXY_NAME": "MCP Gateway",
441
+ "PROXY_PORT": "8080",
442
+ "HEALTH_CHECK_INTERVAL": "60000",
443
+ "HEALTH_CHECK_TIMEOUT": "10000",
444
+ "HEALTH_CHECK_GRACE_PERIOD": "120000"
445
+ }
446
+ }
447
+ }
448
+ }
449
+ ```
450
+
451
+ ## Configuration
452
+
453
+ Le proxy charge automatiquement le fichier `.env` s'il existe (voir `.env.example` pour un template).
454
+
455
+ ### Variables du proxy
456
+
457
+ | Variable | Défaut | Description |
458
+ |----------|--------|-------------|
459
+ | `PROXY_NAME` | `Proxy Server` | Nom affiché sur la page d'accueil |
460
+ | `PROXY_PORT` | `3000` | Port d'écoute du proxy |
461
+ | `HEALTH_CHECK_INTERVAL` | `30000` | Intervalle du health check polling (ms) |
462
+ | `HEALTH_CHECK_TIMEOUT` | `5000` | Timeout pour chaque health check (ms) |
463
+ | `HEALTH_CHECK_GRACE_PERIOD` | `60000` | Période de grâce pour les nouveaux serveurs (ms) |
464
+ | `PROXY_CONFIG_DIR` | `.` | Dossier de stockage des configurations (meta-configs.json) |
465
+
466
+ ### Variables pour les applications clientes
467
+
468
+ | Variable | Défaut | Description |
469
+ |----------|--------|-------------|
470
+ | `PROXY_URL` | `http://localhost:3000` | URL du proxy (pour les apps) |
471
+ | `APP_PATH` | `/example` | Chemin de l'application |
472
+ | `APP_NAME` | `Example App` | Nom de l'application |
473
+
474
+ ## Plage de ports
475
+
476
+ Par défaut, le proxy attribue des ports entre **4000** et **5000** (1000 ports disponibles).
477
+
478
+ Pour modifier cette plage, éditez `proxy.js` :
479
+
480
+ ```javascript
481
+ const INTERNAL_PORT_START = 4000;
482
+ const INTERNAL_PORT_END = 5000;
483
+ ```
484
+
485
+ ## Fonctionnalités
486
+
487
+ - **Attribution sécurisée des ports** : utilisation d'un mutex pour éviter les conflits lors d'enregistrements simultanés
488
+ - **Période de grâce** : les nouveaux serveurs ne sont pas vérifiés immédiatement, leur laissant le temps de démarrer
489
+ - **Négociation de contenu** : l'URL racine `/` retourne HTML pour les navigateurs, JSON pour les API
490
+ - **Health check automatique** : désenregistrement automatique des serveurs non répondants
491
+
492
+ ## 📝 Logs et Débogage
493
+
494
+ Le serveur enregistre ses activités (requêtes, erreurs, enregistrements) dans un fichier de log persistant :
495
+
496
+ - **Windows** : `C:\var\log\proxy-server\server.log`
497
+ - **Linux/Mac** : `/var/log/proxy-server/server.log`
498
+
499
+ Le format des logs inclut la date, l'heure, le PID et le tag `[PROXY]`.
500
+
501
+ ## Limitations
502
+
503
+ - Les routes sont stockées en mémoire (perdues au redémarrage du proxy)
504
+ - Pas de persistance des enregistrements
505
+ - Pas d'authentification sur l'API de registration
506
+ - Les applications doivent implémenter `GET /proxy/health` pour ne pas être désenregistrées automatiquement
507
+
508
+ ## Licence
509
+
510
+ ISC
package/config.js ADDED
@@ -0,0 +1,31 @@
1
+ const crypto = require('crypto');
2
+
3
+ const PROXY_PORT = process.env.PROXY_PORT || 3000;
4
+ const PROXY_NAME = process.env.PROXY_NAME || 'Proxy Server';
5
+ const INTERNAL_PORT_START = 4000;
6
+ const INTERNAL_PORT_END = 5000;
7
+ const HEALTH_CHECK_INTERVAL = process.env.HEALTH_CHECK_INTERVAL || 30000;
8
+ const HEALTH_CHECK_TIMEOUT = process.env.HEALTH_CHECK_TIMEOUT || 5000;
9
+ const HEALTH_CHECK_GRACE_PERIOD = process.env.HEALTH_CHECK_GRACE_PERIOD || 60000;
10
+
11
+ const ADMIN_PASSWORD = process.env.PROXY_ADMIN_PASSWORD;
12
+ const SESSION_SECRET = process.env.PROXY_SESSION_SECRET || crypto.randomBytes(32).toString('hex');
13
+
14
+ const TERMINAL_CWD = process.env.PROXY_TERMINAL_CWD || process.env.HOME || process.env.USERPROFILE;
15
+ const TERMINAL_COMMAND = process.env.PROXY_TERMINAL_COMMAND || '';
16
+ const OPENCODE_CLEAN_UP = process.env.PROXY_OPENCODE_CLEAN_UP || '';
17
+
18
+ module.exports = {
19
+ PROXY_PORT,
20
+ PROXY_NAME,
21
+ INTERNAL_PORT_START,
22
+ INTERNAL_PORT_END,
23
+ HEALTH_CHECK_INTERVAL,
24
+ HEALTH_CHECK_TIMEOUT,
25
+ HEALTH_CHECK_GRACE_PERIOD,
26
+ ADMIN_PASSWORD,
27
+ SESSION_SECRET,
28
+ TERMINAL_CWD,
29
+ TERMINAL_COMMAND,
30
+ OPENCODE_CLEAN_UP,
31
+ };
package/logger.js ADDED
@@ -0,0 +1,88 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ // FIXME: le prefix global n'est pas thread-safe si plusieurs workers partagent ce module
5
+ let currentPrefix = 'PROXY';
6
+
7
+ const SENSITIVE_KEYS = /password|secret|token|authorization|cookie|api[_-]?key/i;
8
+
9
+ function redactObject(obj, depth = 0) {
10
+ if (depth > 5 || obj === null || typeof obj !== 'object') return obj;
11
+ if (Array.isArray(obj)) return obj.map(item => redactObject(item, depth + 1));
12
+ const redacted = {};
13
+ for (const [key, value] of Object.entries(obj)) {
14
+ if (SENSITIVE_KEYS.test(key)) {
15
+ redacted[key] = '[REDACTED]';
16
+ } else if (typeof value === 'object' && value !== null) {
17
+ redacted[key] = redactObject(value, depth + 1);
18
+ } else {
19
+ redacted[key] = value;
20
+ }
21
+ }
22
+ return redacted;
23
+ }
24
+
25
+ /**
26
+ * Log a message to a file at the root of the disk.
27
+ * @param {string} message The message to log
28
+ * @param {string} [prefix] Optional prefix to override
29
+ */
30
+ function logToFile(message, prefix = '') {
31
+ const logDir = process.platform === 'win32'
32
+ ? 'C:\\var\\log\\proxy-server'
33
+ : '/var/log/proxy-server';
34
+ const logFile = path.join(logDir, 'server.log');
35
+ const now = new Date().toLocaleString('fr-FR', {
36
+ year: 'numeric',
37
+ month: '2-digit',
38
+ day: '2-digit',
39
+ hour: '2-digit',
40
+ minute: '2-digit',
41
+ second: '2-digit',
42
+ });
43
+
44
+ const effectivePrefix = prefix || currentPrefix;
45
+ const label = effectivePrefix ? ` [${effectivePrefix}]` : "";
46
+ const logEntry = `[${now}] [PID ${process.pid}]${label} ${message}\n`;
47
+
48
+ try {
49
+ if (!fs.existsSync(logDir)) {
50
+ fs.mkdirSync(logDir, { recursive: true });
51
+ }
52
+ fs.appendFileSync(logFile, logEntry);
53
+ } catch (error) {
54
+ // On ne log pas l'erreur de log ici pour éviter une boucle infinie
55
+ // mais on l'affiche sur la console
56
+ process.stderr.write(`Critical: Failed to write to log file: ${error.message}\n`);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Override console methods to also log to file
62
+ * @param {string} [prefix] Prefix for this process
63
+ */
64
+ function setupLogging(prefix = 'PROXY') {
65
+ currentPrefix = prefix;
66
+ const originalLog = console.log;
67
+ const originalError = console.error;
68
+
69
+ console.log = (...args) => {
70
+ const message = args.map(arg =>
71
+ typeof arg === 'object' ? JSON.stringify(redactObject(arg), null, 2) : arg
72
+ ).join(' ');
73
+ logToFile(message);
74
+ originalLog.apply(console, args);
75
+ };
76
+
77
+ console.error = (...args) => {
78
+ const message = args.map(arg =>
79
+ typeof arg === 'object' ? JSON.stringify(redactObject(arg), null, 2) : arg
80
+ ).join(' ');
81
+ logToFile(`[ERROR] ${message}`);
82
+ originalError.apply(console, args);
83
+ };
84
+
85
+ logToFile('--- Logging system initialized ---');
86
+ }
87
+
88
+ module.exports = { logToFile, setupLogging };