@@ -154,21 +154,224 @@ FastAPI (uvicorn)
154154
155155## REST API
156156
157- The dashboard exposes a REST API you can use directly:
158-
159- | Method | Endpoint | Description |
160- | --------| -------------------------------------------------------| --------------------------------|
161- | POST | ` /api/runs ` | Create and start a new run |
162- | GET | ` /api/runs ` | List all runs |
163- | GET | ` /api/runs/{run_id} ` | Get run details and iterations |
164- | POST | ` /api/runs/{run_id}/pause ` | Pause a running run |
165- | POST | ` /api/runs/{run_id}/resume ` | Resume a paused run |
166- | POST | ` /api/runs/{run_id}/stop ` | Stop a run |
167- | PATCH | ` /api/runs/{run_id}/settings ` | Update runtime settings |
168- | GET | ` /api/projects/{project_dir}/primitives ` | List all primitives |
169- | GET | ` /api/projects/{project_dir}/primitives/{kind}/{name} ` | Get a specific primitive |
170- | PUT | ` /api/projects/{project_dir}/primitives/{kind}/{name} ` | Update a primitive |
171- | POST | ` /api/projects/{project_dir}/primitives/{kind} ` | Create a new primitive |
172- | DELETE | ` /api/projects/{project_dir}/primitives/{kind}/{name} ` | Delete a primitive |
173-
174- Connect to ` /ws ` for live event streaming via WebSocket.
157+ The dashboard exposes a REST API you can use to script runs, manage primitives,
158+ and build custom integrations. All examples below assume the dashboard is running
159+ at ` http://127.0.0.1:8765 ` .
160+
161+ ### Runs
162+
163+ #### Start a new run
164+
165+ ``` bash
166+ curl -X POST http://127.0.0.1:8765/api/runs \
167+ -H " Content-Type: application/json" \
168+ -d ' {
169+ "project_dir": ".",
170+ "max_iterations": 5,
171+ "delay": 2,
172+ "timeout": 300,
173+ "stop_on_error": true
174+ }'
175+ ```
176+
177+ The request body accepts these fields:
178+
179+ | Field | Type | Default | Description |
180+ | ------------------| ---------------| --------------| -----------------------------------------------------|
181+ | ` project_dir ` | string | ` "." ` | Path to the project directory |
182+ | ` prompt_file ` | string | ` "PROMPT.md" ` | Path to the prompt file |
183+ | ` prompt_text ` | string\| null | ` null ` | Inline prompt text (overrides ` prompt_file ` ) |
184+ | ` prompt_name ` | string\| null | ` null ` | Named prompt to use from ` .ralph/prompts/ ` |
185+ | ` command ` | string\| null | ` null ` | Agent command (reads from ` ralph.toml ` if omitted) |
186+ | ` args ` | list\| null | ` null ` | Agent arguments (reads from ` ralph.toml ` if omitted)|
187+ | ` max_iterations ` | int\| null | ` null ` | Max iterations (` null ` = unlimited) |
188+ | ` delay ` | float | ` 0 ` | Seconds to wait between iterations |
189+ | ` timeout ` | float\| null | ` null ` | Seconds before killing a stuck iteration |
190+ | ` stop_on_error ` | bool | ` false ` | Stop the loop if the agent exits non-zero |
191+ | ` log_dir ` | string\| null | ` null ` | Directory to save iteration logs |
192+
193+ Response:
194+
195+ ``` json
196+ {
197+ "run_id" : " a1b2c3d4" ,
198+ "status" : " running" ,
199+ "iteration" : 0 ,
200+ "completed" : 0 ,
201+ "failed" : 0 ,
202+ "timed_out" : 0
203+ }
204+ ```
205+
206+ You can provide a prompt three ways — pick one:
207+
208+ - ** ` prompt_file ` ** — path to a markdown file (default: reads ` PROMPT.md ` )
209+ - ** ` prompt_text ` ** — raw prompt string passed inline
210+ - ** ` prompt_name ` ** — name of a prompt in ` .ralph/prompts/ `
211+
212+ #### List all runs
213+
214+ ``` bash
215+ curl http://127.0.0.1:8765/api/runs
216+ ```
217+
218+ ``` json
219+ [
220+ {
221+ "run_id" : " a1b2c3d4" ,
222+ "status" : " running" ,
223+ "iteration" : 3 ,
224+ "completed" : 2 ,
225+ "failed" : 1 ,
226+ "timed_out" : 0
227+ }
228+ ]
229+ ```
230+
231+ #### Get run details
232+
233+ ``` bash
234+ curl http://127.0.0.1:8765/api/runs/a1b2c3d4
235+ ```
236+
237+ Returns the same shape as the list response, for a single run.
238+
239+ #### Pause, resume, and stop
240+
241+ ``` bash
242+ curl -X POST http://127.0.0.1:8765/api/runs/a1b2c3d4/pause
243+ curl -X POST http://127.0.0.1:8765/api/runs/a1b2c3d4/resume
244+ curl -X POST http://127.0.0.1:8765/api/runs/a1b2c3d4/stop
245+ ```
246+
247+ All three return the updated run status.
248+
249+ #### Update settings mid-run
250+
251+ Change runtime settings without restarting:
252+
253+ ``` bash
254+ curl -X PATCH http://127.0.0.1:8765/api/runs/a1b2c3d4/settings \
255+ -H " Content-Type: application/json" \
256+ -d ' {"max_iterations": 10, "delay": 5}'
257+ ```
258+
259+ All fields are optional — only the ones you include are updated:
260+
261+ | Field | Type | Description |
262+ | ------------------| --------------| ---------------------------------------|
263+ | ` max_iterations ` | int\| null | New iteration limit |
264+ | ` delay ` | float\| null | New delay between iterations |
265+ | ` timeout ` | float\| null | New timeout per iteration |
266+ | ` stop_on_error ` | bool\| null | Whether to stop on agent errors |
267+
268+ ### Primitives
269+
270+ Primitive endpoints use a base64-encoded ` project_dir ` in the URL path. Encode
271+ it with:
272+
273+ ``` bash
274+ PROJECT=$( echo -n " /path/to/project" | base64)
275+ ```
276+
277+ #### List all primitives
278+
279+ ``` bash
280+ curl http://127.0.0.1:8765/api/projects/$PROJECT /primitives
281+ ```
282+
283+ ``` json
284+ [
285+ {
286+ "kind" : " checks" ,
287+ "name" : " tests" ,
288+ "enabled" : true ,
289+ "content" : " Fix all failing tests." ,
290+ "frontmatter" : {"command" : " uv run pytest -x" , "timeout" : 120 , "enabled" : true }
291+ },
292+ {
293+ "kind" : " contexts" ,
294+ "name" : " git-log" ,
295+ "enabled" : true ,
296+ "content" : " ## Recent commits" ,
297+ "frontmatter" : {"command" : " git log --oneline -10" , "timeout" : 10 , "enabled" : true }
298+ }
299+ ]
300+ ```
301+
302+ #### Get a specific primitive
303+
304+ ``` bash
305+ curl http://127.0.0.1:8765/api/projects/$PROJECT /primitives/checks/tests
306+ ```
307+
308+ #### Create a new primitive
309+
310+ ``` bash
311+ curl -X POST http://127.0.0.1:8765/api/projects/$PROJECT /primitives/checks \
312+ -H " Content-Type: application/json" \
313+ -d ' {
314+ "content": "Fix all type errors.",
315+ "frontmatter": {
316+ "name": "typecheck",
317+ "command": "uv run mypy src/",
318+ "timeout": 60,
319+ "enabled": true
320+ }
321+ }'
322+ ```
323+
324+ The ` name ` field in ` frontmatter ` is required — it determines the directory name
325+ under ` .ralph/checks/typecheck/CHECK.md ` .
326+
327+ #### Update a primitive
328+
329+ ``` bash
330+ curl -X PUT http://127.0.0.1:8765/api/projects/$PROJECT /primitives/checks/tests \
331+ -H " Content-Type: application/json" \
332+ -d ' {
333+ "content": "Fix all failing tests. Do not skip or delete tests.",
334+ "frontmatter": {"command": "uv run pytest -x", "timeout": 180, "enabled": true}
335+ }'
336+ ```
337+
338+ #### Delete a primitive
339+
340+ ``` bash
341+ curl -X DELETE http://127.0.0.1:8765/api/projects/$PROJECT /primitives/checks/typecheck
342+ ```
343+
344+ Returns ` 204 No Content ` on success.
345+
346+ ### WebSocket
347+
348+ Connect to ` /api/ws ` for live event streaming:
349+
350+ ``` javascript
351+ const ws = new WebSocket (" ws://127.0.0.1:8765/api/ws" );
352+
353+ ws .onmessage = (event ) => {
354+ const data = JSON .parse (event .data );
355+ console .log (data .type , data .run_id , data .data );
356+ };
357+ ```
358+
359+ Events are JSON objects with this shape:
360+
361+ ``` json
362+ {
363+ "type" : " iteration_start" ,
364+ "run_id" : " a1b2c3d4" ,
365+ "timestamp" : " 2026-03-11T14:23:01.123456" ,
366+ "data" : { }
367+ }
368+ ```
369+
370+ You can filter events by run ID:
371+
372+ ``` javascript
373+ ws .send (JSON .stringify ({ " action" : " subscribe" , " run_id" : " a1b2c3d4" }));
374+ ```
375+
376+ Send ` {"action": "subscribe", "run_id": "*"} ` to receive events from all runs
377+ (this is the default on connect).
0 commit comments