← back to Approval Command Center
server.js
232 lines
'use strict';
/*
* Approval Command Center — a button-pushing surface over the 105-item
* pending-approval digest (2026-08-09). Steve's click IS the human gate.
*
* Model (realizes option 1 from the 10:00 AM email):
* - Every draft is a GATED runbook memo. 81/105 are irreversible.
* - The unattended web server NEVER auto-fires an irreversible prod runbook.
* - Steve triages each item with a click:
* APPROVE -> records his approval + appends to execute-queue.jsonl
* (drained by a live officer-yolo session: DTD -> officer ->
* contrarian -> verify per item). Draft moves to approved/.
* REJECT -> moves to _rejected/ with a note.
* HOLD -> moves to _held/.
* - Reversible items are labeled green so Steve can safely fast-track them.
*
* Zero dependencies (Node http only). Basic Auth admin/DW2024!.
*/
const http = require('http');
const fs = require('fs');
const path = require('path');
const os = require('os');
const { execFile } = require('child_process');
const PORT = process.env.PORT || 9795;
const AUTH_USER = process.env.ACC_USER || 'admin';
const AUTH_PASS = process.env.ACC_PASS || 'DW2024!';
const HOME = os.homedir();
const Q = path.join(HOME, '.claude', 'yolo-queue');
const PENDING = path.join(Q, 'pending-approval');
const APPROVED = path.join(Q, 'approved');
const REJECTED = path.join(Q, '_rejected');
const HELD = path.join(Q, '_held');
const MANIFEST = path.join(Q, 'digest-manifest-2026-08-09.json');
const EXEC_QUEUE = path.join(Q, 'execute-queue.jsonl');
const ACTION_LOG = path.join(Q, 'command-center-actions.jsonl');
for (const d of [APPROVED, REJECTED, HELD]) {
try { fs.mkdirSync(d, { recursive: true }); } catch (e) { /* ignore */ }
}
// ---- gate severity classification -------------------------------------------
// Which gates the running server may NEVER fire itself even on Steve's click
// (his own standing rules: spend / bulk-send / irreversible / DNS confirm-first).
const HARD_GATES = new Set([
'destructive',
'DNS / domain',
'send (comms-law — NEVER auto)',
'dw_unified canonical',
]);
function severity(gate, reversible) {
if (!reversible || HARD_GATES.has(gate)) return 'hard'; // live officer-yolo only
return 'soft'; // reversible fast-track ok
}
// ---- data -------------------------------------------------------------------
// Auto-classify a .md file that isn't in the manifest yet.
function autoClassify(slug, body) {
const low = (body || '').toLowerCase();
// reversible heuristics
const reversible = /reversible|dry.?run|rollback|staged|undo|no.?prod.?write|read.?only/.test(low)
&& !/irreversible|destructive|cannot.?undo|permanent/.test(low);
// gate heuristics
let gate = 'deploy / prod write';
if (/dns|domain|nameserver|cloudflare zone/.test(low)) gate = 'DNS / domain';
else if (/spend|budget|stripe|charge|payment|paid api/.test(low)) gate = 'spend';
else if (/send.*(email|sms|push|list)|mail.*(blast|send)/.test(low)) gate = 'send (comms-law — NEVER auto)';
else if (/dw_unified.*canonical|canonical.*dw_unified/.test(low)) gate = 'dw_unified canonical';
else if (/shopify|publish|live store|customer.?facing/.test(low)) gate = 'deploy / prod write';
// officer heuristic from slug/body
const officer = /commerce|shopify|gmc|merchant|catalog/.test(low) ? 'vp-dw-commerce'
: /marketing|instagram|tiktok|social|seo/.test(low) ? 'vp-dw-marketing'
: /engineer|code|build|deploy|server/.test(low) ? 'vp-engineering'
: 'unassigned';
// title: first H1 or first line
const h1 = (body || '').match(/^#\s+(.+)/m);
const title = h1 ? h1[1].replace(/\*+/g, '').trim() : slug;
return { slug, title, officer, state: 'PENDING', signoff: '—', dtd: '—', gate, reversible };
}
function loadManifest() {
const m = JSON.parse(fs.readFileSync(MANIFEST, 'utf8'));
const manifestSlugs = new Set(m.drafts.map((d) => d.slug));
// Dynamically detect files in pending-approval/ not yet in the manifest.
let extra = [];
try {
const files = fs.readdirSync(PENDING).filter((f) => f.endsWith('.md'));
for (const f of files) {
const slug = f.slice(0, -3); // strip .md
if (!manifestSlugs.has(slug)) {
let body = '';
try { body = fs.readFileSync(path.join(PENDING, f), 'utf8'); } catch (e) { /* ignore */ }
extra.push(autoClassify(slug, body));
}
}
} catch (e) { /* ignore */ }
return [...m.drafts, ...extra].map((d) => {
const item = { ...d };
// find the backing .md across queue folders (state may have changed)
const candidates = [
[PENDING, 'pending'], [APPROVED, 'approved'],
[REJECTED, 'rejected'], [HELD, 'held'],
];
let body = '', where = 'missing', created = null, mdname = null;
for (const [dir, label] of candidates) {
const f = path.join(dir, d.slug + '.md');
if (fs.existsSync(f)) {
try {
body = fs.readFileSync(f, 'utf8');
const st = fs.statSync(f);
created = st.mtimeMs;
where = label; mdname = d.slug + '.md';
} catch (e) { /* ignore */ }
break;
}
}
item.body = body;
item.where = where;
item.created = created;
item.mdname = mdname;
item.severity = severity(d.gate, d.reversible);
return item;
});
}
function logAction(entry) {
try { fs.appendFileSync(ACTION_LOG, JSON.stringify(entry) + '\n'); } catch (e) { /* ignore */ }
}
function moveDraft(slug, destDir) {
const src = path.join(PENDING, slug + '.md');
const dst = path.join(destDir, slug + '.md');
if (fs.existsSync(src)) { fs.renameSync(src, dst); return true; }
// already moved from pending — try to relocate from wherever it is
for (const dir of [APPROVED, REJECTED, HELD]) {
const f = path.join(dir, slug + '.md');
if (fs.existsSync(f) && dir !== destDir) { fs.renameSync(f, dst); return true; }
}
return false;
}
function doAction(slug, action, note) {
const ts = new Date().toISOString();
const manifest = JSON.parse(fs.readFileSync(MANIFEST, 'utf8'));
const meta = manifest.drafts.find((d) => d.slug === slug) || { slug };
const rec = { ts, slug, action, note: note || '', gate: meta.gate, reversible: meta.reversible, by: 'steve', via: 'command-center' };
if (action === 'approve') {
moveDraft(slug, APPROVED);
// enqueue for the live officer-yolo session to execute with verification
fs.appendFileSync(EXEC_QUEUE, JSON.stringify({
...rec, status: 'approved-queued',
execution_path: meta.reversible ? 'reversible-fast-track' : 'officer-yolo-verified',
}) + '\n');
} else if (action === 'reject') {
moveDraft(slug, REJECTED);
} else if (action === 'hold') {
moveDraft(slug, HELD);
} else {
throw new Error('unknown action: ' + action);
}
logAction(rec);
return rec;
}
// ---- http -------------------------------------------------------------------
function unauthorized(res) {
res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Approval Command Center"' });
res.end('Auth required');
}
function checkAuth(req) {
const h = req.headers.authorization || '';
if (!h.startsWith('Basic ')) return false;
const [u, p] = Buffer.from(h.slice(6), 'base64').toString().split(':');
return u === AUTH_USER && p === AUTH_PASS;
}
function body(req) {
return new Promise((resolve) => {
let d = ''; req.on('data', (c) => (d += c)); req.on('end', () => resolve(d));
});
}
const server = http.createServer(async (req, res) => {
if (!checkAuth(req)) return unauthorized(res);
try {
if (req.method === 'GET' && (req.url === '/' || req.url === '/index.html')) {
const html = fs.readFileSync(path.join(__dirname, 'public', 'index.html'));
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
return res.end(html);
}
if (req.method === 'GET' && req.url === '/api/items') {
const items = loadManifest();
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({ count: items.length, items }));
}
if (req.method === 'POST' && req.url === '/api/action') {
const { slug, action, note } = JSON.parse((await body(req)) || '{}');
if (!slug || !action) { res.writeHead(400); return res.end('slug+action required'); }
const rec = doAction(slug, action, note);
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({ ok: true, rec }));
}
if (req.method === 'POST' && req.url === '/api/bulk') {
const { slugs, action, note } = JSON.parse((await body(req)) || '{}');
if (!Array.isArray(slugs) || !action) { res.writeHead(400); return res.end('slugs[]+action required'); }
const recs = slugs.map((s) => { try { return doAction(s, action, note); } catch (e) { return { slug: s, error: e.message }; } });
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({ ok: true, recs }));
}
// static assets under public/ (e.g. /nav-agent/nav-agent.js|css) — additive, path-traversal-guarded
if (req.method === 'GET') {
const MIME = { '.js': 'text/javascript', '.css': 'text/css', '.html': 'text/html', '.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg', '.svg': 'image/svg+xml', '.ico': 'image/x-icon', '.woff2': 'font/woff2' };
const pub = path.join(__dirname, 'public');
const urlPath = decodeURIComponent(req.url.split('?')[0]);
const fp = path.normalize(path.join(pub, urlPath));
if (fp.startsWith(pub + path.sep) && fs.existsSync(fp) && fs.statSync(fp).isFile()) {
res.writeHead(200, { 'Content-Type': MIME[path.extname(fp).toLowerCase()] || 'application/octet-stream' });
return res.end(fs.readFileSync(fp));
}
}
res.writeHead(404); res.end('not found');
} catch (e) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: e.message }));
}
});
server.listen(PORT, () => {
console.log(`Approval Command Center on http://127.0.0.1:${PORT} (auth ${AUTH_USER}/${AUTH_PASS})`);
});