@pcircle/memesh 4.5.1 → 4.6.1
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/AGENTS.md +116 -0
- package/README.de.md +141 -48
- package/README.md +173 -48
- package/README.zh-TW.md +142 -48
- package/dashboard/dist/index.html +15 -14
- package/dist/cli/view-live.js +3 -3
- package/dist/core/analytics.d.ts +9 -0
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/core/analytics.js +36 -18
- 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 +92 -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.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.map +1 -1
- package/dist/core/demo.js +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 +2 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +59 -62
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/dreamer.d.ts +5 -2
- package/dist/core/dreamer.d.ts.map +1 -1
- package/dist/core/dreamer.js +329 -25
- package/dist/core/dreamer.js.map +1 -1
- package/dist/core/embedder.d.ts +8 -4
- package/dist/core/embedder.d.ts.map +1 -1
- package/dist/core/embedder.js +82 -24
- package/dist/core/embedder.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 +12 -0
- package/dist/core/graph.d.ts.map +1 -1
- package/dist/core/graph.js +56 -1
- package/dist/core/graph.js.map +1 -1
- package/dist/core/guards.d.ts +20 -0
- package/dist/core/guards.d.ts.map +1 -0
- package/dist/core/guards.js +103 -0
- package/dist/core/guards.js.map +1 -0
- 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 +5 -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 +5 -2
- package/dist/core/kg-backfill.d.ts.map +1 -1
- package/dist/core/kg-backfill.js +155 -5
- package/dist/core/kg-backfill.js.map +1 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +14 -21
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/memory-tool.d.ts.map +1 -1
- package/dist/core/memory-tool.js +4 -4
- package/dist/core/memory-tool.js.map +1 -1
- package/dist/core/operations.d.ts +13 -2
- package/dist/core/operations.d.ts.map +1 -1
- package/dist/core/operations.js +115 -28
- package/dist/core/operations.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 +31 -0
- package/dist/core/schema-export.js.map +1 -1
- package/dist/core/serializer.d.ts.map +1 -1
- package/dist/core/serializer.js +8 -0
- 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/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-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 +5 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/why.d.ts +54 -0
- package/dist/core/why.d.ts.map +1 -0
- package/dist/core/why.js +168 -0
- package/dist/core/why.js.map +1 -0
- package/dist/core/work-topology.d.ts +36 -0
- package/dist/core/work-topology.d.ts.map +1 -0
- package/dist/core/work-topology.js +192 -0
- package/dist/core/work-topology.js.map +1 -0
- package/dist/db.d.ts +33 -11
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +307 -315
- package/dist/db.js.map +1 -1
- package/dist/knowledge-graph.d.ts +1 -0
- package/dist/knowledge-graph.d.ts.map +1 -1
- package/dist/knowledge-graph.js +50 -40
- package/dist/knowledge-graph.js.map +1 -1
- package/dist/skills-manifest.json +62 -22
- 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 +4 -2
- 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.map +1 -1
- package/dist/storage/sqlite.js +1 -1
- package/dist/storage/sqlite.js.map +1 -1
- 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 +579 -66
- package/dist/transports/cli/cli.js.map +1 -1
- package/dist/transports/http/server.d.ts.map +1 -1
- package/dist/transports/http/server.js +242 -303
- package/dist/transports/http/server.js.map +1 -1
- package/dist/transports/mcp/handlers.d.ts +46 -0
- package/dist/transports/mcp/handlers.d.ts.map +1 -1
- package/dist/transports/mcp/handlers.js +59 -4
- package/dist/transports/mcp/handlers.js.map +1 -1
- package/dist/transports/schemas.d.ts +29 -10
- package/dist/transports/schemas.d.ts.map +1 -1
- package/dist/transports/schemas.js +33 -8
- package/dist/transports/schemas.js.map +1 -1
- package/hooks/hooks.json +10 -0
- package/llms-install.md +138 -0
- package/package.json +14 -9
- package/scripts/hooks/_generated/capture-flag.js +17 -0
- package/scripts/hooks/_generated/fts-index.js +16 -4
- package/scripts/hooks/_generated/guards.js +110 -0
- package/scripts/hooks/_generated/schema.js +281 -0
- package/scripts/hooks/_generated/sqlite.js +1 -1
- 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 +199 -0
- package/scripts/hooks/_shared.js +197 -480
- package/scripts/hooks/guard-check.js +76 -0
- package/scripts/hooks/post-commit.js +31 -1
- package/scripts/hooks/pre-compact.js +13 -1
- package/scripts/hooks/pre-edit-recall.js +158 -120
- package/scripts/hooks/session-start.js +169 -82
- package/scripts/hooks/session-summary.js +78 -90
- package/skills/memesh/SKILL.md +108 -76
- package/README.es.md +0 -467
- package/README.fr.md +0 -459
- package/README.ja.md +0 -467
- package/README.ko.md +0 -467
- package/README.pt.md +0 -459
- package/README.th.md +0 -460
- package/README.vi.md +0 -459
- package/README.zh-CN.md +0 -466
- 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
|
@@ -12,8 +12,8 @@ import { computePatterns } from '../../core/patterns.js';
|
|
|
12
12
|
import { computeAnalytics, computePmAnalytics } from '../../core/analytics.js';
|
|
13
13
|
import { computeStats } from '../../core/stats.js';
|
|
14
14
|
import { computeProjects } from '../../core/projects.js';
|
|
15
|
-
import { computeGraph } from '../../core/graph.js';
|
|
16
|
-
import { RememberSchema as RememberBody, RecallSchema as RecallBody, ForgetSchema as ForgetBody, ExportSchema as ExportBody, ImportSchema as ImportBody, LearnSchema as LearnBody, } from '../schemas.js';
|
|
15
|
+
import { computeGraph, computeWorkGraph, computeNodeEvidence } from '../../core/graph.js';
|
|
16
|
+
import { RememberSchema as RememberBody, RecallSchema as RecallBody, ForgetSchema as ForgetBody, ExportSchema as ExportBody, ImportSchema as ImportBody, LearnSchema as LearnBody, WhySchema as WhyBody, } from '../schemas.js';
|
|
17
17
|
import { checkForUpdate, getLastUpdateCheck, getUpdateCheck } from '../../core/version-check.js';
|
|
18
18
|
import { getCurrentInstallChannel, getInstallChannelSupport } from '../../core/install-channel.js';
|
|
19
19
|
import { getDbPath, getMemeshDirFromDbPath, redactSecrets, redactUserPaths } from '../../core/paths.js';
|
|
@@ -203,20 +203,14 @@ app.get('/v1/health', (_req, res) => {
|
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
205
|
});
|
|
206
|
-
app.get('/v1/doctor',
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
res.json({ success: true, data: safe });
|
|
215
|
-
}
|
|
216
|
-
catch (err) {
|
|
217
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
218
|
-
}
|
|
219
|
-
});
|
|
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
|
+
}));
|
|
220
214
|
function requireJsonBody(req, res) {
|
|
221
215
|
if (req.body !== undefined)
|
|
222
216
|
return true;
|
|
@@ -228,46 +222,73 @@ function requireJsonBody(req, res) {
|
|
|
228
222
|
});
|
|
229
223
|
return false;
|
|
230
224
|
}
|
|
231
|
-
|
|
232
|
-
|
|
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 });
|
|
233
237
|
return;
|
|
234
|
-
|
|
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));
|
|
235
268
|
if (!parsed.success) {
|
|
236
269
|
res.status(400).json({
|
|
237
270
|
success: false,
|
|
238
271
|
errorCode: 'validation.bad-body',
|
|
239
|
-
error: parsed.error
|
|
272
|
+
error: zodErrorText(parsed.error),
|
|
240
273
|
});
|
|
241
274
|
return;
|
|
242
275
|
}
|
|
243
276
|
Promise.resolve()
|
|
244
277
|
.then(() => handler(parsed.data))
|
|
245
278
|
.then((data) => res.json({ success: true, data }))
|
|
246
|
-
.catch((err) => res
|
|
279
|
+
.catch((err) => sendError(res, err, opts?.errorStatus ?? 400, opts?.errorCode ?? 'operation.failed'));
|
|
247
280
|
}
|
|
248
281
|
function handleGet(res, produce) {
|
|
249
282
|
Promise.resolve()
|
|
250
283
|
.then(produce)
|
|
251
284
|
.then((data) => res.json({ success: true, data }))
|
|
252
|
-
.catch((err) => res
|
|
285
|
+
.catch((err) => sendError(res, err, 500, 'server.internal'));
|
|
253
286
|
}
|
|
254
287
|
app.post('/v1/remember', (req, res) => handlePost(RememberBody, req, res, (data) => remember({ ...data, sourceHost: 'http' })));
|
|
255
|
-
app.post('/v1/recall',
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (!parsed.success) {
|
|
260
|
-
res.status(400).json({ success: false, errorCode: 'validation.bad-body', error: parsed.error.issues.map(i => `${i.path.join('.')}: ${i.message}`).join('; ') });
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
try {
|
|
264
|
-
const { entities, conflicts } = await recallWithConflicts(parsed.data);
|
|
265
|
-
res.json({ success: true, data: conflicts.length > 0 ? { entities, conflicts } : entities });
|
|
266
|
-
}
|
|
267
|
-
catch (err) {
|
|
268
|
-
res.status(400).json({ success: false, errorCode: 'operation.failed', error: err instanceof Error ? err.message : String(err) });
|
|
269
|
-
}
|
|
270
|
-
});
|
|
288
|
+
app.post('/v1/recall', (req, res) => handlePost(RecallBody, req, res, async (data) => {
|
|
289
|
+
const { entities, conflicts, retrieval } = await recallWithConflicts(data);
|
|
290
|
+
return conflicts.length > 0 ? { entities, retrieval, conflicts } : { entities, retrieval };
|
|
291
|
+
}));
|
|
271
292
|
app.post('/v1/forget', (req, res) => handlePost(ForgetBody, req, res, forget));
|
|
272
293
|
app.post('/v1/consolidate', (_req, res) => {
|
|
273
294
|
res.status(410).json({ success: false, errorCode: 'route.retired', error: RETIRED_ROUTES['/v1/consolidate'] });
|
|
@@ -275,6 +296,15 @@ app.post('/v1/consolidate', (_req, res) => {
|
|
|
275
296
|
app.post('/v1/export', (req, res) => handlePost(ExportBody, req, res, exportMemories));
|
|
276
297
|
app.post('/v1/import', (req, res) => handlePost(ImportBody, req, res, importMemories));
|
|
277
298
|
app.post('/v1/learn', (req, res) => handlePost(LearnBody, req, res, (data) => learn({ ...data, sourceHost: 'http' })));
|
|
299
|
+
app.post('/v1/why', (req, res) => handlePost(WhyBody, req, res, async (data) => {
|
|
300
|
+
const { explainCommits } = await import('../../core/why.js');
|
|
301
|
+
return explainCommits(getDatabase(), {
|
|
302
|
+
file: data.file,
|
|
303
|
+
commits: data.commits?.map((hash) => ({ hash })),
|
|
304
|
+
project: data.project ?? null,
|
|
305
|
+
limit: data.limit,
|
|
306
|
+
});
|
|
307
|
+
}));
|
|
278
308
|
app.post('/v1/verify', (_req, res) => {
|
|
279
309
|
res.status(410).json({ success: false, errorCode: 'route.retired', error: RETIRED_ROUTES['/v1/verify'] });
|
|
280
310
|
});
|
|
@@ -305,16 +335,11 @@ function preserveFallbackApiKeys(incoming, stored) {
|
|
|
305
335
|
return clean;
|
|
306
336
|
});
|
|
307
337
|
}
|
|
308
|
-
app.get('/v1/config', (_req, res) => {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
catch (err) {
|
|
315
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
316
|
-
}
|
|
317
|
-
});
|
|
338
|
+
app.get('/v1/config', (_req, res) => handleGet(res, () => {
|
|
339
|
+
const config = readConfig();
|
|
340
|
+
const caps = detectCapabilities(config);
|
|
341
|
+
return { config: maskLlmSecrets(config), capabilities: maskLlmSecrets(caps) };
|
|
342
|
+
}));
|
|
318
343
|
const ConfigBody = z.object({
|
|
319
344
|
llm: z.union([
|
|
320
345
|
z.object({
|
|
@@ -340,109 +365,101 @@ const ConfigBody = z.object({
|
|
|
340
365
|
.optional(),
|
|
341
366
|
setupCompleted: z.boolean().optional(),
|
|
342
367
|
}).strip();
|
|
343
|
-
app.post('/v1/config',
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
res.status(400).json({ success: false, errorCode: 'validation.bad-body', error: parsed.error.issues.map(i => `${i.path.join('.')}: ${i.message}`).join('; ') });
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
try {
|
|
352
|
-
const before = readConfig();
|
|
353
|
-
if (parsed.data.llm && parsed.data.llm.apiKey === API_KEY_MASK) {
|
|
354
|
-
if (before.llm && before.llm.provider === parsed.data.llm.provider && before.llm.apiKey) {
|
|
355
|
-
parsed.data.llm.apiKey = before.llm.apiKey;
|
|
356
|
-
}
|
|
357
|
-
else {
|
|
358
|
-
delete parsed.data.llm.apiKey;
|
|
359
|
-
}
|
|
368
|
+
app.post('/v1/config', (req, res) => handlePost(ConfigBody, req, res, (data) => {
|
|
369
|
+
const before = readConfig();
|
|
370
|
+
if (data.llm && data.llm.apiKey === API_KEY_MASK) {
|
|
371
|
+
if (before.llm && before.llm.provider === data.llm.provider && before.llm.apiKey) {
|
|
372
|
+
data.llm.apiKey = before.llm.apiKey;
|
|
360
373
|
}
|
|
361
|
-
|
|
362
|
-
|
|
374
|
+
else {
|
|
375
|
+
delete data.llm.apiKey;
|
|
363
376
|
}
|
|
364
|
-
const updated = updateConfig(parsed.data);
|
|
365
|
-
res.json({ success: true, data: maskLlmSecrets(updated) });
|
|
366
377
|
}
|
|
367
|
-
|
|
368
|
-
|
|
378
|
+
if (data.llmFallbacks) {
|
|
379
|
+
data.llmFallbacks = preserveFallbackApiKeys(data.llmFallbacks, before.llmFallbacks);
|
|
369
380
|
}
|
|
370
|
-
|
|
381
|
+
const updated = updateConfig(data);
|
|
382
|
+
return maskLlmSecrets(updated);
|
|
383
|
+
}));
|
|
371
384
|
const ConfigTestBody = z.object({
|
|
372
385
|
provider: z.enum(['anthropic', 'openai', 'ollama']),
|
|
373
386
|
apiKey: z.string().max(500).optional(),
|
|
374
387
|
host: z.string().max(500).optional(),
|
|
375
388
|
fallbackIndex: z.number().int().nonnegative().optional(),
|
|
376
389
|
});
|
|
377
|
-
app.post('/v1/config/test',
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
if (!
|
|
390
|
+
app.post('/v1/config/test', (req, res) => handlePost(ConfigTestBody, req, res, async (data) => {
|
|
391
|
+
const { probeProvider } = await import('../../core/llm-validator.js');
|
|
392
|
+
const { provider, host, fallbackIndex } = data;
|
|
393
|
+
let { apiKey } = data;
|
|
394
|
+
if (!apiKey && (provider === 'anthropic' || provider === 'openai')) {
|
|
395
|
+
const existing = readConfig();
|
|
396
|
+
if (typeof fallbackIndex === 'number') {
|
|
397
|
+
const fb = existing.llmFallbacks?.[fallbackIndex];
|
|
398
|
+
if (fb && fb.provider === provider && fb.apiKey)
|
|
399
|
+
apiKey = fb.apiKey;
|
|
400
|
+
}
|
|
401
|
+
else if (existing.llm?.provider === provider && existing.llm.apiKey) {
|
|
402
|
+
apiKey = existing.llm.apiKey;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return probeProvider(provider, apiKey, host);
|
|
406
|
+
}, { errorStatus: 500, errorCode: 'server.internal' }));
|
|
407
|
+
app.get('/v1/update-status', (req, res) => handleGet(res, async () => {
|
|
408
|
+
const cached = req.query.cached === '1' || req.query.cached === 'true';
|
|
409
|
+
const install = getCurrentInstallChannel({ packageRoot });
|
|
410
|
+
const installSupport = getInstallChannelSupport(install);
|
|
411
|
+
const update = await getUpdateCheck(packageVersion, { preferFresh: !cached });
|
|
412
|
+
return {
|
|
413
|
+
currentVersion: packageVersion,
|
|
414
|
+
latestVersion: update?.latestVersion ?? null,
|
|
415
|
+
checkedAt: update?.checkedAt ?? null,
|
|
416
|
+
lastAttemptAt: update?.lastAttemptAt ?? null,
|
|
417
|
+
lastSuccessfulCheckAt: update?.lastSuccessfulCheckAt ?? null,
|
|
418
|
+
lastError: update?.lastError ?? null,
|
|
419
|
+
updateAvailable: update?.updateAvailable ?? false,
|
|
420
|
+
checkSucceeded: update?.checkSucceeded ?? false,
|
|
421
|
+
source: update?.source ?? null,
|
|
422
|
+
freshness: update?.freshness ?? 'unavailable',
|
|
423
|
+
installChannel: installSupport.channel,
|
|
424
|
+
canSelfUpdate: installSupport.canSelfUpdate,
|
|
425
|
+
recommendedCommand: (update?.currentVersionDeprecated
|
|
426
|
+
&& update.latestVersion
|
|
427
|
+
&& update.latestVersion === update.currentVersion
|
|
428
|
+
&& update.freshness === 'fresh') ? null : installSupport.recommendedCommand,
|
|
429
|
+
currentVersionDeprecated: update?.currentVersionDeprecated ?? false,
|
|
430
|
+
deprecationMessage: update?.deprecationMessage ?? null,
|
|
431
|
+
};
|
|
432
|
+
}));
|
|
433
|
+
app.get('/v1/graph', (req, res) => {
|
|
434
|
+
const layer = req.query.layer;
|
|
435
|
+
if (layer !== undefined && layer !== 'work') {
|
|
382
436
|
res.status(400).json({
|
|
383
437
|
success: false,
|
|
384
|
-
errorCode: 'validation.bad-
|
|
385
|
-
error:
|
|
438
|
+
errorCode: 'validation.bad-param',
|
|
439
|
+
error: "layer must be 'work' (omit the parameter for the full graph)",
|
|
386
440
|
});
|
|
387
441
|
return;
|
|
388
442
|
}
|
|
389
|
-
|
|
390
|
-
const { probeProvider } = await import('../../core/llm-validator.js');
|
|
391
|
-
const { provider, host, fallbackIndex } = parsed.data;
|
|
392
|
-
let { apiKey } = parsed.data;
|
|
393
|
-
if (!apiKey && (provider === 'anthropic' || provider === 'openai')) {
|
|
394
|
-
const existing = readConfig();
|
|
395
|
-
if (typeof fallbackIndex === 'number') {
|
|
396
|
-
const fb = existing.llmFallbacks?.[fallbackIndex];
|
|
397
|
-
if (fb && fb.provider === provider && fb.apiKey)
|
|
398
|
-
apiKey = fb.apiKey;
|
|
399
|
-
}
|
|
400
|
-
else if (existing.llm?.provider === provider && existing.llm.apiKey) {
|
|
401
|
-
apiKey = existing.llm.apiKey;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
const result = await probeProvider(provider, apiKey, host);
|
|
405
|
-
res.json({ success: true, data: result });
|
|
406
|
-
}
|
|
407
|
-
catch (err) {
|
|
408
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
409
|
-
}
|
|
443
|
+
handleGet(res, () => (layer === 'work' ? computeWorkGraph(getDatabase()) : computeGraph(getDatabase())));
|
|
410
444
|
});
|
|
411
|
-
app.get('/v1/
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
success: true,
|
|
419
|
-
data: {
|
|
420
|
-
currentVersion: packageVersion,
|
|
421
|
-
latestVersion: update?.latestVersion ?? null,
|
|
422
|
-
checkedAt: update?.checkedAt ?? null,
|
|
423
|
-
lastAttemptAt: update?.lastAttemptAt ?? null,
|
|
424
|
-
lastSuccessfulCheckAt: update?.lastSuccessfulCheckAt ?? null,
|
|
425
|
-
lastError: update?.lastError ?? null,
|
|
426
|
-
updateAvailable: update?.updateAvailable ?? false,
|
|
427
|
-
checkSucceeded: update?.checkSucceeded ?? false,
|
|
428
|
-
source: update?.source ?? null,
|
|
429
|
-
freshness: update?.freshness ?? 'unavailable',
|
|
430
|
-
installChannel: installSupport.channel,
|
|
431
|
-
canSelfUpdate: installSupport.canSelfUpdate,
|
|
432
|
-
recommendedCommand: (update?.currentVersionDeprecated
|
|
433
|
-
&& update.latestVersion
|
|
434
|
-
&& update.latestVersion === update.currentVersion
|
|
435
|
-
&& update.freshness === 'fresh') ? null : installSupport.recommendedCommand,
|
|
436
|
-
currentVersionDeprecated: update?.currentVersionDeprecated ?? false,
|
|
437
|
-
deprecationMessage: update?.deprecationMessage ?? null,
|
|
438
|
-
},
|
|
445
|
+
app.get('/v1/graph/evidence', (req, res) => {
|
|
446
|
+
const node = req.query.node;
|
|
447
|
+
if (typeof node !== 'string' || node.length === 0) {
|
|
448
|
+
res.status(400).json({
|
|
449
|
+
success: false,
|
|
450
|
+
errorCode: 'validation.bad-param',
|
|
451
|
+
error: 'node query parameter is required (the work-node entity name)',
|
|
439
452
|
});
|
|
453
|
+
return;
|
|
440
454
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
455
|
+
handleGet(res, () => {
|
|
456
|
+
const result = computeNodeEvidence(getDatabase(), node);
|
|
457
|
+
if (result === null) {
|
|
458
|
+
throw new HttpError(404, 'resource.not-found', `Entity "${node}" not found`);
|
|
459
|
+
}
|
|
460
|
+
return result;
|
|
461
|
+
});
|
|
444
462
|
});
|
|
445
|
-
app.get('/v1/graph', (_req, res) => handleGet(res, () => computeGraph(getDatabase())));
|
|
446
463
|
app.get('/v1/stats', (_req, res) => handleGet(res, () => computeStats(getDatabase())));
|
|
447
464
|
app.get('/v1/analytics', (_req, res) => handleGet(res, () => computeAnalytics(getDatabase())));
|
|
448
465
|
app.get('/v1/analytics/pm', (req, res) => {
|
|
@@ -451,80 +468,51 @@ app.get('/v1/analytics/pm', (req, res) => {
|
|
|
451
468
|
const windowDays = Number.isFinite(window) && window > 0 ? window : 30;
|
|
452
469
|
handleGet(res, () => computePmAnalytics(getDatabase(), windowDays));
|
|
453
470
|
});
|
|
454
|
-
app.post('/v1/demo/seed',
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
app.post('/v1/demo/reset', async (_req, res) => {
|
|
465
|
-
try {
|
|
466
|
-
const { seedDemo } = await import('../../core/demo.js');
|
|
467
|
-
const data = seedDemo(getDatabase(), { reset: true });
|
|
468
|
-
res.json({ success: true, data });
|
|
469
|
-
}
|
|
470
|
-
catch (err) {
|
|
471
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
472
|
-
}
|
|
473
|
-
});
|
|
471
|
+
app.post('/v1/demo/seed', (_req, res) => handleGet(res, async () => {
|
|
472
|
+
const { seedDemo } = await import('../../core/demo.js');
|
|
473
|
+
return seedDemo(getDatabase());
|
|
474
|
+
}));
|
|
475
|
+
app.post('/v1/demo/reset', (_req, res) => handleGet(res, async () => {
|
|
476
|
+
const { seedDemo } = await import('../../core/demo.js');
|
|
477
|
+
return seedDemo(getDatabase(), { reset: true });
|
|
478
|
+
}));
|
|
474
479
|
app.get('/v1/projects', (_req, res) => handleGet(res, () => computeProjects(getDatabase())));
|
|
475
480
|
app.get('/v1/patterns', (_req, res) => handleGet(res, () => computePatterns(getDatabase())));
|
|
476
481
|
const TelemetryQuerySchema = z.object({
|
|
477
482
|
window: z.coerce.number().int().min(1).max(365).default(30),
|
|
478
483
|
});
|
|
479
|
-
app.get('/v1/telemetry',
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
484
|
+
app.get('/v1/telemetry', (req, res) => {
|
|
485
|
+
const query = parseQuery(TelemetryQuerySchema, req, res);
|
|
486
|
+
if (!query)
|
|
487
|
+
return;
|
|
488
|
+
handleGet(res, async () => {
|
|
486
489
|
const { summariseTelemetry } = await import('../../core/llm-telemetry.js');
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
}
|
|
490
|
-
catch (err) {
|
|
491
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
492
|
-
}
|
|
490
|
+
return { window_days: query.window, summaries: summariseTelemetry(query.window) };
|
|
491
|
+
});
|
|
493
492
|
});
|
|
494
493
|
const DreamProposalsQuerySchema = z.object({
|
|
495
494
|
status: z.enum(['pending', 'applied', 'rejected', 'all']).default('pending'),
|
|
496
495
|
});
|
|
497
496
|
app.get('/v1/dream/proposals', (req, res) => {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
? [...listProposals(db, 'pending'), ...listProposals(db, 'applied'), ...listProposals(db, 'rejected')]
|
|
509
|
-
: listProposals(db, status);
|
|
510
|
-
res.json({ success: true, data: rows });
|
|
511
|
-
}).catch((err) => res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) }));
|
|
512
|
-
}
|
|
513
|
-
catch (err) {
|
|
514
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
515
|
-
}
|
|
497
|
+
const query = parseQuery(DreamProposalsQuerySchema, req, res);
|
|
498
|
+
if (!query)
|
|
499
|
+
return;
|
|
500
|
+
handleGet(res, async () => {
|
|
501
|
+
const { listProposals } = await import('../../core/dreamer.js');
|
|
502
|
+
const db = getDatabase();
|
|
503
|
+
return query.status === 'all'
|
|
504
|
+
? [...listProposals(db, 'pending'), ...listProposals(db, 'applied'), ...listProposals(db, 'rejected')]
|
|
505
|
+
: listProposals(db, query.status);
|
|
506
|
+
});
|
|
516
507
|
});
|
|
517
508
|
app.get('/v1/dream/proposals/:id', (req, res) => {
|
|
518
|
-
const id =
|
|
519
|
-
if (
|
|
520
|
-
res.status(400).json({ success: false, errorCode: 'validation.bad-param', error: 'invalid id' });
|
|
509
|
+
const id = requireIdParam(req, res);
|
|
510
|
+
if (id === null)
|
|
521
511
|
return;
|
|
522
|
-
|
|
523
|
-
try {
|
|
512
|
+
handleGet(res, () => {
|
|
524
513
|
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);
|
|
525
514
|
if (!row) {
|
|
526
|
-
|
|
527
|
-
return;
|
|
515
|
+
throw new HttpError(404, 'resource.not-found', `proposal #${id} not found`);
|
|
528
516
|
}
|
|
529
517
|
let digest = null;
|
|
530
518
|
let sourceIds = [];
|
|
@@ -536,11 +524,8 @@ app.get('/v1/dream/proposals/:id', (req, res) => {
|
|
|
536
524
|
sourceIds = JSON.parse(row.source_ids);
|
|
537
525
|
}
|
|
538
526
|
catch { }
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
catch (err) {
|
|
542
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
543
|
-
}
|
|
527
|
+
return { ...row, proposed_digest: digest, source_ids: sourceIds };
|
|
528
|
+
});
|
|
544
529
|
});
|
|
545
530
|
const DreamRunBody = z.object({
|
|
546
531
|
project: z.string().min(1).max(100).optional(),
|
|
@@ -548,96 +533,64 @@ const DreamRunBody = z.object({
|
|
|
548
533
|
maxLlmCalls: z.number().int().min(1).max(20).default(5),
|
|
549
534
|
validate: z.boolean().default(false),
|
|
550
535
|
});
|
|
551
|
-
app.post('/v1/dream/run',
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
error: 'No LLM configured — dream run requires Smart Mode. Configure a provider in Settings.',
|
|
570
|
-
});
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
const result = await runDreamer(getDatabase(), llm, {
|
|
574
|
-
project: parsed.data.project,
|
|
575
|
-
windowDays: parsed.data.windowDays,
|
|
576
|
-
maxLlmCalls: parsed.data.maxLlmCalls,
|
|
577
|
-
fallbacks: caps.llmFallbacks,
|
|
578
|
-
validateBeforeStage: parsed.data.validate,
|
|
579
|
-
});
|
|
580
|
-
res.json({ success: true, data: result });
|
|
581
|
-
}
|
|
582
|
-
catch (err) {
|
|
583
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
584
|
-
}
|
|
585
|
-
});
|
|
586
|
-
app.post('/v1/dream/proposals/:id/accept', async (req, res) => {
|
|
587
|
-
const id = parseInt(req.params.id, 10);
|
|
588
|
-
if (!Number.isInteger(id) || id < 1) {
|
|
589
|
-
res.status(400).json({ success: false, errorCode: 'validation.bad-param', error: 'invalid id' });
|
|
536
|
+
app.post('/v1/dream/run', (req, res) => handlePost(DreamRunBody, req, res, async (data) => {
|
|
537
|
+
const { runDreamer } = await import('../../core/dreamer.js');
|
|
538
|
+
const caps = detectCapabilities();
|
|
539
|
+
const llm = caps.llm;
|
|
540
|
+
if (!llm) {
|
|
541
|
+
throw new HttpError(400, 'llm.not-configured', 'No LLM configured — dream run requires Smart Mode. Configure a provider in Settings.');
|
|
542
|
+
}
|
|
543
|
+
return runDreamer(getDatabase(), llm, {
|
|
544
|
+
project: data.project,
|
|
545
|
+
windowDays: data.windowDays,
|
|
546
|
+
maxLlmCalls: data.maxLlmCalls,
|
|
547
|
+
fallbacks: caps.llmFallbacks,
|
|
548
|
+
validateBeforeStage: data.validate,
|
|
549
|
+
});
|
|
550
|
+
}, { allowEmptyBody: true, errorStatus: 500, errorCode: 'server.internal' }));
|
|
551
|
+
app.post('/v1/dream/proposals/:id/accept', (req, res) => {
|
|
552
|
+
const id = requireIdParam(req, res);
|
|
553
|
+
if (id === null)
|
|
590
554
|
return;
|
|
591
|
-
|
|
592
|
-
let NothingToClaim;
|
|
593
|
-
try {
|
|
555
|
+
handleGet(res, async () => {
|
|
594
556
|
const dreamer = await import('../../core/dreamer.js');
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
res.json({ success: true, data: result });
|
|
599
|
-
}
|
|
600
|
-
catch (err) {
|
|
601
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
602
|
-
if (NothingToClaim && err instanceof NothingToClaim) {
|
|
603
|
-
res.status(400).json({ success: false, errorCode: 'operation.failed', error: msg });
|
|
604
|
-
}
|
|
605
|
-
else if (/not found or not pending/.test(msg)) {
|
|
606
|
-
res.status(404).json({ success: false, errorCode: 'resource.not-found', error: msg });
|
|
557
|
+
try {
|
|
558
|
+
const kg = new KnowledgeGraph(getDatabase());
|
|
559
|
+
return dreamer.applyProposal(getDatabase(), id, kg);
|
|
607
560
|
}
|
|
608
|
-
|
|
609
|
-
|
|
561
|
+
catch (err) {
|
|
562
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
563
|
+
if (err instanceof dreamer.NothingToClaimError) {
|
|
564
|
+
throw new HttpError(400, 'operation.failed', msg);
|
|
565
|
+
}
|
|
566
|
+
if (/not found or not pending/.test(msg)) {
|
|
567
|
+
throw new HttpError(404, 'resource.not-found', msg);
|
|
568
|
+
}
|
|
569
|
+
throw err;
|
|
610
570
|
}
|
|
611
|
-
}
|
|
571
|
+
});
|
|
612
572
|
});
|
|
613
573
|
const RejectBodySchema = z.object({
|
|
614
574
|
reason: z.string().max(500).optional(),
|
|
615
575
|
});
|
|
616
|
-
app.post('/v1/dream/proposals/:id/reject',
|
|
617
|
-
const id =
|
|
618
|
-
if (
|
|
619
|
-
res.status(400).json({ success: false, errorCode: 'validation.bad-param', error: 'invalid id' });
|
|
620
|
-
return;
|
|
621
|
-
}
|
|
622
|
-
const parsed = RejectBodySchema.safeParse(req.body ?? {});
|
|
623
|
-
if (!parsed.success) {
|
|
624
|
-
res.status(400).json({ success: false, errorCode: 'validation.bad-body', error: parsed.error.issues.map(i => i.message).join('; ') });
|
|
576
|
+
app.post('/v1/dream/proposals/:id/reject', (req, res) => {
|
|
577
|
+
const id = requireIdParam(req, res);
|
|
578
|
+
if (id === null)
|
|
625
579
|
return;
|
|
626
|
-
|
|
627
|
-
try {
|
|
580
|
+
handlePost(RejectBodySchema, req, res, async (data) => {
|
|
628
581
|
const { rejectProposal } = await import('../../core/dreamer.js');
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
}
|
|
632
|
-
catch (err) {
|
|
633
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
634
|
-
if (/not found or not pending/.test(msg)) {
|
|
635
|
-
res.status(404).json({ success: false, errorCode: 'resource.not-found', error: msg });
|
|
582
|
+
try {
|
|
583
|
+
rejectProposal(getDatabase(), id, data.reason);
|
|
636
584
|
}
|
|
637
|
-
|
|
638
|
-
|
|
585
|
+
catch (err) {
|
|
586
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
587
|
+
if (/not found or not pending/.test(msg)) {
|
|
588
|
+
throw new HttpError(404, 'resource.not-found', msg);
|
|
589
|
+
}
|
|
590
|
+
throw err;
|
|
639
591
|
}
|
|
640
|
-
|
|
592
|
+
return { id, status: 'rejected' };
|
|
593
|
+
}, { allowEmptyBody: true, errorStatus: 500, errorCode: 'server.internal' });
|
|
641
594
|
});
|
|
642
595
|
const EntitiesQuerySchema = z.object({
|
|
643
596
|
type: z.string().min(1).max(100).optional(),
|
|
@@ -645,40 +598,26 @@ const EntitiesQuerySchema = z.object({
|
|
|
645
598
|
status: z.enum(['all', 'active']).optional(),
|
|
646
599
|
});
|
|
647
600
|
app.get('/v1/entities', (req, res) => {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}
|
|
654
|
-
const { type: typeFilter, limit, status } = parsed.data;
|
|
601
|
+
const query = parseQuery(EntitiesQuerySchema, req, res);
|
|
602
|
+
if (!query)
|
|
603
|
+
return;
|
|
604
|
+
handleGet(res, () => {
|
|
605
|
+
const { type: typeFilter, limit, status } = query;
|
|
655
606
|
const includeArchived = status === 'all';
|
|
656
|
-
const
|
|
657
|
-
|
|
658
|
-
const entities = typeFilter
|
|
607
|
+
const kg = new KnowledgeGraph(getDatabase());
|
|
608
|
+
return typeFilter
|
|
659
609
|
? kg.listByType(typeFilter, limit, includeArchived)
|
|
660
610
|
: kg.listRecent(limit, includeArchived);
|
|
661
|
-
|
|
662
|
-
}
|
|
663
|
-
catch (err) {
|
|
664
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
app.get('/v1/entities/:name', (req, res) => {
|
|
668
|
-
try {
|
|
669
|
-
const db = getDatabase();
|
|
670
|
-
const kg = new KnowledgeGraph(db);
|
|
671
|
-
const entity = kg.getEntity(req.params.name);
|
|
672
|
-
if (!entity) {
|
|
673
|
-
res.status(404).json({ success: false, errorCode: 'resource.not-found', error: `Entity "${req.params.name}" not found` });
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
res.json({ success: true, data: entity });
|
|
677
|
-
}
|
|
678
|
-
catch (err) {
|
|
679
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
680
|
-
}
|
|
611
|
+
});
|
|
681
612
|
});
|
|
613
|
+
app.get('/v1/entities/:name', (req, res) => handleGet(res, () => {
|
|
614
|
+
const kg = new KnowledgeGraph(getDatabase());
|
|
615
|
+
const entity = kg.getEntity(String(req.params.name));
|
|
616
|
+
if (!entity) {
|
|
617
|
+
throw new HttpError(404, 'resource.not-found', `Entity "${String(req.params.name)}" not found`);
|
|
618
|
+
}
|
|
619
|
+
return entity;
|
|
620
|
+
}));
|
|
682
621
|
const HOST = process.env.MEMESH_HTTP_HOST || '127.0.0.1';
|
|
683
622
|
const PORT = parseInt(process.env.MEMESH_HTTP_PORT || '3737');
|
|
684
623
|
const ALLOW_REMOTE_BY_ENV = /^(1|true|yes)$/i.test(process.env.MEMESH_HTTP_ALLOW_REMOTE || '');
|