ahok-skill 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +8 -0
- package/Dockerfile +59 -0
- package/RAW_SKILL.md +219 -0
- package/README.md +277 -0
- package/SKILL.md +58 -0
- package/bin/opm.js +268 -0
- package/data/openmemory.sqlite +0 -0
- package/data/openmemory.sqlite-shm +0 -0
- package/data/openmemory.sqlite-wal +0 -0
- package/dist/ai/graph.js +293 -0
- package/dist/ai/mcp.js +397 -0
- package/dist/cli.js +78 -0
- package/dist/core/cfg.js +87 -0
- package/dist/core/db.js +636 -0
- package/dist/core/memory.js +116 -0
- package/dist/core/migrate.js +227 -0
- package/dist/core/models.js +105 -0
- package/dist/core/telemetry.js +57 -0
- package/dist/core/types.js +2 -0
- package/dist/core/vector/postgres.js +52 -0
- package/dist/core/vector/valkey.js +246 -0
- package/dist/core/vector_store.js +2 -0
- package/dist/index.js +44 -0
- package/dist/memory/decay.js +301 -0
- package/dist/memory/embed.js +675 -0
- package/dist/memory/hsg.js +959 -0
- package/dist/memory/reflect.js +131 -0
- package/dist/memory/user_summary.js +99 -0
- package/dist/migrate.js +9 -0
- package/dist/ops/compress.js +255 -0
- package/dist/ops/dynamics.js +189 -0
- package/dist/ops/extract.js +333 -0
- package/dist/ops/ingest.js +214 -0
- package/dist/server/index.js +109 -0
- package/dist/server/middleware/auth.js +137 -0
- package/dist/server/routes/auth.js +186 -0
- package/dist/server/routes/compression.js +108 -0
- package/dist/server/routes/dashboard.js +399 -0
- package/dist/server/routes/docs.js +241 -0
- package/dist/server/routes/dynamics.js +312 -0
- package/dist/server/routes/ide.js +280 -0
- package/dist/server/routes/index.js +33 -0
- package/dist/server/routes/keys.js +132 -0
- package/dist/server/routes/langgraph.js +61 -0
- package/dist/server/routes/memory.js +213 -0
- package/dist/server/routes/sources.js +140 -0
- package/dist/server/routes/system.js +63 -0
- package/dist/server/routes/temporal.js +293 -0
- package/dist/server/routes/users.js +101 -0
- package/dist/server/routes/vercel.js +57 -0
- package/dist/server/server.js +211 -0
- package/dist/server.js +3 -0
- package/dist/sources/base.js +223 -0
- package/dist/sources/github.js +171 -0
- package/dist/sources/google_drive.js +166 -0
- package/dist/sources/google_sheets.js +112 -0
- package/dist/sources/google_slides.js +139 -0
- package/dist/sources/index.js +34 -0
- package/dist/sources/notion.js +165 -0
- package/dist/sources/onedrive.js +143 -0
- package/dist/sources/web_crawler.js +166 -0
- package/dist/temporal_graph/index.js +20 -0
- package/dist/temporal_graph/query.js +240 -0
- package/dist/temporal_graph/store.js +116 -0
- package/dist/temporal_graph/timeline.js +241 -0
- package/dist/temporal_graph/types.js +2 -0
- package/dist/utils/chunking.js +60 -0
- package/dist/utils/index.js +31 -0
- package/dist/utils/keyword.js +94 -0
- package/dist/utils/text.js +120 -0
- package/nodemon.json +7 -0
- package/package.json +50 -0
- package/references/api_reference.md +66 -0
- package/references/examples.md +45 -0
- package/src/ai/graph.ts +363 -0
- package/src/ai/mcp.ts +494 -0
- package/src/cli.ts +94 -0
- package/src/core/cfg.ts +110 -0
- package/src/core/db.ts +1052 -0
- package/src/core/memory.ts +99 -0
- package/src/core/migrate.ts +302 -0
- package/src/core/models.ts +107 -0
- package/src/core/telemetry.ts +47 -0
- package/src/core/types.ts +130 -0
- package/src/core/vector/postgres.ts +61 -0
- package/src/core/vector/valkey.ts +261 -0
- package/src/core/vector_store.ts +9 -0
- package/src/index.ts +5 -0
- package/src/memory/decay.ts +427 -0
- package/src/memory/embed.ts +707 -0
- package/src/memory/hsg.ts +1245 -0
- package/src/memory/reflect.ts +158 -0
- package/src/memory/user_summary.ts +110 -0
- package/src/migrate.ts +8 -0
- package/src/ops/compress.ts +296 -0
- package/src/ops/dynamics.ts +272 -0
- package/src/ops/extract.ts +360 -0
- package/src/ops/ingest.ts +286 -0
- package/src/server/index.ts +159 -0
- package/src/server/middleware/auth.ts +156 -0
- package/src/server/routes/auth.ts +223 -0
- package/src/server/routes/compression.ts +106 -0
- package/src/server/routes/dashboard.ts +420 -0
- package/src/server/routes/docs.ts +380 -0
- package/src/server/routes/dynamics.ts +516 -0
- package/src/server/routes/ide.ts +283 -0
- package/src/server/routes/index.ts +32 -0
- package/src/server/routes/keys.ts +131 -0
- package/src/server/routes/langgraph.ts +71 -0
- package/src/server/routes/memory.ts +440 -0
- package/src/server/routes/sources.ts +111 -0
- package/src/server/routes/system.ts +68 -0
- package/src/server/routes/temporal.ts +335 -0
- package/src/server/routes/users.ts +111 -0
- package/src/server/routes/vercel.ts +55 -0
- package/src/server/server.js +215 -0
- package/src/server.ts +1 -0
- package/src/sources/base.ts +257 -0
- package/src/sources/github.ts +156 -0
- package/src/sources/google_drive.ts +144 -0
- package/src/sources/google_sheets.ts +85 -0
- package/src/sources/google_slides.ts +115 -0
- package/src/sources/index.ts +19 -0
- package/src/sources/notion.ts +148 -0
- package/src/sources/onedrive.ts +131 -0
- package/src/sources/web_crawler.ts +161 -0
- package/src/temporal_graph/index.ts +4 -0
- package/src/temporal_graph/query.ts +299 -0
- package/src/temporal_graph/store.ts +156 -0
- package/src/temporal_graph/timeline.ts +319 -0
- package/src/temporal_graph/types.ts +41 -0
- package/src/utils/chunking.ts +66 -0
- package/src/utils/index.ts +25 -0
- package/src/utils/keyword.ts +137 -0
- package/src/utils/text.ts +115 -0
- package/tests/test_api_workspace_management.ts +413 -0
- package/tests/test_bulk_delete.ts +267 -0
- package/tests/test_omnibus.ts +166 -0
- package/tests/test_workspace_management.ts +278 -0
- package/tests/verify.ts +104 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import { q, all_async, run_async } from "../../core/db";
|
|
2
|
+
import { env } from "../../core/cfg";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
|
|
6
|
+
const is_pg = env.metadata_backend === "postgres";
|
|
7
|
+
|
|
8
|
+
const get_mem_table = () => {
|
|
9
|
+
if (is_pg) {
|
|
10
|
+
const sc = process.env.OM_PG_SCHEMA || "public";
|
|
11
|
+
const tbl = process.env.OM_PG_TABLE || "openmemory_memories";
|
|
12
|
+
return `"${sc}"."${tbl}"`;
|
|
13
|
+
}
|
|
14
|
+
return "memories";
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
let reqz = {
|
|
18
|
+
win_start: Date.now(),
|
|
19
|
+
win_cnt: 0,
|
|
20
|
+
qps_hist: [] as number[],
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const log_metric = async (type: string, value: number) => {
|
|
24
|
+
try {
|
|
25
|
+
const sc = process.env.OM_PG_SCHEMA || "public";
|
|
26
|
+
const sql = is_pg
|
|
27
|
+
? `insert into "${sc}"."stats"(type,count,ts) values($1,$2,$3)`
|
|
28
|
+
: "insert into stats(type,count,ts) values(?,?,?)";
|
|
29
|
+
await run_async(sql, [type, value, Date.now()]);
|
|
30
|
+
} catch (e) {
|
|
31
|
+
console.error("[metrics] log err:", e);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export function track_req(success: boolean) {
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
if (now - reqz.win_start >= 1000) {
|
|
38
|
+
const qps = reqz.win_cnt;
|
|
39
|
+
|
|
40
|
+
reqz.qps_hist.push(qps);
|
|
41
|
+
if (reqz.qps_hist.length > 5) reqz.qps_hist.shift();
|
|
42
|
+
|
|
43
|
+
// Log metrics to database every second
|
|
44
|
+
log_metric("qps", qps).catch(console.error);
|
|
45
|
+
if (!success) log_metric("error", 1).catch(console.error);
|
|
46
|
+
|
|
47
|
+
reqz.win_start = now;
|
|
48
|
+
reqz.win_cnt = 1;
|
|
49
|
+
} else {
|
|
50
|
+
reqz.win_cnt++;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function req_tracker_mw() {
|
|
55
|
+
return (req: any, res: any, next: any) => {
|
|
56
|
+
if (req.url.startsWith("/dashboard") || req.url.startsWith("/health")) {
|
|
57
|
+
return next();
|
|
58
|
+
}
|
|
59
|
+
const orig = res.json.bind(res);
|
|
60
|
+
res.json = (data: any) => {
|
|
61
|
+
track_req(res.statusCode < 400);
|
|
62
|
+
return orig(data);
|
|
63
|
+
};
|
|
64
|
+
next();
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const get_db_sz = async (): Promise<number> => {
|
|
69
|
+
try {
|
|
70
|
+
if (is_pg) {
|
|
71
|
+
const db_name = process.env.OM_PG_DB || "openmemory";
|
|
72
|
+
const result = await all_async(
|
|
73
|
+
`SELECT pg_database_size('${db_name}') as size`,
|
|
74
|
+
);
|
|
75
|
+
return result[0]?.size
|
|
76
|
+
? Math.round(result[0].size / 1024 / 1024)
|
|
77
|
+
: 0;
|
|
78
|
+
} else {
|
|
79
|
+
const dbp = path.resolve(process.cwd(), "./backend", env.db_path);
|
|
80
|
+
|
|
81
|
+
if (fs.existsSync(dbp)) {
|
|
82
|
+
const st = fs.statSync(dbp);
|
|
83
|
+
return Math.round(st.size / 1024 / 1024);
|
|
84
|
+
}
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
} catch (e) {
|
|
88
|
+
console.error("[db_sz] err:", e);
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export function dash(app: any) {
|
|
94
|
+
app.get("/dashboard/stats", async (_req: any, res: any) => {
|
|
95
|
+
try {
|
|
96
|
+
const mem_table = get_mem_table();
|
|
97
|
+
const totmem = await all_async(
|
|
98
|
+
`SELECT COUNT(*) as count FROM ${mem_table}`,
|
|
99
|
+
);
|
|
100
|
+
const sectcnt = await all_async(`
|
|
101
|
+
SELECT primary_sector, COUNT(*) as count
|
|
102
|
+
FROM ${mem_table}
|
|
103
|
+
GROUP BY primary_sector
|
|
104
|
+
`);
|
|
105
|
+
const dayago = Date.now() - 24 * 60 * 60 * 1000;
|
|
106
|
+
const recmem = await all_async(
|
|
107
|
+
is_pg
|
|
108
|
+
? `SELECT COUNT(*) as count FROM ${mem_table} WHERE created_at > $1`
|
|
109
|
+
: `SELECT COUNT(*) as count FROM ${mem_table} WHERE created_at > ?`,
|
|
110
|
+
[dayago],
|
|
111
|
+
);
|
|
112
|
+
const avgsal = await all_async(
|
|
113
|
+
`SELECT AVG(salience) as avg FROM ${mem_table}`,
|
|
114
|
+
);
|
|
115
|
+
const decst = await all_async(`
|
|
116
|
+
SELECT
|
|
117
|
+
COUNT(*) as total,
|
|
118
|
+
AVG(decay_lambda) as avg_lambda,
|
|
119
|
+
MIN(salience) as min_salience,
|
|
120
|
+
MAX(salience) as max_salience
|
|
121
|
+
FROM ${mem_table}
|
|
122
|
+
`);
|
|
123
|
+
const upt = process.uptime();
|
|
124
|
+
|
|
125
|
+
// Calculate QPS stats from database (last hour)
|
|
126
|
+
const hour_ago = Date.now() - 60 * 60 * 1000;
|
|
127
|
+
const sc = process.env.OM_PG_SCHEMA || "public";
|
|
128
|
+
const qps_data = await all_async(
|
|
129
|
+
is_pg
|
|
130
|
+
? `SELECT count, ts FROM "${sc}"."stats" WHERE type=$1 AND ts > $2 ORDER BY ts DESC`
|
|
131
|
+
: "SELECT count, ts FROM stats WHERE type=? AND ts > ? ORDER BY ts DESC",
|
|
132
|
+
["qps", hour_ago],
|
|
133
|
+
);
|
|
134
|
+
const err_data = await all_async(
|
|
135
|
+
is_pg
|
|
136
|
+
? `SELECT COUNT(*) as total FROM "${sc}"."stats" WHERE type=$1 AND ts > $2`
|
|
137
|
+
: "SELECT COUNT(*) as total FROM stats WHERE type=? AND ts > ?",
|
|
138
|
+
["error", hour_ago],
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const peak_qps =
|
|
142
|
+
qps_data.length > 0
|
|
143
|
+
? Math.max(...qps_data.map((d: any) => d.count))
|
|
144
|
+
: 0;
|
|
145
|
+
const avg_qps =
|
|
146
|
+
reqz.qps_hist.length > 0
|
|
147
|
+
? Math.round(
|
|
148
|
+
(reqz.qps_hist.reduce((a, b) => a + b, 0) /
|
|
149
|
+
reqz.qps_hist.length) *
|
|
150
|
+
100,
|
|
151
|
+
) / 100
|
|
152
|
+
: 0;
|
|
153
|
+
const total_reqs = qps_data.reduce(
|
|
154
|
+
(sum: number, d: any) => sum + d.count,
|
|
155
|
+
0,
|
|
156
|
+
);
|
|
157
|
+
const total_errs = err_data[0]?.total || 0;
|
|
158
|
+
const err_rate =
|
|
159
|
+
total_reqs > 0
|
|
160
|
+
? ((total_errs / total_reqs) * 100).toFixed(1)
|
|
161
|
+
: "0.0";
|
|
162
|
+
|
|
163
|
+
const dbsz = await get_db_sz();
|
|
164
|
+
const dbpct =
|
|
165
|
+
dbsz > 0 ? Math.min(100, Math.round((dbsz / 1024) * 100)) : 0;
|
|
166
|
+
const cachit =
|
|
167
|
+
totmem[0]?.count > 0
|
|
168
|
+
? Math.round(
|
|
169
|
+
(totmem[0].count /
|
|
170
|
+
(totmem[0].count + total_errs * 2)) *
|
|
171
|
+
100,
|
|
172
|
+
)
|
|
173
|
+
: 0;
|
|
174
|
+
|
|
175
|
+
res.json({
|
|
176
|
+
totalMemories: totmem[0]?.count || 0,
|
|
177
|
+
recentMemories: recmem[0]?.count || 0,
|
|
178
|
+
sectorCounts: sectcnt.reduce((acc: any, row: any) => {
|
|
179
|
+
acc[row.primary_sector] = row.count;
|
|
180
|
+
return acc;
|
|
181
|
+
}, {}),
|
|
182
|
+
avgSalience: Number(avgsal[0]?.avg || 0).toFixed(3),
|
|
183
|
+
decayStats: {
|
|
184
|
+
total: decst[0]?.total || 0,
|
|
185
|
+
avgLambda: Number(decst[0]?.avg_lambda || 0).toFixed(3),
|
|
186
|
+
minSalience: Number(decst[0]?.min_salience || 0).toFixed(3),
|
|
187
|
+
maxSalience: Number(decst[0]?.max_salience || 0).toFixed(3),
|
|
188
|
+
},
|
|
189
|
+
requests: {
|
|
190
|
+
total: total_reqs,
|
|
191
|
+
errors: total_errs,
|
|
192
|
+
errorRate: err_rate,
|
|
193
|
+
lastHour: qps_data.length,
|
|
194
|
+
},
|
|
195
|
+
qps: { peak: peak_qps, average: avg_qps, cacheHitRate: cachit },
|
|
196
|
+
system: {
|
|
197
|
+
memoryUsage: dbpct,
|
|
198
|
+
heapUsed: dbsz,
|
|
199
|
+
heapTotal: 1024,
|
|
200
|
+
uptime: {
|
|
201
|
+
seconds: Math.floor(upt),
|
|
202
|
+
days: Math.floor(upt / 86400),
|
|
203
|
+
hours: Math.floor((upt % 86400) / 3600),
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
config: {
|
|
207
|
+
port: env.port,
|
|
208
|
+
vecDim: env.vec_dim,
|
|
209
|
+
cacheSegments: env.cache_segments,
|
|
210
|
+
maxActive: env.max_active,
|
|
211
|
+
decayInterval: env.decay_interval_minutes,
|
|
212
|
+
embedProvider: env.emb_kind,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
} catch (e: any) {
|
|
216
|
+
console.error("[dash] stats err:", e);
|
|
217
|
+
res.status(500).json({ err: "internal", message: e.message });
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
app.get("/dashboard/health", async (_req: any, res: any) => {
|
|
222
|
+
try {
|
|
223
|
+
const memusg = process.memoryUsage();
|
|
224
|
+
const upt = process.uptime();
|
|
225
|
+
res.json({
|
|
226
|
+
memory: {
|
|
227
|
+
heapUsed: Math.round(memusg.heapUsed / 1024 / 1024),
|
|
228
|
+
heapTotal: Math.round(memusg.heapTotal / 1024 / 1024),
|
|
229
|
+
rss: Math.round(memusg.rss / 1024 / 1024),
|
|
230
|
+
external: Math.round(memusg.external / 1024 / 1024),
|
|
231
|
+
},
|
|
232
|
+
uptime: {
|
|
233
|
+
seconds: Math.floor(upt),
|
|
234
|
+
days: Math.floor(upt / 86400),
|
|
235
|
+
hours: Math.floor((upt % 86400) / 3600),
|
|
236
|
+
},
|
|
237
|
+
process: {
|
|
238
|
+
pid: process.pid,
|
|
239
|
+
version: process.version,
|
|
240
|
+
platform: process.platform,
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
} catch (e: any) {
|
|
244
|
+
res.status(500).json({ err: "internal", message: e.message });
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
app.get("/dashboard/activity", async (req: any, res: any) => {
|
|
249
|
+
try {
|
|
250
|
+
const mem_table = get_mem_table();
|
|
251
|
+
const lim = parseInt(req.query.limit || "50");
|
|
252
|
+
const recmem = await all_async(
|
|
253
|
+
is_pg
|
|
254
|
+
? `SELECT id, content, primary_sector, salience, created_at, updated_at, last_seen_at
|
|
255
|
+
FROM ${mem_table} ORDER BY updated_at DESC LIMIT $1`
|
|
256
|
+
: `SELECT id, content, primary_sector, salience, created_at, updated_at, last_seen_at
|
|
257
|
+
FROM ${mem_table} ORDER BY updated_at DESC LIMIT ?`,
|
|
258
|
+
[lim],
|
|
259
|
+
);
|
|
260
|
+
res.json({
|
|
261
|
+
activities: recmem.map((m: any) => ({
|
|
262
|
+
id: m.id,
|
|
263
|
+
type: "memory_updated",
|
|
264
|
+
sector: m.primary_sector,
|
|
265
|
+
content: m.content.substring(0, 100) + "...",
|
|
266
|
+
salience: m.salience,
|
|
267
|
+
timestamp: m.updated_at || m.created_at,
|
|
268
|
+
})),
|
|
269
|
+
});
|
|
270
|
+
} catch (e: any) {
|
|
271
|
+
res.status(500).json({ err: "internal", message: e.message });
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
app.get("/dashboard/sectors/timeline", async (req: any, res: any) => {
|
|
276
|
+
try {
|
|
277
|
+
const mem_table = get_mem_table();
|
|
278
|
+
const hrs = parseInt(req.query.hours || "24");
|
|
279
|
+
const strt = Date.now() - hrs * 60 * 60 * 1000;
|
|
280
|
+
|
|
281
|
+
// Use different grouping based on time range
|
|
282
|
+
let displayFormat: string;
|
|
283
|
+
let sortFormat: string;
|
|
284
|
+
let timeKey: string;
|
|
285
|
+
if (hrs <= 24) {
|
|
286
|
+
// For 24 hours or less, group by date+hour for sorting, display only hour
|
|
287
|
+
displayFormat = is_pg
|
|
288
|
+
? "to_char(to_timestamp(created_at/1000), 'HH24:00')"
|
|
289
|
+
: "strftime('%H:00', datetime(created_at/1000, 'unixepoch', 'localtime'))";
|
|
290
|
+
sortFormat = is_pg
|
|
291
|
+
? "to_char(to_timestamp(created_at/1000), 'YYYY-MM-DD HH24:00')"
|
|
292
|
+
: "strftime('%Y-%m-%d %H:00', datetime(created_at/1000, 'unixepoch', 'localtime'))";
|
|
293
|
+
timeKey = "hour";
|
|
294
|
+
} else if (hrs <= 168) {
|
|
295
|
+
// For up to 7 days, group by day
|
|
296
|
+
displayFormat = is_pg
|
|
297
|
+
? "to_char(to_timestamp(created_at/1000), 'MM-DD')"
|
|
298
|
+
: "strftime('%m-%d', datetime(created_at/1000, 'unixepoch', 'localtime'))";
|
|
299
|
+
sortFormat = is_pg
|
|
300
|
+
? "to_char(to_timestamp(created_at/1000), 'YYYY-MM-DD')"
|
|
301
|
+
: "strftime('%Y-%m-%d', datetime(created_at/1000, 'unixepoch', 'localtime'))";
|
|
302
|
+
timeKey = "day";
|
|
303
|
+
} else {
|
|
304
|
+
// For longer periods (30 days), group by day showing month-day
|
|
305
|
+
displayFormat = is_pg
|
|
306
|
+
? "to_char(to_timestamp(created_at/1000), 'MM-DD')"
|
|
307
|
+
: "strftime('%m-%d', datetime(created_at/1000, 'unixepoch', 'localtime'))";
|
|
308
|
+
sortFormat = is_pg
|
|
309
|
+
? "to_char(to_timestamp(created_at/1000), 'YYYY-MM-DD')"
|
|
310
|
+
: "strftime('%Y-%m-%d', datetime(created_at/1000, 'unixepoch', 'localtime'))";
|
|
311
|
+
timeKey = "day";
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const tl = await all_async(
|
|
315
|
+
is_pg
|
|
316
|
+
? `SELECT primary_sector, ${displayFormat} as label, ${sortFormat} as sort_key, COUNT(*) as count
|
|
317
|
+
FROM ${mem_table} WHERE created_at > $1 GROUP BY primary_sector, ${sortFormat} ORDER BY sort_key`
|
|
318
|
+
: `SELECT primary_sector, ${displayFormat} as label, ${sortFormat} as sort_key, COUNT(*) as count
|
|
319
|
+
FROM ${mem_table} WHERE created_at > ? GROUP BY primary_sector, ${sortFormat} ORDER BY sort_key`,
|
|
320
|
+
[strt],
|
|
321
|
+
);
|
|
322
|
+
res.json({
|
|
323
|
+
timeline: tl.map((row: any) => ({ ...row, hour: row.label })),
|
|
324
|
+
grouping: timeKey,
|
|
325
|
+
});
|
|
326
|
+
} catch (e: any) {
|
|
327
|
+
res.status(500).json({ err: "internal", message: e.message });
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
app.get("/dashboard/top-memories", async (req: any, res: any) => {
|
|
332
|
+
try {
|
|
333
|
+
const mem_table = get_mem_table();
|
|
334
|
+
const lim = parseInt(req.query.limit || "10");
|
|
335
|
+
const topm = await all_async(
|
|
336
|
+
is_pg
|
|
337
|
+
? `SELECT id, content, primary_sector, salience, last_seen_at
|
|
338
|
+
FROM ${mem_table} ORDER BY salience DESC LIMIT $1`
|
|
339
|
+
: `SELECT id, content, primary_sector, salience, last_seen_at
|
|
340
|
+
FROM ${mem_table} ORDER BY salience DESC LIMIT ?`,
|
|
341
|
+
[lim],
|
|
342
|
+
);
|
|
343
|
+
res.json({
|
|
344
|
+
memories: topm.map((m: any) => ({
|
|
345
|
+
id: m.id,
|
|
346
|
+
content: m.content,
|
|
347
|
+
sector: m.primary_sector,
|
|
348
|
+
salience: m.salience,
|
|
349
|
+
lastSeen: m.last_seen_at,
|
|
350
|
+
})),
|
|
351
|
+
});
|
|
352
|
+
} catch (e: any) {
|
|
353
|
+
res.status(500).json({ err: "internal", message: e.message });
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
app.get("/dashboard/maintenance", async (req: any, res: any) => {
|
|
358
|
+
try {
|
|
359
|
+
const hrs = parseInt(req.query.hours || "24");
|
|
360
|
+
const strt = Date.now() - hrs * 60 * 60 * 1000;
|
|
361
|
+
const sc = process.env.OM_PG_SCHEMA || "public";
|
|
362
|
+
|
|
363
|
+
const ops = await all_async(
|
|
364
|
+
is_pg
|
|
365
|
+
? `SELECT type, to_char(to_timestamp(ts/1000), 'HH24:00') as hour, SUM(count) as cnt
|
|
366
|
+
FROM "${sc}"."stats" WHERE ts > $1 GROUP BY type, hour ORDER BY hour`
|
|
367
|
+
: `SELECT type, strftime('%H:00', datetime(ts/1000, 'unixepoch', 'localtime')) as hour, SUM(count) as cnt
|
|
368
|
+
FROM stats WHERE ts > ? GROUP BY type, hour ORDER BY hour`,
|
|
369
|
+
[strt],
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
const totals = await all_async(
|
|
373
|
+
is_pg
|
|
374
|
+
? `SELECT type, SUM(count) as total FROM "${sc}"."stats" WHERE ts > $1 GROUP BY type`
|
|
375
|
+
: `SELECT type, SUM(count) as total FROM stats WHERE ts > ? GROUP BY type`,
|
|
376
|
+
[strt],
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
const by_hr: Record<string, any> = {};
|
|
380
|
+
for (const op of ops) {
|
|
381
|
+
if (!by_hr[op.hour])
|
|
382
|
+
by_hr[op.hour] = {
|
|
383
|
+
hour: op.hour,
|
|
384
|
+
decay: 0,
|
|
385
|
+
reflection: 0,
|
|
386
|
+
consolidation: 0,
|
|
387
|
+
};
|
|
388
|
+
if (op.type === "decay") by_hr[op.hour].decay = op.cnt;
|
|
389
|
+
else if (op.type === "reflect")
|
|
390
|
+
by_hr[op.hour].reflection = op.cnt;
|
|
391
|
+
else if (op.type === "consolidate")
|
|
392
|
+
by_hr[op.hour].consolidation = op.cnt;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const tot_decay =
|
|
396
|
+
totals.find((t: any) => t.type === "decay")?.total || 0;
|
|
397
|
+
const tot_reflect =
|
|
398
|
+
totals.find((t: any) => t.type === "reflect")?.total || 0;
|
|
399
|
+
const tot_consol =
|
|
400
|
+
totals.find((t: any) => t.type === "consolidate")?.total || 0;
|
|
401
|
+
const tot_ops = tot_decay + tot_reflect + tot_consol;
|
|
402
|
+
const efficiency =
|
|
403
|
+
tot_ops > 0
|
|
404
|
+
? Math.round(((tot_reflect + tot_consol) / tot_ops) * 100)
|
|
405
|
+
: 0;
|
|
406
|
+
|
|
407
|
+
res.json({
|
|
408
|
+
operations: Object.values(by_hr),
|
|
409
|
+
totals: {
|
|
410
|
+
cycles: tot_decay,
|
|
411
|
+
reflections: tot_reflect,
|
|
412
|
+
consolidations: tot_consol,
|
|
413
|
+
efficiency,
|
|
414
|
+
},
|
|
415
|
+
});
|
|
416
|
+
} catch (e: any) {
|
|
417
|
+
res.status(500).json({ err: "internal", message: e.message });
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
}
|