UNPKG

kestrel.markets

Version:

A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.

191 lines (186 loc) 6.01 kB
import { sha256 } from "./bin-8pchjxn2.js"; import { asOfferResponse, decodeTrialCapability, isSseEventType, isTerminalSseEvent } from "./bin-2ywrx58g.js"; // src/client/sse-transport.ts var MAX_RECONNECTS = 5; class FailClosed { cause; constructor(cause) { this.cause = cause; } } async function consumeSseStream(opts) { let cursor = opts.cursor; const budget = opts.maxReconnects ?? MAX_RECONNECTS; for (let attempt = 0;attempt < budget; attempt++) { const q = new URLSearchParams({ cursor }); const res = await opts.fetch(`${opts.endpoint}?${q.toString()}`, { headers: { ...opts.headers, accept: "text/event-stream", "last-event-id": cursor } }); if (!res.ok) throw opts.httpError(res.status, "event stream failed"); const stream = res.body; if (!stream) throw opts.httpError(500, "event stream had no body"); try { for await (const f of parseSSE(stream)) { if (f.id !== undefined) cursor = f.id; if (f.data === "" && f.event === undefined) continue; const type = f.event ?? ""; if (!opts.isEventType(type)) throw new FailClosed(opts.unknownEventError(type)); let out; try { out = opts.onFrame(type, f.data, cursor); } catch (e) { throw new FailClosed(e); } if (out.cursor) cursor = out.cursor; if (out.done) return; } } catch (e) { if (e instanceof FailClosed) throw e.cause; continue; } } throw opts.httpError(504, "event stream did not reach a terminal state after retries"); } async function* parseSSE(body) { const reader = body.getReader(); const dec = new TextDecoder; let buf = ""; for (;; ) { const { value, done } = await reader.read(); if (done) break; buf += dec.decode(value, { stream: true }); let idx; while ((idx = buf.indexOf(` `)) >= 0) { const frame = buf.slice(0, idx); buf = buf.slice(idx + 2); let event; let data = ""; let id; for (const line of frame.split(` `)) { if (line.startsWith(":")) continue; if (line.startsWith("event:")) event = line.slice(6).trim(); else if (line.startsWith("data:")) data += (data ? ` ` : "") + line.slice(5).replace(/^ /, ""); else if (line.startsWith("id:")) id = line.slice(3).trim(); } yield { event, data, id }; } } } // src/client/platform-wire.ts var DEFAULT_API = "https://api.kestrel.markets"; function jsonHeaders() { return { "content-type": "application/json", accept: "application/json" }; } function bearerHeader(bearer) { return bearer !== undefined ? { authorization: `Bearer ${bearer}` } : {}; } function idemKey(method, path, body) { return "idem_" + sha256(`${method} ${path} ${JSON.stringify(body)}`).slice(0, 40); } async function decodeOfferResponse(res, httpError) { let body; try { body = await res.json(); } catch { throw httpError(402, "402 body was not a structured OfferResponse"); } const offer = asOfferResponse(body); if (offer === null) throw httpError(402, "402 body missing operation_id/offer{offer_id,scope,amount}/settlement_methods"); return offer; } async function parseValidateDiagnostics(res) { try { const body = await res.json(); return (body.diagnostics ?? []).map((d) => `${d.severity ?? "error"} ${d.code ?? ""}: ${d.message ?? ""}`.trim()); } catch { return ["validation_failed"]; } } async function mintTrialCapability(opts) { const body = {}; const res = await opts.fetch(`${opts.base}/capabilities/trial`, { method: "POST", headers: { ...jsonHeaders(), "idempotency-key": idemKey("POST", "/capabilities/trial", body) }, body: JSON.stringify(body) }); if (!res.ok) throw await opts.onHttpError(res, "trial capability mint failed"); const wire = await res.json(); return { capability: decodeTrialCapability(wire), bearer: wire.capability }; } async function postEffectful(opts) { const res = await opts.fetch(`${opts.base}${opts.path}`, { method: "POST", headers: { ...jsonHeaders(), ...opts.authHeaders, "idempotency-key": idemKey("POST", opts.path, opts.body) }, body: JSON.stringify(opts.body) }); if (res.status === 402) return { gated: true, payment: await decodeOfferResponse(res, opts.httpError) }; if (!res.ok) throw await opts.onHttpError(res, `POST ${opts.path} failed`); return { gated: false, value: await res.json() }; } async function consumeOperationStream(opts) { const payload = {}; await consumeSseStream({ fetch: opts.fetch, endpoint: `${opts.base}/operations/${encodeURIComponent(opts.operationId)}/events`, cursor: opts.cursor, ...opts.headers !== undefined ? { headers: opts.headers } : {}, isEventType: isSseEventType, httpError: opts.httpError, unknownEventError: opts.unknownEventError, onFrame: (type, data, cursor) => { let env; if (data !== "") { try { env = JSON.parse(data); } catch { throw opts.httpError(502, `SSE ${type} carried non-JSON data`); } } if (opts.onEvent) opts.onEvent({ type, operationId: env?.operation_id ?? opts.operationId, cursor: env?.cursor ?? cursor, ...env?.data !== undefined ? { data: env.data } : {} }); if ((type === "operation.artifact" || type === "operation.receipt") && env?.data) Object.assign(payload, env.data); if (isTerminalSseEvent(type)) { if (type === "operation.failed") throw opts.httpError(502, "operation failed"); return { done: true }; } return env?.cursor ? { cursor: env.cursor } : {}; } }); return { payload }; } export { DEFAULT_API, jsonHeaders, bearerHeader, idemKey, parseValidateDiagnostics, mintTrialCapability, postEffectful, consumeOperationStream };