@pcircle/memesh 4.5.0 → 4.6.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/.claude-plugin/marketplace.json +5 -3
- package/.claude-plugin/plugin.json +6 -4
- package/.mcp.json +1 -1
- package/AGENTS.md +95 -0
- package/README.de.md +130 -39
- package/README.md +188 -43
- package/README.zh-TW.md +133 -41
- package/dashboard/dist/index.html +9 -9
- package/dist/cli/view-live.js +3 -3
- package/dist/core/analytics.d.ts +3 -3
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/core/analytics.js.map +1 -1
- package/dist/core/auto-tagger.d.ts.map +1 -1
- package/dist/core/auto-tagger.js +4 -9
- package/dist/core/auto-tagger.js.map +1 -1
- package/dist/core/briefing.d.ts +8 -0
- package/dist/core/briefing.d.ts.map +1 -0
- package/dist/core/briefing.js +91 -0
- package/dist/core/briefing.js.map +1 -0
- package/dist/core/capture-flag.d.ts +5 -0
- package/dist/core/capture-flag.d.ts.map +1 -0
- package/dist/core/capture-flag.js +10 -0
- package/dist/core/capture-flag.js.map +1 -0
- package/dist/core/config.d.ts +0 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +2 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/conflict-candidates.d.ts +20 -0
- package/dist/core/conflict-candidates.d.ts.map +1 -0
- package/dist/core/conflict-candidates.js +79 -0
- package/dist/core/conflict-candidates.js.map +1 -0
- package/dist/core/conflict-judge.d.ts +47 -0
- package/dist/core/conflict-judge.d.ts.map +1 -0
- package/dist/core/conflict-judge.js +189 -0
- package/dist/core/conflict-judge.js.map +1 -0
- package/dist/core/demo.d.ts +2 -2
- package/dist/core/demo.d.ts.map +1 -1
- package/dist/core/demo.js.map +1 -1
- package/dist/core/digest-validator.d.ts.map +1 -1
- package/dist/core/digest-validator.js +3 -5
- package/dist/core/digest-validator.js.map +1 -1
- package/dist/core/doctor.d.ts +3 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +207 -85
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/dreamer.d.ts +20 -9
- package/dist/core/dreamer.d.ts.map +1 -1
- package/dist/core/dreamer.js +416 -58
- package/dist/core/dreamer.js.map +1 -1
- package/dist/core/embedder.d.ts +7 -5
- package/dist/core/embedder.d.ts.map +1 -1
- package/dist/core/embedder.js +38 -9
- package/dist/core/embedder.js.map +1 -1
- package/dist/core/extractor.d.ts.map +1 -1
- package/dist/core/extractor.js +2 -1
- package/dist/core/extractor.js.map +1 -1
- package/dist/core/failure-analyzer.d.ts.map +1 -1
- package/dist/core/failure-analyzer.js +7 -12
- package/dist/core/failure-analyzer.js.map +1 -1
- package/dist/core/graph.d.ts +4 -4
- package/dist/core/graph.d.ts.map +1 -1
- package/dist/core/graph.js.map +1 -1
- package/dist/core/install-channel.d.ts +1 -1
- package/dist/core/install-channel.d.ts.map +1 -1
- package/dist/core/install-channel.js +16 -5
- package/dist/core/install-channel.js.map +1 -1
- package/dist/core/install-hooks.d.ts +6 -0
- package/dist/core/install-hooks.d.ts.map +1 -1
- package/dist/core/install-hooks.js +0 -0
- package/dist/core/install-hooks.js.map +1 -1
- package/dist/core/json-utils.d.ts +1 -0
- package/dist/core/json-utils.d.ts.map +1 -1
- package/dist/core/json-utils.js +19 -10
- package/dist/core/json-utils.js.map +1 -1
- package/dist/core/kg-backfill.d.ts +3 -4
- package/dist/core/kg-backfill.d.ts.map +1 -1
- package/dist/core/kg-backfill.js +1 -4
- package/dist/core/kg-backfill.js.map +1 -1
- package/dist/core/lesson-engine.d.ts +1 -0
- package/dist/core/lesson-engine.d.ts.map +1 -1
- package/dist/core/lesson-engine.js +1 -0
- package/dist/core/lesson-engine.js.map +1 -1
- package/dist/core/lifecycle.d.ts +4 -4
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +15 -22
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/llm-client.d.ts.map +1 -1
- package/dist/core/llm-client.js +3 -6
- package/dist/core/llm-client.js.map +1 -1
- package/dist/core/llm-telemetry.d.ts +4 -4
- package/dist/core/llm-telemetry.d.ts.map +1 -1
- package/dist/core/llm-telemetry.js +1 -1
- package/dist/core/llm-telemetry.js.map +1 -1
- package/dist/core/memory-tool.d.ts.map +1 -1
- package/dist/core/memory-tool.js +8 -4
- package/dist/core/memory-tool.js.map +1 -1
- package/dist/core/operations.d.ts.map +1 -1
- package/dist/core/operations.js +41 -17
- package/dist/core/operations.js.map +1 -1
- package/dist/core/paths.d.ts +3 -0
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +67 -1
- package/dist/core/paths.js.map +1 -1
- package/dist/core/patterns.d.ts +2 -2
- package/dist/core/patterns.d.ts.map +1 -1
- package/dist/core/patterns.js.map +1 -1
- package/dist/core/project-tags.d.ts +3 -3
- package/dist/core/project-tags.d.ts.map +1 -1
- package/dist/core/project-tags.js.map +1 -1
- package/dist/core/projects.d.ts +2 -2
- package/dist/core/projects.d.ts.map +1 -1
- package/dist/core/projects.js.map +1 -1
- package/dist/core/prompt-safety.d.ts +1 -0
- package/dist/core/prompt-safety.d.ts.map +1 -1
- package/dist/core/prompt-safety.js +7 -0
- package/dist/core/prompt-safety.js.map +1 -1
- package/dist/core/schema-export.d.ts.map +1 -1
- package/dist/core/schema-export.js +27 -30
- package/dist/core/schema-export.js.map +1 -1
- package/dist/core/serializer.d.ts.map +1 -1
- package/dist/core/serializer.js +45 -4
- package/dist/core/serializer.js.map +1 -1
- package/dist/core/setup.d.ts +29 -0
- package/dist/core/setup.d.ts.map +1 -0
- package/dist/core/setup.js +127 -0
- package/dist/core/setup.js.map +1 -0
- package/dist/core/stats.d.ts +2 -2
- package/dist/core/stats.d.ts.map +1 -1
- package/dist/core/stats.js.map +1 -1
- package/dist/core/task-state-store.d.ts +17 -0
- package/dist/core/task-state-store.d.ts.map +1 -0
- package/dist/core/task-state-store.js +45 -0
- package/dist/core/task-state-store.js.map +1 -0
- package/dist/core/task-state.d.ts +19 -0
- package/dist/core/task-state.d.ts.map +1 -0
- package/dist/core/task-state.js +91 -0
- package/dist/core/task-state.js.map +1 -0
- package/dist/core/time-utils.d.ts +2 -0
- package/dist/core/time-utils.d.ts.map +1 -0
- package/dist/core/time-utils.js +14 -0
- package/dist/core/time-utils.js.map +1 -0
- package/dist/core/title.d.ts +5 -0
- package/dist/core/title.d.ts.map +1 -0
- package/dist/core/title.js +14 -0
- package/dist/core/title.js.map +1 -0
- package/dist/core/transcript-extractor.d.ts +5 -6
- package/dist/core/transcript-extractor.d.ts.map +1 -1
- package/dist/core/transcript-extractor.js +4 -24
- package/dist/core/transcript-extractor.js.map +1 -1
- package/dist/core/transcript-source.d.ts.map +1 -1
- package/dist/core/transcript-source.js +2 -3
- package/dist/core/transcript-source.js.map +1 -1
- package/dist/core/types.d.ts +21 -7
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/work-topology.d.ts +33 -0
- package/dist/core/work-topology.d.ts.map +1 -0
- package/dist/core/work-topology.js +183 -0
- package/dist/core/work-topology.js.map +1 -0
- package/dist/db.d.ts +5 -10
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +194 -196
- package/dist/db.js.map +1 -1
- package/dist/knowledge-graph.d.ts +4 -2
- package/dist/knowledge-graph.d.ts.map +1 -1
- package/dist/knowledge-graph.js +68 -49
- package/dist/knowledge-graph.js.map +1 -1
- package/dist/mcp/server.js +2 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/skills-manifest.json +61 -36
- package/dist/storage/conflicts.d.ts +3 -3
- package/dist/storage/conflicts.d.ts.map +1 -1
- package/dist/storage/conflicts.js +2 -7
- package/dist/storage/conflicts.js.map +1 -1
- package/dist/storage/fts-index.d.ts +6 -4
- package/dist/storage/fts-index.d.ts.map +1 -1
- package/dist/storage/fts-index.js +16 -4
- package/dist/storage/fts-index.js.map +1 -1
- package/dist/storage/schema.d.ts +20 -0
- package/dist/storage/schema.d.ts.map +1 -0
- package/dist/storage/schema.js +274 -0
- package/dist/storage/schema.js.map +1 -0
- package/dist/storage/sqlite.d.ts +20 -0
- package/dist/storage/sqlite.d.ts.map +1 -0
- package/dist/storage/sqlite.js +64 -0
- package/dist/storage/sqlite.js.map +1 -0
- package/dist/storage/vector-index.d.ts +3 -0
- package/dist/storage/vector-index.d.ts.map +1 -0
- package/dist/storage/vector-index.js +7 -0
- package/dist/storage/vector-index.js.map +1 -0
- package/dist/transports/cli/cli.d.ts +1 -4
- package/dist/transports/cli/cli.d.ts.map +1 -1
- package/dist/transports/cli/cli.js +494 -76
- package/dist/transports/cli/cli.js.map +1 -1
- package/dist/transports/http/retired-routes.d.ts.map +1 -1
- package/dist/transports/http/retired-routes.js +1 -0
- package/dist/transports/http/retired-routes.js.map +1 -1
- package/dist/transports/http/server.d.ts.map +1 -1
- package/dist/transports/http/server.js +243 -323
- package/dist/transports/http/server.js.map +1 -1
- package/dist/transports/mcp/handlers.d.ts +48 -94
- package/dist/transports/mcp/handlers.d.ts.map +1 -1
- package/dist/transports/mcp/handlers.js +77 -56
- package/dist/transports/mcp/handlers.js.map +1 -1
- package/dist/transports/schemas.d.ts +31 -40
- package/dist/transports/schemas.d.ts.map +1 -1
- package/dist/transports/schemas.js +31 -37
- package/dist/transports/schemas.js.map +1 -1
- package/hooks/hooks.json +0 -10
- package/llms-install.md +138 -0
- package/package.json +19 -18
- package/scripts/hooks/_generated/capture-flag.js +17 -0
- package/scripts/hooks/_generated/core-paths.js +67 -1
- package/scripts/hooks/_generated/fts-index.js +16 -4
- package/scripts/hooks/_generated/schema.js +281 -0
- package/scripts/hooks/_generated/sqlite.js +71 -0
- package/scripts/hooks/_generated/task-state.js +98 -0
- package/scripts/hooks/_generated/time-utils.js +21 -0
- package/scripts/hooks/_generated/title.js +21 -0
- package/scripts/hooks/_generated/work-topology.js +190 -0
- package/scripts/hooks/_shared.js +269 -534
- package/scripts/hooks/post-commit.js +55 -10
- package/scripts/hooks/pre-compact.js +22 -7
- package/scripts/hooks/pre-edit-recall.js +9 -11
- package/scripts/hooks/session-start.js +230 -106
- package/scripts/hooks/session-summary.js +176 -73
- package/scripts/hooks/user-prompt-intent.js +3 -2
- package/skills/memesh/SKILL.md +97 -77
- package/README.es.md +0 -470
- package/README.fr.md +0 -462
- package/README.ja.md +0 -470
- package/README.ko.md +0 -470
- package/README.pt.md +0 -462
- package/README.th.md +0 -463
- package/README.vi.md +0 -462
- package/README.zh-CN.md +0 -469
- package/dist/cli/view.d.ts +0 -3
- package/dist/cli/view.d.ts.map +0 -1
- package/dist/cli/view.js +0 -523
- package/dist/cli/view.js.map +0 -1
- package/dist/core/skill-usage-log.d.ts +0 -11
- package/dist/core/skill-usage-log.d.ts.map +0 -1
- package/dist/core/skill-usage-log.js +0 -125
- package/dist/core/skill-usage-log.js.map +0 -1
- package/dist/core/verifier.d.ts +0 -40
- package/dist/core/verifier.d.ts.map +0 -1
- package/dist/core/verifier.js +0 -206
- package/dist/core/verifier.js.map +0 -1
- package/dist/mcp/launcher.d.ts +0 -3
- package/dist/mcp/launcher.d.ts.map +0 -1
- package/dist/mcp/launcher.js +0 -37
- package/dist/mcp/launcher.js.map +0 -1
- package/scripts/hooks/pre-bash-orchestration-nudge.js +0 -155
- package/scripts/postinstall-rebuild.mjs +0 -41
- package/skills/agentic-orchestration/SKILL.md +0 -399
|
@@ -13,11 +13,10 @@ import { computeAnalytics, computePmAnalytics } from '../../core/analytics.js';
|
|
|
13
13
|
import { computeStats } from '../../core/stats.js';
|
|
14
14
|
import { computeProjects } from '../../core/projects.js';
|
|
15
15
|
import { computeGraph } from '../../core/graph.js';
|
|
16
|
-
import {
|
|
17
|
-
import { RememberSchema as RememberBody, RecallSchema as RecallBody, ForgetSchema as ForgetBody, ExportSchema as ExportBody, ImportSchema as ImportBody, LearnSchema as LearnBody, VerifyAgentWorkSchema as VerifyBody, } from '../schemas.js';
|
|
16
|
+
import { RememberSchema as RememberBody, RecallSchema as RecallBody, ForgetSchema as ForgetBody, ExportSchema as ExportBody, ImportSchema as ImportBody, LearnSchema as LearnBody, } from '../schemas.js';
|
|
18
17
|
import { checkForUpdate, getLastUpdateCheck, getUpdateCheck } from '../../core/version-check.js';
|
|
19
18
|
import { getCurrentInstallChannel, getInstallChannelSupport } from '../../core/install-channel.js';
|
|
20
|
-
import { getDbPath, getMemeshDirFromDbPath } from '../../core/paths.js';
|
|
19
|
+
import { getDbPath, getMemeshDirFromDbPath, redactSecrets, redactUserPaths } from '../../core/paths.js';
|
|
21
20
|
import { RETIRED_ROUTES } from './retired-routes.js';
|
|
22
21
|
import fs from 'fs';
|
|
23
22
|
import path from 'path';
|
|
@@ -204,30 +203,14 @@ app.get('/v1/health', (_req, res) => {
|
|
|
204
203
|
}
|
|
205
204
|
}
|
|
206
205
|
});
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
.replace(/Bearer\s+[A-Za-z0-9._-]{20,}/gi, 'Bearer ***REDACTED***');
|
|
216
|
-
}
|
|
217
|
-
app.get('/v1/doctor', async (_req, res) => {
|
|
218
|
-
try {
|
|
219
|
-
const { runDoctor } = await import('../../core/doctor.js');
|
|
220
|
-
const result = await runDoctor({
|
|
221
|
-
packageRoot,
|
|
222
|
-
packageVersion,
|
|
223
|
-
});
|
|
224
|
-
const safe = JSON.parse(redactSecrets(JSON.stringify(result)));
|
|
225
|
-
res.json({ success: true, data: safe });
|
|
226
|
-
}
|
|
227
|
-
catch (err) {
|
|
228
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
229
|
-
}
|
|
230
|
-
});
|
|
206
|
+
app.get('/v1/doctor', (_req, res) => handleGet(res, async () => {
|
|
207
|
+
const { runDoctor } = await import('../../core/doctor.js');
|
|
208
|
+
const result = await runDoctor({
|
|
209
|
+
packageRoot,
|
|
210
|
+
packageVersion,
|
|
211
|
+
});
|
|
212
|
+
return JSON.parse(redactUserPaths(redactSecrets(JSON.stringify(result))));
|
|
213
|
+
}));
|
|
231
214
|
function requireJsonBody(req, res) {
|
|
232
215
|
if (req.body !== undefined)
|
|
233
216
|
return true;
|
|
@@ -239,53 +222,83 @@ function requireJsonBody(req, res) {
|
|
|
239
222
|
});
|
|
240
223
|
return false;
|
|
241
224
|
}
|
|
242
|
-
|
|
243
|
-
|
|
225
|
+
class HttpError extends Error {
|
|
226
|
+
status;
|
|
227
|
+
code;
|
|
228
|
+
constructor(status, code, message) {
|
|
229
|
+
super(message);
|
|
230
|
+
this.status = status;
|
|
231
|
+
this.code = code;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function sendError(res, err, fallbackStatus, fallbackCode) {
|
|
235
|
+
if (err instanceof HttpError) {
|
|
236
|
+
res.status(err.status).json({ success: false, errorCode: err.code, error: err.message });
|
|
244
237
|
return;
|
|
245
|
-
|
|
238
|
+
}
|
|
239
|
+
res.status(fallbackStatus).json({
|
|
240
|
+
success: false,
|
|
241
|
+
errorCode: fallbackCode,
|
|
242
|
+
error: err instanceof Error ? err.message : String(err),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
function zodErrorText(error) {
|
|
246
|
+
return error.issues.map(i => (i.path.length ? `${i.path.join('.')}: ${i.message}` : i.message)).join('; ');
|
|
247
|
+
}
|
|
248
|
+
function parseQuery(schema, req, res) {
|
|
249
|
+
const parsed = schema.safeParse(req.query);
|
|
250
|
+
if (!parsed.success) {
|
|
251
|
+
res.status(400).json({ success: false, errorCode: 'validation.bad-param', error: zodErrorText(parsed.error) });
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
return parsed.data;
|
|
255
|
+
}
|
|
256
|
+
function requireIdParam(req, res) {
|
|
257
|
+
const id = parseInt(String(req.params.id), 10);
|
|
258
|
+
if (!Number.isInteger(id) || id < 1) {
|
|
259
|
+
res.status(400).json({ success: false, errorCode: 'validation.bad-param', error: 'invalid id' });
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
return id;
|
|
263
|
+
}
|
|
264
|
+
function handlePost(schema, req, res, handler, opts) {
|
|
265
|
+
if (!opts?.allowEmptyBody && !requireJsonBody(req, res))
|
|
266
|
+
return;
|
|
267
|
+
const parsed = schema.safeParse(req.body ?? (opts?.allowEmptyBody ? {} : undefined));
|
|
246
268
|
if (!parsed.success) {
|
|
247
269
|
res.status(400).json({
|
|
248
270
|
success: false,
|
|
249
271
|
errorCode: 'validation.bad-body',
|
|
250
|
-
error: parsed.error
|
|
272
|
+
error: zodErrorText(parsed.error),
|
|
251
273
|
});
|
|
252
274
|
return;
|
|
253
275
|
}
|
|
254
|
-
Promise.resolve(
|
|
276
|
+
Promise.resolve()
|
|
277
|
+
.then(() => handler(parsed.data))
|
|
255
278
|
.then((data) => res.json({ success: true, data }))
|
|
256
|
-
.catch((err) => res
|
|
279
|
+
.catch((err) => sendError(res, err, opts?.errorStatus ?? 400, opts?.errorCode ?? 'operation.failed'));
|
|
257
280
|
}
|
|
258
281
|
function handleGet(res, produce) {
|
|
259
282
|
Promise.resolve()
|
|
260
283
|
.then(produce)
|
|
261
284
|
.then((data) => res.json({ success: true, data }))
|
|
262
|
-
.catch((err) => res
|
|
285
|
+
.catch((err) => sendError(res, err, 500, 'server.internal'));
|
|
263
286
|
}
|
|
264
|
-
app.post('/v1/remember', (req, res) => handlePost(RememberBody, req, res, remember));
|
|
265
|
-
app.post('/v1/recall',
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (!parsed.success) {
|
|
270
|
-
res.status(400).json({ success: false, errorCode: 'validation.bad-body', error: parsed.error.issues.map(i => `${i.path.join('.')}: ${i.message}`).join('; ') });
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
try {
|
|
274
|
-
const { entities, conflicts } = await recallWithConflicts(parsed.data);
|
|
275
|
-
res.json({ success: true, data: conflicts.length > 0 ? { entities, conflicts } : entities });
|
|
276
|
-
}
|
|
277
|
-
catch (err) {
|
|
278
|
-
res.status(400).json({ success: false, errorCode: 'operation.failed', error: err instanceof Error ? err.message : String(err) });
|
|
279
|
-
}
|
|
280
|
-
});
|
|
287
|
+
app.post('/v1/remember', (req, res) => handlePost(RememberBody, req, res, (data) => remember({ ...data, sourceHost: 'http' })));
|
|
288
|
+
app.post('/v1/recall', (req, res) => handlePost(RecallBody, req, res, async (data) => {
|
|
289
|
+
const { entities, conflicts } = await recallWithConflicts(data);
|
|
290
|
+
return conflicts.length > 0 ? { entities, conflicts } : { entities };
|
|
291
|
+
}));
|
|
281
292
|
app.post('/v1/forget', (req, res) => handlePost(ForgetBody, req, res, forget));
|
|
282
293
|
app.post('/v1/consolidate', (_req, res) => {
|
|
283
294
|
res.status(410).json({ success: false, errorCode: 'route.retired', error: RETIRED_ROUTES['/v1/consolidate'] });
|
|
284
295
|
});
|
|
285
296
|
app.post('/v1/export', (req, res) => handlePost(ExportBody, req, res, exportMemories));
|
|
286
297
|
app.post('/v1/import', (req, res) => handlePost(ImportBody, req, res, importMemories));
|
|
287
|
-
app.post('/v1/learn', (req, res) => handlePost(LearnBody, req, res, learn));
|
|
288
|
-
app.post('/v1/verify', (
|
|
298
|
+
app.post('/v1/learn', (req, res) => handlePost(LearnBody, req, res, (data) => learn({ ...data, sourceHost: 'http' })));
|
|
299
|
+
app.post('/v1/verify', (_req, res) => {
|
|
300
|
+
res.status(410).json({ success: false, errorCode: 'route.retired', error: RETIRED_ROUTES['/v1/verify'] });
|
|
301
|
+
});
|
|
289
302
|
const API_KEY_MASK = '***';
|
|
290
303
|
function maskLlmSecrets(obj) {
|
|
291
304
|
const masked = { ...obj };
|
|
@@ -313,16 +326,11 @@ function preserveFallbackApiKeys(incoming, stored) {
|
|
|
313
326
|
return clean;
|
|
314
327
|
});
|
|
315
328
|
}
|
|
316
|
-
app.get('/v1/config', (_req, res) => {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
catch (err) {
|
|
323
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
324
|
-
}
|
|
325
|
-
});
|
|
329
|
+
app.get('/v1/config', (_req, res) => handleGet(res, () => {
|
|
330
|
+
const config = readConfig();
|
|
331
|
+
const caps = detectCapabilities(config);
|
|
332
|
+
return { config: maskLlmSecrets(config), capabilities: maskLlmSecrets(caps) };
|
|
333
|
+
}));
|
|
326
334
|
const ConfigBody = z.object({
|
|
327
335
|
llm: z.union([
|
|
328
336
|
z.object({
|
|
@@ -340,7 +348,6 @@ const ConfigBody = z.object({
|
|
|
340
348
|
})).optional(),
|
|
341
349
|
autoCapture: z.boolean().optional(),
|
|
342
350
|
sessionLimit: z.number().int().min(1).max(100).optional(),
|
|
343
|
-
enableAgenticOrchestration: z.boolean().optional(),
|
|
344
351
|
autoUpdate: z.enum(['off', 'patch', 'minor', 'major']).optional(),
|
|
345
352
|
language: z.string().trim().min(1).max(60)
|
|
346
353
|
.refine((v) => languageValueError(v) === null, {
|
|
@@ -349,108 +356,71 @@ const ConfigBody = z.object({
|
|
|
349
356
|
.optional(),
|
|
350
357
|
setupCompleted: z.boolean().optional(),
|
|
351
358
|
}).strip();
|
|
352
|
-
app.post('/v1/config',
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
res.status(400).json({ success: false, errorCode: 'validation.bad-body', error: parsed.error.issues.map(i => `${i.path.join('.')}: ${i.message}`).join('; ') });
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
try {
|
|
361
|
-
const before = readConfig();
|
|
362
|
-
if (parsed.data.llm && parsed.data.llm.apiKey === API_KEY_MASK) {
|
|
363
|
-
if (before.llm && before.llm.provider === parsed.data.llm.provider && before.llm.apiKey) {
|
|
364
|
-
parsed.data.llm.apiKey = before.llm.apiKey;
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
delete parsed.data.llm.apiKey;
|
|
368
|
-
}
|
|
359
|
+
app.post('/v1/config', (req, res) => handlePost(ConfigBody, req, res, (data) => {
|
|
360
|
+
const before = readConfig();
|
|
361
|
+
if (data.llm && data.llm.apiKey === API_KEY_MASK) {
|
|
362
|
+
if (before.llm && before.llm.provider === data.llm.provider && before.llm.apiKey) {
|
|
363
|
+
data.llm.apiKey = before.llm.apiKey;
|
|
369
364
|
}
|
|
370
|
-
|
|
371
|
-
|
|
365
|
+
else {
|
|
366
|
+
delete data.llm.apiKey;
|
|
372
367
|
}
|
|
373
|
-
const updated = updateConfig(parsed.data);
|
|
374
|
-
res.json({ success: true, data: maskLlmSecrets(updated) });
|
|
375
368
|
}
|
|
376
|
-
|
|
377
|
-
|
|
369
|
+
if (data.llmFallbacks) {
|
|
370
|
+
data.llmFallbacks = preserveFallbackApiKeys(data.llmFallbacks, before.llmFallbacks);
|
|
378
371
|
}
|
|
379
|
-
|
|
372
|
+
const updated = updateConfig(data);
|
|
373
|
+
return maskLlmSecrets(updated);
|
|
374
|
+
}));
|
|
380
375
|
const ConfigTestBody = z.object({
|
|
381
376
|
provider: z.enum(['anthropic', 'openai', 'ollama']),
|
|
382
377
|
apiKey: z.string().max(500).optional(),
|
|
383
378
|
host: z.string().max(500).optional(),
|
|
384
379
|
fallbackIndex: z.number().int().nonnegative().optional(),
|
|
385
380
|
});
|
|
386
|
-
app.post('/v1/config/test',
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
if (!
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const { probeProvider } = await import('../../core/llm-validator.js');
|
|
400
|
-
const { provider, host, fallbackIndex } = parsed.data;
|
|
401
|
-
let { apiKey } = parsed.data;
|
|
402
|
-
if (!apiKey && (provider === 'anthropic' || provider === 'openai')) {
|
|
403
|
-
const existing = readConfig();
|
|
404
|
-
if (typeof fallbackIndex === 'number') {
|
|
405
|
-
const fb = existing.llmFallbacks?.[fallbackIndex];
|
|
406
|
-
if (fb && fb.provider === provider && fb.apiKey)
|
|
407
|
-
apiKey = fb.apiKey;
|
|
408
|
-
}
|
|
409
|
-
else if (existing.llm?.provider === provider && existing.llm.apiKey) {
|
|
410
|
-
apiKey = existing.llm.apiKey;
|
|
411
|
-
}
|
|
381
|
+
app.post('/v1/config/test', (req, res) => handlePost(ConfigTestBody, req, res, async (data) => {
|
|
382
|
+
const { probeProvider } = await import('../../core/llm-validator.js');
|
|
383
|
+
const { provider, host, fallbackIndex } = data;
|
|
384
|
+
let { apiKey } = data;
|
|
385
|
+
if (!apiKey && (provider === 'anthropic' || provider === 'openai')) {
|
|
386
|
+
const existing = readConfig();
|
|
387
|
+
if (typeof fallbackIndex === 'number') {
|
|
388
|
+
const fb = existing.llmFallbacks?.[fallbackIndex];
|
|
389
|
+
if (fb && fb.provider === provider && fb.apiKey)
|
|
390
|
+
apiKey = fb.apiKey;
|
|
391
|
+
}
|
|
392
|
+
else if (existing.llm?.provider === provider && existing.llm.apiKey) {
|
|
393
|
+
apiKey = existing.llm.apiKey;
|
|
412
394
|
}
|
|
413
|
-
const result = await probeProvider(provider, apiKey, host);
|
|
414
|
-
res.json({ success: true, data: result });
|
|
415
|
-
}
|
|
416
|
-
catch (err) {
|
|
417
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
app.get('/v1/update-status', async (req, res) => {
|
|
421
|
-
try {
|
|
422
|
-
const cached = req.query.cached === '1' || req.query.cached === 'true';
|
|
423
|
-
const install = getCurrentInstallChannel({ packageRoot });
|
|
424
|
-
const installSupport = getInstallChannelSupport(install);
|
|
425
|
-
const update = await getUpdateCheck(packageVersion, { preferFresh: !cached });
|
|
426
|
-
res.json({
|
|
427
|
-
success: true,
|
|
428
|
-
data: {
|
|
429
|
-
currentVersion: packageVersion,
|
|
430
|
-
latestVersion: update?.latestVersion ?? null,
|
|
431
|
-
checkedAt: update?.checkedAt ?? null,
|
|
432
|
-
lastAttemptAt: update?.lastAttemptAt ?? null,
|
|
433
|
-
lastSuccessfulCheckAt: update?.lastSuccessfulCheckAt ?? null,
|
|
434
|
-
lastError: update?.lastError ?? null,
|
|
435
|
-
updateAvailable: update?.updateAvailable ?? false,
|
|
436
|
-
checkSucceeded: update?.checkSucceeded ?? false,
|
|
437
|
-
source: update?.source ?? null,
|
|
438
|
-
freshness: update?.freshness ?? 'unavailable',
|
|
439
|
-
installChannel: installSupport.channel,
|
|
440
|
-
canSelfUpdate: installSupport.canSelfUpdate,
|
|
441
|
-
recommendedCommand: (update?.currentVersionDeprecated
|
|
442
|
-
&& update.latestVersion
|
|
443
|
-
&& update.latestVersion === update.currentVersion
|
|
444
|
-
&& update.freshness === 'fresh') ? null : installSupport.recommendedCommand,
|
|
445
|
-
currentVersionDeprecated: update?.currentVersionDeprecated ?? false,
|
|
446
|
-
deprecationMessage: update?.deprecationMessage ?? null,
|
|
447
|
-
},
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
catch (err) {
|
|
451
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
452
395
|
}
|
|
453
|
-
|
|
396
|
+
return probeProvider(provider, apiKey, host);
|
|
397
|
+
}, { errorStatus: 500, errorCode: 'server.internal' }));
|
|
398
|
+
app.get('/v1/update-status', (req, res) => handleGet(res, async () => {
|
|
399
|
+
const cached = req.query.cached === '1' || req.query.cached === 'true';
|
|
400
|
+
const install = getCurrentInstallChannel({ packageRoot });
|
|
401
|
+
const installSupport = getInstallChannelSupport(install);
|
|
402
|
+
const update = await getUpdateCheck(packageVersion, { preferFresh: !cached });
|
|
403
|
+
return {
|
|
404
|
+
currentVersion: packageVersion,
|
|
405
|
+
latestVersion: update?.latestVersion ?? null,
|
|
406
|
+
checkedAt: update?.checkedAt ?? null,
|
|
407
|
+
lastAttemptAt: update?.lastAttemptAt ?? null,
|
|
408
|
+
lastSuccessfulCheckAt: update?.lastSuccessfulCheckAt ?? null,
|
|
409
|
+
lastError: update?.lastError ?? null,
|
|
410
|
+
updateAvailable: update?.updateAvailable ?? false,
|
|
411
|
+
checkSucceeded: update?.checkSucceeded ?? false,
|
|
412
|
+
source: update?.source ?? null,
|
|
413
|
+
freshness: update?.freshness ?? 'unavailable',
|
|
414
|
+
installChannel: installSupport.channel,
|
|
415
|
+
canSelfUpdate: installSupport.canSelfUpdate,
|
|
416
|
+
recommendedCommand: (update?.currentVersionDeprecated
|
|
417
|
+
&& update.latestVersion
|
|
418
|
+
&& update.latestVersion === update.currentVersion
|
|
419
|
+
&& update.freshness === 'fresh') ? null : installSupport.recommendedCommand,
|
|
420
|
+
currentVersionDeprecated: update?.currentVersionDeprecated ?? false,
|
|
421
|
+
deprecationMessage: update?.deprecationMessage ?? null,
|
|
422
|
+
};
|
|
423
|
+
}));
|
|
454
424
|
app.get('/v1/graph', (_req, res) => handleGet(res, () => computeGraph(getDatabase())));
|
|
455
425
|
app.get('/v1/stats', (_req, res) => handleGet(res, () => computeStats(getDatabase())));
|
|
456
426
|
app.get('/v1/analytics', (_req, res) => handleGet(res, () => computeAnalytics(getDatabase())));
|
|
@@ -460,80 +430,51 @@ app.get('/v1/analytics/pm', (req, res) => {
|
|
|
460
430
|
const windowDays = Number.isFinite(window) && window > 0 ? window : 30;
|
|
461
431
|
handleGet(res, () => computePmAnalytics(getDatabase(), windowDays));
|
|
462
432
|
});
|
|
463
|
-
app.post('/v1/demo/seed',
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
app.post('/v1/demo/reset', async (_req, res) => {
|
|
474
|
-
try {
|
|
475
|
-
const { seedDemo } = await import('../../core/demo.js');
|
|
476
|
-
const data = seedDemo(getDatabase(), { reset: true });
|
|
477
|
-
res.json({ success: true, data });
|
|
478
|
-
}
|
|
479
|
-
catch (err) {
|
|
480
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
481
|
-
}
|
|
482
|
-
});
|
|
433
|
+
app.post('/v1/demo/seed', (_req, res) => handleGet(res, async () => {
|
|
434
|
+
const { seedDemo } = await import('../../core/demo.js');
|
|
435
|
+
return seedDemo(getDatabase());
|
|
436
|
+
}));
|
|
437
|
+
app.post('/v1/demo/reset', (_req, res) => handleGet(res, async () => {
|
|
438
|
+
const { seedDemo } = await import('../../core/demo.js');
|
|
439
|
+
return seedDemo(getDatabase(), { reset: true });
|
|
440
|
+
}));
|
|
483
441
|
app.get('/v1/projects', (_req, res) => handleGet(res, () => computeProjects(getDatabase())));
|
|
484
442
|
app.get('/v1/patterns', (_req, res) => handleGet(res, () => computePatterns(getDatabase())));
|
|
485
443
|
const TelemetryQuerySchema = z.object({
|
|
486
444
|
window: z.coerce.number().int().min(1).max(365).default(30),
|
|
487
445
|
});
|
|
488
|
-
app.get('/v1/telemetry',
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
446
|
+
app.get('/v1/telemetry', (req, res) => {
|
|
447
|
+
const query = parseQuery(TelemetryQuerySchema, req, res);
|
|
448
|
+
if (!query)
|
|
449
|
+
return;
|
|
450
|
+
handleGet(res, async () => {
|
|
495
451
|
const { summariseTelemetry } = await import('../../core/llm-telemetry.js');
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
}
|
|
499
|
-
catch (err) {
|
|
500
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
501
|
-
}
|
|
452
|
+
return { window_days: query.window, summaries: summariseTelemetry(query.window) };
|
|
453
|
+
});
|
|
502
454
|
});
|
|
503
455
|
const DreamProposalsQuerySchema = z.object({
|
|
504
456
|
status: z.enum(['pending', 'applied', 'rejected', 'all']).default('pending'),
|
|
505
457
|
});
|
|
506
458
|
app.get('/v1/dream/proposals', (req, res) => {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
? [...listProposals(db, 'pending'), ...listProposals(db, 'applied'), ...listProposals(db, 'rejected')]
|
|
518
|
-
: listProposals(db, status);
|
|
519
|
-
res.json({ success: true, data: rows });
|
|
520
|
-
}).catch((err) => res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) }));
|
|
521
|
-
}
|
|
522
|
-
catch (err) {
|
|
523
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
524
|
-
}
|
|
459
|
+
const query = parseQuery(DreamProposalsQuerySchema, req, res);
|
|
460
|
+
if (!query)
|
|
461
|
+
return;
|
|
462
|
+
handleGet(res, async () => {
|
|
463
|
+
const { listProposals } = await import('../../core/dreamer.js');
|
|
464
|
+
const db = getDatabase();
|
|
465
|
+
return query.status === 'all'
|
|
466
|
+
? [...listProposals(db, 'pending'), ...listProposals(db, 'applied'), ...listProposals(db, 'rejected')]
|
|
467
|
+
: listProposals(db, query.status);
|
|
468
|
+
});
|
|
525
469
|
});
|
|
526
470
|
app.get('/v1/dream/proposals/:id', (req, res) => {
|
|
527
|
-
const id =
|
|
528
|
-
if (
|
|
529
|
-
res.status(400).json({ success: false, errorCode: 'validation.bad-param', error: 'invalid id' });
|
|
471
|
+
const id = requireIdParam(req, res);
|
|
472
|
+
if (id === null)
|
|
530
473
|
return;
|
|
531
|
-
|
|
532
|
-
try {
|
|
474
|
+
handleGet(res, () => {
|
|
533
475
|
const row = getDatabase().prepare('SELECT id, project, cluster_key, source_ids, proposed_digest, llm_model, prompt_version, status, reason, created_at, reviewed_at FROM dream_proposals WHERE id = ?').get(id);
|
|
534
476
|
if (!row) {
|
|
535
|
-
|
|
536
|
-
return;
|
|
477
|
+
throw new HttpError(404, 'resource.not-found', `proposal #${id} not found`);
|
|
537
478
|
}
|
|
538
479
|
let digest = null;
|
|
539
480
|
let sourceIds = [];
|
|
@@ -545,11 +486,8 @@ app.get('/v1/dream/proposals/:id', (req, res) => {
|
|
|
545
486
|
sourceIds = JSON.parse(row.source_ids);
|
|
546
487
|
}
|
|
547
488
|
catch { }
|
|
548
|
-
|
|
549
|
-
}
|
|
550
|
-
catch (err) {
|
|
551
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
552
|
-
}
|
|
489
|
+
return { ...row, proposed_digest: digest, source_ids: sourceIds };
|
|
490
|
+
});
|
|
553
491
|
});
|
|
554
492
|
const DreamRunBody = z.object({
|
|
555
493
|
project: z.string().min(1).max(100).optional(),
|
|
@@ -557,90 +495,64 @@ const DreamRunBody = z.object({
|
|
|
557
495
|
maxLlmCalls: z.number().int().min(1).max(20).default(5),
|
|
558
496
|
validate: z.boolean().default(false),
|
|
559
497
|
});
|
|
560
|
-
app.post('/v1/dream/run',
|
|
561
|
-
const
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
});
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
const result = await runDreamer(getDatabase(), cfg.llm, {
|
|
582
|
-
project: parsed.data.project,
|
|
583
|
-
windowDays: parsed.data.windowDays,
|
|
584
|
-
maxLlmCalls: parsed.data.maxLlmCalls,
|
|
585
|
-
fallbacks: cfg.llmFallbacks,
|
|
586
|
-
validateBeforeStage: parsed.data.validate,
|
|
587
|
-
});
|
|
588
|
-
res.json({ success: true, data: result });
|
|
589
|
-
}
|
|
590
|
-
catch (err) {
|
|
591
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
592
|
-
}
|
|
593
|
-
});
|
|
594
|
-
app.post('/v1/dream/proposals/:id/accept', async (req, res) => {
|
|
595
|
-
const id = parseInt(req.params.id, 10);
|
|
596
|
-
if (!Number.isInteger(id) || id < 1) {
|
|
597
|
-
res.status(400).json({ success: false, errorCode: 'validation.bad-param', error: 'invalid id' });
|
|
498
|
+
app.post('/v1/dream/run', (req, res) => handlePost(DreamRunBody, req, res, async (data) => {
|
|
499
|
+
const { runDreamer } = await import('../../core/dreamer.js');
|
|
500
|
+
const caps = detectCapabilities();
|
|
501
|
+
const llm = caps.llm;
|
|
502
|
+
if (!llm) {
|
|
503
|
+
throw new HttpError(400, 'llm.not-configured', 'No LLM configured — dream run requires Smart Mode. Configure a provider in Settings.');
|
|
504
|
+
}
|
|
505
|
+
return runDreamer(getDatabase(), llm, {
|
|
506
|
+
project: data.project,
|
|
507
|
+
windowDays: data.windowDays,
|
|
508
|
+
maxLlmCalls: data.maxLlmCalls,
|
|
509
|
+
fallbacks: caps.llmFallbacks,
|
|
510
|
+
validateBeforeStage: data.validate,
|
|
511
|
+
});
|
|
512
|
+
}, { allowEmptyBody: true, errorStatus: 500, errorCode: 'server.internal' }));
|
|
513
|
+
app.post('/v1/dream/proposals/:id/accept', (req, res) => {
|
|
514
|
+
const id = requireIdParam(req, res);
|
|
515
|
+
if (id === null)
|
|
598
516
|
return;
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
res.json({ success: true, data: result });
|
|
605
|
-
}
|
|
606
|
-
catch (err) {
|
|
607
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
608
|
-
if (/not found or not pending/.test(msg)) {
|
|
609
|
-
res.status(404).json({ success: false, errorCode: 'resource.not-found', error: msg });
|
|
517
|
+
handleGet(res, async () => {
|
|
518
|
+
const dreamer = await import('../../core/dreamer.js');
|
|
519
|
+
try {
|
|
520
|
+
const kg = new KnowledgeGraph(getDatabase());
|
|
521
|
+
return dreamer.applyProposal(getDatabase(), id, kg);
|
|
610
522
|
}
|
|
611
|
-
|
|
612
|
-
|
|
523
|
+
catch (err) {
|
|
524
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
525
|
+
if (err instanceof dreamer.NothingToClaimError) {
|
|
526
|
+
throw new HttpError(400, 'operation.failed', msg);
|
|
527
|
+
}
|
|
528
|
+
if (/not found or not pending/.test(msg)) {
|
|
529
|
+
throw new HttpError(404, 'resource.not-found', msg);
|
|
530
|
+
}
|
|
531
|
+
throw err;
|
|
613
532
|
}
|
|
614
|
-
}
|
|
533
|
+
});
|
|
615
534
|
});
|
|
616
535
|
const RejectBodySchema = z.object({
|
|
617
536
|
reason: z.string().max(500).optional(),
|
|
618
537
|
});
|
|
619
|
-
app.post('/v1/dream/proposals/:id/reject',
|
|
620
|
-
const id =
|
|
621
|
-
if (
|
|
622
|
-
res.status(400).json({ success: false, errorCode: 'validation.bad-param', error: 'invalid id' });
|
|
538
|
+
app.post('/v1/dream/proposals/:id/reject', (req, res) => {
|
|
539
|
+
const id = requireIdParam(req, res);
|
|
540
|
+
if (id === null)
|
|
623
541
|
return;
|
|
624
|
-
|
|
625
|
-
const parsed = RejectBodySchema.safeParse(req.body ?? {});
|
|
626
|
-
if (!parsed.success) {
|
|
627
|
-
res.status(400).json({ success: false, errorCode: 'validation.bad-body', error: parsed.error.issues.map(i => i.message).join('; ') });
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
try {
|
|
542
|
+
handlePost(RejectBodySchema, req, res, async (data) => {
|
|
631
543
|
const { rejectProposal } = await import('../../core/dreamer.js');
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
}
|
|
635
|
-
catch (err) {
|
|
636
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
637
|
-
if (/not found or not pending/.test(msg)) {
|
|
638
|
-
res.status(404).json({ success: false, errorCode: 'resource.not-found', error: msg });
|
|
544
|
+
try {
|
|
545
|
+
rejectProposal(getDatabase(), id, data.reason);
|
|
639
546
|
}
|
|
640
|
-
|
|
641
|
-
|
|
547
|
+
catch (err) {
|
|
548
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
549
|
+
if (/not found or not pending/.test(msg)) {
|
|
550
|
+
throw new HttpError(404, 'resource.not-found', msg);
|
|
551
|
+
}
|
|
552
|
+
throw err;
|
|
642
553
|
}
|
|
643
|
-
|
|
554
|
+
return { id, status: 'rejected' };
|
|
555
|
+
}, { allowEmptyBody: true, errorStatus: 500, errorCode: 'server.internal' });
|
|
644
556
|
});
|
|
645
557
|
const EntitiesQuerySchema = z.object({
|
|
646
558
|
type: z.string().min(1).max(100).optional(),
|
|
@@ -648,40 +560,26 @@ const EntitiesQuerySchema = z.object({
|
|
|
648
560
|
status: z.enum(['all', 'active']).optional(),
|
|
649
561
|
});
|
|
650
562
|
app.get('/v1/entities', (req, res) => {
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
}
|
|
657
|
-
const { type: typeFilter, limit, status } = parsed.data;
|
|
563
|
+
const query = parseQuery(EntitiesQuerySchema, req, res);
|
|
564
|
+
if (!query)
|
|
565
|
+
return;
|
|
566
|
+
handleGet(res, () => {
|
|
567
|
+
const { type: typeFilter, limit, status } = query;
|
|
658
568
|
const includeArchived = status === 'all';
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
const entities = typeFilter
|
|
569
|
+
const kg = new KnowledgeGraph(getDatabase());
|
|
570
|
+
return typeFilter
|
|
662
571
|
? kg.listByType(typeFilter, limit, includeArchived)
|
|
663
572
|
: kg.listRecent(limit, includeArchived);
|
|
664
|
-
|
|
665
|
-
}
|
|
666
|
-
catch (err) {
|
|
667
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
668
|
-
}
|
|
669
|
-
});
|
|
670
|
-
app.get('/v1/entities/:name', (req, res) => {
|
|
671
|
-
try {
|
|
672
|
-
const db = getDatabase();
|
|
673
|
-
const kg = new KnowledgeGraph(db);
|
|
674
|
-
const entity = kg.getEntity(req.params.name);
|
|
675
|
-
if (!entity) {
|
|
676
|
-
res.status(404).json({ success: false, errorCode: 'resource.not-found', error: `Entity "${req.params.name}" not found` });
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
|
-
res.json({ success: true, data: entity });
|
|
680
|
-
}
|
|
681
|
-
catch (err) {
|
|
682
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
683
|
-
}
|
|
573
|
+
});
|
|
684
574
|
});
|
|
575
|
+
app.get('/v1/entities/:name', (req, res) => handleGet(res, () => {
|
|
576
|
+
const kg = new KnowledgeGraph(getDatabase());
|
|
577
|
+
const entity = kg.getEntity(String(req.params.name));
|
|
578
|
+
if (!entity) {
|
|
579
|
+
throw new HttpError(404, 'resource.not-found', `Entity "${String(req.params.name)}" not found`);
|
|
580
|
+
}
|
|
581
|
+
return entity;
|
|
582
|
+
}));
|
|
685
583
|
const HOST = process.env.MEMESH_HTTP_HOST || '127.0.0.1';
|
|
686
584
|
const PORT = parseInt(process.env.MEMESH_HTTP_PORT || '3737');
|
|
687
585
|
const ALLOW_REMOTE_BY_ENV = /^(1|true|yes)$/i.test(process.env.MEMESH_HTTP_ALLOW_REMOTE || '');
|
|
@@ -710,6 +608,10 @@ export function startServer(host = HOST, port = PORT, opts) {
|
|
|
710
608
|
if (!allowRemote && isRemote) {
|
|
711
609
|
throw new Error(`Refusing to bind MeMesh HTTP server to non-loopback host "${host}" without explicit remote access opt-in. Use --allow-remote or MEMESH_HTTP_ALLOW_REMOTE=true.`);
|
|
712
610
|
}
|
|
611
|
+
if (allowRemote && !isRemote) {
|
|
612
|
+
process.stderr.write(`MeMesh HTTP: --allow-remote has no effect on loopback host "${host}" — the server stays local ` +
|
|
613
|
+
'and no bearer token is generated. Add --host <address> to bind somewhere reachable.\n');
|
|
614
|
+
}
|
|
713
615
|
if (isRemote) {
|
|
714
616
|
const { token, freshlyCreated } = loadOrCreateRemoteToken();
|
|
715
617
|
remoteToken = token;
|
|
@@ -767,10 +669,21 @@ export function startServer(host = HOST, port = PORT, opts) {
|
|
|
767
669
|
console.log(`MeMesh HTTP server running at http://${addr.address}:${addr.port}`);
|
|
768
670
|
console.log(`MeMesh dashboard: http://${addr.address}:${addr.port}/dashboard`);
|
|
769
671
|
}
|
|
672
|
+
});
|
|
673
|
+
server.on('error', (err) => {
|
|
674
|
+
const where = `${host}:${port}`;
|
|
675
|
+
if (err.code === 'EADDRINUSE') {
|
|
676
|
+
console.error(`MeMesh: cannot start — ${where} is already in use.`);
|
|
677
|
+
console.error(`MeMesh: stop whatever is listening there, or pick another port with --port <n>.`);
|
|
678
|
+
}
|
|
679
|
+
else if (err.code === 'EACCES') {
|
|
680
|
+
console.error(`MeMesh: cannot start — not permitted to bind ${where}.`);
|
|
681
|
+
console.error(`MeMesh: ports below 1024 need elevated privileges; pick a port above 1024 with --port <n>.`);
|
|
682
|
+
}
|
|
770
683
|
else {
|
|
771
|
-
console.
|
|
772
|
-
console.log(`MeMesh dashboard: http://${host}:${port}/dashboard`);
|
|
684
|
+
console.error(`MeMesh: cannot start on ${where} — ${err.message}`);
|
|
773
685
|
}
|
|
686
|
+
process.exit(1);
|
|
774
687
|
});
|
|
775
688
|
serverAuthRequired.set(server, isRemote);
|
|
776
689
|
return server;
|
|
@@ -780,7 +693,14 @@ export function __setRemoteTokenForTest(value) {
|
|
|
780
693
|
}
|
|
781
694
|
const isMain = process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\/g, '/'));
|
|
782
695
|
if (isMain || process.argv[1]?.endsWith('memesh-http')) {
|
|
783
|
-
|
|
696
|
+
let server;
|
|
697
|
+
try {
|
|
698
|
+
server = startServer();
|
|
699
|
+
}
|
|
700
|
+
catch (err) {
|
|
701
|
+
console.error(`MeMesh: ${err instanceof Error ? err.message : String(err)}`);
|
|
702
|
+
process.exit(1);
|
|
703
|
+
}
|
|
784
704
|
function shutdown() {
|
|
785
705
|
server.close();
|
|
786
706
|
try {
|