/** * Quattrino TypeScript SDK (single file, fetch-based, zero dependencies). * * Usage: * import { Quattrino } from "./quattrino"; * const q = new Quattrino("https://YOUR-QUATTRINO-HOST", * "qtrn_agent_key..."); * const pre = await q.preflight({ amount_minor: 1999, * merchant: "openai" }); */ export class QuattrinoError extends Error { constructor(public status: number, public payload: unknown) { super(`Quattrino API error ${status}`); } } type Json = Record; export class Quattrino { private base: string; constructor(baseUrl: string, private apiKey: string) { this.base = baseUrl.replace(/\/+$/, "") + "/api/v1"; } private async call(method: string, path: string, body?: Json, params?: Record, headers?: Record): Promise { const url = new URL(this.base + path); if (params) Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v)); const res = await fetch(url.toString(), { method, headers: { "X-API-Key": this.apiKey, "Content-Type": "application/json", ...(headers ?? {}) }, body: body ? JSON.stringify(body) : undefined, }); const payload = await res.json().catch(() => res.text()); if (!res.ok) throw new QuattrinoError(res.status, payload); return payload as Json; } // ---- identity + purchase lifecycle ---------------------------------- whoami() { return this.call("GET", "/agent/whoami"); } /** Side-effect-free preflight: policy dry-run, routes, fees. */ preflight(body: Json) { return this.call("POST", "/agent/preflight", body); } requestTransaction(body: Json, idempotencyKey?: string) { return this.call("POST", "/agent/transactions", body, undefined, { "Idempotency-Key": idempotencyKey ?? crypto.randomUUID() }); } getTransaction(transactionId: string) { return this.call("GET", `/agent/transactions/${transactionId}`); } checkApproval(approvalId: string) { return this.call("GET", `/agent/approvals/${approvalId}`); } submitReceipt(transactionId: string, receipt: Json) { return this.call("POST", `/agent/transactions/${transactionId}/receipt`, receipt); } // ---- marketplace ------------------------------------------------------ marketplaceSearch(query = "", category?: string) { return this.call("GET", "/agent/marketplace/search", undefined, category ? { query, category } : { query }); } getServiceQuote(serviceId: string, quantity = 1) { return this.call("POST", `/agent/services/${serviceId}/quote`, { quantity }); } /** Deterministic negotiation. ACCEPTED responses include quote.quote_id. */ negotiate(serviceId: string, proposedUnitPriceMinor: number, quantity = 1) { return this.call("POST", `/agent/services/${serviceId}/negotiations`, { proposed_unit_price_minor: proposedUnitPriceMinor, quantity }); } respondNegotiation(negotiationId: string, action: "accept_counter" | "propose" = "accept_counter", proposedUnitPriceMinor?: number) { return this.call("POST", `/agent/negotiations/${negotiationId}/respond`, { action, ...(proposedUnitPriceMinor != null ? { proposed_unit_price_minor: proposedUnitPriceMinor } : {}) }); } purchaseService(serviceId: string, opts: { idempotencyKey?: string; quantity?: number; quoteId?: string } = {}) { return this.call("POST", `/agent/services/${serviceId}/purchase`, { quantity: opts.quantity ?? 1, ...(opts.quoteId ? { quote_id: opts.quoteId } : {}), }, undefined, { "Idempotency-Key": opts.idempotencyKey ?? crypto.randomUUID() }); } }