overmind-mcp 2.8.19 β†’ 2.8.22

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,351 @@
1
+ # πŸ›‘οΈ PLAN SΓ‰CURITΓ‰ OVERMIND β€” GuΓ©rison ComplΓ¨te
2
+
3
+ > **Date** : 28 Mai 2026
4
+ > **Auteur** : Sniper Analyste Financier (via audit Discord)
5
+ > **Relecteur** : sley_73350 (validation en cours)
6
+ > **Version** : 1.0 β€” Brouillon pour validation
7
+
8
+ ---
9
+
10
+ ## πŸ” Γ‰TAT DES LIEUX β€” Diagnostic Critique
11
+
12
+ ### Surface d'attaque actuelle
13
+
14
+ ```
15
+ ╔═══════════════════════════════════════════════════════════════════════════╗
16
+ ║ TRANSPORT │ PROTOCOLE │ POINT D'ENTRÉE │ AUTH? │ PORT ║
17
+ ╠═══════════════════════════════════════════════════════════════════════════╣
18
+ β•‘ STDIO β”‚ JSON-RPC 2.0 β”‚ stdin β†’ stdout β”‚ ❌ β”‚ N/A β•‘
19
+ β•‘ HTTP Stream β”‚ JSON-RPC 2.0 β”‚ POST /mcp β”‚ ❌ β”‚ 3099 β•‘
20
+ β•‘ HTTP Stream β”‚ SSE β”‚ POST /mcp (stream) β”‚ ❌ β”‚ 3099 β•‘
21
+ β•‘ HTTP Stream β”‚ REST β”‚ GET /health β”‚ ❌ β”‚ 3099 β•‘
22
+ β•‘ HTTPS (SSL) β”‚ JSON-RPC 2.0 β”‚ POST /mcp β”‚ ❌ β”‚ 3099 β•‘
23
+ β•‘ HTTPS (SSL) β”‚ SSE β”‚ POST /mcp (stream) β”‚ ❌ β”‚ 3099 β•‘
24
+ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
25
+
26
+ πŸ” CONSTAT : TOUS les endpoints sont OUVERTS β€” 0 authentification cΓ΄tΓ© serveur
27
+ ```
28
+
29
+ ### 14 Outils MCP exposΓ©s sans protection
30
+
31
+ ```
32
+ # β”‚ TOOL β”‚ RISQUE
33
+ ───┼─────────────────────────┼──────────
34
+ 01 β”‚ run_agent β”‚ πŸ”΄ CRITIQUE
35
+ 02 β”‚ run_agents_parallel β”‚ πŸ”΄ CRITIQUE
36
+ 03 β”‚ create_agent β”‚ 🟠 Γ‰LEVΓ‰
37
+ 04 β”‚ delete_agent β”‚ 🟠 Γ‰LEVΓ‰
38
+ 05 β”‚ update_agent_config β”‚ πŸ”΄ CRITIQUE
39
+ 06 β”‚ list_agents β”‚ 🟑 MOYEN
40
+ 07 β”‚ get_agent_configs β”‚ πŸ”΄ CRITIQUE (fuites clΓ©s API)
41
+ 08 β”‚ create_prompt β”‚ 🟑 MOYEN
42
+ 09 β”‚ edit_prompt β”‚ 🟑 MOYEN
43
+ 10 β”‚ memory_search β”‚ 🟑 MOYEN
44
+ 11 β”‚ memory_store β”‚ 🟑 MOYEN
45
+ 12 β”‚ memory_runs β”‚ 🟒 FAIBLE
46
+ 13 β”‚ config_example β”‚ 🟒 FAIBLE
47
+ 14 β”‚ agent_control β”‚ πŸ”΄ CRITIQUE (kill process)
48
+ ```
49
+
50
+ ### Modules bibliothèque — Couverture Bearer
51
+
52
+ ```
53
+ MODULE │ TYPE │ ENVOIE BEARER │ REÇOIT/VERIFY │ STATUS
54
+ ────────────────────────┼─────────┼───────────────┼───────────────┼───────
55
+ overmind-client.ts β”‚ Client β”‚ βœ… OUI β”‚ N/A β”‚ βœ… OK
56
+ cli.ts (serveur) β”‚ Serveur β”‚ N/A β”‚ ❌ NON β”‚ πŸ”΄ GAP
57
+ server.ts (FastMCP) β”‚ Serveur β”‚ N/A β”‚ ❌ NON β”‚ πŸ”΄ GAP
58
+ run_agent.ts β”‚ Tool β”‚ N/A β”‚ ❌ NON β”‚ πŸ”΄ GAP
59
+ run_agents_parallel.ts β”‚ Tool β”‚ N/A β”‚ ❌ NON β”‚ πŸ”΄ GAP
60
+ create_agent.ts β”‚ Tool β”‚ N/A β”‚ ❌ NON β”‚ πŸ”΄ GAP
61
+ manage_agents.ts β”‚ Tool β”‚ N/A β”‚ ❌ NON β”‚ πŸ”΄ GAP
62
+ memory_store.ts β”‚ Tool β”‚ N/A β”‚ ❌ NON β”‚ πŸ”΄ GAP
63
+ memory_search.ts β”‚ Tool β”‚ N/A β”‚ ❌ NON β”‚ πŸ”΄ GAP
64
+ agent_control.ts β”‚ Tool β”‚ N/A β”‚ ❌ NON β”‚ πŸ”΄ GAP
65
+ config.ts β”‚ Config β”‚ N/A β”‚ ❌ NON β”‚ 🟑
66
+ processRegistry.ts β”‚ Runtime β”‚ N/A β”‚ ❌ NON β”‚ πŸ”΄ GAP
67
+ ```
68
+
69
+ ---
70
+
71
+ ## πŸ—οΈ ARCHITECTURE CIBLE β€” AprΓ¨s GuΓ©rison
72
+
73
+ ```
74
+ πŸ” MIDDLEWARE AUTH ACTIF πŸ”
75
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” Bearer β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
76
+ β”‚ Client β”‚ ──────────▢ β”‚ FastMCP │───▢│ 14 Tools β”‚
77
+ β”‚ (TS/JS) β”‚ Header βœ“ β”‚ v4.0.1 β”‚ β”‚ MCP β”‚
78
+ β”‚ β”‚ β”‚ βœ… CHECK βœ“ β”‚ β”‚ (protΓ©gΓ©s)β”‚
79
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
80
+ β”‚
81
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
82
+ β”‚ Transports sΓ©curisΓ©s β”‚
83
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
84
+ β”‚ stdio (pipe local) β”‚
85
+ β”‚ httpStream (port 3099) β”‚
86
+ β”‚ endpoint: /mcp β”‚
87
+ β”‚ βœ… Bearer: REQUIRED β”‚
88
+ β”‚ stateless: true β”‚
89
+ β”‚ SSL: ACTIF β”‚
90
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
91
+ ```
92
+
93
+ ---
94
+
95
+ ## πŸ“‹ PLAN D'IMPLΓ‰MENTATION β€” 5 Phases
96
+
97
+ ### PHASE 1 — Middleware Serveur Bearer (PRIORITÉ MAX)
98
+
99
+ **Objectif** : Bloquer tout accès non authentifié sur HTTP/RPC
100
+
101
+ **Fichier cible** : `src/cli.ts` + `src/server.ts`
102
+
103
+ **TΓ’ches** :
104
+ - [ ] CrΓ©er `src/lib/bearerMiddleware.ts`
105
+ - Fonction `verifyBearer(request): boolean`
106
+ - Lecture `OVERMIND_AUTH` depuis `.env`
107
+ - Comparaison via `crypto.timingSafeEqual()` (anti timing attack)
108
+ - Retourner 401 + message JSON si token absent/invalide
109
+ - [ ] Injecter le middleware dans FastMCP httpStream
110
+ - Hook AVANT `server.start()`
111
+ - Exempter UNIQUEMENT `GET /health` (monitoring)
112
+ - Bloquer tout le reste si token absent
113
+ - [ ] Logger chaque accès (audit trail)
114
+ - IP source, timestamp, tool appelΓ©, rΓ©sultat auth
115
+
116
+ **Code de rΓ©fΓ©rence** :
117
+ ```typescript
118
+ // src/lib/bearerMiddleware.ts
119
+ import crypto from 'crypto';
120
+
121
+ export function verifyBearer(authHeader: string | undefined): boolean {
122
+ const expected = process.env.OVERMIND_AUTH;
123
+ if (!expected) {
124
+ console.warn('[AUTH] ⚠️ OVERMIND_AUTH non dΓ©fini β€” accΓ¨s refusΓ© par dΓ©faut');
125
+ return false;
126
+ }
127
+ if (!authHeader?.startsWith('Bearer ')) return false;
128
+
129
+ const token = authHeader.slice(7);
130
+ const expectedBuf = Buffer.from(expected);
131
+ const tokenBuf = Buffer.from(token);
132
+
133
+ if (tokenBuf.length !== expectedBuf.length) return false;
134
+ return crypto.timingSafeEqual(tokenBuf, expectedBuf);
135
+ }
136
+ ```
137
+
138
+ **Validation** :
139
+ - [ ] Tester sans token β†’ 401
140
+ - [ ] Tester avec mauvais token β†’ 401
141
+ - [ ] Tester avec bon token β†’ 200
142
+ - [ ] GET /health sans token β†’ 200 (exemptΓ©)
143
+
144
+ ---
145
+
146
+ ### PHASE 2 — Intégration dans tous les modules de la bibliothèque
147
+
148
+ **Objectif** : Chaque module serveur vΓ©rifie le Bearer
149
+
150
+ **Fichiers cibles** :
151
+ - `src/cli.ts` (serveur principal)
152
+ - `src/server.ts` (FastMCP)
153
+ - Chaque tool MCP (run_agent, memory_store, etc.)
154
+
155
+ **TΓ’ches** :
156
+ - [ ] Importer `verifyBearer` dans chaque point d'entrΓ©e serveur
157
+ - [ ] Ajouter un wrapper `withAuth(toolHandler)` qui :
158
+ 1. Extrait le header Authorization du contexte MCP
159
+ 2. VΓ©rifie via `verifyBearer()`
160
+ 3. Si Γ©chec β†’ retourne erreur MCP avec code d'auth
161
+ 4. Si succès → exécute le tool normalement
162
+ - [ ] Valider que les 14 tools sont couverts
163
+
164
+ **Code de rΓ©fΓ©rence** :
165
+ ```typescript
166
+ // src/lib/withAuth.ts
167
+ import { verifyBearer } from './bearerMiddleware';
168
+
169
+ export function withAuth(handler: Function) {
170
+ return async (args: any, context: any) => {
171
+ const authHeader = context?.request?.headers?.authorization;
172
+ if (!verifyBearer(authHeader)) {
173
+ throw new Error('[AUTH] ❌ AccΓ¨s refusΓ© β€” Bearer token invalide');
174
+ }
175
+ return handler(args, context);
176
+ };
177
+ }
178
+ ```
179
+
180
+ ---
181
+
182
+ ### PHASE 3 β€” Propagation aux configs agents (.mcp.json)
183
+
184
+ **Objectif** : Chaque agent injecte le Bearer dans ses appels
185
+
186
+ **TΓ’ches** :
187
+ - [ ] VΓ©rifier que `overmind-client.ts` envoie bien le Bearer βœ… (dΓ©jΓ  fait)
188
+ - [ ] Mettre Γ  jour les templates `.mcp.json` des agents
189
+ - Ajouter `Authorization: Bearer ${OVERMIND_AUTH}` dans les headers
190
+ - [ ] Scripts externes β†’ utiliser `OVERMIND_AUTH` depuis `.env`
191
+ - [ ] Documenter la variable dans le README Overmind
192
+
193
+ **Exemple .mcp.json** :
194
+ ```json
195
+ {
196
+ "mcpServers": {
197
+ "overmind": {
198
+ "url": "http://localhost:3099/mcp",
199
+ "headers": {
200
+ "Authorization": "Bearer ${OVERMIND_AUTH}"
201
+ }
202
+ }
203
+ }
204
+ }
205
+ ```
206
+
207
+ ---
208
+
209
+ ### PHASE 4 β€” SSL/TLS + Durcissement RΓ©seau
210
+
211
+ **Objectif** : Chiffrer les communications + rΓ©duire la surface
212
+
213
+ **TΓ’ches** :
214
+ - [ ] Activer SSL sur le port 3099 (HTTPS)
215
+ - GΓ©nΓ©rer certificat auto-signΓ© ou Let's Encrypt
216
+ - Configurer dans FastMCP httpStream
217
+ - [ ] Restreindre le bind Γ  `localhost` uniquement (sauf besoin externe)
218
+ - [ ] Ajouter rate-limiting sur les endpoints
219
+ - Max 100 req/min par IP
220
+ - Ban temporaire après 5 échecs d'auth consécutifs
221
+ - [ ] CORS restrictif (si besoin)
222
+ - [ ] Firewall : bloquer le port 3099 Γ  l'extΓ©rieur sauf reverse proxy
223
+
224
+ ---
225
+
226
+ ### PHASE 5 β€” Audit Trail & Monitoring
227
+
228
+ **Objectif** : Traçabilité complète des accès
229
+
230
+ **TΓ’ches** :
231
+ - [ ] Logger chaque requΓͺte MCP :
232
+ - Timestamp ISO 8601
233
+ - IP source
234
+ - Tool appelΓ©
235
+ - RΓ©sultat auth (success/fail)
236
+ - Agent demandeur
237
+ - [ ] Stocker les logs dans PostgreSQL (table `audit_log`)
238
+ ```sql
239
+ CREATE TABLE audit_log (
240
+ id SERIAL PRIMARY KEY,
241
+ timestamp TIMESTAMPTZ DEFAULT NOW(),
242
+ ip VARCHAR(45),
243
+ tool VARCHAR(100),
244
+ agent VARCHAR(100),
245
+ auth_result BOOLEAN,
246
+ details JSONB
247
+ );
248
+ ```
249
+ - [ ] Alertes Discord automatiques :
250
+ - 3 Γ©checs d'auth consΓ©cutifs β†’ alerte embed
251
+ - Tool critique appelΓ© β†’ notification
252
+ - [ ] Dashboard simple (endpoint `/audit/stats`)
253
+
254
+ ---
255
+
256
+ ## πŸ§ͺ PLAN DE TEST β€” Validation par Phase
257
+
258
+ ### Tests Phase 1 (Middleware)
259
+ ```
260
+ TEST │ DESCRIPTION │ RÉSULTAT ATTENDU
261
+ ─────┼────────────────────────────────┼──────────────────
262
+ T1.1 β”‚ curl POST /mcp sans header β”‚ 401 Unauthorized
263
+ T1.2 β”‚ curl POST /mcp mauvais token β”‚ 401 Unauthorized
264
+ T1.3 β”‚ curl POST /mcp bon token β”‚ 200 OK
265
+ T1.4 β”‚ curl GET /health sans token β”‚ 200 OK (exemptΓ©)
266
+ T1.5 β”‚ curl GET /health mauvais token β”‚ 200 OK (exemptΓ©)
267
+ T1.6 β”‚ Token vide β”‚ 401 Unauthorized
268
+ T1.7 β”‚ OVERMIND_AUTH non dΓ©fini β”‚ 500 / 403
269
+ ```
270
+
271
+ ### Tests Phase 2 (Couverture modules)
272
+ ```
273
+ TEST │ DESCRIPTION │ RÉSULTAT ATTENDU
274
+ ─────┼───────────────────────────────────────┼──────────────────
275
+ T2.1 β”‚ Appeler run_agent sans Bearer β”‚ Erreur MCP auth
276
+ T2.2 β”‚ Appeler run_agent avec Bearer β”‚ ExΓ©cution normale
277
+ T2.3 β”‚ Appeler memory_search sans Bearer β”‚ Erreur MCP auth
278
+ T2.4 β”‚ Appeler agent_control sans Bearer β”‚ Erreur MCP auth
279
+ T2.5 β”‚ VΓ©rifier les 14 tools couverts β”‚ 14/14 βœ…
280
+ ```
281
+
282
+ ### Tests Phase 3 (Propagation)
283
+ ```
284
+ TEST │ DESCRIPTION │ RÉSULTAT ATTENDU
285
+ ─────┼──────────────────────────────────────────┼──────────────────
286
+ T3.1 β”‚ Agent Claude appelle overmind sans Bearer β”‚ Connexion refusΓ©e
287
+ T3.2 β”‚ Agent Kilo appelle overmind avec Bearer β”‚ Connexion OK
288
+ T3.3 β”‚ Script externe sans OVERMIND_AUTH β”‚ Γ‰chec auth
289
+ T3.4 │ Script externe avec OVERMIND_AUTH │ Succès
290
+ ```
291
+
292
+ ### Tests Phase 4 (SSL + Durcissement)
293
+ ```
294
+ TEST │ DESCRIPTION │ RÉSULTAT ATTENDU
295
+ ─────┼──────────────────────────────────────────┼──────────────────
296
+ T4.1 │ Accès HTTP (non SSL) │ Connexion refusée
297
+ T4.2 │ Accès HTTPS avec cert valide │ 200 OK
298
+ T4.3 β”‚ Rate limit : 100+ req/min β”‚ 429 Too Many
299
+ T4.4 β”‚ 5 Γ©checs auth consΓ©cutifs β”‚ Ban temporaire
300
+ T4.5 │ Bind localhost uniquement │ Pas d'accès externe
301
+ ```
302
+
303
+ ### Tests Phase 5 (Audit)
304
+ ```
305
+ TEST │ DESCRIPTION │ RÉSULTAT ATTENDU
306
+ ─────┼──────────────────────────────────────────┼──────────────────
307
+ T5.1 β”‚ Appel tool β†’ vΓ©rifier log PostgreSQL β”‚ EntrΓ©e prΓ©sente
308
+ T5.2 β”‚ Γ‰chec auth β†’ vΓ©rifier log β”‚ EntrΓ©e avec auth_result=false
309
+ T5.3 β”‚ 3 Γ©checs consΓ©cutifs β†’ alerte Discord β”‚ Embed envoyΓ©
310
+ T5.4 β”‚ GET /audit/stats β”‚ JSON statistiques
311
+ ```
312
+
313
+ ---
314
+
315
+ ## πŸ“Š PRIORISATION & EFFORT
316
+
317
+ ```
318
+ PHASE │ PRIORITÉ │ EFFORT │ IMPACT │ DÉPENDANCE
319
+ ──────┼─────────────┼───────────┼────────────┼────────────
320
+ 1 β”‚ πŸ”΄ CRITIQUE β”‚ 🟑 Moyen β”‚ 🟒 MAX β”‚ Aucune
321
+ 2 β”‚ 🟠 Γ‰LEVΓ‰ β”‚ 🟑 Moyen β”‚ 🟒 Γ‰LEVΓ‰ β”‚ Phase 1
322
+ 3 β”‚ 🟑 MOYEN β”‚ 🟒 Faible β”‚ 🟑 MOYEN β”‚ Phase 1
323
+ 4 β”‚ 🟑 MOYEN β”‚ πŸ”΄ Γ‰LEVΓ‰ β”‚ 🟒 Γ‰LEVΓ‰ β”‚ Phase 1
324
+ 5 β”‚ 🟒 FAIBLE β”‚ 🟑 Moyen β”‚ 🟑 MOYEN β”‚ Phase 1+2
325
+ ```
326
+
327
+ **Recommandation** : Phase 1 immΓ©diate β†’ Phase 2 le mΓͺme jour β†’ Phase 3 le lendemain β†’ Phase 4-5 dans la semaine
328
+
329
+ ---
330
+
331
+ ## πŸ”‘ LIVRABLES
332
+
333
+ 1. `src/lib/bearerMiddleware.ts` β€” Middleware de vΓ©rification Bearer
334
+ 2. `src/lib/withAuth.ts` β€” Wrapper pour les handlers MCP
335
+ 3. `.env` β€” Ajout de `OVERMIND_AUTH=<token_sΓ©curisΓ©>`
336
+ 4. `audit_log` table PostgreSQL β€” TraΓ§abilitΓ©
337
+ 5. Documentation mise Γ  jour β€” README + SECURITY.md
338
+
339
+ ---
340
+
341
+ ## ⚠️ POINTS D'ATTENTION (Sley à vérifier)
342
+
343
+ - Le Bearer middleware doit-il Γͺtre dans `cli.ts` OU dans un hook FastMCP ?
344
+ - FastMCP v4.0.1 supporte-t-il un middleware natif ou faut-il un workaround Express ?
345
+ - STDIO doit-il aussi Γͺtre protΓ©gΓ© ou est-il trusted par design (local pipe) ?
346
+ - Le token doit-il Γͺtre rotatif ou statique ?
347
+ - Faut-il un système de scopes (admin vs lecture seule) ?
348
+
349
+ ---
350
+
351
+ *πŸ“ Document en attente de validation par sley_73350*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overmind-mcp",
3
- "version": "2.8.19",
3
+ "version": "2.8.22",
4
4
  "description": "Orchestrateur universel agents IA multi-modeles via MCP. Inclut le protocole 'Custom-Nickname' pour identifier vos agents avec des surnoms originaux (The Chaos Prophet, Shadow Sniper, etc.), l'isolation mΓ©moire (Private Memory Context) et le support pour QwenCli et Nous Hermes. Installation automatique des dΓ©pendances Docker (PostgreSQL, pgvector) inclus.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env bash
