shieldcortex 3.0.2 → 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 +15 -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/{wWaw0Bi8k5Hc3R8vWNUFY → BEvyMAX62LQMyt5iSb-F9}/_buildManifest.js +0 -0
  90. /package/dashboard/.next/standalone/dashboard/.next/static/{wWaw0Bi8k5Hc3R8vWNUFY → BEvyMAX62LQMyt5iSb-F9}/_clientMiddlewareManifest.json +0 -0
  91. /package/dashboard/.next/standalone/dashboard/.next/static/{wWaw0Bi8k5Hc3R8vWNUFY → BEvyMAX62LQMyt5iSb-F9}/_ssgManifest.js +0 -0
@@ -0,0 +1,502 @@
1
+ import { getDatabase } from '../../database/init.js';
2
+ import { getCloudConfig } from '../../cloud/config.js';
3
+ import { queryAgentOperations, queryAgentRegistry, queryAgentTimeline, queryAuditLogs, getAuditStats } from '../../defence/audit/queries.js';
4
+ import { getLicense, activateLicense, deactivateLicense } from '../../license/store.js';
5
+ import { listFeatures } from '../../license/gate.js';
6
+ import { validateOnceNow } from '../../license/validate.js';
7
+ export function registerAdminRoutes(app, deps) {
8
+ const { brainWorker, requireNotLocked, requireProFeature } = deps;
9
+ app.get('/api/v1/audit', (req, res) => {
10
+ try {
11
+ const options = {};
12
+ if (req.query.startTime)
13
+ options.startTime = req.query.startTime;
14
+ if (req.query.endTime)
15
+ options.endTime = req.query.endTime;
16
+ if (req.query.source)
17
+ options.source = req.query.source;
18
+ if (req.query.firewallResult)
19
+ options.firewallResult = req.query.firewallResult;
20
+ if (req.query.limit)
21
+ options.limit = parseInt(req.query.limit, 10);
22
+ if (req.query.project)
23
+ options.project = req.query.project;
24
+ const logs = queryAuditLogs(options);
25
+ res.json({ logs, total: logs.length });
26
+ }
27
+ catch (error) {
28
+ res.status(500).json({ error: error.message });
29
+ }
30
+ });
31
+ app.get('/api/v1/audit/stats', (req, res) => {
32
+ try {
33
+ const timeRange = req.query.timeRange ?? '24h';
34
+ const project = req.query.project;
35
+ res.json(getAuditStats(timeRange, project));
36
+ }
37
+ catch (error) {
38
+ res.status(500).json({ error: error.message });
39
+ }
40
+ });
41
+ app.get('/api/v1/agents', (req, res) => {
42
+ try {
43
+ const timeRange = req.query.timeRange ?? '24h';
44
+ const project = req.query.project;
45
+ res.json({ agents: queryAgentRegistry(timeRange, project) });
46
+ }
47
+ catch (error) {
48
+ res.status(500).json({ error: error.message });
49
+ }
50
+ });
51
+ app.get('/api/v1/agents/:identifier/timeline', (req, res) => {
52
+ try {
53
+ const identifier = decodeURIComponent(req.params.identifier);
54
+ const timeRange = req.query.timeRange ?? '24h';
55
+ const project = req.query.project;
56
+ res.json({ points: queryAgentTimeline(identifier, timeRange, project) });
57
+ }
58
+ catch (error) {
59
+ res.status(500).json({ error: error.message });
60
+ }
61
+ });
62
+ app.get('/api/v1/agents/:identifier/operations', (req, res) => {
63
+ try {
64
+ const identifier = decodeURIComponent(req.params.identifier);
65
+ const limit = parseInt(req.query.limit, 10) || 50;
66
+ const offset = parseInt(req.query.offset, 10) || 0;
67
+ const firewallResult = req.query.firewallResult;
68
+ const project = req.query.project;
69
+ res.json({
70
+ entries: queryAgentOperations(identifier, { limit, offset, project, firewallResult }),
71
+ limit,
72
+ offset,
73
+ });
74
+ }
75
+ catch (error) {
76
+ res.status(500).json({ error: error.message });
77
+ }
78
+ });
79
+ app.get('/api/v1/quarantine', (req, res) => {
80
+ try {
81
+ const db = getDatabase();
82
+ const status = req.query.status ?? 'pending';
83
+ const limit = parseInt(req.query.limit, 10) || 50;
84
+ const project = req.query.project;
85
+ const sql = project
86
+ ? 'SELECT * FROM quarantine WHERE status = ? AND project = ? ORDER BY created_at DESC LIMIT ?'
87
+ : 'SELECT * FROM quarantine WHERE status = ? ORDER BY created_at DESC LIMIT ?';
88
+ const params = project ? [status, project, limit] : [status, limit];
89
+ const rows = db.prepare(sql).all(...params);
90
+ res.json({
91
+ items: rows.map((row) => ({
92
+ ...row,
93
+ title: row.original_title,
94
+ content: row.original_content,
95
+ })),
96
+ total: rows.length,
97
+ });
98
+ }
99
+ catch (error) {
100
+ res.status(500).json({ error: error.message });
101
+ }
102
+ });
103
+ app.post('/api/v1/quarantine/:id/approve', requireNotLocked, (req, res) => {
104
+ try {
105
+ const db = getDatabase();
106
+ const id = parseInt(req.params.id, 10);
107
+ const reviewedBy = req.body?.reviewedBy ?? 'api';
108
+ const result = db.prepare('UPDATE quarantine SET status = ?, reviewed_at = ?, reviewed_by = ? WHERE id = ? AND status = ?').run('approved', new Date().toISOString(), reviewedBy, id, 'pending');
109
+ if (result.changes === 0) {
110
+ return res.status(404).json({ error: 'Quarantine entry not found or already reviewed' });
111
+ }
112
+ res.json({ success: true, id, status: 'approved' });
113
+ }
114
+ catch (error) {
115
+ res.status(500).json({ error: error.message });
116
+ }
117
+ });
118
+ app.post('/api/v1/quarantine/:id/reject', requireNotLocked, (req, res) => {
119
+ try {
120
+ const db = getDatabase();
121
+ const id = parseInt(req.params.id, 10);
122
+ const reviewedBy = req.body?.reviewedBy ?? 'api';
123
+ const result = db.prepare('UPDATE quarantine SET status = ?, reviewed_at = ?, reviewed_by = ? WHERE id = ? AND status = ?').run('rejected', new Date().toISOString(), reviewedBy, id, 'pending');
124
+ if (result.changes === 0) {
125
+ return res.status(404).json({ error: 'Quarantine entry not found or already reviewed' });
126
+ }
127
+ res.json({ success: true, id, status: 'rejected' });
128
+ }
129
+ catch (error) {
130
+ res.status(500).json({ error: error.message });
131
+ }
132
+ });
133
+ app.post('/api/v1/quarantine/bulk-approve', requireNotLocked, (req, res) => {
134
+ try {
135
+ const db = getDatabase();
136
+ const ids = req.body?.ids;
137
+ if (!Array.isArray(ids) || ids.length === 0) {
138
+ return res.status(400).json({ error: 'ids must be a non-empty array of numbers' });
139
+ }
140
+ const reviewedBy = req.body?.reviewedBy ?? 'dashboard';
141
+ const now = new Date().toISOString();
142
+ const stmt = db.prepare('UPDATE quarantine SET status = ?, reviewed_at = ?, reviewed_by = ? WHERE id = ? AND status = ?');
143
+ let updated = 0;
144
+ db.transaction(() => {
145
+ for (const id of ids) {
146
+ updated += stmt.run('approved', now, reviewedBy, id, 'pending').changes;
147
+ }
148
+ })();
149
+ res.json({ success: true, updated, total: ids.length });
150
+ }
151
+ catch (error) {
152
+ res.status(500).json({ error: error.message });
153
+ }
154
+ });
155
+ app.post('/api/v1/quarantine/bulk-reject', requireNotLocked, (req, res) => {
156
+ try {
157
+ const db = getDatabase();
158
+ const ids = req.body?.ids;
159
+ if (!Array.isArray(ids) || ids.length === 0) {
160
+ return res.status(400).json({ error: 'ids must be a non-empty array of numbers' });
161
+ }
162
+ const reviewedBy = req.body?.reviewedBy ?? 'dashboard';
163
+ const now = new Date().toISOString();
164
+ const stmt = db.prepare('UPDATE quarantine SET status = ?, reviewed_at = ?, reviewed_by = ? WHERE id = ? AND status = ?');
165
+ let updated = 0;
166
+ db.transaction(() => {
167
+ for (const id of ids) {
168
+ updated += stmt.run('rejected', now, reviewedBy, id, 'pending').changes;
169
+ }
170
+ })();
171
+ res.json({ success: true, updated, total: ids.length });
172
+ }
173
+ catch (error) {
174
+ res.status(500).json({ error: error.message });
175
+ }
176
+ });
177
+ app.post('/api/quarantine/sync-to-cloud', requireNotLocked, async (_req, res) => {
178
+ try {
179
+ const config = getCloudConfig();
180
+ if (!config.cloudEnabled || !config.cloudApiKey) {
181
+ return res.status(400).json({ error: 'Cloud not configured. Enable cloud sync first.' });
182
+ }
183
+ const db = getDatabase();
184
+ const rows = db.prepare('SELECT * FROM quarantine WHERE status = ? ORDER BY created_at ASC')
185
+ .all('pending');
186
+ if (rows.length === 0) {
187
+ return res.json({ synced: 0, message: 'No pending quarantine items to sync.' });
188
+ }
189
+ let synced = 0;
190
+ const errors = [];
191
+ for (const row of rows) {
192
+ try {
193
+ const indicators = (() => {
194
+ try {
195
+ return JSON.parse(row.threat_indicators ?? '[]');
196
+ }
197
+ catch {
198
+ return [];
199
+ }
200
+ })();
201
+ const response = await fetch(`${config.cloudBaseUrl}/v1/quarantine/ingest`, {
202
+ method: 'POST',
203
+ headers: {
204
+ 'Content-Type': 'application/json',
205
+ Authorization: `Bearer ${config.cloudApiKey}`,
206
+ },
207
+ body: JSON.stringify({
208
+ original_content: row.original_content,
209
+ original_title: row.original_title ?? undefined,
210
+ source_type: row.source_type ?? 'unknown',
211
+ source_identifier: row.source_identifier ?? 'unknown',
212
+ reason: row.reason ?? 'Unknown reason',
213
+ threat_indicators: indicators,
214
+ anomaly_score: row.anomaly_score ?? 0,
215
+ firewall_result: row.firewall_result ?? 'QUARANTINE',
216
+ }),
217
+ signal: AbortSignal.timeout(10_000),
218
+ });
219
+ if (response.ok) {
220
+ synced++;
221
+ }
222
+ else {
223
+ const body = await response.text().catch(() => '');
224
+ errors.push(`Item ${row.id}: ${response.status} ${body.substring(0, 100)}`);
225
+ }
226
+ }
227
+ catch (error) {
228
+ errors.push(`Item ${row.id}: ${error.message}`);
229
+ }
230
+ }
231
+ res.json({ synced, total: rows.length, errors: errors.length > 0 ? errors : undefined });
232
+ }
233
+ catch (error) {
234
+ res.status(500).json({ error: error.message });
235
+ }
236
+ });
237
+ app.get('/api/worker/status', (_req, res) => {
238
+ try {
239
+ res.json(brainWorker.getStatus());
240
+ }
241
+ catch (error) {
242
+ res.status(500).json({ error: error.message });
243
+ }
244
+ });
245
+ app.post('/api/worker/trigger-light', requireNotLocked, async (_req, res) => {
246
+ try {
247
+ const result = await brainWorker.triggerLightTick();
248
+ res.json({ success: true, ...result, timestamp: result.timestamp.toISOString() });
249
+ }
250
+ catch (error) {
251
+ res.status(500).json({ error: error.message });
252
+ }
253
+ });
254
+ app.post('/api/worker/trigger-medium', requireNotLocked, async (_req, res) => {
255
+ try {
256
+ const result = await brainWorker.triggerMediumTick();
257
+ res.json({ success: true, ...result, timestamp: result.timestamp.toISOString() });
258
+ }
259
+ catch (error) {
260
+ res.status(500).json({ error: error.message });
261
+ }
262
+ });
263
+ app.get('/api/license/status', (_req, res) => {
264
+ try {
265
+ const info = getLicense();
266
+ res.json({
267
+ tier: info.tier,
268
+ valid: info.valid,
269
+ email: info.email,
270
+ expiresAt: info.expiresAt?.toISOString() ?? null,
271
+ daysUntilExpiry: info.daysUntilExpiry,
272
+ teamId: info.teamId,
273
+ features: listFeatures(),
274
+ });
275
+ }
276
+ catch (error) {
277
+ res.status(500).json({ error: error.message });
278
+ }
279
+ });
280
+ app.post('/api/license/activate', async (req, res) => {
281
+ try {
282
+ const { key } = req.body;
283
+ if (!key || typeof key !== 'string') {
284
+ return res.status(400).json({ error: 'License key is required' });
285
+ }
286
+ const info = activateLicense(key.trim());
287
+ const validationStatus = await validateOnceNow();
288
+ res.json({
289
+ success: true,
290
+ tier: info.tier,
291
+ valid: info.valid,
292
+ email: info.email,
293
+ expiresAt: info.expiresAt?.toISOString() ?? null,
294
+ daysUntilExpiry: info.daysUntilExpiry,
295
+ validationStatus,
296
+ features: listFeatures(),
297
+ });
298
+ }
299
+ catch (error) {
300
+ res.status(400).json({ error: error.message });
301
+ }
302
+ });
303
+ app.post('/api/license/deactivate', (_req, res) => {
304
+ try {
305
+ deactivateLicense();
306
+ res.json({ success: true, tier: 'free', features: listFeatures() });
307
+ }
308
+ catch (error) {
309
+ res.status(500).json({ error: error.message });
310
+ }
311
+ });
312
+ app.get('/api/firewall-rules', requireProFeature('custom_firewall_rules'), async (_req, res) => {
313
+ try {
314
+ const { listFirewallRules } = await import('../../defence/custom-rules/store.js');
315
+ const rules = listFirewallRules();
316
+ res.json({ rules, total: rules.length });
317
+ }
318
+ catch (error) {
319
+ res.status(500).json({ error: error.message });
320
+ }
321
+ });
322
+ app.post('/api/firewall-rules', requireProFeature('custom_firewall_rules'), async (req, res) => {
323
+ try {
324
+ const { createFirewallRule } = await import('../../defence/custom-rules/store.js');
325
+ const { name, priority, condition_type, condition_value, action } = req.body;
326
+ if (!name || !condition_type || !condition_value || !action) {
327
+ return res.status(400).json({ error: 'name, condition_type, condition_value, and action are required' });
328
+ }
329
+ const rule = createFirewallRule({ name, priority: priority ?? 100, condition_type, condition_value, action });
330
+ res.status(201).json(rule);
331
+ }
332
+ catch (error) {
333
+ const msg = error.message;
334
+ res.status(msg.includes('Maximum') ? 400 : 500).json({ error: msg });
335
+ }
336
+ });
337
+ app.patch('/api/firewall-rules/:id', requireProFeature('custom_firewall_rules'), async (req, res) => {
338
+ try {
339
+ const { updateFirewallRule } = await import('../../defence/custom-rules/store.js');
340
+ const rule = updateFirewallRule(Number(req.params.id), req.body);
341
+ if (!rule)
342
+ return res.status(404).json({ error: 'Rule not found' });
343
+ res.json(rule);
344
+ }
345
+ catch (error) {
346
+ res.status(500).json({ error: error.message });
347
+ }
348
+ });
349
+ app.delete('/api/firewall-rules/:id', requireProFeature('custom_firewall_rules'), async (req, res) => {
350
+ try {
351
+ const { deleteFirewallRule } = await import('../../defence/custom-rules/store.js');
352
+ const deleted = deleteFirewallRule(Number(req.params.id));
353
+ if (!deleted)
354
+ return res.status(404).json({ error: 'Rule not found' });
355
+ res.json({ success: true, id: Number(req.params.id) });
356
+ }
357
+ catch (error) {
358
+ res.status(500).json({ error: error.message });
359
+ }
360
+ });
361
+ app.get('/api/patterns', requireProFeature('custom_injection_patterns'), async (_req, res) => {
362
+ try {
363
+ const { listCustomPatterns } = await import('../../defence/custom-patterns/store.js');
364
+ const patterns = listCustomPatterns();
365
+ res.json({ patterns, total: patterns.length });
366
+ }
367
+ catch (error) {
368
+ res.status(500).json({ error: error.message });
369
+ }
370
+ });
371
+ app.post('/api/patterns', requireProFeature('custom_injection_patterns'), async (req, res) => {
372
+ try {
373
+ const { createCustomPattern, validateRegex } = await import('../../defence/custom-patterns/store.js');
374
+ const { name, category, severity, regex, description } = req.body;
375
+ if (!name || !regex) {
376
+ return res.status(400).json({ error: 'name and regex are required' });
377
+ }
378
+ const validation = validateRegex(regex);
379
+ if (!validation.valid) {
380
+ return res.status(400).json({ error: validation.error });
381
+ }
382
+ const pattern = createCustomPattern({
383
+ name,
384
+ category: category || 'custom',
385
+ severity: severity || 'medium',
386
+ regex,
387
+ description,
388
+ });
389
+ res.status(201).json(pattern);
390
+ }
391
+ catch (error) {
392
+ const msg = error.message;
393
+ const status = msg.includes('Maximum') || msg.includes('Invalid') || msg.includes('rejected') ? 400 : 500;
394
+ res.status(status).json({ error: msg });
395
+ }
396
+ });
397
+ app.delete('/api/patterns/:id', requireProFeature('custom_injection_patterns'), async (req, res) => {
398
+ try {
399
+ const { deleteCustomPattern } = await import('../../defence/custom-patterns/store.js');
400
+ const deleted = deleteCustomPattern(Number(req.params.id));
401
+ if (!deleted)
402
+ return res.status(404).json({ error: 'Pattern not found' });
403
+ res.json({ success: true, id: Number(req.params.id) });
404
+ }
405
+ catch (error) {
406
+ res.status(500).json({ error: error.message });
407
+ }
408
+ });
409
+ app.post('/api/patterns/:id/test', requireProFeature('custom_injection_patterns'), async (req, res) => {
410
+ try {
411
+ const { testPattern } = await import('../../defence/custom-patterns/store.js');
412
+ const { text } = req.body;
413
+ if (!text)
414
+ return res.status(400).json({ error: 'text is required' });
415
+ res.json(testPattern(Number(req.params.id), text));
416
+ }
417
+ catch (error) {
418
+ res.status(500).json({ error: error.message });
419
+ }
420
+ });
421
+ app.get('/api/iron-dome/policies', requireProFeature('custom_iron_dome_policies'), async (_req, res) => {
422
+ try {
423
+ const { listIronDomePolicies } = await import('../../defence/iron-dome/custom-policies.js');
424
+ const policies = listIronDomePolicies();
425
+ res.json({ policies, total: policies.length });
426
+ }
427
+ catch (error) {
428
+ res.status(500).json({ error: error.message });
429
+ }
430
+ });
431
+ app.post('/api/iron-dome/policies', requireProFeature('custom_iron_dome_policies'), async (req, res) => {
432
+ try {
433
+ const { createIronDomePolicy } = await import('../../defence/iron-dome/custom-policies.js');
434
+ const { name, description, config } = req.body;
435
+ if (!name)
436
+ return res.status(400).json({ error: 'name is required' });
437
+ res.status(201).json(createIronDomePolicy({ name, description, config: config || {} }));
438
+ }
439
+ catch (error) {
440
+ const msg = error.message;
441
+ res.status(msg.includes('Maximum') ? 400 : 500).json({ error: msg });
442
+ }
443
+ });
444
+ app.delete('/api/iron-dome/policies/:id', requireProFeature('custom_iron_dome_policies'), async (req, res) => {
445
+ try {
446
+ const { deleteIronDomePolicy } = await import('../../defence/iron-dome/custom-policies.js');
447
+ const deleted = deleteIronDomePolicy(Number(req.params.id));
448
+ if (!deleted)
449
+ return res.status(404).json({ error: 'Policy not found' });
450
+ res.json({ success: true, id: Number(req.params.id) });
451
+ }
452
+ catch (error) {
453
+ res.status(500).json({ error: error.message });
454
+ }
455
+ });
456
+ app.put('/api/iron-dome/policies/:id/activate', requireProFeature('custom_iron_dome_policies'), async (req, res) => {
457
+ try {
458
+ const { activateIronDomePolicy } = await import('../../defence/iron-dome/custom-policies.js');
459
+ const policy = activateIronDomePolicy(Number(req.params.id));
460
+ if (!policy)
461
+ return res.status(404).json({ error: 'Policy not found' });
462
+ res.json(policy);
463
+ }
464
+ catch (error) {
465
+ res.status(500).json({ error: error.message });
466
+ }
467
+ });
468
+ app.get('/api/audit/export', requireProFeature('audit_export'), async (req, res) => {
469
+ try {
470
+ const { exportAuditJSON, exportAuditCSV } = await import('../../defence/audit/export.js');
471
+ const format = req.query.format || 'json';
472
+ const startTime = req.query.startTime;
473
+ const endTime = req.query.endTime;
474
+ if (format === 'csv') {
475
+ const csv = exportAuditCSV(startTime, endTime);
476
+ res.setHeader('Content-Type', 'text/csv');
477
+ res.setHeader('Content-Disposition', `attachment; filename="shieldcortex-audit-${Date.now()}.csv"`);
478
+ return res.send(csv);
479
+ }
480
+ const json = exportAuditJSON(startTime, endTime);
481
+ res.setHeader('Content-Type', 'application/json');
482
+ res.setHeader('Content-Disposition', `attachment; filename="shieldcortex-audit-${Date.now()}.json"`);
483
+ res.send(json);
484
+ }
485
+ catch (error) {
486
+ res.status(500).json({ error: error.message });
487
+ }
488
+ });
489
+ app.post('/api/skills/deep-scan', requireProFeature('skill_scanner_deep'), async (req, res) => {
490
+ try {
491
+ const { runDeepScan } = await import('../../defence/skill-scanner/deep-scan.js');
492
+ const { files } = req.body;
493
+ if (!files || !Array.isArray(files) || files.length === 0) {
494
+ return res.status(400).json({ error: 'files array is required (each with name and content)' });
495
+ }
496
+ res.json(await runDeepScan(files));
497
+ }
498
+ catch (error) {
499
+ res.status(500).json({ error: error.message });
500
+ }
501
+ });
502
+ }
@@ -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 registerGraphRoutes(app: Express, requireNotLocked: Middleware): void;
4
+ export {};