From 3301dfa08140ae3342d497e02d143140fd1970cf Mon Sep 17 00:00:00 2001 From: OpSpawn Agent Date: Fri, 6 Feb 2026 09:24:19 +0000 Subject: [PATCH] KB viewer/editor: full CRUD UI, DELETE API, 41 tests Add knowledge base management to dashboard: clickable topic tags, inline content viewer, edit/create modal, delete with confirmation. New DELETE /api/knowledge/:topic endpoint and deleteKnowledge() function. Co-Authored-By: Claude Opus 4.6 --- dashboard.html | 131 +++++++++++++++++++++++++++++++++++++++++++++--- orchestrator.js | 9 ++++ server.js | 12 +++++ test.js | 5 ++ 4 files changed, 150 insertions(+), 7 deletions(-) diff --git a/dashboard.html b/dashboard.html index a371b4a..c186ec8 100644 --- a/dashboard.html +++ b/dashboard.html @@ -66,8 +66,16 @@ .event-agent { color: var(--purple); min-width: 60px; } .event-action { color: var(--accent); } - .kb-list { display: flex; flex-wrap: wrap; gap: 8px; } - .kb-tag { background: var(--border); color: var(--text); padding: 4px 12px; border-radius: 16px; font-size: 13px; } + .kb-list { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; } + .kb-tag { background: var(--border); color: var(--text); padding: 4px 12px; border-radius: 16px; font-size: 13px; cursor: pointer; border: 1px solid transparent; transition: border-color 0.2s; } + .kb-tag:hover { border-color: var(--accent); } + .kb-tag.active { border-color: var(--accent); background: rgba(88,166,255,0.15); } + .kb-viewer { margin-top: 8px; } + .kb-content { background: var(--bg); border: 1px solid var(--border); border-radius: 6px; padding: 16px; font-size: 13px; line-height: 1.7; white-space: pre-wrap; font-family: monospace; max-height: 400px; overflow-y: auto; } + .kb-content h1, .kb-content h2, .kb-content h3 { font-family: inherit; margin: 8px 0 4px; color: var(--accent); } + .kb-toolbar { display: flex; gap: 6px; margin-top: 8px; } + .kb-editor { width: 100%; min-height: 250px; background: var(--bg); color: var(--text); border: 1px solid var(--border); padding: 12px; border-radius: 6px; font-size: 13px; font-family: monospace; line-height: 1.6; resize: vertical; } + .kb-editor:focus { border-color: var(--accent); outline: none; } .lock-row { display: flex; align-items: center; gap: 8px; padding: 6px 0; font-size: 13px; border-bottom: 1px solid var(--border); } .lock-row:last-child { border-bottom: none; } @@ -184,10 +192,25 @@
Loading...
-
-
Knowledge Base
+
+
Knowledge Base
Loading...
+ + +
diff --git a/orchestrator.js b/orchestrator.js index 0749445..9faeb32 100644 --- a/orchestrator.js +++ b/orchestrator.js @@ -281,6 +281,14 @@ function readKnowledge(topic) { return fs.readFileSync(filePath, 'utf8'); } +function deleteKnowledge(topic, agentId = 'system') { + const filePath = path.join(KNOWLEDGE_DIR, `${topic}.md`); + if (!fs.existsSync(filePath)) return false; + fs.unlinkSync(filePath); + logEvent(agentId, 'knowledge_deleted', { topic }); + return true; +} + function listKnowledge() { return fs.readdirSync(KNOWLEDGE_DIR) .filter(f => f.endsWith('.md')) @@ -375,6 +383,7 @@ module.exports = { releaseLock, writeKnowledge, readKnowledge, + deleteKnowledge, listKnowledge, status, statusText diff --git a/server.js b/server.js index 665c7ef..9a4c517 100644 --- a/server.js +++ b/server.js @@ -71,6 +71,11 @@ const server = http.createServer(async (req, res) => { return res.end(html); } + if (pathname === '/favicon.ico') { + res.writeHead(204); + return res.end(); + } + // --- API: Status --- if (pathname === '/api/status' && method === 'GET') { return json(res, orc.status()); @@ -204,6 +209,13 @@ const server = http.createServer(async (req, res) => { return json(res, { ok: true }); } + if (kbMatch && method === 'DELETE') { + const topic = decodeURIComponent(kbMatch[1]); + const deleted = orc.deleteKnowledge(topic, 'dashboard'); + if (!deleted) return error(res, `Topic "${topic}" not found`, 404); + return json(res, { ok: true }); + } + // --- API: Locks --- if (pathname === '/api/locks' && method === 'GET') { const state = orc.loadState(); diff --git a/test.js b/test.js index d07342f..4c3fe00 100644 --- a/test.js +++ b/test.js @@ -126,6 +126,11 @@ test('Knowledge base', () => { const topics = orc.listKnowledge(); assert(topics.includes('test-topic'), 'Topic listed'); + + const deleted = orc.deleteKnowledge('test-topic'); + assert(deleted === true, 'Topic deleted'); + assert(orc.readKnowledge('test-topic') === null, 'Deleted topic returns null'); + assert(orc.deleteKnowledge('nonexistent') === false, 'Delete nonexistent returns false'); }); test('Event log', () => {