@fkom13/mcp-sftp-orchestrator 6.0.0 → 11.3.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/README.md CHANGED
@@ -1,102 +1,374 @@
1
- # 🚀 MCP SFTP/SSH Orchestrator
1
+ # 🚀 MCP Orchestrator — SSH/SFTP Infrastructure Orchestration Server
2
2
 
3
- Un serveur MCP (Model-Context-Protocol) puissant pour l'orchestration de tâches distantes. Il gère des connexions SSH et SFTP, une file d'attente de tâches persistante, et expose un ensemble riche d'outils pour la gestion de serveurs, le monitoring, et l'exécution de commandes via une interface `stdio` compatible avec les LLM.
3
+ **Version** : 11.3.0
4
+ **License** : MIT
5
+ **Node** : >= 18.0.0
4
6
 
5
- **Fonctionnalités Principales**
7
+ A Model Context Protocol (MCP) server that turns any AI agent (Claude, OpenCode, Cursor...) into a full-fledged system administrator. Persistent queue, SSH connection pool, hybrid sync/async execution.
6
8
 
7
- - **Gestion de Serveurs** : Ajoutez, listez et supprimez des configurations de serveurs SSH/SFTP.
8
- - **Exécution de Tâches SSH** : Simple, interactive, ou en séquence.
9
- - **Transferts de Fichiers SFTP** : Upload/Download de fichiers et dossiers, avec support des patterns `glob`.
10
- - **File d'Attente Intelligente** : Mode hybride (synchrone/asynchrone) et persistance des tâches.
11
- - **Pooling de Connexions** : Gère un pool de connexions SSH pour une exécution ultra-rapide.
12
- - **Outils de Monitoring** : Surveillez les ressources système, le statut des services (systemd, Docker, PM2) et Fail2Ban.
13
- - **Outils de Logs** : Récupérez les logs de PM2, Docker, ou suivez la fin d'un fichier (`tail`).
9
+ ### Key Features
10
+ - **63 MCP tools** SSH, SFTP, file ops, monitoring, snapshots, tunnels
11
+ - **Built-in security** Command blocklist, port allowlist, hash protection
12
+ - **Multi-server** `task_exec {alias:["vps1","vps2"]}` or `alias:"all"}`
13
+ - **Persistence** tmux + persistent queue = sessions that survive crashes
14
+ - **Snapshots** Deduplicated versioning of your critical files
15
+ - **SSH Tunnels** Local, Remote, SOCKS5 with secured port allowlist
16
+ - **AI Guide** — Built-in manual for the agent (section:index/workflows/audit/security)
17
+
18
+ > **🇫🇷 Version française :** [README.fr.md](README.fr.md)
19
+
20
+ ---
14
21
 
15
22
  ## 📦 Installation
16
23
 
17
- Ce projet est conçu pour être utilisé comme un outil MCP dans un environnement compatible (comme la `gemini-cli`).
24
+ ### Via npx (recommended)
25
+ ```bash
26
+ npx @fkom13/mcp-sftp-orchestrator
27
+ ```
28
+
29
+ ### Via git
30
+ ```bash
31
+ git clone https://github.com/fkom13/mcp-sftp-orchestrator.git
32
+ cd mcp-sftp-orchestrator
33
+ npm install
34
+ cp .env.example .env
35
+ # Edit .env with your paths
36
+ ```
37
+
38
+ Requirements: Node.js >= 18.0.0
39
+
40
+ ---
18
41
 
