@cybermem/cli 0.9.12 → 0.13.3
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/commands/install.js +430 -0
- package/dist/commands/reset.js +18 -2
- package/dist/commands/uninstall.js +145 -0
- package/dist/commands/upgrade.js +91 -52
- package/dist/index.js +18 -5
- package/dist/templates/ansible/playbooks/deploy-cybermem.yml +201 -24
- package/dist/templates/ansible/playbooks/reset-db.yml +44 -0
- package/dist/templates/auth-sidecar/Dockerfile +2 -10
- package/dist/templates/auth-sidecar/package.json +1 -3
- package/dist/templates/auth-sidecar/server.js +149 -110
- package/dist/templates/charts/cybermem/.helmignore +13 -0
- package/dist/templates/charts/cybermem/templates/dashboard-deployment.yaml +31 -7
- package/dist/templates/charts/cybermem/templates/dashboard-service.yaml +4 -4
- package/dist/templates/charts/cybermem/templates/openmemory-deployment.yaml +14 -8
- package/dist/templates/charts/cybermem/templates/openmemory-service.yaml +3 -3
- package/dist/templates/charts/cybermem/templates/secret.yaml +9 -0
- package/dist/templates/charts/cybermem/templates/traefik-config.yaml +67 -0
- package/dist/templates/charts/cybermem/templates/traefik-deployment.yaml +53 -0
- package/dist/templates/charts/cybermem/templates/traefik-service.yaml +17 -0
- package/dist/templates/charts/cybermem/values-vps.yaml +8 -4
- package/dist/templates/charts/cybermem/values.yaml +17 -9
- package/dist/templates/docker-compose.yml +103 -78
- package/dist/templates/monitoring/log_exporter/exporter.py +22 -29
- package/dist/templates/monitoring/traefik/traefik.yml +1 -4
- package/package.json +9 -3
- package/templates/ansible/playbooks/deploy-cybermem.yml +201 -24
- package/templates/ansible/playbooks/reset-db.yml +44 -0
- package/templates/auth-sidecar/Dockerfile +2 -10
- package/templates/auth-sidecar/package.json +1 -3
- package/templates/auth-sidecar/server.js +149 -110
- package/templates/charts/cybermem/.helmignore +13 -0
- package/templates/charts/cybermem/templates/dashboard-deployment.yaml +31 -7
- package/templates/charts/cybermem/templates/dashboard-service.yaml +4 -4
- package/templates/charts/cybermem/templates/openmemory-deployment.yaml +14 -8
- package/templates/charts/cybermem/templates/openmemory-service.yaml +3 -3
- package/templates/charts/cybermem/templates/secret.yaml +9 -0
- package/templates/charts/cybermem/templates/traefik-config.yaml +67 -0
- package/templates/charts/cybermem/templates/traefik-deployment.yaml +53 -0
- package/templates/charts/cybermem/templates/traefik-service.yaml +17 -0
- package/templates/charts/cybermem/values-vps.yaml +8 -4
- package/templates/charts/cybermem/values.yaml +17 -9
- package/templates/docker-compose.yml +103 -78
- package/templates/monitoring/log_exporter/exporter.py +22 -29
- package/templates/monitoring/traefik/traefik.yml +1 -4
- package/dist/commands/__tests__/backup.test.js +0 -75
- package/dist/commands/__tests__/restore.test.js +0 -70
- package/dist/commands/deploy.js +0 -239
- package/dist/commands/init.js +0 -362
- package/dist/commands/login.js +0 -165
- package/dist/templates/envs/local.example +0 -27
- package/dist/templates/envs/rpi.example +0 -27
- package/dist/templates/envs/vps.example +0 -25
- package/dist/templates/monitoring/instructions_injector/Dockerfile +0 -15
- package/dist/templates/monitoring/instructions_injector/injector.py +0 -137
- package/dist/templates/monitoring/instructions_injector/requirements.txt +0 -3
- package/dist/templates/openmemory/Dockerfile +0 -19
package/dist/commands/login.js
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* CyberMem CLI Auth Module
|
|
4
|
-
*
|
|
5
|
-
* Token storage and browser-based OAuth login flow.
|
|
6
|
-
*/
|
|
7
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
-
if (k2 === undefined) k2 = k;
|
|
9
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
-
}
|
|
13
|
-
Object.defineProperty(o, k2, desc);
|
|
14
|
-
}) : (function(o, m, k, k2) {
|
|
15
|
-
if (k2 === undefined) k2 = k;
|
|
16
|
-
o[k2] = m[k];
|
|
17
|
-
}));
|
|
18
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
-
}) : function(o, v) {
|
|
21
|
-
o["default"] = v;
|
|
22
|
-
});
|
|
23
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
-
var ownKeys = function(o) {
|
|
25
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
-
var ar = [];
|
|
27
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
-
return ar;
|
|
29
|
-
};
|
|
30
|
-
return ownKeys(o);
|
|
31
|
-
};
|
|
32
|
-
return function (mod) {
|
|
33
|
-
if (mod && mod.__esModule) return mod;
|
|
34
|
-
var result = {};
|
|
35
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
-
__setModuleDefault(result, mod);
|
|
37
|
-
return result;
|
|
38
|
-
};
|
|
39
|
-
})();
|
|
40
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
41
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
|
-
};
|
|
43
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
-
exports.login = login;
|
|
45
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
46
|
-
const fs = __importStar(require("fs"));
|
|
47
|
-
const http = __importStar(require("http"));
|
|
48
|
-
const os = __importStar(require("os"));
|
|
49
|
-
const path = __importStar(require("path"));
|
|
50
|
-
const AUTH_DIR = path.join(os.homedir(), ".cybermem");
|
|
51
|
-
const TOKEN_FILE = path.join(AUTH_DIR, "token.json");
|
|
52
|
-
const AUTH_URL = process.env.CYBERMEM_AUTH_URL || "https://cybermem.dev";
|
|
53
|
-
/**
|
|
54
|
-
* Ensure the .cybermem directory exists
|
|
55
|
-
*/
|
|
56
|
-
function ensureAuthDir() {
|
|
57
|
-
if (!fs.existsSync(AUTH_DIR)) {
|
|
58
|
-
fs.mkdirSync(AUTH_DIR, { recursive: true, mode: 0o700 });
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Save token to disk
|
|
63
|
-
*/
|
|
64
|
-
function saveToken(token, expiresIn, email, name) {
|
|
65
|
-
ensureAuthDir();
|
|
66
|
-
const expiresAt = new Date(Date.now() + expiresIn * 1000);
|
|
67
|
-
const data = {
|
|
68
|
-
access_token: token,
|
|
69
|
-
expires_at: expiresAt.toISOString(),
|
|
70
|
-
email,
|
|
71
|
-
name,
|
|
72
|
-
};
|
|
73
|
-
fs.writeFileSync(TOKEN_FILE, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Start OAuth login flow
|
|
77
|
-
* Opens browser and waits for callback with token
|
|
78
|
-
*/
|
|
79
|
-
async function login() {
|
|
80
|
-
return new Promise((resolve, reject) => {
|
|
81
|
-
// Find available port
|
|
82
|
-
const server = http.createServer();
|
|
83
|
-
server.listen(0, "127.0.0.1", () => {
|
|
84
|
-
const address = server.address();
|
|
85
|
-
if (!address || typeof address === "string") {
|
|
86
|
-
reject(new Error("Failed to start callback server"));
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const port = address.port;
|
|
90
|
-
const callbackUrl = `http://localhost:${port}/callback`;
|
|
91
|
-
// Redirect to landing auth endpoint which starts GitHub flow
|
|
92
|
-
// We pass our local callbackUrl as 'redirect' param to the intermediate CLI callback handler
|
|
93
|
-
const authUrl = `${AUTH_URL}/api/auth/signin?callbackUrl=${encodeURIComponent(`${AUTH_URL}/api/auth/cli/callback?redirect=${encodeURIComponent(callbackUrl)}`)}`;
|
|
94
|
-
console.log(chalk_1.default.blue("🔐 Opening browser for GitHub login..."));
|
|
95
|
-
console.log(chalk_1.default.gray(` If browser doesn't open, visit: ${authUrl}`));
|
|
96
|
-
// Open browser
|
|
97
|
-
const open = async (url) => {
|
|
98
|
-
const { default: openBrowser } = await Promise.resolve().then(() => __importStar(require("open")));
|
|
99
|
-
await openBrowser(url);
|
|
100
|
-
};
|
|
101
|
-
open(authUrl);
|
|
102
|
-
// Handle callback
|
|
103
|
-
server.on("request", async (req, res) => {
|
|
104
|
-
if (!req.url?.startsWith("/callback")) {
|
|
105
|
-
res.writeHead(404);
|
|
106
|
-
res.end("Not found");
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
const url = new URL(req.url, `http://localhost:${port}`);
|
|
110
|
-
const token = url.searchParams.get("token");
|
|
111
|
-
if (!token) {
|
|
112
|
-
res.writeHead(400);
|
|
113
|
-
res.end("Missing token");
|
|
114
|
-
server.close();
|
|
115
|
-
reject(new Error("No token received"));
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
// Decode token to get user info (JWT payload)
|
|
119
|
-
let email;
|
|
120
|
-
let name;
|
|
121
|
-
try {
|
|
122
|
-
const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64").toString());
|
|
123
|
-
email = payload.email;
|
|
124
|
-
name = payload.name;
|
|
125
|
-
}
|
|
126
|
-
catch {
|
|
127
|
-
// Ignore decode errors
|
|
128
|
-
}
|
|
129
|
-
// Save token (30 days expiry)
|
|
130
|
-
saveToken(token, 30 * 24 * 60 * 60, email, name);
|
|
131
|
-
// Send success page
|
|
132
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
133
|
-
res.end(`
|
|
134
|
-
<!DOCTYPE html>
|
|
135
|
-
<html>
|
|
136
|
-
<head>
|
|
137
|
-
<title>CyberMem - Logged In</title>
|
|
138
|
-
<style>
|
|
139
|
-
body { font-family: system-ui; text-align: center; padding: 50px; background: #0a0a0a; color: #fff; }
|
|
140
|
-
h1 { color: #22c55e; }
|
|
141
|
-
.logo { font-size: 48px; margin-bottom: 20px; }
|
|
142
|
-
</style>
|
|
143
|
-
</head>
|
|
144
|
-
<body>
|
|
145
|
-
<div class="logo">🧠</div>
|
|
146
|
-
<h1>Successfully Logged In!</h1>
|
|
147
|
-
<p>You can close this window and return to your terminal.</p>
|
|
148
|
-
<p style="color: #888;">Logged in as: ${email || name || "Unknown"}</p>
|
|
149
|
-
</body>
|
|
150
|
-
</html>
|
|
151
|
-
`);
|
|
152
|
-
console.log("");
|
|
153
|
-
console.log(chalk_1.default.green("✅ Successfully logged in as:"), chalk_1.default.bold(email || name || "Unknown"));
|
|
154
|
-
console.log(chalk_1.default.gray(` Token saved to: ${TOKEN_FILE}`));
|
|
155
|
-
server.close();
|
|
156
|
-
resolve();
|
|
157
|
-
});
|
|
158
|
-
// Timeout after 5 minutes
|
|
159
|
-
setTimeout(() => {
|
|
160
|
-
server.close();
|
|
161
|
-
reject(new Error("Login timeout - no callback received"));
|
|
162
|
-
}, 5 * 60 * 1000);
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# Example environment configuration
|
|
2
|
-
# Copy to .env and customize
|
|
3
|
-
|
|
4
|
-
# Embeddings provider: ollama (local) or openai (cloud)
|
|
5
|
-
EMBEDDINGS_PROVIDER=ollama
|
|
6
|
-
OLLAMA_URL=http://ollama:11434
|
|
7
|
-
OPENAI_API_KEY=
|
|
8
|
-
|
|
9
|
-
# Database backend: sqlite (local/rpi) or postgres (vps)
|
|
10
|
-
DB_BACKEND=sqlite
|
|
11
|
-
DB_PATH=/data/openmemory.sqlite
|
|
12
|
-
VECTOR_BACKEND=sqlite
|
|
13
|
-
|
|
14
|
-
# PostgreSQL settings (only for DB_BACKEND=postgres)
|
|
15
|
-
PG_HOST=postgres
|
|
16
|
-
PG_PORT=5432
|
|
17
|
-
PG_DB=openmemory
|
|
18
|
-
PG_USER=openmemory
|
|
19
|
-
PG_PASSWORD=change-me
|
|
20
|
-
|
|
21
|
-
# OpenMemory API key (Optional for local mode)
|
|
22
|
-
# OM_API_KEY=
|
|
23
|
-
|
|
24
|
-
# Monitoring
|
|
25
|
-
PROM_RETENTION=7d
|
|
26
|
-
GRAFANA_USER=admin
|
|
27
|
-
GRAFANA_PASSWORD=admin
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# Raspberry Pi environment
|
|
2
|
-
# Optimized for low memory (1GB total)
|
|
3
|
-
DOCKER_PLATFORM=linux/arm64
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# Embeddings (use Ollama with small models)
|
|
7
|
-
EMBEDDINGS_PROVIDER=ollama
|
|
8
|
-
OLLAMA_URL=http://ollama:11434
|
|
9
|
-
OPENAI_API_KEY=
|
|
10
|
-
|
|
11
|
-
# Database (SQLite for RPi - low memory footprint)
|
|
12
|
-
DB_BACKEND=sqlite
|
|
13
|
-
DB_PATH=/data/openmemory.sqlite
|
|
14
|
-
|
|
15
|
-
# PostgreSQL (not used)
|
|
16
|
-
PG_HOST=postgres
|
|
17
|
-
PG_DB=openmemory
|
|
18
|
-
PG_USER=openmemory
|
|
19
|
-
PG_PASSWORD=not-used
|
|
20
|
-
|
|
21
|
-
# OpenMemory
|
|
22
|
-
OM_API_KEY=key-change-me
|
|
23
|
-
|
|
24
|
-
# Monitoring (short retention for disk space)
|
|
25
|
-
PROM_RETENTION=3d
|
|
26
|
-
GRAFANA_USER=admin
|
|
27
|
-
GRAFANA_PASSWORD=admin
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# VPS production environment
|
|
2
|
-
# For Hetzner CX22 or similar (2 vCPU, 4GB RAM)
|
|
3
|
-
|
|
4
|
-
# Embeddings (use OpenAI for production)
|
|
5
|
-
EMBEDDINGS_PROVIDER=openai
|
|
6
|
-
OPENAI_API_KEY=sk-change-me-in-production
|
|
7
|
-
OLLAMA_URL=
|
|
8
|
-
|
|
9
|
-
# Database (PostgreSQL for production)
|
|
10
|
-
DB_BACKEND=postgres
|
|
11
|
-
VECTOR_BACKEND=postgres
|
|
12
|
-
|
|
13
|
-
# PostgreSQL
|
|
14
|
-
PG_HOST=postgres
|
|
15
|
-
PG_DB=openmemory
|
|
16
|
-
PG_USER=openmemory
|
|
17
|
-
PG_PASSWORD=change-me-in-production-use-secrets
|
|
18
|
-
|
|
19
|
-
# OpenMemory
|
|
20
|
-
OM_API_KEY=change-me-in-production-use-secrets
|
|
21
|
-
|
|
22
|
-
# Monitoring
|
|
23
|
-
PROM_RETENTION=30d
|
|
24
|
-
GRAFANA_USER=admin
|
|
25
|
-
GRAFANA_PASSWORD=change-me-in-production-use-secrets
|
|
@@ -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)
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# OpenMemory using official npm package
|
|
2
|
-
FROM node:20-alpine
|
|
3
|
-
|
|
4
|
-
WORKDIR /app
|
|
5
|
-
|
|
6
|
-
# Install openmemory-js from npm (waiting for release with MCP fix)
|
|
7
|
-
RUN npm install openmemory-js@1.3.2
|
|
8
|
-
|
|
9
|
-
# Create data directory
|
|
10
|
-
RUN mkdir -p /data && chown -R node:node /data /app
|
|
11
|
-
|
|
12
|
-
USER node
|
|
13
|
-
|
|
14
|
-
EXPOSE 8080
|
|
15
|
-
|
|
16
|
-
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
|
17
|
-
CMD node -e "require('http').get('http://localhost:8080/health', (res) => process.exit(res.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"
|
|
18
|
-
|
|
19
|
-
CMD ["npm", "start", "--prefix", "/app/node_modules/openmemory-js"]
|