@hasna/mementos 0.14.18 → 0.14.19
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 +473 -30
- package/dist/mcp/index.js +64 -31
- package/dist/sdk/index.js +397 -0
- package/dist/server/index.js +649 -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,371 @@ 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
|
+
return parseTask(row);
|
|
14913
|
+
}
|
|
14914
|
+
function getTask(db, id) {
|
|
14915
|
+
const row = db.query("SELECT * FROM tasks WHERE id = ?").get(id);
|
|
14916
|
+
return row ? parseTask(row) : null;
|
|
14917
|
+
}
|
|
14918
|
+
function listTasks(db, filter) {
|
|
14919
|
+
let sql = "SELECT * FROM tasks WHERE 1=1";
|
|
14920
|
+
let countSql = "SELECT COUNT(*) as c FROM tasks WHERE 1=1";
|
|
14921
|
+
const params = [];
|
|
14922
|
+
const countParams = [];
|
|
14923
|
+
if (filter?.status) {
|
|
14924
|
+
sql += " AND status = ?";
|
|
14925
|
+
countSql += " AND status = ?";
|
|
14926
|
+
params.push(filter.status);
|
|
14927
|
+
countParams.push(filter.status);
|
|
14928
|
+
}
|
|
14929
|
+
if (filter?.priority) {
|
|
14930
|
+
sql += " AND priority = ?";
|
|
14931
|
+
countSql += " AND priority = ?";
|
|
14932
|
+
params.push(filter.priority);
|
|
14933
|
+
countParams.push(filter.priority);
|
|
14934
|
+
}
|
|
14935
|
+
if (filter?.assigned_agent_id) {
|
|
14936
|
+
sql += " AND assigned_agent_id = ?";
|
|
14937
|
+
countSql += " AND assigned_agent_id = ?";
|
|
14938
|
+
params.push(filter.assigned_agent_id);
|
|
14939
|
+
countParams.push(filter.assigned_agent_id);
|
|
14940
|
+
}
|
|
14941
|
+
if (filter?.project_id) {
|
|
14942
|
+
sql += " AND project_id = ?";
|
|
14943
|
+
countSql += " AND project_id = ?";
|
|
14944
|
+
params.push(filter.project_id);
|
|
14945
|
+
countParams.push(filter.project_id);
|
|
14946
|
+
}
|
|
14947
|
+
if (filter?.session_id) {
|
|
14948
|
+
sql += " AND session_id = ?";
|
|
14949
|
+
countSql += " AND session_id = ?";
|
|
14950
|
+
params.push(filter.session_id);
|
|
14951
|
+
countParams.push(filter.session_id);
|
|
14952
|
+
}
|
|
14953
|
+
if (filter?.parent_task_id !== undefined) {
|
|
14954
|
+
if (filter.parent_task_id === null) {
|
|
14955
|
+
sql += " AND parent_task_id IS NULL";
|
|
14956
|
+
countSql += " AND parent_task_id IS NULL";
|
|
14957
|
+
} else {
|
|
14958
|
+
sql += " AND parent_task_id = ?";
|
|
14959
|
+
countSql += " AND parent_task_id = ?";
|
|
14960
|
+
params.push(filter.parent_task_id);
|
|
14961
|
+
countParams.push(filter.parent_task_id);
|
|
14962
|
+
}
|
|
14963
|
+
}
|
|
14964
|
+
if (filter?.tags?.length) {
|
|
14965
|
+
const placeholders = filter.tags.map(() => "?").join(", ");
|
|
14966
|
+
sql += ` AND EXISTS (SELECT 1 FROM json_each(tags) WHERE value IN (${placeholders}))`;
|
|
14967
|
+
countSql += ` AND EXISTS (SELECT 1 FROM json_each(tags) WHERE value IN (${placeholders}))`;
|
|
14968
|
+
params.push(...filter.tags);
|
|
14969
|
+
countParams.push(...filter.tags);
|
|
14970
|
+
}
|
|
14971
|
+
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";
|
|
14972
|
+
if (filter?.limit !== undefined) {
|
|
14973
|
+
sql += " LIMIT ?";
|
|
14974
|
+
params.push(filter.limit);
|
|
14975
|
+
}
|
|
14976
|
+
if (filter?.offset !== undefined) {
|
|
14977
|
+
sql += " OFFSET ?";
|
|
14978
|
+
params.push(filter.offset);
|
|
14979
|
+
}
|
|
14980
|
+
const rows = db.query(sql).all(...params);
|
|
14981
|
+
const countRow = db.query(countSql).get(...countParams);
|
|
14982
|
+
return { tasks: rows.map(parseTask), count: countRow.c };
|
|
14983
|
+
}
|
|
14984
|
+
function updateTask(db, id, input) {
|
|
14985
|
+
const existing = getTask(db, id);
|
|
14986
|
+
if (!existing)
|
|
14987
|
+
return null;
|
|
14988
|
+
const updates = [];
|
|
14989
|
+
const params = [];
|
|
14990
|
+
const ts = now();
|
|
14991
|
+
if (input.subject !== undefined) {
|
|
14992
|
+
updates.push("subject = ?");
|
|
14993
|
+
params.push(input.subject);
|
|
14994
|
+
}
|
|
14995
|
+
if (input.description !== undefined) {
|
|
14996
|
+
updates.push("description = ?");
|
|
14997
|
+
params.push(input.description);
|
|
14998
|
+
}
|
|
14999
|
+
if (input.status !== undefined) {
|
|
15000
|
+
updates.push("status = ?");
|
|
15001
|
+
params.push(input.status);
|
|
15002
|
+
if (input.status === "in_progress" && !existing.started_at) {
|
|
15003
|
+
updates.push("started_at = ?");
|
|
15004
|
+
params.push(ts);
|
|
15005
|
+
}
|
|
15006
|
+
if (input.status === "completed") {
|
|
15007
|
+
updates.push("completed_at = ?");
|
|
15008
|
+
params.push(ts);
|
|
15009
|
+
updates.push("progress = ?");
|
|
15010
|
+
params.push(1);
|
|
15011
|
+
}
|
|
15012
|
+
if (input.status === "failed") {
|
|
15013
|
+
updates.push("failed_at = ?");
|
|
15014
|
+
params.push(ts);
|
|
15015
|
+
}
|
|
15016
|
+
}
|
|
15017
|
+
if (input.priority !== undefined) {
|
|
15018
|
+
updates.push("priority = ?");
|
|
15019
|
+
params.push(input.priority);
|
|
15020
|
+
}
|
|
15021
|
+
if (input.tags !== undefined) {
|
|
15022
|
+
updates.push("tags = ?");
|
|
15023
|
+
params.push(JSON.stringify(input.tags));
|
|
15024
|
+
}
|
|
15025
|
+
if (input.assigned_agent_id !== undefined) {
|
|
15026
|
+
updates.push("assigned_agent_id = ?");
|
|
15027
|
+
params.push(input.assigned_agent_id);
|
|
15028
|
+
}
|
|
15029
|
+
if (input.metadata !== undefined) {
|
|
15030
|
+
updates.push("metadata = ?");
|
|
15031
|
+
params.push(JSON.stringify(input.metadata));
|
|
15032
|
+
}
|
|
15033
|
+
if (input.progress !== undefined) {
|
|
15034
|
+
updates.push("progress = ?");
|
|
15035
|
+
params.push(input.progress);
|
|
15036
|
+
}
|
|
15037
|
+
if (input.due_at !== undefined) {
|
|
15038
|
+
updates.push("due_at = ?");
|
|
15039
|
+
params.push(input.due_at);
|
|
15040
|
+
}
|
|
15041
|
+
if (input.error !== undefined) {
|
|
15042
|
+
updates.push("error = ?");
|
|
15043
|
+
params.push(input.error);
|
|
15044
|
+
}
|
|
15045
|
+
if (updates.length === 0)
|
|
15046
|
+
return existing;
|
|
15047
|
+
updates.push("updated_at = ?");
|
|
15048
|
+
params.push(ts);
|
|
15049
|
+
params.push(id);
|
|
15050
|
+
db.run(`UPDATE tasks SET ${updates.join(", ")} WHERE id = ?`, ...params);
|
|
15051
|
+
return getTask(db, id);
|
|
15052
|
+
}
|
|
15053
|
+
function deleteTask(db, id) {
|
|
15054
|
+
const result = db.run("DELETE FROM tasks WHERE id = ?", id);
|
|
15055
|
+
return result.changes > 0;
|
|
15056
|
+
}
|
|
15057
|
+
function addTaskComment(db, taskId, body, agentId) {
|
|
15058
|
+
const id = uuid();
|
|
15059
|
+
db.run("INSERT INTO task_comments (id, task_id, agent_id, body) VALUES (?, ?, ?, ?)", [id, taskId, agentId ?? null, body]);
|
|
15060
|
+
const row = db.query("SELECT * FROM task_comments WHERE id = ?").get(id);
|
|
15061
|
+
return parseTaskComment(row);
|
|
15062
|
+
}
|
|
15063
|
+
function parseTaskComment(row) {
|
|
15064
|
+
return {
|
|
15065
|
+
id: row.id,
|
|
15066
|
+
task_id: row.task_id,
|
|
15067
|
+
agent_id: row.agent_id ?? null,
|
|
15068
|
+
body: row.body,
|
|
15069
|
+
created_at: row.created_at
|
|
15070
|
+
};
|
|
15071
|
+
}
|
|
15072
|
+
function listTaskComments(db, taskId) {
|
|
15073
|
+
const rows = db.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at ASC").all(taskId);
|
|
15074
|
+
return { comments: rows.map(parseTaskComment), count: rows.length };
|
|
15075
|
+
}
|
|
15076
|
+
function deleteTaskComment(db, id) {
|
|
15077
|
+
const result = db.run("DELETE FROM task_comments WHERE id = ?", id);
|
|
15078
|
+
return result.changes > 0;
|
|
15079
|
+
}
|
|
15080
|
+
function getTaskStats(db, filter) {
|
|
15081
|
+
let where = "WHERE 1=1";
|
|
15082
|
+
const params = [];
|
|
15083
|
+
if (filter?.project_id) {
|
|
15084
|
+
where += " AND project_id = ?";
|
|
15085
|
+
params.push(filter.project_id);
|
|
15086
|
+
}
|
|
15087
|
+
if (filter?.agent_id) {
|
|
15088
|
+
where += " AND assigned_agent_id = ?";
|
|
15089
|
+
params.push(filter.agent_id);
|
|
15090
|
+
}
|
|
15091
|
+
const total = db.query(`SELECT COUNT(*) as c FROM tasks ${where}`).get(...params).c;
|
|
15092
|
+
const byStatus = {};
|
|
15093
|
+
const statusRows = db.query(`SELECT status, COUNT(*) as c FROM tasks ${where} GROUP BY status`).all(...params);
|
|
15094
|
+
for (const row of statusRows)
|
|
15095
|
+
byStatus[row.status] = row.c;
|
|
15096
|
+
const byPriority = {};
|
|
15097
|
+
const priorityRows = db.query(`SELECT priority, COUNT(*) as c FROM tasks ${where} GROUP BY priority`).all(...params);
|
|
15098
|
+
for (const row of priorityRows)
|
|
15099
|
+
byPriority[row.priority] = row.c;
|
|
15100
|
+
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;
|
|
15101
|
+
return {
|
|
15102
|
+
total,
|
|
15103
|
+
by_status: {
|
|
15104
|
+
pending: byStatus["pending"] ?? 0,
|
|
15105
|
+
in_progress: byStatus["in_progress"] ?? 0,
|
|
15106
|
+
completed: byStatus["completed"] ?? 0,
|
|
15107
|
+
failed: byStatus["failed"] ?? 0,
|
|
15108
|
+
cancelled: byStatus["cancelled"] ?? 0
|
|
15109
|
+
},
|
|
15110
|
+
by_priority: {
|
|
15111
|
+
critical: byPriority["critical"] ?? 0,
|
|
15112
|
+
high: byPriority["high"] ?? 0,
|
|
15113
|
+
medium: byPriority["medium"] ?? 0,
|
|
15114
|
+
low: byPriority["low"] ?? 0
|
|
15115
|
+
},
|
|
15116
|
+
overdue
|
|
15117
|
+
};
|
|
15118
|
+
}
|
|
15119
|
+
|
|
15120
|
+
// src/lib/task-runner.ts
|
|
15121
|
+
var _handlers = new Map;
|
|
15122
|
+
var _defaultHandler = null;
|
|
15123
|
+
function resolveHandler(task) {
|
|
15124
|
+
for (const tag of task.tags) {
|
|
15125
|
+
const h = _handlers.get(tag);
|
|
15126
|
+
if (h)
|
|
15127
|
+
return h;
|
|
15128
|
+
}
|
|
15129
|
+
const metaType = task.metadata?.["type"];
|
|
15130
|
+
if (metaType) {
|
|
15131
|
+
const h = _handlers.get(metaType);
|
|
15132
|
+
if (h)
|
|
15133
|
+
return h;
|
|
15134
|
+
}
|
|
15135
|
+
return _defaultHandler;
|
|
15136
|
+
}
|
|
15137
|
+
var _workerStarted2 = false;
|
|
15138
|
+
var _processing = false;
|
|
15139
|
+
var _totalProcessed = 0;
|
|
15140
|
+
var _pollIntervalMs = 60000;
|
|
15141
|
+
function startTaskRunner(intervalMs) {
|
|
15142
|
+
if (_workerStarted2)
|
|
15143
|
+
return;
|
|
15144
|
+
_workerStarted2 = true;
|
|
15145
|
+
if (intervalMs)
|
|
15146
|
+
_pollIntervalMs = intervalMs;
|
|
15147
|
+
console.log(`[task-runner] Started, polling every ${_pollIntervalMs / 1000}s`);
|
|
15148
|
+
_tick();
|
|
15149
|
+
setInterval(() => {
|
|
15150
|
+
_tick();
|
|
15151
|
+
}, _pollIntervalMs);
|
|
15152
|
+
}
|
|
15153
|
+
async function _tick() {
|
|
15154
|
+
if (_processing)
|
|
15155
|
+
return;
|
|
15156
|
+
let task = null;
|
|
15157
|
+
try {
|
|
15158
|
+
const db = getDatabase();
|
|
15159
|
+
const result = listTasks(db, { status: "pending", limit: 1 });
|
|
15160
|
+
if (result.tasks.length === 0)
|
|
15161
|
+
return;
|
|
15162
|
+
task = result.tasks[0] ?? null;
|
|
15163
|
+
} catch (e) {
|
|
15164
|
+
console.error("[task-runner] Failed to list pending tasks:", e);
|
|
15165
|
+
return;
|
|
15166
|
+
}
|
|
15167
|
+
if (!task)
|
|
15168
|
+
return;
|
|
15169
|
+
_processing = true;
|
|
15170
|
+
try {
|
|
15171
|
+
const db = getDatabase();
|
|
15172
|
+
updateTask(db, task.id, { status: "in_progress" });
|
|
15173
|
+
task = getTask(db, task.id);
|
|
15174
|
+
} catch (e) {
|
|
15175
|
+
console.error("[task-runner] Failed to claim task:", e);
|
|
15176
|
+
_processing = false;
|
|
15177
|
+
return;
|
|
15178
|
+
}
|
|
15179
|
+
const handler = resolveHandler(task);
|
|
15180
|
+
if (!handler) {
|
|
15181
|
+
console.warn(`[task-runner] No handler for task: ${task.subject} (tags: ${JSON.stringify(task.tags)}, meta.type: ${task.metadata?.["type"]})`);
|
|
15182
|
+
try {
|
|
15183
|
+
const db = getDatabase();
|
|
15184
|
+
updateTask(db, task.id, {
|
|
15185
|
+
status: "failed",
|
|
15186
|
+
error: "No handler registered for this task type"
|
|
15187
|
+
});
|
|
15188
|
+
} catch {}
|
|
15189
|
+
_processing = false;
|
|
15190
|
+
_totalProcessed++;
|
|
15191
|
+
return;
|
|
15192
|
+
}
|
|
15193
|
+
const taskId = task.id;
|
|
15194
|
+
let progress = 0;
|
|
15195
|
+
const ctx = {
|
|
15196
|
+
task,
|
|
15197
|
+
updateProgress: (p) => {
|
|
15198
|
+
progress = Math.min(1, Math.max(0, p));
|
|
15199
|
+
try {
|
|
15200
|
+
const db = getDatabase();
|
|
15201
|
+
updateTask(db, taskId, { progress });
|
|
15202
|
+
} catch {}
|
|
15203
|
+
},
|
|
15204
|
+
addComment: (body, agentId) => {
|
|
15205
|
+
try {
|
|
15206
|
+
const db = getDatabase();
|
|
15207
|
+
addTaskComment(db, taskId, body, agentId);
|
|
15208
|
+
} catch {}
|
|
15209
|
+
}
|
|
15210
|
+
};
|
|
15211
|
+
try {
|
|
15212
|
+
await handler(ctx);
|
|
15213
|
+
const db = getDatabase();
|
|
15214
|
+
updateTask(db, taskId, { status: "completed", progress: 1 });
|
|
15215
|
+
console.log(`[task-runner] Completed: ${task.subject}`);
|
|
15216
|
+
} catch (e) {
|
|
15217
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
15218
|
+
try {
|
|
15219
|
+
const db = getDatabase();
|
|
15220
|
+
updateTask(db, taskId, { status: "failed", error: errorMsg });
|
|
15221
|
+
} catch {}
|
|
15222
|
+
console.error(`[task-runner] Failed: ${task.subject} \u2014 ${errorMsg}`);
|
|
15223
|
+
} finally {
|
|
15224
|
+
_processing = false;
|
|
15225
|
+
_totalProcessed++;
|
|
15226
|
+
}
|
|
15227
|
+
}
|
|
15228
|
+
|
|
14844
15229
|
// src/server/router.ts
|
|
14845
15230
|
var routes = [];
|
|
14846
15231
|
var nextRouteOrder = 0;
|
|
@@ -14917,8 +15302,13 @@ function errorResponse(message, status, details) {
|
|
|
14917
15302
|
body["details"] = details;
|
|
14918
15303
|
return json(body, status);
|
|
14919
15304
|
}
|
|
15305
|
+
var MAX_BODY_BYTES = 1 * 1024 * 1024;
|
|
14920
15306
|
async function readJson(req) {
|
|
14921
15307
|
try {
|
|
15308
|
+
const contentLength = req.headers.get("content-length");
|
|
15309
|
+
if (contentLength && Number(contentLength) > MAX_BODY_BYTES) {
|
|
15310
|
+
throw Object.assign(new Error("Payload too large"), { status: 413 });
|
|
15311
|
+
}
|
|
14922
15312
|
return await req.json();
|
|
14923
15313
|
} catch {
|
|
14924
15314
|
return null;
|
|
@@ -15057,6 +15447,8 @@ addRoute("GET", "/api/memories", (_req, url) => {
|
|
|
15057
15447
|
filter.project_id = q["project_id"];
|
|
15058
15448
|
if (q["session_id"])
|
|
15059
15449
|
filter.session_id = q["session_id"];
|
|
15450
|
+
if (q["namespace"])
|
|
15451
|
+
filter.namespace = q["namespace"];
|
|
15060
15452
|
if (q["status"])
|
|
15061
15453
|
filter.status = q["status"];
|
|
15062
15454
|
if (q["limit"])
|
|
@@ -15316,6 +15708,10 @@ addRoute("POST", "/api/memories/search", async (req) => {
|
|
|
15316
15708
|
filter.category = body["category"];
|
|
15317
15709
|
if (body["tags"])
|
|
15318
15710
|
filter.tags = body["tags"];
|
|
15711
|
+
if (body["session_id"])
|
|
15712
|
+
filter.session_id = body["session_id"];
|
|
15713
|
+
if (body["namespace"])
|
|
15714
|
+
filter.namespace = body["namespace"];
|
|
15319
15715
|
if (body["limit"])
|
|
15320
15716
|
filter.limit = body["limit"];
|
|
15321
15717
|
const results = searchMemories(body["query"], filter);
|
|
@@ -15337,6 +15733,10 @@ addRoute("POST", "/api/memories/search/hybrid", async (req) => {
|
|
|
15337
15733
|
filter.agent_id = body["agent_id"];
|
|
15338
15734
|
if (body["project_id"])
|
|
15339
15735
|
filter.project_id = body["project_id"];
|
|
15736
|
+
if (body["session_id"])
|
|
15737
|
+
filter.session_id = body["session_id"];
|
|
15738
|
+
if (body["namespace"])
|
|
15739
|
+
filter.namespace = body["namespace"];
|
|
15340
15740
|
const results = await hybridSearch(body["query"], {
|
|
15341
15741
|
filter,
|
|
15342
15742
|
semantic_threshold: body["semantic_threshold"] ?? undefined,
|
|
@@ -15360,6 +15760,10 @@ addRoute("POST", "/api/memories/search/bm25", async (req) => {
|
|
|
15360
15760
|
filter.agent_id = body["agent_id"];
|
|
15361
15761
|
if (body["project_id"])
|
|
15362
15762
|
filter.project_id = body["project_id"];
|
|
15763
|
+
if (body["session_id"])
|
|
15764
|
+
filter.session_id = body["session_id"];
|
|
15765
|
+
if (body["namespace"])
|
|
15766
|
+
filter.namespace = body["namespace"];
|
|
15363
15767
|
if (body["limit"])
|
|
15364
15768
|
filter.limit = body["limit"];
|
|
15365
15769
|
const results = searchWithBm25(body["query"], filter);
|
|
@@ -16248,6 +16652,81 @@ addRoute("GET", "/api/graph/:entityId", (_req, url, params) => {
|
|
|
16248
16652
|
}
|
|
16249
16653
|
});
|
|
16250
16654
|
|
|
16655
|
+
// src/server/routes/tasks.ts
|
|
16656
|
+
init_database();
|
|
16657
|
+
addRoute("POST", "/api/tasks", async (req, _url) => {
|
|
16658
|
+
const body = await readJson(req);
|
|
16659
|
+
if (!body)
|
|
16660
|
+
return errorResponse("Invalid JSON body", 400);
|
|
16661
|
+
if (!body.subject)
|
|
16662
|
+
return errorResponse("subject is required", 400);
|
|
16663
|
+
const task = createTask(getDatabase(), body);
|
|
16664
|
+
return json(task, 201);
|
|
16665
|
+
});
|
|
16666
|
+
addRoute("GET", "/api/tasks", async (_req, url) => {
|
|
16667
|
+
const q = getSearchParams(url);
|
|
16668
|
+
const filter = {};
|
|
16669
|
+
for (const key of ["status", "priority", "assigned_agent_id", "project_id", "session_id", "parent_task_id"]) {
|
|
16670
|
+
if (q[key])
|
|
16671
|
+
filter[key] = q[key];
|
|
16672
|
+
}
|
|
16673
|
+
if (q["tags"])
|
|
16674
|
+
filter.tags = q["tags"].split(",");
|
|
16675
|
+
if (q["limit"])
|
|
16676
|
+
filter.limit = parseInt(q["limit"]);
|
|
16677
|
+
if (q["offset"])
|
|
16678
|
+
filter.offset = parseInt(q["offset"]);
|
|
16679
|
+
return json(listTasks(getDatabase(), filter));
|
|
16680
|
+
});
|
|
16681
|
+
addRoute("GET", "/api/tasks/stats", async (_req, url) => {
|
|
16682
|
+
const q = getSearchParams(url);
|
|
16683
|
+
const filter = {};
|
|
16684
|
+
if (q["project_id"])
|
|
16685
|
+
filter.project_id = q["project_id"];
|
|
16686
|
+
if (q["agent_id"])
|
|
16687
|
+
filter.agent_id = q["agent_id"];
|
|
16688
|
+
return json(getTaskStats(getDatabase(), filter));
|
|
16689
|
+
});
|
|
16690
|
+
addRoute("GET", "/api/tasks/:id", async (_req, _url, params) => {
|
|
16691
|
+
const task = getTask(getDatabase(), params.id);
|
|
16692
|
+
if (!task)
|
|
16693
|
+
return errorResponse("Task not found", 404);
|
|
16694
|
+
return json(task);
|
|
16695
|
+
});
|
|
16696
|
+
addRoute("PATCH", "/api/tasks/:id", async (req, _url, params) => {
|
|
16697
|
+
const body = await readJson(req);
|
|
16698
|
+
if (!body)
|
|
16699
|
+
return errorResponse("Invalid JSON body", 400);
|
|
16700
|
+
const task = updateTask(getDatabase(), params.id, body);
|
|
16701
|
+
if (!task)
|
|
16702
|
+
return errorResponse("Task not found", 404);
|
|
16703
|
+
return json(task);
|
|
16704
|
+
});
|
|
16705
|
+
addRoute("DELETE", "/api/tasks/:id", async (_req, _url, params) => {
|
|
16706
|
+
const deleted = deleteTask(getDatabase(), params.id);
|
|
16707
|
+
if (!deleted)
|
|
16708
|
+
return errorResponse("Task not found", 404);
|
|
16709
|
+
return json({ deleted: true });
|
|
16710
|
+
});
|
|
16711
|
+
addRoute("GET", "/api/tasks/:id/comments", async (_req, _url, params) => {
|
|
16712
|
+
return json(listTaskComments(getDatabase(), params.id));
|
|
16713
|
+
});
|
|
16714
|
+
addRoute("POST", "/api/tasks/:id/comments", async (req, _url, params) => {
|
|
16715
|
+
const body = await readJson(req);
|
|
16716
|
+
if (!body)
|
|
16717
|
+
return errorResponse("Invalid JSON body", 400);
|
|
16718
|
+
if (!body.body)
|
|
16719
|
+
return errorResponse("body is required", 400);
|
|
16720
|
+
const comment = addTaskComment(getDatabase(), params.id, body.body, body.agent_id);
|
|
16721
|
+
return json(comment, 201);
|
|
16722
|
+
});
|
|
16723
|
+
addRoute("DELETE", "/api/tasks/:id/comments/:commentId", async (_req, _url, params) => {
|
|
16724
|
+
const deleted = deleteTaskComment(getDatabase(), params.commentId);
|
|
16725
|
+
if (!deleted)
|
|
16726
|
+
return errorResponse("Comment not found", 404);
|
|
16727
|
+
return json({ deleted: true });
|
|
16728
|
+
});
|
|
16729
|
+
|
|
16251
16730
|
// src/server/routes/system-auto-memory.ts
|
|
16252
16731
|
init_auto_memory();
|
|
16253
16732
|
init_registry();
|
|
@@ -16295,14 +16774,18 @@ function registerSystemAutoMemoryRoutes() {
|
|
|
16295
16774
|
const provider = providerName ? providerRegistry.getProvider(providerName) : providerRegistry.getAvailable();
|
|
16296
16775
|
if (!provider)
|
|
16297
16776
|
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
|
-
|
|
16777
|
+
try {
|
|
16778
|
+
const memories = await provider.extractMemories(turn, { agentId: agent_id, projectId: project_id });
|
|
16779
|
+
return json({
|
|
16780
|
+
provider: provider.name,
|
|
16781
|
+
model: provider.config.model,
|
|
16782
|
+
extracted: memories,
|
|
16783
|
+
count: memories.length,
|
|
16784
|
+
note: "DRY RUN \u2014 nothing was saved"
|
|
16785
|
+
});
|
|
16786
|
+
} catch (e) {
|
|
16787
|
+
return json({ error: e instanceof Error ? e.message : String(e) }, 500);
|
|
16788
|
+
}
|
|
16306
16789
|
});
|
|
16307
16790
|
}
|
|
16308
16791
|
|
|
@@ -17284,14 +17767,18 @@ init_profile_synthesizer();
|
|
|
17284
17767
|
function registerSystemSynthesisRoutes() {
|
|
17285
17768
|
addRoute("POST", "/api/synthesis/run", async (req) => {
|
|
17286
17769
|
const body = await readJson(req) ?? {};
|
|
17287
|
-
|
|
17288
|
-
|
|
17289
|
-
|
|
17290
|
-
|
|
17291
|
-
|
|
17292
|
-
|
|
17293
|
-
|
|
17294
|
-
|
|
17770
|
+
try {
|
|
17771
|
+
const result = await runSynthesis({
|
|
17772
|
+
projectId: body.project_id,
|
|
17773
|
+
agentId: body.agent_id,
|
|
17774
|
+
dryRun: body.dry_run,
|
|
17775
|
+
maxProposals: body.max_proposals,
|
|
17776
|
+
provider: body.provider
|
|
17777
|
+
});
|
|
17778
|
+
return json(result, result.dryRun ? 200 : 201);
|
|
17779
|
+
} catch (e) {
|
|
17780
|
+
return json({ error: e instanceof Error ? e.message : String(e) }, 500);
|
|
17781
|
+
}
|
|
17295
17782
|
});
|
|
17296
17783
|
addRoute("GET", "/api/synthesis/runs", (_req, url) => {
|
|
17297
17784
|
const projectId = url.searchParams.get("project_id") ?? undefined;
|
|
@@ -17305,20 +17792,28 @@ function registerSystemSynthesisRoutes() {
|
|
|
17305
17792
|
return json(getSynthesisStatus(runId, projectId));
|
|
17306
17793
|
});
|
|
17307
17794
|
addRoute("POST", "/api/synthesis/rollback/:run_id", async (_req, _url, params) => {
|
|
17308
|
-
|
|
17309
|
-
|
|
17795
|
+
try {
|
|
17796
|
+
const result = await rollbackSynthesis(params["run_id"]);
|
|
17797
|
+
return json(result);
|
|
17798
|
+
} catch (e) {
|
|
17799
|
+
return json({ error: e instanceof Error ? e.message : String(e) }, 500);
|
|
17800
|
+
}
|
|
17310
17801
|
});
|
|
17311
17802
|
addRoute("GET", "/api/profile/synthesize", async (_req, url) => {
|
|
17312
17803
|
const q = getSearchParams(url);
|
|
17313
|
-
|
|
17314
|
-
|
|
17315
|
-
|
|
17316
|
-
|
|
17317
|
-
|
|
17318
|
-
|
|
17319
|
-
|
|
17804
|
+
try {
|
|
17805
|
+
const result = await synthesizeProfile({
|
|
17806
|
+
project_id: q["project_id"] || undefined,
|
|
17807
|
+
agent_id: q["agent_id"] || undefined,
|
|
17808
|
+
force_refresh: q["force_refresh"] === "true"
|
|
17809
|
+
});
|
|
17810
|
+
if (!result) {
|
|
17811
|
+
return json({ profile: null, message: "No preference/fact memories found to synthesize" });
|
|
17812
|
+
}
|
|
17813
|
+
return json(result);
|
|
17814
|
+
} catch (e) {
|
|
17815
|
+
return json({ error: e instanceof Error ? e.message : String(e) }, 500);
|
|
17320
17816
|
}
|
|
17321
|
-
return json(result);
|
|
17322
17817
|
});
|
|
17323
17818
|
}
|
|
17324
17819
|
|
|
@@ -17582,126 +18077,116 @@ function initServer() {
|
|
|
17582
18077
|
warnIfPrimaryMachineUnset();
|
|
17583
18078
|
loadWebhooksFromDb();
|
|
17584
18079
|
startSessionQueueWorker();
|
|
18080
|
+
startTaskRunner();
|
|
17585
18081
|
}
|
|
17586
|
-
function startServer(port
|
|
17587
|
-
const maxRetries = 100;
|
|
18082
|
+
function startServer(port) {
|
|
17588
18083
|
initServer();
|
|
17589
18084
|
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
|
-
|
|
18085
|
+
Bun.serve({
|
|
18086
|
+
port,
|
|
18087
|
+
hostname: hostname2,
|
|
18088
|
+
maxRequestBodySize: 1048576,
|
|
18089
|
+
async fetch(req) {
|
|
18090
|
+
const url = new URL(req.url);
|
|
18091
|
+
const { pathname } = url;
|
|
18092
|
+
if (req.method === "OPTIONS") {
|
|
18093
|
+
const origin = req.headers.get("origin");
|
|
18094
|
+
const allowedOrigin = process.env["MEMENTOS_CORS_ORIGIN"] ?? "http://localhost:19428";
|
|
18095
|
+
if (origin && origin !== allowedOrigin) {
|
|
18096
|
+
return new Response(null, { status: 403 });
|
|
18097
|
+
}
|
|
18098
|
+
return new Response(null, { status: 204, headers: getCorsHeaders(req) });
|
|
18099
|
+
}
|
|
18100
|
+
if (pathname === "/api/health" || pathname === "/health") {
|
|
18101
|
+
const profile = getActiveProfile();
|
|
18102
|
+
const { createRequire: createRequire2 } = await import("module");
|
|
18103
|
+
const req2 = createRequire2(import.meta.url);
|
|
18104
|
+
const pkg = req2("../../package.json");
|
|
18105
|
+
const db = getDatabase();
|
|
18106
|
+
const total = db.query("SELECT COUNT(*) as c FROM memories WHERE status = 'active'").get().c;
|
|
18107
|
+
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;
|
|
18108
|
+
const pinned = db.query("SELECT COUNT(*) as c FROM memories WHERE status = 'active' AND pinned = 1").get().c;
|
|
18109
|
+
const agents = db.query("SELECT COUNT(*) as c FROM agents").get().c;
|
|
18110
|
+
const projects = db.query("SELECT COUNT(*) as c FROM projects").get().c;
|
|
18111
|
+
const status = expired > 50 ? "warn" : "ok";
|
|
18112
|
+
return json({ status, version: pkg.version, profile: profile ?? "default", db_path: getDbPath(), hostname: hostname2, memories: { total, expired, pinned }, agents, projects });
|
|
18113
|
+
}
|
|
18114
|
+
if (pathname.startsWith("/api/") && pathname !== "/api/health") {
|
|
18115
|
+
const authError = authenticateRequest(req);
|
|
18116
|
+
if (authError)
|
|
18117
|
+
return authError;
|
|
18118
|
+
}
|
|
18119
|
+
if (pathname === "/api/profile" && req.method === "GET") {
|
|
18120
|
+
const profile = getActiveProfile();
|
|
18121
|
+
return json({ active: profile ?? null, profiles: listProfiles(), db_path: getDbPath() });
|
|
18122
|
+
}
|
|
18123
|
+
if (pathname === "/api/memories/stream" && req.method === "GET") {
|
|
18124
|
+
const stream = new ReadableStream({
|
|
18125
|
+
start(controller) {
|
|
18126
|
+
const encoder = new TextEncoder;
|
|
18127
|
+
let lastSeen = new Date().toISOString();
|
|
18128
|
+
const send = (data) => {
|
|
18129
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
17635
18130
|
|
|
17636
18131
|
`));
|
|
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);
|
|
18132
|
+
};
|
|
18133
|
+
send({ type: "connected", timestamp: lastSeen });
|
|
18134
|
+
const interval = setInterval(() => {
|
|
18135
|
+
try {
|
|
18136
|
+
const db = getDatabase();
|
|
18137
|
+
const rows = db.query("SELECT * FROM memories WHERE updated_at > ? OR created_at > ? ORDER BY updated_at DESC LIMIT 50").all(lastSeen, lastSeen);
|
|
18138
|
+
if (rows.length > 0) {
|
|
18139
|
+
lastSeen = new Date().toISOString();
|
|
18140
|
+
send({ type: "memories", data: rows, count: rows.length });
|
|
18141
|
+
}
|
|
18142
|
+
} catch {}
|
|
18143
|
+
}, 1000);
|
|
18144
|
+
req.signal.addEventListener("abort", () => {
|
|
18145
|
+
clearInterval(interval);
|
|
18146
|
+
controller.close();
|
|
18147
|
+
});
|
|
17668
18148
|
}
|
|
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;
|
|
18149
|
+
});
|
|
18150
|
+
return new Response(stream, {
|
|
18151
|
+
headers: {
|
|
18152
|
+
"Content-Type": "text/event-stream",
|
|
18153
|
+
"Cache-Control": "no-cache",
|
|
18154
|
+
Connection: "keep-alive",
|
|
18155
|
+
...CORS_HEADERS
|
|
17683
18156
|
}
|
|
18157
|
+
});
|
|
18158
|
+
}
|
|
18159
|
+
const matched = matchRoute(req.method, pathname);
|
|
18160
|
+
if (!matched) {
|
|
18161
|
+
if (pathname.startsWith("/api/")) {
|
|
17684
18162
|
return errorResponse("Not found", 404);
|
|
17685
18163
|
}
|
|
17686
|
-
|
|
17687
|
-
|
|
17688
|
-
|
|
17689
|
-
|
|
17690
|
-
|
|
18164
|
+
const dashDir = resolveDashboardDir();
|
|
18165
|
+
if (existsSync7(dashDir) && (req.method === "GET" || req.method === "HEAD")) {
|
|
18166
|
+
if (pathname !== "/") {
|
|
18167
|
+
const resolvedDash = resolve3(dashDir) + sep;
|
|
18168
|
+
const requestedPath = resolve3(join8(dashDir, pathname));
|
|
18169
|
+
if (requestedPath.startsWith(resolvedDash)) {
|
|
18170
|
+
const staticRes = serveStaticFile(requestedPath);
|
|
18171
|
+
if (staticRes)
|
|
18172
|
+
return staticRes;
|
|
18173
|
+
}
|
|
18174
|
+
}
|
|
18175
|
+
const indexRes = serveStaticFile(join8(dashDir, "index.html"));
|
|
18176
|
+
if (indexRes)
|
|
18177
|
+
return indexRes;
|
|
17691
18178
|
}
|
|
18179
|
+
return errorResponse("Not found", 404);
|
|
18180
|
+
}
|
|
18181
|
+
try {
|
|
18182
|
+
return await matched.handler(req, url, matched.params);
|
|
18183
|
+
} catch (e) {
|
|
18184
|
+
console.error(`[mementos-serve] ${req.method} ${pathname}:`, e);
|
|
18185
|
+
return errorResponse("Internal server error", 500);
|
|
17692
18186
|
}
|
|
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
18187
|
}
|
|
17704
|
-
}
|
|
18188
|
+
});
|
|
18189
|
+
console.log(`Mementos server listening on http://${hostname2}:${port}`);
|
|
17705
18190
|
}
|
|
17706
18191
|
async function main() {
|
|
17707
18192
|
if (hasFlag("--help", "-h")) {
|