19
- Enregistrez ce MCP auprès de votre client en utilisant la configuration suivante :
42
+ ## ⚙️ Configuration (.env)
43
+
44
+ All variables are optional. Defaults are designed for standard usage.
45
+
46
+ | Variable | Default | Description |
47
+ |----------|---------|-------------|
48
+ | `MCP_DATA_DIR` | `~/.config/mcp-orchestrator` | Data directory (servers.json, apis.json, queue.json) |
49
+ | `MCP_SYNC_TIMEOUT_S` | `120` | Seconds before background execution |
50
+ | `MCP_DEFAULT_CMD_TIMEOUT_S` | `600` | Default SSH timeout (0 = unlimited) |
51
+ | `MCP_INTERACTIVE_CMD_TIMEOUT_S` | `300` | Interactive command timeout (0 = unlimited) |
52
+ | `MCP_MAX_WAIT_TIMEOUT_S` | `600` | Max timeout for `task_wait` |
53
+ | `MAX_CONNECTIONS_PER_SERVER` | `5` | Max parallel SSH connections per server |
54
+ | `MIN_CONNECTIONS_PER_SERVER` | `1` | Min pooled connections per server |
55
+ | `IDLE_TIMEOUT` | `300000` | Idle connection close delay (ms) |
56
+ | `KEEP_ALIVE_INTERVAL` | `30000` | SSH keepalive interval (ms) |
57
+ | `MAX_QUEUE_SIZE` | `1000` | Max jobs in queue |
58
+ | `SAVE_INTERVAL` | `5000` | Queue disk save interval (ms) |
59
+ | `MCP_ALLOWED_ROOTS` | *(empty)* | Restrict file access to these roots (comma-separated). Empty = full access |
60
+ | `MCP_DEBUG` | `false` | Enable detailed debug logs |
61
+
62
+ ---
63
+
64
+ ## 🔌 MCP Client Configuration (OpenCode, Claude Desktop, etc.)
20
65
 
21
66
  ```json
22
67
  {
23
68
  "mcpServers": {
24
- "mcp-sftp-orchestrator": {
25
- "command": "npx",
26
- "args": [
27
- "@fkom13/mcp-sftp-orchestrator"
28
- ],
69
+ "orchestrator": {
70
+ "command": "node",
71
+ "args": ["/path/to/sftp-mcp/server.js"],
29
72
  "env": {
30
- "MCP_DATA_DIR": "~/.config/mcp-orchestrator"
73
+ "MCP_DATA_DIR": "/path/to/sftp-mcp/data"
31
74
  }
32
75
  }
33
76
  }
34
77
  }
35
78
  ```
36
79
 
37
- Le client MCP lancera automatiquement le serveur via `npx` lors de son premier appel.
80
+ ---
81
+
82
+ ## 🧰 Tool Reference (63 tools)
83
+
84
+ ### Help & Diagnostics
85
+ | Tool | Description |
86
+ |------|-------------|
87
+ | `help` | Complete guide: tools, env vars, parameter schemas |
88
+ | `guide` | AI manual: workflows, cheatsheet, audit, security |
89
+ | `system_diagnostics` | Full system diagnostic (queue, pool, servers, APIs) |
90
+
91
+ ### Server Management
92
+ | Tool | Description |
93
+ |------|-------------|
94
+ | `server_add` | Add/update a server alias |
95
+ | `server_list` | List all configured servers |
96
+ | `server_remove` | Remove a server alias |
97
+ | `infra_overview` | Fleet-wide overview (roles, services, warnings) |
98
+ | `server_note_set/get/list/remove` | Documented server context |
38
99
 
39
- ## 🛠️ Configuration
100
+ ### Security (Blocklist)
101
+ | Tool | Description |
102
+ |------|-------------|
103
+ | `policy_blocklist_list` | List blocked commands |
104
+ | `policy_blocklist_add` | Add a pattern to blocklist |
105
+ | `policy_blocklist_remove` | Remove a pattern from blocklist |
40
106
 
41
- Le serveur est configurable via des variables d'environnement. Vous pouvez créer un fichier `.env` à la racine du projet si vous l'exécutez localement pour le développement.
107
+ ### Task Execution
108
+ | Tool | Description |
109
+ |------|-------------|
110
+ | `task_exec` | SSH one-shot. Supports `alias:["vps1","vps2"]` or `alias:"all"` |
111
+ | `task_exec_interactive` | SSH with interactive prompt handling |
112
+ | `task_exec_sequence` | Sequential SSH commands |
113
+ | `task_transfer` | SFTP transfer. Supports `server_to_server` |
114
+ | `task_transfer_multi` | Bulk transfers with glob patterns |
42
115
 
43
- - `MCP_DATA_DIR`: Le dossier où seront stockées les données (configurations des serveurs, historique, etc.). Par défaut : `~/.config/mcp-orchestrator`.
44
- - `MCP_SYNC_TIMEOUT_S`: Le délai en secondes avant qu'une tâche longue ne passe en arrière-plan. Par défaut : `30`.
116
+ ### Monitoring
117
+ | Tool | Description |
118
+ |------|-------------|
119
+ | `get_system_resources` | CPU, RAM, Disk metrics |
120
+ | `get_services_status` | systemd, Docker, PM2 status (graceful fallback) |
121
+ | `get_fail2ban_status` | Fail2Ban status |
122
+ | `check_api_health` | HTTP health check |
45
123
 
