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:
@@ -10,6 +10,10 @@ servers:
|
||||
tags:
|
||||
- name: health
|
||||
description: Health operations
|
||||
- name: jobs
|
||||
description: Payment-gated agent job operations
|
||||
- name: demo
|
||||
description: Free demo endpoint (rate-limited)
|
||||
paths:
|
||||
/healthz:
|
||||
get:
|
||||
@@ -24,6 +28,105 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/HealthStatus"
|
||||
/jobs:
|
||||
post:
|
||||
operationId: createJob
|
||||
tags: [jobs]
|
||||
summary: Create a new agent job
|
||||
description: Accepts a request, creates a job row, and issues an eval fee Lightning invoice.
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/CreateJobRequest"
|
||||
responses:
|
||||
"201":
|
||||
description: Job created successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/CreateJobResponse"
|
||||
"400":
|
||||
description: Invalid request body
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
"500":
|
||||
description: Server error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
/jobs/{id}:
|
||||
get:
|
||||
operationId: getJob
|
||||
tags: [jobs]
|
||||
summary: Get job status
|
||||
description: Returns current job state. Automatically advances the state machine when a pending invoice is found to be paid.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: Job status
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/JobStatusResponse"
|
||||
"404":
|
||||
description: Job not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
"500":
|
||||
description: Server error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
/demo:
|
||||
get:
|
||||
operationId: runDemo
|
||||
tags: [demo]
|
||||
summary: Free demo (rate-limited)
|
||||
description: Runs the agent without payment. Limited to 5 requests per IP per hour.
|
||||
parameters:
|
||||
- name: request
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: Demo result
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DemoResponse"
|
||||
"400":
|
||||
description: Missing or invalid request param
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
"429":
|
||||
description: Rate limit exceeded
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
"500":
|
||||
description: Server error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
components:
|
||||
schemas:
|
||||
HealthStatus:
|
||||
@@ -33,4 +136,75 @@ components:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
|
||||
ErrorResponse:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
InvoiceInfo:
|
||||
type: object
|
||||
required:
|
||||
- paymentRequest
|
||||
- amountSats
|
||||
properties:
|
||||
paymentRequest:
|
||||
type: string
|
||||
amountSats:
|
||||
type: integer
|
||||
CreateJobRequest:
|
||||
type: object
|
||||
required:
|
||||
- request
|
||||
properties:
|
||||
request:
|
||||
type: string
|
||||
minLength: 1
|
||||
CreateJobResponse:
|
||||
type: object
|
||||
required:
|
||||
- jobId
|
||||
- evalInvoice
|
||||
properties:
|
||||
jobId:
|
||||
type: string
|
||||
evalInvoice:
|
||||
$ref: "#/components/schemas/InvoiceInfo"
|
||||
JobState:
|
||||
type: string
|
||||
enum:
|
||||
- awaiting_eval_payment
|
||||
- evaluating
|
||||
- rejected
|
||||
- awaiting_work_payment
|
||||
- executing
|
||||
- complete
|
||||
- failed
|
||||
JobStatusResponse:
|
||||
type: object
|
||||
required:
|
||||
- jobId
|
||||
- state
|
||||
properties:
|
||||
jobId:
|
||||
type: string
|
||||
state:
|
||||
$ref: "#/components/schemas/JobState"
|
||||
evalInvoice:
|
||||
$ref: "#/components/schemas/InvoiceInfo"
|
||||
workInvoice:
|
||||
$ref: "#/components/schemas/InvoiceInfo"
|
||||
reason:
|
||||
type: string
|
||||
result:
|
||||
type: string
|
||||
errorMessage:
|
||||
type: string
|
||||
DemoResponse:
|
||||
type: object
|
||||
required:
|
||||
- result
|
||||
properties:
|
||||
result:
|
||||
type: string
|
||||
|
||||
Reference in New Issue
Block a user