import { Router } from 'express'; import { asyncWrap } from '../errors.js'; import { makeActionService } from '../../actions/service.js'; import { loadActions } from '../../actions/registry.js'; import * as aa from '../../db/repos/agent_actions.js'; // Owner OR an agent with capabilities.act (Little Blue) may run/list. Approve/reject // are owner-only. The service enforces tier-gating regardless of caller. function svc() { return makeActionService({ registry: loadActions(process.env.ACTIONS_CONFIG || undefined) }); } function canAct(req) { const a = req.actor; return a?.kind === 'user' || (a?.kind === 'agent' && a.capabilities?.act); } function ownerOnly(req, res, next) { if (req.actor?.kind === 'user') return next(); return res.status(403).json({ error: { code: 'owner_only', message: 'owner approval required' } }); } export const router = Router(); router.get('/', asyncWrap(async (req, res) => { if (!canAct(req)) return res.status(403).json({ error: { code: 'forbidden' } }); res.json({ actions: svc().list() }); })); router.post('/:id/run', asyncWrap(async (req, res) => { if (!canAct(req)) return res.status(403).json({ error: { code: 'forbidden' } }); const agent_id = req.actor?.kind === 'agent' ? req.actor.id : null; res.json(await svc().run(req.params.id, req.actor, agent_id)); })); router.get('/pending', ownerOnly, asyncWrap(async (_req, res) => { res.json({ pending: await aa.listPending() }); })); router.post('/pending/:rowId/approve', ownerOnly, asyncWrap(async (req, res) => { res.json(await svc().approve(req.params.rowId, req.actor)); })); router.post('/pending/:rowId/reject', ownerOnly, asyncWrap(async (req, res) => { res.json(await svc().reject(req.params.rowId, req.actor)); })); router.get('/recent', ownerOnly, asyncWrap(async (_req, res) => { res.json({ recent: await aa.recent() }); }));