46
- ## 🧰 Référence des Outils (API)
124
+ ### Logs
125
+ | Tool | Description |
126
+ |------|-------------|
127
+ | `get_pm2_logs` | PM2 logs |
128
+ | `get_docker_logs` | Docker logs |
129
+ | `tail_file` | Tail a remote file |
47
130
 
48
- Voici la liste complète des outils exposés par ce serveur MCP.
131
+ ### File Operations (Local + Remote)
132
+ | Tool | Description |
133
+ |------|-------------|
134
+ | `file_read` | Read file + SHA-256 hash (edit protection) |
135
+ | `file_write` | Create/overwrite with `dryRun` and `backup` |
136
+ | `file_edit` | Surgical or full edit + hash protection |
49
137
 
50
- ### Gestion des Serveurs
138
+ ### Comparison & Drift Detection
139
+ | Tool | Description |
140
+ |------|-------------|
141
+ | `diff_files` | Compare 2 files (local/remote, cross-server) |
142
+ | `diff_folders` | Compare 2 directories |
143
+ | `compare_all_sources` | Detect drifts across N servers |
51
144
 
52
- - `server_add`: Enregistre ou met à jour les informations de connexion d'un serveur.
53
- - `server_list`: Affiche la liste de tous les alias de serveurs configurés.
54
- - `server_remove`: Supprime un alias de serveur de la configuration.
145
+ ### Persistent Shell Sessions
146
+ | Tool | Description |
147
+ |------|-------------|
148
+ | `shell_create` | Open a persistent shell (cd/env preserved) |
149
+ | `shell_exec` | Execute in an existing session |
150
+ | `shell_list` / `shell_close` | List/close sessions |
55
151
 
56
- ### Exécution de Tâches
152
+ ### tmux (Surviving Terminal Sessions)
153
+ | Tool | Description |
154
+ |------|-------------|
155
+ | `tmux_create` | Create a persistent tmux session |
156
+ | `tmux_exec` | Send a command to a session |
157
+ | `tmux_read` | Read session buffer |
158
+ | `tmux_list` / `tmux_kill` | List/kill sessions |
57
159
 
58
- - `task_exec`: Exécute une commande SSH (hybride synchrone/asynchrone).
59
- - `task_transfer`: Transfère un fichier ou dossier via SFTP (hybride synchrone/asynchrone).
60
- - `task_exec_interactive`: Exécute une commande SSH interactive (gère les prompts `yes/no`, etc.).
61
- - `task_exec_sequence`: Exécute plusieurs commandes SSH en séquence sur le même serveur.
62
- - `task_transfer_multi`: Transfère plusieurs fichiers/dossiers avec support de patterns `glob`.
160
+ ### SSH Tunnels
161
+ | Tool | Description |
162
+ |------|-------------|
163
+ | `tunnel_create` | Local/remote/SOCKS5 tunnel, persistent via tmux |
164
+ | `tunnel_list` | List active tunnels |
165
+ | `tunnel_close` | Close a tunnel |
166
+ | `tunnel_allowlist_add/remove` | Manage allowed ports |
63
167
 
64
- ### Monitoring & Diagnostics
168
+ ### Snapshots (File Versioning)
169
+ | Tool | Description |
170
+ |------|-------------|
171
+ | `snapshot_create` | Capture file state with deduplication |
172
+ | `snapshot_list` | List snapshots |
173
+ | `snapshot_diff` | Compare 2 snapshots |
174
+ | `snapshot_restore` | Restore (dryRun by default) |
175
+ | `snapshot_delete` | Delete + orphan cleanup |
65
176
 
66
- - `get_system_resources`: Récupère les métriques système vitales (CPU, RAM, Disque).
67
- - `get_services_status`: Récupère le statut des services (systemd, Docker, PM2).
68
- - `get_fail2ban_status`: Récupère les informations du service Fail2Ban.
177
+ ### Queue & Monitoring
178
+ | Tool | Description |
179
+ |------|-------------|
180
+ | `task_queue` | View all active/pending tasks |
181
+ | `task_status` | Task detail by ID |
182
+ | `task_history` | Filterable history |
183
+ | `task_retry` | Retry a failed task |
184
+ | `task_wait` | Wait for a background task |
185
+ | `task_logs` | MCP internal logs |
186
+ | `queue_stats` / `pool_stats` | Queue and SSH pool stats |
69
187
 
