Task #3: MVP API — payment-gated jobs + demo endpoint

OpenAPI spec (lib/api-spec/openapi.yaml)
- Added POST /jobs, GET /jobs/{id}, GET /demo endpoints
- Added schemas: CreateJobRequest, CreateJobResponse, JobStatusResponse,
  InvoiceInfo, JobState, DemoResponse, ErrorResponse
- Ran codegen: generated CreateJobBody, GetJobResponse, RunDemoQueryParams etc.

Jobs router (artifacts/api-server/src/routes/jobs.ts)
- POST /jobs: validates body, creates LNbits eval invoice, inserts job +
  invoice in a DB transaction, returns { jobId, evalInvoice }
- GET /jobs/🆔 fetches job, calls advanceJob() helper, returns state-
  appropriate payload (eval/work invoice, reason, result, errorMessage)
- advanceJob() state machine:
  - awaiting_eval_payment: checks LNbits, atomically marks paid + advances
    state via optimistic WHERE state='awaiting_eval_payment'; runs
    AgentService.evaluateRequest, branches to awaiting_work_payment or rejected
  - awaiting_work_payment: same pattern for work invoice, runs
    AgentService.executeWork, advances to complete
  - Any agent/LNbits error transitions job to failed

Demo router (artifacts/api-server/src/routes/demo.ts)
- GET /demo?request=...: in-memory rate limiter (5 req/hour per IP)
- Explicit guard for missing request param (coerce.string() workaround)
- Calls AgentService.executeWork directly, returns { result }

Dev router (artifacts/api-server/src/routes/dev.ts)
- POST /dev/stub/pay/:paymentHash: marks stub invoice paid in-memory
- Only mounted when NODE_ENV !== 'production'

Route index updated to mount all three routers

replit.md: documented full curl flow with all 6 steps, demo endpoint,
and dev stub-pay trigger

End-to-end verified with curl:
- Full flow: create → eval pay → evaluating → work pay → executing → complete
- Error cases: 400 on missing body/param, 404 on unknown job
This commit is contained in:
alexpaynex
2026-03-18 15:31:26 +00:00
parent 9ec5e20a10
commit 4e8adbcb93
19 changed files with 1050 additions and 6 deletions

View File

@@ -111,6 +111,50 @@ Generated Zod schemas from the OpenAPI spec (e.g. `HealthCheckResponse`). Used b
Generated React Query hooks and fetch client from the OpenAPI spec (e.g. `useHealthCheck`, `healthCheck`).
### `artifacts/api-server` — Timmy API endpoints
#### Payment-gated job flow
```bash
BASE="https://${REPLIT_DEV_DOMAIN}" # or http://localhost:8080 in dev
# 1. Create a job (returns eval invoice)
curl -s -X POST "$BASE/api/jobs" \
-H "Content-Type: application/json" \
-d '{"request": "Write a haiku about lightning payments"}'
# → {"jobId":"…","evalInvoice":{"paymentRequest":"lnbcrt10u1…","amountSats":10}}
# 2. Poll status (returns eval invoice while unpaid)
curl -s "$BASE/api/jobs/<jobId>"
# 3. (Stub mode only) Mark eval invoice paid
curl -s -X POST "$BASE/api/dev/stub/pay/<paymentHash>"
# 4. Poll again — auto-advances to awaiting_work_payment, returns work invoice
curl -s "$BASE/api/jobs/<jobId>"
# 5. (Stub mode only) Mark work invoice paid
curl -s -X POST "$BASE/api/dev/stub/pay/<workPaymentHash>"
# 6. Poll again — auto-advances to complete, returns result
curl -s "$BASE/api/jobs/<jobId>"
# → {"jobId":"…","state":"complete","result":"…"}
```
Job states: `awaiting_eval_payment``evaluating``awaiting_work_payment``executing``complete` | `rejected` | `failed`
#### Free demo endpoint (rate-limited: 5 req/hour per IP)
```bash
curl -s "$BASE/api/demo?request=Explain+proof+of+work+in+one+sentence"
# → {"result":"…"}
```
#### Dev-only stub payment trigger
`POST /api/dev/stub/pay/:paymentHash` — marks a stub invoice paid in-memory.
Only available in development (`NODE_ENV !== 'production'`).
### `scripts` (`@workspace/scripts`)
Utility scripts package. Each script is a `.ts` file in `src/` with a corresponding npm script in `package.json`. Run scripts via `pnpm --filter @workspace/scripts run <script>`. Scripts can import any workspace package (e.g., `@workspace/db`) by adding it as a dependency in `scripts/package.json`.