dbrain 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 (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +205 -0
  3. package/dist/cli/connect.d.ts +2 -0
  4. package/dist/cli/connect.d.ts.map +1 -0
  5. package/dist/cli/connect.js +108 -0
  6. package/dist/cli/connect.js.map +1 -0
  7. package/dist/cli/index.d.ts +3 -0
  8. package/dist/cli/index.d.ts.map +1 -0
  9. package/dist/cli/index.js +46 -0
  10. package/dist/cli/index.js.map +1 -0
  11. package/dist/cli/init.d.ts +4 -0
  12. package/dist/cli/init.d.ts.map +1 -0
  13. package/dist/cli/init.js +177 -0
  14. package/dist/cli/init.js.map +1 -0
  15. package/dist/cli/start.d.ts +2 -0
  16. package/dist/cli/start.d.ts.map +1 -0
  17. package/dist/cli/start.js +33 -0
  18. package/dist/cli/start.js.map +1 -0
  19. package/dist/cli/status.d.ts +2 -0
  20. package/dist/cli/status.d.ts.map +1 -0
  21. package/dist/cli/status.js +30 -0
  22. package/dist/cli/status.js.map +1 -0
  23. package/dist/core/config.d.ts +15 -0
  24. package/dist/core/config.d.ts.map +1 -0
  25. package/dist/core/config.js +24 -0
  26. package/dist/core/config.js.map +1 -0
  27. package/dist/core/db.d.ts +4 -0
  28. package/dist/core/db.d.ts.map +1 -0
  29. package/dist/core/db.js +90 -0
  30. package/dist/core/db.js.map +1 -0
  31. package/dist/core/memory.d.ts +4 -0
  32. package/dist/core/memory.d.ts.map +1 -0
  33. package/dist/core/memory.js +9 -0
  34. package/dist/core/memory.js.map +1 -0
  35. package/dist/core/models.d.ts +67 -0
  36. package/dist/core/models.d.ts.map +1 -0
  37. package/dist/core/models.js +29 -0
  38. package/dist/core/models.js.map +1 -0
  39. package/dist/dashboard/index.html +676 -0
  40. package/dist/dashboard/server.d.ts +2 -0
  41. package/dist/dashboard/server.d.ts.map +1 -0
  42. package/dist/dashboard/server.js +20 -0
  43. package/dist/dashboard/server.js.map +1 -0
  44. package/dist/mcp/server.d.ts +5 -0
  45. package/dist/mcp/server.d.ts.map +1 -0
  46. package/dist/mcp/server.js +386 -0
  47. package/dist/mcp/server.js.map +1 -0
  48. package/dist/server/index.d.ts +7 -0
  49. package/dist/server/index.d.ts.map +1 -0
  50. package/dist/server/index.js +41 -0
  51. package/dist/server/index.js.map +1 -0
  52. package/dist/server/routes/conversations.d.ts +3 -0
  53. package/dist/server/routes/conversations.d.ts.map +1 -0
  54. package/dist/server/routes/conversations.js +86 -0
  55. package/dist/server/routes/conversations.js.map +1 -0
  56. package/dist/server/routes/entities.d.ts +3 -0
  57. package/dist/server/routes/entities.d.ts.map +1 -0
  58. package/dist/server/routes/entities.js +51 -0
  59. package/dist/server/routes/entities.js.map +1 -0
  60. package/dist/server/routes/facts.d.ts +3 -0
  61. package/dist/server/routes/facts.d.ts.map +1 -0
  62. package/dist/server/routes/facts.js +41 -0
  63. package/dist/server/routes/facts.js.map +1 -0
  64. package/dist/server/routes/health.d.ts +3 -0
  65. package/dist/server/routes/health.d.ts.map +1 -0
  66. package/dist/server/routes/health.js +72 -0
  67. package/dist/server/routes/health.js.map +1 -0
  68. package/dist/server/routes/search.d.ts +3 -0
  69. package/dist/server/routes/search.d.ts.map +1 -0
  70. package/dist/server/routes/search.js +60 -0
  71. package/dist/server/routes/search.js.map +1 -0
  72. package/dist/server/routes/workspace.d.ts +3 -0
  73. package/dist/server/routes/workspace.d.ts.map +1 -0
  74. package/dist/server/routes/workspace.js +34 -0
  75. package/dist/server/routes/workspace.js.map +1 -0
  76. package/package.json +82 -0
@@ -0,0 +1,51 @@
1
+ export async function entityRoutes(app) {
2
+ const db = app.db;
3
+ app.get('/entities', async (request) => {
4
+ const { category, type } = request.query;
5
+ let sql = "SELECT * FROM entities WHERE status = 'active'";
6
+ const params = [];
7
+ if (category) {
8
+ sql += ' AND category = ?';
9
+ params.push(category);
10
+ }
11
+ if (type) {
12
+ sql += ' AND type = ?';
13
+ params.push(type);
14
+ }
15
+ sql += ' ORDER BY updated_at DESC';
16
+ return db.prepare(sql).all(...params).map((e) => ({
17
+ ...e,
18
+ metadata: e.metadata ? JSON.parse(e.metadata) : null,
19
+ }));
20
+ });
21
+ app.get('/entities/:id', async (request, reply) => {
22
+ const { id } = request.params;
23
+ const entity = db.prepare('SELECT * FROM entities WHERE id = ?').get(id);
24
+ if (!entity)
25
+ return reply.code(404).send({ error: 'Entity not found' });
26
+ const facts = db
27
+ .prepare('SELECT * FROM facts WHERE entity_id = ? ORDER BY tier ASC, access_count DESC')
28
+ .all(id);
29
+ return {
30
+ ...entity,
31
+ metadata: entity.metadata ? JSON.parse(entity.metadata) : null,
32
+ facts: facts.map((f) => ({ ...f, related_entities: JSON.parse(f.related_entities) })),
33
+ };
34
+ });
35
+ app.post('/entities', async (request, reply) => {
36
+ const { id, name, type, category, metadata } = request.body;
37
+ const now = new Date().toISOString();
38
+ db.prepare('INSERT INTO entities (id, name, type, category, created_at, updated_at, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)').run(id, name, type, category, now, now, metadata ? JSON.stringify(metadata) : null);
39
+ return reply.code(201).send({ id, name, type, category });
40
+ });
41
+ app.delete('/entities/:id', async (request, reply) => {
42
+ const { id } = request.params;
43
+ const result = db
44
+ .prepare("UPDATE entities SET status = 'archived', updated_at = ? WHERE id = ?")
45
+ .run(new Date().toISOString(), id);
46
+ if (result.changes === 0)
47
+ return reply.code(404).send({ error: 'Entity not found' });
48
+ return { id, status: 'archived' };
49
+ });
50
+ }
51
+ //# sourceMappingURL=entities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entities.js","sourceRoot":"","sources":["../../../src/server/routes/entities.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ export declare function factRoutes(app: FastifyInstance): Promise<void>;
3
+ //# sourceMappingURL=facts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facts.d.ts","sourceRoot":"","sources":["../../../src/server/routes/facts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAO/C,wBAAsB,UAAU,CAAC,GAAG,EAAE,eAAe,iBAiEpD"}
@@ -0,0 +1,41 @@
1
+ export async function factRoutes(app) {
2
+ const db = app.db;
3
+ app.get('/entities/:entityId/facts', async (request, reply) => {
4
+ const { entityId } = request.params;
5
+ const { tier } = request.query;
6
+ if (!db.prepare('SELECT id FROM entities WHERE id = ?').get(entityId))
7
+ return reply.code(404).send({ error: 'Entity not found' });
8
+ let sql = 'SELECT * FROM facts WHERE entity_id = ?';
9
+ const params = [entityId];
10
+ if (tier) {
11
+ sql += ' AND tier = ?';
12
+ params.push(tier);
13
+ }
14
+ sql += ' ORDER BY access_count DESC, last_accessed DESC';
15
+ return db.prepare(sql).all(...params).map((f) => ({
16
+ ...f,
17
+ related_entities: JSON.parse(f.related_entities),
18
+ }));
19
+ });
20
+ app.post('/entities/:entityId/facts', async (request, reply) => {
21
+ const { entityId } = request.params;
22
+ const { id, fact, category, timestamp, source, relatedEntities } = request.body;
23
+ if (!db.prepare('SELECT id FROM entities WHERE id = ?').get(entityId))
24
+ return reply.code(404).send({ error: 'Entity not found' });
25
+ const now = new Date().toISOString().split('T')[0];
26
+ db.prepare("INSERT INTO facts (id, entity_id, fact, category, timestamp, last_accessed, access_count, tier, source, related_entities) VALUES (?, ?, ?, ?, ?, ?, 1, 'hot', ?, ?)").run(id, entityId, fact, category || 'context', timestamp || now, now, source || 'api', JSON.stringify(relatedEntities || []));
27
+ db.prepare('UPDATE entities SET updated_at = ? WHERE id = ?').run(now, entityId);
28
+ return reply.code(201).send({ id, entityId, fact });
29
+ });
30
+ app.patch('/facts/:id/access', async (request, reply) => {
31
+ const { id } = request.params;
32
+ const now = new Date().toISOString().split('T')[0];
33
+ const result = db
34
+ .prepare("UPDATE facts SET last_accessed = ?, access_count = access_count + 1, tier = 'hot' WHERE id = ?")
35
+ .run(now, id);
36
+ if (result.changes === 0)
37
+ return reply.code(404).send({ error: 'Fact not found' });
38
+ return { id, bumped: true };
39
+ });
40
+ }
41
+ //# sourceMappingURL=facts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facts.js","sourceRoot":"","sources":["../../../src/server/routes/facts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ export declare function healthRoutes(app: FastifyInstance): Promise<void>;
3
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../../src/server/routes/health.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA+B/C,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,iBAoDtD"}
@@ -0,0 +1,72 @@
1
+ const CLAUDE_MD_CONTENT = `# dbrain
2
+
3
+ You have an AI brain connected via MCP (dbrain). This is your persistent memory across all conversations and machines.
4
+
5
+ ## What's in the brain
6
+
7
+ - **Documents**: Identity (who you are), User (who the user is), Soul (how to behave), Memory (narrative reflections). These are returned automatically with every \`recall\` call.
8
+ - **Entities + Facts**: Structured knowledge organized by PARA (Projects, Areas, Resources, Archives). Each entity (person, project, system, event) has facts attached. Facts have tiers: hot (recent/frequent), warm, cold.
9
+ - **Conversations**: Raw chat history from every AI session. Stored with source (e.g. 'claude-code-home', 'gemini-mobile').
10
+
11
+ ## When to use each tool
12
+
13
+ - \`recall\` — Search before answering. Use this BEFORE responding to any question about the user, their preferences, projects, people they know, or past conversations. Also returns your identity docs. When in doubt, search first.
14
+ - \`remember\` — Save important facts. Use when the user shares preferences, makes decisions, mentions personal details, or says "remember this". One clear atomic fact per call.
15
+ - \`log\` — Store conversation messages. Use periodically to log what's happening in the conversation. Send both user and assistant messages.
16
+ - \`get_entity\` — Deep dive. When you need full context about a specific project, person, or system.
17
+ - \`list_entities\` — Discover. When you need to see what entities exist, filter by category or type.
18
+ - \`create_entity\` — New knowledge. When the user mentions a new project, person, or system worth tracking. Create the entity first, then \`remember\` facts about it.
19
+ - \`bump\` — Keep memories alive. When you use a fact to answer a question, bump it so it stays hot.
20
+ - \`overview\` — Brain stats. When the user asks "what do you know?" or you need a high-level picture.
21
+ - \`wake_up\` — Full identity load. Only needed if you want the complete identity documents outside of a recall.
22
+
23
+ ## Rules
24
+
25
+ - At the start of every conversation, call \`recall\` with the user's first question or topic. This gives you both search results AND your identity.
26
+ - Never say "I don't know" about the user without searching first.
27
+ - When storing facts, be specific and atomic. "Favorite ice cream is pistachio" not "We talked about food preferences".
28
+ `;
29
+ export async function healthRoutes(app) {
30
+ app.get('/health', async () => {
31
+ const db = app.db;
32
+ const { entities } = db.prepare('SELECT COUNT(*) as entities FROM entities').get();
33
+ const { facts } = db.prepare('SELECT COUNT(*) as facts FROM facts').get();
34
+ const { documents } = db.prepare('SELECT COUNT(*) as documents FROM documents').get();
35
+ const { conversations } = db
36
+ .prepare('SELECT COUNT(*) as conversations FROM conversations')
37
+ .get();
38
+ const { unprocessed } = db
39
+ .prepare('SELECT COUNT(*) as unprocessed FROM messages WHERE processed = 0')
40
+ .get();
41
+ const identity = db.prepare("SELECT content FROM documents WHERE key = 'identity'").get();
42
+ const name = identity?.content?.match(/\*\*Name:\*\* (.+)/)?.[1] || 'dbrain';
43
+ return {
44
+ status: 'awake',
45
+ name,
46
+ version: '0.1.0',
47
+ entities,
48
+ facts,
49
+ documents,
50
+ conversations,
51
+ unprocessed,
52
+ };
53
+ });
54
+ app.get('/connect', async (request) => {
55
+ const config = app.config;
56
+ const host = request.headers.host || `localhost:${config.port}`;
57
+ const protocol = request.headers['x-forwarded-proto'] || 'http';
58
+ const baseUrl = `${protocol}://${host}`;
59
+ return {
60
+ mcp: {
61
+ dbrain: {
62
+ type: 'http',
63
+ url: `${baseUrl}/mcp`,
64
+ headers: { Authorization: `Bearer ${config.token}` },
65
+ },
66
+ },
67
+ permissions: ['mcp__dbrain__*'],
68
+ claudeMd: CLAUDE_MD_CONTENT,
69
+ };
70
+ });
71
+ }
72
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../../src/server/routes/health.ts"],"names":[],"mappings":"AAEA,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BzB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAoB;IACrD,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QAC5B,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;QAClB,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,2CAA2C,CAAC,CAAC,GAAG,EAE/E,CAAC;QACF,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAC,GAAG,EAEtE,CAAC;QACF,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,6CAA6C,CAAC,CAAC,GAAG,EAElF,CAAC;QACF,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE;aACzB,OAAO,CAAC,qDAAqD,CAAC;aAC9D,GAAG,EAA+B,CAAC;QACtC,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE;aACvB,OAAO,CAAC,kEAAkE,CAAC;aAC3E,GAAG,EAA6B,CAAC;QACpC,MAAM,QAAQ,GAAG,EAAE,CAAC,OAAO,CAAC,sDAAsD,CAAC,CAAC,GAAG,EAE1E,CAAC;QACd,MAAM,IAAI,GAAG,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC;QAC7E,OAAO;YACL,MAAM,EAAE,OAAO;YACf,IAAI;YACJ,OAAO,EAAE,OAAO;YAChB,QAAQ;YACR,KAAK;YACL,SAAS;YACT,aAAa;YACb,WAAW;SACZ,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACpC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,aAAa,MAAM,CAAC,IAAI,EAAE,CAAC;QAChE,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,MAAM,CAAC;QAChE,MAAM,OAAO,GAAG,GAAG,QAAQ,MAAM,IAAI,EAAE,CAAC;QAExC,OAAO;YACL,GAAG,EAAE;gBACH,MAAM,EAAE;oBACN,IAAI,EAAE,MAAM;oBACZ,GAAG,EAAE,GAAG,OAAO,MAAM;oBACrB,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,CAAC,KAAK,EAAE,EAAE;iBACrD;aACF;YACD,WAAW,EAAE,CAAC,gBAAgB,CAAC;YAC/B,QAAQ,EAAE,iBAAiB;SAC5B,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ export declare function searchRoutes(app: FastifyInstance): Promise<void>;
3
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/server/routes/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAmB/C,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,iBAyEtD"}
@@ -0,0 +1,60 @@
1
+ export async function searchRoutes(app) {
2
+ const db = app.db;
3
+ app.post('/search', async (request) => {
4
+ const { query, limit = 10, entityId, tier, } = request.body;
5
+ const ftsQuery = query.split(/\s+/).filter(Boolean).join(' OR ');
6
+ let sql = `
7
+ SELECT f.*, e.name as entity_name, e.type as entity_type, e.category as entity_category, rank
8
+ FROM facts_fts fts
9
+ JOIN facts f ON f.rowid = fts.rowid
10
+ JOIN entities e ON e.id = f.entity_id
11
+ WHERE facts_fts MATCH ?
12
+ `;
13
+ const params = [ftsQuery];
14
+ if (entityId) {
15
+ sql += ' AND f.entity_id = ?';
16
+ params.push(entityId);
17
+ }
18
+ if (tier) {
19
+ sql += ' AND f.tier = ?';
20
+ params.push(tier);
21
+ }
22
+ sql += ' ORDER BY rank LIMIT ?';
23
+ params.push(limit);
24
+ return db.prepare(sql).all(...params).map((r) => ({
25
+ fact: {
26
+ id: r.id,
27
+ entityId: r.entity_id,
28
+ fact: r.fact,
29
+ category: r.category,
30
+ timestamp: r.timestamp,
31
+ status: r.status,
32
+ lastAccessed: r.last_accessed,
33
+ accessCount: r.access_count,
34
+ tier: r.tier,
35
+ source: r.source,
36
+ },
37
+ entity: {
38
+ id: r.entity_id,
39
+ name: r.entity_name,
40
+ type: r.entity_type,
41
+ category: r.entity_category,
42
+ },
43
+ score: -r.rank,
44
+ }));
45
+ });
46
+ app.get('/memory/summary', async () => {
47
+ return db
48
+ .prepare(`
49
+ SELECT e.id, e.name, e.type, e.category,
50
+ COUNT(CASE WHEN f.tier = 'hot' THEN 1 END) as hot,
51
+ COUNT(CASE WHEN f.tier = 'warm' THEN 1 END) as warm,
52
+ COUNT(CASE WHEN f.tier = 'cold' THEN 1 END) as cold,
53
+ COUNT(f.id) as total
54
+ FROM entities e LEFT JOIN facts f ON f.entity_id = e.id
55
+ WHERE e.status = 'active' GROUP BY e.id ORDER BY total DESC
56
+ `)
57
+ .all();
58
+ });
59
+ }
60
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../../src/server/routes/search.ts"],"names":[],"mappings":"AAmBA,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAoB;IACrD,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;IAElB,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACpC,MAAM,EACJ,KAAK,EACL,KAAK,GAAG,EAAE,EACV,QAAQ,EACR,IAAI,GACL,GAAG,OAAO,CAAC,IAKX,CAAC;QACF,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjE,IAAI,GAAG,GAAG;;;;;;KAMT,CAAC;QACF,MAAM,MAAM,GAAwB,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,QAAQ,EAAE,CAAC;YACb,GAAG,IAAI,sBAAsB,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxB,CAAC;QACD,IAAI,IAAI,EAAE,CAAC;YACT,GAAG,IAAI,iBAAiB,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;QACD,GAAG,IAAI,wBAAwB,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEnB,OAAQ,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAuB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACvE,IAAI,EAAE;gBACJ,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,QAAQ,EAAE,CAAC,CAAC,SAAS;gBACrB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,YAAY,EAAE,CAAC,CAAC,aAAa;gBAC7B,WAAW,EAAE,CAAC,CAAC,YAAY;gBAC3B,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,MAAM,EAAE,CAAC,CAAC,MAAM;aACjB;YACD,MAAM,EAAE;gBACN,EAAE,EAAE,CAAC,CAAC,SAAS;gBACf,IAAI,EAAE,CAAC,CAAC,WAAW;gBACnB,IAAI,EAAE,CAAC,CAAC,WAAW;gBACnB,QAAQ,EAAE,CAAC,CAAC,eAAe;aAC5B;YACD,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI;SACf,CAAC,CAAC,CAAC;IACN,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,IAAI,EAAE;QACpC,OAAO,EAAE;aACN,OAAO,CACN;;;;;;;;KAQH,CACE;aACA,GAAG,EAAE,CAAC;IACX,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ export declare function workspaceRoutes(app: FastifyInstance): Promise<void>;
3
+ //# sourceMappingURL=workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../../src/server/routes/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,wBAAsB,eAAe,CAAC,GAAG,EAAE,eAAe,iBA6CzD"}
@@ -0,0 +1,34 @@
1
+ export async function workspaceRoutes(app) {
2
+ const db = app.db;
3
+ app.get('/workspace', async () => {
4
+ return db.prepare('SELECT key, title, updated_at FROM documents ORDER BY key').all();
5
+ });
6
+ app.get('/workspace/:key', async (request, reply) => {
7
+ const { key } = request.params;
8
+ const doc = db.prepare('SELECT * FROM documents WHERE key = ?').get(key);
9
+ if (!doc)
10
+ return reply.code(404).send({ error: 'Document not found' });
11
+ return doc;
12
+ });
13
+ app.put('/workspace/:key', async (request, reply) => {
14
+ const { key } = request.params;
15
+ const { title, content } = request.body;
16
+ const now = new Date().toISOString();
17
+ const existing = db.prepare('SELECT key FROM documents WHERE key = ?').get(key);
18
+ if (existing) {
19
+ db.prepare('UPDATE documents SET title = ?, content = ?, updated_at = ? WHERE key = ?').run(title, content, now, key);
20
+ }
21
+ else {
22
+ db.prepare('INSERT INTO documents (key, title, content, updated_at) VALUES (?, ?, ?, ?)').run(key, title, content, now);
23
+ }
24
+ return reply.code(existing ? 200 : 201).send({ key, title, updated_at: now });
25
+ });
26
+ app.delete('/workspace/:key', async (request, reply) => {
27
+ const { key } = request.params;
28
+ const result = db.prepare('DELETE FROM documents WHERE key = ?').run(key);
29
+ if (result.changes === 0)
30
+ return reply.code(404).send({ error: 'Document not found' });
31
+ return { key, deleted: true };
32
+ });
33
+ }
34
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../src/server/routes/workspace.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAoB;IACxD,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;IAElB,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;QAC/B,OAAO,EAAE,CAAC,OAAO,CAAC,2DAA2D,CAAC,CAAC,GAAG,EAAE,CAAC;IACvF,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAClD,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,MAAyB,CAAC;QAClD,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,uCAAuC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACzE,IAAI,CAAC,GAAG;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;QACvE,OAAO,GAAG,CAAC;IACb,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAClD,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,MAAyB,CAAC;QAClD,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAA0C,CAAC;QAC9E,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAErC,MAAM,QAAQ,GAAG,EAAE,CAAC,OAAO,CAAC,yCAAyC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChF,IAAI,QAAQ,EAAE,CAAC;YACb,EAAE,CAAC,OAAO,CAAC,2EAA2E,CAAC,CAAC,GAAG,CACzF,KAAK,EACL,OAAO,EACP,GAAG,EACH,GAAG,CACJ,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,EAAE,CAAC,OAAO,CAAC,6EAA6E,CAAC,CAAC,GAAG,CAC3F,GAAG,EACH,KAAK,EACL,OAAO,EACP,GAAG,CACJ,CAAC;QACJ,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CAAC,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACrD,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,MAAyB,CAAC;QAClD,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1E,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;QACvF,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC"}
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "dbrain",
3
+ "version": "0.1.0",
4
+ "description": "Your distributed mind. Persistent knowledge across all your AIs.",
5
+ "type": "module",
6
+ "bin": {
7
+ "dbrain": "./dist/cli/index.js"
8
+ },
9
+ "scripts": {
10
+ "dev": "tsx watch src/cli/index.ts start",
11
+ "cli": "tsx src/cli/index.ts",
12
+ "build": "tsc && cp src/dashboard/index.html dist/dashboard/index.html && chmod +x dist/cli/index.js",
13
+ "prepare": "husky",
14
+ "start": "node dist/cli/index.js start",
15
+ "test": "vitest",
16
+ "lint": "eslint .",
17
+ "lint:fix": "eslint . --fix",
18
+ "format": "prettier --write \"src/**/*.ts\"",
19
+ "format:check": "prettier --check \"src/**/*.ts\"",
20
+ "check": "npm run lint:fix && npm run format && npm run build"
21
+ },
22
+ "lint-staged": {
23
+ "*.{ts,js}": [
24
+ "eslint --fix",
25
+ "prettier --write"
26
+ ],
27
+ "*.json": [
28
+ "prettier --write"
29
+ ]
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/ivncmp/dbrain.git"
39
+ },
40
+ "homepage": "https://github.com/ivncmp/dbrain",
41
+ "bugs": "https://github.com/ivncmp/dbrain/issues",
42
+ "author": "Iván Campillo <ivncmp@gmail.com>",
43
+ "license": "MIT",
44
+ "keywords": [
45
+ "ai",
46
+ "memory",
47
+ "mcp",
48
+ "brain",
49
+ "knowledge",
50
+ "claude",
51
+ "llm",
52
+ "sqlite"
53
+ ],
54
+ "engines": {
55
+ "node": ">=20"
56
+ },
57
+ "dependencies": {
58
+ "@clack/prompts": "^1.3.0",
59
+ "@modelcontextprotocol/sdk": "^1.29.0",
60
+ "better-sqlite3": "^12.9.0",
61
+ "dotenv": "^17.4.2",
62
+ "fastify": "^5.8.5",
63
+ "picocolors": "^1.1.1",
64
+ "zod": "^4.4.2"
65
+ },
66
+ "devDependencies": {
67
+ "@eslint/js": "^9.25.0",
68
+ "@types/better-sqlite3": "^7.6.13",
69
+ "@types/node": "^25.6.0",
70
+ "eslint": "^9.25.0",
71
+ "eslint-config-prettier": "^10.1.8",
72
+ "eslint-plugin-import-x": "^4.16.1",
73
+ "globals": "^16.0.0",
74
+ "husky": "^9.1.7",
75
+ "lint-staged": "^16.1.5",
76
+ "prettier": "^3.8.1",
77
+ "tsx": "^4.21.0",
78
+ "typescript": "^6.0.3",
79
+ "typescript-eslint": "^8.30.1",
80
+ "vitest": "^4.1.5"
81
+ }
82
+ }