70
- ### Récupération de Logs
188
+ ### API Catalog
189
+ | Tool | Description |
190
+ |------|-------------|
191
+ | `api_add` / `api_list` / `api_remove` | API monitoring catalog |
192
+ | `api_check` | Health check via SSH + curl |
71
193
 
72
- - `get_pm2_logs`: Raccourci pour récupérer les logs PM2.
73
- - `get_docker_logs`: Raccourci pour récupérer les logs d'un container Docker.
74
- - `tail_file`: Affiche les dernières lignes d'un fichier distant.
194
+ ---
75
195
 
76
- ### Gestion de la File d'Attente (Queue)
196
+ ## 📖 Usage Examples
197
+
198
+ ### Multi-server
199
+ ```bash
200
+ # One command, multiple servers
201
+ task_exec {alias:["vps1","vps2","vps3"], cmd:"uptime"}
202
+
203
+ # Entire fleet
204
+ task_exec {alias:"all", cmd:"df -h /"}
205
+ ```
206
+
207
+ ### SOCKS5 Proxy Tunnel
208
+ ```bash
209
+ tunnel_create {name:"proxy", type:"socks", listen_port:1080, via:"vps_paris"}
210
+ # → Browser → SOCKS5 127.0.0.1:1080 → Paris VPS
211
+ ```
212
+
213
+ ### Local Tunnel (access remote service)
214
+ ```bash
215
+ tunnel_create {name:"crm", type:"local", listen_port:8080, target:"127.0.0.1:3100", via:"vps_prod"}
216
+ # → http://localhost:8080 → production CRM
217
+ ```
218
+
219
+ ### Remote Tunnel (expose local service)
220
+ ```bash
221
+ tunnel_create {name:"dev", type:"remote", listen_port:9090, target:"127.0.0.1:3000", via:"vps", source:"vps_prod", key_path:"/home/user/.ssh/vps.key"}
222
+ # → vps_prod:9090 → your local machine:3000
223
+ ```
224
+
225
+ ### Persistent tmux Session
226
+ ```bash
227
+ tmux_create {alias:"vps", name:"build", start_cmd:"npm run build"}
228
+ tmux_read {alias:"vps", session:"build"}
229
+ tmux_kill {alias:"vps", session:"build"}
230
+ ```
231
+
232
+ ### Security Blocklist
233
+ ```bash
234
+ # List blocked commands
235
+ policy_blocklist_list
236
+ # → ["rm -rf /", "mkfs*", ...]
237
+
238
+ # Conscious bypass
239
+ task_exec {alias:"vps", cmd:"rm -rf /tmp/cache", skip_policy:true}
240
+ ```
77
241
 