2
+ # ════════════════════════════════════════════════════════════════════
3
+ # Overmind Bridge β€” Smoke Test (post-install validation)
4
+ # ════════════════════════════════════════════════════════════════════
5
+ #
6
+ # VΓ©rifie en moins de 10 secondes qu'un bridge nouvellement installΓ©
7
+ # répond correctement. À lancer après `systemctl start overmind-bridge`.
8
+ #
9
+ # Usage:
10
+ # ./scripts/bridge-smoke-test.sh
11
+ # ./scripts/bridge-smoke-test.sh --host 192.168.1.10 --port 3100
12
+ # ./scripts/bridge-smoke-test.sh --auth-token secret
13
+ #
14
+ # Exit codes:
15
+ # 0 = tous les tests OK
16
+ # 1 = au moins un test a Γ©chouΓ©
17
+ #
18
+ # DΓ©pendances : bash 4+, curl, jq (optionnel mais recommandΓ©)
19
+ # ════════════════════════════════════════════════════════════════════
20
+
21
+ set -uo pipefail
22
+
23
+ # ─── Defaults ──────────────────────────────────────────────────────
24
+ HOST="127.0.0.1"
25
+ PORT="3100"
26
+ TOKEN=""
27
+ ENV_FILE="/etc/overmind/bridge.env"
28
+ TIMEOUT=5
29
+
30
+ # ─── Couleurs (auto-dΓ©sactivΓ©es si pas TTY) ────────────────────────
31
+ if [[ -t 1 ]]; then
32
+ C_RED=$'\033[0;31m'
33
+ C_GREEN=$'\033[0;32m'
34
+ C_YELLOW=$'\033[1;33m'
35
+ C_BLUE=$'\033[0;34m'
36
+ C_BOLD=$'\033[1m'
37
+ C_RESET=$'\033[0m'
38
+ else
39
+ C_RED="" C_GREEN="" C_YELLOW="" C_BLUE="" C_BOLD="" C_RESET=""
40
+ fi
41
+
42
+ # ─── Args parsing ─────────────────────────────────────────────────
43
+ while [[ $# -gt 0 ]]; do
44
+ case "$1" in
45
+ --host) HOST="$2"; shift 2 ;;
46
+ --port) PORT="$2"; shift 2 ;;
47
+ --auth-token) TOKEN="$2"; shift 2 ;;
48
+ --env-file) ENV_FILE="$2"; shift 2 ;;
49
+ -h|--help)
50
+ sed -n '2,20p' "$0" | sed 's/^# *//'
51
+ exit 0
52
+ ;;
53
+ *)
54
+ echo "Unknown arg: $1" >&2
55
+ exit 2
56
+ ;;
57
+ esac
58
+ done
59
+
60
+ # ─── Load token from env file si pas fourni ───────────────────────
61
+ if [[ -z "$TOKEN" && -f "$ENV_FILE" ]]; then
62
+ TOKEN=$(grep '^BRIDGE_AUTH_TOKEN=' "$ENV_FILE" | cut -d= -f2- | tr -d '"' | tr -d "'" || true)
63
+ fi
64
+
65
+ BASE_URL="http://${HOST}:${PORT}"
66
+ PASS=0
67
+ FAIL=0
68
+
69
+ # ─── Helpers ──────────────────────────────────────────────────────
70
+ have_jq() { command -v jq >/dev/null 2>&1; }
71
+
72
+ check() {
73
+ local name="$1"
74
+ local result="$2"
75
+ if [[ "$result" == "ok" ]]; then
76
+ echo -e " ${C_GREEN}βœ“${C_RESET} $name"
77
+ PASS=$((PASS + 1))
78
+ else
79
+ echo -e " ${C_RED}βœ—${C_RESET} $name"
80
+ FAIL=$((FAIL + 1))
81
+ fi
82
+ }
83
+
84
+ section() {
85
+ echo
86
+ echo -e "${C_BOLD}${C_BLUE}β–Ά $1${C_RESET}"
87
+ }
88
+
89
+ # ─── Banner ───────────────────────────────────────────────────────
90
+ echo -e "${C_BOLD}═══════════════════════════════════════════════════════${C_RESET}"
91
+ echo -e "${C_BOLD} Overmind Bridge β€” Smoke Test${C_RESET}"
92
+ echo -e "${C_BOLD} Target: ${BASE_URL}${C_RESET}"
93
+ echo -e "${C_BOLD}═══════════════════════════════════════════════════════${C_RESET}"
94
+
95
+ # ─── Test 1 : service joignable (TCP) ─────────────────────────────
96
+ section "1. Reachability"
97
+ if curl -sS --max-time "$TIMEOUT" -o /dev/null -w "%{http_code}" \
98
+ "$BASE_URL/health" 2>/dev/null | grep -qE '^(200|401|403)$'; then
99
+ check "HTTP /health rΓ©pond (200/401/403)" ok
100
+ else
101
+ check "HTTP /health ne rΓ©pond pas β€” bridge down ?" fail
102
+ echo
103
+ echo -e "${C_RED}βœ— Bridge inaccessible. Abandon.${C_RESET}"
104
+ echo -e " VΓ©rifier : ${C_YELLOW}systemctl status overmind-bridge${C_RESET}"
105
+ exit 1
106
+ fi
107
+
108
+ # ─── Test 2 : /health retourne un JSON valide ────────────────────
109
+ section "2. /health endpoint"
110
+ HEALTH_BODY=$(curl -sS --max-time "$TIMEOUT" "$BASE_URL/health" 2>/dev/null || true)
111
+
112
+ if have_jq; then
113
+ HEALTH_STATUS=$(echo "$HEALTH_BODY" | jq -r '.status // "missing"' 2>/dev/null)
114
+ else
115
+ HEALTH_STATUS=$(echo "$HEALTH_BODY" | grep -oE '"status"[[:space:]]*:[[:space:]]*"[^"]+"' | head -1 | cut -d'"' -f4)
116
+ fi
117
+
118
+ if [[ -n "$HEALTH_STATUS" && "$HEALTH_STATUS" != "missing" ]]; then
119
+ check "/health expose un status ($HEALTH_STATUS)" ok
120
+ else
121
+ check "/health n'expose pas de status β€” endpoint cassΓ© ?" fail
122
+ fi
123
+
124
+ # ─── Build AUTH_HEADER array for reuse ───────────────────────────
125
+ AUTH_HEADER=()
126
+ if [[ -n "$TOKEN" ]]; then
127
+ AUTH_HEADER=(-H "Authorization: Bearer $TOKEN")
128
+ fi
129
+
130
+ # ─── Test 3 : auth requise si token configurΓ© ────────────────────
131
+ section "3. Authentication"
132
+ if [[ -n "$TOKEN" ]]; then
133
+ HTTP_NO_AUTH=$(curl -sS --max-time "$TIMEOUT" -o /dev/null -w "%{http_code}" \
134
+ -X POST "$BASE_URL/rpc" \
135
+ -H 'Content-Type: application/json' \
136
+ -d '{"jsonrpc":"2.0","id":1,"method":"health.ping","params":{}}' 2>/dev/null || echo "000")
137
+
138
+ if [[ "$HTTP_NO_AUTH" == "401" ]]; then
139
+ check "Sans token β†’ 401 (auth actif)" ok
140
+ else
141
+ check "Sans token β†’ $HTTP_NO_AUTH (attendu: 401)" fail
142
+ fi
143
+
144
+ HTTP_WITH_AUTH=$(curl -sS --max-time "$TIMEOUT" -o /dev/null -w "%{http_code}" \
145
+ -X POST "$BASE_URL/rpc" \
146
+ "${AUTH_HEADER[@]}" \
147
+ -H 'Content-Type: application/json' \
148
+ -d '{"jsonrpc":"2.0","id":1,"method":"health.ping","params":{}}' 2>/dev/null || echo "000")
149
+
150
+ if [[ "$HTTP_WITH_AUTH" == "200" ]]; then
151
+ check "Avec token β†’ 200 (auth OK)" ok
152
+ else
153
+ check "Avec token β†’ $HTTP_WITH_AUTH (attendu: 200)" fail
154
+ fi
155
+ else
156
+ HTTP_OPEN=$(curl -sS --max-time "$TIMEOUT" -o /dev/null -w "%{http_code}" \
157
+ -X POST "$BASE_URL/rpc" \
158
+ -H 'Content-Type: application/json' \
159
+ -d '{"jsonrpc":"2.0","id":1,"method":"health.ping","params":{}}' 2>/dev/null || echo "000")
160
+
161
+ if [[ "$HTTP_OPEN" == "200" ]]; then
162
+ check "/rpc ouvert (auth non configurΓ©e)" ok
163
+ else
164
+ check "/rpc ouvert β†’ $HTTP_OPEN (attendu: 200)" fail
165
+ fi
166
+ fi
167
+
168
+ # ─── Test 4 : health.ping RPC ────────────────────────────────────
169
+ section "4. JSON-RPC methods"
170
+ PING_BODY=$(curl -sS --max-time "$TIMEOUT" \
171
+ "${AUTH_HEADER[@]}" \
172
+ -H 'Content-Type: application/json' \
173
+ -X POST "$BASE_URL/rpc" \
174
+ -d '{"jsonrpc":"2.0","id":1,"method":"health.ping","params":{}}' 2>/dev/null || true)
175
+
176
+ if echo "$PING_BODY" | grep -q '"pong"[[:space:]]*:[[:space:]]*true'; then
177
+ check "RPC health.ping rΓ©pond" ok
178
+ else
179
+ check "RPC health.ping Γ©choue β€” body: ${PING_BODY:0:100}" fail
180
+ fi
181
+
182
+ # ─── Test 5 : JSON-RPC validation (params invalides) ─────────────
183
+ section "5. Error handling"
184
+ INVALID_BODY=$(curl -sS --max-time "$TIMEOUT" \
185
+ "${AUTH_HEADER[@]}" \
186
+ -H 'Content-Type: application/json' \
187
+ -X POST "$BASE_URL/rpc" \
188
+ -d '{"jsonrpc":"2.0","id":1,"method":"agent.run","params":{}}' 2>/dev/null || true)
189
+
190
+ if echo "$INVALID_BODY" | grep -q '"code"[[:space:]]*:[[:space:]]*-32602'; then
191
+ check "Params invalides β†’ -32602 (Invalid params)" ok
192
+ else
193
+ check "Params invalides β€” code attendu: -32602, reΓ§u: $INVALID_BODY" fail
194
+ fi
195
+
196
+ # ─── Test 6 : batch request ──────────────────────────────────────
197
+ section "6. Batch RPC"
198
+ BATCH_BODY=$(curl -sS --max-time "$TIMEOUT" \
199
+ "${AUTH_HEADER[@]}" \
200
+ -H 'Content-Type: application/json' \
201
+ -X POST "$BASE_URL/rpc" \
202
+ -d '[
203
+ {"jsonrpc":"2.0","id":1,"method":"health.ping","params":{}},
204
+ {"jsonrpc":"2.0","id":2,"method":"health.ping","params":{}}
205
+ ]' 2>/dev/null || true)
206
+
207
+ BATCH_COUNT=$(echo "$BATCH_BODY" | grep -oE '"id"[[:space:]]*:[[:space:]]*[12]' | wc -l | tr -d ' ')
208
+ if [[ "$BATCH_COUNT" == "2" ]]; then
209
+ check "Batch RPC retourne 2 rΓ©ponses" ok
210
+ else
211
+ check "Batch RPC β€” reΓ§u: $BATCH_COUNT rΓ©ponses (attendu: 2)" fail
212
+ fi
213
+
214
+ # ─── RΓ©sumΓ© ───────────────────────────────────────────────────────
215
+ echo
216
+ echo -e "${C_BOLD}═══════════════════════════════════════════════════════${C_RESET}"
217
+ if [[ $FAIL -eq 0 ]]; then
218
+ echo -e "${C_GREEN}${C_BOLD} βœ“ $PASS tests OK, 0 Γ©chec${C_RESET}"
219
+ echo -e "${C_GREEN} Bridge opΓ©rationnel sur ${BASE_URL}${C_RESET}"
220
+ echo -e "${C_BOLD}═══════════════════════════════════════════════════════${C_RESET}"
221
+ exit 0
222
+ else
223
+ echo -e "${C_RED}${C_BOLD} βœ— $FAIL Γ©chec(s), $PASS tests OK${C_RESET}"
224
+ echo
225
+ echo -e " ${C_YELLOW}Diagnostic :${C_RESET}"
226
+ echo -e " 1. ${C_YELLOW}systemctl status overmind-bridge${C_RESET}"
227
+ echo -e " 2. ${C_YELLOW}journalctl -u overmind-bridge -n 100${C_RESET}"
228
+ echo -e " 3. ${C_YELLOW}curl -v $BASE_URL/health${C_RESET}"
229
+ echo -e "${C_BOLD}═══════════════════════════════════════════════════════${C_RESET}"
230
+ exit 1
231
+ fi