UNPKG

framework

Version:

The (AI) Framework: turnkey, zero-config AI orchestration that wraps a coding-agent CLI (Claude Code) as a black box and takes you from an idea to a running app. Vite for AI.

90 lines 5.78 kB
import { EventStream } from '../event-stream.js'; import type { FrameworkEvent } from '../events.js'; import { type AgentMeta } from '../store/index.js'; import type { StartAgentKind, StartAgentOptions, StartAgentResult } from './types.js'; /** * The server-side half of "run on a connected device" (#1067). The local daemon holds the saved * device's token, so it - not the browser - drives the remote daemon: it POSTs the agent to the * remote's `/_relay/start` and then fetch-streams the remote's `/_relay/events` back into a local * {@link EventStream}, which the dashboard reads over its normal same-origin `onEvents` channel. So * the browser never talks cross-origin and the token never leaves the two daemons (issue #1067 (b)). * * Authentication is the shared-token cookie (#1051), sent daemon-to-daemon: `Cookie: fw_daemon=<token>` with no * `Origin` header. The remote's guard admits a matching cookie without the browser-only `?token=` * 302, and its `/_rpc` CSRF check (absent Origin passes) is not even on these raw routes. */ /** Where a relayed agent executes: the remote daemon's origin and its #1051 token. Memory-only. */ export interface RemoteTarget { url: string; token: string; } /** The body a relay start forwards to the remote's `/_relay/start`. */ export interface RelayStartBody { prompt: string; kind: StartAgentKind; options: StartAgentOptions; } /** * Health-check a saved device (#1072): a cookie'd `GET /_relay/ping`, true on any 2xx, false on a * non-2xx, an unreachable host, or the timeout. The token stays in memory for the check only, never * persisted, same as {@link startRemoteAgent}. This is how the browser's status dots learn reachable * from not: it has the tokens, the daemon does the cross-origin request. */ export declare function pingRemote(target: RemoteTarget): Promise<boolean>; /** * Start an agent on the remote daemon and return its {@link StartAgentResult} (with the remote's own run * id). A non-2xx or a transport failure surfaces as an `ok: false` result the dashboard shows, the * same shape a local refusal has, so the caller does not special-case remote errors. */ export declare function startRemoteAgent(target: RemoteTarget, body: RelayStartBody): Promise<StartAgentResult>; /** * Relay one run-scoped RPC to the device that owns a remote agent (#1067 slice 2). The local daemon * holds the device token, so a read/diff/handoff/push/PR for a relayed agent runs ON the device: POST * {fn, args} to the remote's /_relay/rpc over the shared-token cookie (#1051) (no Origin), returning the device's * result. Throws on an unreachable device or a non-2xx so the caller falls back to its own empty/error * shape, the same way a failed local read does. */ export declare function relayRpc(target: RemoteTarget, fn: string, args: unknown[]): Promise<unknown>; /** * Fetch-stream a remote agent's newline-delimited events into `onEvent` until the remote closes the * body, the agent ends, or `cancel()` is called. A 401 (the token was rotated) ends the stream * cleanly rather than as an error, so the dashboard sees a normal `done`, not a lost connection. * Returns a cancel function; calling it aborts the fetch and releases the reader. */ export declare function streamRemoteEvents(target: RemoteTarget, agentId: string, onEvent: (event: FrameworkEvent) => void, onEnd?: () => void): () => void; /** * The local daemon's live relayed runs (#1067), keyed by the remote agent id. Registering an agent opens * an {@link EventStream} the dashboard reads through `onEvents`, fed by {@link streamRemoteEvents} * from the remote. This map is where a saved device's token lives daemon-side: in memory, for the * run's lifetime. * * The `targets` map outlives the event pump (#1067 slice 2): a finished remote agent's post-run reads, * push and open-PR still have to reach the device after its event stream has ended, so the device * target is kept until {@link dispose} clears it, not dropped when the stream closes. * * The `metas` map (#1077) holds a local {@link AgentMeta} stub per relayed agent so `onAgents` can show a * remote run in the session list and re-open it after a dashboard reload; {@link list} projects it * per project. Same lifetime as `targets`: it outlives the event stream and is cleared on dispose. */ export declare class RelayedAgents { private readonly agents; private readonly targets; private readonly metas; /** Open a local stream for a remote agent and start pumping the remote's events into it. */ register(agentId: string, target: RemoteTarget, meta: AgentMeta, projectId: string): void; /** The live event stream for a relayed agent, or undefined when this daemon is not relaying it. */ get(agentId: string | undefined): EventStream<FrameworkEvent> | undefined; /** The device a relayed agent runs on, kept past the event stream so post-run push/PR still reach it. */ target(agentId: string | undefined): RemoteTarget | undefined; /** A project's relayed run stubs (#1077), newest-first, so `onAgents` can surface them in the list. */ list(projectId: string): AgentMeta[]; /** Fold each relayed event into the agent's list row via the store's own reducer (#1077), so the * local stub mirrors the device: the terminal status on `end`, the waiting flag while it is parked * (#785), the driver once its session starts. Events carry no write time, so this stamps its own. */ private apply; /** Close a relayed agent's event stream (not its target). Idempotent. */ private endStream; /** Stop every pump, close every stream, and forget every device target + list stub, on daemon shutdown. */ dispose(): void; } //# sourceMappingURL=remote-run.d.ts.map