- Terminal renamed Eithan: mobile font A−/A+ (per-URL ttyd opts), same-origin xterm Copy/Paste buttons, scroll-to-live, touch-default 17px - Dross voice: no keyboard pop after transcribe (fine-pointer only focus), autogrow textarea to ~5 lines, live amplitude meter on the mic while recording - Dross improvements: propose_improvement tool (CSS layer, exfil-sanitized, owner-approved, per-improvement rollback/restore), public /improvements.css, Settings panel. External MCP registry unchanged (no tool leak). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
34 lines
1.4 KiB
JavaScript
34 lines
1.4 KiB
JavaScript
import { Router } from 'express';
|
|
import { asyncWrap } from '../errors.js';
|
|
import { requireOwner } from '../cap.js';
|
|
import * as repo from '../../db/repos/improvements.js';
|
|
import { recordAudit } from '../../db/repos/audit.js';
|
|
|
|
export const router = Router();
|
|
|
|
router.get('/', asyncWrap(async (_req, res) => res.json(await repo.list())));
|
|
|
|
router.get('/:id', asyncWrap(async (req, res) => {
|
|
const row = await repo.get(req.params.id);
|
|
if (!row) return res.status(404).json({ error: 'not_found' });
|
|
res.json(row);
|
|
}));
|
|
|
|
for (const verb of ['approve', 'rollback', 'restore', 'reject']) {
|
|
router.post(`/:id/${verb}`, requireOwner, asyncWrap(async (req, res) => {
|
|
const row = await repo.transition(req.params.id, verb, 'owner');
|
|
if (!row) return res.status(409).json({ error: 'invalid_transition' });
|
|
const auditAction = { approve: 'approve', reject: 'reject', rollback: 'update', restore: 'update' }[verb];
|
|
await recordAudit({ kind: 'user' }, auditAction, 'improvement', row.id, null, { verb, summary: row.summary });
|
|
res.json(row);
|
|
}));
|
|
}
|
|
|
|
// Public stylesheet of ACTIVE improvements. Unauthenticated by design: it carries
|
|
// no secrets (owner-approved, exfil-sanitized CSS only) and <link> can't send a
|
|
// bearer token. Mounted on the app root, outside the /api auth wall.
|
|
export async function cssHandler(_req, res) {
|
|
res.set({ 'Content-Type': 'text/css; charset=utf-8', 'Cache-Control': 'no-cache' });
|
|
res.send(await repo.activeCss());
|
|
}
|