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.

108 lines 5.61 kB
import type { IncomingMessage, ServerResponse } from 'node:http'; /** * The browser bridge (#1237): the one endpoint an extension running in the user's own Claude * session posts to, so a question a cloud agent is parked on becomes visible in the dashboard. * * **Why this route carries its own token, unlike every other one.** The #1051 guard in * `startDashboard` only exists on a non-loopback bind, and what protects a loopback daemon is * the same-origin check on `/_rpc`: a page on another origin is refused outright. This * route is the first that is *meant* to be reached from another origin, so neither of those * protects it, and it demands `Authorization: Bearer <daemonToken>` unconditionally instead. * * **No CORS headers, on purpose.** An extension's service worker holding `host_permissions` * fetches without a preflight, so the bridge does not need `Access-Control-Allow-Origin` and * must not have it: a wildcard here would let any page the user visits post to their daemon. * The cost is that the extension has to post from its background worker rather than from the * content script, which is a line in the extension and a much better trade. * * **What it accepts is deliberately tiny.** One shape, fully validated, with no path, command, * prompt or free text anywhere in it. The worst a stolen token buys is a bogus question card in * someone's dashboard, which is the point: this is attached to a daemon that spawns processes. */ export declare const BRIDGE_PREFIX = "/_bridge"; /** * The extension version this daemon speaks (#1519). The extension states its own version on * every call, in this header, and a daemon expecting another refuses outright: a version-skewed * extension does not fail loudly, it half-works — missed messages, silently ignored fields — * which reads as a framework bug and burns a debugging session. The extension's manifest must * carry the same number; a test keeps the two in lockstep. */ export declare const EXPECTED_EXTENSION_VERSION = "0.8.1"; /** The header the extension states its version in. Lowercase, as node presents all headers. */ export declare const EXTENSION_VERSION_HEADER = "x-tf-extension-version"; /** A question a cloud session is parked on, as reported by the bridge. */ export interface BridgeQuestion { /** The cloud session that asked, which joins back to an agent through `AgentMeta.sessionId`. */ sessionId: string; title: string; options: { label: string; detail?: string; }[]; recommended?: string; /** When the daemon accepted it. Set here, never by the caller. */ receivedAt: string; } /** * One thing a cloud session did, as scraped from its page (#1237). * * `seq` is the message's position in the transcript, assigned by the extension, and it is what * makes this idempotent: the page is re-read on every DOM change, so the same message arrives * many times and the daemon keeps one copy per position rather than a growing pile of repeats. */ export interface BridgeEvent { sessionId: string; seq: number; role: 'agent' | 'user'; text: string; receivedAt: string; } /** What the page half of the bridge reports about itself, for diagnosis. */ export interface BridgeHello { version: string; sessionId?: string | undefined; note: string; at: string; } /** A cloud session the extension should be watching. */ export interface BridgeSession { id: string; url: string; } /** What the daemon wires behind the bridge. Absent when the feature is off, which 404s it. */ export interface BridgeHandlers { /** The shared secret every bridge call must present. */ token: string; /** * The extension version to insist on (#1519). When set, every route past the token demands a * matching {@link EXTENSION_VERSION_HEADER} and answers 426 otherwise — no degraded mode, * ping included, so the only path forward from a stale extension is updating it. */ expectedExtensionVersion?: string; /** What version the caller claimed and whether it was turned away, for the dashboard. */ extensionVersion?: (got: string, blocked: boolean) => void; record: (question: BridgeQuestion) => void; /** Record what the session said, keyed by its position in the transcript. */ recordEvent?: (event: BridgeEvent) => void; /** Note that something reached the bridge, including when it was refused. */ contact?: (route: string, status: number) => void; /** What the injected page script reports about itself. */ hello?: (hello: BridgeHello) => void; /** * The cloud sessions worth watching, newest first. The extension cannot know an agent started: * it only sees pages the user is already on, so without this the bridge works only when * somebody happens to be looking at claude.ai. This is how a tab gets opened for them. */ sessions?: () => Promise<BridgeSession[]>; /** The answer queued in the dashboard for that session, waiting to be delivered (#1237). */ answer?: (sessionId: string) => { id: string; label: string; } | undefined; /** The extension's word on what a delivery attempt did. */ answered?: (sessionId: string, id: string, ok: boolean, note?: string) => void; now?: () => Date; } /** Route a `/_bridge/*` request. A daemon with the bridge off 404s every route. */ export declare function handleBridgeRequest(req: IncomingMessage, res: ServerResponse, pathname: string, handlers: BridgeHandlers | undefined): Promise<void>; //# sourceMappingURL=bridge-endpoints.d.ts.map