public-transit
v0.1.2
Published
A WebSocket relay that connects agent handlers (running in Node.js) with browser clients. Handlers register with the relay server and expose an async generator interface. Browser clients connect over WebSocket to invoke handlers, stream responses, and man
Readme
public-transit
A WebSocket relay that connects agent handlers (running in Node.js) with browser clients. Handlers register with the relay server and expose an async generator interface. Browser clients connect over WebSocket to invoke handlers, stream responses, and manage sessions with abort/undo/redo support.
This is useful when you need to bridge a long-running backend process (like an AI agent, build tool, or CLI) with a browser frontend over WebSocket, with built-in session management, reconnection, and multiplexing.
Install
npm install public-transitHow it works
┌─────────┐ WebSocket ┌──────────────┐ WebSocket ┌─────────┐
│ Browser │ ◄───────────► │ Relay Server │ ◄───────────► │ Handler │
│ (client) │ │ (hub) │ │ (agent) │
└─────────┘ └──────────────┘ └─────────┘- A relay server runs on a known port (default
4722) - Handlers connect and register themselves by ID (e.g.
"my-agent") - Browser clients connect and can invoke any registered handler
- Messages stream back from handler → relay → browser in real time
Usage
1. Start a relay with a handler (Node.js)
The simplest way — connectRelay auto-starts a relay server if one isn't already running, then registers your handler:
import { connectRelay } from "public-transit";
const connection = await connectRelay({
handler: {
agentId: "my-agent",
run: async function* (prompt, options) {
yield { type: "status", content: "Thinking..." };
// do work...
yield { type: "done", content: "Here's the result." };
},
abort: (sessionId) => {
// cancel in-progress work
},
},
});
// later: connection.disconnect()2. Connect from the browser
import { createRelayClient } from "public-transit/client";
const client = createRelayClient();
await client.connect();
// Listen for handler availability
client.onHandlersChange((handlers) => {
console.log("Available handlers:", handlers);
});
// Send a request
client.sendAgentRequest("my-agent", {
prompt: "Refactor this component",
content: ["const App = () => <div>hello</div>"],
});
// Stream responses
client.onMessage((message) => {
if (message.type === "agent-status") {
console.log("Status:", message.content);
} else if (message.type === "agent-done") {
console.log("Done:", message.content);
} else if (message.type === "agent-error") {
console.error("Error:", message.content);
}
});3. Use the high-level agent provider (browser)
For a more ergonomic async-iterable interface:
import { createRelayClient, createRelayAgentProvider } from "public-transit/client";
const client = createRelayClient();
await client.connect();
const agent = createRelayAgentProvider({
relayClient: client,
agentId: "my-agent",
});
const controller = new AbortController();
for await (const chunk of agent.send(
{ prompt: "Fix the bug", content: ["..."] },
controller.signal,
)) {
console.log(chunk);
}4. Standalone relay server (advanced)
If you want to run the relay server separately from handlers:
import { createRelayServer } from "public-transit/server";
const server = createRelayServer({ port: 4722, token: "secret" });
await server.start();
// Handlers and browsers connect over WebSocket
// Token is validated on connectionExports
| Path | Platform | Description |
| ----------------- | -------- | ---------------------------------------------- |
| public-transit | Node.js | Everything (server + client + protocol) |
| public-transit/server | Node.js | createRelayServer |
| public-transit/client | Browser | createRelayClient, createRelayAgentProvider|
| public-transit/protocol | Any | Shared types and constants |
Authentication
Pass a token option to the server and clients. The relay validates the token on both HTTP health checks and WebSocket connections:
// Server
createRelayServer({ token: "secret" });
// Handler
connectRelay({ handler, token: "secret" });
// Browser
createRelayClient({ token: "secret" });License
MIT
