@membank/dashboard 0.6.0 → 0.7.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.
Files changed (33) hide show
  1. package/dist/bin.mjs +103 -3
  2. package/dist/client/assets/WorkspaceCenter-F8ctl0ZL.js +1 -0
  3. package/dist/client/assets/WorkspaceMemoryDetail-BLFoEX3q.js +1 -0
  4. package/dist/client/assets/_projectId-D3UHHv9-.js +1 -0
  5. package/dist/client/assets/_projectId._memoryId-CZOJ7L4F.js +1 -0
  6. package/dist/client/assets/_projectId.index-Dc3jTh6p.js +1 -0
  7. package/dist/client/assets/collections-y7eMFwFc.js +19 -0
  8. package/dist/client/assets/empty-C4Q9vAt-.js +1 -0
  9. package/dist/client/assets/global-BZgjloNm.js +1 -0
  10. package/dist/client/assets/global._memoryId-DbiHUsW4.js +1 -0
  11. package/dist/client/assets/global.index-w6voN-YE.js +1 -0
  12. package/dist/client/assets/index-B3Ej81eU.js +10 -0
  13. package/dist/client/assets/index-DHtBroPC.css +2 -0
  14. package/dist/client/assets/input-BdBFU9te.js +1 -0
  15. package/dist/client/assets/preload-helper-DYD3iRAO.js +1 -0
  16. package/dist/client/assets/routes-DdtLVWUF.js +1 -0
  17. package/dist/client/assets/schemas-jSuIeSIL.js +39 -0
  18. package/dist/client/assets/skeleton-BDvkvqhf.js +1 -0
  19. package/dist/client/assets/types-DT_fBrSI.js +1 -0
  20. package/dist/client/assets/useRenderElement-DhejdWAQ.js +1 -0
  21. package/dist/client/index.html +7 -2
  22. package/dist/index.cjs +102 -2
  23. package/dist/index.d.cts +2 -2
  24. package/dist/index.d.mts +2 -2
  25. package/dist/index.mjs +103 -3
  26. package/package.json +7 -5
  27. package/dist/client/assets/MagnifyingGlass.es-ERvQA8v5.js +0 -1
  28. package/dist/client/assets/index-BCPCyhdz.js +0 -119
  29. package/dist/client/assets/index-ByUTKMgP.css +0 -1
  30. package/dist/client/assets/memories-C4n5opTn.js +0 -1
  31. package/dist/client/assets/memories._id-CrT5UPVk.js +0 -1
  32. package/dist/client/assets/memories.index-Bg5p8A21.js +0 -1
  33. package/dist/client/assets/native-select-Boj2GMjk.js +0 -1
package/dist/index.cjs CHANGED
@@ -32,6 +32,20 @@ let open = require("open");
32
32
  open = __toESM(open, 1);
33
33
  //#region src/server/index.ts
34
34
  const PREFERRED_PORT = 3847;
35
+ function buildSynthesisTools(repo, querier) {
36
+ return {
37
+ queryMemory: async (args) => {
38
+ const results = await querier.query({
39
+ query: args.query,
40
+ projectHash: args.global === true ? void 0 : args.projectHash,
41
+ limit: args.limit ?? 20,
42
+ includePinned: true
43
+ });
44
+ return JSON.stringify(results);
45
+ },
46
+ getMemorySummary: async () => JSON.stringify(repo.stats())
47
+ };
48
+ }
35
49
  const MIME = {
36
50
  ".js": "application/javascript",
37
51
  ".mjs": "application/javascript",
@@ -68,7 +82,23 @@ async function findFreePort(preferred) {
68
82
  });
69
83
  }
70
84
  }