78
- - `task_queue`: Affiche le statut de toutes les tâches dans la file d'attente.
79
- - `task_status`: Récupère les détails d'une tâche par son ID.
80
- - `task_history`: Affiche l'historique des dernières tâches lancées.
81
- - `task_retry`: Relance une tâche qui a échoué ou crashé.
82
- - `queue_stats`: Affiche les statistiques de la queue de tâches.
242
+ ### Secure File Editing
243
+ ```bash
244
+ # Read + hash
245
+ file_read {source:{type:"remote", alias:"vps", path:"/etc/nginx/nginx.conf"}}
246
+ # content + hash
83
247
 
84
- ### Gestion des APIs (Monitoring Externe)
248
+ # Edit with protection
249
+ file_edit {source:{type:"remote", alias:"vps", path:"/etc/nginx/nginx.conf"},
250
+ oldString:"worker_connections 768;",
251
+ newString:"worker_connections 1024;",
252
+ expectedHash:"abc123...",
253
+ backup:true}
85
254
 
86
- - `api_add`: Ajoute une API au catalogue de monitoring.
87
- - `api_list`: Affiche toutes les APIs configurées.
88
- - `api_remove`: Supprime une API du catalogue.
89
- - `api_check`: Lance un test de santé sur une API.
255
+ # Preview without writing
256
+ file_edit {source:{type:"remote", alias:"vps", path:"/etc/nginx/nginx.conf"},
257
+ oldString:"worker_connections 768;",
258
+ newString:"worker_connections 1024;",
259
+ dryRun:true}
260
+ ```
261
+
262
+ ### Snapshot Before Risky Changes
263
+ ```bash
264
+ # Before
265
+ snapshot_create {source:{type:"remote", alias:"vps"}, paths:["/etc/nginx/"], tag:"before-fix"}
266
+
267
+ # After if something broke
268
+ snapshot_restore {snapshotId:"...", target:{type:"remote", alias:"vps"}, dryRun:false, force:true}
269
+ ```
270
+
271
+ ### Multi-server Drift Detection
272
+ ```bash
273
+ compare_all_sources {sources:[
274
+ {type:"remote", alias:"vps1", path:"/etc/nginx/nginx.conf", label:"prod"},
275
+ {type:"remote", alias:"vps2", path:"/etc/nginx/nginx.conf", label:"staging"}
276
+ ]}
277
+ ```
278
+
279
+ ---
280
+
281
+ ## 📚 AI Built-in Manual (guide)
282
+
283
+ The orchestrator includes an interactive manual for your AI agent:
284
+
285
+ ```bash
286
+ guide section:index # Table of contents
287
+ guide section:workflows # Copy-paste recipes
288
+ guide section:cheatsheet # Tool → usage table
289
+ guide section:audit # Full fleet audit in 8 steps
290
+ guide section:security # Blocklist + tunnels
291
+ guide section:pitfalls # Common mistakes
292
+ ```
293
+
294
+ ---
295
+
296
+ ## 🏗️ Architecture
297
+
298
+ ```
299
+ MCP Client (stdio)
300
+
301
+ server.js ─── 63 MCP tools registered
302
+
303
+ ├── queue.js ─────── Persistent job queue (JSON + backup)
304
+ ├── ssh.js ───────── SSH execution (pool + dedicated connections)
305
+ ├── sftp.js ──────── SFTP transfers (upload/download/multi)
306
+ ├── sshPool.js ───── Persistent SSH connection pool
307
+ ├── servers.js ───── CRUD server aliases
308
+ ├── apis.js ──────── CRUD API catalog
309
+ ├── history.js ───── Task history
310
+ ├── config.js ────── Centralized configuration
311
+ ├── utils.js ─────── Utilities (escapeShellArg)
312
+ ├── fileOps.js ───── File operations (read/write/edit)
313
+ ├── diffEngine.js ── Diff engine (files/dirs/sources)
314
+ ├── compareEngine.js ─ Multi-source comparison
315
+ ├── diffFormatter.js ─ Diff formatting
316
+ ├── sourceAdapter.js ─ Local/remote abstraction
317
+ ├── shellSessions.js ─ Persistent shell sessions
318
+ ├── snapshotManager.js ─ Versioned snapshots
319
+ ├── notes.js ──────── Documented server context
320
+ ├── policies.js ──── Command blocklist
321
+ ├── tunnels.js ────── SSH tunnels (local/remote/SOCKS)
322
+ ├── guide.js ──────── AI built-in manual
323
+ └── diagnose.js ───── Diagnostics
324
+ ```
325
+
326
+ ### Job Lifecycle
327
+
328
+ ```
329
+ pending → running → completed / failed
330
+ ↓ (on restart)
331
+ crashed → retry → pending
332
+ ```
333
+
334
+ ---
335
+
336
+ ## 🔒 Security
337
+
338
+ - **Command Blocklist** : `rm -rf /`, `mkfs*`, fork bombs, and other destructive commands are blocked by default
339
+ - **Conscious bypass** : `skip_policy: true` to force execution
340
+ - **Tunnel port allowlist** : only explicitly allowed ports can be used
341
+ - **File access restriction** : `MCP_ALLOWED_ROOTS` env var to limit file operations to specific directories
342
+ - **`escapeShellArg()`** : all URLs and paths are escaped before being passed to curl/shell
343
+ - **Plaintext secret detection** : warning on startup if passwords/API keys are in plaintext
344
+ - **Pre-modification snapshots** : `backup:true` on file_edit/file_write for instant rollback
345
+ - **Recommendation** : use SSH keys (not passwords), store secrets in Vaultwarden
346
+
347
+ ---
348
+
349
+ ## 🧪 Tests
350
+
351
+ ```bash
352
+ node diagnose.js # Full diagnostic
353
+ node test_mcp.js # MCP smoke test
354
+ node test_features.js # Unit tests (queue, pool, glob, prompts, crash)
355
+ ```
90
356
 
