overmind-mcp 2.8.22 → 2.8.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overmind-mcp",
3
- "version": "2.8.22",
3
+ "version": "2.8.24",
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",
@@ -18,6 +18,10 @@
18
18
  "import": "./dist/index.js",
19
19
  "types": "./dist/index.d.ts"
20
20
  },
21
+ "./bridge": {
22
+ "import": "./dist/bridge/index.js",
23
+ "types": "./dist/bridge/index.d.ts"
24
+ },
21
25
  "./package.json": "./package.json"
22
26
  },
23
27
  "files": [
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env bash
2
- # ════════════════════════════════════════════════════════════════════
3
- # Overmind Bridge Smoke Test (post-install validation)
4
- # ════════════════════════════════════════════════════════════════════
2
+ # ============================================================================
3
+ # Overmind Bridge - Smoke Test (post-install validation)
4
+ # ============================================================================
5
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`.
6
+ # Verifie en moins de 10s qu'un bridge nouvellement installe repond
7
+ # correctement. A lancer apres "systemctl start overmind-bridge".
8
8
  #
9
9
  # Usage:
10
10
  # ./scripts/bridge-smoke-test.sh
@@ -13,21 +13,21 @@
13
13
  #
14
14
  # Exit codes:
15
15
  # 0 = tous les tests OK
16
- # 1 = au moins un test a échoué
16
+ # 1 = au moins un test a echoue
17
17
  #
18
- # Dépendances : bash 4+, curl, jq (optionnel mais recommandé)
19
- # ════════════════════════════════════════════════════════════════════
18
+ # Dependances : bash 4+, curl, jq (optionnel)
19
+ # ============================================================================
20
20
 
21
21
  set -uo pipefail
22
22
 
23
- # ─── Defaults ──────────────────────────────────────────────────────
23
+ # === Defaults ===
24
24
  HOST="127.0.0.1"
25
25
  PORT="3100"
26
- TOKEN=""
26
+ AUTH_TOKEN_VAL=""
27
27
  ENV_FILE="/etc/overmind/bridge.env"
28
28
  TIMEOUT=5
29
29
 
30
- # ─── Couleurs (auto-désactivées si pas TTY) ────────────────────────
30
+ # === Couleurs, auto-desactivees si pas TTY ===
31
31
  if [[ -t 1 ]]; then
32
32
  C_RED=$'\033[0;31m'
33
33
  C_GREEN=$'\033[0;32m'
@@ -36,16 +36,22 @@ if [[ -t 1 ]]; then
36
36
  C_BOLD=$'\033[1m'
37
37
  C_RESET=$'\033[0m'
38
38
  else
39
- C_RED="" C_GREEN="" C_YELLOW="" C_BLUE="" C_BOLD="" C_RESET=""
39
+ C_RED=""
40
+ C_GREEN=""
41
+ C_YELLOW=""
42
+ C_BLUE=""
43
+ C_BOLD=""
44
+ C_RESET=""
40
45
  fi
41
46
 
42
- # ─── Args parsing ─────────────────────────────────────────────────
47
+ # === Args parsing ===
43
48
  while [[ $# -gt 0 ]]; do
44
49
  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 ;;
50
+ --host) HOST="$2"; shift 2 ;;
51
+ --port) PORT="$2"; shift 2 ;;
52
+ --auth-token) AUTH_TOKEN_VAL="$2"; shift 2 ;;
53
+ --env-file) ENV_FILE="$2"; shift 2 ;;
54
+ --timeout) TIMEOUT="$2"; shift 2 ;;
49
55
  -h|--help)
50
56
  sed -n '2,20p' "$0" | sed 's/^# *//'
51
57
  exit 0
@@ -57,55 +63,56 @@ while [[ $# -gt 0 ]]; do
57
63
  esac
58
64
  done
59
65
 
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)
66
+ # === Load token from env file si pas fourni ===
67
+ if [[ -z "${AUTH_TOKEN_VAL:-}" && -f "$ENV_FILE" ]]; then
68
+ AUTH_TOKEN_VAL="$(grep '^BRIDGE_AUTH_TOKEN=' "$ENV_FILE" | cut -d= -f2- | tr -d '"' | tr -d "'" || true)"
63
69
  fi
64
70
 
65
71
  BASE_URL="http://${HOST}:${PORT}"
66
72
  PASS=0
67
73
  FAIL=0
68
74
 
69
- # ─── Helpers ──────────────────────────────────────────────────────
75
+ # === Helpers ===
70
76
  have_jq() { command -v jq >/dev/null 2>&1; }
71
77
 
72
78
  check() {
73
79
  local name="$1"
74
80
  local result="$2"
75
81
  if [[ "$result" == "ok" ]]; then
76
- echo -e " ${C_GREEN}✓${C_RESET} $name"
82
+ echo -e " ${C_GREEN}OK${C_RESET} $name"
77
83
  PASS=$((PASS + 1))
78
84
  else
79
- echo -e " ${C_RED}✗${C_RESET} $name"
85
+ echo -e " ${C_RED}FAIL${C_RESET} $name"
80
86
  FAIL=$((FAIL + 1))
81
87
  fi
82
88
  }
83
89
 
84
90
  section() {
85
91
  echo
86
- echo -e "${C_BOLD}${C_BLUE} $1${C_RESET}"
92
+ echo -e "${C_BOLD}${C_BLUE}>> $1${C_RESET}"
87
93
  }
88
94
 
89
- # ─── Banner ───────────────────────────────────────────────────────
90
- echo -e "${C_BOLD}═══════════════════════════════════════════════════════${C_RESET}"
91
- echo -e "${C_BOLD} Overmind Bridge Smoke Test${C_RESET}"
95
+ # === Banner ===
96
+ echo -e "${C_BOLD}=======================================================${C_RESET}"
97
+ echo -e "${C_BOLD} Overmind Bridge - Smoke Test${C_RESET}"
92
98
  echo -e "${C_BOLD} Target: ${BASE_URL}${C_RESET}"
93
- echo -e "${C_BOLD}═══════════════════════════════════════════════════════${C_RESET}"
99
+ echo -e "${C_BOLD}=======================================================${C_RESET}"
94
100
 
95
- # ─── Test 1 : service joignable (TCP) ─────────────────────────────
101
+ # === Test 1 : service joignable ===
96
102
  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
103
+ HTTP_CODE=$(curl -sS --max-time "$TIMEOUT" -o /dev/null -w "%{http_code}" \
104
+ "$BASE_URL/health" 2>/dev/null || echo "000")
105
+ if echo "$HTTP_CODE" | grep -qE '^(200|401|403)$'; then
106
+ check "HTTP /health repond [200/401/403]" ok
100
107
  else
101
- check "HTTP /health ne répond pas bridge down ?" fail
108
+ check "HTTP /health ne repond pas - bridge down" fail
102
109
  echo
103
- echo -e "${C_RED}Bridge inaccessible. Abandon.${C_RESET}"
104
- echo -e " Vérifier : ${C_YELLOW}systemctl status overmind-bridge${C_RESET}"
110
+ echo -e "${C_RED}Bridge inaccessible. Abandon.${C_RESET}"
111
+ echo -e " Verifier : ${C_YELLOW}systemctl status overmind-bridge${C_RESET}"
105
112
  exit 1
106
113
  fi
107
114
 
108
- # ─── Test 2 : /health retourne un JSON valide ────────────────────
115
+ # === Test 2 : /health retourne un JSON valide ===
109
116
  section "2. /health endpoint"
110
117
  HEALTH_BODY=$(curl -sS --max-time "$TIMEOUT" "$BASE_URL/health" 2>/dev/null || true)
111
118
 
@@ -115,30 +122,30 @@ else
115
122
  HEALTH_STATUS=$(echo "$HEALTH_BODY" | grep -oE '"status"[[:space:]]*:[[:space:]]*"[^"]+"' | head -1 | cut -d'"' -f4)
116
123
  fi
117
124
 
118
- if [[ -n "$HEALTH_STATUS" && "$HEALTH_STATUS" != "missing" ]]; then
119
- check "/health expose un status ($HEALTH_STATUS)" ok
125
+ if [[ -n "${HEALTH_STATUS:-}" && "$HEALTH_STATUS" != "missing" ]]; then
126
+ check "/health expose un status : $HEALTH_STATUS" ok
120
127
  else
121
- check "/health n'expose pas de status endpoint cassé ?" fail
128
+ check "/health n expose pas de status - endpoint casse" fail
122
129
  fi
123
130
 
124
- # ─── Build AUTH_HEADER array for reuse ───────────────────────────
131
+ # === Build AUTH_HEADER array for reuse ===
125
132
  AUTH_HEADER=()
126
- if [[ -n "$TOKEN" ]]; then
127
- AUTH_HEADER=(-H "Authorization: Bearer $TOKEN")
133
+ if [[ -n "${AUTH_TOKEN_VAL:-}" ]]; then
134
+ AUTH_HEADER=(-H "Authorization: Bearer ${AUTH_TOKEN_VAL}")
128
135
  fi
129
136
 
130
- # ─── Test 3 : auth requise si token configuré ────────────────────
137
+ # === Test 3 : auth requise si token configure ===
131
138
  section "3. Authentication"
132
- if [[ -n "$TOKEN" ]]; then
139
+ if [[ -n "${AUTH_TOKEN_VAL:-}" ]]; then
133
140
  HTTP_NO_AUTH=$(curl -sS --max-time "$TIMEOUT" -o /dev/null -w "%{http_code}" \
134
141
  -X POST "$BASE_URL/rpc" \
135
142
  -H 'Content-Type: application/json' \
136
143
  -d '{"jsonrpc":"2.0","id":1,"method":"health.ping","params":{}}' 2>/dev/null || echo "000")
137
144
 
138
145
  if [[ "$HTTP_NO_AUTH" == "401" ]]; then
139
- check "Sans token 401 (auth actif)" ok
146
+ check "Sans token -> 401, auth actif" ok
140
147
  else
141
- check "Sans token $HTTP_NO_AUTH (attendu: 401)" fail
148
+ check "Sans token -> $HTTP_NO_AUTH [attendu: 401]" fail
142
149
  fi
143
150
 
144
151
  HTTP_WITH_AUTH=$(curl -sS --max-time "$TIMEOUT" -o /dev/null -w "%{http_code}" \
@@ -148,9 +155,9 @@ if [[ -n "$TOKEN" ]]; then
148
155
  -d '{"jsonrpc":"2.0","id":1,"method":"health.ping","params":{}}' 2>/dev/null || echo "000")
149
156
 
150
157
  if [[ "$HTTP_WITH_AUTH" == "200" ]]; then
151
- check "Avec token 200 (auth OK)" ok
158
+ check "Avec token -> 200, auth OK" ok
152
159
  else
153
- check "Avec token $HTTP_WITH_AUTH (attendu: 200)" fail
160
+ check "Avec token -> $HTTP_WITH_AUTH [attendu: 200]" fail
154
161
  fi
155
162
  else
156
163
  HTTP_OPEN=$(curl -sS --max-time "$TIMEOUT" -o /dev/null -w "%{http_code}" \
@@ -159,13 +166,13 @@ else
159
166
  -d '{"jsonrpc":"2.0","id":1,"method":"health.ping","params":{}}' 2>/dev/null || echo "000")
160
167
 
161
168
  if [[ "$HTTP_OPEN" == "200" ]]; then
162
- check "/rpc ouvert (auth non configurée)" ok
169
+ check "/rpc ouvert, auth non configuree" ok
163
170
  else
164
- check "/rpc ouvert $HTTP_OPEN (attendu: 200)" fail
171
+ check "/rpc ouvert -> $HTTP_OPEN [attendu: 200]" fail
165
172
  fi
166
173
  fi
167
174
 
168
- # ─── Test 4 : health.ping RPC ────────────────────────────────────
175
+ # === Test 4 : health.ping RPC ===
169
176
  section "4. JSON-RPC methods"
170
177
  PING_BODY=$(curl -sS --max-time "$TIMEOUT" \
171
178
  "${AUTH_HEADER[@]}" \
@@ -174,12 +181,12 @@ PING_BODY=$(curl -sS --max-time "$TIMEOUT" \
174
181
  -d '{"jsonrpc":"2.0","id":1,"method":"health.ping","params":{}}' 2>/dev/null || true)
175
182
 
176
183
  if echo "$PING_BODY" | grep -q '"pong"[[:space:]]*:[[:space:]]*true'; then
177
- check "RPC health.ping répond" ok
184
+ check "RPC health.ping repond" ok
178
185
  else
179
- check "RPC health.ping échoue body: ${PING_BODY:0:100}" fail
186
+ check "RPC health.ping echoue - body: ${PING_BODY:0:100}" fail
180
187
  fi
181
188
 
182
- # ─── Test 5 : JSON-RPC validation (params invalides) ─────────────
189
+ # === Test 5 : JSON-RPC validation (params invalides) ===
183
190
  section "5. Error handling"
184
191
  INVALID_BODY=$(curl -sS --max-time "$TIMEOUT" \
185
192
  "${AUTH_HEADER[@]}" \
@@ -188,44 +195,44 @@ INVALID_BODY=$(curl -sS --max-time "$TIMEOUT" \
188
195
  -d '{"jsonrpc":"2.0","id":1,"method":"agent.run","params":{}}' 2>/dev/null || true)
189
196
 
190
197
  if echo "$INVALID_BODY" | grep -q '"code"[[:space:]]*:[[:space:]]*-32602'; then
191
- check "Params invalides -32602 (Invalid params)" ok
198
+ check "Params invalides -> -32602 Invalid params" ok
192
199
  else
193
- check "Params invalides code attendu: -32602, reçu: $INVALID_BODY" fail
200
+ check "Params invalides - code attendu: -32602, recu: $INVALID_BODY" fail
194
201
  fi
195
202
 
196
- # ─── Test 6 : batch request ──────────────────────────────────────
203
+ # === Test 6 : batch request ===
197
204
  section "6. Batch RPC"
198
205
  BATCH_BODY=$(curl -sS --max-time "$TIMEOUT" \
199
206
  "${AUTH_HEADER[@]}" \
200
207
  -H 'Content-Type: application/json' \
201
208
  -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":{}}
209
+ -d '[\
210
+ {"jsonrpc":"2.0","id":1,"method":"health.ping","params":{}},\
211
+ {"jsonrpc":"2.0","id":2,"method":"health.ping","params":{}}\
205
212
  ]' 2>/dev/null || true)
206
213
 
207
214
  BATCH_COUNT=$(echo "$BATCH_BODY" | grep -oE '"id"[[:space:]]*:[[:space:]]*[12]' | wc -l | tr -d ' ')
208
215
  if [[ "$BATCH_COUNT" == "2" ]]; then
209
- check "Batch RPC retourne 2 réponses" ok
216
+ check "Batch RPC retourne 2 reponses" ok
210
217
  else
211
- check "Batch RPC reçu: $BATCH_COUNT réponses (attendu: 2)" fail
218
+ check "Batch RPC - recu: $BATCH_COUNT reponses [attendu: 2]" fail
212
219
  fi
213
220
 
214
- # ─── Résumé ───────────────────────────────────────────────────────
221
+ # === Resume ===
215
222
  echo
216
- echo -e "${C_BOLD}═══════════════════════════════════════════════════════${C_RESET}"
223
+ echo -e "${C_BOLD}=======================================================${C_RESET}"
217
224
  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}"
225
+ echo -e "${C_GREEN}${C_BOLD} OK $PASS tests OK, 0 echec${C_RESET}"
226
+ echo -e "${C_GREEN} Bridge operationnel sur ${BASE_URL}${C_RESET}"
227
+ echo -e "${C_BOLD}=======================================================${C_RESET}"
221
228
  exit 0
222
229
  else
223
- echo -e "${C_RED}${C_BOLD} $FAIL échec(s), $PASS tests OK${C_RESET}"
230
+ echo -e "${C_RED}${C_BOLD} FAIL $FAIL echec(s), $PASS tests OK${C_RESET}"
224
231
  echo
225
232
  echo -e " ${C_YELLOW}Diagnostic :${C_RESET}"
226
233
  echo -e " 1. ${C_YELLOW}systemctl status overmind-bridge${C_RESET}"
227
234
  echo -e " 2. ${C_YELLOW}journalctl -u overmind-bridge -n 100${C_RESET}"
228
235
  echo -e " 3. ${C_YELLOW}curl -v $BASE_URL/health${C_RESET}"
229
- echo -e "${C_BOLD}═══════════════════════════════════════════════════════${C_RESET}"
236
+ echo -e "${C_BOLD}=======================================================${C_RESET}"
230
237
  exit 1
231
238
  fi
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env python3
2
+ """Post l'embed du wrapper Overmind Bridge sur Discord."""
3
+ import os
4
+ import json
5
+ import urllib.request
6
+ import urllib.error
7
+
8
+ # Récupère le token depuis .env
9
+ env_path = r"C:\Users\Deamon\Desktop\Backup\Serveur MCP\discord_llm\.env"
10
+ TOKEN_PREFIX = "DISCORD_BOT_TOKEN"
11
+ token = None
12
+ with open(env_path, 'r', encoding='utf-8') as f:
13
+ for line in f:
14
+ if line.startswith(TOKEN_PREFIX + "="):
15
+ token = line.split('=', 1)[1].strip()
16
+ break
17
+ if not token:
18
+ raise SystemExit("Token non trouve dans .env")
19
+
20
+ CHANNEL_ID = "1491078210279182500"
21
+
22
+ embed = {
23
+ "title": "🌉 Overmind Bridge — Couche RPC unifiée",
24
+ "description": (
25
+ "Couche **JSON-RPC 2.0** stable au-dessus du serveur MCP Overmind.\n"
26
+ "Transforme n'importe quel client HTTP (curl, Discord, SMS, autre agent) "
27
+ "en orchestrateur multi-agents avec **15 methodes RPC**, session store, "
28
+ "circuit breaker, retry, et webhook adapter prets a l'emploi.\n\n"
29
+ "Paquetage prod : `import { ... } from 'overmind-mcp/bridge'`"
30
+ ),
31
+ "color": 0x5865F2,
32
+ "fields": [
33
+ {
34
+ "name": "⚡ Demarrage rapide",
35
+ "value": (
36
+ "```\n"
37
+ "POST /rpc\n"
38
+ "Content-Type: application/json\n\n"
39
+ "{\n"
40
+ ' "jsonrpc": "2.0",\n'
41
+ ' "id": 1,\n'
42
+ ' "method": "agent.run",\n'
43
+ ' "params": {\n'
44
+ ' "agentName": "sniperbot_analyst",\n'
45
+ ' "runner": "hermes",\n'
46
+ ' "prompt": "...",\n'
47
+ ' "externalKey": "+1418..."\n'
48
+ " }\n"
49
+ "}\n"
50
+ "```"
51
+ ),
52
+ "inline": False
53
+ },
54
+ {
55
+ "name": "📚 15 methodes RPC (5 categories)",
56
+ "value": (
57
+ "▸ **Health** : `health.ping`\n"
58
+ "▸ **Agent** : `agent.run` · `agent.a2a` · `agent.status` · `agent.list` · `agent.kill`\n"
59
+ "▸ **MessageLog** : `message.history` · `message.get` · `message.replay` · `message.stats`\n"
60
+ "▸ **Session** : `session.get` · `session.list` · `session.delete` · `session.stats`\n"
61
+ "▸ **Webhook** : `webhook.sms` (voipms · twilio · discord · generic)"
62
+ ),
63
+ "inline": False
64
+ },
65
+ {
66
+ "name": "🛡️ Features production",
67
+ "value": (
68
+ "✅ Circuit breaker (5 echecs → open 30s)\n"
69
+ "✅ Triple timeout (connect + body + per-chunk)\n"
70
+ "✅ Retry auto sur ETIMEDOUT/EBODYREAD/ECONNRESET\n"
71
+ "✅ SessionStore multi-tenant (phone, userId, channelId)\n"
72
+ "✅ DirectiveParser (auto-update SESSION_ID: reponse)\n"
73
+ "✅ AgentRegistry + mutex (1 run par agent)\n"
74
+ "✅ MessageLog Postgres (optionnel)\n"
75
+ "✅ Path traversal bloque\n"
76
+ "✅ JSON-RPC 2.0 strict (batch + error codes)"
77
+ ),
78
+ "inline": False
79
+ },
80
+ {
81
+ "name": "🏗️ Architecture (3 couches)",
82
+ "value": (
83
+ "```\n"
84
+ "Client → BridgeProxy\n"
85
+ " ↓\n"
86
+ " OverBridgeService (session + types)\n"
87
+ " ↓\n"
88
+ " OverBridgeServer (HTTP + /rpc + /webhook)\n"
89
+ " ↓\n"
90
+ " Overmind MCP :3099 (run_agent)\n"
91
+ "```"
92
+ ),
93
+ "inline": False
94
+ },
95
+ {
96
+ "name": "📊 Codes d'erreur (JSON-RPC 2.0)",
97
+ "value": (
98
+ "▸ `-32700` PARSE_ERROR (JSON malforme)\n"
99
+ "▸ `-32600` INVALID_REQUEST\n"
100
+ "▸ `-32601` METHOD_NOT_FOUND\n"
101
+ "▸ `-32602` INVALID_PARAMS\n"
102
+ "▸ `-32603` INTERNAL_ERROR\n"
103
+ "▸ `-32003` FEATURE_DISABLED (ex: MessageLog off)\n"
104
+ "▸ `-32004` NOT_FOUND (ex: UUID inconnu)"
105
+ ),
106
+ "inline": False
107
+ },
108
+ {
109
+ "name": "🔌 Endpoints HTTP",
110
+ "value": (
111
+ "▸ `POST /rpc` — JSON-RPC 2.0 principal\n"
112
+ "▸ `GET /health` — Liveness probe (K8s/Docker)\n"
113
+ "▸ `POST /webhook/:provider` — voipms/twilio/discord/generic\n"
114
+ "▸ `GET /f/:filename` — Static files (path traversal bloque)"
115
+ ),
116
+ "inline": False
117
+ },
118
+ {
119
+ "name": "🚀 Clients deja migres",
120
+ "value": (
121
+ "✅ **discord_llm v5.0** — 100% aligne sur le wrapper, expose `/rpc` + 11 methodes testees sur sniperbot_analyst\n"
122
+ "🔄 **bt-sms** — migration prevue (copy-paste du pattern discord_llm)"
123
+ ),
124
+ "inline": False
125
+ },
126
+ {
127
+ "name": "🧪 Test live (sniperbot_analyst)",
128
+ "value": (
129
+ "```bash\n"
130
+ "# 1. Run agent avec session multi-tenant\n"
131
+ "curl -X POST http://127.0.0.1:3001/rpc -d '...agent.run...'\n"
132
+ "# 2. A2A loopback\n"
133
+ "curl -X POST http://127.0.0.1:3001/rpc -d '...agent.a2a...'\n"
134
+ "# 3. Webhook VoIP.ms → auto-dispatch\n"
135
+ "curl -X POST http://127.0.0.1:3001/rpc -d '...webhook.sms...'\n"
136
+ "```\n"
137
+ "Tous testes ✅ sur discord_llm v5.0"
138
+ ),
139
+ "inline": False
140
+ }
141
+ ],
142
+ "footer": {
143
+ "text": "Overmind Bridge v5.0 · discord_llm v5.0 · sniperbot_analyst sur runner=hermes (glm-5.1)"
144
+ }
145
+ }
146
+
147
+ payload = {"embeds": [embed]}
148
+ data = json.dumps(payload).encode('utf-8')
149
+
150
+ req = urllib.request.Request(
151
+ f"https://discord.com/api/v10/channels/{CHANNEL_ID}/messages",
152
+ data=data,
153
+ method='POST',
154
+ headers={
155
+ 'Authorization': f'Bot {token}',
156
+ 'Content-Type': 'application/json',
157
+ 'User-Agent': 'DiscordBot (overmind-bridge, 5.0.0)',
158
+ }
159
+ )
160
+
161
+ try:
162
+ with urllib.request.urlopen(req, timeout=30) as resp:
163
+ body = resp.read().decode('utf-8')
164
+ result = json.loads(body)
165
+ print(f"OK: embed poste")
166
+ print(f" message_id = {result.get('id')}")
167
+ print(f" channel_id = {result.get('channel_id')}")
168
+ guild = result.get('guild_id')
169
+ if guild:
170
+ print(f" url = https://discord.com/channels/{guild}/{result.get('channel_id')}/{result.get('id')}")
171
+ else:
172
+ print(f" url = https://discord.com/channels/@me/{result.get('channel_id')}/{result.get('id')}")
173
+ except urllib.error.HTTPError as e:
174
+ print(f"ERREUR HTTP {e.code}: {e.reason}")
175
+ try:
176
+ print(f" body: {e.read().decode('utf-8')[:1000]}")
177
+ except: pass
178
+ except Exception as e:
179
+ print(f"ERREUR: {e}")
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Test standalone des nouvelles fonctionnalités RPC du wrapper.
4
+ *
5
+ * Lance un OverBridgeServer sur :3100 qui pointe vers le MCP live (:3099)
6
+ * et exerce chaque méthode RPC documentée.
7
+ *
8
+ * Usage: node scripts/test-wrapper-rpc.mjs
9
+ */
10
+
11
+ import { OverBridgeService, OverBridgeServer } from '../dist/bridge/index.js';
12
+
13
+ const WRAPPER_PORT = 3100;
14
+ const UPSTREAM_MCP = process.env.MCP_URL || 'http://localhost:3099/mcp';
15
+ const BASE = `http://127.0.0.1:${WRAPPER_PORT}`;
16
+ let pass = 0;
17
+ let fail = 0;
18
+ const results = [];
19
+
20
+ const service = new OverBridgeService(
21
+ { mcpUrl: UPSTREAM_MCP, defaultTimeoutMs: 60_000, agentTimeoutMs: 180_000 },
22
+ {
23
+ debug: (m) => console.log(` [debug] ${m}`),
24
+ info: (m) => console.log(` [info] ${m}`),
25
+ warn: (m) => console.log(` [warn] ${m}`),
26
+ error: (m) => console.log(` [error] ${m}`),
27
+ },
28
+ );
29
+
30
+ const server = new OverBridgeServer(
31
+ service,
32
+ {
33
+ port: WRAPPER_PORT,
34
+ host: '127.0.0.1',
35
+ postgres: {
36
+ host: 'localhost', port: 5432, user: 'postgres', password: '', database: 'overmind_memory',
37
+ },
38
+ enableMessageLog: false, // on désactive MessageLog (pas de Postgres ici)
39
+ enableSessionStore: true, // multi-tenant (in-memory)
40
+ enableDirectives: true,
41
+ enableWebhooks: true,
42
+ healthCheckIntervalMs: 0, // pas de heartbeat
43
+ jsonBodyLimit: '5mb',
44
+ },
45
+ {
46
+ debug: () => {},
47
+ info: (m) => console.log(` [server] ${m}`),
48
+ warn: (m) => console.log(` [server.warn] ${m}`),
49
+ error: (m) => console.log(` [server.error] ${m}`),
50
+ },
51
+ );
52
+
53
+ async function rpc(method, params = {}, id = 1) {
54
+ const res = await fetch(`${BASE}/rpc`, {
55
+ method: 'POST',
56
+ headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
57
+ body: JSON.stringify({ jsonrpc: '2.0', id, method, params }),
58
+ });
59
+ return await res.json();
60
+ }
61
+
62
+ function check(name, ok, detail = '') {
63
+ if (ok) { pass++; results.push(` PASS ${name}${detail ? ' - ' + detail : ''}`); }
64
+ else { fail++; results.push(` FAIL ${name}${detail ? ' - ' + detail : ''}`); }
65
+ }
66
+
67
+ async function main() {
68
+ console.log('═══════════════════════════════════════════════════════');
69
+ console.log(' Test Standalone - Wrapper RPC');
70
+ console.log(` Wrapper: ${BASE}/rpc`);
71
+ console.log(` Upstream MCP: ${UPSTREAM_MCP}`);
72
+ console.log('═══════════════════════════════════════════════════════');
73
+
74
+ await server.start();
75
+ console.log('');
76
+
77
+ // ─── 1. health.ping ──────────────────────────────────────────
78
+ {
79
+ const r = await rpc('health.ping');
80
+ check('health.ping', r.result?.pong === true, `pong=${r.result?.pong}`);
81
+ }
82
+
83
+ // ─── 2. agent.list (filtre runner=hermes) ───────────────────
84
+ {
85
+ const r = await rpc('agent.list', { runner: 'hermes' });
86
+ const agents = JSON.parse(r.result?.agents?.[0] || '[]');
87
+ // Le retour est wrappé dans content[0].text (format MCP).
88
+ // On accepte si pas d'erreur.
89
+ check('agent.list (runner=hermes)', !r.error, `agents.length=${r.result?.stats?.total}`);
90
+ }
91
+
92
+ // ─── 3. agent.status (state local) ──────────────────────────
93
+ {
94
+ const r = await rpc('agent.status', { agentName: 'sniperbot_analyst' });
95
+ const hasLocal = r.result?.local !== undefined;
96
+ check('agent.status', hasLocal, hasLocal ? `status=${r.result.local.status}` : JSON.stringify(r).slice(0,200));
97
+ }
98
+
99
+ // ─── 4. agent.run AVEC externalKey (SessionStore) ──────────
100
+ {
101
+ const r = await rpc('agent.run', {
102
+ agentName: 'sniperbot_analyst',
103
+ runner: 'hermes',
104
+ prompt: 'Test SessionStore — reponds juste OK',
105
+ externalKey: 'sniper-test-phone-001',
106
+ });
107
+ const hasSession = r.result?.sessionId !== undefined;
108
+ check('agent.run + externalKey', hasSession && !r.error?.message?.includes('Invalid'),
109
+ `sessionId=${r.result?.sessionId}, content[0]=${r.result?.content?.[0]?.text?.slice(0,80)}`);
110
+ }
111
+
112
+ // ─── 5. session.get ───────────────────────────────────────
113
+ {
114
+ const r = await rpc('session.get', {
115
+ externalKey: 'sniper-test-phone-001',
116
+ agentName: 'sniperbot_analyst',
117
+ });
118
+ const found = r.result?.session !== null && r.result?.session !== undefined;
119
+ check('session.get', found, `sessionId=${r.result?.session?.sessionId?.slice(0,20)}...`);
120
+ }
121
+
122
+ // ─── 6. session.list ──────────────────────────────────────
123
+ {
124
+ const r = await rpc('session.list', {});
125
+ const list = r.result?.sessions || [];
126
+ check('session.list', Array.isArray(list) && list.length > 0, `count=${list.length}`);
127
+ }
128
+
129
+ // ─── 7. session.stats ─────────────────────────────────────
130
+ {
131
+ const r = await rpc('session.stats', {});
132
+ const total = r.result?.total;
133
+ check('session.stats', typeof total === 'number', `total=${total}, byAgent=${JSON.stringify(r.result?.byAgent)}`);
134
+ }
135
+
136
+ // ─── 8. webhook.sms avec autoDispatch (voipms → sniperbot) ─
137
+ {
138
+ const r = await rpc('webhook.sms', {
139
+ provider: 'voipms',
140
+ payload: {
141
+ from: '+14181234567',
142
+ to: '+14187654321',
143
+ message: 'BTC test via webhook',
144
+ id: 'msg-test-001',
145
+ date: new Date().toISOString(),
146
+ },
147
+ externalKey: 'sniper-test-phone-001',
148
+ autoDispatch: {
149
+ agentName: 'sniperbot_analyst',
150
+ runner: 'hermes',
151
+ },
152
+ });
153
+ const dispatched = r.result?.messageId !== undefined || r.result?.content !== undefined;
154
+ check('webhook.sms + autoDispatch', dispatched || !r.error,
155
+ dispatched ? `messageId=${r.result?.messageId?.slice(0,8)}...` : JSON.stringify(r).slice(0,200));
156
+ }
157
+
158
+ // ─── 9. batch RPC (array de 3 requêtes) ───────────────────
159
+ {
160
+ const batch = await fetch(`${BASE}/rpc`, {
161
+ method: 'POST',
162
+ headers: { 'Content-Type': 'application/json' },
163
+ body: JSON.stringify([
164
+ { jsonrpc: '2.0', id: 1, method: 'health.ping', params: {} },
165
+ { jsonrpc: '2.0', id: 2, method: 'health.ping', params: {} },
166
+ { jsonrpc: '2.0', id: 3, method: 'health.ping', params: {} },
167
+ ]),
168
+ });
169
+ const arr = await batch.json();
170
+ const allOk = Array.isArray(arr) && arr.length === 3 && arr.every((r) => r.result?.pong === true);
171
+ check('batch RPC (3 requêtes)', allOk, `count=${arr.length}`);
172
+ }
173
+
174
+ // ─── 10. method unknown → -32601 ──────────────────────────
175
+ {
176
+ const r = await rpc('does.not.exist', {});
177
+ check('method unknown → -32601', r.error?.code === -32601, `code=${r.error?.code}`);
178
+ }
179
+
180
+ // ─── 11. params invalides → -32602 ────────────────────────
181
+ {
182
+ const r = await rpc('agent.run', {}); // manque agentName, runner, prompt
183
+ check('params invalides → -32602', r.error?.code === -32602, `code=${r.error?.code}, issues=${r.error?.data?.length || 0}`);
184
+ }
185
+
186
+ // ─── 12. /health endpoint HTTP ────────────────────────────
187
+ {
188
+ const r = await fetch(`${BASE}/health`).then((r) => r.json());
189
+ const hasFields = r.status && r.mcp && r.features !== undefined;
190
+ check('GET /health', hasFields, `status=${r.status}, features.sessionStore=${r.features?.sessionStore}`);
191
+ }
192
+
193
+ // ─── 13. /webhook/:provider endpoint HTTP ───────────────────
194
+ {
195
+ const r = await fetch(`${BASE}/webhook/voipms`, {
196
+ method: 'POST',
197
+ headers: { 'Content-Type': 'application/json' },
198
+ body: JSON.stringify({ from: '+14181112222', message: 'hi', id: 'm1' }),
199
+ });
200
+ const j = await r.json();
201
+ check('POST /webhook/voipms', j.received === true, `provider=${j.provider}, externalKey=${j.externalKey}`);
202
+ }
203
+
204
+ // ─── 14. path traversal bloqué sur /f/ ────────────────────
205
+ {
206
+ const r = await fetch(`${BASE}/f/..%2Fetc%2Fpasswd`);
207
+ const status = r.status;
208
+ check('path traversal bloqué', status === 403 || status === 400, `status=${status}`);
209
+ }
210
+
211
+ // ─── 15. session.delete ───────────────────────────────────
212
+ {
213
+ const r = await rpc('session.delete', {
214
+ externalKey: 'sniper-test-phone-001',
215
+ agentName: 'sniperbot_analyst',
216
+ });
217
+ check('session.delete', r.result?.deleted === true, `deleted=${r.result?.deleted}`);
218
+ }
219
+
220
+ // ─── Cleanup ─────────────────────────────────────────────
221
+ await server.stop();
222
+ console.log('');
223
+ console.log('═══════════════════════════════════════════════════════');
224
+ for (const line of results) console.log(line);
225
+ console.log('═══════════════════════════════════════════════════════');
226
+ console.log(` ${pass} pass, ${fail} fail`);
227
+ console.log('═══════════════════════════════════════════════════════');
228
+ process.exit(fail > 0 ? 1 : 0);
229
+ }
230
+
231
+ main().catch((err) => {
232
+ console.error('FATAL:', err);
233
+ process.exit(1);
234
+ });