71
- function createApiApp(repo, projectRepo, embedder) {
85
+ function aggregateActivity(memories, days) {
86
+ const cutoff = /* @__PURE__ */ new Date();
87
+ cutoff.setDate(cutoff.getDate() - days);
88
+ const cutoffStr = cutoff.toISOString().slice(0, 10);
89
+ const dayCounts = {};
90
+ for (const m of memories) {
91
+ const day = m.createdAt.slice(0, 10);
92
+ if (day >= cutoffStr) dayCounts[day] = (dayCounts[day] ?? 0) + 1;
93
+ const updateDay = m.updatedAt.slice(0, 10);
94
+ if (updateDay !== day && updateDay >= cutoffStr) dayCounts[updateDay] = (dayCounts[updateDay] ?? 0) + 1;
95
+ }
96
+ return Object.entries(dayCounts).map(([date, count]) => ({
97
+ date,
98
+ count
99
+ })).sort((a, b) => a.date.localeCompare(b.date));
100
+ }
101
+ function createApiApp(repo, projectRepo, embedder, queryEngine, synthRepo) {
72
102
  const app = new hono.Hono();
73
103
  app.get("/api/memories", (c) => {
74
104
  const { type, pinned, needsReview, search, projectId } = c.req.query();
@@ -137,6 +167,75 @@ function createApiApp(repo, projectRepo, embedder) {
137
167
  needsReview
138
168
  });
139
169
  });
170
+ app.get("/api/syntheses", (c) => {
171
+ return c.json(synthRepo.listAll());
172
+ });
173
+ app.get("/api/projects/:id/synthesis", (c) => {
174
+ const project = projectRepo.list().find((p) => p.id === c.req.param("id"));
175
+ if (!project) return c.json({ error: "Not found" }, 404);
176
+ return c.json(synthRepo.getSynthesis(project.scopeHash) ?? null);
177
+ });
178
+ app.post("/api/projects/:id/synthesis", (c) => {
179
+ if (!(0, _membank_core.isSynthesisEnabled)()) return c.json({ error: "Synthesis is disabled" }, 503);
180
+ const project = projectRepo.list().find((p) => p.id === c.req.param("id"));
181
+ if (!project) return c.json({ error: "Not found" }, 404);
182
+ const agentRunner = (0, _membank_core.createSynthesisAgentRunner)(buildSynthesisTools(repo, queryEngine), { enabled: true });
183
+ (0, _membank_core.runSynthesis)(project.scopeHash, {
184
+ synthRepo,
185
+ agentRunner
186
+ });
187
+ return c.json({ ok: true }, 202);
188
+ });
189
+ app.delete("/api/projects/:id/synthesis/in-flight", (c) => {
190
+ const project = projectRepo.list().find((p) => p.id === c.req.param("id"));
191
+ if (!project) return c.json({ error: "Not found" }, 404);
192
+ synthRepo.clearInFlight(project.scopeHash);
193
+ return c.json({ ok: true });
194
+ });
195
+ app.get("/api/projects/:id/stats", (c) => {
196
+ const project = projectRepo.list().find((p) => p.id === c.req.param("id"));
197
+ if (!project) return c.json({ error: "Not found" }, 404);
198
+ const memories = repo.list({ projectId: project.id });
199
+ const byType = {
200
+ correction: 0,
201
+ preference: 0,
202
+ decision: 0,
203
+ learning: 0,
204
+ fact: 0
205
+ };
206
+ for (const m of memories) byType[m.type] = (byType[m.type] ?? 0) + 1;
207
+ const mostCommonType = memories.length > 0 ? Object.entries(byType).sort((a, b) => b[1] - a[1])[0]?.[0] ?? null : null;
208
+ const needsReview = memories.filter((m) => m.reviewEvents.length > 0).length;
209
+ const pinned = memories.filter((m) => m.pinned).length;
210
+ const lastActive = memories.reduce((latest, m) => {
211
+ const d = m.updatedAt > m.createdAt ? m.updatedAt : m.createdAt;
212
+ return d > latest ? d : latest;
213
+ }, "");
214
+ const activeDaySet = new Set(memories.map((m) => m.createdAt.slice(0, 10)));
215
+ const harnessCounts = {};
216
+ for (const m of memories) if (m.sourceHarness) harnessCounts[m.sourceHarness] = (harnessCounts[m.sourceHarness] ?? 0) + 1;
217
+ const harness = Object.entries(harnessCounts).sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
218
+ return c.json({
219
+ total: memories.length,
220
+ byType,
221
+ needsReview,
222
+ pinned,
223
+ mostCommonType,
224
+ lastActive: lastActive || null,
225
+ harness,
226
+ activeDays: activeDaySet.size
227
+ });
228
+ });
229
+ app.get("/api/projects/:id/activity", (c) => {
230
+ const project = projectRepo.list().find((p) => p.id === c.req.param("id"));
231
+ if (!project) return c.json({ error: "Not found" }, 404);
232
+ const daysParam = Math.max(1, parseInt(c.req.query("days") ?? "365", 10));
233
+ return c.json(aggregateActivity(repo.list({ projectId: project.id }), daysParam));
234
+ });
235
+ app.get("/api/activity", (c) => {
236
+ const daysParam = Math.max(1, parseInt(c.req.query("days") ?? "365", 10));
237
+ return c.json(aggregateActivity(repo.list(), daysParam));
238
+ });
140
239
  return app;
141
240
  }
