@pcircle/memesh 4.5.1 → 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/AGENTS.md +95 -0
- package/README.de.md +129 -35
- package/README.md +161 -34
- package/README.zh-TW.md +130 -35
- package/dashboard/dist/index.html +10 -10
- package/dist/cli/view-live.js +3 -3
- 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/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/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 +34 -56
- 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 +108 -25
- package/dist/core/dreamer.js.map +1 -1
- package/dist/core/embedder.d.ts +5 -4
- package/dist/core/embedder.d.ts.map +1 -1
- package/dist/core/embedder.js +16 -8
- 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/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 +0 -1
- package/dist/core/kg-backfill.d.ts.map +1 -1
- package/dist/core/kg-backfill.js +0 -3
- 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.map +1 -1
- package/dist/core/operations.js +22 -13
- 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/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 +4 -0
- package/dist/core/types.d.ts.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 +2 -7
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +144 -284
- 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 +48 -18
- 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/transports/cli/cli.d.ts +1 -4
- package/dist/transports/cli/cli.d.ts.map +1 -1
- package/dist/transports/cli/cli.js +382 -6
- 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 +208 -307
- 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 +57 -2
- package/dist/transports/mcp/handlers.js.map +1 -1
- package/dist/transports/schemas.d.ts +21 -10
- package/dist/transports/schemas.d.ts.map +1 -1
- package/dist/transports/schemas.js +26 -8
- package/dist/transports/schemas.js.map +1 -1
- 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/schema.js +281 -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 +122 -478
- package/scripts/hooks/post-commit.js +4 -1
- package/scripts/hooks/pre-compact.js +13 -1
- package/scripts/hooks/pre-edit-recall.js +5 -3
- package/scripts/hooks/session-start.js +135 -59
- package/scripts/hooks/session-summary.js +59 -24
- package/skills/memesh/SKILL.md +97 -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
|
@@ -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 } = await recallWithConflicts(data);
|
|
290
|
+
return conflicts.length > 0 ? { entities, conflicts } : { entities };
|
|
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'] });
|
|
@@ -305,16 +326,11 @@ function preserveFallbackApiKeys(incoming, stored) {
|
|
|
305
326
|
return clean;
|
|
306
327
|
});
|
|
307
328
|
}
|
|
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
|
-
});
|
|
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
|
+
}));
|
|
318
334
|
const ConfigBody = z.object({
|
|
319
335
|
llm: z.union([
|
|
320
336
|
z.object({
|
|
@@ -340,108 +356,71 @@ const ConfigBody = z.object({
|
|
|
340
356
|
.optional(),
|
|
341
357
|
setupCompleted: z.boolean().optional(),
|
|
342
358
|
}).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
|
-
}
|
|
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;
|
|
360
364
|
}
|
|
361
|
-
|
|
362
|
-
|
|
365
|
+
else {
|
|
366
|
+
delete data.llm.apiKey;
|
|
363
367
|
}
|
|
364
|
-
const updated = updateConfig(parsed.data);
|
|
365
|
-
res.json({ success: true, data: maskLlmSecrets(updated) });
|
|
366
368
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
+
if (data.llmFallbacks) {
|
|
370
|
+
data.llmFallbacks = preserveFallbackApiKeys(data.llmFallbacks, before.llmFallbacks);
|
|
369
371
|
}
|
|
370
|
-
|
|
372
|
+
const updated = updateConfig(data);
|
|
373
|
+
return maskLlmSecrets(updated);
|
|
374
|
+
}));
|
|
371
375
|
const ConfigTestBody = z.object({
|
|
372
376
|
provider: z.enum(['anthropic', 'openai', 'ollama']),
|
|
373
377
|
apiKey: z.string().max(500).optional(),
|
|
374
378
|
host: z.string().max(500).optional(),
|
|
375
379
|
fallbackIndex: z.number().int().nonnegative().optional(),
|
|
376
380
|
});
|
|
377
|
-
app.post('/v1/config/test',
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
if (!
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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
|
-
}
|
|
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;
|
|
403
394
|
}
|
|
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
|
-
}
|
|
410
|
-
});
|
|
411
|
-
app.get('/v1/update-status', async (req, res) => {
|
|
412
|
-
try {
|
|
413
|
-
const cached = req.query.cached === '1' || req.query.cached === 'true';
|
|
414
|
-
const install = getCurrentInstallChannel({ packageRoot });
|
|
415
|
-
const installSupport = getInstallChannelSupport(install);
|
|
416
|
-
const update = await getUpdateCheck(packageVersion, { preferFresh: !cached });
|
|
417
|
-
res.json({
|
|
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
|
-
},
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
catch (err) {
|
|
442
|
-
res.status(500).json({ success: false, errorCode: 'server.internal', error: err instanceof Error ? err.message : String(err) });
|
|
443
395
|
}
|
|
444
|
-
|
|
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
|
+
}));
|
|
445
424
|
app.get('/v1/graph', (_req, res) => handleGet(res, () => computeGraph(getDatabase())));
|
|
446
425
|
app.get('/v1/stats', (_req, res) => handleGet(res, () => computeStats(getDatabase())));
|
|
447
426
|
app.get('/v1/analytics', (_req, res) => handleGet(res, () => computeAnalytics(getDatabase())));
|
|
@@ -451,80 +430,51 @@ app.get('/v1/analytics/pm', (req, res) => {
|
|
|
451
430
|
const windowDays = Number.isFinite(window) && window > 0 ? window : 30;
|
|
452
431
|
handleGet(res, () => computePmAnalytics(getDatabase(), windowDays));
|
|
453
432
|
});
|
|
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
|
-
});
|
|
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
|
+
}));
|
|
474
441
|
app.get('/v1/projects', (_req, res) => handleGet(res, () => computeProjects(getDatabase())));
|
|
475
442
|
app.get('/v1/patterns', (_req, res) => handleGet(res, () => computePatterns(getDatabase())));
|
|
476
443
|
const TelemetryQuerySchema = z.object({
|
|
477
444
|
window: z.coerce.number().int().min(1).max(365).default(30),
|
|
478
445
|
});
|
|
479
|
-
app.get('/v1/telemetry',
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
446
|
+
app.get('/v1/telemetry', (req, res) => {
|
|
447
|
+
const query = parseQuery(TelemetryQuerySchema, req, res);
|
|
448
|
+
if (!query)
|
|
449
|
+
return;
|
|
450
|
+
handleGet(res, async () => {
|
|
486
451
|
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
|
-
}
|
|
452
|
+
return { window_days: query.window, summaries: summariseTelemetry(query.window) };
|
|
453
|
+
});
|
|
493
454
|
});
|
|
494
455
|
const DreamProposalsQuerySchema = z.object({
|
|
495
456
|
status: z.enum(['pending', 'applied', 'rejected', 'all']).default('pending'),
|
|
496
457
|
});
|
|
497
458
|
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
|
-
}
|
|
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
|
+
});
|
|
516
469
|
});
|
|
517
470
|
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' });
|
|
471
|
+
const id = requireIdParam(req, res);
|
|
472
|
+
if (id === null)
|
|
521
473
|
return;
|
|
522
|
-
|
|
523
|
-
try {
|
|
474
|
+
handleGet(res, () => {
|
|
524
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);
|
|
525
476
|
if (!row) {
|
|
526
|
-
|
|
527
|
-
return;
|
|
477
|
+
throw new HttpError(404, 'resource.not-found', `proposal #${id} not found`);
|
|
528
478
|
}
|
|
529
479
|
let digest = null;
|
|
530
480
|
let sourceIds = [];
|
|
@@ -536,11 +486,8 @@ app.get('/v1/dream/proposals/:id', (req, res) => {
|
|
|
536
486
|
sourceIds = JSON.parse(row.source_ids);
|
|
537
487
|
}
|
|
538
488
|
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
|
-
}
|
|
489
|
+
return { ...row, proposed_digest: digest, source_ids: sourceIds };
|
|
490
|
+
});
|
|
544
491
|
});
|
|
545
492
|
const DreamRunBody = z.object({
|
|
546
493
|
project: z.string().min(1).max(100).optional(),
|
|
@@ -548,96 +495,64 @@ const DreamRunBody = z.object({
|
|
|
548
495
|
maxLlmCalls: z.number().int().min(1).max(20).default(5),
|
|
549
496
|
validate: z.boolean().default(false),
|
|
550
497
|
});
|
|
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' });
|
|
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)
|
|
590
516
|
return;
|
|
591
|
-
|
|
592
|
-
let NothingToClaim;
|
|
593
|
-
try {
|
|
517
|
+
handleGet(res, async () => {
|
|
594
518
|
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 });
|
|
519
|
+
try {
|
|
520
|
+
const kg = new KnowledgeGraph(getDatabase());
|
|
521
|
+
return dreamer.applyProposal(getDatabase(), id, kg);
|
|
607
522
|
}
|
|
608
|
-
|
|
609
|
-
|
|
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;
|
|
610
532
|
}
|
|
611
|
-
}
|
|
533
|
+
});
|
|
612
534
|
});
|
|
613
535
|
const RejectBodySchema = z.object({
|
|
614
536
|
reason: z.string().max(500).optional(),
|
|
615
537
|
});
|
|
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('; ') });
|
|
538
|
+
app.post('/v1/dream/proposals/:id/reject', (req, res) => {
|
|
539
|
+
const id = requireIdParam(req, res);
|
|
540
|
+
if (id === null)
|
|
625
541
|
return;
|
|
626
|
-
|
|
627
|
-
try {
|
|
542
|
+
handlePost(RejectBodySchema, req, res, async (data) => {
|
|
628
543
|
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 });
|
|
544
|
+
try {
|
|
545
|
+
rejectProposal(getDatabase(), id, data.reason);
|
|
636
546
|
}
|
|
637
|
-
|
|
638
|
-
|
|
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;
|
|
639
553
|
}
|
|
640
|
-
|
|
554
|
+
return { id, status: 'rejected' };
|
|
555
|
+
}, { allowEmptyBody: true, errorStatus: 500, errorCode: 'server.internal' });
|
|
641
556
|
});
|
|
642
557
|
const EntitiesQuerySchema = z.object({
|
|
643
558
|
type: z.string().min(1).max(100).optional(),
|
|
@@ -645,40 +560,26 @@ const EntitiesQuerySchema = z.object({
|
|
|
645
560
|
status: z.enum(['all', 'active']).optional(),
|
|
646
561
|
});
|
|
647
562
|
app.get('/v1/entities', (req, res) => {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}
|
|
654
|
-
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;
|
|
655
568
|
const includeArchived = status === 'all';
|
|
656
|
-
const
|
|
657
|
-
|
|
658
|
-
const entities = typeFilter
|
|
569
|
+
const kg = new KnowledgeGraph(getDatabase());
|
|
570
|
+
return typeFilter
|
|
659
571
|
? kg.listByType(typeFilter, limit, includeArchived)
|
|
660
572
|
: 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
|
-
}
|
|
573
|
+
});
|
|
681
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
|
+
}));
|
|
682
583
|
const HOST = process.env.MEMESH_HTTP_HOST || '127.0.0.1';
|
|
683
584
|
const PORT = parseInt(process.env.MEMESH_HTTP_PORT || '3737');
|
|
684
585
|
const ALLOW_REMOTE_BY_ENV = /^(1|true|yes)$/i.test(process.env.MEMESH_HTTP_ALLOW_REMOTE || '');
|