chore: fork with local patches for Syslog Solution
Patches applied: - enforceRelayTTL — auto-deletes expired relay nodes (>7d) - updateNode — tenant enforcement (block updates on NULL-namespace nodes) - buildCanonicalMetadata — reject nested objects, sanitize values - createNode — source-based and title-based deduplication - getContext — NPE-safe count queries, stale node detection - repair_log integration on updateNode - bridge-validation.js — auto-fill defaults for tenant/namespace Origin: github.com/bradwmorris/ra-h_os@70e95e7
This commit is contained in:
@@ -21,7 +21,10 @@ function getEdges(filters = {}) {
|
||||
|
||||
const rows = query(sql, params);
|
||||
|
||||
return rows.map(formatEdgeRow);
|
||||
return rows.map(row => ({
|
||||
...row,
|
||||
context: parseContext(row.context)
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,7 +34,11 @@ function getEdgeById(id) {
|
||||
const rows = query('SELECT * FROM edges WHERE id = ?', [id]);
|
||||
if (rows.length === 0) return null;
|
||||
|
||||
return formatEdgeRow(rows[0]);
|
||||
const row = rows[0];
|
||||
return {
|
||||
...row,
|
||||
context: parseContext(row.context)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +59,21 @@ function createEdge(edgeData) {
|
||||
throw new Error('Edge explanation is required');
|
||||
}
|
||||
|
||||
const cleanExplanation = explanation.trim();
|
||||
// P1 — Dedup check before edge creation
|
||||
const existing = db.prepare(
|
||||
`SELECT id FROM edges
|
||||
WHERE from_node_id = ?
|
||||
AND to_node_id = ?
|
||||
AND (explanation = ? OR json_extract(context, '$.explanation') = ?)`
|
||||
).get(from_node_id, to_node_id, explanation.trim(), explanation.trim());
|
||||
|
||||
if (existing) {
|
||||
return {
|
||||
id: existing.id,
|
||||
status: 'existing',
|
||||
warning: 'duplicate_edge_skipped'
|
||||
};
|
||||
}
|
||||
|
||||
// Simple context without AI inference
|
||||
// The main app can re-infer types when it loads
|
||||
@@ -60,13 +81,13 @@ function createEdge(edgeData) {
|
||||
type: 'related_to',
|
||||
confidence: 0.5,
|
||||
inferred_at: now,
|
||||
explanation: cleanExplanation,
|
||||
explanation: explanation.trim(),
|
||||
created_via: 'mcp'
|
||||
};
|
||||
|
||||
const stmt = db.prepare(`
|
||||
INSERT INTO edges (from_node_id, to_node_id, context, source, created_at, explanation)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO edges (from_node_id, to_node_id, context, source, created_at)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
const result = runWithBusyRetry(() => stmt.run(
|
||||
@@ -74,13 +95,18 @@ function createEdge(edgeData) {
|
||||
to_node_id,
|
||||
JSON.stringify(context),
|
||||
source,
|
||||
now,
|
||||
cleanExplanation
|
||||
now
|
||||
),
|
||||
'createEdge'
|
||||
);
|
||||
|
||||
// P0 — After writing edge to DB, sync explanation column
|
||||
const edgeId = Number(result.lastInsertRowid);
|
||||
if (context.explanation) {
|
||||
const syncStmt = db.prepare('UPDATE edges SET explanation = ? WHERE id = ?');
|
||||
runWithBusyRetry(() => syncStmt.run(context.explanation, edgeId), 'createEdge_explanation_sync');
|
||||
}
|
||||
|
||||
return getEdgeById(edgeId);
|
||||
}
|
||||
|
||||
@@ -96,36 +122,29 @@ function updateEdge(id, updates) {
|
||||
throw new Error(`Edge with ID ${id} not found. Use rah_query_edges to find edges by node ID.`);
|
||||
}
|
||||
|
||||
const cleanExplanation = typeof explanation === 'string' ? explanation.trim() : '';
|
||||
|
||||
// If explanation changed, update both the normal field and the compatibility copy.
|
||||
if (cleanExplanation) {
|
||||
// If explanation changed, update context
|
||||
if (explanation && explanation.trim()) {
|
||||
const now = new Date().toISOString();
|
||||
const newContext = {
|
||||
...existing.context,
|
||||
explanation: cleanExplanation,
|
||||
explanation: explanation.trim(),
|
||||
inferred_at: now,
|
||||
created_via: 'mcp'
|
||||
};
|
||||
|
||||
const stmt = db.prepare('UPDATE edges SET context = ?, explanation = ? WHERE id = ?');
|
||||
runWithBusyRetry(() => stmt.run(JSON.stringify(newContext), cleanExplanation, id), 'updateEdge');
|
||||
const stmt = db.prepare('UPDATE edges SET context = ? WHERE id = ?');
|
||||
runWithBusyRetry(() => stmt.run(JSON.stringify(newContext), id), 'updateEdge');
|
||||
|
||||
// P0 — After updating edge, sync explanation column
|
||||
const syncStmt = db.prepare('UPDATE edges SET explanation = ? WHERE id = ?');
|
||||
runWithBusyRetry(() => syncStmt.run(explanation.trim(), id), 'updateEdge_explanation_sync');
|
||||
} else if (contextUpdates) {
|
||||
const contextExplanation = typeof contextUpdates.explanation === 'string'
|
||||
? contextUpdates.explanation.trim()
|
||||
: '';
|
||||
const newContext = {
|
||||
...existing.context,
|
||||
...contextUpdates,
|
||||
...(contextExplanation ? { explanation: contextExplanation } : {})
|
||||
...contextUpdates
|
||||
};
|
||||
if (contextExplanation) {
|
||||
const stmt = db.prepare('UPDATE edges SET context = ?, explanation = ? WHERE id = ?');
|
||||
runWithBusyRetry(() => stmt.run(JSON.stringify(newContext), contextExplanation, id), 'updateEdge');
|
||||
} else {
|
||||
const stmt = db.prepare('UPDATE edges SET context = ? WHERE id = ?');
|
||||
runWithBusyRetry(() => stmt.run(JSON.stringify(newContext), id), 'updateEdge');
|
||||
}
|
||||
const stmt = db.prepare('UPDATE edges SET context = ? WHERE id = ?');
|
||||
runWithBusyRetry(() => stmt.run(JSON.stringify(newContext), id), 'updateEdge');
|
||||
}
|
||||
|
||||
return getEdgeById(id);
|
||||
@@ -211,15 +230,6 @@ function getEdgeCount() {
|
||||
return Number(rows[0].count);
|
||||
}
|
||||
|
||||
function formatEdgeRow(row) {
|
||||
const context = parseContext(row.context);
|
||||
return {
|
||||
...row,
|
||||
context,
|
||||
explanation: row.explanation || context?.explanation || null
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse context JSON safely.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user