shieldcortex 3.0.3 → 3.1.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.
Files changed (135) hide show
  1. package/README.md +5 -2
  2. package/dashboard/.next/standalone/dashboard/.next/BUILD_ID +1 -1
  3. package/dashboard/.next/standalone/dashboard/.next/build-manifest.json +2 -2
  4. package/dashboard/.next/standalone/dashboard/.next/prerender-manifest.json +3 -3
  5. package/dashboard/.next/standalone/dashboard/.next/required-server-files.json +4 -4
  6. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.html +2 -2
  7. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.rsc +1 -1
  8. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.html +1 -1
  15. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.rsc +2 -2
  16. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  17. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  18. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  19. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  20. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  21. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  22. package/dashboard/.next/standalone/dashboard/.next/server/app/index.html +1 -1
  23. package/dashboard/.next/standalone/dashboard/.next/server/app/index.rsc +3 -3
  24. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  25. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_full.segment.rsc +3 -3
  26. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_head.segment.rsc +1 -1
  27. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_index.segment.rsc +2 -2
  28. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  29. package/dashboard/.next/standalone/dashboard/.next/server/app/page/react-loadable-manifest.json +1 -1
  30. package/dashboard/.next/standalone/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
  31. package/dashboard/.next/standalone/dashboard/.next/server/chunks/ssr/dashboard_3051539d._.js +1 -1
  32. package/dashboard/.next/standalone/dashboard/.next/server/pages/404.html +1 -1
  33. package/dashboard/.next/standalone/dashboard/.next/server/pages/500.html +2 -2
  34. package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.js +1 -1
  35. package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.json +1 -1
  36. package/dashboard/.next/standalone/dashboard/.next/static/chunks/0a69eb25d08447ee.js +1 -0
  37. package/dashboard/.next/standalone/dashboard/.next/static/chunks/9232a2d99b47b21f.js +3 -0
  38. package/dashboard/.next/standalone/dashboard/.next/static/chunks/97537d3db46c8467.css +3 -0
  39. package/dashboard/.next/standalone/dashboard/.next/static/chunks/aa6e9b8a52353969.js +9 -0
  40. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-darwin-arm64/lib/sharp-darwin-arm64.node +0 -0
  41. package/dashboard/.next/standalone/{node_modules/@img/sharp-linux-x64 → dashboard/node_modules/@img/sharp-darwin-arm64}/package.json +7 -13
  42. package/dashboard/.next/standalone/dashboard/node_modules/@img/{sharp-libvips-linux-x64 → sharp-libvips-darwin-arm64}/README.md +2 -2
  43. package/dashboard/.next/standalone/dashboard/node_modules/@img/{sharp-libvips-linux-x64 → sharp-libvips-darwin-arm64}/lib/glib-2.0/include/glibconfig.h +8 -9
  44. package/dashboard/.next/standalone/dashboard/node_modules/@img/{sharp-libvips-linux-x64/lib/libvips-cpp.so.8.17.3 → sharp-libvips-darwin-arm64/lib/libvips-cpp.8.17.3.dylib} +0 -0
  45. package/dashboard/.next/standalone/dashboard/node_modules/@img/{sharp-libvips-linux-x64 → sharp-libvips-darwin-arm64}/package.json +5 -11
  46. package/dashboard/.next/standalone/dashboard/server.js +1 -1
  47. package/dashboard/.next/standalone/{dashboard/node_modules/@img/sharp-linux-x64 → node_modules/@img/sharp-darwin-arm64}/package.json +7 -13
  48. package/dashboard/.next/standalone/node_modules/@img/{sharp-libvips-linux-x64 → sharp-libvips-darwin-arm64}/package.json +5 -11
  49. package/dist/api/routes/admin.d.ts +12 -0
  50. package/dist/api/routes/admin.js +502 -0
  51. package/dist/api/routes/graph.d.ts +4 -0
  52. package/dist/api/routes/graph.js +333 -0
  53. package/dist/api/routes/incidents.d.ts +2 -0
  54. package/dist/api/routes/incidents.js +32 -0
  55. package/dist/api/routes/memories.d.ts +4 -0
  56. package/dist/api/routes/memories.js +659 -0
  57. package/dist/api/routes/recall.d.ts +4 -0
  58. package/dist/api/routes/recall.js +36 -0
  59. package/dist/api/routes/system.d.ts +9 -0
  60. package/dist/api/routes/system.js +266 -0
  61. package/dist/api/visualization-server.js +31 -1913
  62. package/dist/cloud/cli.d.ts +1 -0
  63. package/dist/cloud/cli.js +40 -0
  64. package/dist/cloud/config.d.ts +10 -0
  65. package/dist/cloud/config.js +54 -0
  66. package/dist/cloud/graph-sync.d.ts +45 -0
  67. package/dist/cloud/graph-sync.js +257 -0
  68. package/dist/cloud/memory-sync.d.ts +36 -0
  69. package/dist/cloud/memory-sync.js +183 -0
  70. package/dist/cloud/sync-queue.d.ts +24 -0
  71. package/dist/cloud/sync-queue.js +126 -7
  72. package/dist/database/init.js +24 -0
  73. package/dist/graph/backfill.js +3 -5
  74. package/dist/graph/resolve.d.ts +10 -0
  75. package/dist/graph/resolve.js +63 -1
  76. package/dist/index.d.ts +2 -0
  77. package/dist/index.js +61 -4
  78. package/dist/memory/search.d.ts +37 -0
  79. package/dist/memory/search.js +143 -0
  80. package/dist/memory/store.js +47 -171
  81. package/dist/memory/types.d.ts +2 -0
  82. package/dist/service/install.d.ts +1 -0
  83. package/dist/service/install.js +43 -1
  84. package/dist/tools/recall.d.ts +1 -1
  85. package/hooks/openclaw/cortex-memory/handler.ts +5 -141
  86. package/hooks/openclaw/cortex-memory/runtime.mjs +129 -0
  87. package/package.json +8 -4
  88. package/plugins/openclaw/dist/index.js +5 -39
  89. package/scripts/run-jest.mjs +25 -1
  90. package/dashboard/.next/standalone/dashboard/.next/static/chunks/be6970da20a17c0b.js +0 -9
  91. package/dashboard/.next/standalone/dashboard/.next/static/chunks/e63d2228780629dd.css +0 -3
  92. package/dashboard/.next/standalone/dashboard/.next/static/chunks/f69fd1c5e71fbbfd.js +0 -1
  93. package/dashboard/.next/standalone/dashboard/.next/static/chunks/fa5217550a8ab9a6.js +0 -3
  94. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-libvips-linuxmusl-x64/README.md +0 -46
  95. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/glib-2.0/include/glibconfig.h +0 -221
  96. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/index.js +0 -1
  97. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/libvips-cpp.so.8.17.3 +0 -0
  98. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-libvips-linuxmusl-x64/package.json +0 -42
  99. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-libvips-linuxmusl-x64/versions.json +0 -30
  100. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-linux-x64/lib/sharp-linux-x64.node +0 -0
  101. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-linuxmusl-x64/lib/sharp-linuxmusl-x64.node +0 -0
  102. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-linuxmusl-x64/package.json +0 -46
  103. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/_tsc.js +0 -133818
  104. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/_tsserver.js +0 -659
  105. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/_typingsInstaller.js +0 -222
  106. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +0 -2122
  107. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/de/diagnosticMessages.generated.json +0 -2122
  108. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/es/diagnosticMessages.generated.json +0 -2122
  109. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +0 -2122
  110. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/it/diagnosticMessages.generated.json +0 -2122
  111. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +0 -2122
  112. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +0 -2122
  113. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +0 -2122
  114. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +0 -2122
  115. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +0 -2122
  116. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +0 -2122
  117. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tsc.js +0 -8
  118. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tsserver.js +0 -8
  119. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tsserverlibrary.js +0 -21
  120. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/typesMap.json +0 -497
  121. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/typescript.js +0 -200276
  122. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/typingsInstaller.js +0 -8
  123. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/watchGuard.js +0 -53
  124. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +0 -2122
  125. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +0 -2122
  126. package/dashboard/.next/standalone/dashboard/node_modules/typescript/package.json +0 -120
  127. package/dashboard/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/package.json +0 -42
  128. package/dashboard/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/package.json +0 -46
  129. package/scripts/start-dashboard.sh +0 -41
  130. package/scripts/stop-dashboard.sh +0 -21
  131. /package/dashboard/.next/standalone/dashboard/.next/static/{THy6JENQ0c1sq6jQhvIDp → RnvqrTXo_jN8SuMdaNcIj}/_buildManifest.js +0 -0
  132. /package/dashboard/.next/standalone/dashboard/.next/static/{THy6JENQ0c1sq6jQhvIDp → RnvqrTXo_jN8SuMdaNcIj}/_clientMiddlewareManifest.json +0 -0
  133. /package/dashboard/.next/standalone/dashboard/.next/static/{THy6JENQ0c1sq6jQhvIDp → RnvqrTXo_jN8SuMdaNcIj}/_ssgManifest.js +0 -0
  134. /package/dashboard/.next/standalone/dashboard/node_modules/@img/{sharp-libvips-linux-x64 → sharp-libvips-darwin-arm64}/lib/index.js +0 -0
  135. /package/dashboard/.next/standalone/dashboard/node_modules/@img/{sharp-libvips-linux-x64 → sharp-libvips-darwin-arm64}/versions.json +0 -0
