@vheins/local-memory-mcp 0.18.13 → 0.19.0
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/README.id.md +9 -1
- package/README.md +32 -17
- package/dist/{chunk-BK2QIQXS.js → chunk-AKFMCVQ4.js} +857 -680
- package/dist/dashboard/public/assets/{index-wAYh22Zy.css → index-CtKekPIh.css} +1 -1
- package/dist/dashboard/public/assets/index-DsmurBbn.js +150 -0
- package/dist/dashboard/public/index.html +2 -2
- package/dist/dashboard/server.js +227 -171
- package/dist/mcp/server.js +1919 -727
- package/dist/prompts/create-task.md +10 -2
- package/dist/prompts/documentation-sync.md +1 -1
- package/dist/prompts/learning-retrospective.md +1 -1
- package/dist/prompts/memory-agent-core.md +8 -2
- package/dist/prompts/project-briefing.md +2 -2
- package/dist/prompts/review-and-audit.md +2 -2
- package/dist/prompts/review-and-post-issue.md +3 -3
- package/dist/prompts/scrum-master.md +1 -1
- package/dist/prompts/sentinel-issue-resolver.md +1 -1
- package/dist/prompts/server/instructions.md +10 -2
- package/dist/prompts/session-planner.md +1 -1
- package/dist/prompts/task-memory-executor.md +2 -2
- package/dist/prompts/tool-usage-guidelines.md +9 -2
- package/package.json +5 -1
- package/dist/dashboard/public/assets/index-DWRMdSsg.js +0 -150
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
9
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
10
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-DsmurBbn.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CtKekPIh.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="app"></div>
|
package/dist/dashboard/server.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
MCP_PROTOCOL_VERSION,
|
|
4
3
|
PROMPTS,
|
|
5
4
|
RealVectorStore,
|
|
6
5
|
SQLiteStore,
|
|
@@ -16,7 +15,7 @@ import {
|
|
|
16
15
|
handleTaskClaim,
|
|
17
16
|
listResources,
|
|
18
17
|
logger
|
|
19
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-AKFMCVQ4.js";
|
|
20
19
|
|
|
21
20
|
// src/dashboard/server.ts
|
|
22
21
|
import express from "express";
|
|
@@ -25,191 +24,60 @@ import fs3 from "fs";
|
|
|
25
24
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
26
25
|
|
|
27
26
|
// src/mcp/client.ts
|
|
28
|
-
import { spawn } from "child_process";
|
|
29
|
-
import { createInterface } from "readline";
|
|
30
27
|
import path from "path";
|
|
31
28
|
import fs from "fs";
|
|
32
29
|
import { fileURLToPath } from "url";
|
|
30
|
+
import { Client } from "@modelcontextprotocol/client";
|
|
31
|
+
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
|
33
32
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
34
|
-
var RETRY_DELAYS = [1e3, 2e3, 4e3];
|
|
35
|
-
var MAX_RETRIES = 3;
|
|
36
|
-
var MAX_RESTARTS = 3;
|
|
37
|
-
var REQUEST_TIMEOUT_MS = 1e4;
|
|
38
33
|
var MCPClient = class {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
pendingRequests = /* @__PURE__ */ new Map();
|
|
43
|
-
isInitialized = false;
|
|
34
|
+
client;
|
|
35
|
+
transport = null;
|
|
36
|
+
_connected = false;
|
|
44
37
|
serverPathOverride;
|
|
45
|
-
restartCount = 0;
|
|
46
38
|
constructor(serverPath) {
|
|
47
39
|
this.serverPathOverride = serverPath;
|
|
40
|
+
this.client = new Client(
|
|
41
|
+
{ name: "mcp-client", version: "1.0.0" },
|
|
42
|
+
{
|
|
43
|
+
enforceStrictCapabilities: false
|
|
44
|
+
}
|
|
45
|
+
);
|
|
48
46
|
}
|
|
49
47
|
async start() {
|
|
50
|
-
if (this.
|
|
51
|
-
if (this.starting) return this.starting;
|
|
52
|
-
this.starting = this.startInternal();
|
|
53
|
-
try {
|
|
54
|
-
await this.starting;
|
|
55
|
-
} finally {
|
|
56
|
-
this.starting = null;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
async startInternal() {
|
|
60
|
-
if (this.process) return;
|
|
48
|
+
if (this._connected) return;
|
|
61
49
|
const serverPath = this.serverPathOverride || (fs.existsSync(path.join(__dirname, "../mcp/server.js")) ? path.join(__dirname, "../mcp/server.js") : path.join(__dirname, "./server.js"));
|
|
62
|
-
this.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (!this.process.stdout || !this.process.stdin) {
|
|
66
|
-
throw new Error("Failed to spawn MCP server");
|
|
67
|
-
}
|
|
68
|
-
this.process.on("close", (code) => {
|
|
69
|
-
if (this.process === null) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
logger.error("MCP server process closed unexpectedly", { code, restartCount: this.restartCount });
|
|
73
|
-
this.process = null;
|
|
74
|
-
this.isInitialized = false;
|
|
75
|
-
this.starting = null;
|
|
76
|
-
if (this.restartCount < MAX_RESTARTS) {
|
|
77
|
-
this.restartCount++;
|
|
78
|
-
logger.info("Attempting to restart MCP server", { attempt: this.restartCount });
|
|
79
|
-
this.start().catch((err) => {
|
|
80
|
-
logger.error("Failed to restart MCP server", { error: String(err) });
|
|
81
|
-
});
|
|
82
|
-
} else {
|
|
83
|
-
logger.error("Max restart attempts reached, giving up", { maxRestarts: MAX_RESTARTS });
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
const rl = createInterface({
|
|
87
|
-
input: this.process.stdout,
|
|
88
|
-
crlfDelay: Infinity
|
|
89
|
-
});
|
|
90
|
-
rl.on("line", (line) => {
|
|
91
|
-
try {
|
|
92
|
-
const response = JSON.parse(line);
|
|
93
|
-
if (!response.id) return;
|
|
94
|
-
const pending = this.pendingRequests.get(response.id);
|
|
95
|
-
if (pending) {
|
|
96
|
-
this.pendingRequests.delete(response.id);
|
|
97
|
-
if (response.error) {
|
|
98
|
-
pending.reject(new Error(response.error.message));
|
|
99
|
-
} else {
|
|
100
|
-
pending.resolve(response.result);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
} catch (err) {
|
|
104
|
-
logger.error("Failed to parse MCP response", { error: String(err), rawLine: line });
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
await this.callWithRetry("initialize", {
|
|
108
|
-
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
109
|
-
capabilities: {},
|
|
110
|
-
clientInfo: { name: "mcp-client", version: "1.0.0" }
|
|
111
|
-
});
|
|
112
|
-
if (this.process?.stdin) {
|
|
113
|
-
this.process.stdin.write(
|
|
114
|
-
JSON.stringify({
|
|
115
|
-
jsonrpc: "2.0",
|
|
116
|
-
method: "notifications/initialized"
|
|
117
|
-
}) + "\n"
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
this.isInitialized = true;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Send a single request without retry. Returns a Promise that rejects on timeout.
|
|
124
|
-
*/
|
|
125
|
-
callOnce(method, params = {}) {
|
|
126
|
-
if (!this.process || !this.process.stdin) {
|
|
127
|
-
return Promise.reject(new Error("MCP server not started"));
|
|
128
|
-
}
|
|
129
|
-
const id = ++this.requestId;
|
|
130
|
-
const request = { jsonrpc: "2.0", id, method, params };
|
|
131
|
-
return new Promise((resolve, reject) => {
|
|
132
|
-
this.pendingRequests.set(id, { resolve, reject });
|
|
133
|
-
const timer = setTimeout(() => {
|
|
134
|
-
if (this.pendingRequests.has(id)) {
|
|
135
|
-
this.pendingRequests.delete(id);
|
|
136
|
-
reject(new Error("Request timeout"));
|
|
137
|
-
}
|
|
138
|
-
}, REQUEST_TIMEOUT_MS);
|
|
139
|
-
if (timer.unref) timer.unref();
|
|
140
|
-
this.process.stdin.write(JSON.stringify(request) + "\n");
|
|
50
|
+
this.transport = new StdioClientTransport({
|
|
51
|
+
command: "node",
|
|
52
|
+
args: [serverPath]
|
|
141
53
|
});
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
* Call with retry on timeout — up to MAX_RETRIES retries with exponential backoff.
|
|
145
|
-
* Req 18.1: retry up to 3x with delays 1s, 2s, 4s.
|
|
146
|
-
*/
|
|
147
|
-
async callWithRetry(method, params = {}) {
|
|
148
|
-
let lastError;
|
|
149
|
-
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
150
|
-
try {
|
|
151
|
-
return await this.callOnce(method, params);
|
|
152
|
-
} catch (err) {
|
|
153
|
-
lastError = err;
|
|
154
|
-
const isTimeout = err instanceof Error && err.message === "Request timeout";
|
|
155
|
-
if (!isTimeout || attempt === MAX_RETRIES) {
|
|
156
|
-
logger.error("MCP request failed", {
|
|
157
|
-
method,
|
|
158
|
-
attempt,
|
|
159
|
-
error: String(err)
|
|
160
|
-
});
|
|
161
|
-
throw err;
|
|
162
|
-
}
|
|
163
|
-
const delay = RETRY_DELAYS[attempt];
|
|
164
|
-
logger.warn("MCP request timed out, retrying", {
|
|
165
|
-
method,
|
|
166
|
-
attempt: attempt + 1,
|
|
167
|
-
delayMs: delay
|
|
168
|
-
});
|
|
169
|
-
await sleep(delay);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
throw lastError;
|
|
54
|
+
await this.client.connect(this.transport);
|
|
55
|
+
this._connected = true;
|
|
173
56
|
}
|
|
174
57
|
async call(method, params = {}) {
|
|
175
|
-
return this.
|
|
58
|
+
return this.client.request({ method, params });
|
|
176
59
|
}
|
|
177
60
|
async callTool(toolName, args) {
|
|
178
|
-
return this.
|
|
61
|
+
return this.client.callTool({
|
|
62
|
+
name: toolName,
|
|
63
|
+
arguments: args
|
|
64
|
+
});
|
|
179
65
|
}
|
|
180
66
|
async readResource(uri) {
|
|
181
|
-
return this.
|
|
67
|
+
return this.client.readResource({ uri });
|
|
182
68
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
stop() {
|
|
188
|
-
for (const [id, pending] of this.pendingRequests) {
|
|
189
|
-
pending.reject(new Error("Client stopped"));
|
|
190
|
-
this.pendingRequests.delete(id);
|
|
191
|
-
}
|
|
192
|
-
if (this.process) {
|
|
193
|
-
const proc = this.process;
|
|
194
|
-
this.process = null;
|
|
195
|
-
this.isInitialized = false;
|
|
196
|
-
proc.kill();
|
|
197
|
-
}
|
|
69
|
+
async stop() {
|
|
70
|
+
this._connected = false;
|
|
71
|
+
await this.client.close();
|
|
72
|
+
this.transport = null;
|
|
198
73
|
}
|
|
199
74
|
isConnected() {
|
|
200
|
-
return this.
|
|
75
|
+
return this._connected;
|
|
201
76
|
}
|
|
202
|
-
/**
|
|
203
|
-
* Returns the number of requests currently awaiting a response.
|
|
204
|
-
* Req 18.5
|
|
205
|
-
*/
|
|
206
77
|
getPendingCount() {
|
|
207
|
-
return
|
|
78
|
+
return 0;
|
|
208
79
|
}
|
|
209
80
|
};
|
|
210
|
-
function sleep(ms) {
|
|
211
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
212
|
-
}
|
|
213
81
|
|
|
214
82
|
// src/dashboard/lib/context.ts
|
|
215
83
|
var db = await SQLiteStore.create();
|
|
@@ -221,7 +89,7 @@ vectors.initialize().catch((err) => {
|
|
|
221
89
|
});
|
|
222
90
|
|
|
223
91
|
// src/dashboard/routes/index.ts
|
|
224
|
-
import { Router as
|
|
92
|
+
import { Router as Router7 } from "express";
|
|
225
93
|
|
|
226
94
|
// src/dashboard/routes/system.routes.ts
|
|
227
95
|
import { Router } from "express";
|
|
@@ -1298,14 +1166,202 @@ router5.get("/claims", CoordinationController.listClaims);
|
|
|
1298
1166
|
router5.post("/claims/release", CoordinationController.releaseClaim);
|
|
1299
1167
|
var coordination_routes_default = router5;
|
|
1300
1168
|
|
|
1301
|
-
// src/dashboard/routes/
|
|
1169
|
+
// src/dashboard/routes/kg.routes.ts
|
|
1170
|
+
import { Router as Router6 } from "express";
|
|
1171
|
+
|
|
1172
|
+
// src/dashboard/controllers/KGController.ts
|
|
1173
|
+
var KGController = class {
|
|
1174
|
+
static async listEntities(req, res) {
|
|
1175
|
+
try {
|
|
1176
|
+
await db.refresh();
|
|
1177
|
+
const repo = req.query.repo;
|
|
1178
|
+
if (!repo) return res.status(400).json(jsonApiError("repo is required", 400));
|
|
1179
|
+
const type = req.query.type;
|
|
1180
|
+
const search = req.query.search;
|
|
1181
|
+
let sql = "SELECT * FROM entities WHERE repo = ?";
|
|
1182
|
+
const params = [repo];
|
|
1183
|
+
if (type) {
|
|
1184
|
+
sql += " AND type = ?";
|
|
1185
|
+
params.push(type);
|
|
1186
|
+
}
|
|
1187
|
+
if (search) {
|
|
1188
|
+
sql += " AND name LIKE ?";
|
|
1189
|
+
params.push(`%${search}%`);
|
|
1190
|
+
}
|
|
1191
|
+
sql += " ORDER BY name";
|
|
1192
|
+
const items = db.db.prepare(sql).all(...params);
|
|
1193
|
+
res.json(jsonApiRes(items, "entity"));
|
|
1194
|
+
} catch (err) {
|
|
1195
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
1196
|
+
res.status(500).json(jsonApiError(message));
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
static async getEntity(req, res) {
|
|
1200
|
+
try {
|
|
1201
|
+
await db.refresh();
|
|
1202
|
+
const name = req.params.name;
|
|
1203
|
+
const entity = db.db.prepare("SELECT * FROM entities WHERE name = ?").get(name);
|
|
1204
|
+
if (!entity) return res.status(404).json(jsonApiError("Entity not found", 404));
|
|
1205
|
+
const relations = db.db.prepare("SELECT * FROM relations WHERE from_entity = ? OR to_entity = ? ORDER BY relation_type").all(name, name);
|
|
1206
|
+
const observations = db.db.prepare("SELECT * FROM observations WHERE entity_name = ? ORDER BY created_at DESC").all(name);
|
|
1207
|
+
res.json(jsonApiRes({ id: entity.name, entity, relations, observations }, "entity"));
|
|
1208
|
+
} catch (err) {
|
|
1209
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
1210
|
+
res.status(500).json(jsonApiError(message));
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
static async listRelations(req, res) {
|
|
1214
|
+
try {
|
|
1215
|
+
await db.refresh();
|
|
1216
|
+
const repo = req.query.repo;
|
|
1217
|
+
if (!repo) return res.status(400).json(jsonApiError("repo is required", 400));
|
|
1218
|
+
const items = db.db.prepare("SELECT * FROM relations WHERE repo = ? ORDER BY from_entity, to_entity").all(repo);
|
|
1219
|
+
res.json(jsonApiRes(items, "relation"));
|
|
1220
|
+
} catch (err) {
|
|
1221
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
1222
|
+
res.status(500).json(jsonApiError(message));
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
static async listGraph(req, res) {
|
|
1226
|
+
try {
|
|
1227
|
+
await db.refresh();
|
|
1228
|
+
const repo = req.query.repo;
|
|
1229
|
+
if (!repo) return res.status(400).json(jsonApiError("repo is required", 400));
|
|
1230
|
+
const nodes = db.db.prepare(
|
|
1231
|
+
`SELECT e.name, e.type, e.description, COUNT(o.id) as memoryCount
|
|
1232
|
+
FROM entities e
|
|
1233
|
+
LEFT JOIN observations o ON o.entity_name = e.name
|
|
1234
|
+
WHERE e.repo = ?
|
|
1235
|
+
GROUP BY e.name
|
|
1236
|
+
ORDER BY e.name`
|
|
1237
|
+
).all(repo);
|
|
1238
|
+
const edges = db.db.prepare(
|
|
1239
|
+
"SELECT from_entity as source, to_entity as target, relation_type FROM relations WHERE repo = ? ORDER BY from_entity, to_entity"
|
|
1240
|
+
).all(repo);
|
|
1241
|
+
res.json(jsonApiRes({ id: `graph-${repo}`, nodes, edges }, "graph"));
|
|
1242
|
+
} catch (err) {
|
|
1243
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
1244
|
+
res.status(500).json(jsonApiError(message));
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
static async createEntity(req, res) {
|
|
1248
|
+
try {
|
|
1249
|
+
await db.refresh();
|
|
1250
|
+
const attributes = getAttributes(req);
|
|
1251
|
+
const { name, type, description, repo, owner } = attributes;
|
|
1252
|
+
if (!name) return res.status(400).json(jsonApiError("name is required", 400));
|
|
1253
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1254
|
+
db.db.prepare(
|
|
1255
|
+
`INSERT INTO entities (name, type, description, repo, owner, created_at, updated_at)
|
|
1256
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
1257
|
+
).run(name, type || "unknown", description || null, repo || "", owner || "", now, now);
|
|
1258
|
+
const entity = db.db.prepare("SELECT * FROM entities WHERE name = ?").get(name);
|
|
1259
|
+
res.json(jsonApiRes(entity, "entity"));
|
|
1260
|
+
} catch (err) {
|
|
1261
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
1262
|
+
res.status(500).json(jsonApiError(message));
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
static async deleteEntity(req, res) {
|
|
1266
|
+
try {
|
|
1267
|
+
await db.refresh();
|
|
1268
|
+
const name = req.params.name;
|
|
1269
|
+
const existing = db.db.prepare("SELECT name FROM entities WHERE name = ?").get(name);
|
|
1270
|
+
if (!existing) return res.status(404).json(jsonApiError("Entity not found", 404));
|
|
1271
|
+
db.db.prepare("DELETE FROM entities WHERE name = ?").run(name);
|
|
1272
|
+
res.json(jsonApiRes({ message: "Deleted", name }, "status"));
|
|
1273
|
+
} catch (err) {
|
|
1274
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
1275
|
+
res.status(500).json(jsonApiError(message));
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
static async createRelation(req, res) {
|
|
1279
|
+
try {
|
|
1280
|
+
await db.refresh();
|
|
1281
|
+
const attributes = getAttributes(req);
|
|
1282
|
+
const { from_entity, to_entity, relation_type, repo, owner } = attributes;
|
|
1283
|
+
if (!from_entity || !to_entity || !relation_type) {
|
|
1284
|
+
return res.status(400).json(jsonApiError("from_entity, to_entity, and relation_type are required", 400));
|
|
1285
|
+
}
|
|
1286
|
+
const fromExists = db.db.prepare("SELECT 1 FROM entities WHERE name = ?").get(from_entity);
|
|
1287
|
+
if (!fromExists) {
|
|
1288
|
+
return res.status(400).json(jsonApiError(`Source entity '${from_entity}' not found`, 400));
|
|
1289
|
+
}
|
|
1290
|
+
const toExists = db.db.prepare("SELECT 1 FROM entities WHERE name = ?").get(to_entity);
|
|
1291
|
+
if (!toExists) {
|
|
1292
|
+
return res.status(400).json(jsonApiError(`Target entity '${to_entity}' not found`, 400));
|
|
1293
|
+
}
|
|
1294
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1295
|
+
db.db.prepare(
|
|
1296
|
+
`INSERT INTO relations (from_entity, to_entity, relation_type, repo, owner, created_at)
|
|
1297
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
1298
|
+
).run(from_entity, to_entity, relation_type, repo || "", owner || "", now);
|
|
1299
|
+
res.json(jsonApiRes({ from_entity, to_entity, relation_type }, "relation"));
|
|
1300
|
+
} catch (err) {
|
|
1301
|
+
const sqlerr = err;
|
|
1302
|
+
if (sqlerr.code === "SQLITE_CONSTRAINT_PRIMARYKEY") {
|
|
1303
|
+
return res.status(409).json(jsonApiError("Relation already exists", 409));
|
|
1304
|
+
}
|
|
1305
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
1306
|
+
res.status(500).json(jsonApiError(message));
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
static async deleteRelation(req, res) {
|
|
1310
|
+
try {
|
|
1311
|
+
await db.refresh();
|
|
1312
|
+
const attributes = getAttributes(req);
|
|
1313
|
+
const { from_entity, to_entity, relation_type } = attributes;
|
|
1314
|
+
if (!from_entity || !to_entity || !relation_type) {
|
|
1315
|
+
return res.status(400).json(jsonApiError("from_entity, to_entity, and relation_type are required", 400));
|
|
1316
|
+
}
|
|
1317
|
+
const result = db.db.prepare("DELETE FROM relations WHERE from_entity = ? AND to_entity = ? AND relation_type = ?").run(from_entity, to_entity, relation_type);
|
|
1318
|
+
if (result.changes === 0) {
|
|
1319
|
+
return res.status(404).json(jsonApiError("Relation not found", 404));
|
|
1320
|
+
}
|
|
1321
|
+
res.json(jsonApiRes({ message: "Deleted" }, "status"));
|
|
1322
|
+
} catch (err) {
|
|
1323
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
1324
|
+
res.status(500).json(jsonApiError(message));
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
static async deleteObservation(req, res) {
|
|
1328
|
+
try {
|
|
1329
|
+
await db.refresh();
|
|
1330
|
+
const id = req.params.id;
|
|
1331
|
+
const result = db.db.prepare("DELETE FROM observations WHERE id = ?").run(id);
|
|
1332
|
+
if (result.changes === 0) {
|
|
1333
|
+
return res.status(404).json(jsonApiError("Observation not found", 404));
|
|
1334
|
+
}
|
|
1335
|
+
res.json(jsonApiRes({ message: "Deleted", id }, "status"));
|
|
1336
|
+
} catch (err) {
|
|
1337
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
1338
|
+
res.status(500).json(jsonApiError(message));
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
|
|
1343
|
+
// src/dashboard/routes/kg.routes.ts
|
|
1302
1344
|
var router6 = Router6();
|
|
1303
|
-
router6.
|
|
1304
|
-
router6.
|
|
1305
|
-
router6.
|
|
1306
|
-
router6.
|
|
1307
|
-
router6.
|
|
1308
|
-
|
|
1345
|
+
router6.get("/kg/entities", KGController.listEntities);
|
|
1346
|
+
router6.get("/kg/entities/:name", KGController.getEntity);
|
|
1347
|
+
router6.post("/kg/entities", KGController.createEntity);
|
|
1348
|
+
router6.delete("/kg/entities/:name", KGController.deleteEntity);
|
|
1349
|
+
router6.get("/kg/relations", KGController.listRelations);
|
|
1350
|
+
router6.post("/kg/relations", KGController.createRelation);
|
|
1351
|
+
router6.delete("/kg/relations", KGController.deleteRelation);
|
|
1352
|
+
router6.delete("/kg/observations/:id", KGController.deleteObservation);
|
|
1353
|
+
router6.get("/kg/graph", KGController.listGraph);
|
|
1354
|
+
var kg_routes_default = router6;
|
|
1355
|
+
|
|
1356
|
+
// src/dashboard/routes/index.ts
|
|
1357
|
+
var router7 = Router7();
|
|
1358
|
+
router7.use("/", system_routes_default);
|
|
1359
|
+
router7.use("/memories", memory_routes_default);
|
|
1360
|
+
router7.use("/tasks", task_routes_default);
|
|
1361
|
+
router7.use("/standards", standard_routes_default);
|
|
1362
|
+
router7.use("/coordination", coordination_routes_default);
|
|
1363
|
+
router7.use("/", kg_routes_default);
|
|
1364
|
+
var routes_default = router7;
|
|
1309
1365
|
|
|
1310
1366
|
// src/dashboard/server.ts
|
|
1311
1367
|
var __dirname3 = path3.dirname(fileURLToPath3(import.meta.url));
|
|
@@ -1414,7 +1470,7 @@ app.use((req, res, next) => {
|
|
|
1414
1470
|
`);
|
|
1415
1471
|
}
|
|
1416
1472
|
});
|
|
1417
|
-
app.use((err, req, res,
|
|
1473
|
+
app.use((err, req, res, _next) => {
|
|
1418
1474
|
if (err.status === 404) return res.status(404).end();
|
|
1419
1475
|
logger.error("Unhandled error", { error: err.message });
|
|
1420
1476
|
res.status(500).end();
|