shieldcortex 3.0.3 → 3.0.4

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.
Files changed (91) hide show
  1. package/dashboard/.next/standalone/dashboard/.next/BUILD_ID +1 -1
  2. package/dashboard/.next/standalone/dashboard/.next/build-manifest.json +2 -2
  3. package/dashboard/.next/standalone/dashboard/.next/prerender-manifest.json +3 -3
  4. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.html +2 -2
  5. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.rsc +1 -1
  6. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  9. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  10. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.html +1 -1
  13. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.rsc +2 -2
  14. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  15. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  16. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  17. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  18. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  19. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  20. package/dashboard/.next/standalone/dashboard/.next/server/app/index.html +1 -1
  21. package/dashboard/.next/standalone/dashboard/.next/server/app/index.rsc +3 -3
  22. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  23. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_full.segment.rsc +3 -3
  24. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_head.segment.rsc +1 -1
  25. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_index.segment.rsc +2 -2
  26. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  27. package/dashboard/.next/standalone/dashboard/.next/server/app/page/react-loadable-manifest.json +1 -1
  28. package/dashboard/.next/standalone/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
  29. package/dashboard/.next/standalone/dashboard/.next/server/chunks/ssr/dashboard_3051539d._.js +1 -1
  30. package/dashboard/.next/standalone/dashboard/.next/server/pages/404.html +1 -1
  31. package/dashboard/.next/standalone/dashboard/.next/server/pages/500.html +2 -2
  32. package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.js +1 -1
  33. package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.json +1 -1
  34. package/dashboard/.next/standalone/dashboard/.next/static/chunks/313c0d327bbf244a.js +9 -0
  35. package/dashboard/.next/standalone/dashboard/.next/static/chunks/{fa5217550a8ab9a6.js → 49c1cec591af1460.js} +2 -2
  36. package/dashboard/.next/standalone/dashboard/.next/static/chunks/{f69fd1c5e71fbbfd.js → ca21f348cb163905.js} +1 -1
  37. package/dashboard/.next/standalone/dashboard/.next/static/chunks/f4ca424319f58dc7.css +3 -0
  38. package/dist/api/routes/admin.d.ts +12 -0
  39. package/dist/api/routes/admin.js +502 -0
  40. package/dist/api/routes/graph.d.ts +4 -0
  41. package/dist/api/routes/graph.js +333 -0
  42. package/dist/api/routes/incidents.d.ts +2 -0
  43. package/dist/api/routes/incidents.js +32 -0
  44. package/dist/api/routes/memories.d.ts +4 -0
  45. package/dist/api/routes/memories.js +659 -0
  46. package/dist/api/routes/recall.d.ts +4 -0
  47. package/dist/api/routes/recall.js +36 -0
  48. package/dist/api/routes/system.d.ts +9 -0
  49. package/dist/api/routes/system.js +201 -0
  50. package/dist/api/visualization-server.js +31 -1913
  51. package/dist/memory/search.d.ts +37 -0
  52. package/dist/memory/search.js +143 -0
  53. package/dist/memory/store.js +2 -166
  54. package/dist/tools/forget.d.ts +2 -2
  55. package/dist/tools/recall.d.ts +2 -2
  56. package/hooks/openclaw/cortex-memory/handler.ts +5 -141
  57. package/hooks/openclaw/cortex-memory/runtime.mjs +129 -0
  58. package/package.json +8 -4
  59. package/plugins/openclaw/dist/index.js +5 -39
  60. package/scripts/run-jest.mjs +25 -1
  61. package/dashboard/.next/standalone/dashboard/.next/static/chunks/be6970da20a17c0b.js +0 -9
  62. package/dashboard/.next/standalone/dashboard/.next/static/chunks/e63d2228780629dd.css +0 -3
  63. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/_tsc.js +0 -133818
  64. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/_tsserver.js +0 -659
  65. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/_typingsInstaller.js +0 -222
  66. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +0 -2122
  67. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/de/diagnosticMessages.generated.json +0 -2122
  68. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/es/diagnosticMessages.generated.json +0 -2122
  69. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +0 -2122
  70. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/it/diagnosticMessages.generated.json +0 -2122
  71. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +0 -2122
  72. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +0 -2122
  73. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +0 -2122
  74. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +0 -2122
  75. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +0 -2122
  76. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +0 -2122
  77. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tsc.js +0 -8
  78. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tsserver.js +0 -8
  79. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tsserverlibrary.js +0 -21
  80. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/typesMap.json +0 -497
  81. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/typescript.js +0 -200276
  82. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/typingsInstaller.js +0 -8
  83. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/watchGuard.js +0 -53
  84. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +0 -2122
  85. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +0 -2122
  86. package/dashboard/.next/standalone/dashboard/node_modules/typescript/package.json +0 -120
  87. package/scripts/start-dashboard.sh +0 -41
  88. package/scripts/stop-dashboard.sh +0 -21
  89. /package/dashboard/.next/standalone/dashboard/.next/static/{THy6JENQ0c1sq6jQhvIDp → BEvyMAX62LQMyt5iSb-F9}/_buildManifest.js +0 -0
  90. /package/dashboard/.next/standalone/dashboard/.next/static/{THy6JENQ0c1sq6jQhvIDp → BEvyMAX62LQMyt5iSb-F9}/_clientMiddlewareManifest.json +0 -0
  91. /package/dashboard/.next/standalone/dashboard/.next/static/{THy6JENQ0c1sq6jQhvIDp → BEvyMAX62LQMyt5iSb-F9}/_ssgManifest.js +0 -0
