@ralioco/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/index.cjs +635 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +317 -0
- package/dist/index.d.ts +317 -0
- package/dist/index.js +604 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/crypto.ts","../src/errors.ts","../src/auth.ts","../src/registration.ts","../src/types.ts","../src/resources/chat.ts","../src/resources/transactions.ts","../src/transport.ts"],"sourcesContent":["/** Official TypeScript SDK for the Ralio agentic payment API. */\n\nexport { RalioClient, type RalioClientOptions } from \"./client.js\";\nexport { register, DEFAULT_BASE_URL, type RegisterOptions } from \"./registration.js\";\n\nexport type {\n ChatReply,\n ChatStreamEvent,\n CredentialBinding,\n Message,\n Transaction,\n} from \"./types.js\";\nexport type { ChatParams, ListTransactionsParams } from \"./resources/index.js\";\n\nexport {\n RalioError,\n RalioConfigError,\n RalioRegistrationError,\n RalioAPIError,\n RalioAuthError,\n RalioPermissionError,\n RalioNotFoundError,\n RalioValidationError,\n RalioRateLimitError,\n} from \"./errors.js\";\n","/** The top-level {@link RalioClient}. */\n\nimport { readFile } from \"node:fs/promises\";\n\nimport { TokenManager } from \"./auth.js\";\nimport { loadPrivateKey } from \"./crypto.js\";\nimport { DEFAULT_BASE_URL } from \"./registration.js\";\nimport { ChatResource, TransactionsResource } from \"./resources/index.js\";\nimport { Transport } from \"./transport.js\";\n\nexport interface RalioClientOptions {\n clientId: string;\n /** Path to the PKCS8 PEM private key written by {@link register}. */\n privateKeyPath: string;\n baseUrl?: string;\n scopes?: string[];\n /** Per-request timeout in ms (default 30000). Streams are not bounded. */\n timeoutMs?: number;\n}\n\n/**\n * Client for the Ralio API, authenticated via a credential binding (OAuth 2.1\n * `client_credentials` + `private_key_jwt` + DPoP).\n *\n * Obtain `clientId` and the private key once via {@link register}, then:\n *\n * ```ts\n * const client = await RalioClient.create({\n * clientId: \"cb_...\",\n * privateKeyPath: \"ralio-key.pem\",\n * });\n * const reply = await client.chat.send({ agentId: \"...\", message: \"What's my balance?\" });\n * ```\n */\nexport class RalioClient {\n readonly chat: ChatResource;\n readonly transactions: TransactionsResource;\n\n private constructor(transport: Transport) {\n this.chat = new ChatResource(transport);\n this.transactions = new TransactionsResource(transport);\n }\n\n /** Load the private key and build a ready-to-use client. */\n static async create(opts: RalioClientOptions): Promise<RalioClient> {\n const baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const pem = await readFile(opts.privateKeyPath, \"utf8\");\n const { privateKey, publicJwk, kid } = await loadPrivateKey(pem);\n\n const tokens = new TokenManager({\n clientId: opts.clientId,\n privateKey,\n kid,\n tokenUrl: `${baseUrl}/oauth/token`,\n scopes: opts.scopes,\n });\n const transport = new Transport({\n baseUrl,\n tokens,\n privateKey,\n publicJwk,\n requestTimeoutMs: opts.timeoutMs ?? 30_000,\n });\n return new RalioClient(transport);\n }\n\n /**\n * Release resources. Present for API symmetry and forward compatibility;\n * the native `fetch` transport holds no long-lived connections of its own.\n */\n close(): void {\n // no-op today\n }\n\n [Symbol.dispose](): void {\n this.close();\n }\n}\n","/**\n * ES256 / DPoP crypto primitives for the Ralio machine-auth path.\n *\n * Everything here mirrors what the Ralio API expects byte-for-byte:\n *\n * - P-256 (ES256) is the only curve the token endpoint accepts.\n * - The public JWK is the canonical RFC 7638 form (`crv`/`kty`/`x`/`y` only),\n * so the thumbprint computed here matches the `cnf.jkt` the server stamps on\n * the access token and the fingerprint the owner confirms.\n * - Client assertions follow RFC 7521/7523; DPoP proofs follow RFC 9449.\n */\n\nimport { createHash, randomBytes, type webcrypto } from \"node:crypto\";\nimport {\n SignJWT,\n exportPKCS8,\n exportJWK,\n importPKCS8,\n generateKeyPair as joseGenerateKeyPair,\n calculateJwkThumbprint,\n} from \"jose\";\n\n/**\n * Web-Crypto `CryptoKey`, re-exported from `node:crypto` so consumers don't\n * need DOM lib types. This is what `jose` v6 returns from `importPKCS8` and\n * `generateKeyPair` and what we accept everywhere a private key is needed.\n */\nexport type CryptoKey = webcrypto.CryptoKey;\n\nexport const CLIENT_ASSERTION_TYPE = \"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\";\n\n// The server rejects assertions older than 300s; stay well under to absorb skew.\nconst CLIENT_ASSERTION_TTL_SECONDS = 60;\n\n/** Canonical RFC 7638 public JWK — exactly these members, in this order. */\nexport interface PublicJwk {\n crv: \"P-256\";\n kty: \"EC\";\n x: string;\n y: string;\n}\n\n/** A loaded credential: the private key plus its canonical public JWK and kid. */\nexport interface KeyMaterial {\n privateKey: CryptoKey;\n publicJwk: PublicJwk;\n kid: string;\n}\n\n/** Unpadded base64url — the only form RFC 7515/7517/7638 accept. */\nexport function b64url(raw: Buffer | Uint8Array): string {\n return Buffer.from(raw).toString(\"base64url\");\n}\n\nfunction canonicalize(jwk: { crv?: string; kty?: string; x?: string; y?: string }): PublicJwk {\n if (jwk.kty !== \"EC\" || jwk.crv !== \"P-256\" || !jwk.x || !jwk.y) {\n throw new Error(\"Ralio credentials require a P-256 (EC) key\");\n }\n return { crv: \"P-256\", kty: \"EC\", x: jwk.x, y: jwk.y };\n}\n\n/** RFC 7638 thumbprint of a public JWK — used as `kid` and key id. */\nexport async function jwkThumbprint(jwk: PublicJwk): Promise<string> {\n return calculateJwkThumbprint(jwk, \"sha256\");\n}\n\n/** Mint a P-256 keypair and return its key material. */\nexport async function generateKeypair(): Promise<KeyMaterial> {\n const { privateKey } = await joseGenerateKeyPair(\"ES256\", { extractable: true });\n return fromPrivateKey(privateKey);\n}\n\n/** Derive the canonical public JWK + kid from an already-loaded private key. */\nexport async function fromPrivateKey(privateKey: CryptoKey): Promise<KeyMaterial> {\n const publicJwk = canonicalize(await exportJWK(privateKey));\n const kid = await jwkThumbprint(publicJwk);\n return { privateKey, publicJwk, kid };\n}\n\n/** Serialize a private key to PKCS8 PEM. */\nexport async function privateKeyToPem(privateKey: CryptoKey): Promise<string> {\n return exportPKCS8(privateKey);\n}\n\n/** Load a PKCS8 PEM P-256 private key, returning full key material. */\nexport async function loadPrivateKey(pem: string): Promise<KeyMaterial> {\n let key: CryptoKey;\n try {\n // Extractable so we can derive the public JWK + kid that the server's\n // `cnf.jkt` and the DPoP proof header must reference.\n key = await importPKCS8(pem, \"ES256\", { extractable: true });\n } catch (err) {\n throw new Error(\n `Ralio credentials require a P-256 (EC) private key: ${(err as Error).message}`,\n { cause: err },\n );\n }\n return fromPrivateKey(key);\n}\n\n/**\n * Sign an RFC 7523 JWT bearer client assertion.\n *\n * `iss` and `sub` both equal `clientId`; `aud` is the absolute token endpoint\n * URL. `kid` is the JWK thumbprint so the server can locate the binding.\n */\nexport async function signClientAssertion(\n privateKey: CryptoKey,\n opts: {\n clientId: string;\n audience: string;\n kid: string;\n ttlSeconds?: number;\n },\n): Promise<string> {\n const ttl = opts.ttlSeconds ?? CLIENT_ASSERTION_TTL_SECONDS;\n const iat = Math.floor(Date.now() / 1000);\n return new SignJWT({})\n .setProtectedHeader({ alg: \"ES256\", kid: opts.kid })\n .setIssuer(opts.clientId)\n .setSubject(opts.clientId)\n .setAudience(opts.audience)\n .setIssuedAt(iat)\n .setExpirationTime(iat + ttl)\n .setJti(b64url(randomBytes(16)))\n .sign(privateKey);\n}\n\n/**\n * Sign a single-use DPoP proof (RFC 9449) for one method + URL + token.\n *\n * `url` must already have its query and fragment stripped (`htu` per RFC 9449\n * §4.2). The embedded `jwk` must be the canonical public JWK so its thumbprint\n * matches the access token's `cnf.jkt`.\n */\nexport async function signDpopProof(\n privateKey: CryptoKey,\n opts: {\n method: string;\n url: string;\n accessToken: string;\n jwk: PublicJwk;\n },\n): Promise<string> {\n const iat = Math.floor(Date.now() / 1000);\n const ath = createHash(\"sha256\").update(opts.accessToken, \"ascii\").digest(\"base64url\");\n return new SignJWT({\n htm: opts.method.toUpperCase(),\n htu: opts.url,\n ath,\n })\n .setProtectedHeader({ typ: \"dpop+jwt\", alg: \"ES256\", jwk: { ...opts.jwk } })\n .setIssuedAt(iat)\n .setJti(b64url(randomBytes(16)))\n .sign(privateKey);\n}\n","/** Exception hierarchy for the Ralio SDK. */\n\n/** Base class for every error raised by the SDK. */\nexport class RalioError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Local configuration problem — missing key, bad arguments. */\nexport class RalioConfigError extends RalioError {}\n\n/** A credential-binding registration was rejected, expired, or timed out. */\nexport class RalioRegistrationError extends RalioError {}\n\n/**\n * An error response from the Ralio API.\n *\n * Carries the HTTP `statusCode`, the server-supplied `detail` string, and the\n * `WWW-Authenticate` challenge when present (DPoP/OAuth failures put the\n * specific reason there).\n */\nexport class RalioAPIError extends RalioError {\n readonly statusCode: number;\n readonly detail: string | null;\n readonly wwwAuthenticate: string | null;\n\n constructor(\n message: string,\n opts: { statusCode: number; detail?: string | null; wwwAuthenticate?: string | null },\n ) {\n super(message);\n this.statusCode = opts.statusCode;\n this.detail = opts.detail ?? null;\n this.wwwAuthenticate = opts.wwwAuthenticate ?? null;\n }\n}\n\n/** 401 — missing/invalid token, failed client assertion, or rejected proof. */\nexport class RalioAuthError extends RalioAPIError {}\n\n/** 403 — token lacks the required scope, or the resource isn't owned. */\nexport class RalioPermissionError extends RalioAPIError {}\n\n/** 404 — resource does not exist. */\nexport class RalioNotFoundError extends RalioAPIError {}\n\n/** 422 — invalid field values or a business-rule violation. */\nexport class RalioValidationError extends RalioAPIError {}\n\n/** 429 — rate limited. Back off and retry. */\nexport class RalioRateLimitError extends RalioAPIError {}\n\nconst STATUS_MAP: Record<\n number,\n new (m: string, o: ConstructorParameters<typeof RalioAPIError>[1]) => RalioAPIError\n> = {\n 401: RalioAuthError,\n 403: RalioPermissionError,\n 404: RalioNotFoundError,\n 422: RalioValidationError,\n 429: RalioRateLimitError,\n};\n\n/** Throw the appropriate {@link RalioAPIError} if `response` is an error. */\nexport async function raiseForResponse(response: Response): Promise<void> {\n if (response.ok) return;\n const detail = await extractDetail(response);\n const Cls = STATUS_MAP[response.status] ?? RalioAPIError;\n const message = detail ?? `HTTP ${response.status}`;\n throw new Cls(message, {\n statusCode: response.status,\n detail,\n wwwAuthenticate: response.headers.get(\"www-authenticate\"),\n });\n}\n\n/** Return the FastAPI `detail` field, or the raw body as a fallback. */\nasync function extractDetail(response: Response): Promise<string | null> {\n const text = await response.text().catch(() => \"\");\n let payload: unknown;\n try {\n payload = JSON.parse(text);\n } catch {\n return text || null;\n }\n if (payload && typeof payload === \"object\") {\n const obj = payload as Record<string, unknown>;\n if (typeof obj.detail === \"string\") return obj.detail;\n if (obj.detail != null) return JSON.stringify(obj.detail);\n // OAuth-style error bodies use error/error_description instead.\n const oauth = obj.error_description ?? obj.error;\n if (typeof oauth === \"string\") return oauth;\n }\n return text || null;\n}\n","/**\n * Access-token lifecycle for the machine path.\n *\n * A {@link TokenManager} mints tokens via the `private_key_jwt` client\n * assertion, caches the access token until shortly before expiry, and rotates\n * the refresh token. All token mutations are serialised behind a lock:\n *\n * > Presenting a previously-rotated refresh token is treated as a replay\n * > attack and revokes the whole chain.\n *\n * so two concurrent callers must never race a refresh.\n */\n\nimport { CLIENT_ASSERTION_TYPE, signClientAssertion, type CryptoKey } from \"./crypto.js\";\nimport { raiseForResponse } from \"./errors.js\";\n\nexport interface TokenManagerOptions {\n clientId: string;\n privateKey: CryptoKey;\n kid: string;\n tokenUrl: string;\n scopes?: string[];\n /** Refresh this many seconds before the token actually expires. */\n refreshLeewaySeconds?: number;\n}\n\nexport class TokenManager {\n private readonly clientId: string;\n private readonly privateKey: CryptoKey;\n private readonly kid: string;\n private readonly tokenUrl: string;\n private readonly scopes?: string[];\n private readonly leewayMs: number;\n\n private accessTokenValue: string | null = null;\n private refreshTokenValue: string | null = null;\n private expiresAtMs = 0;\n\n // Serialises all token mutations: each exclusive section runs after the\n // previous one settles, so two callers never mint/refresh concurrently.\n private chain: Promise<unknown> = Promise.resolve();\n\n constructor(opts: TokenManagerOptions) {\n this.clientId = opts.clientId;\n this.privateKey = opts.privateKey;\n this.kid = opts.kid;\n this.tokenUrl = opts.tokenUrl;\n this.scopes = opts.scopes;\n this.leewayMs = (opts.refreshLeewaySeconds ?? 300) * 1000;\n }\n\n /** Return a valid access token, minting or refreshing as needed. */\n async accessToken(): Promise<string> {\n if (this.accessTokenValue && Date.now() < this.expiresAtMs - this.leewayMs) {\n return this.accessTokenValue;\n }\n return this.withLock(async () => {\n // Re-check inside the lock: a concurrent caller may have just refreshed.\n if (this.accessTokenValue && Date.now() < this.expiresAtMs - this.leewayMs) {\n return this.accessTokenValue;\n }\n return this.obtain();\n });\n }\n\n /** Discard the cached token and obtain a fresh one. Used after a 401. */\n async forceRefresh(): Promise<string> {\n return this.withLock(async () => {\n this.accessTokenValue = null;\n return this.obtain();\n });\n }\n\n private withLock<T>(fn: () => Promise<T>): Promise<T> {\n const run = this.chain.then(fn, fn);\n // Keep the chain alive regardless of success/failure of this section.\n this.chain = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n }\n\n private async obtain(): Promise<string> {\n if (this.refreshTokenValue) {\n try {\n return await this.refresh();\n } catch {\n // Refresh chains can be revoked or expired; fall back to a fresh\n // client-assertion mint, which always works while the binding is active.\n this.refreshTokenValue = null;\n }\n }\n return this.mint();\n }\n\n private async mint(): Promise<string> {\n const assertion = await signClientAssertion(this.privateKey, {\n clientId: this.clientId,\n audience: this.tokenUrl,\n kid: this.kid,\n });\n const data: Record<string, string> = {\n grant_type: \"client_credentials\",\n client_assertion_type: CLIENT_ASSERTION_TYPE,\n client_assertion: assertion,\n };\n if (this.scopes && this.scopes.length > 0) {\n data.scope = this.scopes.join(\" \");\n }\n return this.exchange(data);\n }\n\n private async refresh(): Promise<string> {\n if (!this.refreshTokenValue) throw new Error(\"no refresh token\");\n return this.exchange({\n grant_type: \"refresh_token\",\n refresh_token: this.refreshTokenValue,\n client_id: this.clientId,\n });\n }\n\n private async exchange(data: Record<string, string>): Promise<string> {\n const response = await fetch(this.tokenUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams(data).toString(),\n });\n await raiseForResponse(response);\n const body = (await response.json()) as {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n };\n this.accessTokenValue = body.access_token;\n this.refreshTokenValue = body.refresh_token ?? this.refreshTokenValue;\n this.expiresAtMs = Date.now() + (body.expires_in ?? 1800) * 1000;\n return this.accessTokenValue;\n }\n}\n","/**\n * One-time credential-binding registration (the operator side).\n *\n * The owner mints a `ralio-reg-…` ticket in the console. The operator calls\n * {@link register} on the agent host: it generates a P-256 keypair locally,\n * submits the public key with the ticket, and polls until the owner approves\n * the binding in the console. The private key never leaves the host.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { open, rename, rm, access } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { generateKeypair, privateKeyToPem, type PublicJwk } from \"./crypto.js\";\nimport { RalioRegistrationError, raiseForResponse } from \"./errors.js\";\nimport type { CredentialBinding } from \"./types.js\";\n\nexport const DEFAULT_BASE_URL = \"https://api.ralio.co\";\nconst TERMINAL = new Set([\"active\", \"rejected\", \"expired\"]);\n\nexport interface RegisterOptions {\n ticket: string;\n privateKeyPath: string;\n baseUrl?: string;\n requestedScopes?: string[];\n clientMetadata?: Record<string, unknown>;\n /** Poll interval in milliseconds (default 3000). */\n pollIntervalMs?: number;\n /** Overall timeout in milliseconds (default 900000 = 15 min). */\n timeoutMs?: number;\n /** Replace an existing key file. Off by default to avoid clobbering. */\n overwrite?: boolean;\n}\n\n/**\n * Run the registration flow and return the approved binding.\n *\n * Generates a keypair, writes the private key to `privateKeyPath`, and blocks\n * until the owner approves (up to `timeoutMs`). Rejects with a\n * {@link RalioRegistrationError} if the binding is rejected, expires, or the\n * timeout elapses.\n */\nexport async function register(opts: RegisterOptions): Promise<CredentialBinding> {\n const base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const pollIntervalMs = opts.pollIntervalMs ?? 3000;\n const timeoutMs = opts.timeoutMs ?? 900_000;\n\n if (!opts.overwrite && (await exists(opts.privateKeyPath))) {\n throw new RalioRegistrationError(\n `${opts.privateKeyPath} already exists; pass overwrite: true to replace it`,\n );\n }\n\n const { privateKey, publicJwk, kid } = await generateKeypair();\n await savePrivateKey(opts.privateKeyPath, await privateKeyToPem(privateKey));\n\n const pollToken = await submit(base, opts, publicJwk, kid);\n const clientId = await poll(base, pollToken, pollIntervalMs, timeoutMs);\n\n return { clientId, scopes: opts.requestedScopes ?? [] };\n}\n\nasync function submit(\n base: string,\n opts: RegisterOptions,\n publicJwk: PublicJwk,\n fingerprint: string,\n): Promise<string> {\n const body: Record<string, unknown> = { ticket: opts.ticket, public_key_jwk: publicJwk };\n if (opts.requestedScopes) body.requested_scopes = opts.requestedScopes;\n if (opts.clientMetadata) body.client_metadata = opts.clientMetadata;\n\n const response = await fetch(`${base}/api/credential-bindings/registrations`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n await raiseForResponse(response);\n const payload = (await response.json()) as { fingerprint?: string; poll_token?: string };\n\n // The server echoes the JWK thumbprint it computed. A mismatch means our\n // public key was rewritten in flight — refuse to proceed rather than let the\n // owner approve a binding for a key we don't hold.\n if (payload.fingerprint && payload.fingerprint !== fingerprint) {\n throw new RalioRegistrationError(\n \"fingerprint mismatch between local key and server response; aborting\",\n );\n }\n if (typeof payload.poll_token !== \"string\" || !payload.poll_token) {\n throw new RalioRegistrationError(\"registration response did not include a poll_token\");\n }\n return payload.poll_token;\n}\n\nasync function poll(\n base: string,\n pollToken: string,\n intervalMs: number,\n timeoutMs: number,\n): Promise<string> {\n const deadline = Date.now() + timeoutMs;\n const url = `${base}/api/credential-bindings/registrations/${pollToken}`;\n for (;;) {\n const response = await fetch(url);\n if (response.status === 404) {\n throw new RalioRegistrationError(\"registration expired before approval\");\n }\n await raiseForResponse(response);\n const body = (await response.json()) as { status?: string; client_id?: string };\n const status = body.status ?? \"\";\n if (status === \"active\") {\n if (typeof body.client_id !== \"string\" || !body.client_id) {\n throw new RalioRegistrationError(\"binding active but no client_id returned\");\n }\n return body.client_id;\n }\n if (TERMINAL.has(status)) {\n throw new RalioRegistrationError(`registration ${status}`);\n }\n if (Date.now() >= deadline) {\n throw new RalioRegistrationError(\"timed out waiting for owner approval\");\n }\n await sleep(intervalMs);\n }\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Write `pem` at `path`, mode 0600, atomically (temp file + rename). */\nasync function savePrivateKey(path: string, pem: string): Promise<void> {\n const dir = dirname(path);\n const tmp = join(dir, `.tmp-${randomBytes(8).toString(\"hex\")}`);\n const handle = await open(tmp, \"wx\", 0o600);\n try {\n await handle.writeFile(pem);\n await handle.close();\n await rename(tmp, path);\n } catch (err) {\n await handle.close().catch(() => undefined);\n await rm(tmp, { force: true });\n throw err;\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Response types returned by the SDK.\n *\n * Parsing is forgiving: unknown fields the API may add later are ignored, so a\n * server-side addition never breaks a pinned client.\n */\n\ntype Json = Record<string, unknown>;\n\nfunction str(value: unknown, fallback = \"\"): string {\n return typeof value === \"string\" ? value : fallback;\n}\n\nfunction optStr(value: unknown): string | null {\n return typeof value === \"string\" ? value : null;\n}\n\n/**\n * The result of a completed registration. `clientId` is the `cb_…` handle used\n * to mint tokens; the private key lives on disk.\n */\nexport interface CredentialBinding {\n clientId: string;\n scopes: string[];\n}\n\nexport interface Message {\n id: string;\n role: string;\n content: string;\n createdAt: string | null;\n}\n\nexport function parseMessage(data: Json): Message {\n return {\n id: str(data.id),\n role: str(data.role),\n content: str(data.content),\n createdAt: optStr(data.created_at),\n };\n}\n\n/** A synchronous `chat.send` response. */\nexport interface ChatReply {\n reply: string;\n conversationId: string;\n newMessages: Message[];\n}\n\nexport function parseChatReply(data: Json): ChatReply {\n const raw = Array.isArray(data.new_messages) ? data.new_messages : [];\n return {\n reply: str(data.reply),\n conversationId: str(data.conversation_id),\n newMessages: raw.map((m) => parseMessage((m ?? {}) as Json)),\n };\n}\n\n/**\n * One server-sent event from `chat.stream`.\n *\n * `event` is the SSE event name (`conversation`, `tool_started`,\n * `tool_completed`, `tool_failed`, `text_delta`, `reply`, `error`); `data` is\n * the decoded JSON payload. `text` is a convenience for the common\n * `text_delta` / `reply` case (empty string when absent).\n */\nexport interface ChatStreamEvent {\n event: string;\n data: Json;\n text: string;\n}\n\nexport function buildStreamEvent(event: string, data: Json): ChatStreamEvent {\n return { event, data, text: typeof data.text === \"string\" ? data.text : \"\" };\n}\n\nexport interface Transaction {\n id: string;\n amount: string;\n currency: string;\n status: string;\n date: string | null;\n creditor: string | null;\n debtor: string | null;\n reference: string | null;\n paymentIntentId: string | null;\n}\n\nexport function parseTransaction(data: Json): Transaction {\n return {\n id: str(data.id),\n amount: str(data.amount),\n currency: str(data.currency),\n status: str(data.status),\n date: optStr(data.date),\n creditor: optStr(data.creditor),\n debtor: optStr(data.debtor),\n reference: optStr(data.reference),\n paymentIntentId: optStr(data.payment_intent_id),\n };\n}\n","/** Chat resource — drive an agent with natural language (`agents:execute`). */\n\nimport type { Transport } from \"../transport.js\";\nimport { parseChatReply, type ChatReply, type ChatStreamEvent } from \"../types.js\";\n\nexport interface ChatParams {\n agentId: string;\n message: string;\n conversationId?: string;\n}\n\nexport class ChatResource {\n constructor(private readonly transport: Transport) {}\n\n /**\n * Send a message and wait for the agent's complete reply.\n *\n * Times out server-side after 120s. For interactive approval flows where a\n * human may take longer, use {@link stream} instead.\n */\n async send(params: ChatParams): Promise<ChatReply> {\n const response = await this.transport.request(\"POST\", \"/api/chat\", {\n jsonBody: body(params),\n });\n return parseChatReply((await response.json()) as Record<string, unknown>);\n }\n\n /**\n * Stream the agent's reply as server-sent events.\n *\n * Yields `conversation`, `tool_started`/`tool_completed`/`tool_failed`,\n * `text_delta`, and a final `reply` event.\n */\n stream(params: ChatParams): AsyncGenerator<ChatStreamEvent> {\n return this.transport.streamSse(\"POST\", \"/api/chat/stream\", {\n jsonBody: body(params),\n });\n }\n}\n\nfunction body(params: ChatParams): Record<string, unknown> {\n const out: Record<string, unknown> = {\n agent_id: params.agentId,\n message: params.message,\n };\n if (params.conversationId !== undefined) out.conversation_id = params.conversationId;\n return out;\n}\n","/** Transactions resource — read executed payments (`transactions:read`). */\n\nimport type { Transport } from \"../transport.js\";\nimport { parseTransaction, type Transaction } from \"../types.js\";\n\nexport interface ListTransactionsParams {\n agentId?: string;\n limit?: number;\n}\n\nexport class TransactionsResource {\n constructor(private readonly transport: Transport) {}\n\n /** List transactions across the caller's agents, newest first. */\n async list(params: ListTransactionsParams = {}): Promise<Transaction[]> {\n const response = await this.transport.request(\"GET\", \"/api/transactions\", {\n params: { limit: params.limit ?? 50, agent_id: params.agentId },\n });\n const items = (await response.json()) as Record<string, unknown>[];\n return items.map(parseTransaction);\n }\n}\n","/**\n * DPoP-bound HTTP transport.\n *\n * Every request carries `Authorization: DPoP <token>` plus a freshly-signed\n * `DPoP` proof for that exact method + URL + token. On a 401 the transport\n * refreshes the token once and retries with a brand-new proof (the proof `jti`\n * is single-use server-side, so the retry must re-sign).\n */\n\nimport type { TokenManager } from \"./auth.js\";\nimport { signDpopProof, type CryptoKey, type PublicJwk } from \"./crypto.js\";\nimport { RalioAPIError, raiseForResponse } from \"./errors.js\";\nimport { buildStreamEvent, type ChatStreamEvent } from \"./types.js\";\n\nexport interface TransportOptions {\n baseUrl: string;\n tokens: TokenManager;\n privateKey: CryptoKey;\n publicJwk: PublicJwk;\n /** Per-request timeout in ms. Applies to `request`, not to SSE streams. */\n requestTimeoutMs?: number;\n}\n\ninterface RequestOptions {\n jsonBody?: Record<string, unknown>;\n params?: Record<string, string | number | undefined>;\n}\n\nexport class Transport {\n private readonly baseUrl: string;\n private readonly tokens: TokenManager;\n private readonly privateKey: CryptoKey;\n private readonly publicJwk: PublicJwk;\n private readonly requestTimeoutMs?: number;\n\n constructor(opts: TransportOptions) {\n this.baseUrl = opts.baseUrl.replace(/\\/+$/, \"\");\n this.tokens = opts.tokens;\n this.privateKey = opts.privateKey;\n this.publicJwk = opts.publicJwk;\n this.requestTimeoutMs = opts.requestTimeoutMs;\n }\n\n async request(method: string, path: string, opts: RequestOptions = {}): Promise<Response> {\n const url = this.buildUrl(path, opts.params);\n const init = (headers: Record<string, string>): RequestInit => ({\n method,\n headers: opts.jsonBody ? { ...headers, \"Content-Type\": \"application/json\" } : headers,\n body: opts.jsonBody ? JSON.stringify(opts.jsonBody) : undefined,\n signal: this.requestTimeoutMs ? AbortSignal.timeout(this.requestTimeoutMs) : undefined,\n });\n\n let response = await fetch(url, init(await this.authHeaders(method, url)));\n if (response.status === 401) {\n await this.tokens.forceRefresh();\n response = await fetch(url, init(await this.authHeaders(method, url)));\n }\n await raiseForResponse(response);\n return response;\n }\n\n async *streamSse(\n method: string,\n path: string,\n opts: { jsonBody?: Record<string, unknown> } = {},\n ): AsyncGenerator<ChatStreamEvent> {\n const url = this.buildUrl(path);\n const init = async (): Promise<RequestInit> => ({\n method,\n headers: {\n ...(await this.authHeaders(method, url)),\n Accept: \"text/event-stream\",\n ...(opts.jsonBody ? { \"Content-Type\": \"application/json\" } : {}),\n },\n body: opts.jsonBody ? JSON.stringify(opts.jsonBody) : undefined,\n });\n\n let response = await fetch(url, await init());\n if (response.status === 401) {\n await response.body?.cancel();\n await this.tokens.forceRefresh();\n response = await fetch(url, await init());\n }\n await raiseForResponse(response);\n yield* parseSse(response);\n }\n\n private buildUrl(path: string, params?: RequestOptions[\"params\"]): string {\n const url = new URL(`${this.baseUrl}${path}`);\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) url.searchParams.set(key, String(value));\n }\n }\n return url.toString();\n }\n\n private async authHeaders(method: string, url: string): Promise<Record<string, string>> {\n const token = await this.tokens.accessToken();\n const proof = await signDpopProof(this.privateKey, {\n method,\n url: htu(url),\n accessToken: token,\n jwk: this.publicJwk,\n });\n return { Authorization: `DPoP ${token}`, DPoP: proof };\n }\n}\n\n/** Strip query and fragment — the `htu` claim must be scheme+host+path. */\nfunction htu(url: string): string {\n const parsed = new URL(url);\n return `${parsed.origin}${parsed.pathname}`;\n}\n\n/** Yield one {@link ChatStreamEvent} per SSE record (blank-line delimited). */\nasync function* parseSse(response: Response): AsyncGenerator<ChatStreamEvent> {\n if (!response.body) return;\n const decoder = new TextDecoder();\n let buffer = \"\";\n let eventName = \"message\";\n let dataLines: string[] = [];\n\n const flush = (): ChatStreamEvent | null => {\n if (dataLines.length === 0) return null;\n const event = buildEvent(eventName, dataLines);\n eventName = \"message\";\n dataLines = [];\n return event;\n };\n\n const handleLine = (line: string): ChatStreamEvent | null => {\n if (line === \"\") return flush();\n if (line.startsWith(\":\")) return null; // comment / keep-alive\n const idx = line.indexOf(\":\");\n const field = idx === -1 ? line : line.slice(0, idx);\n let value = idx === -1 ? \"\" : line.slice(idx + 1);\n if (value.startsWith(\" \")) value = value.slice(1);\n if (field === \"event\") eventName = value;\n else if (field === \"data\") dataLines.push(value);\n return null;\n };\n\n for await (const chunk of response.body as unknown as AsyncIterable<Uint8Array>) {\n buffer += decoder.decode(chunk, { stream: true });\n let newlineIdx: number;\n while ((newlineIdx = buffer.indexOf(\"\\n\")) !== -1) {\n const raw = buffer.slice(0, newlineIdx);\n buffer = buffer.slice(newlineIdx + 1);\n const line = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n const event = handleLine(line);\n if (event) yield event;\n }\n }\n // Trailing record with no terminating blank line.\n const tail = flush();\n if (tail) yield tail;\n}\n\nfunction buildEvent(eventName: string, dataLines: string[]): ChatStreamEvent {\n const raw = dataLines.join(\"\\n\");\n let payload: Record<string, unknown>;\n try {\n const parsed: unknown = JSON.parse(raw);\n payload =\n parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : { value: parsed };\n } catch {\n payload = { raw };\n }\n if (eventName === \"error\") {\n const message = typeof payload.message === \"string\" ? payload.message : \"stream error\";\n throw new RalioAPIError(message, { statusCode: 200, detail: message });\n }\n return buildStreamEvent(eventName, payload);\n}\n"],"mappings":"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","names":["import_promises","joseGenerateKeyPair","body","import_node_crypto","body"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { webcrypto } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ES256 / DPoP crypto primitives for the Ralio machine-auth path.
|
|
5
|
+
*
|
|
6
|
+
* Everything here mirrors what the Ralio API expects byte-for-byte:
|
|
7
|
+
*
|
|
8
|
+
* - P-256 (ES256) is the only curve the token endpoint accepts.
|
|
9
|
+
* - The public JWK is the canonical RFC 7638 form (`crv`/`kty`/`x`/`y` only),
|
|
10
|
+
* so the thumbprint computed here matches the `cnf.jkt` the server stamps on
|
|
11
|
+
* the access token and the fingerprint the owner confirms.
|
|
12
|
+
* - Client assertions follow RFC 7521/7523; DPoP proofs follow RFC 9449.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Web-Crypto `CryptoKey`, re-exported from `node:crypto` so consumers don't
|
|
17
|
+
* need DOM lib types. This is what `jose` v6 returns from `importPKCS8` and
|
|
18
|
+
* `generateKeyPair` and what we accept everywhere a private key is needed.
|
|
19
|
+
*/
|
|
20
|
+
type CryptoKey = webcrypto.CryptoKey;
|
|
21
|
+
/** Canonical RFC 7638 public JWK — exactly these members, in this order. */
|
|
22
|
+
interface PublicJwk {
|
|
23
|
+
crv: "P-256";
|
|
24
|
+
kty: "EC";
|
|
25
|
+
x: string;
|
|
26
|
+
y: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Access-token lifecycle for the machine path.
|
|
31
|
+
*
|
|
32
|
+
* A {@link TokenManager} mints tokens via the `private_key_jwt` client
|
|
33
|
+
* assertion, caches the access token until shortly before expiry, and rotates
|
|
34
|
+
* the refresh token. All token mutations are serialised behind a lock:
|
|
35
|
+
*
|
|
36
|
+
* > Presenting a previously-rotated refresh token is treated as a replay
|
|
37
|
+
* > attack and revokes the whole chain.
|
|
38
|
+
*
|
|
39
|
+
* so two concurrent callers must never race a refresh.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
interface TokenManagerOptions {
|
|
43
|
+
clientId: string;
|
|
44
|
+
privateKey: CryptoKey;
|
|
45
|
+
kid: string;
|
|
46
|
+
tokenUrl: string;
|
|
47
|
+
scopes?: string[];
|
|
48
|
+
/** Refresh this many seconds before the token actually expires. */
|
|
49
|
+
refreshLeewaySeconds?: number;
|
|
50
|
+
}
|
|
51
|
+
declare class TokenManager {
|
|
52
|
+
private readonly clientId;
|
|
53
|
+
private readonly privateKey;
|
|
54
|
+
private readonly kid;
|
|
55
|
+
private readonly tokenUrl;
|
|
56
|
+
private readonly scopes?;
|
|
57
|
+
private readonly leewayMs;
|
|
58
|
+
private accessTokenValue;
|
|
59
|
+
private refreshTokenValue;
|
|
60
|
+
private expiresAtMs;
|
|
61
|
+
private chain;
|
|
62
|
+
constructor(opts: TokenManagerOptions);
|
|
63
|
+
/** Return a valid access token, minting or refreshing as needed. */
|
|
64
|
+
accessToken(): Promise<string>;
|
|
65
|
+
/** Discard the cached token and obtain a fresh one. Used after a 401. */
|
|
66
|
+
forceRefresh(): Promise<string>;
|
|
67
|
+
private withLock;
|
|
68
|
+
private obtain;
|
|
69
|
+
private mint;
|
|
70
|
+
private refresh;
|
|
71
|
+
private exchange;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Response types returned by the SDK.
|
|
76
|
+
*
|
|
77
|
+
* Parsing is forgiving: unknown fields the API may add later are ignored, so a
|
|
78
|
+
* server-side addition never breaks a pinned client.
|
|
79
|
+
*/
|
|
80
|
+
type Json = Record<string, unknown>;
|
|
81
|
+
/**
|
|
82
|
+
* The result of a completed registration. `clientId` is the `cb_…` handle used
|
|
83
|
+
* to mint tokens; the private key lives on disk.
|
|
84
|
+
*/
|
|
85
|
+
interface CredentialBinding {
|
|
86
|
+
clientId: string;
|
|
87
|
+
scopes: string[];
|
|
88
|
+
}
|
|
89
|
+
interface Message {
|
|
90
|
+
id: string;
|
|
91
|
+
role: string;
|
|
92
|
+
content: string;
|
|
93
|
+
createdAt: string | null;
|
|
94
|
+
}
|
|
95
|
+
/** A synchronous `chat.send` response. */
|
|
96
|
+
interface ChatReply {
|
|
97
|
+
reply: string;
|
|
98
|
+
conversationId: string;
|
|
99
|
+
newMessages: Message[];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* One server-sent event from `chat.stream`.
|
|
103
|
+
*
|
|
104
|
+
* `event` is the SSE event name (`conversation`, `tool_started`,
|
|
105
|
+
* `tool_completed`, `tool_failed`, `text_delta`, `reply`, `error`); `data` is
|
|
106
|
+
* the decoded JSON payload. `text` is a convenience for the common
|
|
107
|
+
* `text_delta` / `reply` case (empty string when absent).
|
|
108
|
+
*/
|
|
109
|
+
interface ChatStreamEvent {
|
|
110
|
+
event: string;
|
|
111
|
+
data: Json;
|
|
112
|
+
text: string;
|
|
113
|
+
}
|
|
114
|
+
interface Transaction {
|
|
115
|
+
id: string;
|
|
116
|
+
amount: string;
|
|
117
|
+
currency: string;
|
|
118
|
+
status: string;
|
|
119
|
+
date: string | null;
|
|
120
|
+
creditor: string | null;
|
|
121
|
+
debtor: string | null;
|
|
122
|
+
reference: string | null;
|
|
123
|
+
paymentIntentId: string | null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* DPoP-bound HTTP transport.
|
|
128
|
+
*
|
|
129
|
+
* Every request carries `Authorization: DPoP <token>` plus a freshly-signed
|
|
130
|
+
* `DPoP` proof for that exact method + URL + token. On a 401 the transport
|
|
131
|
+
* refreshes the token once and retries with a brand-new proof (the proof `jti`
|
|
132
|
+
* is single-use server-side, so the retry must re-sign).
|
|
133
|
+
*/
|
|
134
|
+
|
|
135
|
+
interface TransportOptions {
|
|
136
|
+
baseUrl: string;
|
|
137
|
+
tokens: TokenManager;
|
|
138
|
+
privateKey: CryptoKey;
|
|
139
|
+
publicJwk: PublicJwk;
|
|
140
|
+
/** Per-request timeout in ms. Applies to `request`, not to SSE streams. */
|
|
141
|
+
requestTimeoutMs?: number;
|
|
142
|
+
}
|
|
143
|
+
interface RequestOptions {
|
|
144
|
+
jsonBody?: Record<string, unknown>;
|
|
145
|
+
params?: Record<string, string | number | undefined>;
|
|
146
|
+
}
|
|
147
|
+
declare class Transport {
|
|
148
|
+
private readonly baseUrl;
|
|
149
|
+
private readonly tokens;
|
|
150
|
+
private readonly privateKey;
|
|
151
|
+
private readonly publicJwk;
|
|
152
|
+
private readonly requestTimeoutMs?;
|
|
153
|
+
constructor(opts: TransportOptions);
|
|
154
|
+
request(method: string, path: string, opts?: RequestOptions): Promise<Response>;
|
|
155
|
+
streamSse(method: string, path: string, opts?: {
|
|
156
|
+
jsonBody?: Record<string, unknown>;
|
|
157
|
+
}): AsyncGenerator<ChatStreamEvent>;
|
|
158
|
+
private buildUrl;
|
|
159
|
+
private authHeaders;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Chat resource — drive an agent with natural language (`agents:execute`). */
|
|
163
|
+
|
|
164
|
+
interface ChatParams {
|
|
165
|
+
agentId: string;
|
|
166
|
+
message: string;
|
|
167
|
+
conversationId?: string;
|
|
168
|
+
}
|
|
169
|
+
declare class ChatResource {
|
|
170
|
+
private readonly transport;
|
|
171
|
+
constructor(transport: Transport);
|
|
172
|
+
/**
|
|
173
|
+
* Send a message and wait for the agent's complete reply.
|
|
174
|
+
*
|
|
175
|
+
* Times out server-side after 120s. For interactive approval flows where a
|
|
176
|
+
* human may take longer, use {@link stream} instead.
|
|
177
|
+
*/
|
|
178
|
+
send(params: ChatParams): Promise<ChatReply>;
|
|
179
|
+
/**
|
|
180
|
+
* Stream the agent's reply as server-sent events.
|
|
181
|
+
*
|
|
182
|
+
* Yields `conversation`, `tool_started`/`tool_completed`/`tool_failed`,
|
|
183
|
+
* `text_delta`, and a final `reply` event.
|
|
184
|
+
*/
|
|
185
|
+
stream(params: ChatParams): AsyncGenerator<ChatStreamEvent>;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Transactions resource — read executed payments (`transactions:read`). */
|
|
189
|
+
|
|
190
|
+
interface ListTransactionsParams {
|
|
191
|
+
agentId?: string;
|
|
192
|
+
limit?: number;
|
|
193
|
+
}
|
|
194
|
+
declare class TransactionsResource {
|
|
195
|
+
private readonly transport;
|
|
196
|
+
constructor(transport: Transport);
|
|
197
|
+
/** List transactions across the caller's agents, newest first. */
|
|
198
|
+
list(params?: ListTransactionsParams): Promise<Transaction[]>;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** The top-level {@link RalioClient}. */
|
|
202
|
+
|
|
203
|
+
interface RalioClientOptions {
|
|
204
|
+
clientId: string;
|
|
205
|
+
/** Path to the PKCS8 PEM private key written by {@link register}. */
|
|
206
|
+
privateKeyPath: string;
|
|
207
|
+
baseUrl?: string;
|
|
208
|
+
scopes?: string[];
|
|
209
|
+
/** Per-request timeout in ms (default 30000). Streams are not bounded. */
|
|
210
|
+
timeoutMs?: number;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Client for the Ralio API, authenticated via a credential binding (OAuth 2.1
|
|
214
|
+
* `client_credentials` + `private_key_jwt` + DPoP).
|
|
215
|
+
*
|
|
216
|
+
* Obtain `clientId` and the private key once via {@link register}, then:
|
|
217
|
+
*
|
|
218
|
+
* ```ts
|
|
219
|
+
* const client = await RalioClient.create({
|
|
220
|
+
* clientId: "cb_...",
|
|
221
|
+
* privateKeyPath: "ralio-key.pem",
|
|
222
|
+
* });
|
|
223
|
+
* const reply = await client.chat.send({ agentId: "...", message: "What's my balance?" });
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
declare class RalioClient {
|
|
227
|
+
readonly chat: ChatResource;
|
|
228
|
+
readonly transactions: TransactionsResource;
|
|
229
|
+
private constructor();
|
|
230
|
+
/** Load the private key and build a ready-to-use client. */
|
|
231
|
+
static create(opts: RalioClientOptions): Promise<RalioClient>;
|
|
232
|
+
/**
|
|
233
|
+
* Release resources. Present for API symmetry and forward compatibility;
|
|
234
|
+
* the native `fetch` transport holds no long-lived connections of its own.
|
|
235
|
+
*/
|
|
236
|
+
close(): void;
|
|
237
|
+
[Symbol.dispose](): void;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* One-time credential-binding registration (the operator side).
|
|
242
|
+
*
|
|
243
|
+
* The owner mints a `ralio-reg-…` ticket in the console. The operator calls
|
|
244
|
+
* {@link register} on the agent host: it generates a P-256 keypair locally,
|
|
245
|
+
* submits the public key with the ticket, and polls until the owner approves
|
|
246
|
+
* the binding in the console. The private key never leaves the host.
|
|
247
|
+
*/
|
|
248
|
+
|
|
249
|
+
declare const DEFAULT_BASE_URL = "https://api.ralio.co";
|
|
250
|
+
interface RegisterOptions {
|
|
251
|
+
ticket: string;
|
|
252
|
+
privateKeyPath: string;
|
|
253
|
+
baseUrl?: string;
|
|
254
|
+
requestedScopes?: string[];
|
|
255
|
+
clientMetadata?: Record<string, unknown>;
|
|
256
|
+
/** Poll interval in milliseconds (default 3000). */
|
|
257
|
+
pollIntervalMs?: number;
|
|
258
|
+
/** Overall timeout in milliseconds (default 900000 = 15 min). */
|
|
259
|
+
timeoutMs?: number;
|
|
260
|
+
/** Replace an existing key file. Off by default to avoid clobbering. */
|
|
261
|
+
overwrite?: boolean;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Run the registration flow and return the approved binding.
|
|
265
|
+
*
|
|
266
|
+
* Generates a keypair, writes the private key to `privateKeyPath`, and blocks
|
|
267
|
+
* until the owner approves (up to `timeoutMs`). Rejects with a
|
|
268
|
+
* {@link RalioRegistrationError} if the binding is rejected, expires, or the
|
|
269
|
+
* timeout elapses.
|
|
270
|
+
*/
|
|
271
|
+
declare function register(opts: RegisterOptions): Promise<CredentialBinding>;
|
|
272
|
+
|
|
273
|
+
/** Exception hierarchy for the Ralio SDK. */
|
|
274
|
+
/** Base class for every error raised by the SDK. */
|
|
275
|
+
declare class RalioError extends Error {
|
|
276
|
+
constructor(message: string);
|
|
277
|
+
}
|
|
278
|
+
/** Local configuration problem — missing key, bad arguments. */
|
|
279
|
+
declare class RalioConfigError extends RalioError {
|
|
280
|
+
}
|
|
281
|
+
/** A credential-binding registration was rejected, expired, or timed out. */
|
|
282
|
+
declare class RalioRegistrationError extends RalioError {
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* An error response from the Ralio API.
|
|
286
|
+
*
|
|
287
|
+
* Carries the HTTP `statusCode`, the server-supplied `detail` string, and the
|
|
288
|
+
* `WWW-Authenticate` challenge when present (DPoP/OAuth failures put the
|
|
289
|
+
* specific reason there).
|
|
290
|
+
*/
|
|
291
|
+
declare class RalioAPIError extends RalioError {
|
|
292
|
+
readonly statusCode: number;
|
|
293
|
+
readonly detail: string | null;
|
|
294
|
+
readonly wwwAuthenticate: string | null;
|
|
295
|
+
constructor(message: string, opts: {
|
|
296
|
+
statusCode: number;
|
|
297
|
+
detail?: string | null;
|
|
298
|
+
wwwAuthenticate?: string | null;
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
/** 401 — missing/invalid token, failed client assertion, or rejected proof. */
|
|
302
|
+
declare class RalioAuthError extends RalioAPIError {
|
|
303
|
+
}
|
|
304
|
+
/** 403 — token lacks the required scope, or the resource isn't owned. */
|
|
305
|
+
declare class RalioPermissionError extends RalioAPIError {
|
|
306
|
+
}
|
|
307
|
+
/** 404 — resource does not exist. */
|
|
308
|
+
declare class RalioNotFoundError extends RalioAPIError {
|
|
309
|
+
}
|
|
310
|
+
/** 422 — invalid field values or a business-rule violation. */
|
|
311
|
+
declare class RalioValidationError extends RalioAPIError {
|
|
312
|
+
}
|
|
313
|
+
/** 429 — rate limited. Back off and retry. */
|
|
314
|
+
declare class RalioRateLimitError extends RalioAPIError {
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export { type ChatParams, type ChatReply, type ChatStreamEvent, type CredentialBinding, DEFAULT_BASE_URL, type ListTransactionsParams, type Message, RalioAPIError, RalioAuthError, RalioClient, type RalioClientOptions, RalioConfigError, RalioError, RalioNotFoundError, RalioPermissionError, RalioRateLimitError, RalioRegistrationError, RalioValidationError, type RegisterOptions, type Transaction, register };
|