142
241
  async function startDashboard(opts) {
@@ -144,7 +243,8 @@ async function startDashboard(opts) {
144
243
  const db = _membank_core.DatabaseManager.open();
145
244
  const embedding = new _membank_core.EmbeddingService();
146
245
  const projects = (0, _membank_core.createProjectRepository)(db);
147
- const app = createApiApp((0, _membank_core.createMemoryRepository)(db, projects), projects, embedding);
246
+ const repo = (0, _membank_core.createMemoryRepository)(db, projects);
247
+ const app = createApiApp(repo, projects, embedding, new _membank_core.QueryEngine(db, embedding, repo), (0, _membank_core.createSynthesisRepository)(db));
148
248
  const clientDir = (0, node_path.join)((0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href)), "client");
149
249
  app.get("*", (c) => {
150
250
  const filePath = (0, node_path.join)(clientDir, c.req.path === "/" ? "/index.html" : c.req.path);
package/dist/index.d.cts CHANGED
@@ -1,8 +1,8 @@
1
- import { Embedder, MemoryRepository, ProjectRepository } from "@membank/core";
1
+ import { Embedder, MemoryRepository, ProjectRepository, QueryEngine, SynthesisRepository } from "@membank/core";
2
2
  import { Hono } from "hono";
3
3
 
4
4
  //#region src/server/index.d.ts
5
- declare function createApiApp(repo: MemoryRepository, projectRepo: ProjectRepository, embedder: Embedder): Hono;
5
+ declare function createApiApp(repo: MemoryRepository, projectRepo: ProjectRepository, embedder: Embedder, queryEngine: QueryEngine, synthRepo: SynthesisRepository): Hono;
6
6
  declare function startDashboard(opts?: {
7
7
  port?: number;
8
8
  open?: boolean;
package/dist/index.d.mts CHANGED
@@ -1,8 +1,8 @@
1
- import { Embedder, MemoryRepository, ProjectRepository } from "@membank/core";
1
+ import { Embedder, MemoryRepository, ProjectRepository, QueryEngine, SynthesisRepository } from "@membank/core";
2
2
  import { Hono } from "hono";
3
3
 
4
4
  //#region src/server/index.d.ts
5
- declare function createApiApp(repo: MemoryRepository, projectRepo: ProjectRepository, embedder: Embedder): Hono;
5
+ declare function createApiApp(repo: MemoryRepository, projectRepo: ProjectRepository, embedder: Embedder, queryEngine: QueryEngine, synthRepo: SynthesisRepository): Hono;
6
6
  declare function startDashboard(opts?: {
7
7
  port?: number;
8
8
  open?: boolean;
package/dist/index.mjs CHANGED
@@ -3,11 +3,25 @@ import { createServer } from "node:net";
3
3
  import { dirname, extname, join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { serve } from "@hono/node-server";
6
- import { DatabaseManager, EmbeddingService, createMemoryRepository, createProjectRepository, updateMemory } from "@membank/core";
6
+ import { DatabaseManager, EmbeddingService, QueryEngine, createMemoryRepository, createProjectRepository, createSynthesisAgentRunner, createSynthesisRepository, isSynthesisEnabled, runSynthesis, updateMemory } from "@membank/core";
7
7
  import { Hono } from "hono";
8
8
  import open from "open";
9
9
  //#region src/server/index.ts
10
10
  const PREFERRED_PORT = 3847;
11
+ function buildSynthesisTools(repo, querier) {
12
+ return {
13
+ queryMemory: async (args) => {
14
+ const results = await querier.query({
15
+ query: args.query,
16
+ projectHash: args.global === true ? void 0 : args.projectHash,
17
+ limit: args.limit ?? 20,
18
+ includePinned: true
19
+ });
20
+ return JSON.stringify(results);
21
+ },
22
+ getMemorySummary: async () => JSON.stringify(repo.stats())
23
+ };
24
+ }
11
25
  const MIME = {
12
26
  ".js": "application/javascript",
13
27
  ".mjs": "application/javascript",
@@ -44,7 +58,23 @@ async function findFreePort(preferred) {
44
58
  });
45
59
  }
46
60
  }
47
- function createApiApp(repo, projectRepo, embedder) {
61
+ function aggregateActivity(memories, days) {
62
+ const cutoff = /* @__PURE__ */ new Date();
63
+ cutoff.setDate(cutoff.getDate() - days);
64
+ const cutoffStr = cutoff.toISOString().slice(0, 10);
65
+ const dayCounts = {};
66
+ for (const m of memories) {
67
+ const day = m.createdAt.slice(0, 10);
68
+ if (day >= cutoffStr) dayCounts[day] = (dayCounts[day] ?? 0) + 1;
69
+ const updateDay = m.updatedAt.slice(0, 10);
70
+ if (updateDay !== day && updateDay >= cutoffStr) dayCounts[updateDay] = (dayCounts[updateDay] ?? 0) + 1;
71
+ }
72
+ return Object.entries(dayCounts).map(([date, count]) => ({
73
+ date,
74
+ count
75
+ })).sort((a, b) => a.date.localeCompare(b.date));
76
+ }
77
+ function createApiApp(repo, projectRepo, embedder, queryEngine, synthRepo) {
48
78
  const app = new Hono();
49
79
  app.get("/api/memories", (c) => {
50
80
  const { type, pinned, needsReview, search, projectId } = c.req.query();
@@ -113,6 +143,75 @@ function createApiApp(repo, projectRepo, embedder) {
113
143
  needsReview
114
144
  });
115
145
  });
146
+ app.get("/api/syntheses", (c) => {
147
+ return c.json(synthRepo.listAll());
148
+ });
149
+ app.get("/api/projects/:id/synthesis", (c) => {
150
+ const project = projectRepo.list().find((p) => p.id === c.req.param("id"));
151
+ if (!project) return c.json({ error: "Not found" }, 404);
152
+ return c.json(synthRepo.getSynthesis(project.scopeHash) ?? null);
153
+ });
154
+ app.post("/api/projects/:id/synthesis", (c) => {
155
+ if (!isSynthesisEnabled()) return c.json({ error: "Synthesis is disabled" }, 503);
156
+ const project = projectRepo.list().find((p) => p.id === c.req.param("id"));
157
+ if (!project) return c.json({ error: "Not found" }, 404);
158
+ const agentRunner = createSynthesisAgentRunner(buildSynthesisTools(repo, queryEngine), { enabled: true });
159
+ runSynthesis(project.scopeHash, {
160
+ synthRepo,
161
+ agentRunner
162
+ });
163
+ return c.json({ ok: true }, 202);
164
+ });
165
+ app.delete("/api/projects/:id/synthesis/in-flight", (c) => {
166
+ const project = projectRepo.list().find((p) => p.id === c.req.param("id"));
167
+ if (!project) return c.json({ error: "Not found" }, 404);
168
+ synthRepo.clearInFlight(project.scopeHash);
169
+ return c.json({ ok: true });
170
+ });
171
+ app.get("/api/projects/:id/stats", (c) => {
172
+ const project = projectRepo.list().find((p) => p.id === c.req.param("id"));
173
+ if (!project) return c.json({ error: "Not found" }, 404);
174
+ const memories = repo.list({ projectId: project.id });
175
+ const byType = {
176
+ correction: 0,
177
+ preference: 0,
178
+ decision: 0,
179
+ learning: 0,
180
+ fact: 0
181
+ };
182
+ for (const m of memories) byType[m.type] = (byType[m.type] ?? 0) + 1;
183
+ const mostCommonType = memories.length > 0 ? Object.entries(byType).sort((a, b) => b[1] - a[1])[0]?.[0] ?? null : null;
184
+ const needsReview = memories.filter((m) => m.reviewEvents.length > 0).length;
185
+ const pinned = memories.filter((m) => m.pinned).length;
186
+ const lastActive = memories.reduce((latest, m) => {
187
+ const d = m.updatedAt > m.createdAt ? m.updatedAt : m.createdAt;
188
+ return d > latest ? d : latest;
189
+ }, "");
190
+ const activeDaySet = new Set(memories.map((m) => m.createdAt.slice(0, 10)));
191
+ const harnessCounts = {};
192
+ for (const m of memories) if (m.sourceHarness) harnessCounts[m.sourceHarness] = (harnessCounts[m.sourceHarness] ?? 0) + 1;
193
+ const harness = Object.entries(harnessCounts).sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
194
+ return c.json({
195
+ total: memories.length,
196
+ byType,
197
+ needsReview,
198
+ pinned,
199
+ mostCommonType,
200
+ lastActive: lastActive || null,
201
+ harness,
202
+ activeDays: activeDaySet.size
203
+ });
204
+ });
205
+ app.get("/api/projects/:id/activity", (c) => {
206
+ const project = projectRepo.list().find((p) => p.id === c.req.param("id"));
207
+ if (!project) return c.json({ error: "Not found" }, 404);
208
+ const daysParam = Math.max(1, parseInt(c.req.query("days") ?? "365", 10));
209
+ return c.json(aggregateActivity(repo.list({ projectId: project.id }), daysParam));
210
+ });
211
+ app.get("/api/activity", (c) => {
212
+ const daysParam = Math.max(1, parseInt(c.req.query("days") ?? "365", 10));
213
+ return c.json(aggregateActivity(repo.list(), daysParam));
214
+ });
116
215
  return app;
117
216
  }
118
217
  async function startDashboard(opts) {
@@ -120,7 +219,8 @@ async function startDashboard(opts) {
120
219
  const db = DatabaseManager.open();
121
220
  const embedding = new EmbeddingService();
122
221
  const projects = createProjectRepository(db);
123
- const app = createApiApp(createMemoryRepository(db, projects), projects, embedding);
222
+ const repo = createMemoryRepository(db, projects);
223
+ const app = createApiApp(repo, projects, embedding, new QueryEngine(db, embedding, repo), createSynthesisRepository(db));
124
224
  const clientDir = join(dirname(fileURLToPath(import.meta.url)), "client");
125
225
  app.get("*", (c) => {
126
226
  const filePath = join(clientDir, c.req.path === "/" ? "/index.html" : c.req.path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@membank/dashboard",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -56,19 +56,21 @@
56
56
  "tailwind-merge": "^3.0.0",
57
57
  "tw-animate-css": "^1.0.0",
58
58
  "zod": "^4.3.6",
59
- "@membank/core": "0.10.0"
59
+ "@membank/core": "0.11.1"
60
60
  },
61
61
  "devDependencies": {
62
- "@tailwindcss/vite": "^4.0.0",
62
+ "@rolldown/plugin-babel": "^0.2.3",
63
+ "@tailwindcss/vite": "^4.2.4",
63
64
  "@types/node": "^25.6.0",
64
65
  "@types/react": "^19.0.0",
65
66
  "@types/react-dom": "^19.0.0",
66
- "@vitejs/plugin-react": "^4.0.0",
67
+ "@vitejs/plugin-react": "^6.0.1",
68
+ "babel-plugin-react-compiler": "^1.0.0",
67
69
  "concurrently": "^9.0.0",
68
70
  "tailwindcss": "^4.0.0",
69
71
  "tsx": "^4.0.0",
70
72
  "typescript": "^6.0.3",
71
- "vite": "^6.0.0"
73
+ "vite": "^8.0.11"
72
74
  },
73
75
  "scripts": {
74
76
  "build": "vite build && tsdown",
@@ -1 +0,0 @@
1
- import{r as e,B as n}from"./index-BCPCyhdz.js";const r=new Map([["bold",e.createElement(e.Fragment,null,e.createElement("path",{d:"M232.49,215.51,185,168a92.12,92.12,0,1,0-17,17l47.53,47.54a12,12,0,0,0,17-17ZM44,112a68,68,0,1,1,68,68A68.07,68.07,0,0,1,44,112Z"}))],["duotone",e.createElement(e.Fragment,null,e.createElement("path",{d:"M192,112a80,80,0,1,1-80-80A80,80,0,0,1,192,112Z",opacity:"0.2"}),e.createElement("path",{d:"M229.66,218.34,179.6,168.28a88.21,88.21,0,1,0-11.32,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z"}))],["fill",e.createElement(e.Fragment,null,e.createElement("path",{d:"M168,112a56,56,0,1,1-56-56A56,56,0,0,1,168,112Zm61.66,117.66a8,8,0,0,1-11.32,0l-50.06-50.07a88,88,0,1,1,11.32-11.31l50.06,50.06A8,8,0,0,1,229.66,229.66ZM112,184a72,72,0,1,0-72-72A72.08,72.08,0,0,0,112,184Z"}))],["light",e.createElement(e.Fragment,null,e.createElement("path",{d:"M228.24,219.76l-51.38-51.38a86.15,86.15,0,1,0-8.48,8.48l51.38,51.38a6,6,0,0,0,8.48-8.48ZM38,112a74,74,0,1,1,74,74A74.09,74.09,0,0,1,38,112Z"}))],["regular",e.createElement(e.Fragment,null,e.createElement("path",{d:"M229.66,218.34l-50.07-50.06a88.11,88.11,0,1,0-11.31,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z"}))],["thin",e.createElement(e.Fragment,null,e.createElement("path",{d:"M226.83,221.17l-52.7-52.7a84.1,84.1,0,1,0-5.66,5.66l52.7,52.7a4,4,0,0,0,5.66-5.66ZM36,112a76,76,0,1,1,76,76A76.08,76.08,0,0,1,36,112Z"}))]]),a=e.forwardRef((t,l)=>e.createElement(n,{ref:l,...t,weights:r}));a.displayName="MagnifyingGlassIcon";const c=a;export{c as f};