A lightweight HTTP front‑end for the Pi coding‑agent's RPC client. The server lets you:
- Create a new
RpcClientinstance on demand, passing any of the RPC client options in the request body. - Send prompts (and optional images) to a specific client via a simple
POSTrequest. - Stream the agent's JSON‑L events back to the HTTP client in real time.
- Stop and clean up a client with a
DELETErequest.
Multiple clients can run concurrently; each is kept in memory and identified by a UUID.
- Installation
- Running the Server
- API Endpoints
- Configuration
- Example usage (cURL)
- Notes & Limitations
# From the repository root
npm installThe server is written in TypeScript and compiled to JavaScript in dist/.
# Development (ts-node, no compilation)
npm run dev
# Production (compiled output)
npm run build && npm startThe server listens on PORT (default 3000) and logs the listening address.
A helper script is provided at bin/run.sh which will install dependencies, build the project, and start the server in one command. Make it executable and run:
chmod +x bin/run.sh # one‑time
./bin/run.sh # install → build → start (default workflow)
# Or pass a custom npm script, e.g.:
./bin/run.sh dev # runs `npm run dev`The script automatically changes to the repository root before invoking npm, so it works from any location.
All request/response bodies are JSON unless otherwise noted.
Note: The server now uses a Winston logger for structured logging (configurable via LOG_LEVEL). Metrics are collected using prom-client; however, the /metrics endpoint has been removed from the server. You can expose the Prometheus metrics registry through your own endpoint or reverse proxy.
POST /clients
{
"name": "my‑assistant", // optional human‑readable identifier
"provider": "openai",
"model": "gpt-4o-mini",
"cwd": "/my/project",
"env": { "OPENAI_API_KEY": "sk-..." },
"args": ["--no-session"]
}- Body – any subset of
RpcClientOptionsplus an optionalnamestring. Ifnameis provided it must be unique among active clients; otherwise a UUID is generated. - Response –
201 Created
{ "id": "<uuid>" }The response always contains the generated UUID (id). The supplied name (if any) can be used interchangeably with the UUID for subsequent calls.
DELETE /clients/:id
- Stops the underlying RPC process and removes it from memory.
- Responses
204 No Content– client successfully stopped.404 Not Found– no client with that UUID.
POST /clients/:id/message
The body must contain a type field that matches one of the RPC commands defined in rpc.md. If type is omitted it defaults to "prompt".
type |
Required fields | Description |
|---|---|---|
prompt |
message (string) – optional images |
Sends a user prompt to the agent. |
steer |
message (string) – optional images |
Queues a steering message while the agent is running. |
follow_up |
message (string) – optional images |
Queues a follow‑up message to be processed after the agent finishes. |
For these three commands the server streams events (AgentEvent) back to the client using JSON‑L (Content‑Type: application/jsonl). Each line is a JSON‑encoded event such as agent_start, message_update, agent_end, etc. The stream ends when an agent_end event is received.
For all other RPC commands the request body must include type and the command‑specific parameters. The server executes the command and returns a single JSON object:
{ "type": "<command>", "result": <command‑specific‑payload> }Supported non‑streaming commands include (but are not limited to):
abortbash(requirescommandstring)abort_bashnew_session(optionalparentSession)set_model(provider&modelId)cycle_modelget_stateset_steering_mode(mode)set_follow_up_mode(mode)compact(optionalcustomInstructions)set_auto_compaction(enabled)set_auto_retry(enabled)abort_retryget_session_statsexport_html(optionaloutputPath)switch_session(sessionPath)fork(entryId)get_fork_messagesget_last_assistant_textset_session_name(name)get_messagesget_commands
Headers – Content-Type: application/json for the request.
Error handling – If the request payload is malformed, a required field is missing, or the client identifier does not exist, the server returns a JSON error with the appropriate HTTP status (400/404/500).
The server itself has minimal configuration; most behaviour is driven by the RpcClientOptions supplied when creating a client.
| Option | Description |
|---|---|
cliPath |
Path to the Pi CLI entry point (defaults to dist/cli.js). |
cwd |
Working directory for the spawned agent process. |
env |
Environment variables for the child process (e.g., API keys). |
provider |
Default LLM provider (openai, anthropic, google, …). |
model |
Default model identifier. |
args |
Additional CLI flags (e.g., --no-session). |
All options are optional; omitted values fall back to the Pi defaults.
The server can automatically start a set of RPC clients on launch when the environment variable RPC_CONFIG_PATH points to a JSON file. The file must export an array of client specifications. Each entry may contain:
name(optional) – a human‑readable identifier. Must be unique among the list.options– an object matching theRpcClientOptionstype (same fields described above).
Example rpc-clients.json
[
{
"name": "assistant‑openai",
"provider": "openai",
"model": "gpt-4o-mini",
"env": { "OPENAI_API_KEY": "sk-..." }
},
{
"provider": "anthropic",
"model": "claude-3-5-sonnet",
"args": ["--no-session"]
}
]When the server starts, it will read this file, create each client, and log success or failure. Clients can later be accessed via their name (if provided) or the generated UUID.
# 1️⃣ Create a client
client_id=$(curl -s -X POST http://localhost:3000/clients \
-H "Content-Type: application/json" \
-d '{"provider":"openai","model":"gpt-4o-mini"}' | jq -r .id)
echo "Created client $client_id"
# 2️⃣ Send a prompt and stream events (requires jq for pretty‑printing)
curl -N -X POST http://localhost:3000/clients/$client_id/message \
-H "Content-Type: application/json" \
-d '{"type":"prompt","message":"Write a short Python script that prints \"Hello, world!\""}' |
while IFS= read -r line; do
echo "Event:"; echo "$line" | jq .
done
# 3️⃣ When done, delete the client
curl -X DELETE http://localhost:3000/clients/$client_idThe -N flag tells curl to not buffer the output, allowing you to see events as they arrive.
- Clients are stored in‑process memory only. Restarting the server will discard all active clients.
- The server does not implement authentication or rate‑limiting – add a reverse proxy or middleware if needed.
- Image handling expects the same shape as
ImageContentfrom the Pi SDK (base64 data, MIME type, etc.). - Because the RPC client streams events over stdout, the HTTP endpoint forwards them unchanged; any client capable of parsing JSONL can consume the stream.
- Logging is handled by Winston; set
LOG_LEVEL(e.g.,debug,info,warn,error) to control verbosity. - Metrics are collected via
prom-client. The server no longer exposes a built‑in/metricsendpoint; expose theregisterregistry yourself if you need Prometheus scraping.
The hf-push-sessions utility can push saved RPC client session files to a Hugging Face dataset. After running it, the dataset will be publicly available (or private depending on the repo settings) at:
https://huggingface.co/datasets/rgruchalski/combust-labs_pi-mono-http-proxy
You can browse the uploaded session files there, or use the Hugging Face Hub API to download them for analysis.
This project is licensed under the Apache License, Version 2.0. See the LICENSE file for details.
The server uses Winston for all logging. By default logs are printed to the console with timestamps and colourised levels. Adjust the log level with the LOG_LEVEL environment variable (default info).