@@ -0,0 +1,36 @@
1
+ import { searchMemoriesExplained } from '../../memory/store.js';
2
+ export function registerRecallRoutes(app, requireNotLocked) {
3
+ app.get('/api/recall/explain', requireNotLocked, async (req, res) => {
4
+ try {
5
+ const query = typeof req.query.query === 'string' ? req.query.query.trim() : '';
6
+ if (!query) {
7
+ return res.status(400).json({ error: 'query is required' });
8
+ }
9
+ const project = typeof req.query.project === 'string' ? req.query.project : undefined;
10
+ const type = typeof req.query.type === 'string' ? req.query.type : undefined;
11
+ const category = typeof req.query.category === 'string' ? req.query.category : undefined;
12
+ const limit = Math.min(parseInt(req.query.limit, 10) || 10, 50);
13
+ const includeDecayed = req.query.includeDecayed === 'true';
14
+ const includeGlobal = req.query.includeGlobal !== 'false';
15
+ const results = await searchMemoriesExplained({
16
+ query,
17
+ project,
18
+ type: type,
19
+ category: category,
20
+ limit,
21
+ includeDecayed,
22
+ includeGlobal,
23
+ });
24
+ res.json({
25
+ query,
26
+ project: project ?? null,
27
+ total: results.length,
28
+ sideEffects: 'disabled',
29
+ results,
30
+ });
31
+ }
32
+ catch (error) {
33
+ res.status(500).json({ error: error.message });
34
+ }
35
+ });
36
+ }
@@ -0,0 +1,9 @@
1
+ import type { Express } from 'express';
2
+ import { WebSocket } from 'ws';
3
+ import type { MemoryEvent } from '../events.js';
4
+ interface SystemRouteDeps {
5
+ broadcast: (event: MemoryEvent) => void;
6
+ clients: Set<WebSocket>;
7
+ }
8
+ export declare function registerSystemRoutes(app: Express, deps: SystemRouteDeps): void;
9
+ export {};
@@ -0,0 +1,266 @@
1
+ import { WebSocket } from 'ws';
2
+ import { getCloudConfig, getCloudSyncControls, getDeviceId, getDeviceName, getDefenceMode, getOpenClawMemoryConfig, isConfigTampered, readRawConfig, setCloudConfig, setCloudSyncControls, setDefenceMode, setOpenClawMemoryConfig, } from '../../cloud/config.js';
3
+ import { getQueueStats } from '../../cloud/sync-queue.js';
4
+ import { getDatabase } from '../../database/init.js';
5
+ import { getRequiredTier, isFeatureEnabled } from '../../license/gate.js';
6
+ import { getControlStatus, isKillSwitchActive, pause, resume } from '../control.js';
7
+ import { checkForUpdates, getCurrentVersion, getRunningVersion, performUpdate, scheduleRestart, } from '../version.js';
8
+ export function registerSystemRoutes(app, deps) {
9
+ const { broadcast, clients } = deps;
10
+ app.get('/api/control/status', (_req, res) => {
11
+ try {
12
+ res.json(getControlStatus());
13
+ }
14
+ catch (error) {
15
+ res.status(500).json({ error: error.message });
16
+ }
17
+ });
18
+ app.post('/api/control/pause', (_req, res) => {
19
+ try {
20
+ if (isKillSwitchActive()) {
21
+ return res.status(409).json({ error: 'Kill switch is active — use /api/iron-dome/resume to deactivate first', code: 'KILL_SWITCH_ACTIVE' });
22
+ }
23
+ pause();
24
+ res.json({ paused: true, message: 'Memory creation paused' });
25
+ }
26
+ catch (error) {
27
+ res.status(500).json({ error: error.message });
28
+ }
29
+ });
30
+ app.post('/api/control/resume', (_req, res) => {
31
+ try {
32
+ if (isKillSwitchActive()) {
33
+ return res.status(409).json({ error: 'Kill switch is active — use /api/iron-dome/resume to deactivate first', code: 'KILL_SWITCH_ACTIVE' });
34
+ }
35
+ resume();
36
+ res.json({ paused: false, message: 'Memory creation resumed' });
37
+ }
38
+ catch (error) {
39
+ res.status(500).json({ error: error.message });
40
+ }
41
+ });
42
+ app.get('/api/cloud/config', (_req, res) => {
43
+ try {
44
+ const config = getCloudConfig();
45
+ res.json({
46
+ enabled: config.cloudEnabled,
47
+ apiKeySet: !!config.cloudApiKey,
48
+ baseUrl: config.cloudBaseUrl,
49
+ syncControls: getCloudSyncControls(),
50
+ openclawMemory: getOpenClawMemoryConfig(),
51
+ });
52
+ }
53
+ catch (error) {
54
+ res.status(500).json({ error: error.message });
55
+ }
56
+ });
57
+ app.post('/api/cloud/config', (req, res) => {
58
+ try {
59
+ const { cloudApiKey, cloudEnabled, cloudBaseUrl, cloudSyncProjectMode, cloudSyncProjects, cloudSyncContentMode, cloudSyncExcludeSensitive, openclawAutoMemory, openclawAutoMemoryDedupe, openclawAutoMemoryNoveltyThreshold, openclawAutoMemoryMaxRecent, } = req.body;
60
+ if (openclawAutoMemory !== undefined && typeof openclawAutoMemory !== 'boolean') {
61
+ return res.status(400).json({ error: 'openclawAutoMemory must be a boolean' });
62
+ }
63
+ if (openclawAutoMemoryDedupe !== undefined && typeof openclawAutoMemoryDedupe !== 'boolean') {
64
+ return res.status(400).json({ error: 'openclawAutoMemoryDedupe must be a boolean' });
65
+ }
66
+ if (openclawAutoMemoryNoveltyThreshold !== undefined &&
67
+ (typeof openclawAutoMemoryNoveltyThreshold !== 'number' || Number.isNaN(openclawAutoMemoryNoveltyThreshold))) {
68
+ return res.status(400).json({ error: 'openclawAutoMemoryNoveltyThreshold must be a number' });
69
+ }
70
+ if (openclawAutoMemoryMaxRecent !== undefined &&
71
+ (typeof openclawAutoMemoryMaxRecent !== 'number' || Number.isNaN(openclawAutoMemoryMaxRecent))) {
72
+ return res.status(400).json({ error: 'openclawAutoMemoryMaxRecent must be a number' });
73
+ }
74
+ if (cloudSyncProjectMode !== undefined &&
75
+ cloudSyncProjectMode !== 'all' &&
76
+ cloudSyncProjectMode !== 'include' &&
77
+ cloudSyncProjectMode !== 'exclude') {
78
+ return res.status(400).json({ error: 'cloudSyncProjectMode must be all, include, or exclude' });
79
+ }
80
+ if (cloudSyncContentMode !== undefined &&
81
+ cloudSyncContentMode !== 'full' &&
82
+ cloudSyncContentMode !== 'metadata') {
83
+ return res.status(400).json({ error: 'cloudSyncContentMode must be full or metadata' });
84
+ }
85
+ if (cloudSyncExcludeSensitive !== undefined &&
86
+ typeof cloudSyncExcludeSensitive !== 'boolean') {
87
+ return res.status(400).json({ error: 'cloudSyncExcludeSensitive must be a boolean' });
88
+ }
89
+ if (cloudSyncProjects !== undefined &&
90
+ (!Array.isArray(cloudSyncProjects) || cloudSyncProjects.some((value) => typeof value !== 'string'))) {
91
+ return res.status(400).json({ error: 'cloudSyncProjects must be an array of strings' });
92
+ }
93
+ setCloudConfig({
94
+ ...(cloudApiKey !== undefined && { cloudApiKey }),
95
+ ...(cloudEnabled !== undefined && { cloudEnabled }),
96
+ ...(cloudBaseUrl !== undefined && { cloudBaseUrl }),
97
+ });
98
+ setCloudSyncControls({
99
+ ...(cloudSyncProjectMode !== undefined && { projectMode: cloudSyncProjectMode }),
100
+ ...(cloudSyncProjects !== undefined && { projects: cloudSyncProjects }),
101
+ ...(cloudSyncContentMode !== undefined && { contentMode: cloudSyncContentMode }),
102
+ ...(cloudSyncExcludeSensitive !== undefined && { excludeSensitive: cloudSyncExcludeSensitive }),
103
+ });
104
+ setOpenClawMemoryConfig({
105
+ ...(openclawAutoMemory !== undefined && { autoMemory: openclawAutoMemory }),
106
+ ...(openclawAutoMemoryDedupe !== undefined && { dedupe: openclawAutoMemoryDedupe }),
107
+ ...(openclawAutoMemoryNoveltyThreshold !== undefined && { noveltyThreshold: openclawAutoMemoryNoveltyThreshold }),
108
+ ...(openclawAutoMemoryMaxRecent !== undefined && { maxRecent: openclawAutoMemoryMaxRecent }),
109
+ });
110
+ const updated = getCloudConfig();
111
+ res.json({
112
+ success: true,
113
+ enabled: updated.cloudEnabled,
114
+ apiKeySet: !!updated.cloudApiKey,
115
+ baseUrl: updated.cloudBaseUrl,
116
+ syncControls: getCloudSyncControls(),
117
+ openclawMemory: getOpenClawMemoryConfig(),
118
+ });
119
+ }
120
+ catch (error) {
121
+ res.status(500).json({ error: error.message });
122
+ }
123
+ });
124
+ app.get('/api/defence/config', (_req, res) => {
125
+ try {
126
+ res.json({ mode: getDefenceMode(), tampered: isConfigTampered() });
127
+ }
128
+ catch (error) {
129
+ res.status(500).json({ error: error.message });
130
+ }
131
+ });
132
+ app.post('/api/defence/config', (req, res) => {
133
+ try {
134
+ const { mode } = req.body;
135
+ const validModes = ['strict', 'balanced', 'permissive'];
136
+ if (!mode || !validModes.includes(mode)) {
137
+ return res.status(400).json({ error: `Invalid mode. Must be one of: ${validModes.join(', ')}` });
138
+ }
139
+ setDefenceMode(mode);
140
+ res.json({ success: true, mode });
141
+ }
142
+ catch (error) {
143
+ res.status(500).json({ error: error.message });
144
+ }
145
+ });
146
+ app.get('/api/cloud/sync-status', (_req, res) => {
147
+ try {
148
+ const config = getCloudConfig();
149
+ const raw = readRawConfig();
150
+ const queue = getQueueStats();
151
+ res.json({
152
+ enabled: config.cloudEnabled,
153
+ apiKeySet: !!config.cloudApiKey,
154
+ baseUrl: config.cloudBaseUrl,
155
+ featureEnabled: isFeatureEnabled('cloud_sync'),
156
+ requiredTier: getRequiredTier('cloud_sync'),
157
+ controls: getCloudSyncControls(),
158
+ lastSyncAt: (typeof raw.lastSyncAt === 'string' ? raw.lastSyncAt : null),
159
+ device: {
160
+ id: getDeviceId(),
161
+ name: getDeviceName(),
162
+ platform: `${process.platform}/${process.arch}`,
163
+ },
164
+ queue: {
165
+ pending: queue.pending,
166
+ failed: queue.failed,
167
+ synced: queue.synced,
168
+ byKind: queue.byKind,
169
+ oldestPendingAt: queue.oldestPendingAt,
170
+ nextRetryAt: queue.nextRetryAt,
171
+ lastError: queue.lastError,
172
+ lastErrorKind: queue.lastErrorKind,
173
+ },
174
+ });
175
+ }
176
+ catch (error) {
177
+ res.status(500).json({ error: error.message });
178
+ }
179
+ });
180
+ app.get('/api/cloud/projects', (_req, res) => {
181
+ try {
182
+ const db = getDatabase();
183
+ const rows = db.prepare(`
184
+ SELECT project, COUNT(*) as count
185
+ FROM memories
186
+ WHERE project IS NOT NULL AND TRIM(project) != ''
187
+ GROUP BY project
188
+ ORDER BY count DESC, project ASC
189
+ `).all();
190
+ res.json({
191
+ projects: rows.map((row) => ({
192
+ project: row.project,
193
+ count: Number(row.count ?? 0),
194
+ })),
195
+ });
196
+ }
197
+ catch (error) {
198
+ res.status(500).json({ error: error.message });
199
+ }
200
+ });
201
+ app.get('/api/version', (_req, res) => {
202
+ try {
203
+ const version = getCurrentVersion();
204
+ const runningVersion = getRunningVersion();
205
+ res.json({ version, runningVersion, stale: runningVersion !== version });
206
+ }
207
+ catch (error) {
208
+ res.status(500).json({ error: error.message });
209
+ }
210
+ });
211
+ app.get('/api/version/check', async (req, res) => {
212
+ try {
213
+ res.json(await checkForUpdates(req.query.force === 'true'));
214
+ }
215
+ catch (error) {
216
+ res.status(500).json({ error: error.message });
217
+ }
218
+ });
219
+ app.post('/api/version/update', async (_req, res) => {
220
+ try {
221
+ broadcast({
222
+ type: 'update_started',
223
+ timestamp: new Date().toISOString(),
224
+ data: { message: 'Update in progress...' },
225
+ });
226
+ const result = await performUpdate();
227
+ broadcast({
228
+ type: result.success ? 'update_complete' : 'update_failed',
229
+ timestamp: new Date().toISOString(),
230
+ data: result,
231
+ });
232
+ res.json(result);
233
+ }
234
+ catch (error) {
235
+ res.status(500).json({ error: error.message });
236
+ }
237
+ });
238
+ app.post('/api/version/restart', (_req, res) => {
239
+ try {
240
+ broadcast({
241
+ type: 'server_restarting',
242
+ timestamp: new Date().toISOString(),
243
+ data: { message: 'Server restarting in 3 seconds...' },
244
+ });
245
+ for (const client of clients) {
246
+ try {
247
+ if (client.readyState === WebSocket.OPEN) {
248
+ client.send(JSON.stringify({
249
+ type: 'server_restarting',
250
+ timestamp: new Date().toISOString(),
251
+ data: { reconnectIn: 5000 },
252
+ }));
253
+ }
254
+ }
255
+ catch (error) {
256
+ console.error('[shieldcortex] WebSocket send failed during restart:', error);
257
+ }
258
+ }
259
+ res.json({ success: true, message: 'Server will restart in 3 seconds' });
260
+ scheduleRestart(3000);
261
+ }
262
+ catch (error) {
263
+ res.status(500).json({ error: error.message });
264
+ }
265
+ });
266
+ }