91
- ### Administration du Serveur MCP
357
+ ---
92
358
 
93
- - `task_logs`: Affiche les logs du système MCP lui-même.
94
- - `pool_stats`: Affiche les statistiques du pool de connexions SSH.
359
+ ## 🛣️ Roadmap
95
360
 
96
- ## 🤝 Contribution
361
+ | Version | Changes |
362
+ |---------|---------|
363
+ | 10.0.0 | New tools: file_read/write/edit, diff, snapshots, shell, notes |
364
+ | 10.4.0 | server_to_server, help with schemas, audit guide |
365
+ | 11.0.0 | Command Blocklist, Multi-host (`alias:"all"`), tmux |
366
+ | 11.2.0 | SSH Tunnels (local/remote/SOCKS5), allowlist, ssh2 stderr fix |
367
+ | 11.3.0 | AllowedRoots (`MCP_ALLOWED_ROOTS`) for file restriction |
368
+ | 12.0.0 (planned) | Auto key setup for tunnels, webhooks, static dashboard |
97
369
 
98
- Les contributions sont les bienvenues ! N'hésitez pas à ouvrir une *Issue* pour signaler un bug ou proposer une fonctionnalité, ou une *Pull Request* pour soumettre des modifications.
370
+ ---
99
371
 
100
- ## 📄 Licence
372
+ ## 📄 License
101
373
 
