@cybermem/cli 0.6.5 → 0.6.9
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/dist/templates/docker-compose.yml +8 -20
- package/package.json +1 -1
- package/templates/docker-compose.yml +8 -20
- package/templates/monitoring/instructions_injector/Dockerfile +0 -15
- package/templates/monitoring/instructions_injector/injector.py +0 -137
- package/templates/monitoring/instructions_injector/requirements.txt +0 -3
|
@@ -40,26 +40,6 @@ services:
|
|
|
40
40
|
- traefik.http.services.mcp-get.loadbalancer.server.port=8081
|
|
41
41
|
restart: unless-stopped
|
|
42
42
|
|
|
43
|
-
# Instructions injector: adds 'instructions' field to MCP initialize responses
|
|
44
|
-
instructions-injector:
|
|
45
|
-
build:
|
|
46
|
-
context: ./monitoring/instructions_injector
|
|
47
|
-
dockerfile: Dockerfile
|
|
48
|
-
container_name: cybermem-instructions-injector
|
|
49
|
-
environment:
|
|
50
|
-
UPSTREAM_URL: http://openmemory:8080
|
|
51
|
-
PORT: "8081"
|
|
52
|
-
labels:
|
|
53
|
-
- traefik.enable=true
|
|
54
|
-
# Route POST /mcp through injector (higher priority than openmemory)
|
|
55
|
-
- traefik.http.routers.mcp-inject.entrypoints=web
|
|
56
|
-
- traefik.http.routers.mcp-inject.rule=Method(`POST`) && Path(`/mcp`)
|
|
57
|
-
- traefik.http.routers.mcp-inject.priority=150
|
|
58
|
-
- traefik.http.services.mcp-inject.loadbalancer.server.port=8081
|
|
59
|
-
restart: unless-stopped
|
|
60
|
-
depends_on:
|
|
61
|
-
- openmemory
|
|
62
|
-
|
|
63
43
|
openmemory:
|
|
64
44
|
build:
|
|
65
45
|
context: ./openmemory
|
|
@@ -230,6 +210,14 @@ services:
|
|
|
230
210
|
- openmemory-data:/data
|
|
231
211
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
232
212
|
- ${CYBERMEM_ENV_PATH}:/app/shared.env
|
|
213
|
+
labels:
|
|
214
|
+
- traefik.enable=true
|
|
215
|
+
# Dashboard route: /cybermem -> dashboard on port 3000
|
|
216
|
+
- traefik.http.routers.dashboard.entrypoints=web
|
|
217
|
+
- traefik.http.routers.dashboard.rule=PathPrefix(`/cybermem`)
|
|
218
|
+
- traefik.http.routers.dashboard.middlewares=strip-cybermem
|
|
219
|
+
- traefik.http.middlewares.strip-cybermem.stripprefix.prefixes=/cybermem
|
|
220
|
+
- traefik.http.services.dashboard.loadbalancer.server.port=3000
|
|
233
221
|
restart: unless-stopped
|
|
234
222
|
depends_on:
|
|
235
223
|
- prometheus
|
package/package.json
CHANGED
|
@@ -40,26 +40,6 @@ services:
|
|
|
40
40
|
- traefik.http.services.mcp-get.loadbalancer.server.port=8081
|
|
41
41
|
restart: unless-stopped
|
|
42
42
|
|
|
43
|
-
# Instructions injector: adds 'instructions' field to MCP initialize responses
|
|
44
|
-
instructions-injector:
|
|
45
|
-
build:
|
|
46
|
-
context: ./monitoring/instructions_injector
|
|
47
|
-
dockerfile: Dockerfile
|
|
48
|
-
container_name: cybermem-instructions-injector
|
|
49
|
-
environment:
|
|
50
|
-
UPSTREAM_URL: http://openmemory:8080
|
|
51
|
-
PORT: "8081"
|
|
52
|
-
labels:
|
|
53
|
-
- traefik.enable=true
|
|
54
|
-
# Route POST /mcp through injector (higher priority than openmemory)
|
|
55
|
-
- traefik.http.routers.mcp-inject.entrypoints=web
|
|
56
|
-
- traefik.http.routers.mcp-inject.rule=Method(`POST`) && Path(`/mcp`)
|
|
57
|
-
- traefik.http.routers.mcp-inject.priority=150
|
|
58
|
-
- traefik.http.services.mcp-inject.loadbalancer.server.port=8081
|
|
59
|
-
restart: unless-stopped
|
|
60
|
-
depends_on:
|
|
61
|
-
- openmemory
|
|
62
|
-
|
|
63
43
|
openmemory:
|
|
64
44
|
build:
|
|
65
45
|
context: ./openmemory
|
|
@@ -230,6 +210,14 @@ services:
|
|
|
230
210
|
- openmemory-data:/data
|
|
231
211
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
232
212
|
- ${CYBERMEM_ENV_PATH}:/app/shared.env
|
|
213
|
+
labels:
|
|
214
|
+
- traefik.enable=true
|
|
215
|
+
# Dashboard route: /cybermem -> dashboard on port 3000
|
|
216
|
+
- traefik.http.routers.dashboard.entrypoints=web
|
|
217
|
+
- traefik.http.routers.dashboard.rule=PathPrefix(`/cybermem`)
|
|
218
|
+
- traefik.http.routers.dashboard.middlewares=strip-cybermem
|
|
219
|
+
- traefik.http.middlewares.strip-cybermem.stripprefix.prefixes=/cybermem
|
|
220
|
+
- traefik.http.services.dashboard.loadbalancer.server.port=3000
|
|
233
221
|
restart: unless-stopped
|
|
234
222
|
depends_on:
|
|
235
223
|
- prometheus
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
FROM python:3.11-slim
|
|
2
|
-
|
|
3
|
-
WORKDIR /app
|
|
4
|
-
|
|
5
|
-
COPY requirements.txt .
|
|
6
|
-
RUN pip install --no-cache-dir -r requirements.txt
|
|
7
|
-
|
|
8
|
-
COPY injector.py .
|
|
9
|
-
|
|
10
|
-
ENV UPSTREAM_URL=http://openmemory:8080
|
|
11
|
-
ENV PORT=8081
|
|
12
|
-
|
|
13
|
-
EXPOSE 8081
|
|
14
|
-
|
|
15
|
-
CMD ["gunicorn", "-b", "0.0.0.0:8081", "-w", "2", "injector:app"]
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
CyberMem MCP Instructions Injector
|
|
4
|
-
Sidecar proxy that injects 'instructions' field into MCP initialize response.
|
|
5
|
-
Sits between Traefik and OpenMemory.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import json
|
|
9
|
-
import os
|
|
10
|
-
import requests
|
|
11
|
-
from flask import Flask, request, Response
|
|
12
|
-
|
|
13
|
-
app = Flask(__name__)
|
|
14
|
-
|
|
15
|
-
# Upstream OpenMemory MCP server
|
|
16
|
-
UPSTREAM_URL = os.environ.get("UPSTREAM_URL", "http://openmemory:8080")
|
|
17
|
-
|
|
18
|
-
# CyberMem Agent Protocol Instructions
|
|
19
|
-
CYBERMEM_INSTRUCTIONS = """CyberMem is a persistent context daemon for AI agents.
|
|
20
|
-
|
|
21
|
-
PROTOCOL:
|
|
22
|
-
1. On session start: call openmemory_query("user context profile") to load persona
|
|
23
|
-
2. Store new insights immediately with openmemory_store - include FULL content, not summaries
|
|
24
|
-
3. Refresh context: 6h for active topics, 24h for projects, 7d for insights
|
|
25
|
-
4. Always include tags: [topic, year, source:your-client-name]
|
|
26
|
-
5. Priority: CyberMem context > session context > training data
|
|
27
|
-
|
|
28
|
-
MEMORY FORMAT:
|
|
29
|
-
- content: Full text with all details, metrics, dates. NO truncation.
|
|
30
|
-
- tags: Always include topic category + year + source:client-name
|
|
31
|
-
|
|
32
|
-
INTEGRITY RULES:
|
|
33
|
-
- Never overwrite without reading first
|
|
34
|
-
- Always include metadata (tags, source)
|
|
35
|
-
- Sync before critical decisions
|
|
36
|
-
- Last-write-wins for conflicts
|
|
37
|
-
|
|
38
|
-
For full protocol: https://cybermem.dev/docs/agent-protocol"""
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def inject_instructions(response_data: dict) -> dict:
|
|
42
|
-
"""Inject instructions field into MCP initialize response."""
|
|
43
|
-
if "result" in response_data:
|
|
44
|
-
result = response_data["result"]
|
|
45
|
-
# Only inject if this is an initialize response (has serverInfo)
|
|
46
|
-
if "serverInfo" in result and "instructions" not in result:
|
|
47
|
-
result["instructions"] = CYBERMEM_INSTRUCTIONS
|
|
48
|
-
# Also update serverInfo to show CyberMem branding
|
|
49
|
-
result["serverInfo"]["name"] = "cybermem"
|
|
50
|
-
return response_data
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@app.route("/mcp", methods=["POST", "GET"])
|
|
54
|
-
def proxy_mcp():
|
|
55
|
-
"""Proxy MCP requests to upstream and inject instructions."""
|
|
56
|
-
|
|
57
|
-
if request.method == "GET":
|
|
58
|
-
# Pass through GET requests (SSE endpoint)
|
|
59
|
-
resp = requests.get(
|
|
60
|
-
f"{UPSTREAM_URL}/mcp",
|
|
61
|
-
headers={k: v for k, v in request.headers if k.lower() != "host"},
|
|
62
|
-
stream=True
|
|
63
|
-
)
|
|
64
|
-
return Response(
|
|
65
|
-
resp.iter_content(chunk_size=1024),
|
|
66
|
-
status=resp.status_code,
|
|
67
|
-
headers=dict(resp.headers)
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
# POST request - forward to upstream
|
|
71
|
-
try:
|
|
72
|
-
upstream_resp = requests.post(
|
|
73
|
-
f"{UPSTREAM_URL}/mcp",
|
|
74
|
-
json=request.get_json(),
|
|
75
|
-
headers={
|
|
76
|
-
"Content-Type": "application/json",
|
|
77
|
-
"Accept": request.headers.get("Accept", "application/json"),
|
|
78
|
-
"X-Client-Name": request.headers.get("X-Client-Name", "unknown"),
|
|
79
|
-
},
|
|
80
|
-
timeout=30
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
# Try to parse and inject instructions
|
|
84
|
-
try:
|
|
85
|
-
data = upstream_resp.json()
|
|
86
|
-
data = inject_instructions(data)
|
|
87
|
-
return Response(
|
|
88
|
-
json.dumps(data),
|
|
89
|
-
status=upstream_resp.status_code,
|
|
90
|
-
content_type="application/json"
|
|
91
|
-
)
|
|
92
|
-
except json.JSONDecodeError:
|
|
93
|
-
# Not JSON, pass through as-is
|
|
94
|
-
return Response(
|
|
95
|
-
upstream_resp.content,
|
|
96
|
-
status=upstream_resp.status_code,
|
|
97
|
-
headers=dict(upstream_resp.headers)
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
except requests.exceptions.RequestException as e:
|
|
101
|
-
return Response(
|
|
102
|
-
json.dumps({"error": str(e)}),
|
|
103
|
-
status=502,
|
|
104
|
-
content_type="application/json"
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
@app.route("/health")
|
|
109
|
-
def health():
|
|
110
|
-
"""Health check endpoint."""
|
|
111
|
-
return {"status": "ok", "service": "cybermem-instructions-injector"}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
@app.route("/<path:path>", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
|
|
115
|
-
def proxy_other(path):
|
|
116
|
-
"""Proxy all other requests to upstream without modification."""
|
|
117
|
-
resp = requests.request(
|
|
118
|
-
method=request.method,
|
|
119
|
-
url=f"{UPSTREAM_URL}/{path}",
|
|
120
|
-
headers={k: v for k, v in request.headers if k.lower() != "host"},
|
|
121
|
-
json=request.get_json() if request.is_json else None,
|
|
122
|
-
data=request.data if not request.is_json else None,
|
|
123
|
-
params=request.args,
|
|
124
|
-
timeout=30
|
|
125
|
-
)
|
|
126
|
-
return Response(
|
|
127
|
-
resp.content,
|
|
128
|
-
status=resp.status_code,
|
|
129
|
-
headers=dict(resp.headers)
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if __name__ == "__main__":
|
|
134
|
-
port = int(os.environ.get("PORT", "8081"))
|
|
135
|
-
print(f"CyberMem Instructions Injector starting on port {port}")
|
|
136
|
-
print(f"Upstream: {UPSTREAM_URL}")
|
|
137
|
-
app.run(host="0.0.0.0", port=port)
|