@poprobertdaniel/openclaw-memory 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +410 -0
  3. package/dist/chunk-CRPEAZ44.cjs +1881 -0
  4. package/dist/chunk-CRPEAZ44.cjs.map +1 -0
  5. package/dist/chunk-JNWCMHOB.js +309 -0
  6. package/dist/chunk-JNWCMHOB.js.map +1 -0
  7. package/dist/chunk-JSQBXYDM.js +1881 -0
  8. package/dist/chunk-JSQBXYDM.js.map +1 -0
  9. package/dist/chunk-NHFPLDZK.js +236 -0
  10. package/dist/chunk-NHFPLDZK.js.map +1 -0
  11. package/dist/chunk-NMUPGLJW.cjs +752 -0
  12. package/dist/chunk-NMUPGLJW.cjs.map +1 -0
  13. package/dist/chunk-RFLG2CCR.js +752 -0
  14. package/dist/chunk-RFLG2CCR.js.map +1 -0
  15. package/dist/chunk-VXULEX3A.cjs +236 -0
  16. package/dist/chunk-VXULEX3A.cjs.map +1 -0
  17. package/dist/chunk-ZY2C2CJQ.cjs +309 -0
  18. package/dist/chunk-ZY2C2CJQ.cjs.map +1 -0
  19. package/dist/cli/index.cjs +764 -0
  20. package/dist/cli/index.cjs.map +1 -0
  21. package/dist/cli/index.d.cts +1 -0
  22. package/dist/cli/index.d.ts +1 -0
  23. package/dist/cli/index.js +764 -0
  24. package/dist/cli/index.js.map +1 -0
  25. package/dist/index.cjs +48 -0
  26. package/dist/index.cjs.map +1 -0
  27. package/dist/index.d.cts +790 -0
  28. package/dist/index.d.ts +790 -0
  29. package/dist/index.js +48 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/memory-service-6WDMF6KX.cjs +9 -0
  32. package/dist/memory-service-6WDMF6KX.cjs.map +1 -0
  33. package/dist/memory-service-GKEG6J2D.js +9 -0
  34. package/dist/memory-service-GKEG6J2D.js.map +1 -0
  35. package/dist/server-BTbRv-yX.d.cts +1199 -0
  36. package/dist/server-BTbRv-yX.d.ts +1199 -0
  37. package/dist/server.cjs +9 -0
  38. package/dist/server.cjs.map +1 -0
  39. package/dist/server.d.cts +2 -0
  40. package/dist/server.d.ts +2 -0
  41. package/dist/server.js +9 -0
  42. package/dist/server.js.map +1 -0
  43. package/docker/full.yml +45 -0
  44. package/docker/standard.yml +26 -0
  45. package/package.json +109 -0
  46. package/skill/SKILL.md +139 -0
  47. package/templates/.env.example +42 -0
  48. package/templates/docker-compose.full.yml +45 -0
  49. package/templates/docker-compose.standard.yml +26 -0
  50. package/templates/openclaw-memory.config.ts +49 -0
