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,399 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.track_req = track_req;
|
|
37
|
+
exports.req_tracker_mw = req_tracker_mw;
|
|
38
|
+
exports.dash = dash;
|
|
39
|
+
const db_1 = require("../../core/db");
|
|
40
|
+
const cfg_1 = require("../../core/cfg");
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const is_pg = cfg_1.env.metadata_backend === "postgres";
|
|
44
|
+
const get_mem_table = () => {
|
|
45
|
+
if (is_pg) {
|
|
46
|
+
const sc = process.env.OM_PG_SCHEMA || "public";
|
|
47
|
+
const tbl = process.env.OM_PG_TABLE || "openmemory_memories";
|
|
48
|
+
return `"${sc}"."${tbl}"`;
|
|
49
|
+
}
|
|
50
|
+
return "memories";
|
|
51
|
+
};
|
|
52
|
+
let reqz = {
|
|
53
|
+
win_start: Date.now(),
|
|
54
|
+
win_cnt: 0,
|
|
55
|
+
qps_hist: [],
|
|
56
|
+
};
|
|
57
|
+
const log_metric = async (type, value) => {
|
|
58
|
+
try {
|
|
59
|
+
const sc = process.env.OM_PG_SCHEMA || "public";
|
|
60
|
+
const sql = is_pg
|
|
61
|
+
? `insert into "${sc}"."stats"(type,count,ts) values($1,$2,$3)`
|
|
62
|
+
: "insert into stats(type,count,ts) values(?,?,?)";
|
|
63
|
+
await (0, db_1.run_async)(sql, [type, value, Date.now()]);
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
console.error("[metrics] log err:", e);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
function track_req(success) {
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
if (now - reqz.win_start >= 1000) {
|
|
72
|
+
const qps = reqz.win_cnt;
|
|
73
|
+
reqz.qps_hist.push(qps);
|
|
74
|
+
if (reqz.qps_hist.length > 5)
|
|
75
|
+
reqz.qps_hist.shift();
|
|
76
|
+
// Log metrics to database every second
|
|
77
|
+
log_metric("qps", qps).catch(console.error);
|
|
78
|
+
if (!success)
|
|
79
|
+
log_metric("error", 1).catch(console.error);
|
|
80
|
+
reqz.win_start = now;
|
|
81
|
+
reqz.win_cnt = 1;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
reqz.win_cnt++;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function req_tracker_mw() {
|
|
88
|
+
return (req, res, next) => {
|
|
89
|
+
if (req.url.startsWith("/dashboard") || req.url.startsWith("/health")) {
|
|
90
|
+
return next();
|
|
91
|
+
}
|
|
92
|
+
const orig = res.json.bind(res);
|
|
93
|
+
res.json = (data) => {
|
|
94
|
+
track_req(res.statusCode < 400);
|
|
95
|
+
return orig(data);
|
|
96
|
+
};
|
|
97
|
+
next();
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const get_db_sz = async () => {
|
|
101
|
+
try {
|
|
102
|
+
if (is_pg) {
|
|
103
|
+
const db_name = process.env.OM_PG_DB || "openmemory";
|
|
104
|
+
const result = await (0, db_1.all_async)(`SELECT pg_database_size('${db_name}') as size`);
|
|
105
|
+
return result[0]?.size
|
|
106
|
+
? Math.round(result[0].size / 1024 / 1024)
|
|
107
|
+
: 0;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
const dbp = path.resolve(process.cwd(), "./backend", cfg_1.env.db_path);
|
|
111
|
+
if (fs.existsSync(dbp)) {
|
|
112
|
+
const st = fs.statSync(dbp);
|
|
113
|
+
return Math.round(st.size / 1024 / 1024);
|
|
114
|
+
}
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
console.error("[db_sz] err:", e);
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
function dash(app) {
|
|
124
|
+
app.get("/dashboard/stats", async (_req, res) => {
|
|
125
|
+
try {
|
|
126
|
+
const mem_table = get_mem_table();
|
|
127
|
+
const totmem = await (0, db_1.all_async)(`SELECT COUNT(*) as count FROM ${mem_table}`);
|
|
128
|
+
const sectcnt = await (0, db_1.all_async)(`
|
|
129
|
+
SELECT primary_sector, COUNT(*) as count
|
|
130
|
+
FROM ${mem_table}
|
|
131
|
+
GROUP BY primary_sector
|
|
132
|
+
`);
|
|
133
|
+
const dayago = Date.now() - 24 * 60 * 60 * 1000;
|
|
134
|
+
const recmem = await (0, db_1.all_async)(is_pg
|
|
135
|
+
? `SELECT COUNT(*) as count FROM ${mem_table} WHERE created_at > $1`
|
|
136
|
+
: `SELECT COUNT(*) as count FROM ${mem_table} WHERE created_at > ?`, [dayago]);
|
|
137
|
+
const avgsal = await (0, db_1.all_async)(`SELECT AVG(salience) as avg FROM ${mem_table}`);
|
|
138
|
+
const decst = await (0, db_1.all_async)(`
|
|
139
|
+
SELECT
|
|
140
|
+
COUNT(*) as total,
|
|
141
|
+
AVG(decay_lambda) as avg_lambda,
|
|
142
|
+
MIN(salience) as min_salience,
|
|
143
|
+
MAX(salience) as max_salience
|
|
144
|
+
FROM ${mem_table}
|
|
145
|
+
`);
|
|
146
|
+
const upt = process.uptime();
|
|
147
|
+
// Calculate QPS stats from database (last hour)
|
|
148
|
+
const hour_ago = Date.now() - 60 * 60 * 1000;
|
|
149
|
+
const sc = process.env.OM_PG_SCHEMA || "public";
|
|
150
|
+
const qps_data = await (0, db_1.all_async)(is_pg
|
|
151
|
+
? `SELECT count, ts FROM "${sc}"."stats" WHERE type=$1 AND ts > $2 ORDER BY ts DESC`
|
|
152
|
+
: "SELECT count, ts FROM stats WHERE type=? AND ts > ? ORDER BY ts DESC", ["qps", hour_ago]);
|
|
153
|
+
const err_data = await (0, db_1.all_async)(is_pg
|
|
154
|
+
? `SELECT COUNT(*) as total FROM "${sc}"."stats" WHERE type=$1 AND ts > $2`
|
|
155
|
+
: "SELECT COUNT(*) as total FROM stats WHERE type=? AND ts > ?", ["error", hour_ago]);
|
|
156
|
+
const peak_qps = qps_data.length > 0
|
|
157
|
+
? Math.max(...qps_data.map((d) => d.count))
|
|
158
|
+
: 0;
|
|
159
|
+
const avg_qps = reqz.qps_hist.length > 0
|
|
160
|
+
? Math.round((reqz.qps_hist.reduce((a, b) => a + b, 0) /
|
|
161
|
+
reqz.qps_hist.length) *
|
|
162
|
+
100) / 100
|
|
163
|
+
: 0;
|
|
164
|
+
const total_reqs = qps_data.reduce((sum, d) => sum + d.count, 0);
|
|
165
|
+
const total_errs = err_data[0]?.total || 0;
|
|
166
|
+
const err_rate = total_reqs > 0
|
|
167
|
+
? ((total_errs / total_reqs) * 100).toFixed(1)
|
|
168
|
+
: "0.0";
|
|
169
|
+
const dbsz = await get_db_sz();
|
|
170
|
+
const dbpct = dbsz > 0 ? Math.min(100, Math.round((dbsz / 1024) * 100)) : 0;
|
|
171
|
+
const cachit = totmem[0]?.count > 0
|
|
172
|
+
? Math.round((totmem[0].count /
|
|
173
|
+
(totmem[0].count + total_errs * 2)) *
|
|
174
|
+
100)
|
|
175
|
+
: 0;
|
|
176
|
+
res.json({
|
|
177
|
+
totalMemories: totmem[0]?.count || 0,
|
|
178
|
+
recentMemories: recmem[0]?.count || 0,
|
|
179
|
+
sectorCounts: sectcnt.reduce((acc, row) => {
|
|
180
|
+
acc[row.primary_sector] = row.count;
|
|
181
|
+
return acc;
|
|
182
|
+
}, {}),
|
|
183
|
+
avgSalience: Number(avgsal[0]?.avg || 0).toFixed(3),
|
|
184
|
+
decayStats: {
|
|
185
|
+
total: decst[0]?.total || 0,
|
|
186
|
+
avgLambda: Number(decst[0]?.avg_lambda || 0).toFixed(3),
|
|
187
|
+
minSalience: Number(decst[0]?.min_salience || 0).toFixed(3),
|
|
188
|
+
maxSalience: Number(decst[0]?.max_salience || 0).toFixed(3),
|
|
189
|
+
},
|
|
190
|
+
requests: {
|
|
191
|
+
total: total_reqs,
|
|
192
|
+
errors: total_errs,
|
|
193
|
+
errorRate: err_rate,
|
|
194
|
+
lastHour: qps_data.length,
|
|
195
|
+
},
|
|
196
|
+
qps: { peak: peak_qps, average: avg_qps, cacheHitRate: cachit },
|
|
197
|
+
system: {
|
|
198
|
+
memoryUsage: dbpct,
|
|
199
|
+
heapUsed: dbsz,
|
|
200
|
+
heapTotal: 1024,
|
|
201
|
+
uptime: {
|
|
202
|
+
seconds: Math.floor(upt),
|
|
203
|
+
days: Math.floor(upt / 86400),
|
|
204
|
+
hours: Math.floor((upt % 86400) / 3600),
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
config: {
|
|
208
|
+
port: cfg_1.env.port,
|
|
209
|
+
vecDim: cfg_1.env.vec_dim,
|
|
210
|
+
cacheSegments: cfg_1.env.cache_segments,
|
|
211
|
+
maxActive: cfg_1.env.max_active,
|
|
212
|
+
decayInterval: cfg_1.env.decay_interval_minutes,
|
|
213
|
+
embedProvider: cfg_1.env.emb_kind,
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
catch (e) {
|
|
218
|
+
console.error("[dash] stats err:", e);
|
|
219
|
+
res.status(500).json({ err: "internal", message: e.message });
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
app.get("/dashboard/health", async (_req, res) => {
|
|
223
|
+
try {
|
|
224
|
+
const memusg = process.memoryUsage();
|
|
225
|
+
const upt = process.uptime();
|
|
226
|
+
res.json({
|
|
227
|
+
memory: {
|
|
228
|
+
heapUsed: Math.round(memusg.heapUsed / 1024 / 1024),
|
|
229
|
+
heapTotal: Math.round(memusg.heapTotal / 1024 / 1024),
|
|
230
|
+
rss: Math.round(memusg.rss / 1024 / 1024),
|
|
231
|
+
external: Math.round(memusg.external / 1024 / 1024),
|
|
232
|
+
},
|
|
233
|
+
uptime: {
|
|
234
|
+
seconds: Math.floor(upt),
|
|
235
|
+
days: Math.floor(upt / 86400),
|
|
236
|
+
hours: Math.floor((upt % 86400) / 3600),
|
|
237
|
+
},
|
|
238
|
+
process: {
|
|
239
|
+
pid: process.pid,
|
|
240
|
+
version: process.version,
|
|
241
|
+
platform: process.platform,
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
catch (e) {
|
|
246
|
+
res.status(500).json({ err: "internal", message: e.message });
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
app.get("/dashboard/activity", async (req, res) => {
|
|
250
|
+
try {
|
|
251
|
+
const mem_table = get_mem_table();
|
|
252
|
+
const lim = parseInt(req.query.limit || "50");
|
|
253
|
+
const recmem = await (0, db_1.all_async)(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 ?`, [lim]);
|
|
258
|
+
res.json({
|
|
259
|
+
activities: recmem.map((m) => ({
|
|
260
|
+
id: m.id,
|
|
261
|
+
type: "memory_updated",
|
|
262
|
+
sector: m.primary_sector,
|
|
263
|
+
content: m.content.substring(0, 100) + "...",
|
|
264
|
+
salience: m.salience,
|
|
265
|
+
timestamp: m.updated_at || m.created_at,
|
|
266
|
+
})),
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
catch (e) {
|
|
270
|
+
res.status(500).json({ err: "internal", message: e.message });
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
app.get("/dashboard/sectors/timeline", async (req, res) => {
|
|
274
|
+
try {
|
|
275
|
+
const mem_table = get_mem_table();
|
|
276
|
+
const hrs = parseInt(req.query.hours || "24");
|
|
277
|
+
const strt = Date.now() - hrs * 60 * 60 * 1000;
|
|
278
|
+
// Use different grouping based on time range
|
|
279
|
+
let displayFormat;
|
|
280
|
+
let sortFormat;
|
|
281
|
+
let timeKey;
|
|
282
|
+
if (hrs <= 24) {
|
|
283
|
+
// For 24 hours or less, group by date+hour for sorting, display only hour
|
|
284
|
+
displayFormat = is_pg
|
|
285
|
+
? "to_char(to_timestamp(created_at/1000), 'HH24:00')"
|
|
286
|
+
: "strftime('%H:00', datetime(created_at/1000, 'unixepoch', 'localtime'))";
|
|
287
|
+
sortFormat = is_pg
|
|
288
|
+
? "to_char(to_timestamp(created_at/1000), 'YYYY-MM-DD HH24:00')"
|
|
289
|
+
: "strftime('%Y-%m-%d %H:00', datetime(created_at/1000, 'unixepoch', 'localtime'))";
|
|
290
|
+
timeKey = "hour";
|
|
291
|
+
}
|
|
292
|
+
else if (hrs <= 168) {
|
|
293
|
+
// For up to 7 days, group by day
|
|
294
|
+
displayFormat = is_pg
|
|
295
|
+
? "to_char(to_timestamp(created_at/1000), 'MM-DD')"
|
|
296
|
+
: "strftime('%m-%d', datetime(created_at/1000, 'unixepoch', 'localtime'))";
|
|
297
|
+
sortFormat = is_pg
|
|
298
|
+
? "to_char(to_timestamp(created_at/1000), 'YYYY-MM-DD')"
|
|
299
|
+
: "strftime('%Y-%m-%d', datetime(created_at/1000, 'unixepoch', 'localtime'))";
|
|
300
|
+
timeKey = "day";
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
// For longer periods (30 days), group by day showing month-day
|
|
304
|
+
displayFormat = is_pg
|
|
305
|
+
? "to_char(to_timestamp(created_at/1000), 'MM-DD')"
|
|
306
|
+
: "strftime('%m-%d', datetime(created_at/1000, 'unixepoch', 'localtime'))";
|
|
307
|
+
sortFormat = is_pg
|
|
308
|
+
? "to_char(to_timestamp(created_at/1000), 'YYYY-MM-DD')"
|
|
309
|
+
: "strftime('%Y-%m-%d', datetime(created_at/1000, 'unixepoch', 'localtime'))";
|
|
310
|
+
timeKey = "day";
|
|
311
|
+
}
|
|
312
|
+
const tl = await (0, db_1.all_async)(is_pg
|
|
313
|
+
? `SELECT primary_sector, ${displayFormat} as label, ${sortFormat} as sort_key, COUNT(*) as count
|
|
314
|
+
FROM ${mem_table} WHERE created_at > $1 GROUP BY primary_sector, ${sortFormat} ORDER BY sort_key`
|
|
315
|
+
: `SELECT primary_sector, ${displayFormat} as label, ${sortFormat} as sort_key, COUNT(*) as count
|
|
316
|
+
FROM ${mem_table} WHERE created_at > ? GROUP BY primary_sector, ${sortFormat} ORDER BY sort_key`, [strt]);
|
|
317
|
+
res.json({
|
|
318
|
+
timeline: tl.map((row) => ({ ...row, hour: row.label })),
|
|
319
|
+
grouping: timeKey,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
catch (e) {
|
|
323
|
+
res.status(500).json({ err: "internal", message: e.message });
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
app.get("/dashboard/top-memories", async (req, res) => {
|
|
327
|
+
try {
|
|
328
|
+
const mem_table = get_mem_table();
|
|
329
|
+
const lim = parseInt(req.query.limit || "10");
|
|
330
|
+
const topm = await (0, db_1.all_async)(is_pg
|
|
331
|
+
? `SELECT id, content, primary_sector, salience, last_seen_at
|
|
332
|
+
FROM ${mem_table} ORDER BY salience DESC LIMIT $1`
|
|
333
|
+
: `SELECT id, content, primary_sector, salience, last_seen_at
|
|
334
|
+
FROM ${mem_table} ORDER BY salience DESC LIMIT ?`, [lim]);
|
|
335
|
+
res.json({
|
|
336
|
+
memories: topm.map((m) => ({
|
|
337
|
+
id: m.id,
|
|
338
|
+
content: m.content,
|
|
339
|
+
sector: m.primary_sector,
|
|
340
|
+
salience: m.salience,
|
|
341
|
+
lastSeen: m.last_seen_at,
|
|
342
|
+
})),
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
catch (e) {
|
|
346
|
+
res.status(500).json({ err: "internal", message: e.message });
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
app.get("/dashboard/maintenance", async (req, res) => {
|
|
350
|
+
try {
|
|
351
|
+
const hrs = parseInt(req.query.hours || "24");
|
|
352
|
+
const strt = Date.now() - hrs * 60 * 60 * 1000;
|
|
353
|
+
const sc = process.env.OM_PG_SCHEMA || "public";
|
|
354
|
+
const ops = await (0, db_1.all_async)(is_pg
|
|
355
|
+
? `SELECT type, to_char(to_timestamp(ts/1000), 'HH24:00') as hour, SUM(count) as cnt
|
|
356
|
+
FROM "${sc}"."stats" WHERE ts > $1 GROUP BY type, hour ORDER BY hour`
|
|
357
|
+
: `SELECT type, strftime('%H:00', datetime(ts/1000, 'unixepoch', 'localtime')) as hour, SUM(count) as cnt
|
|
358
|
+
FROM stats WHERE ts > ? GROUP BY type, hour ORDER BY hour`, [strt]);
|
|
359
|
+
const totals = await (0, db_1.all_async)(is_pg
|
|
360
|
+
? `SELECT type, SUM(count) as total FROM "${sc}"."stats" WHERE ts > $1 GROUP BY type`
|
|
361
|
+
: `SELECT type, SUM(count) as total FROM stats WHERE ts > ? GROUP BY type`, [strt]);
|
|
362
|
+
const by_hr = {};
|
|
363
|
+
for (const op of ops) {
|
|
364
|
+
if (!by_hr[op.hour])
|
|
365
|
+
by_hr[op.hour] = {
|
|
366
|
+
hour: op.hour,
|
|
367
|
+
decay: 0,
|
|
368
|
+
reflection: 0,
|
|
369
|
+
consolidation: 0,
|
|
370
|
+
};
|
|
371
|
+
if (op.type === "decay")
|
|
372
|
+
by_hr[op.hour].decay = op.cnt;
|
|
373
|
+
else if (op.type === "reflect")
|
|
374
|
+
by_hr[op.hour].reflection = op.cnt;
|
|
375
|
+
else if (op.type === "consolidate")
|
|
376
|
+
by_hr[op.hour].consolidation = op.cnt;
|
|
377
|
+
}
|
|
378
|
+
const tot_decay = totals.find((t) => t.type === "decay")?.total || 0;
|
|
379
|
+
const tot_reflect = totals.find((t) => t.type === "reflect")?.total || 0;
|
|
380
|
+
const tot_consol = totals.find((t) => t.type === "consolidate")?.total || 0;
|
|
381
|
+
const tot_ops = tot_decay + tot_reflect + tot_consol;
|
|
382
|
+
const efficiency = tot_ops > 0
|
|
383
|
+
? Math.round(((tot_reflect + tot_consol) / tot_ops) * 100)
|
|
384
|
+
: 0;
|
|
385
|
+
res.json({
|
|
386
|
+
operations: Object.values(by_hr),
|
|
387
|
+
totals: {
|
|
388
|
+
cycles: tot_decay,
|
|
389
|
+
reflections: tot_reflect,
|
|
390
|
+
consolidations: tot_consol,
|
|
391
|
+
efficiency,
|
|
392
|
+
},
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
catch (e) {
|
|
396
|
+
res.status(500).json({ err: "internal", message: e.message });
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.docs_route = docs_route;
|
|
4
|
+
const OPENAPI_SPEC = {
|
|
5
|
+
openapi: "3.1.0",
|
|
6
|
+
info: {
|
|
7
|
+
title: "Ahok Memory Cloud API",
|
|
8
|
+
description: "Universal Long-Term Memory for AI Agents and Applications. High-performance, multi-sector vector memory based on Hierarchical Segmented Graphs (HSG).",
|
|
9
|
+
version: "2.0.0",
|
|
10
|
+
},
|
|
11
|
+
servers: [
|
|
12
|
+
{
|
|
13
|
+
url: "https://zqmt62peqz.us-east-1.awsapprunner.com",
|
|
14
|
+
description: "Production Environment",
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
components: {
|
|
18
|
+
securitySchemes: {
|
|
19
|
+
ApiKeyAuth: {
|
|
20
|
+
type: "apiKey",
|
|
21
|
+
in: "header",
|
|
22
|
+
name: "x-api-key",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
schemas: {
|
|
26
|
+
Memory: {
|
|
27
|
+
type: "object",
|
|
28
|
+
properties: {
|
|
29
|
+
id: { type: "string", format: "uuid" },
|
|
30
|
+
content: { type: "string" },
|
|
31
|
+
primary_sector: { type: "string" },
|
|
32
|
+
sectors: { type: "array", items: { type: "string" } },
|
|
33
|
+
tags: { type: "array", items: { type: "string" } },
|
|
34
|
+
metadata: { type: "object" },
|
|
35
|
+
created_at: { type: "number" },
|
|
36
|
+
updated_at: { type: "number" },
|
|
37
|
+
salience: { type: "number" },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
security: [{ ApiKeyAuth: [] }],
|
|
43
|
+
paths: {
|
|
44
|
+
"/memory/add": {
|
|
45
|
+
post: {
|
|
46
|
+
summary: "Add a new memory",
|
|
47
|
+
description: "Stores a piece of information in the long-term memory. It will be automatically classified and indexed across relevant sectors.",
|
|
48
|
+
requestBody: {
|
|
49
|
+
required: true,
|
|
50
|
+
content: {
|
|
51
|
+
"application/json": {
|
|
52
|
+
schema: {
|
|
53
|
+
type: "object",
|
|
54
|
+
required: ["content"],
|
|
55
|
+
properties: {
|
|
56
|
+
content: { type: "string", description: "The text content to remember." },
|
|
57
|
+
tags: { type: "array", items: { type: "string" }, description: "Optional tags for categorization." },
|
|
58
|
+
metadata: { type: "object", description: "Optional arbitrary metadata." },
|
|
59
|
+
user_id: { type: "string", description: "Optional user identifier for multi-tenant isolation." },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
responses: {
|
|
66
|
+
200: {
|
|
67
|
+
description: "Memory successfully added",
|
|
68
|
+
content: {
|
|
69
|
+
"application/json": {
|
|
70
|
+
schema: { $ref: "#/components/schemas/Memory" },
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
"/memory/query": {
|
|
78
|
+
post: {
|
|
79
|
+
summary: "Query contextual memory",
|
|
80
|
+
description: "Performs a semantic and contextual search across memories. Returns the most relevant matches based on similarity, salience, and temporal factors.",
|
|
81
|
+
requestBody: {
|
|
82
|
+
required: true,
|
|
83
|
+
content: {
|
|
84
|
+
"application/json": {
|
|
85
|
+
schema: {
|
|
86
|
+
type: "object",
|
|
87
|
+
required: ["query"],
|
|
88
|
+
properties: {
|
|
89
|
+
query: { type: "string", description: "The search query (natural language)." },
|
|
90
|
+
k: { type: "integer", default: 8, description: "Number of results to return." },
|
|
91
|
+
user_id: { type: "string", description: "Filter by user identifier." },
|
|
92
|
+
filters: {
|
|
93
|
+
type: "object",
|
|
94
|
+
properties: {
|
|
95
|
+
sector: { type: "string", description: "Filter by a specific memory sector." },
|
|
96
|
+
min_score: { type: "number", description: "Minimum relevance score (0-1)." },
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
responses: {
|
|
105
|
+
200: {
|
|
106
|
+
description: "Matching memories found",
|
|
107
|
+
content: {
|
|
108
|
+
"application/json": {
|
|
109
|
+
schema: {
|
|
110
|
+
type: "object",
|
|
111
|
+
properties: {
|
|
112
|
+
query: { type: "string" },
|
|
113
|
+
matches: {
|
|
114
|
+
type: "array",
|
|
115
|
+
items: {
|
|
116
|
+
type: "object",
|
|
117
|
+
properties: {
|
|
118
|
+
id: { type: "string" },
|
|
119
|
+
content: { type: "string" },
|
|
120
|
+
score: { type: "number" },
|
|
121
|
+
sectors: { type: "array", items: { type: "string" } },
|
|
122
|
+
salience: { type: "number" },
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
"/memory/all": {
|
|
135
|
+
get: {
|
|
136
|
+
summary: "List all memories",
|
|
137
|
+
description: "Retrieves a paginated list of all stored memories.",
|
|
138
|
+
parameters: [
|
|
139
|
+
{ name: "user_id", in: "query", schema: { type: "string" } },
|
|
140
|
+
{ name: "l", in: "query", schema: { type: "integer", default: 50 }, description: "Limit" },
|
|
141
|
+
{ name: "u", in: "query", schema: { type: "integer", default: 0 }, description: "Offset" },
|
|
142
|
+
],
|
|
143
|
+
responses: {
|
|
144
|
+
200: {
|
|
145
|
+
description: "List of memories",
|
|
146
|
+
content: {
|
|
147
|
+
"application/json": {
|
|
148
|
+
schema: {
|
|
149
|
+
type: "object",
|
|
150
|
+
properties: {
|
|
151
|
+
items: { type: "array", items: { $ref: "#/components/schemas/Memory" } },
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
"/memory/{id}": {
|
|
161
|
+
delete: {
|
|
162
|
+
summary: "Delete a memory",
|
|
163
|
+
parameters: [
|
|
164
|
+
{ name: "id", in: "path", required: true, schema: { type: "string" } },
|
|
165
|
+
{ name: "user_id", in: "query", schema: { type: "string" } },
|
|
166
|
+
],
|
|
167
|
+
responses: {
|
|
168
|
+
200: {
|
|
169
|
+
description: "Memory deleted",
|
|
170
|
+
content: { "application/json": { schema: { type: "object", properties: { ok: { type: "boolean" } } } } },
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
"/health": {
|
|
176
|
+
get: {
|
|
177
|
+
summary: "System health check",
|
|
178
|
+
security: [],
|
|
179
|
+
responses: {
|
|
180
|
+
200: {
|
|
181
|
+
description: "System is healthy",
|
|
182
|
+
content: { "application/json": { schema: { type: "object", properties: { ok: { type: "boolean" } } } } },
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
function docs_route(app) {
|
|
190
|
+
app.get("/openapi.json", (req, res) => {
|
|
191
|
+
res.json(OPENAPI_SPEC);
|
|
192
|
+
});
|
|
193
|
+
app.get("/docs", (req, res) => {
|
|
194
|
+
const html = `<!DOCTYPE html>
|
|
195
|
+
<html lang="en">
|
|
196
|
+
<head>
|
|
197
|
+
<meta charset="UTF-8">
|
|
198
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
199
|
+
<title>Ahok Memory Cloud API Docs</title>
|
|
200
|
+
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css" />
|
|
201
|
+
<style>
|
|
202
|
+
body { margin: 0; background: #020617; }
|
|
203
|
+
.swagger-ui .topbar { display: none; }
|
|
204
|
+
.swagger-ui .info .title { color: #f8fafc; }
|
|
205
|
+
.swagger-ui .scheme-container { background: #0f172a; box-shadow: none; border-bottom: 1px solid #1e293b; }
|
|
206
|
+
.swagger-ui select, .swagger-ui input { background: #1e293b; color: #f8fafc; border: 1px solid #334155; }
|
|
207
|
+
.swagger-ui .opblock { border-radius: 12px; background: #0f172a; border: 1px solid #1e293b; }
|
|
208
|
+
.swagger-ui .opblock-tag { color: #f8fafc; border-bottom: 1px solid #1e293b; }
|
|
209
|
+
.swagger-ui .opblock .opblock-summary-description { color: #94a3b8; }
|
|
210
|
+
.swagger-ui section.models { border: 1px solid #1e293b; background: #0f172a; border-radius: 12px; }
|
|
211
|
+
.swagger-ui section.models h4 { color: #f8fafc; }
|
|
212
|
+
.swagger-ui .model-box { background: #020617; border: 1px solid #1e293b; }
|
|
213
|
+
.swagger-ui .btn.authorize { color: #10b981; border-color: #10b981; }
|
|
214
|
+
.swagger-ui .btn.authorize svg { fill: #10b981; }
|
|
215
|
+
.swagger-ui .opblock.opblock-post { border-color: #0ea5e9; }
|
|
216
|
+
.swagger-ui .opblock.opblock-post .opblock-summary { border-color: #0ea5e9; }
|
|
217
|
+
.swagger-ui .opblock.opblock-post .opblock-summary-method { background: #0ea5e9; }
|
|
218
|
+
</style>
|
|
219
|
+
</head>
|
|
220
|
+
<body>
|
|
221
|
+
<div id="swagger-ui"></div>
|
|
222
|
+
<script src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-bundle.js"></script>
|
|
223
|
+
<script>
|
|
224
|
+
window.onload = () => {
|
|
225
|
+
window.ui = SwaggerUIBundle({
|
|
226
|
+
url: '/openapi.json',
|
|
227
|
+
dom_id: '#swagger-ui',
|
|
228
|
+
deepLinking: true,
|
|
229
|
+
presets: [
|
|
230
|
+
SwaggerUIBundle.presets.apis,
|
|
231
|
+
],
|
|
232
|
+
layout: "BaseLayout"
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
</script>
|
|
236
|
+
</body>
|
|
237
|
+
</html>`;
|
|
238
|
+
res.setHeader("Content-Type", "text/html");
|
|
239
|
+
res.send(html);
|
|
240
|
+
});
|
|
241
|
+
}
|