Adolf: decommission Cognee (H4) + standalone hindsight-llm + vault access
H4 (kb#76) - Cognee -> Hindsight migration finished: - New openai/hindsight-llm/ (clone of cognee-llm, :8012, own volume) so Hindsight's Kimi LLM no longer depends on the cognee stack - Repointed hindsight service LLM at hindsight-llm:8012 (+ depends_on) - Removed cognee, cognee-mcp, cognee-llm services + cognee-llm-home volume from openai/docker-compose.yml - Removed the disabled cognee-memory plugin entry from openclaw.json Vault access (kb#64): wired the shared agap-mcp (:3100, same MCP Claude uses) into Adolf's registry - "agap" server in shared-mcp.json + openclaw.json mcp.servers. Adolf can now fetch credentials from Vaultwarden (verified). Note: agap-mcp/docker-compose.yml (repointed to the adolf46 account) is deliberately NOT in this commit - it holds the master password in plaintext. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014t8Qg9gi7H7HtT8MncoXAB
This commit is contained in:
@@ -131,6 +131,15 @@
|
|||||||
Authorization: "Bearer ${MARKETPLACE_MCP_TOKEN}",
|
Authorization: "Bearer ${MARKETPLACE_MCP_TOKEN}",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// agap-mcp (kb#64) -- the SAME shared agap-mcp instance Claude Code uses
|
||||||
|
// (network_mode: host, :3100, unauthenticated on localhost). Grants Adolf
|
||||||
|
// the same access as Claude: vault (vw_* for credential fetching) plus
|
||||||
|
// gitea/ha/zabbix/radicale. Reached via host.docker.internal like
|
||||||
|
// kanboard/marketplace above.
|
||||||
|
agap: {
|
||||||
|
type: "http",
|
||||||
|
url: "http://host.docker.internal:3100/mcp",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -138,27 +147,6 @@
|
|||||||
// load a plugin at startup (discovery alone is not enough).
|
// load a plugin at startup (discovery alone is not enough).
|
||||||
plugins: {
|
plugins: {
|
||||||
entries: {
|
entries: {
|
||||||
// Cognee memory plugin — DISABLED as of kb #75 (H3): superseded by
|
|
||||||
// hindsight-memory below. Left `enabled: false` rather than removed —
|
|
||||||
// full teardown (plugin dir, cognee/cognee-mcp/cognee-llm containers)
|
|
||||||
// is kb #75's H4, a separate step so the Hindsight path can be proven
|
|
||||||
// out first. Kept disabled (not both active) to avoid double
|
|
||||||
// prependContext injection and double persisted turns while both
|
|
||||||
// backends exist side by side.
|
|
||||||
"cognee-memory": {
|
|
||||||
enabled: false,
|
|
||||||
// External (non-bundled) plugins must opt in to conversation + prompt-injection
|
|
||||||
// hook access explicitly. before_prompt_build => allowPromptInjection;
|
|
||||||
// agent_end => allowConversationAccess.
|
|
||||||
hooks: { allowConversationAccess: true, allowPromptInjection: true },
|
|
||||||
// Throttle background cognify hard to stop it draining the Kimi quota:
|
|
||||||
// one cognify per dataset per hour (was 5 min default), and a longer
|
|
||||||
// persist timeout so /add doesn't fail-and-retry when cognee is busy.
|
|
||||||
config: {
|
|
||||||
sweepIntervalMs: 3600000, // 1h (default 300000 = 5min)
|
|
||||||
persistTimeoutMs: 20000, // 20s (default 8000)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Hindsight memory plugin (kb #75, H3) — installed external plugin
|
// Hindsight memory plugin (kb #75, H3) — installed external plugin
|
||||||
// under .openclaw/extensions/hindsight-memory, bind-mounted read-only
|
// under .openclaw/extensions/hindsight-memory, bind-mounted read-only
|
||||||
// from openai/hindsight-openclaw-plugin (see that project's
|
// from openai/hindsight-openclaw-plugin (see that project's
|
||||||
|
|||||||
@@ -210,17 +210,17 @@ services:
|
|||||||
["node", "dist/index.js", "gateway", "--bind", "lan", "--port", "18789"]
|
["node", "dist/index.js", "gateway", "--bind", "lan", "--port", "18789"]
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# cognee-llm — stateless one-shot Kimi-CLI wrapper for Cognee's batch cognify
|
# hindsight-llm — standalone clone of cognee-llm (kb#76, H4 option B): the
|
||||||
# (P3). Opposite policy to kimi-agent: no resume, non-streaming, text-only.
|
# dedicated Kimi-CLI wrapper that is now Hindsight's LLM, so the whole cognee
|
||||||
# Note (SPIKE-FINDINGS gate 5): Cognee should DEFAULT its LLM to LiteLLM; this
|
# stack (incl. cognee-llm) can be decommissioned. Own port (:8012) + own
|
||||||
# is the optional low-volume path. Needs `kimi login` in its own volume.
|
# kimi-code volume; needs a one-time `kimi login` seeded into hindsight-llm-home.
|
||||||
cognee-llm:
|
hindsight-llm:
|
||||||
build: ./cognee-llm
|
build: ./hindsight-llm
|
||||||
container_name: cognee-llm
|
container_name: hindsight-llm
|
||||||
ports:
|
ports:
|
||||||
- "8011:8011"
|
- "8012:8012"
|
||||||
volumes:
|
volumes:
|
||||||
- cognee-llm-home:/root/.kimi-code
|
- hindsight-llm-home:/root/.kimi-code
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# adolf-llm — conversational Kimi-CLI wrapper (:8010), the model backend for
|
# adolf-llm — conversational Kimi-CLI wrapper (:8010), the model backend for
|
||||||
@@ -244,68 +244,6 @@ services:
|
|||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:host-gateway"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# cognee — Adolf's memory backend (P4). FastAPI + embedded Kuzu graph +
|
|
||||||
# Qdrant vectors. LLM via cognee-llm:8011 (Kimi CLI wrapper), embeddings via
|
|
||||||
# ollama directly (host.docker.internal:11436, separate compose project —
|
|
||||||
# hence extra_hosts below). Sole owner of the on-disk Kuzu/SQLite files
|
|
||||||
# under /mnt/ssd/dbs/cognee/ (Kuzu is not safe for concurrent multi-process
|
|
||||||
# access) — never run a second process against those files.
|
|
||||||
cognee:
|
|
||||||
build: ./cognee
|
|
||||||
container_name: cognee
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
# Real OS env var, not just the mounted .env file: the qdrant vector
|
|
||||||
# adapter's registration hook (cognee/Dockerfile's sitecustomize.py)
|
|
||||||
# gates on os.environ.get("VECTOR_DB_PROVIDER") at Python interpreter
|
|
||||||
# start, which only sees actual container env vars — pydantic-settings'
|
|
||||||
# env_file=".env" parsing (used for the rest of cognee.env) never
|
|
||||||
# populates os.environ itself. Without this, cognee raises
|
|
||||||
# "Unsupported vector database provider: qdrant" at startup even though
|
|
||||||
# cognee.env sets VECTOR_DB_PROVIDER=qdrant. Verified 2026-07-05.
|
|
||||||
- VECTOR_DB_PROVIDER=qdrant
|
|
||||||
volumes:
|
|
||||||
- ./cognee/cognee.env:/app/.env
|
|
||||||
- /mnt/ssd/dbs/cognee/data:/data
|
|
||||||
- /mnt/ssd/dbs/cognee/system:/system
|
|
||||||
extra_hosts:
|
|
||||||
- "host.docker.internal:host-gateway"
|
|
||||||
# Not published to the host — only cognee-mcp (same compose network)
|
|
||||||
# needs to reach it. Uncomment for local debugging:
|
|
||||||
# ports:
|
|
||||||
# - "8000:8000"
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 40s
|
|
||||||
|
|
||||||
# cognee-mcp — thin MCP-to-HTTP proxy in API mode (API_URL=cognee:8000).
|
|
||||||
# Never opens the graph/vector files itself, so it's safe to run alongside
|
|
||||||
# `cognee` without a second writer on the same Kuzu database. Exposes 3
|
|
||||||
# tools: remember / recall / forget.
|
|
||||||
#
|
|
||||||
# Built from a local Dockerfile (kb#70 fix) instead of the bare upstream
|
|
||||||
# image: forget was missing a data_id parameter end-to-end, so agents
|
|
||||||
# could delete a whole dataset but never a single entry. See
|
|
||||||
# ./cognee-mcp/Dockerfile and ./cognee-mcp/src/ for the patched files.
|
|
||||||
cognee-mcp:
|
|
||||||
build: ./cognee-mcp
|
|
||||||
container_name: cognee-mcp
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- ENV=local
|
|
||||||
- LOG_LEVEL=INFO
|
|
||||||
- PYTHONUNBUFFERED=1
|
|
||||||
- TRANSPORT_MODE=http
|
|
||||||
- API_URL=http://cognee:8000
|
|
||||||
- MCP_ALLOWED_HOSTS=cognee-mcp:*
|
|
||||||
ports:
|
|
||||||
- "8001:8000"
|
|
||||||
depends_on:
|
|
||||||
- cognee
|
|
||||||
|
|
||||||
# hindsight — Adolf memory backend, replacing cognee/cognee-mcp/cognee-llm
|
# hindsight — Adolf memory backend, replacing cognee/cognee-mcp/cognee-llm
|
||||||
# (kb#73, migration doc agap_git/adolf/HINDSIGHT-MIGRATION.md, H1). One
|
# (kb#73, migration doc agap_git/adolf/HINDSIGHT-MIGRATION.md, H1). One
|
||||||
# container: REST API :8888 (also serves the built-in MCP at /mcp/{bank}/),
|
# container: REST API :8888 (also serves the built-in MCP at /mcp/{bank}/),
|
||||||
@@ -317,7 +255,7 @@ services:
|
|||||||
# LLM + embeddings reconfigured 2026-07-15 (kb#84) to fix two wrong H1
|
# LLM + embeddings reconfigured 2026-07-15 (kb#84) to fix two wrong H1
|
||||||
# choices for a Russian/multilingual use case:
|
# choices for a Russian/multilingual use case:
|
||||||
#
|
#
|
||||||
# LLM -> cognee-llm:8011 (the existing Kimi-CLI wrapper, same shim cognee
|
# LLM -> hindsight-llm:8012 (dedicated Kimi-CLI wrapper cloned from the shim cognee
|
||||||
# uses — see cognee/cognee.env's LLM section for the full precedent,
|
# uses — see cognee/cognee.env's LLM section for the full precedent,
|
||||||
# including why LLM_INSTRUCTOR_MODE=json_mode isn't needed here since
|
# including why LLM_INSTRUCTOR_MODE=json_mode isn't needed here since
|
||||||
# Hindsight's own client doesn't go through `instructor`). Replaces the
|
# Hindsight's own client doesn't go through `instructor`). Replaces the
|
||||||
@@ -353,11 +291,11 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- HINDSIGHT_API_LLM_PROVIDER=openai
|
- HINDSIGHT_API_LLM_PROVIDER=openai
|
||||||
- HINDSIGHT_API_LLM_BASE_URL=http://cognee-llm:8011/v1
|
- HINDSIGHT_API_LLM_BASE_URL=http://hindsight-llm:8012/v1
|
||||||
- HINDSIGHT_API_LLM_MODEL=openai/cognee-llm
|
- HINDSIGHT_API_LLM_MODEL=openai/hindsight-llm
|
||||||
# cognee-llm ignores the key entirely (Kimi CLI wrapper, no real
|
# hindsight-llm ignores the key entirely (Kimi CLI wrapper, no real
|
||||||
# OpenAI auth) — same dummy value cognee.env uses for LLM_API_KEY.
|
# OpenAI auth) — dummy value, non-empty so the client constructs.
|
||||||
- HINDSIGHT_API_LLM_API_KEY=sk-cognee-llm-local
|
- HINDSIGHT_API_LLM_API_KEY=sk-hindsight-llm-local
|
||||||
- HINDSIGHT_API_EMBEDDINGS_PROVIDER=openai
|
- HINDSIGHT_API_EMBEDDINGS_PROVIDER=openai
|
||||||
- HINDSIGHT_API_EMBEDDINGS_OPENAI_BASE_URL=http://host.docker.internal:11436/v1
|
- HINDSIGHT_API_EMBEDDINGS_OPENAI_BASE_URL=http://host.docker.internal:11436/v1
|
||||||
- HINDSIGHT_API_EMBEDDINGS_OPENAI_MODEL=bge-m3
|
- HINDSIGHT_API_EMBEDDINGS_OPENAI_MODEL=bge-m3
|
||||||
@@ -393,7 +331,7 @@ services:
|
|||||||
# file.
|
# file.
|
||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:host-gateway"
|
||||||
depends_on:
|
depends_on:
|
||||||
- cognee-llm
|
- hindsight-llm
|
||||||
|
|
||||||
# openclaw-tools — MCP bridge (P5) exposing a minimal slice of the Adolf
|
# openclaw-tools — MCP bridge (P5) exposing a minimal slice of the Adolf
|
||||||
# OpenClaw gateway's agent tools (message/cron/nodes/browser) over MCP
|
# OpenClaw gateway's agent tools (message/cron/nodes/browser) over MCP
|
||||||
@@ -417,6 +355,6 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
kimi-agent-home:
|
kimi-agent-home:
|
||||||
adolf-state:
|
adolf-state:
|
||||||
cognee-llm-home:
|
hindsight-llm-home:
|
||||||
adolf-llm-workspace:
|
adolf-llm-workspace:
|
||||||
adolf-llm-home:
|
adolf-llm-home:
|
||||||
|
|||||||
11
openai/hindsight-llm/Dockerfile
Normal file
11
openai/hindsight-llm/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
FROM node:22-slim
|
||||||
|
|
||||||
|
RUN npm install -g @moonshot-ai/kimi-code
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
COPY server.js /app/server.js
|
||||||
|
|
||||||
|
EXPOSE 8012
|
||||||
|
|
||||||
|
ENTRYPOINT ["node", "/app/server.js"]
|
||||||
178
openai/hindsight-llm/server.js
Normal file
178
openai/hindsight-llm/server.js
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
const http = require('http');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const { spawn } = require('child_process');
|
||||||
|
|
||||||
|
const PORT = 8012;
|
||||||
|
const MODEL_ID = 'hindsight-llm';
|
||||||
|
const TIMEOUT_MS = 5 * 60 * 1000; // one-shot structured calls; generous but bounded
|
||||||
|
// Bounded parallelism: SPIKE-FINDINGS.md gate 5 flagged the Kimi subscription as a
|
||||||
|
// single-seat, interactive-oriented plan — batch cognify must not hammer it with
|
||||||
|
// unbounded concurrent CLI spawns (rate-limit/throttle risk on a shared live account).
|
||||||
|
const MAX_CONCURRENCY = 3;
|
||||||
|
|
||||||
|
const WORKSPACE = '/workspace';
|
||||||
|
fs.mkdirSync(WORKSPACE, { recursive: true });
|
||||||
|
|
||||||
|
// The CLI has no raw sampling-temperature knob (it's an agent loop, not a
|
||||||
|
// completions API) — "low temperature" for structured extraction is enforced
|
||||||
|
// via an instruction preamble instead, prepended to whatever system prompt
|
||||||
|
// the caller (Cognee) supplies.
|
||||||
|
const STRUCTURED_SYSTEM_PREAMBLE = [
|
||||||
|
'You are a stateless structured-extraction engine.',
|
||||||
|
'This is a one-shot call with no memory of prior calls: do not reference earlier turns.',
|
||||||
|
'Respond deterministically and concisely. When asked for JSON, output raw JSON only',
|
||||||
|
'- no prose, no markdown code fences, no commentary before or after.',
|
||||||
|
].join(' ');
|
||||||
|
|
||||||
|
// --- message helpers ---------------------------------------------------------
|
||||||
|
// Text only, no media parts: this wrapper's policy is no-media/no-MCP, unlike
|
||||||
|
// adolf-llm which persists inbound images and lets the CLI's ReadMediaFile
|
||||||
|
// tool read them.
|
||||||
|
function textOf(msg) {
|
||||||
|
const c = msg.content;
|
||||||
|
if (Array.isArray(c)) return c.map(p => (typeof p.text === 'string' ? p.text : '')).join('\n');
|
||||||
|
return c == null ? '' : String(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPrompt(messages) {
|
||||||
|
const systemParts = messages.filter(m => m.role === 'system').map(textOf);
|
||||||
|
const rest = messages.filter(m => m.role !== 'system');
|
||||||
|
const preamble = [STRUCTURED_SYSTEM_PREAMBLE, ...systemParts].join('\n\n');
|
||||||
|
const transcript = rest
|
||||||
|
.map(m => `${m.role === 'assistant' ? 'Assistant' : 'User'}: ${textOf(m)}`)
|
||||||
|
.join('\n\n');
|
||||||
|
return `${preamble}\n\n${transcript}`.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- bounded concurrency queue -----------------------------------------------
|
||||||
|
let active = 0;
|
||||||
|
const queue = [];
|
||||||
|
function drain() {
|
||||||
|
if (queue.length && active < MAX_CONCURRENCY) queue.shift()();
|
||||||
|
}
|
||||||
|
function withSlot(fn) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const run = () => {
|
||||||
|
active++;
|
||||||
|
fn().then(
|
||||||
|
v => { active--; drain(); resolve(v); },
|
||||||
|
e => { active--; drain(); reject(e); },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
if (active < MAX_CONCURRENCY) run();
|
||||||
|
else queue.push(run);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- kimi invocation: stateless one-shot, no resume --------------------------
|
||||||
|
// Fresh temp dir per call, NO -r/-S session flag, discard the dir after.
|
||||||
|
// Returns the assembled text from --output-format stream-json:
|
||||||
|
// {"role":"assistant","content":"..."}
|
||||||
|
// (reuses the same parse core as kimi-agent/server.js's runKimi, minus the
|
||||||
|
// resume/session-id bookkeeping that wrapper needs and this one deliberately
|
||||||
|
// does not).
|
||||||
|
function runKimi({ prompt, cwd }) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const args = ['-p', prompt, '--output-format', 'stream-json'];
|
||||||
|
const child = spawn('kimi', args, { cwd, timeout: TIMEOUT_MS });
|
||||||
|
|
||||||
|
let stdout = '';
|
||||||
|
let stderr = '';
|
||||||
|
child.stdout.on('data', d => { stdout += d; });
|
||||||
|
child.stderr.on('data', d => { stderr += d; });
|
||||||
|
|
||||||
|
child.on('error', reject);
|
||||||
|
child.on('close', code => {
|
||||||
|
const parts = [];
|
||||||
|
for (const line of stdout.split('\n')) {
|
||||||
|
const t = line.trim();
|
||||||
|
if (!t) continue;
|
||||||
|
let obj;
|
||||||
|
try { obj = JSON.parse(t); } catch { continue; }
|
||||||
|
if (obj.role === 'assistant' && obj.content) parts.push(obj.content);
|
||||||
|
}
|
||||||
|
const text = parts.join('').trim();
|
||||||
|
if (!text && code !== 0) {
|
||||||
|
reject(new Error(`kimi exited ${code}: ${stderr.slice(0, 2000)}`));
|
||||||
|
} else {
|
||||||
|
resolve(text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleTurn(messages) {
|
||||||
|
const prompt = buildPrompt(messages || []);
|
||||||
|
const reqId = crypto.randomUUID();
|
||||||
|
const dir = path.join(WORKSPACE, reqId);
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
try {
|
||||||
|
return await withSlot(() => runKimi({ prompt, cwd: dir }));
|
||||||
|
} finally {
|
||||||
|
// Stateless one-shot: nothing about this call is meant to survive it, so
|
||||||
|
// the temp dir is discarded unconditionally, success or failure.
|
||||||
|
fs.rm(dir, { recursive: true, force: true }, () => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- OpenAI-compatible HTTP surface (non-streaming only) ---------------------
|
||||||
|
function completionBody(text) {
|
||||||
|
return {
|
||||||
|
id: `chatcmpl-${Date.now()}`,
|
||||||
|
object: 'chat.completion',
|
||||||
|
created: Math.floor(Date.now() / 1000),
|
||||||
|
model: MODEL_ID,
|
||||||
|
choices: [{
|
||||||
|
index: 0,
|
||||||
|
message: { role: 'assistant', content: text },
|
||||||
|
finish_reason: 'stop',
|
||||||
|
}],
|
||||||
|
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = http.createServer((req, res) => {
|
||||||
|
if (req.method === 'GET' && req.url === '/v1/models') {
|
||||||
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
object: 'list',
|
||||||
|
data: [{ id: MODEL_ID, object: 'model', owned_by: 'moonshot' }],
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.method === 'POST' && req.url === '/v1/chat/completions') {
|
||||||
|
let body = '';
|
||||||
|
req.on('data', d => { body += d; });
|
||||||
|
req.on('end', async () => {
|
||||||
|
let parsed;
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(body);
|
||||||
|
} catch {
|
||||||
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify({ error: 'invalid JSON body' }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const text = await handleTurn(parsed.messages || []);
|
||||||
|
// Non-streaming policy: always return the full body even if the
|
||||||
|
// caller sets stream:true. Cognee's batch cognify has no use for SSE,
|
||||||
|
// and a one-shot call has nothing to incrementally stream anyway.
|
||||||
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify(completionBody(text)));
|
||||||
|
} catch (err) {
|
||||||
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify({ error: String(err.message || err) }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify({ error: 'not found' }));
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(PORT, () => console.log(`hindsight-llm wrapper listening on :${PORT}`));
|
||||||
@@ -11,6 +11,10 @@
|
|||||||
"kanboard": {
|
"kanboard": {
|
||||||
"type": "http",
|
"type": "http",
|
||||||
"url": "http://host.docker.internal:3104/mcp"
|
"url": "http://host.docker.internal:3104/mcp"
|
||||||
|
},
|
||||||
|
"agap": {
|
||||||
|
"type": "http",
|
||||||
|
"url": "http://host.docker.internal:3100/mcp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user