@@ -0,0 +1,752 @@
1
+ import {
2
+ ConversationSummarizer,
3
+ SearchEngine,
4
+ StorageOrchestrator
5
+ } from "./chunk-JSQBXYDM.js";
6
+ import {
7
+ AuthError,
8
+ MemoryError,
9
+ NotFoundError,
10
+ ValidationError,
11
+ configSummary,
12
+ loadConfig
13
+ } from "./chunk-JNWCMHOB.js";
14
+
15
+ // src/server.ts
16
+ import { fileURLToPath } from "url";
17
+ import { resolve } from "path";
18
+
19
+ // src/api/router.ts
20
+ import { Elysia as Elysia6 } from "elysia";
21
+ import { cors } from "@elysiajs/cors";
22
+
23
+ // src/api/memories.ts
24
+ import { Elysia, t } from "elysia";
25
+ function memoriesRoutes(orchestrator) {
26
+ return new Elysia({ prefix: "/api/memories" }).post(
27
+ "/",
28
+ async ({ body, set }) => {
29
+ try {
30
+ const result = await orchestrator.createMemory(body);
31
+ set.status = 201;
32
+ return result;
33
+ } catch (error) {
34
+ set.status = 500;
35
+ return {
36
+ error: "Failed to create memory",
37
+ details: error instanceof Error ? error.message : String(error)
38
+ };
39
+ }
40
+ },
41
+ {
42
+ body: t.Object({
43
+ agent_id: t.String(),
44
+ scope: t.Union([
45
+ t.Literal("user"),
46
+ t.Literal("agent"),
47
+ t.Literal("global"),
48
+ t.Literal("project"),
49
+ t.Literal("session")
50
+ ]),
51
+ subject_id: t.Optional(t.Nullable(t.String())),
52
+ content: t.String(),
53
+ tags: t.Optional(t.Array(t.String())),
54
+ source: t.Optional(
55
+ t.Union([
56
+ t.Literal("explicit"),
57
+ t.Literal("derived"),
58
+ t.Literal("observation"),
59
+ t.Literal("conversation_summary"),
60
+ t.Literal("entity_extraction"),
61
+ t.Literal("daily_digest"),
62
+ t.Literal("migration")
63
+ ])
64
+ ),
65
+ created_by: t.Optional(t.Nullable(t.String())),
66
+ extract_entities: t.Optional(t.Boolean()),
67
+ expires_at: t.Optional(t.Nullable(t.String()))
68
+ })
69
+ }
70
+ ).get("/:id", ({ params, set }) => {
71
+ const memory = orchestrator.sqlite.getMemory(params.id);
72
+ if (!memory) {
73
+ set.status = 404;
74
+ return { error: "Memory not found" };
75
+ }
76
+ return memory;
77
+ }).put(
78
+ "/:id",
79
+ async ({ params, body, set }) => {
80
+ try {
81
+ const result = await orchestrator.updateMemory(
82
+ params.id,
83
+ body
84
+ );
85
+ if (!result) {
86
+ set.status = 404;
87
+ return { error: "Memory not found" };
88
+ }
89
+ return result;
90
+ } catch (error) {
91
+ set.status = 500;
92
+ return {
93
+ error: "Failed to update memory",
94
+ details: error instanceof Error ? error.message : String(error)
95
+ };
96
+ }
97
+ },
98
+ {
99
+ body: t.Object({
100
+ content: t.Optional(t.String()),
101
+ tags: t.Optional(t.Array(t.String())),
102
+ scope: t.Optional(
103
+ t.Union([
104
+ t.Literal("user"),
105
+ t.Literal("agent"),
106
+ t.Literal("global"),
107
+ t.Literal("project"),
108
+ t.Literal("session")
109
+ ])
110
+ ),
111
+ subject_id: t.Optional(t.Nullable(t.String())),
112
+ expires_at: t.Optional(t.Nullable(t.String())),
113
+ extract_entities: t.Optional(t.Boolean())
114
+ })
115
+ }
116
+ ).delete("/:id", async ({ params, set }) => {
117
+ try {
118
+ const deleted = await orchestrator.deleteMemory(params.id);
119
+ if (!deleted) {
120
+ set.status = 404;
121
+ return { error: "Memory not found" };
122
+ }
123
+ return { deleted: true, id: params.id };
124
+ } catch (error) {
125
+ set.status = 500;
126
+ return {
127
+ error: "Failed to delete memory",
128
+ details: error instanceof Error ? error.message : String(error)
129
+ };
130
+ }
131
+ }).get("/", ({ query }) => {
132
+ const memories = orchestrator.sqlite.listMemories({
133
+ agent_id: query.agent_id,
134
+ scope: query.scope,
135
+ subject_id: query.subject_id,
136
+ source: query.source,
137
+ tags: query.tags,
138
+ limit: query.limit ? parseInt(String(query.limit), 10) : void 0,
139
+ offset: query.offset ? parseInt(String(query.offset), 10) : void 0,
140
+ order: query.order || "desc"
141
+ });
142
+ return { memories, count: memories.length };
143
+ });
144
+ }
145
+
146
+ // src/api/search.ts
147
+ import { Elysia as Elysia2, t as t2 } from "elysia";
148
+ function searchRoutes(orchestrator) {
149
+ const searchEngine = new SearchEngine(orchestrator);
150
+ return new Elysia2({ prefix: "/api/search" }).post(
151
+ "/",
152
+ async ({ body, set }) => {
153
+ try {
154
+ const req = body;
155
+ if (!req.agent_id) req.cross_agent = true;
156
+ return await searchEngine.search(req);
157
+ } catch (error) {
158
+ set.status = 500;
159
+ return {
160
+ error: "Search failed",
161
+ details: error instanceof Error ? error.message : String(error)
162
+ };
163
+ }
164
+ },
165
+ {
166
+ body: t2.Object({
167
+ agent_id: t2.Optional(t2.String()),
168
+ query: t2.String(),
169
+ scopes: t2.Optional(
170
+ t2.Array(
171
+ t2.Union([
172
+ t2.Literal("user"),
173
+ t2.Literal("agent"),
174
+ t2.Literal("global"),
175
+ t2.Literal("project"),
176
+ t2.Literal("session")
177
+ ])
178
+ )
179
+ ),
180
+ subject_id: t2.Optional(t2.Nullable(t2.String())),
181
+ limit: t2.Optional(t2.Number()),
182
+ include_graph: t2.Optional(t2.Boolean()),
183
+ cross_agent: t2.Optional(t2.Boolean()),
184
+ strategy: t2.Optional(
185
+ t2.Union([
186
+ t2.Literal("auto"),
187
+ t2.Literal("semantic"),
188
+ t2.Literal("fulltext"),
189
+ t2.Literal("graph"),
190
+ t2.Literal("all")
191
+ ])
192
+ )
193
+ })
194
+ }
195
+ ).post(
196
+ "/semantic",
197
+ async ({ body, set }) => {
198
+ try {
199
+ return await searchEngine.search({
200
+ ...body,
201
+ strategy: "semantic"
202
+ });
203
+ } catch (error) {
204
+ set.status = 500;
205
+ return {
206
+ error: "Semantic search failed",
207
+ details: error instanceof Error ? error.message : String(error)
208
+ };
209
+ }
210
+ },
211
+ {
212
+ body: t2.Object({
213
+ agent_id: t2.Optional(t2.String()),
214
+ query: t2.String(),
215
+ scopes: t2.Optional(
216
+ t2.Array(
217
+ t2.Union([
218
+ t2.Literal("user"),
219
+ t2.Literal("agent"),
220
+ t2.Literal("global"),
221
+ t2.Literal("project"),
222
+ t2.Literal("session")
223
+ ])
224
+ )
225
+ ),
226
+ subject_id: t2.Optional(t2.Nullable(t2.String())),
227
+ limit: t2.Optional(t2.Number()),
228
+ cross_agent: t2.Optional(t2.Boolean())
229
+ })
230
+ }
231
+ ).post(
232
+ "/graph",
233
+ async ({ body, set }) => {
234
+ try {
235
+ return await searchEngine.search({
236
+ ...body,
237
+ strategy: "graph",
238
+ include_graph: true
239
+ });
240
+ } catch (error) {
241
+ set.status = 500;
242
+ return {
243
+ error: "Graph search failed",
244
+ details: error instanceof Error ? error.message : String(error)
245
+ };
246
+ }
247
+ },
248
+ {
249
+ body: t2.Object({
250
+ agent_id: t2.Optional(t2.String()),
251
+ query: t2.String(),
252
+ scopes: t2.Optional(
253
+ t2.Array(
254
+ t2.Union([
255
+ t2.Literal("user"),
256
+ t2.Literal("agent"),
257
+ t2.Literal("global"),
258
+ t2.Literal("project"),
259
+ t2.Literal("session")
260
+ ])
261
+ )
262
+ ),
263
+ subject_id: t2.Optional(t2.Nullable(t2.String())),
264
+ limit: t2.Optional(t2.Number())
265
+ })
266
+ }
267
+ ).post(
268
+ "/fulltext",
269
+ async ({ body, set }) => {
270
+ try {
271
+ return await searchEngine.search({
272
+ ...body,
273
+ strategy: "fulltext",
274
+ include_graph: false
275
+ });
276
+ } catch (error) {
277
+ set.status = 500;
278
+ return {
279
+ error: "Fulltext search failed",
280
+ details: error instanceof Error ? error.message : String(error)
281
+ };
282
+ }
283
+ },
284
+ {
285
+ body: t2.Object({
286
+ agent_id: t2.Optional(t2.String()),
287
+ query: t2.String(),
288
+ scopes: t2.Optional(t2.Array(t2.String())),
289
+ subject_id: t2.Optional(t2.Nullable(t2.String())),
290
+ limit: t2.Optional(t2.Number())
291
+ })
292
+ }
293
+ );
294
+ }
295
+
296
+ // src/api/conversations.ts
297
+ import { Elysia as Elysia3, t as t3 } from "elysia";
298
+ function conversationRoutes(orchestrator, config) {
299
+ const summarizer = config.extraction ? new ConversationSummarizer({
300
+ apiKey: config.extraction.apiKey,
301
+ baseUrl: config.extraction.baseUrl,
302
+ model: config.extraction.model
303
+ }) : null;
304
+ return new Elysia3({ prefix: "/api/conversations" }).post(
305
+ "/log",
306
+ ({ body, set }) => {
307
+ try {
308
+ orchestrator.sqlite.appendConversationLog(body);
309
+ set.status = 201;
310
+ return { ok: true };
311
+ } catch (error) {
312
+ set.status = 500;
313
+ return {
314
+ error: "Failed to append conversation log",
315
+ details: error instanceof Error ? error.message : String(error)
316
+ };
317
+ }
318
+ },
319
+ {
320
+ body: t3.Object({
321
+ agent_id: t3.String(),
322
+ session_id: t3.String(),
323
+ user_id: t3.String(),
324
+ channel: t3.String(),
325
+ role: t3.Union([
326
+ t3.Literal("user"),
327
+ t3.Literal("assistant"),
328
+ t3.Literal("system")
329
+ ]),
330
+ content: t3.String(),
331
+ timestamp: t3.String()
332
+ })
333
+ }
334
+ ).post(
335
+ "/summarize",
336
+ async ({ body, set }) => {
337
+ if (!summarizer) {
338
+ set.status = 501;
339
+ return { error: "Summarization not available \u2014 extraction config required" };
340
+ }
341
+ try {
342
+ const { agent_id, session_id, user_id, channel, messages } = body;
343
+ const summary = await summarizer.summarize(messages);
344
+ if (!summary) {
345
+ set.status = 422;
346
+ return { error: "Failed to generate summary" };
347
+ }
348
+ const memoryReq = {
349
+ agent_id,
350
+ scope: "session",
351
+ subject_id: user_id,
352
+ content: summary,
353
+ tags: ["conversation_summary", channel, `session:${session_id}`],
354
+ source: "conversation_summary",
355
+ created_by: agent_id,
356
+ extract_entities: true
357
+ };
358
+ const result = await orchestrator.createMemory(memoryReq);
359
+ const response = {
360
+ memory_id: result.id,
361
+ summary,
362
+ entities_extracted: result.entities,
363
+ relationships_created: 0
364
+ };
365
+ set.status = 201;
366
+ return response;
367
+ } catch (error) {
368
+ set.status = 500;
369
+ return {
370
+ error: "Summarization failed",
371
+ details: error instanceof Error ? error.message : String(error)
372
+ };
373
+ }
374
+ },
375
+ {
376
+ body: t3.Object({
377
+ agent_id: t3.String(),
378
+ session_id: t3.String(),
379
+ user_id: t3.String(),
380
+ channel: t3.String(),
381
+ messages: t3.Array(
382
+ t3.Object({
383
+ role: t3.Union([
384
+ t3.Literal("user"),
385
+ t3.Literal("assistant"),
386
+ t3.Literal("system")
387
+ ]),
388
+ content: t3.String(),
389
+ timestamp: t3.String()
390
+ })
391
+ ),
392
+ reason: t3.Optional(t3.String())
393
+ })
394
+ }
395
+ );
396
+ }
397
+
398
+ // src/api/entities.ts
399
+ import { Elysia as Elysia4, t as t4 } from "elysia";
400
+ function entityRoutes(orchestrator) {
401
+ return new Elysia4({ prefix: "/api/entities" }).get("/:type", async ({ params, query, set }) => {
402
+ if (!orchestrator.age) {
403
+ set.status = 501;
404
+ return { error: "Graph layer not available \u2014 requires Full tier" };
405
+ }
406
+ try {
407
+ const entities = await orchestrator.age.listEntities(
408
+ params.type,
409
+ query.agent_id,
410
+ query.limit ? parseInt(String(query.limit), 10) : 50
411
+ );
412
+ return { entities, count: entities.length };
413
+ } catch (error) {
414
+ set.status = 500;
415
+ return {
416
+ error: "Failed to list entities",
417
+ details: error instanceof Error ? error.message : String(error)
418
+ };
419
+ }
420
+ }).get("/:type/:id", async ({ params, set }) => {
421
+ if (!orchestrator.age) {
422
+ set.status = 501;
423
+ return { error: "Graph layer not available \u2014 requires Full tier" };
424
+ }
425
+ try {
426
+ const result = await orchestrator.age.getEntityWithRelationships(
427
+ params.type,
428
+ params.id
429
+ );
430
+ if (!result.entity) {
431
+ set.status = 404;
432
+ return { error: "Entity not found" };
433
+ }
434
+ return result;
435
+ } catch (error) {
436
+ set.status = 500;
437
+ return {
438
+ error: "Failed to get entity",
439
+ details: error instanceof Error ? error.message : String(error)
440
+ };
441
+ }
442
+ }).get("/:type/:id/related", async ({ params, query, set }) => {
443
+ if (!orchestrator.age) {
444
+ set.status = 501;
445
+ return { error: "Graph layer not available \u2014 requires Full tier" };
446
+ }
447
+ try {
448
+ const depth = query.depth ? parseInt(String(query.depth), 10) : 2;
449
+ const related = await orchestrator.age.getRelatedEntities(
450
+ params.id,
451
+ depth
452
+ );
453
+ return { related, count: related.length };
454
+ } catch (error) {
455
+ set.status = 500;
456
+ return {
457
+ error: "Failed to get related entities",
458
+ details: error instanceof Error ? error.message : String(error)
459
+ };
460
+ }
461
+ }).post(
462
+ "/extract",
463
+ async ({ body, set }) => {
464
+ if (!orchestrator.entityExtractor) {
465
+ set.status = 501;
466
+ return { error: "Entity extraction not available \u2014 requires extraction config" };
467
+ }
468
+ try {
469
+ const result = await orchestrator.entityExtractor.extract(body.text);
470
+ return result;
471
+ } catch (error) {
472
+ set.status = 500;
473
+ return {
474
+ error: "Entity extraction failed",
475
+ details: error instanceof Error ? error.message : String(error)
476
+ };
477
+ }
478
+ },
479
+ {
480
+ body: t4.Object({
481
+ text: t4.String()
482
+ })
483
+ }
484
+ );
485
+ }
486
+
487
+ // src/api/admin.ts
488
+ import { Elysia as Elysia5, t as t5 } from "elysia";
489
+ import fs from "fs";
490
+ import path from "path";
491
+ function adminRoutes(orchestrator) {
492
+ return new Elysia5().get("/api/health", async () => {
493
+ return await orchestrator.healthCheck();
494
+ }).post("/api/sync/retry", async ({ set }) => {
495
+ try {
496
+ const result = await orchestrator.retrySyncQueue();
497
+ return result;
498
+ } catch (error) {
499
+ set.status = 500;
500
+ return {
501
+ error: "Sync retry failed",
502
+ details: error instanceof Error ? error.message : String(error)
503
+ };
504
+ }
505
+ }).get("/api/sync/queue", () => {
506
+ const items = orchestrator.sqlite.getSyncQueue(100);
507
+ return { items, count: items.length };
508
+ }).post(
509
+ "/api/admin/migrate-markdown",
510
+ async ({ body, set }) => {
511
+ try {
512
+ const results = await migrateMarkdownFiles(orchestrator, body);
513
+ return results;
514
+ } catch (error) {
515
+ set.status = 500;
516
+ return {
517
+ error: "Migration failed",
518
+ details: error instanceof Error ? error.message : String(error)
519
+ };
520
+ }
521
+ },
522
+ {
523
+ body: t5.Object({
524
+ markdown_paths: t5.Array(t5.String()),
525
+ agent_id: t5.String(),
526
+ dry_run: t5.Optional(t5.Boolean())
527
+ })
528
+ }
529
+ ).post("/api/admin/daily-digest", async ({ set }) => {
530
+ set.status = 501;
531
+ return { error: "Not yet implemented" };
532
+ });
533
+ }
534
+ async function migrateMarkdownFiles(orchestrator, request) {
535
+ const { markdown_paths, agent_id, dry_run } = request;
536
+ let migrated = 0;
537
+ let skipped = 0;
538
+ const errors = [];
539
+ const memories = [];
540
+ for (const filePath of markdown_paths) {
541
+ try {
542
+ if (!fs.existsSync(filePath)) {
543
+ errors.push(`File not found: ${filePath}`);
544
+ skipped++;
545
+ continue;
546
+ }
547
+ const content = fs.readFileSync(filePath, "utf-8");
548
+ const fileName = path.basename(filePath, ".md");
549
+ const sections = parseMarkdownSections(content);
550
+ for (const section of sections) {
551
+ if (section.content.trim().length < 10) {
552
+ skipped++;
553
+ continue;
554
+ }
555
+ if (dry_run) {
556
+ memories.push({
557
+ id: "(dry-run)",
558
+ content_preview: section.content.slice(0, 100)
559
+ });
560
+ migrated++;
561
+ continue;
562
+ }
563
+ const scope = inferScope(section.heading, fileName);
564
+ const source = inferSource(fileName);
565
+ const tags = inferTags(section.heading, fileName);
566
+ const req = {
567
+ agent_id,
568
+ scope,
569
+ subject_id: null,
570
+ content: section.content.trim(),
571
+ tags,
572
+ source: source || "migration",
573
+ created_by: "migration",
574
+ extract_entities: true
575
+ };
576
+ try {
577
+ const result = await orchestrator.createMemory(req);
578
+ memories.push({
579
+ id: result.id,
580
+ content_preview: section.content.slice(0, 100)
581
+ });
582
+ migrated++;
583
+ } catch (error) {
584
+ errors.push(
585
+ `Failed to migrate section "${section.heading}": ${error instanceof Error ? error.message : String(error)}`
586
+ );
587
+ }
588
+ }
589
+ } catch (error) {
590
+ errors.push(
591
+ `Failed to process ${filePath}: ${error instanceof Error ? error.message : String(error)}`
592
+ );
593
+ }
594
+ }
595
+ return { migrated, skipped, errors, memories };
596
+ }
597
+ function parseMarkdownSections(markdown) {
598
+ const lines = markdown.split("\n");
599
+ const sections = [];
600
+ let currentHeading = "root";
601
+ let currentLevel = 0;
602
+ let currentContent = [];
603
+ for (const line of lines) {
604
+ const headingMatch = line.match(/^(#{1,3})\s+(.+)/);
605
+ if (headingMatch) {
606
+ if (currentContent.length > 0) {
607
+ sections.push({
608
+ heading: currentHeading,
609
+ content: currentContent.join("\n").trim(),
610
+ level: currentLevel
611
+ });
612
+ }
613
+ currentHeading = headingMatch[2].trim();
614
+ currentLevel = headingMatch[1].length;
615
+ currentContent = [];
616
+ } else {
617
+ currentContent.push(line);
618
+ }
619
+ }
620
+ if (currentContent.length > 0) {
621
+ sections.push({
622
+ heading: currentHeading,
623
+ content: currentContent.join("\n").trim(),
624
+ level: currentLevel
625
+ });
626
+ }
627
+ return sections;
628
+ }
629
+ function inferScope(heading, fileName) {
630
+ const h = heading.toLowerCase();
631
+ const f = fileName.toLowerCase();
632
+ if (h.includes("about") || h.includes("personal")) return "user";
633
+ if (h.includes("project")) return "project";
634
+ if (h.includes("agent")) return "agent";
635
+ if (h.includes("session") || f.match(/^\d{4}-\d{2}-\d{2}/)) return "session";
636
+ return "global";
637
+ }
638
+ function inferSource(fileName) {
639
+ if (fileName.match(/^\d{4}-\d{2}-\d{2}/)) return "daily_digest";
640
+ return "migration";
641
+ }
642
+ function inferTags(heading, fileName) {
643
+ const tags = ["migration"];
644
+ if (fileName.match(/^\d{4}-\d{2}-\d{2}/)) {
645
+ tags.push("daily", fileName);
646
+ }
647
+ if (heading !== "root") {
648
+ tags.push(heading.toLowerCase().replace(/[^a-z0-9]+/g, "-"));
649
+ }
650
+ return tags;
651
+ }
652
+
653
+ // src/api/router.ts
654
+ function createApp(orchestrator, config) {
655
+ const app = new Elysia6().use(cors()).derive(({ headers, set, path: path2 }) => {
656
+ if (path2 === "/api/health") return {};
657
+ if (!config.auth.enabled || !config.auth.token) return {};
658
+ const authHeader = headers.authorization;
659
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
660
+ set.status = 401;
661
+ throw new AuthError("Missing or invalid Authorization header");
662
+ }
663
+ const token = authHeader.slice(7);
664
+ if (token !== config.auth.token) {
665
+ set.status = 403;
666
+ throw new AuthError("Invalid token");
667
+ }
668
+ return {};
669
+ }).onError(({ code, error, set }) => {
670
+ if (error instanceof AuthError) {
671
+ const status = error.message.includes("Invalid token") ? 403 : 401;
672
+ set.status = status;
673
+ return { error: error.message, code: error.code };
674
+ }
675
+ if (error instanceof NotFoundError) {
676
+ set.status = 404;
677
+ return { error: error.message, code: error.code };
678
+ }
679
+ if (error instanceof ValidationError) {
680
+ set.status = 400;
681
+ return { error: error.message, code: error.code, details: error.details };
682
+ }
683
+ if (error instanceof MemoryError) {
684
+ set.status = 500;
685
+ return { error: error.message, code: error.code };
686
+ }
687
+ if (code === "VALIDATION") {
688
+ const msg2 = error && "message" in error ? error.message : String(error);
689
+ set.status = 400;
690
+ return { error: "Validation error", details: msg2 };
691
+ }
692
+ const msg = error instanceof Error ? error.stack || error.message : String(error);
693
+ console.error(`[api] Unhandled error: ${msg}`);
694
+ set.status = 500;
695
+ return { error: "Internal server error" };
696
+ }).use(memoriesRoutes(orchestrator)).use(searchRoutes(orchestrator)).use(conversationRoutes(orchestrator, config)).use(entityRoutes(orchestrator)).use(adminRoutes(orchestrator));
697
+ return app;
698
+ }
699
+
700
+ // src/server.ts
701
+ async function createServer(configPath) {
702
+ const config = await loadConfig(configPath);
703
+ const orchestrator = new StorageOrchestrator(config);
704
+ await orchestrator.init();
705
+ const app = createApp(orchestrator, config);
706
+ return { app, orchestrator, config };
707
+ }
708
+ async function main() {
709
+ console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
710
+ console.log("\u2502 openclaw-memory service v0.1.0 \u2502");
711
+ console.log("\u2502 Triple-Layer Memory System \u2502");
712
+ console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
713
+ const { app, orchestrator, config } = await createServer();
714
+ console.log("[config]");
715
+ console.log(configSummary(config).split("\n").map((l) => ` ${l}`).join("\n"));
716
+ app.listen(config.port);
717
+ console.log(`[server] Listening on http://${config.host}:${config.port}`);
718
+ console.log(`[server] Health check: http://localhost:${config.port}/api/health`);
719
+ const shutdown = async () => {
720
+ console.log("\n[server] Shutting down...");
721
+ await orchestrator.close();
722
+ process.exit(0);
723
+ };
724
+ process.on("SIGINT", shutdown);
725
+ process.on("SIGTERM", shutdown);
726
+ }
727
+ function isMainModule() {
728
+ try {
729
+ if (typeof globalThis.Bun !== "undefined") {
730
+ const bun = globalThis.Bun;
731
+ return bun.main === fileURLToPath(import.meta.url);
732
+ }
733
+ if (process.argv[1]) {
734
+ return resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url));
735
+ }
736
+ return false;
737
+ } catch {
738
+ return false;
739
+ }
740
+ }
741
+ if (isMainModule()) {
742
+ main().catch((error) => {
743
+ console.error("[fatal]", error);
744
+ process.exit(1);
745
+ });
746
+ }
747
+
748
+ export {
749
+ createApp,
750
+ createServer
751
+ };
752
+ //# sourceMappingURL=chunk-RFLG2CCR.js.map