@@ -0,0 +1,333 @@
1
+ import { getDatabase } from '../../database/init.js';
2
+ function parseAliases(raw) {
3
+ try {
4
+ return JSON.parse(raw || '[]');
5
+ }
6
+ catch {
7
+ return [];
8
+ }
9
+ }
10
+ export function registerGraphRoutes(app, requireNotLocked) {
11
+ app.get('/api/graph/entities', requireNotLocked, (req, res) => {
12
+ try {
13
+ const db = getDatabase();
14
+ const type = typeof req.query.type === 'string' ? req.query.type : undefined;
15
+ const minMentions = typeof req.query.minMentions === 'string' ? parseInt(req.query.minMentions, 10) : 0;
16
+ const limit = typeof req.query.limit === 'string' ? Math.min(parseInt(req.query.limit, 10), 500) : 100;
17
+ const offset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : 0;
18
+ let whereClause = 'WHERE 1=1';
19
+ const params = [];
20
+ if (type) {
21
+ whereClause += ' AND type = ?';
22
+ params.push(type);
23
+ }
24
+ if (minMentions > 0) {
25
+ whereClause += ' AND memory_count >= ?';
26
+ params.push(minMentions);
27
+ }
28
+ const total = db.prepare(`SELECT COUNT(*) as count FROM entities ${whereClause}`).get(...params).count;
29
+ const rows = db.prepare(`SELECT * FROM entities ${whereClause} ORDER BY memory_count DESC LIMIT ? OFFSET ?`).all(...params, limit, offset);
30
+ res.json({
31
+ entities: rows.map((row) => ({
32
+ id: row.id,
33
+ name: row.name,
34
+ type: row.type,
35
+ memoryCount: row.memory_count ?? 0,
36
+ aliases: parseAliases(row.aliases),
37
+ createdAt: row.created_at,
38
+ updatedAt: row.updated_at,
39
+ })),
40
+ total,
41
+ offset,
42
+ limit,
43
+ hasMore: offset + limit < total,
44
+ });
45
+ }
46
+ catch (error) {
47
+ res.status(500).json({ error: error.message });
48
+ }
49
+ });
50
+ app.get('/api/graph/entities/:id/triples', requireNotLocked, (req, res) => {
51
+ try {
52
+ const db = getDatabase();
53
+ const id = parseInt(req.params.id, 10);
54
+ if (Number.isNaN(id)) {
55
+ return res.status(400).json({ error: 'Invalid entity ID' });
56
+ }
57
+ const rows = db.prepare(`
58
+ SELECT t.*, s.name as subject_name, s.type as subject_type,
59
+ o.name as object_name, o.type as object_type
60
+ FROM triples t
61
+ JOIN entities s ON s.id = t.subject_id
62
+ JOIN entities o ON o.id = t.object_id
63
+ WHERE t.subject_id = ? OR t.object_id = ?
64
+ ORDER BY t.created_at DESC
65
+ `).all(id, id);
66
+ res.json({ triples: rows });
67
+ }
68
+ catch (error) {
69
+ res.status(500).json({ error: error.message });
70
+ }
71
+ });
72
+ app.get('/api/graph/entities/:id/memories', requireNotLocked, (req, res) => {
73
+ try {
74
+ const db = getDatabase();
75
+ const id = parseInt(req.params.id, 10);
76
+ if (Number.isNaN(id)) {
77
+ return res.status(400).json({ error: 'Invalid entity ID' });
78
+ }
79
+ const rows = db.prepare(`
80
+ SELECT m.id, m.title, m.type, m.category, m.salience, m.created_at
81
+ FROM memories m
82
+ JOIN memory_entities me ON me.memory_id = m.id
83
+ WHERE me.entity_id = ?
84
+ ORDER BY m.salience DESC, m.created_at DESC
85
+ LIMIT 50
86
+ `).all(id);
87
+ res.json({ memories: rows });
88
+ }
89
+ catch (error) {
90
+ res.status(500).json({ error: error.message });
91
+ }
92
+ });
93
+ app.get('/api/graph/entities/:id/neighbourhood', requireNotLocked, (req, res) => {
94
+ try {
95
+ const db = getDatabase();
96
+ const id = parseInt(req.params.id, 10);
97
+ if (Number.isNaN(id)) {
98
+ return res.status(400).json({ error: 'Invalid entity ID' });
99
+ }
100
+ const focal = db.prepare('SELECT id, name, type, memory_count as memoryCount, aliases FROM entities WHERE id = ?').get(id);
101
+ if (!focal) {
102
+ return res.status(404).json({ error: 'Entity not found' });
103
+ }
104
+ focal.aliases = parseAliases(focal.aliases);
105
+ const triplesAll = db.prepare(`
106
+ SELECT t.id, t.subject_id, t.object_id, t.predicate,
107
+ s.name as subject_name, s.type as subject_type, s.memory_count as subject_count,
108
+ o.name as object_name, o.type as object_type, o.memory_count as object_count
109
+ FROM triples t
110
+ JOIN entities s ON s.id = t.subject_id
111
+ JOIN entities o ON o.id = t.object_id
112
+ WHERE (t.subject_id = ? OR t.object_id = ?)
113
+ ORDER BY
114
+ CASE WHEN t.predicate != 'related_to' THEN 0 ELSE 1 END,
115
+ CASE WHEN t.subject_id = ? THEN o.memory_count ELSE s.memory_count END DESC
116
+ `).all(id, id, id);
117
+ const neighbourIds = new Map();
118
+ const meaningfulTriples = [];
119
+ const relatedToTriples = [];
120
+ for (const triple of triplesAll) {
121
+ const neighbourId = triple.subject_id === id ? triple.object_id : triple.subject_id;
122
+ const count = triple.subject_id === id ? triple.object_count : triple.subject_count;
123
+ if (neighbourId === id)
124
+ continue;
125
+ if (triple.predicate !== 'related_to') {
126
+ meaningfulTriples.push(triple);
127
+ if (!neighbourIds.has(neighbourId)) {
128
+ neighbourIds.set(neighbourId, { predicate: triple.predicate, count });
129
+ }
130
+ }
131
+ else {
132
+ relatedToTriples.push(triple);
133
+ }
134
+ }
135
+ for (const triple of relatedToTriples) {
136
+ if (neighbourIds.size >= 25)
137
+ break;
138
+ const neighbourId = triple.subject_id === id ? triple.object_id : triple.subject_id;
139
+ const count = triple.subject_id === id ? triple.object_count : triple.subject_count;
140
+ if (!neighbourIds.has(neighbourId)) {
141
+ neighbourIds.set(neighbourId, { predicate: 'related_to', count });
142
+ }
143
+ }
144
+ const includedTriples = [
145
+ ...meaningfulTriples.filter((triple) => {
146
+ const neighbourId = triple.subject_id === id ? triple.object_id : triple.subject_id;
147
+ return neighbourIds.has(neighbourId);
148
+ }),
149
+ ...relatedToTriples.filter((triple) => {
150
+ const neighbourId = triple.subject_id === id ? triple.object_id : triple.subject_id;
151
+ return neighbourIds.has(neighbourId);
152
+ }),
153
+ ];
154
+ const seenTriples = new Set();
155
+ const uniqueTriples = includedTriples.filter((triple) => {
156
+ if (seenTriples.has(triple.id))
157
+ return false;
158
+ seenTriples.add(triple.id);
159
+ return true;
160
+ });
161
+ const neighbours = [];
162
+ if (neighbourIds.size > 0) {
163
+ const ids = [...neighbourIds.keys()];
164
+ const placeholders = ids.map(() => '?').join(',');
165
+ const rows = db.prepare(`
166
+ SELECT id, name, type, memory_count as memoryCount, aliases
167
+ FROM entities WHERE id IN (${placeholders})
168
+ `).all(...ids);
169
+ for (const row of rows) {
170
+ row.aliases = parseAliases(row.aliases);
171
+ neighbours.push(row);
172
+ }
173
+ }
174
+ res.json({
175
+ focal,
176
+ neighbours,
177
+ triples: uniqueTriples,
178
+ totalConnections: triplesAll.length,
179
+ });
180
+ }
181
+ catch (error) {
182
+ res.status(500).json({ error: error.message });
183
+ }
184
+ });
185
+ app.get('/api/graph/triples', requireNotLocked, (req, res) => {
186
+ try {
187
+ const db = getDatabase();
188
+ const predicate = typeof req.query.predicate === 'string' ? req.query.predicate : undefined;
189
+ const limit = typeof req.query.limit === 'string' ? Math.min(parseInt(req.query.limit, 10), 10000) : 100;
190
+ const offset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : 0;
191
+ let whereClause = '';
192
+ const params = [];
193
+ if (predicate) {
194
+ whereClause = 'WHERE t.predicate = ?';
195
+ params.push(predicate);
196
+ }
197
+ const total = db.prepare(`SELECT COUNT(*) as count FROM triples t ${whereClause}`).get(...params).count;
198
+ const triples = db.prepare(`
199
+ SELECT t.*, s.name as subject_name, s.type as subject_type,
200
+ o.name as object_name, o.type as object_type
201
+ FROM triples t
202
+ JOIN entities s ON s.id = t.subject_id
203
+ JOIN entities o ON o.id = t.object_id
204
+ ${whereClause}
205
+ ORDER BY t.created_at DESC
206
+ LIMIT ? OFFSET ?
207
+ `).all(...params, limit, offset);
208
+ res.json({ triples, total, offset, limit, hasMore: offset + limit < total });
209
+ }
210
+ catch (error) {
211
+ res.status(500).json({ error: error.message });
212
+ }
213
+ });
214
+ app.get('/api/graph/search', requireNotLocked, (req, res) => {
215
+ try {
216
+ const db = getDatabase();
217
+ const q = typeof req.query.q === 'string' ? req.query.q : '';
218
+ if (!q) {
219
+ return res.status(400).json({ error: 'Query parameter "q" is required' });
220
+ }
221
+ const rows = db.prepare(`SELECT * FROM entities WHERE LOWER(name) LIKE ? ORDER BY memory_count DESC LIMIT 20`).all(`%${q.toLowerCase()}%`);
222
+ res.json({
223
+ entities: rows.map((row) => ({
224
+ id: row.id,
225
+ name: row.name,
226
+ type: row.type,
227
+ memoryCount: row.memory_count ?? 0,
228
+ aliases: parseAliases(row.aliases),
229
+ })),
230
+ });
231
+ }
232
+ catch (error) {
233
+ res.status(500).json({ error: error.message });
234
+ }
235
+ });
236
+ app.get('/api/graph/paths', requireNotLocked, (req, res) => {
237
+ try {
238
+ const db = getDatabase();
239
+ const fromName = typeof req.query.from === 'string' ? req.query.from : '';
240
+ const toName = typeof req.query.to === 'string' ? req.query.to : '';
241
+ if (!fromName || !toName) {
242
+ return res.status(400).json({ error: 'Both "from" and "to" query parameters are required' });
243
+ }
244
+ const fromRow = db.prepare('SELECT * FROM entities WHERE LOWER(name) = LOWER(?)').get(fromName);
245
+ if (!fromRow) {
246
+ return res.status(404).json({ error: `Entity "${fromName}" not found` });
247
+ }
248
+ const toRow = db.prepare('SELECT * FROM entities WHERE LOWER(name) = LOWER(?)').get(toName);
249
+ if (!toRow) {
250
+ return res.status(404).json({ error: `Entity "${toName}" not found` });
251
+ }
252
+ if (fromRow.id === toRow.id) {
253
+ return res.json({ path: [{ entity: fromRow.name, predicate: '(self)' }], sourceMemories: [] });
254
+ }
255
+ const maxDepth = 4;
256
+ const visited = new Map();
257
+ visited.set(fromRow.id, {
258
+ id: fromRow.id,
259
+ name: fromRow.name,
260
+ parentId: null,
261
+ predicate: '',
262
+ sourceMemoryId: null,
263
+ });
264
+ let frontier = [fromRow.id];
265
+ let found = false;
266
+ for (let depth = 0; depth < maxDepth && !found; depth++) {
267
+ const nextFrontier = [];
268
+ for (const nodeId of frontier) {
269
+ const outgoing = db.prepare('SELECT t.object_id as next_id, t.predicate, t.source_memory_id, e.name FROM triples t JOIN entities e ON e.id = t.object_id WHERE t.subject_id = ?').all(nodeId);
270
+ for (const row of outgoing) {
271
+ if (!visited.has(row.next_id)) {
272
+ visited.set(row.next_id, {
273
+ id: row.next_id,
274
+ name: row.name,
275
+ parentId: nodeId,
276
+ predicate: row.predicate,
277
+ sourceMemoryId: row.source_memory_id,
278
+ });
279
+ nextFrontier.push(row.next_id);
280
+ if (row.next_id === toRow.id) {
281
+ found = true;
282
+ break;
283
+ }
284
+ }
285
+ }
286
+ if (found)
287
+ break;
288
+ const incoming = db.prepare('SELECT t.subject_id as next_id, t.predicate, t.source_memory_id, e.name FROM triples t JOIN entities e ON e.id = t.subject_id WHERE t.object_id = ?').all(nodeId);
289
+ for (const row of incoming) {
290
+ if (!visited.has(row.next_id)) {
291
+ visited.set(row.next_id, {
292
+ id: row.next_id,
293
+ name: row.name,
294
+ parentId: nodeId,
295
+ predicate: `~${row.predicate}`,
296
+ sourceMemoryId: row.source_memory_id,
297
+ });
298
+ nextFrontier.push(row.next_id);
299
+ if (row.next_id === toRow.id) {
300
+ found = true;
301
+ break;
302
+ }
303
+ }
304
+ }
305
+ if (found)
306
+ break;
307
+ }
308
+ frontier = nextFrontier;
309
+ if (frontier.length === 0)
310
+ break;
311
+ }
312
+ if (!found) {
313
+ return res.json({ path: [], sourceMemories: [], message: 'No path found' });
314
+ }
315
+ const path = [];
316
+ const sourceMemoryIds = [];
317
+ let current = visited.get(toRow.id);
318
+ while (current) {
319
+ path.unshift({ entity: current.name, predicate: current.predicate });
320
+ if (current.sourceMemoryId)
321
+ sourceMemoryIds.push(current.sourceMemoryId);
322
+ current = current.parentId !== null ? visited.get(current.parentId) : undefined;
323
+ }
324
+ const sourceMemories = sourceMemoryIds.length > 0
325
+ ? db.prepare(`SELECT id, title FROM memories WHERE id IN (${sourceMemoryIds.map(() => '?').join(',')})`).all(...sourceMemoryIds)
326
+ : [];
327
+ res.json({ path, sourceMemories });
328
+ }
329
+ catch (error) {
330
+ res.status(500).json({ error: error.message });
331
+ }
332
+ });
333
+ }
@@ -0,0 +1,2 @@
1
+ import type { Express } from 'express';
2
+ export declare function registerIncidentRoutes(app: Express): void;
@@ -0,0 +1,32 @@
1
+ import { queryIncidentReplay } from '../../defence/audit/queries.js';
2
+ export function registerIncidentRoutes(app) {
3
+ app.get('/api/v1/incidents/replay', (req, res) => {
4
+ try {
5
+ const limit = Math.min(parseInt(req.query.limit, 10) || 200, 500);
6
+ const startTime = typeof req.query.startTime === 'string' ? req.query.startTime : undefined;
7
+ const endTime = typeof req.query.endTime === 'string' ? req.query.endTime : undefined;
8
+ const project = typeof req.query.project === 'string' ? req.query.project : undefined;
9
+ const sourceIdentifier = typeof req.query.sourceIdentifier === 'string' ? req.query.sourceIdentifier : undefined;
10
+ const memoryId = req.query.memoryId ? parseInt(req.query.memoryId, 10) : undefined;
11
+ const events = queryIncidentReplay({
12
+ startTime,
13
+ endTime,
14
+ project,
15
+ sourceIdentifier,
16
+ memoryId,
17
+ limit,
18
+ });
19
+ res.json({
20
+ events,
21
+ total: events.length,
22
+ coverage: {
23
+ sources: ['defence_audit', 'quarantine', 'events'],
24
+ note: 'Replay is best-effort. Durable audit and quarantine history is complete; generic event coverage depends on the retained events table window.',
25
+ },
26
+ });
27
+ }
28
+ catch (error) {
29
+ res.status(500).json({ error: error.message });
30
+ }
31
+ });
32
+ }
@@ -0,0 +1,4 @@
1
+ import type { Express, Request, Response } from 'express';
2
+ type Middleware = (_req: Request, res: Response, next: (err?: unknown) => void) => void;
3
+ export declare function registerMemoryRoutes(app: Express, requireNotLocked: Middleware): void;
4
+ export {};