UNPKG

spaps

Version:

Sweet Potato Authentication & Payment Service CLI - Docker Compose orchestrator for local Python/FastAPI SPAPS server with built-in admin middleware

209 lines (197 loc) 11.4 kB
/** * Domain registry for SPAPS CLI. * * Each entry declares the routes a domain exposes and how the CLI should * surface them: tool-spec entries, doctor mount probe, and metadata flags. * * Shape: * { * key: 'dayrate', * label: 'Dayrate', * probe: { method: 'GET', path: '/api/dayrate/availability' }, * tools: [ * { name, description, method, path, * parameters?: JSONSchema, * admin_required?: boolean, // D1 + D5 * agent_callable?: boolean } // D5, default true * ] * } * * Path prefixes reflect the live backend routers as of 2026-04-16. * Public = API key only. Admin = API key + JWT + admin role (admin_required:true). * The CLI does not invent schemas; when OpenAPI is available ai-tool-spec.js * enriches parameters/responses automatically. */ const DAYRATE = { key: 'dayrate', label: 'Dayrate', probe: { method: 'GET', path: '/api/dayrate/availability' }, tools: [ { name: 'dayrate_availability', method: 'GET', path: '/api/dayrate/availability', description: 'List bookable slots within the configured horizon with dynamic pricing.' }, { name: 'dayrate_book', method: 'POST', path: '/api/dayrate/book', description: 'Book a single slot. Returns FreeBookResponse when an entitled user has remaining allotment, otherwise a Stripe Checkout URL.' }, { name: 'dayrate_book_multi', method: 'POST', path: '/api/dayrate/book-multi', description: 'Book multiple slots in one Stripe session with running fill-rate pricing.' }, { name: 'dayrate_checkout_status', method: 'GET', path: '/api/dayrate/checkout-status', description: 'Poll a Stripe session to learn the resulting booking status.' }, { name: 'dayrate_cancel', method: 'POST', path: '/api/dayrate/cancel', description: 'Cancel an existing booking. Owner verified by user_id then email.' }, { name: 'dayrate_allotment', method: 'GET', path: '/api/dayrate/allotment', description: 'Inspect free allotment status for a policy in the rolling window.' }, { name: 'dayrate_admin_get_config', method: 'GET', path: '/api/dayrate/admin/config', description: 'Fetch dayrate pricing/availability config.', admin_required: true }, { name: 'dayrate_admin_update_config', method: 'PUT', path: '/api/dayrate/admin/config', description: 'Upsert dayrate config (rates, tiers, horizon, timezone, slot definitions).', admin_required: true }, { name: 'dayrate_admin_list_bookings', method: 'GET', path: '/api/dayrate/admin/bookings', description: 'Admin view of bookings with status/date/email/enrollment/user/is_free filters.', admin_required: true }, { name: 'dayrate_admin_list_policies', method: 'GET', path: '/api/dayrate/admin/policies', description: 'List active booking policies.', admin_required: true }, { name: 'dayrate_admin_create_policy', method: 'POST', path: '/api/dayrate/admin/policies', description: 'Create a booking policy (entitlement key, free allotment, overage rate, reschedule rules).', admin_required: true }, { name: 'dayrate_admin_update_policy', method: 'PUT', path: '/api/dayrate/admin/policies/{policy_id}', description: 'Partial update of a booking policy.', admin_required: true }, { name: 'dayrate_admin_delete_policy', method: 'DELETE', path: '/api/dayrate/admin/policies/{policy_id}', description: 'Delete a booking policy.', admin_required: true }, ], }; const EMAIL = { key: 'email', label: 'Email', probe: { method: 'GET', path: '/api/email/templates/__doctor__/preview', ok_on_404: true }, tools: [ { name: 'email_send', method: 'POST', path: '/api/email/send', description: 'Send a transactional email by template key. In local mode Mailgun calls are short-circuited.' }, { name: 'email_get_template', method: 'GET', path: '/api/email/templates/{template_key}', description: 'Fetch a template definition by key.' }, { name: 'email_preview_template', method: 'GET', path: '/api/email/templates/{template_key}/preview', description: 'Render a template with its sample context.' }, { name: 'email_preview_template_with_context', method: 'POST', path: '/api/email/templates/{template_key}/preview', description: 'Render a template with caller-provided context.' }, { name: 'email_list_logs', method: 'GET', path: '/api/email/logs', description: 'List email sends. Non-admin users are scoped to their own logs; admins can filter by user_id or owner_id.' }, { name: 'email_admin_list_templates', method: 'GET', path: '/api/email/templates', description: 'List all templates for the application.', admin_required: true }, { name: 'email_admin_create_template', method: 'POST', path: '/api/email/templates', description: 'Create a template (subject, html, text, variables, sample context).', admin_required: true }, { name: 'email_admin_update_template', method: 'PUT', path: '/api/email/templates/{template_key}', description: 'Update an existing template.', admin_required: true }, { name: 'email_admin_get_override', method: 'GET', path: '/api/email/templates/{template_key}/override', description: 'Get subject/body override for a template.' }, { name: 'email_admin_set_override', method: 'PUT', path: '/api/email/templates/{template_key}/override', description: 'Set subject/body override for a template.', admin_required: true }, { name: 'email_admin_clear_override', method: 'DELETE', path: '/api/email/templates/{template_key}/override', description: 'Clear subject/body override for a template.', admin_required: true }, ], }; const WEBHOOKS = { key: 'webhooks', label: 'Webhooks', probe: { method: 'GET', path: '/api/webhooks/events' }, tools: [ { name: 'webhook_list', method: 'GET', path: '/api/webhooks', description: 'List registered outbound webhooks.' }, { name: 'webhook_register', method: 'POST', path: '/api/webhooks', description: 'Register an outbound webhook. The create response returns a one-time signing_secret.' }, { name: 'webhook_update', method: 'PUT', path: '/api/webhooks/{webhook_id}', description: 'Update url, events, is_active, or headers on an existing webhook.' }, { name: 'webhook_delete', method: 'DELETE', path: '/api/webhooks/{webhook_id}', description: 'Remove a registered webhook.' }, { name: 'webhook_deliveries', method: 'GET', path: '/api/webhooks/{webhook_id}/deliveries', description: 'List delivery attempts for a webhook.' }, { name: 'webhook_events', method: 'GET', path: '/api/webhooks/events', description: 'List supported outbound event types.' }, { name: 'webhook_mailgun_inbound', method: 'POST', path: '/api/webhooks/mailgun/events', description: 'Mailgun inbound event receiver. Signature-verified; not callable from agents.', agent_callable: false }, ], }; const POLICIES = { key: 'policies', label: 'Policies', // /policies/evaluations is admin-gated; 401/403 still proves the router is mounted. probe: { method: 'GET', path: '/api/policies/evaluations', ok_on_auth_error: true }, tools: [ { name: 'policy_authorize', method: 'POST', path: '/api/policies/authorize', description: 'Evaluate a named policy against a user/context. Returns allow/deny with reasons.' }, { name: 'policy_admin_list', method: 'GET', path: '/api/policies', description: 'List policies (is_active filter).', admin_required: true }, { name: 'policy_admin_create', method: 'POST', path: '/api/policies', description: 'Create a policy (name, effect, conditions, priority, metadata).', admin_required: true }, { name: 'policy_admin_get', method: 'GET', path: '/api/policies/{policy_id}', description: 'Fetch a single policy.', admin_required: true }, { name: 'policy_admin_update', method: 'PUT', path: '/api/policies/{policy_id}', description: 'Partial update of a policy.', admin_required: true }, { name: 'policy_admin_delete', method: 'DELETE', path: '/api/policies/{policy_id}', description: 'Delete a policy.', admin_required: true }, { name: 'policy_admin_evaluations', method: 'GET', path: '/api/policies/evaluations', description: 'List policy evaluation audit logs.', admin_required: true }, ], }; const BILLING = { key: 'billing', label: 'Billing Accounts', // Admin-gated; 401/403 still proves the operator route is mounted. probe: { method: 'GET', path: '/api/admin/billing/status', ok_on_auth_error: true }, tools: [ { name: 'billing_status', method: 'GET', path: '/api/admin/billing/status', description: 'Show current application billing-account resolution and redacted Stripe diagnostics.', admin_required: true }, { name: 'billing_verify', method: 'GET', path: '/api/admin/billing/verify', description: 'Verify that the current application has usable Stripe secret and webhook configuration.', admin_required: true }, { name: 'billing_attach', method: 'POST', path: '/api/admin/billing/attach', description: 'Bind the current application to an existing active Stripe billing account.', admin_required: true }, ], }; const ISSUE_REPORTING = { key: 'issue_reporting', label: 'Issue Reporting', // /v1/issue-reports/status requires auth; any 401/403 still proves mounted. probe: { method: 'GET', path: '/api/v1/issue-reports/status', ok_on_auth_error: true }, tools: [ { name: 'issue_report_create', method: 'POST', path: '/api/v1/issue-reports', description: 'Create an issue report. Requires the issue_reporting capability.' }, { name: 'issue_report_list_mine', method: 'GET', path: '/api/v1/issue-reports', description: 'List the caller\u2019s issue reports.' }, { name: 'issue_report_status', method: 'GET', path: '/api/v1/issue-reports/status', description: 'Aggregate status for the caller\u2019s issues.' }, { name: 'issue_report_get', method: 'GET', path: '/api/v1/issue-reports/{issue_report_id}', description: 'Fetch a single issue report.' }, { name: 'issue_report_update_note', method: 'PATCH', path: '/api/v1/issue-reports/{issue_report_id}', description: 'Update the note on an existing report.' }, { name: 'issue_report_reply', method: 'POST', path: '/api/v1/issue-reports/{issue_report_id}/replies', description: 'Reply to an issue; creates a child report and reopens the linked support case.' }, ], }; const DOMAINS = [DAYRATE, EMAIL, WEBHOOKS, POLICIES, BILLING, ISSUE_REPORTING]; function listDomains() { return DOMAINS; } function findDomain(key) { return DOMAINS.find((d) => d.key === key) || null; } function buildDomainTools({ includeNonAgent = false } = {}) { const out = []; for (const domain of DOMAINS) { for (const t of domain.tools) { const agentCallable = t.agent_callable !== false; if (!agentCallable && !includeNonAgent) continue; out.push({ name: t.name, description: t.description, method: t.method, path: t.path, parameters: t.parameters || { type: 'object', properties: {} }, domain: domain.key, admin_required: Boolean(t.admin_required), agent_callable: agentCallable, }); } } return out; } module.exports = { DOMAINS, listDomains, findDomain, buildDomainTools, };