102
- Ce projet est sous licence MIT.
374
+ MIT Copyright (c) 2025-2026 Franck (fkom13)
package/ROADMAP.md ADDED
@@ -0,0 +1,21 @@
1
+ # Roadmap MCP Orchestrator
2
+
3
+ ## v11.2.0 (actuelle)
4
+ - ✅ Command Blocklist (policies.js) — sécurité des commandes
5
+ - ✅ Multi-host execution (alias array / "all")
6
+ - ✅ tmux sessions persistantes (create/exec/read/list/kill)
7
+ - ✅ Tunnels SSH (local/remote/SOCKS5) avec allowlist
8
+ - ✅ Guide IA intégré (audit, security, cheatsheet)
9
+ - ✅ Help avec schémas JSON des paramètres
10
+ - ✅ Snapshots versionnés avec déduplication
11
+ - ✅ File edit chirurgical avec protection hash
12
+ - ✅ Diff multi-serveur (compare_all_sources)
13
+ - ✅ get_services_status graceful fallback
14
+
15
+ ## v12.x (planifiée)
16
+ - [ ] Tunnels auto-setup de clés entre serveurs
17
+ - [ ] Webhook on tunnel up/down (n8n compatible)
18
+ - [ ] Dashboard web statique pour l'état du parc
19
+ - [ ] Mode "agent" : exécution planifiée (cron-like)
20
+ - [ ] Intégration Vaultwarden pour les secrets
21
+ - [ ] Tests automatisés CI/CD
package/apis.js CHANGED
@@ -45,6 +45,23 @@ async function saveApis() {
45
45
  // Ajouter ou mettre à jour une API
46
46
  async function addApi(alias, apiConfig) {
47
47
  await ensureInitialized();
48
+
49
+ // Validation
50
+ if (!alias || typeof alias !== 'string') {
51
+ throw new Error("L'alias doit être une chaîne non vide.");
52
+ }
53
+
54
+ if (!apiConfig.url) {
55
+ throw new Error("L'URL de l'API est obligatoire.");
56
+ }
57
+
58
+ // Valider l'URL
59
+ try {
60
+ new URL(apiConfig.url);
61
+ } catch (e) {
62
+ throw new Error(`URL invalide: ${apiConfig.url}`);
63
+ }
64
+
48
65
  apis[alias] = apiConfig;
49
66
  await saveApis();
50
67
  return { success: true, message: `API '${alias}' ajoutée/mise à jour avec succès.` };
@@ -0,0 +1,132 @@
1
+ import { createTwoFilesPatch } from 'diff';
2
+ import sourceAdapter from './sourceAdapter.js';
3
+ import fileOps from './fileOps.js';
4
+
5
+ /**
6
+ * compareEngine — Comparaison d'un même fichier (ou de fichiers équivalents à
7
+ * des emplacements différents) sur plusieurs sources.
8
+ * Détecte les "drifts" de configuration entre serveurs (et localhost).
9
+ *
10
+ * Regroupe les sources par hash : chaque groupe = ensemble de sources ayant
11
+ * un contenu identique. Plus d'un groupe → drift détecté.
12
+ *
13
+ * Chaque source peut porter :
14
+ * - un path différent d'une source à l'autre (ex: .bashrc ici, .zshrc là)
15
+ * - un label personnalisé (source.label) pour un affichage clair
16
+ * Quand un drift est détecté, on peut générer le DIFF réel entre la version
17
+ * de référence (groupe majoritaire) et chaque version divergente.
18
+ */
19
+
20
+ export default {
21
+ /**
22
+ * Compare le contenu d'un fichier sur plusieurs sources.
23
+ * sources = [{ type, path, alias?, label? }, ...] (min 2, local et/ou remote)
24
+ * options = { includeContent: bool (défaut false), includeDiff: bool (défaut true) }
25
+ *
26
+ * Retourne :
27
+ * {
28
+ * drift: bool, // true si plus d'une version distincte
29
+ * reference: { label, sources } | null, // groupe majoritaire (référence)
30
+ * groups: [{ hash, size, sources:[{server,path,label}], content?, isReference }],
31
+ * drifts: [{ from, to, added, removed, diff }], // diff référence → chaque divergence
32
+ * errors: [{ source:{server,path,label}, error }],
33
+ * stats: { totalSources, successfulReads, uniqueVersions, unreadable }
34
+ * }
35
+ */
36
+ async compareSources(sources, options = {}) {
37
+ const includeContent = options.includeContent === true;
38
+ const includeDiff = options.includeDiff !== false; // défaut true
39
+ const results = [];
40
+ const errors = [];
41
+
42
+ // Lecture de chaque source (séquentiel : simple et robuste ; le pool gère le reste)
43
+ for (const source of sources) {
44
+ const origin = sourceAdapter.describe(source);
45
+ try {
46
+ const file = await fileOps.readFile(source);
47
+ results.push({ origin, hash: file.hash, size: file.size, content: file.content });
48
+ } catch (e) {
49
+ errors.push({ source: origin, error: e.message });
50
+ }
51
+ }
52
+
53
+ // Regroupement par hash (on garde le contenu en interne pour le diff)
54
+ const groupsMap = new Map();
55
+ for (const r of results) {
56
+ if (!groupsMap.has(r.hash)) {
57
+ groupsMap.set(r.hash, {
58
+ hash: r.hash,
59
+ size: r.size,
60
+ sources: [],
61
+ _content: r.content // interne, retiré à la fin si includeContent=false
62
+ });
63
+ }
64
+ groupsMap.get(r.hash).sources.push(r.origin);
65
+ }
66
+
67
+ const groups = Array.from(groupsMap.values())
68
+ // le groupe le plus répandu en premier (majorité = référence probable)
69
+ .sort((a, b) => b.sources.length - a.sources.length);
70
+
71
+ const drift = groups.length > 1;
72
+
73
+ // Marque le groupe de référence (majoritaire)
74
+ groups.forEach((g, i) => { g.isReference = i === 0; });
75
+ const reference = groups.length > 0
76
+ ? { label: groups[0].sources[0].label, sources: groups[0].sources }
77
+ : null;
78
+
79
+ // Génère les diffs référence → chaque groupe divergent
80
+ const drifts = [];
81
+ if (drift && includeDiff) {
82
+ const ref = groups[0];
83
+ const refLabel = ref.sources[0].label;
84
+ for (let i = 1; i < groups.length; i++) {
85
+ const g = groups[i];
86
+ const gLabel = g.sources[0].label;
87
+ try {
88
+ const patch = createTwoFilesPatch(
89
+ refLabel, gLabel,
90
+ ref._content, g._content,
91
+ 'référence', 'divergent'
92
+ );
93
+ let added = 0, removed = 0;
94
+ for (const line of patch.split('\n')) {
95
+ if (line.startsWith('+') && !line.startsWith('+++')) added++;
96
+ else if (line.startsWith('-') && !line.startsWith('---')) removed++;
97
+ }
98
+ drifts.push({
99
+ from: refLabel,
100
+ to: gLabel,
101
+ affectedSources: g.sources.map(s => s.label),
102
+ added,
103
+ removed,
104
+ diff: patch
105
+ });
106
+ } catch (e) {
107
+ drifts.push({ from: refLabel, to: gLabel, note: 'diff indisponible (binaire ?)' });
108
+ }
109
+ }
110
+ }
111
+
112
+ // Nettoie le contenu interne (ou l'expose si demandé)
113
+ for (const g of groups) {
114
+ if (includeContent) g.content = g._content;
115
+ delete g._content;
116
+ }
117
+
118
+ return {
119
+ drift,
120
+ reference,
121
+ groups,
122
+ drifts,
123
+ errors,
124
+ stats: {
125
+ totalSources: sources.length,
126
+ successfulReads: results.length,
127
+ uniqueVersions: groups.length,
128
+ unreadable: errors.length
129
+ }
130
+ };
131
+ }
132
+ };
package/config.js CHANGED
@@ -21,13 +21,44 @@ const args = process.argv.slice(2).reduce((acc, arg) => {
21
21
  }, {});
22
22
 
23
23
  const config = {
24
- // La hiérarchie reste la même : les arguments en ligne de commande écrasent le .env, qui écrase le défaut.
25
24
  dataDir: args['data-dir'] || process.env.MCP_DATA_DIR || path.join(os.homedir(), '.config', 'mcp-orchestrator'),
26
- syncTimeout: parseInt(args['sync-timeout'] || process.env.MCP_SYNC_TIMEOUT_S || '10', 10) * 1000,
27
- defaultCommandTimeout: parseInt(process.env.MCP_DEFAULT_CMD_TIMEOUT_MS || '300000', 10),
28
- interactiveCommandTimeout: parseInt(process.env.MCP_INTERACTIVE_CMD_TIMEOUT_MS || '120000', 10)
25
+
26
+ // Timeouts en millisecondes (plus cohérent)
27
+ syncTimeout: parseInt(args['sync-timeout'] || process.env.MCP_SYNC_TIMEOUT_S || '120', 10) * 1000,
28
+ defaultCommandTimeout: parseInt(process.env.MCP_DEFAULT_CMD_TIMEOUT_S || '600', 10) * 1000,
29
+ interactiveCommandTimeout: parseInt(process.env.MCP_INTERACTIVE_CMD_TIMEOUT_S || '300', 10) * 1000,
30
+ maxWaitTimeout: parseInt(process.env.MCP_MAX_WAIT_TIMEOUT_S || '600', 10) * 1000,
31
+
32
+ // Configuration Pool SSH
33
+ maxConnectionsPerServer: parseInt(process.env.MAX_CONNECTIONS_PER_SERVER || '5', 10),
34
+ minConnectionsPerServer: parseInt(process.env.MIN_CONNECTIONS_PER_SERVER || '1', 10),
35
+ idleTimeout: parseInt(process.env.IDLE_TIMEOUT || '300000', 10),
36
+ keepAliveInterval: parseInt(process.env.KEEP_ALIVE_INTERVAL || '30000', 10),
37
+
38
+ // Sécurité
39
+ allowedRoots: (process.env.MCP_ALLOWED_ROOTS || '').split(',').map(s => s.trim()).filter(Boolean),
40
+
41
+ // Configuration Queue
42
+ maxQueueSize: parseInt(process.env.MAX_QUEUE_SIZE || '1000', 10),
43
+ saveInterval: parseInt(process.env.SAVE_INTERVAL || '5000', 10),
44
+ historyRetention: parseInt(process.env.HISTORY_RETENTION || '2678400000', 10), // 31 jours
29
45
  };
30
46
 
47
+ // Validation
48
+ if (config.syncTimeout < 1000) {
49
+ if (process.env.MCP_DEBUG === 'true') {
50
+ console.error('⚠️ syncTimeout trop court, minimum 1000ms');
51
+ }
52
+ config.syncTimeout = 1000;
53
+ }
54
+
55
+ if (config.maxConnectionsPerServer < config.minConnectionsPerServer) {
56
+ if (process.env.MCP_DEBUG === 'true') {
57
+ console.error('⚠️ maxConnections < minConnections, ajustement automatique');
58
+ }
59
+ config.maxConnectionsPerServer = config.minConnectionsPerServer;
60
+ }
61
+
31
62
  // Créer le dossier de données s'il n'existe pas
32
63
  fs.mkdirSync(config.dataDir, { recursive: true });
33
64