@hasna/mementos 0.14.18 → 0.14.20
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/dist/cli/index.js +73 -41
- package/dist/index.js +476 -30
- package/dist/mcp/index.js +64 -31
- package/dist/sdk/index.js +397 -0
- package/dist/server/index.js +652 -164
- package/package.json +6 -2
package/dist/server/index.js
CHANGED
|
@@ -9790,6 +9790,7 @@ CREATE TABLE IF NOT EXISTS machines (
|
|
|
9790
9790
|
name TEXT NOT NULL UNIQUE,
|
|
9791
9791
|
hostname TEXT NOT NULL,
|
|
9792
9792
|
platform TEXT NOT NULL DEFAULT 'unknown',
|
|
9793
|
+
is_primary INTEGER NOT NULL DEFAULT 0,
|
|
9793
9794
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
9794
9795
|
last_seen_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
9795
9796
|
);
|
|
@@ -10169,33 +10170,44 @@ CREATE INDEX IF NOT EXISTS idx_memories_sequence_group ON memories(sequence_grou
|
|
|
10169
10170
|
INSERT OR IGNORE INTO _migrations (id) VALUES (32);
|
|
10170
10171
|
`,
|
|
10171
10172
|
`
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
|
|
10177
|
-
|
|
10178
|
-
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
|
|
10185
|
-
|
|
10186
|
-
|
|
10187
|
-
|
|
10188
|
-
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
|
|
10194
|
-
|
|
10195
|
-
|
|
10196
|
-
|
|
10197
|
-
|
|
10198
|
-
|
|
10173
|
+
CREATE TABLE IF NOT EXISTS tasks (
|
|
10174
|
+
id TEXT PRIMARY KEY,
|
|
10175
|
+
subject TEXT NOT NULL,
|
|
10176
|
+
description TEXT DEFAULT '',
|
|
10177
|
+
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'in_progress', 'completed', 'failed', 'cancelled')),
|
|
10178
|
+
priority TEXT NOT NULL DEFAULT 'medium' CHECK(priority IN ('critical', 'high', 'medium', 'low')),
|
|
10179
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
10180
|
+
assigned_agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
|
|
10181
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
10182
|
+
session_id TEXT,
|
|
10183
|
+
parent_task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
|
|
10184
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
10185
|
+
progress REAL NOT NULL DEFAULT 0 CHECK(progress >= 0 AND progress <= 1),
|
|
10186
|
+
due_at TEXT,
|
|
10187
|
+
started_at TEXT,
|
|
10188
|
+
completed_at TEXT,
|
|
10189
|
+
failed_at TEXT,
|
|
10190
|
+
error TEXT,
|
|
10191
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
10192
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
10193
|
+
);
|
|
10194
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
|
|
10195
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_priority ON tasks(priority);
|
|
10196
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_agent ON tasks(assigned_agent_id);
|
|
10197
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project_id);
|
|
10198
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_session ON tasks(session_id);
|
|
10199
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_task_id);
|
|
10200
|
+
|
|
10201
|
+
CREATE TABLE IF NOT EXISTS task_comments (
|
|
10202
|
+
id TEXT PRIMARY KEY,
|
|
10203
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
10204
|
+
agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
|
|
10205
|
+
body TEXT NOT NULL,
|
|
10206
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
10207
|
+
);
|
|
10208
|
+
CREATE INDEX IF NOT EXISTS idx_task_comments_task ON task_comments(task_id);
|
|
10209
|
+
CREATE INDEX IF NOT EXISTS idx_task_comments_agent ON task_comments(agent_id);
|
|
10210
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (34);
|
|
10199
10211
|
`
|
|
10200
10212
|
];
|
|
10201
10213
|
});
|
|
@@ -10886,7 +10898,7 @@ function createMemory(input, dedupeMode = "merge", db) {
|
|
|
10886
10898
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 1, ?, ?, ?, ?, ?, ?)`, [
|
|
10887
10899
|
id,
|
|
10888
10900
|
input.key,
|
|
10889
|
-
|
|
10901
|
+
safeValue,
|
|
10890
10902
|
input.category || "knowledge",
|
|
10891
10903
|
input.scope || "private",
|
|
10892
10904
|
input.summary || null,
|
|
@@ -11793,6 +11805,10 @@ function buildFilterConditions(filter) {
|
|
|
11793
11805
|
params.push(tag);
|
|
11794
11806
|
}
|
|
11795
11807
|
}
|
|
11808
|
+
if (filter.namespace) {
|
|
11809
|
+
conditions.push("m.namespace = ?");
|
|
11810
|
+
params.push(filter.namespace);
|
|
11811
|
+
}
|
|
11796
11812
|
return { conditions, params };
|
|
11797
11813
|
}
|
|
11798
11814
|
function searchWithFts5(d, query, queryLower, filter, graphBoostedIds) {
|
|
@@ -13756,8 +13772,12 @@ function parseMachine(row) {
|
|
|
13756
13772
|
is_primary: Boolean(row.is_primary)
|
|
13757
13773
|
};
|
|
13758
13774
|
}
|
|
13775
|
+
function normalizeHostname(host) {
|
|
13776
|
+
return host.replace(/\.(local|lan|home|internal)$/i, "");
|
|
13777
|
+
}
|
|
13759
13778
|
function registerMachine(name, db = getDatabase()) {
|
|
13760
|
-
const
|
|
13779
|
+
const rawHost = hostname();
|
|
13780
|
+
const host = normalizeHostname(rawHost);
|
|
13761
13781
|
const plat = platform2();
|
|
13762
13782
|
const machineName = name?.trim() || host;
|
|
13763
13783
|
const existing = parseMachine(db.query("SELECT * FROM machines WHERE hostname = ?").get(host));
|
|
@@ -13795,7 +13815,7 @@ function touchMachine(id, db = getDatabase()) {
|
|
|
13795
13815
|
db.run("UPDATE machines SET last_seen_at = ? WHERE id = ?", [now(), id]);
|
|
13796
13816
|
}
|
|
13797
13817
|
function getCurrentMachineId(db = getDatabase()) {
|
|
13798
|
-
const host = hostname();
|
|
13818
|
+
const host = normalizeHostname(hostname());
|
|
13799
13819
|
const m = db.query("SELECT id FROM machines WHERE hostname = ?").get(host);
|
|
13800
13820
|
if (m) {
|
|
13801
13821
|
touchMachine(m.id, db);
|
|
@@ -14841,6 +14861,374 @@ async function _processNext() {
|
|
|
14841
14861
|
}
|
|
14842
14862
|
}
|
|
14843
14863
|
|
|
14864
|
+
// src/lib/task-runner.ts
|
|
14865
|
+
init_database();
|
|
14866
|
+
|
|
14867
|
+
// src/db/tasks.ts
|
|
14868
|
+
init_database();
|
|
14869
|
+
function parseTask(row) {
|
|
14870
|
+
return {
|
|
14871
|
+
id: row.id,
|
|
14872
|
+
subject: row.subject,
|
|
14873
|
+
description: row.description ?? "",
|
|
14874
|
+
status: row.status,
|
|
14875
|
+
priority: row.priority,
|
|
14876
|
+
tags: JSON.parse(row.tags ?? "[]"),
|
|
14877
|
+
assigned_agent_id: row.assigned_agent_id ?? null,
|
|
14878
|
+
project_id: row.project_id ?? null,
|
|
14879
|
+
session_id: row.session_id ?? null,
|
|
14880
|
+
parent_task_id: row.parent_task_id ?? null,
|
|
14881
|
+
metadata: JSON.parse(row.metadata ?? "{}"),
|
|
14882
|
+
progress: row.progress,
|
|
14883
|
+
due_at: row.due_at ?? null,
|
|
14884
|
+
started_at: row.started_at ?? null,
|
|
14885
|
+
completed_at: row.completed_at ?? null,
|
|
14886
|
+
failed_at: row.failed_at ?? null,
|
|
14887
|
+
error: row.error ?? null,
|
|
14888
|
+
created_at: row.created_at,
|
|
14889
|
+
updated_at: row.updated_at
|
|
14890
|
+
};
|
|
14891
|
+
}
|
|
14892
|
+
function createTask(db, input) {
|
|
14893
|
+
const id = uuid();
|
|
14894
|
+
const ts = now();
|
|
14895
|
+
db.run(`INSERT INTO tasks (id, subject, description, status, priority, tags, assigned_agent_id, project_id, session_id, parent_task_id, metadata, due_at, created_at, updated_at)
|
|
14896
|
+
VALUES (?, ?, ?, 'pending', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
14897
|
+
id,
|
|
14898
|
+
input.subject,
|
|
14899
|
+
input.description ?? "",
|
|
14900
|
+
input.priority ?? "medium",
|
|
14901
|
+
JSON.stringify(input.tags ?? []),
|
|
14902
|
+
input.assigned_agent_id ?? null,
|
|
14903
|
+
input.project_id ?? null,
|
|
14904
|
+
input.session_id ?? null,
|
|
14905
|
+
input.parent_task_id ?? null,
|
|
14906
|
+
JSON.stringify(input.metadata ?? {}),
|
|
14907
|
+
input.due_at ?? null,
|
|
14908
|
+
ts,
|
|
14909
|
+
ts
|
|
14910
|
+
]);
|
|
14911
|
+
const row = db.query("SELECT * FROM tasks WHERE id = ?").get(id);
|
|
14912
|
+
if (!row) {
|
|
14913
|
+
throw new Error(`Failed to load task after create: ${id}`);
|
|
14914
|
+
}
|
|
14915
|
+
return parseTask(row);
|
|
14916
|
+
}
|
|
14917
|
+
function getTask(db, id) {
|
|
14918
|
+
const row = db.query("SELECT * FROM tasks WHERE id = ?").get(id);
|
|
14919
|
+
return row ? parseTask(row) : null;
|
|
14920
|
+
}
|
|
14921
|
+
function listTasks(db, filter) {
|
|
14922
|
+
let sql = "SELECT * FROM tasks WHERE 1=1";
|
|
14923
|
+
let countSql = "SELECT COUNT(*) as c FROM tasks WHERE 1=1";
|
|
14924
|
+
const params = [];
|
|
14925
|
+
const countParams = [];
|
|
14926
|
+
if (filter?.status) {
|
|
14927
|
+
sql += " AND status = ?";
|
|
14928
|
+
countSql += " AND status = ?";
|
|
14929
|
+
params.push(filter.status);
|
|
14930
|
+
countParams.push(filter.status);
|
|
14931
|
+
}
|
|
14932
|
+
if (filter?.priority) {
|
|
14933
|
+
sql += " AND priority = ?";
|
|
14934
|
+
countSql += " AND priority = ?";
|
|
14935
|
+
params.push(filter.priority);
|
|
14936
|
+
countParams.push(filter.priority);
|
|
14937
|
+
}
|
|
14938
|
+
if (filter?.assigned_agent_id) {
|
|
14939
|
+
sql += " AND assigned_agent_id = ?";
|
|
14940
|
+
countSql += " AND assigned_agent_id = ?";
|
|
14941
|
+
params.push(filter.assigned_agent_id);
|
|
14942
|
+
countParams.push(filter.assigned_agent_id);
|
|
14943
|
+
}
|
|
14944
|
+
if (filter?.project_id) {
|
|
14945
|
+
sql += " AND project_id = ?";
|
|
14946
|
+
countSql += " AND project_id = ?";
|
|
14947
|
+
params.push(filter.project_id);
|
|
14948
|
+
countParams.push(filter.project_id);
|
|
14949
|
+
}
|
|
14950
|
+
if (filter?.session_id) {
|
|
14951
|
+
sql += " AND session_id = ?";
|
|
14952
|
+
countSql += " AND session_id = ?";
|
|
14953
|
+
params.push(filter.session_id);
|
|
14954
|
+
countParams.push(filter.session_id);
|
|
14955
|
+
}
|
|
14956
|
+
if (filter?.parent_task_id !== undefined) {
|
|
14957
|
+
if (filter.parent_task_id === null) {
|
|
14958
|
+
sql += " AND parent_task_id IS NULL";
|
|
14959
|
+
countSql += " AND parent_task_id IS NULL";
|
|
14960
|
+
} else {
|
|
14961
|
+
sql += " AND parent_task_id = ?";
|
|
14962
|
+
countSql += " AND parent_task_id = ?";
|
|
14963
|
+
params.push(filter.parent_task_id);
|
|
14964
|
+
countParams.push(filter.parent_task_id);
|
|
14965
|
+
}
|
|
14966
|
+
}
|
|
14967
|
+
if (filter?.tags?.length) {
|
|
14968
|
+
const placeholders = filter.tags.map(() => "?").join(", ");
|
|
14969
|
+
sql += ` AND EXISTS (SELECT 1 FROM json_each(tags) WHERE value IN (${placeholders}))`;
|
|
14970
|
+
countSql += ` AND EXISTS (SELECT 1 FROM json_each(tags) WHERE value IN (${placeholders}))`;
|
|
14971
|
+
params.push(...filter.tags);
|
|
14972
|
+
countParams.push(...filter.tags);
|
|
14973
|
+
}
|
|
14974
|
+
sql += " ORDER BY CASE priority WHEN 'critical' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 WHEN 'low' THEN 4 END, created_at ASC";
|
|
14975
|
+
if (filter?.limit !== undefined) {
|
|
14976
|
+
sql += " LIMIT ?";
|
|
14977
|
+
params.push(filter.limit);
|
|
14978
|
+
}
|
|
14979
|
+
if (filter?.offset !== undefined) {
|
|
14980
|
+
sql += " OFFSET ?";
|
|
14981
|
+
params.push(filter.offset);
|
|
14982
|
+
}
|
|
14983
|
+
const rows = db.query(sql).all(...params);
|
|
14984
|
+
const countRow = db.query(countSql).get(...countParams);
|
|
14985
|
+
return { tasks: rows.map(parseTask), count: countRow.c };
|
|
14986
|
+
}
|
|
14987
|
+
function updateTask(db, id, input) {
|
|
14988
|
+
const existing = getTask(db, id);
|
|
14989
|
+
if (!existing)
|
|
14990
|
+
return null;
|
|
14991
|
+
const updates = [];
|
|
14992
|
+
const params = [];
|
|
14993
|
+
const ts = now();
|
|
14994
|
+
if (input.subject !== undefined) {
|
|
14995
|
+
updates.push("subject = ?");
|
|
14996
|
+
params.push(input.subject);
|
|
14997
|
+
}
|
|
14998
|
+
if (input.description !== undefined) {
|
|
14999
|
+
updates.push("description = ?");
|
|
15000
|
+
params.push(input.description);
|
|
15001
|
+
}
|
|
15002
|
+
if (input.status !== undefined) {
|
|
15003
|
+
updates.push("status = ?");
|
|
15004
|
+
params.push(input.status);
|
|
15005
|
+
if (input.status === "in_progress" && !existing.started_at) {
|
|
15006
|
+
updates.push("started_at = ?");
|
|
15007
|
+
params.push(ts);
|
|
15008
|
+
}
|
|
15009
|
+
if (input.status === "completed") {
|
|
15010
|
+
updates.push("completed_at = ?");
|
|
15011
|
+
params.push(ts);
|
|
15012
|
+
updates.push("progress = ?");
|
|
15013
|
+
params.push(1);
|
|
15014
|
+
}
|
|
15015
|
+
if (input.status === "failed") {
|
|
15016
|
+
updates.push("failed_at = ?");
|
|
15017
|
+
params.push(ts);
|
|
15018
|
+
}
|
|
15019
|
+
}
|
|
15020
|
+
if (input.priority !== undefined) {
|
|
15021
|
+
updates.push("priority = ?");
|
|
15022
|
+
params.push(input.priority);
|
|
15023
|
+
}
|
|
15024
|
+
if (input.tags !== undefined) {
|
|
15025
|
+
updates.push("tags = ?");
|
|
15026
|
+
params.push(JSON.stringify(input.tags));
|
|
15027
|
+
}
|
|
15028
|
+
if (input.assigned_agent_id !== undefined) {
|
|
15029
|
+
updates.push("assigned_agent_id = ?");
|
|
15030
|
+
params.push(input.assigned_agent_id);
|
|
15031
|
+
}
|
|
15032
|
+
if (input.metadata !== undefined) {
|
|
15033
|
+
updates.push("metadata = ?");
|
|
15034
|
+
params.push(JSON.stringify(input.metadata));
|
|
15035
|
+
}
|
|
15036
|
+
if (input.progress !== undefined) {
|
|
15037
|
+
updates.push("progress = ?");
|
|
15038
|
+
params.push(input.progress);
|
|
15039
|
+
}
|
|
15040
|
+
if (input.due_at !== undefined) {
|
|
15041
|
+
updates.push("due_at = ?");
|
|
15042
|
+
params.push(input.due_at);
|
|
15043
|
+
}
|
|
15044
|
+
if (input.error !== undefined) {
|
|
15045
|
+
updates.push("error = ?");
|
|
15046
|
+
params.push(input.error);
|
|
15047
|
+
}
|
|
15048
|
+
if (updates.length === 0)
|
|
15049
|
+
return existing;
|
|
15050
|
+
updates.push("updated_at = ?");
|
|
15051
|
+
params.push(ts);
|
|
15052
|
+
params.push(id);
|
|
15053
|
+
db.run(`UPDATE tasks SET ${updates.join(", ")} WHERE id = ?`, ...params);
|
|
15054
|
+
return getTask(db, id);
|
|
15055
|
+
}
|
|
15056
|
+
function deleteTask(db, id) {
|
|
15057
|
+
const result = db.run("DELETE FROM tasks WHERE id = ?", id);
|
|
15058
|
+
return result.changes > 0;
|
|
15059
|
+
}
|
|
15060
|
+
function addTaskComment(db, taskId, body, agentId) {
|
|
15061
|
+
const id = uuid();
|
|
15062
|
+
db.run("INSERT INTO task_comments (id, task_id, agent_id, body) VALUES (?, ?, ?, ?)", [id, taskId, agentId ?? null, body]);
|
|
15063
|
+
const row = db.query("SELECT * FROM task_comments WHERE id = ?").get(id);
|
|
15064
|
+
return parseTaskComment(row);
|
|
15065
|
+
}
|
|
15066
|
+
function parseTaskComment(row) {
|
|
15067
|
+
return {
|
|
15068
|
+
id: row.id,
|
|
15069
|
+
task_id: row.task_id,
|
|
15070
|
+
agent_id: row.agent_id ?? null,
|
|
15071
|
+
body: row.body,
|
|
15072
|
+
created_at: row.created_at
|
|
15073
|
+
};
|
|
15074
|
+
}
|
|
15075
|
+
function listTaskComments(db, taskId) {
|
|
15076
|
+
const rows = db.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at ASC").all(taskId);
|
|
15077
|
+
return { comments: rows.map(parseTaskComment), count: rows.length };
|
|
15078
|
+
}
|
|
15079
|
+
function deleteTaskComment(db, id) {
|
|
15080
|
+
const result = db.run("DELETE FROM task_comments WHERE id = ?", id);
|
|
15081
|
+
return result.changes > 0;
|
|
15082
|
+
}
|
|
15083
|
+
function getTaskStats(db, filter) {
|
|
15084
|
+
let where = "WHERE 1=1";
|
|
15085
|
+
const params = [];
|
|
15086
|
+
if (filter?.project_id) {
|
|
15087
|
+
where += " AND project_id = ?";
|
|
15088
|
+
params.push(filter.project_id);
|
|
15089
|
+
}
|
|
15090
|
+
if (filter?.agent_id) {
|
|
15091
|
+
where += " AND assigned_agent_id = ?";
|
|
15092
|
+
params.push(filter.agent_id);
|
|
15093
|
+
}
|
|
15094
|
+
const total = db.query(`SELECT COUNT(*) as c FROM tasks ${where}`).get(...params).c;
|
|
15095
|
+
const byStatus = {};
|
|
15096
|
+
const statusRows = db.query(`SELECT status, COUNT(*) as c FROM tasks ${where} GROUP BY status`).all(...params);
|
|
15097
|
+
for (const row of statusRows)
|
|
15098
|
+
byStatus[row.status] = row.c;
|
|
15099
|
+
const byPriority = {};
|
|
15100
|
+
const priorityRows = db.query(`SELECT priority, COUNT(*) as c FROM tasks ${where} GROUP BY priority`).all(...params);
|
|
15101
|
+
for (const row of priorityRows)
|
|
15102
|
+
byPriority[row.priority] = row.c;
|
|
15103
|
+
const overdue = db.query(`SELECT COUNT(*) as c FROM tasks ${where} AND status != 'completed' AND status != 'cancelled' AND due_at IS NOT NULL AND due_at < datetime('now')`).get(...params).c;
|
|
15104
|
+
return {
|
|
15105
|
+
total,
|
|
15106
|
+
by_status: {
|
|
15107
|
+
pending: byStatus["pending"] ?? 0,
|
|
15108
|
+
in_progress: byStatus["in_progress"] ?? 0,
|
|
15109
|
+
completed: byStatus["completed"] ?? 0,
|
|
15110
|
+
failed: byStatus["failed"] ?? 0,
|
|
15111
|
+
cancelled: byStatus["cancelled"] ?? 0
|
|
15112
|
+
},
|
|
15113
|
+
by_priority: {
|
|
15114
|
+
critical: byPriority["critical"] ?? 0,
|
|
15115
|
+
high: byPriority["high"] ?? 0,
|
|
15116
|
+
medium: byPriority["medium"] ?? 0,
|
|
15117
|
+
low: byPriority["low"] ?? 0
|
|
15118
|
+
},
|
|
15119
|
+
overdue
|
|
15120
|
+
};
|
|
15121
|
+
}
|
|
15122
|
+
|
|
15123
|
+
// src/lib/task-runner.ts
|
|
15124
|
+
var _handlers = new Map;
|
|
15125
|
+
var _defaultHandler = null;
|
|
15126
|
+
function resolveHandler(task) {
|
|
15127
|
+
for (const tag of task.tags) {
|
|
15128
|
+
const h = _handlers.get(tag);
|
|
15129
|
+
if (h)
|
|
15130
|
+
return h;
|
|
15131
|
+
}
|
|
15132
|
+
const metaType = task.metadata?.["type"];
|
|
15133
|
+
if (metaType) {
|
|
15134
|
+
const h = _handlers.get(metaType);
|
|
15135
|
+
if (h)
|
|
15136
|
+
return h;
|
|
15137
|
+
}
|
|
15138
|
+
return _defaultHandler;
|
|
15139
|
+
}
|
|
15140
|
+
var _workerStarted2 = false;
|
|
15141
|
+
var _processing = false;
|
|
15142
|
+
var _totalProcessed = 0;
|
|
15143
|
+
var _pollIntervalMs = 60000;
|
|
15144
|
+
function startTaskRunner(intervalMs) {
|
|
15145
|
+
if (_workerStarted2)
|
|
15146
|
+
return;
|
|
15147
|
+
_workerStarted2 = true;
|
|
15148
|
+
if (intervalMs)
|
|
15149
|
+
_pollIntervalMs = intervalMs;
|
|
15150
|
+
console.log(`[task-runner] Started, polling every ${_pollIntervalMs / 1000}s`);
|
|
15151
|
+
_tick();
|
|
15152
|
+
setInterval(() => {
|
|
15153
|
+
_tick();
|
|
15154
|
+
}, _pollIntervalMs);
|
|
15155
|
+
}
|
|
15156
|
+
async function _tick() {
|
|
15157
|
+
if (_processing)
|
|
15158
|
+
return;
|
|
15159
|
+
let task = null;
|
|
15160
|
+
try {
|
|
15161
|
+
const db = getDatabase();
|
|
15162
|
+
const result = listTasks(db, { status: "pending", limit: 1 });
|
|
15163
|
+
if (result.tasks.length === 0)
|
|
15164
|
+
return;
|
|
15165
|
+
task = result.tasks[0] ?? null;
|
|
15166
|
+
} catch (e) {
|
|
15167
|
+
console.error("[task-runner] Failed to list pending tasks:", e);
|
|
15168
|
+
return;
|
|
15169
|
+
}
|
|
15170
|
+
if (!task)
|
|
15171
|
+
return;
|
|
15172
|
+
_processing = true;
|
|
15173
|
+
try {
|
|
15174
|
+
const db = getDatabase();
|
|
15175
|
+
updateTask(db, task.id, { status: "in_progress" });
|
|
15176
|
+
task = getTask(db, task.id);
|
|
15177
|
+
} catch (e) {
|
|
15178
|
+
console.error("[task-runner] Failed to claim task:", e);
|
|
15179
|
+
_processing = false;
|
|
15180
|
+
return;
|
|
15181
|
+
}
|
|
15182
|
+
const handler = resolveHandler(task);
|
|
15183
|
+
if (!handler) {
|
|
15184
|
+
console.warn(`[task-runner] No handler for task: ${task.subject} (tags: ${JSON.stringify(task.tags)}, meta.type: ${task.metadata?.["type"]})`);
|
|
15185
|
+
try {
|
|
15186
|
+
const db = getDatabase();
|
|
15187
|
+
updateTask(db, task.id, {
|
|
15188
|
+
status: "failed",
|
|
15189
|
+
error: "No handler registered for this task type"
|
|
15190
|
+
});
|
|
15191
|
+
} catch {}
|
|
15192
|
+
_processing = false;
|
|
15193
|
+
_totalProcessed++;
|
|
15194
|
+
return;
|
|
15195
|
+
}
|
|
15196
|
+
const taskId = task.id;
|
|
15197
|
+
let progress = 0;
|
|
15198
|
+
const ctx = {
|
|
15199
|
+
task,
|
|
15200
|
+
updateProgress: (p) => {
|
|
15201
|
+
progress = Math.min(1, Math.max(0, p));
|
|
15202
|
+
try {
|
|
15203
|
+
const db = getDatabase();
|
|
15204
|
+
updateTask(db, taskId, { progress });
|
|
15205
|
+
} catch {}
|
|
15206
|
+
},
|
|
15207
|
+
addComment: (body, agentId) => {
|
|
15208
|
+
try {
|
|
15209
|
+
const db = getDatabase();
|
|
15210
|
+
addTaskComment(db, taskId, body, agentId);
|
|
15211
|
+
} catch {}
|
|
15212
|
+
}
|
|
15213
|
+
};
|
|
15214
|
+
try {
|
|
15215
|
+
await handler(ctx);
|
|
15216
|
+
const db = getDatabase();
|
|
15217
|
+
updateTask(db, taskId, { status: "completed", progress: 1 });
|
|
15218
|
+
console.log(`[task-runner] Completed: ${task.subject}`);
|
|
15219
|
+
} catch (e) {
|
|
15220
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
15221
|
+
try {
|
|
15222
|
+
const db = getDatabase();
|
|
15223
|
+
updateTask(db, taskId, { status: "failed", error: errorMsg });
|
|
15224
|
+
} catch {}
|
|
15225
|
+
console.error(`[task-runner] Failed: ${task.subject} \u2014 ${errorMsg}`);
|
|
15226
|
+
} finally {
|
|
15227
|
+
_processing = false;
|
|
15228
|
+
_totalProcessed++;
|
|
15229
|
+
}
|
|
15230
|
+
}
|
|
15231
|
+
|
|
14844
15232
|
// src/server/router.ts
|
|
14845
15233
|
var routes = [];
|
|
14846
15234
|
var nextRouteOrder = 0;
|
|
@@ -14917,8 +15305,13 @@ function errorResponse(message, status, details) {
|
|
|
14917
15305
|
body["details"] = details;
|
|
14918
15306
|
return json(body, status);
|
|
14919
15307
|
}
|
|
15308
|
+
var MAX_BODY_BYTES = 1 * 1024 * 1024;
|
|
14920
15309
|
async function readJson(req) {
|
|
14921
15310
|
try {
|
|
15311
|
+
const contentLength = req.headers.get("content-length");
|
|
15312
|
+
if (contentLength && Number(contentLength) > MAX_BODY_BYTES) {
|
|
15313
|
+
throw Object.assign(new Error("Payload too large"), { status: 413 });
|
|
15314
|
+
}
|
|
14922
15315
|
return await req.json();
|
|
14923
15316
|
} catch {
|
|
14924
15317
|
return null;
|
|
@@ -15057,6 +15450,8 @@ addRoute("GET", "/api/memories", (_req, url) => {
|
|
|
15057
15450
|
filter.project_id = q["project_id"];
|
|
15058
15451
|
if (q["session_id"])
|
|
15059
15452
|
filter.session_id = q["session_id"];
|
|
15453
|
+
if (q["namespace"])
|
|
15454
|
+
filter.namespace = q["namespace"];
|
|
15060
15455
|
if (q["status"])
|
|
15061
15456
|
filter.status = q["status"];
|
|
15062
15457
|
if (q["limit"])
|
|
@@ -15316,6 +15711,10 @@ addRoute("POST", "/api/memories/search", async (req) => {
|
|
|
15316
15711
|
filter.category = body["category"];
|
|
15317
15712
|
if (body["tags"])
|
|
15318
15713
|
filter.tags = body["tags"];
|
|
15714
|
+
if (body["session_id"])
|
|
15715
|
+
filter.session_id = body["session_id"];
|
|
15716
|
+
if (body["namespace"])
|
|
15717
|
+
filter.namespace = body["namespace"];
|
|
15319
15718
|
if (body["limit"])
|
|
15320
15719
|
filter.limit = body["limit"];
|
|
15321
15720
|
const results = searchMemories(body["query"], filter);
|
|
@@ -15337,6 +15736,10 @@ addRoute("POST", "/api/memories/search/hybrid", async (req) => {
|
|
|
15337
15736
|
filter.agent_id = body["agent_id"];
|
|
15338
15737
|
if (body["project_id"])
|
|
15339
15738
|
filter.project_id = body["project_id"];
|
|
15739
|
+
if (body["session_id"])
|
|
15740
|
+
filter.session_id = body["session_id"];
|
|
15741
|
+
if (body["namespace"])
|
|
15742
|
+
filter.namespace = body["namespace"];
|
|
15340
15743
|
const results = await hybridSearch(body["query"], {
|
|
15341
15744
|
filter,
|
|
15342
15745
|
semantic_threshold: body["semantic_threshold"] ?? undefined,
|
|
@@ -15360,6 +15763,10 @@ addRoute("POST", "/api/memories/search/bm25", async (req) => {
|
|
|
15360
15763
|
filter.agent_id = body["agent_id"];
|
|
15361
15764
|
if (body["project_id"])
|
|
15362
15765
|
filter.project_id = body["project_id"];
|
|
15766
|
+
if (body["session_id"])
|
|
15767
|
+
filter.session_id = body["session_id"];
|
|
15768
|
+
if (body["namespace"])
|
|
15769
|
+
filter.namespace = body["namespace"];
|
|
15363
15770
|
if (body["limit"])
|
|
15364
15771
|
filter.limit = body["limit"];
|
|
15365
15772
|
const results = searchWithBm25(body["query"], filter);
|
|
@@ -16248,6 +16655,81 @@ addRoute("GET", "/api/graph/:entityId", (_req, url, params) => {
|
|
|
16248
16655
|
}
|
|
16249
16656
|
});
|
|
16250
16657
|
|
|
16658
|
+
// src/server/routes/tasks.ts
|
|
16659
|
+
init_database();
|
|
16660
|
+
addRoute("POST", "/api/tasks", async (req, _url) => {
|
|
16661
|
+
const body = await readJson(req);
|
|
16662
|
+
if (!body)
|
|
16663
|
+
return errorResponse("Invalid JSON body", 400);
|
|
16664
|
+
if (!body.subject)
|
|
16665
|
+
return errorResponse("subject is required", 400);
|
|
16666
|
+
const task = createTask(getDatabase(), body);
|
|
16667
|
+
return json(task, 201);
|
|
16668
|
+
});
|
|
16669
|
+
addRoute("GET", "/api/tasks", async (_req, url) => {
|
|
16670
|
+
const q = getSearchParams(url);
|
|
16671
|
+
const filter = {};
|
|
16672
|
+
for (const key of ["status", "priority", "assigned_agent_id", "project_id", "session_id", "parent_task_id"]) {
|
|
16673
|
+
if (q[key])
|
|
16674
|
+
filter[key] = q[key];
|
|
16675
|
+
}
|
|
16676
|
+
if (q["tags"])
|
|
16677
|
+
filter.tags = q["tags"].split(",");
|
|
16678
|
+
if (q["limit"])
|
|
16679
|
+
filter.limit = parseInt(q["limit"]);
|
|
16680
|
+
if (q["offset"])
|
|
16681
|
+
filter.offset = parseInt(q["offset"]);
|
|
16682
|
+
return json(listTasks(getDatabase(), filter));
|
|
16683
|
+
});
|
|
16684
|
+
addRoute("GET", "/api/tasks/stats", async (_req, url) => {
|
|
16685
|
+
const q = getSearchParams(url);
|
|
16686
|
+
const filter = {};
|
|
16687
|
+
if (q["project_id"])
|
|
16688
|
+
filter.project_id = q["project_id"];
|
|
16689
|
+
if (q["agent_id"])
|
|
16690
|
+
filter.agent_id = q["agent_id"];
|
|
16691
|
+
return json(getTaskStats(getDatabase(), filter));
|
|
16692
|
+
});
|
|
16693
|
+
addRoute("GET", "/api/tasks/:id", async (_req, _url, params) => {
|
|
16694
|
+
const task = getTask(getDatabase(), params.id);
|
|
16695
|
+
if (!task)
|
|
16696
|
+
return errorResponse("Task not found", 404);
|
|
16697
|
+
return json(task);
|
|
16698
|
+
});
|
|
16699
|
+
addRoute("PATCH", "/api/tasks/:id", async (req, _url, params) => {
|
|
16700
|
+
const body = await readJson(req);
|
|
16701
|
+
if (!body)
|
|
16702
|
+
return errorResponse("Invalid JSON body", 400);
|
|
16703
|
+
const task = updateTask(getDatabase(), params.id, body);
|
|
16704
|
+
if (!task)
|
|
16705
|
+
return errorResponse("Task not found", 404);
|
|
16706
|
+
return json(task);
|
|
16707
|
+
});
|
|
16708
|
+
addRoute("DELETE", "/api/tasks/:id", async (_req, _url, params) => {
|
|
16709
|
+
const deleted = deleteTask(getDatabase(), params.id);
|
|
16710
|
+
if (!deleted)
|
|
16711
|
+
return errorResponse("Task not found", 404);
|
|
16712
|
+
return json({ deleted: true });
|
|
16713
|
+
});
|
|
16714
|
+
addRoute("GET", "/api/tasks/:id/comments", async (_req, _url, params) => {
|
|
16715
|
+
return json(listTaskComments(getDatabase(), params.id));
|
|
16716
|
+
});
|
|
16717
|
+
addRoute("POST", "/api/tasks/:id/comments", async (req, _url, params) => {
|
|
16718
|
+
const body = await readJson(req);
|
|
16719
|
+
if (!body)
|
|
16720
|
+
return errorResponse("Invalid JSON body", 400);
|
|
16721
|
+
if (!body.body)
|
|
16722
|
+
return errorResponse("body is required", 400);
|
|
16723
|
+
const comment = addTaskComment(getDatabase(), params.id, body.body, body.agent_id);
|
|
16724
|
+
return json(comment, 201);
|
|
16725
|
+
});
|
|
16726
|
+
addRoute("DELETE", "/api/tasks/:id/comments/:commentId", async (_req, _url, params) => {
|
|
16727
|
+
const deleted = deleteTaskComment(getDatabase(), params.commentId);
|
|
16728
|
+
if (!deleted)
|
|
16729
|
+
return errorResponse("Comment not found", 404);
|
|
16730
|
+
return json({ deleted: true });
|
|
16731
|
+
});
|
|
16732
|
+
|
|
16251
16733
|
// src/server/routes/system-auto-memory.ts
|
|
16252
16734
|
init_auto_memory();
|
|
16253
16735
|
init_registry();
|
|
@@ -16295,14 +16777,18 @@ function registerSystemAutoMemoryRoutes() {
|
|
|
16295
16777
|
const provider = providerName ? providerRegistry.getProvider(providerName) : providerRegistry.getAvailable();
|
|
16296
16778
|
if (!provider)
|
|
16297
16779
|
return errorResponse("No LLM provider configured. Set an API key (ANTHROPIC_API_KEY, OPENAI_API_KEY, CEREBRAS_API_KEY, or XAI_API_KEY).", 503);
|
|
16298
|
-
|
|
16299
|
-
|
|
16300
|
-
|
|
16301
|
-
|
|
16302
|
-
|
|
16303
|
-
|
|
16304
|
-
|
|
16305
|
-
|
|
16780
|
+
try {
|
|
16781
|
+
const memories = await provider.extractMemories(turn, { agentId: agent_id, projectId: project_id });
|
|
16782
|
+
return json({
|
|
16783
|
+
provider: provider.name,
|
|
16784
|
+
model: provider.config.model,
|
|
16785
|
+
extracted: memories,
|
|
16786
|
+
count: memories.length,
|
|
16787
|
+
note: "DRY RUN \u2014 nothing was saved"
|
|
16788
|
+
});
|
|
16789
|
+
} catch (e) {
|
|
16790
|
+
return json({ error: e instanceof Error ? e.message : String(e) }, 500);
|
|
16791
|
+
}
|
|
16306
16792
|
});
|
|
16307
16793
|
}
|
|
16308
16794
|
|
|
@@ -17284,14 +17770,18 @@ init_profile_synthesizer();
|
|
|
17284
17770
|
function registerSystemSynthesisRoutes() {
|
|
17285
17771
|
addRoute("POST", "/api/synthesis/run", async (req) => {
|
|
17286
17772
|
const body = await readJson(req) ?? {};
|
|
17287
|
-
|
|
17288
|
-
|
|
17289
|
-
|
|
17290
|
-
|
|
17291
|
-
|
|
17292
|
-
|
|
17293
|
-
|
|
17294
|
-
|
|
17773
|
+
try {
|
|
17774
|
+
const result = await runSynthesis({
|
|
17775
|
+
projectId: body.project_id,
|
|
17776
|
+
agentId: body.agent_id,
|
|
17777
|
+
dryRun: body.dry_run,
|
|
17778
|
+
maxProposals: body.max_proposals,
|
|
17779
|
+
provider: body.provider
|
|
17780
|
+
});
|
|
17781
|
+
return json(result, result.dryRun ? 200 : 201);
|
|
17782
|
+
} catch (e) {
|
|
17783
|
+
return json({ error: e instanceof Error ? e.message : String(e) }, 500);
|
|
17784
|
+
}
|
|
17295
17785
|
});
|
|
17296
17786
|
addRoute("GET", "/api/synthesis/runs", (_req, url) => {
|
|
17297
17787
|
const projectId = url.searchParams.get("project_id") ?? undefined;
|
|
@@ -17305,20 +17795,28 @@ function registerSystemSynthesisRoutes() {
|
|
|
17305
17795
|
return json(getSynthesisStatus(runId, projectId));
|
|
17306
17796
|
});
|
|
17307
17797
|
addRoute("POST", "/api/synthesis/rollback/:run_id", async (_req, _url, params) => {
|
|
17308
|
-
|
|
17309
|
-
|
|
17798
|
+
try {
|
|
17799
|
+
const result = await rollbackSynthesis(params["run_id"]);
|
|
17800
|
+
return json(result);
|
|
17801
|
+
} catch (e) {
|
|
17802
|
+
return json({ error: e instanceof Error ? e.message : String(e) }, 500);
|
|
17803
|
+
}
|
|
17310
17804
|
});
|
|
17311
17805
|
addRoute("GET", "/api/profile/synthesize", async (_req, url) => {
|
|
17312
17806
|
const q = getSearchParams(url);
|
|
17313
|
-
|
|
17314
|
-
|
|
17315
|
-
|
|
17316
|
-
|
|
17317
|
-
|
|
17318
|
-
|
|
17319
|
-
|
|
17807
|
+
try {
|
|
17808
|
+
const result = await synthesizeProfile({
|
|
17809
|
+
project_id: q["project_id"] || undefined,
|
|
17810
|
+
agent_id: q["agent_id"] || undefined,
|
|
17811
|
+
force_refresh: q["force_refresh"] === "true"
|
|
17812
|
+
});
|
|
17813
|
+
if (!result) {
|
|
17814
|
+
return json({ profile: null, message: "No preference/fact memories found to synthesize" });
|
|
17815
|
+
}
|
|
17816
|
+
return json(result);
|
|
17817
|
+
} catch (e) {
|
|
17818
|
+
return json({ error: e instanceof Error ? e.message : String(e) }, 500);
|
|
17320
17819
|
}
|
|
17321
|
-
return json(result);
|
|
17322
17820
|
});
|
|
17323
17821
|
}
|
|
17324
17822
|
|
|
@@ -17582,126 +18080,116 @@ function initServer() {
|
|
|
17582
18080
|
warnIfPrimaryMachineUnset();
|
|
17583
18081
|
loadWebhooksFromDb();
|
|
17584
18082
|
startSessionQueueWorker();
|
|
18083
|
+
startTaskRunner();
|
|
17585
18084
|
}
|
|
17586
|
-
function startServer(port
|
|
17587
|
-
const maxRetries = 100;
|
|
18085
|
+
function startServer(port) {
|
|
17588
18086
|
initServer();
|
|
17589
18087
|
const hostname2 = process.env["MEMENTOS_HOST"] ?? "127.0.0.1";
|
|
17590
|
-
|
|
17591
|
-
|
|
17592
|
-
|
|
17593
|
-
|
|
17594
|
-
|
|
17595
|
-
|
|
17596
|
-
|
|
17597
|
-
|
|
17598
|
-
|
|
17599
|
-
|
|
17600
|
-
|
|
17601
|
-
|
|
17602
|
-
|
|
17603
|
-
|
|
17604
|
-
|
|
17605
|
-
|
|
17606
|
-
|
|
17607
|
-
|
|
17608
|
-
|
|
17609
|
-
|
|
17610
|
-
|
|
17611
|
-
|
|
17612
|
-
|
|
17613
|
-
|
|
17614
|
-
|
|
17615
|
-
|
|
17616
|
-
|
|
17617
|
-
|
|
17618
|
-
|
|
17619
|
-
|
|
17620
|
-
|
|
17621
|
-
|
|
17622
|
-
|
|
17623
|
-
|
|
17624
|
-
|
|
17625
|
-
|
|
17626
|
-
|
|
17627
|
-
|
|
17628
|
-
|
|
17629
|
-
|
|
17630
|
-
|
|
17631
|
-
|
|
17632
|
-
|
|
17633
|
-
|
|
17634
|
-
|
|
18088
|
+
Bun.serve({
|
|
18089
|
+
port,
|
|
18090
|
+
hostname: hostname2,
|
|
18091
|
+
maxRequestBodySize: 1048576,
|
|
18092
|
+
async fetch(req) {
|
|
18093
|
+
const url = new URL(req.url);
|
|
18094
|
+
const { pathname } = url;
|
|
18095
|
+
if (req.method === "OPTIONS") {
|
|
18096
|
+
const origin = req.headers.get("origin");
|
|
18097
|
+
const allowedOrigin = process.env["MEMENTOS_CORS_ORIGIN"] ?? "http://localhost:19428";
|
|
18098
|
+
if (origin && origin !== allowedOrigin) {
|
|
18099
|
+
return new Response(null, { status: 403 });
|
|
18100
|
+
}
|
|
18101
|
+
return new Response(null, { status: 204, headers: getCorsHeaders(req) });
|
|
18102
|
+
}
|
|
18103
|
+
if (pathname === "/api/health" || pathname === "/health") {
|
|
18104
|
+
const profile = getActiveProfile();
|
|
18105
|
+
const { createRequire: createRequire2 } = await import("module");
|
|
18106
|
+
const req2 = createRequire2(import.meta.url);
|
|
18107
|
+
const pkg = req2("../../package.json");
|
|
18108
|
+
const db = getDatabase();
|
|
18109
|
+
const total = db.query("SELECT COUNT(*) as c FROM memories WHERE status = 'active'").get().c;
|
|
18110
|
+
const expired = db.query("SELECT COUNT(*) as c FROM memories WHERE status = 'expired' OR (expires_at IS NOT NULL AND expires_at < datetime('now'))").get().c;
|
|
18111
|
+
const pinned = db.query("SELECT COUNT(*) as c FROM memories WHERE status = 'active' AND pinned = 1").get().c;
|
|
18112
|
+
const agents = db.query("SELECT COUNT(*) as c FROM agents").get().c;
|
|
18113
|
+
const projects = db.query("SELECT COUNT(*) as c FROM projects").get().c;
|
|
18114
|
+
const status = expired > 50 ? "warn" : "ok";
|
|
18115
|
+
return json({ status, version: pkg.version, profile: profile ?? "default", db_path: getDbPath(), hostname: hostname2, memories: { total, expired, pinned }, agents, projects });
|
|
18116
|
+
}
|
|
18117
|
+
if (pathname.startsWith("/api/") && pathname !== "/api/health") {
|
|
18118
|
+
const authError = authenticateRequest(req);
|
|
18119
|
+
if (authError)
|
|
18120
|
+
return authError;
|
|
18121
|
+
}
|
|
18122
|
+
if (pathname === "/api/profile" && req.method === "GET") {
|
|
18123
|
+
const profile = getActiveProfile();
|
|
18124
|
+
return json({ active: profile ?? null, profiles: listProfiles(), db_path: getDbPath() });
|
|
18125
|
+
}
|
|
18126
|
+
if (pathname === "/api/memories/stream" && req.method === "GET") {
|
|
18127
|
+
const stream = new ReadableStream({
|
|
18128
|
+
start(controller) {
|
|
18129
|
+
const encoder = new TextEncoder;
|
|
18130
|
+
let lastSeen = new Date().toISOString();
|
|
18131
|
+
const send = (data) => {
|
|
18132
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
17635
18133
|
|
|
17636
18134
|
`));
|
|
17637
|
-
|
|
17638
|
-
|
|
17639
|
-
|
|
17640
|
-
|
|
17641
|
-
|
|
17642
|
-
|
|
17643
|
-
|
|
17644
|
-
|
|
17645
|
-
|
|
17646
|
-
|
|
17647
|
-
|
|
17648
|
-
|
|
17649
|
-
|
|
17650
|
-
|
|
17651
|
-
|
|
17652
|
-
|
|
17653
|
-
}
|
|
17654
|
-
});
|
|
17655
|
-
return new Response(stream, {
|
|
17656
|
-
headers: {
|
|
17657
|
-
"Content-Type": "text/event-stream",
|
|
17658
|
-
"Cache-Control": "no-cache",
|
|
17659
|
-
Connection: "keep-alive",
|
|
17660
|
-
...CORS_HEADERS
|
|
17661
|
-
}
|
|
17662
|
-
});
|
|
17663
|
-
}
|
|
17664
|
-
const matched = matchRoute(req.method, pathname);
|
|
17665
|
-
if (!matched) {
|
|
17666
|
-
if (pathname.startsWith("/api/")) {
|
|
17667
|
-
return errorResponse("Not found", 404);
|
|
18135
|
+
};
|
|
18136
|
+
send({ type: "connected", timestamp: lastSeen });
|
|
18137
|
+
const interval = setInterval(() => {
|
|
18138
|
+
try {
|
|
18139
|
+
const db = getDatabase();
|
|
18140
|
+
const rows = db.query("SELECT * FROM memories WHERE updated_at > ? OR created_at > ? ORDER BY updated_at DESC LIMIT 50").all(lastSeen, lastSeen);
|
|
18141
|
+
if (rows.length > 0) {
|
|
18142
|
+
lastSeen = new Date().toISOString();
|
|
18143
|
+
send({ type: "memories", data: rows, count: rows.length });
|
|
18144
|
+
}
|
|
18145
|
+
} catch {}
|
|
18146
|
+
}, 1000);
|
|
18147
|
+
req.signal.addEventListener("abort", () => {
|
|
18148
|
+
clearInterval(interval);
|
|
18149
|
+
controller.close();
|
|
18150
|
+
});
|
|
17668
18151
|
}
|
|
17669
|
-
|
|
17670
|
-
|
|
17671
|
-
|
|
17672
|
-
|
|
17673
|
-
|
|
17674
|
-
|
|
17675
|
-
|
|
17676
|
-
if (staticRes)
|
|
17677
|
-
return staticRes;
|
|
17678
|
-
}
|
|
17679
|
-
}
|
|
17680
|
-
const indexRes = serveStaticFile(join8(dashDir, "index.html"));
|
|
17681
|
-
if (indexRes)
|
|
17682
|
-
return indexRes;
|
|
18152
|
+
});
|
|
18153
|
+
return new Response(stream, {
|
|
18154
|
+
headers: {
|
|
18155
|
+
"Content-Type": "text/event-stream",
|
|
18156
|
+
"Cache-Control": "no-cache",
|
|
18157
|
+
Connection: "keep-alive",
|
|
18158
|
+
...CORS_HEADERS
|
|
17683
18159
|
}
|
|
18160
|
+
});
|
|
18161
|
+
}
|
|
18162
|
+
const matched = matchRoute(req.method, pathname);
|
|
18163
|
+
if (!matched) {
|
|
18164
|
+
if (pathname.startsWith("/api/")) {
|
|
17684
18165
|
return errorResponse("Not found", 404);
|
|
17685
18166
|
}
|
|
17686
|
-
|
|
17687
|
-
|
|
17688
|
-
|
|
17689
|
-
|
|
17690
|
-
|
|
18167
|
+
const dashDir = resolveDashboardDir();
|
|
18168
|
+
if (existsSync7(dashDir) && (req.method === "GET" || req.method === "HEAD")) {
|
|
18169
|
+
if (pathname !== "/") {
|
|
18170
|
+
const resolvedDash = resolve3(dashDir) + sep;
|
|
18171
|
+
const requestedPath = resolve3(join8(dashDir, pathname));
|
|
18172
|
+
if (requestedPath.startsWith(resolvedDash)) {
|
|
18173
|
+
const staticRes = serveStaticFile(requestedPath);
|
|
18174
|
+
if (staticRes)
|
|
18175
|
+
return staticRes;
|
|
18176
|
+
}
|
|
18177
|
+
}
|
|
18178
|
+
const indexRes = serveStaticFile(join8(dashDir, "index.html"));
|
|
18179
|
+
if (indexRes)
|
|
18180
|
+
return indexRes;
|
|
17691
18181
|
}
|
|
18182
|
+
return errorResponse("Not found", 404);
|
|
18183
|
+
}
|
|
18184
|
+
try {
|
|
18185
|
+
return await matched.handler(req, url, matched.params);
|
|
18186
|
+
} catch (e) {
|
|
18187
|
+
console.error(`[mementos-serve] ${req.method} ${pathname}:`, e);
|
|
18188
|
+
return errorResponse("Internal server error", 500);
|
|
17692
18189
|
}
|
|
17693
|
-
});
|
|
17694
|
-
console.log(`Mementos server listening on http://${hostname2}:${port}`);
|
|
17695
|
-
} catch (e) {
|
|
17696
|
-
const err = e;
|
|
17697
|
-
if (err.code === "EADDRINUSE" && attempt < maxRetries) {
|
|
17698
|
-
const nextPort = port + attempt + 1;
|
|
17699
|
-
console.log(`Port ${port} in use, trying ${nextPort}`);
|
|
17700
|
-
startServer(nextPort, attempt + 1);
|
|
17701
|
-
} else {
|
|
17702
|
-
throw e;
|
|
17703
18190
|
}
|
|
17704
|
-
}
|
|
18191
|
+
});
|
|
18192
|
+
console.log(`Mementos server listening on http://${hostname2}:${port}`);
|
|
17705
18193
|
}
|
|
17706
18194
|
async function main() {
|
|
17707
18195
|
if (hasFlag("--help", "-h")) {
|