Sign In

@clervo/sdk

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@clervo/sdk - npm Package Compare versions

Comparing version
0.2.0
to
0.3.0
+78
dist/index.d.ts
export declare const CLERVO_CONTRACT_VERSION: '2026-07-29.1';
export declare const CLERVO_RELEASE_CANDIDATE_ID: 'clervo-private-core-2026-08-02.2';
export declare const CLERVO_RELEASE_CANDIDATE_INTERFACE_HASH: 'sha256:1b32a86f5725499f90d3e2f167f4432563f67bac477a3ca0e552f0958bf26622';
export type ClervoProductId = 'search.web' | 'search.answer';
export type ClervoExecutionMode = 'preview' | 'challenge';
export interface ClervoSearchRequest {
query: string;
maxResults?: number;
language?: string;
region?: string;
}
export interface ClervoSearchResult {
contractVersion: typeof CLERVO_CONTRACT_VERSION;
operationId: string;
operation: 'search.query';
productId: ClervoProductId;
state: 'RECEIPTED';
replayed: boolean;
fundingMode: 'free' | 'paid';
requestHash: string;
output: {
searchResponse: Record<string, unknown>;
synthesisReport?: Record<string, unknown>;
};
receipt?: unknown;
}
export interface ClervoProblem {
type?: string;
title?: string;
status?: number;
detail?: string;
instance?: string;
code?: string;
operationId?: string;
retryable?: boolean;
[key: string]: unknown;
}
export interface ClervoRequestOptions {
idempotencyKey?: string;
mode?: ClervoExecutionMode;
signal?: AbortSignal;
}
export interface ClervoClientOptions {
baseUrl: string;
fetch?: typeof fetch;
maxResponseBytes?: number;
}
export type ClervoRecoveryCode = 'insufficient_funds' | 'wrong_network_or_asset' | 'expired_quote' | 'rejected' | 'timeout' | 'unknown_settlement';
export interface ClervoRecoveryAction {
code: ClervoRecoveryCode;
action: string;
retry: 'after_action' | 'prohibited_until_reconciled';
}
export declare function recoveryActionFor(value: unknown): Readonly<ClervoRecoveryAction> | undefined;
export declare class ClervoError extends Error {
constructor(message: string, options?: ErrorOptions);
}
export declare class ClervoTransportError extends ClervoError {
}
export declare class ClervoProtocolError extends ClervoError {
}
export declare class ClervoProblemError extends ClervoError {
readonly status: number;
readonly problem: ClervoProblem;
constructor(status: number, problem: ClervoProblem);
}
export declare class ClervoPaymentRequiredError extends ClervoProblemError {
readonly paymentRequired: string | null;
constructor(problem: ClervoProblem, paymentRequired: string | null);
}
export declare class ClervoClient {
#private;
readonly search: {
web: (request: ClervoSearchRequest, options?: ClervoRequestOptions) => Promise<ClervoSearchResult>;
answer: (request: ClervoSearchRequest, options?: ClervoRequestOptions) => Promise<ClervoSearchResult>;
};
constructor(options: ClervoClientOptions);
}
export const CLERVO_CONTRACT_VERSION = '2026-07-29.1';
export const CLERVO_RELEASE_CANDIDATE_ID = 'clervo-private-core-2026-08-02.2';
export const CLERVO_RELEASE_CANDIDATE_INTERFACE_HASH = 'sha256:1b32a86f5725499f90d3e2f167f4432563f67bac477a3ca0e552f0958bf26622';
const recoveryActions = Object.freeze([
Object.freeze({
code: 'insufficient_funds',
problemCodes: Object.freeze(['insufficient_funds']),
action: 'Add enough of the quoted asset on the quoted network, then request a fresh quote.',
retry: 'after_action',
}),
Object.freeze({
code: 'wrong_network_or_asset',
problemCodes: Object.freeze(['wrong_network', 'wrong_asset', 'unsupported_network', 'unsupported_asset']),
action: "Switch to the quote's exact network and asset, then request a fresh quote.",
retry: 'after_action',
}),
Object.freeze({
code: 'expired_quote',
problemCodes: Object.freeze(['quote_expired', 'expired_quote']),
action: 'Request a fresh quote and never reuse the expired authorization.',
retry: 'after_action',
}),
Object.freeze({
code: 'rejected',
problemCodes: Object.freeze(['authorization_rejected', 'payment_rejected', 'user_rejected']),
action: 'Review the maximum charge and approve again only if you still intend to pay.',
retry: 'after_action',
}),
Object.freeze({
code: 'timeout',
problemCodes: Object.freeze(['authorization_timeout', 'payment_timeout']),
action: 'Reconcile the existing idempotency key before deciding whether to retry.',
retry: 'prohibited_until_reconciled',
}),
Object.freeze({
code: 'unknown_settlement',
problemCodes: Object.freeze(['settlement_unknown', 'unknown_settlement']),
action: 'Reconcile the existing operation and do not authorize or retry until settlement is definitive.',
retry: 'prohibited_until_reconciled',
}),
]);
export function recoveryActionFor(value) {
const problemCode = value instanceof ClervoProblemError && typeof value.problem.code === 'string'
? value.problem.code
: typeof value === 'string' ? value : undefined;
if (problemCode === undefined)
return undefined;
const recovery = recoveryActions.find(({ problemCodes }) => problemCodes.includes(problemCode));
if (recovery === undefined)
return undefined;
return Object.freeze({ code: recovery.code, action: recovery.action, retry: recovery.retry });
}
function assertBaseUrl(value) {
let parsed;
try {
parsed = new URL(value);
}
catch {
throw new TypeError('invalid_clervo_base_url');
}
const loopback = parsed.hostname === 'localhost'
|| parsed.hostname === '127.0.0.1'
|| parsed.hostname === '[::1]';
if (parsed.protocol !== 'https:' && !(parsed.protocol === 'http:' && loopback))
throw new TypeError('unsafe_clervo_base_url');
if (parsed.username || parsed.password || parsed.search || parsed.hash)
throw new TypeError('invalid_clervo_base_url');
return parsed.toString().replace(/\/+$/u, '');
}
function assertSearchRequest(request) {
if (request === null || typeof request !== 'object' || Array.isArray(request))
throw new TypeError('invalid_search_request');
if (typeof request.query !== 'string'
|| request.query.trim().length < 1
|| request.query.trim().length > 2_000
|| /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/u.test(request.query))
throw new TypeError('invalid_search_query');
if (request.maxResults !== undefined && (!Number.isInteger(request.maxResults) || request.maxResults < 1 || request.maxResults > 10))
throw new TypeError('invalid_search_max_results');
if (request.language !== undefined && (typeof request.language !== 'string' || !/^[a-z]{2,3}$/u.test(request.language)))
throw new TypeError('invalid_search_language');
if (request.region !== undefined && (typeof request.region !== 'string' || !/^[A-Z]{2}$/u.test(request.region)))
throw new TypeError('invalid_search_region');
}
function idempotencyKey() {
return `clervo_${crypto.randomUUID()}`;
}
async function readResponseText(response, maximumBytes) {
const declared = Number(response.headers.get('content-length'));
if (Number.isFinite(declared) && declared > maximumBytes)
throw new ClervoProtocolError('clervo_response_too_large');
if (response.body === null)
return '';
const reader = response.body.getReader();
const decoder = new TextDecoder();
const parts = [];
let bytes = 0;
for (;;) {
const { done, value } = await reader.read();
if (done)
break;
bytes += value.byteLength;
if (bytes > maximumBytes) {
await reader.cancel();
throw new ClervoProtocolError('clervo_response_too_large');
}
parts.push(decoder.decode(value, { stream: true }));
}
parts.push(decoder.decode());
return parts.join('');
}
function parseJsonObject(text) {
let value;
try {
value = JSON.parse(text);
}
catch {
throw new ClervoProtocolError('clervo_response_invalid_json');
}
if (value === null || typeof value !== 'object' || Array.isArray(value))
throw new ClervoProtocolError('clervo_response_invalid_shape');
return value;
}
function validateResult(value, productId, fundingMode) {
if (value.contractVersion !== CLERVO_CONTRACT_VERSION
|| value.operation !== 'search.query'
|| value.productId !== productId
|| value.state !== 'RECEIPTED'
|| value.fundingMode !== fundingMode
|| typeof value.operationId !== 'string'
|| typeof value.requestHash !== 'string'
|| typeof value.replayed !== 'boolean'
|| value.output === null
|| typeof value.output !== 'object'
|| Array.isArray(value.output))
throw new ClervoProtocolError('clervo_result_contract_mismatch');
return value;
}
export class ClervoError extends Error {
constructor(message, options) {
super(message, options);
this.name = new.target.name;
}
}
export class ClervoTransportError extends ClervoError {
}
export class ClervoProtocolError extends ClervoError {
}
export class ClervoProblemError extends ClervoError {
status;
problem;
constructor(status, problem) {
super(typeof problem.code === 'string' ? problem.code : `clervo_http_${status}`);
this.status = status;
this.problem = problem;
}
}
export class ClervoPaymentRequiredError extends ClervoProblemError {
paymentRequired;
constructor(problem, paymentRequired) {
super(402, problem);
this.paymentRequired = paymentRequired;
}
}
export class ClervoClient {
#baseUrl;
#fetch;
#maxResponseBytes;
search;
constructor(options) {
if (options === null || typeof options !== 'object')
throw new TypeError('invalid_clervo_client_options');
this.#baseUrl = assertBaseUrl(options.baseUrl);
this.#fetch = options.fetch ?? globalThis.fetch;
if (typeof this.#fetch !== 'function')
throw new TypeError('clervo_fetch_unavailable');
this.#maxResponseBytes = options.maxResponseBytes ?? 2_097_152;
if (!Number.isInteger(this.#maxResponseBytes) || this.#maxResponseBytes < 1_024 || this.#maxResponseBytes > 16_777_216)
throw new TypeError('invalid_clervo_response_limit');
this.search = Object.freeze({
web: (request, requestOptions) => this.#execute('search.web', request, requestOptions),
answer: (request, requestOptions) => this.#execute('search.answer', request, requestOptions),
});
}
async #execute(productId, request, options = {}) {
assertSearchRequest(request);
if (options === null || typeof options !== 'object')
throw new TypeError('invalid_clervo_request_options');
const mode = options.mode ?? 'preview';
if (mode !== 'preview' && mode !== 'challenge')
throw new TypeError('invalid_clervo_execution_mode');
const fundingMode = mode === 'preview' ? 'free' : 'paid';
const target = mode === 'preview' ? '/v1/search/free' : '/v1/search/paid';
const requestIdempotencyKey = options.idempotencyKey ?? idempotencyKey();
if (!/^[\x21-\x7E]{8,128}$/u.test(requestIdempotencyKey))
throw new TypeError('invalid_idempotency_key');
const body = {
query: request.query.trim(),
...(request.maxResults === undefined ? {} : { maxResults: request.maxResults }),
synthesize: productId === 'search.answer',
...(request.language === undefined ? {} : { language: request.language }),
...(request.region === undefined ? {} : { region: request.region }),
};
let response;
try {
response = await this.#fetch(`${this.#baseUrl}${target}`, {
method: 'POST',
headers: {
accept: 'application/json, application/problem+json',
'content-type': 'application/json',
'idempotency-key': requestIdempotencyKey,
'x-clervo-client': '@clervo/sdk/0.3.0',
},
body: JSON.stringify(body),
redirect: 'error',
...(options.signal === undefined ? {} : { signal: options.signal }),
});
}
catch (error) {
throw new ClervoTransportError('clervo_transport_failed', { cause: error });
}
const text = await readResponseText(response, this.#maxResponseBytes);
const contentType = response.headers.get('content-type')?.split(';', 1)[0]?.trim().toLowerCase();
if (contentType !== undefined && contentType !== 'application/json' && contentType !== 'application/problem+json')
throw new ClervoProtocolError('clervo_response_unsupported_media_type');
const value = parseJsonObject(text);
if (response.status === 402)
throw new ClervoPaymentRequiredError(value, response.headers.get('payment-required'));
if (!response.ok)
throw new ClervoProblemError(response.status, value);
return validateResult(value, productId, fundingMode);
}
}
//# sourceMappingURL=index.js.map
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
+42
-8
{
"name": "@clervo/sdk",
"version": "0.2.0",
"description": "Clervo x402 Gateway SDK — 23 AI models, 8 free. Pay per call in USDC.",
"version": "0.3.0",
"description": "Typed Clervo client generated from the frozen distribution candidate.",
"license": "UNLICENSED",
"type": "module",
"main": "./src/index.js",
"types": "./src/index.d.ts",
"keywords": ["clervo", "x402", "ai", "claude", "gpt", "deepseek", "llm", "usdc", "solana", "openai-compatible"],
"license": "MIT",
"engines": { "node": ">=18" },
"files": ["src/", "README.md"]
"sideEffects": false,
"keywords": [
"clervo",
"agent",
"search",
"sdk"
],
"homepage": "https://clervo.dev/docs/typescript",
"bugs": {
"url": "https://github.com/clervo/clervo/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/clervo/clervo.git",
"directory": "packages/sdk-typescript"
},
"engines": {
"node": ">=20"
},
"files": [
"dist",
"README.md"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc --project tsconfig.json",
"prepack": "npm run build"
},
"publishConfig": {
"access": "public",
"provenance": true
}
}
+23
-108

@@ -1,121 +0,36 @@

# @clervo/sdk
# `@clervo/sdk`
TypeScript/JavaScript SDK for the [Clervo x402 Gateway](https://api.clervo.dev).
Typed client for Clervo's frozen distribution candidate.
23 AI models. 8 free. Pay per call in USDC on Solana. No API keys.
Current scope is deliberately narrow:
## Install
- `search.web` and `search.answer`;
- repository-local preview execution;
- typed non-payable `402` challenges;
- idempotency and request cancellation;
- no wallet, signer, payment retry, or public-service assumption.
```bash
npm install @clervo/sdk
```
The client requires an explicit `baseUrl` because no public API deployment is
currently verified.
## Quick start
```ts
import { ClervoClient } from '@clervo/sdk';
```typescript
import { Clervo } from '@clervo/sdk';
const clervo = new Clervo();
// Free call — no wallet needed
const response = await clervo.chat('groq/llama-3.1-8b-instant', 'Explain recursion in 2 sentences');
console.log(response);
// List all models
const models = await clervo.models();
const free = models.filter(m => m.lifecycle === 'free_beta');
console.log(`${free.length} free models available`);
const clervo = new ClervoClient({ baseUrl: 'http://127.0.0.1:8080' });
const result = await clervo.search.web({ query: 'payment idempotency' });
```
## Free models
`search.answer` sets synthesis explicitly. Callers cannot silently change the
product identity through the request body.
No payment, no wallet, no signup:
Known future payment failures can be reduced to one bounded next action without
triggering a payment or retry:
| Model | Speed | Best for |
|-------|-------|----------|
| `groq/llama-3.1-8b-instant` | 170ms | Fast responses |
| `groq/llama-3.3-70b` | ~800ms | Strong general |
| `sambanova/deepseek-v3.2` | 1.6s | Reasoning |
| `sambanova/llama-3.3-70b` | 1.5s | General |
| `hcn/qwen3.6-35b` | 0.9s | Fast small |
| `hcn/step-3.7-flash` | 3s | Reasoning |
| `hcn/deepseek-v4-pro` | 9s | Deep reasoning |
| `hcn/auto` | 3s | Auto-routed |
```ts
import { recoveryActionFor } from '@clervo/sdk';
## Paid models (10-20% cheaper than BlockRun)
Fund a Solana wallet with USDC. x402 protocol handles payment automatically.
| Model | Price/req |
|-------|-----------|
| `tongkhokr/claude-haiku-4.5` | $0.002 |
| `tongkhokr/claude-sonnet-5` | $0.015 |
| `tongkhokr/claude-opus-5` | $0.084 |
| `quickai/gpt-5.4-mini` | $0.005 |
| `quickai/gpt-5.5` | $0.035 |
## API
### `new Clervo(options?)`
| Option | Default | Description |
|--------|---------|-------------|
| `apiUrl` | `https://api.clervo.dev` | API base URL |
### `clervo.chat(model, message, options?)`
Simple chat. Returns the text response.
```typescript
const text = await clervo.chat('groq/llama-3.1-8b-instant', 'Hello!');
const recovery = recoveryActionFor(error);
```
### `clervo.chatCompletion(model, messages, options?)`
Full OpenAI-compatible chat completion.
```typescript
const result = await clervo.chatCompletion('sambanova/deepseek-v3.2', [
{ role: 'system', content: 'You are helpful.' },
{ role: 'user', content: 'What is 2+2?' }
]);
console.log(result.choices[0].message.content);
console.log(result.usage);
```
### `clervo.models()`
List all available models.
### `clervo.freeModels()`
List only free models.
### `clervo.operation(operationId)`
Look up an operation by ID (from `_operationId` on completion results).
## OpenAI drop-in replacement
Since Clervo is OpenAI-compatible, you can also use the OpenAI SDK:
```typescript
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://api.clervo.dev/v1',
apiKey: 'unused', // no key needed for free models
});
const response = await client.chat.completions.create({
model: 'groq/llama-3.1-8b-instant',
messages: [{ role: 'user', content: 'Hello!' }],
});
```
## Links
- API: https://api.clervo.dev
- Models: https://api.clervo.dev/v1/models
- Quickstart: https://api.clervo.dev/quickstart.md
- OpenAPI: https://api.clervo.dev/openapi.json
Unknown settlement and payment-timeout actions prohibit retry until the
existing idempotency key is reconciled.
export interface ClervoOptions {
apiUrl?: string;
}
export interface ChatOptions {
system?: string;
maxTokens?: number;
}
export interface CompletionOptions {
maxTokens?: number;
responseFormat?: Record<string, unknown>;
}
export interface Message {
role: 'system' | 'user' | 'assistant';
content: string;
}
export interface ChatCompletion {
id: string;
object: string;
model: string;
choices: Array<{
index: number;
message: { role: string; content: string };
finish_reason: string | null;
}>;
usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };
_operationId?: string;
_replay?: boolean;
}
export interface Model {
id: string;
object: string;
owned_by: string;
name: string;
description: string;
lifecycle: string;
callable: boolean;
purchasable: boolean;
capabilities: string[];
funding: { mode: string } | null;
pricing: Record<string, unknown> | null;
}
export declare class Clervo {
constructor(options?: ClervoOptions);
chat(model: string, message: string, options?: ChatOptions): Promise<string>;
chatCompletion(model: string, messages: Message[], options?: CompletionOptions): Promise<ChatCompletion>;
models(): Promise<Model[]>;
freeModels(): Promise<Model[]>;
operation(operationId: string): Promise<Record<string, unknown> | null>;
}
export default Clervo;
/**
* @clervo/sdk — TypeScript/JavaScript SDK for Clervo x402 Gateway
*
* Usage:
* import { Clervo } from '@clervo/sdk';
* const ai = new Clervo();
*
* // Chat (free models — no payment needed)
* const text = await ai.chat('groq/llama-3.1-8b-instant', 'Hello!');
*
* // Search the web (free)
* const results = await ai.search('x402 protocol');
*
* // Scrape a URL to markdown (free)
* const markdown = await ai.scrape('https://example.com');
*
* // List all models
* const models = await ai.models();
*/
import { randomUUID } from 'node:crypto';
const DEFAULT_API = 'https://api.clervo.dev';
export class Clervo {
#apiUrl;
#defaultModel;
#maxRetries;
/**
* @param {object} [options]
* @param {string} [options.apiUrl] - API base URL (default: https://api.clervo.dev)
* @param {string} [options.defaultModel] - Default model for chat (default: groq/llama-3.1-8b-instant)
* @param {number} [options.maxRetries] - Max retries on 503 (default: 2)
*/
constructor({ apiUrl, defaultModel, maxRetries } = {}) {
this.#apiUrl = apiUrl || process.env.CLERVO_API_URL || DEFAULT_API;
this.#defaultModel = defaultModel || 'groq/llama-3.1-8b-instant';
this.#maxRetries = maxRetries ?? 2;
}
/**
* Simple chat — returns the text response.
* @param {string} modelOrMessage - Model ID, or message if using default model
* @param {string} [message] - User message (if first arg is model)
* @param {object} [options]
* @returns {Promise<string>} The assistant's text response
*/
async chat(modelOrMessage, message, { system, maxTokens = 1024, json = false } = {}) {
// Allow shorthand: ai.chat('Hello') uses default model
let model, userMessage;
if (message === undefined) {
model = this.#defaultModel;
userMessage = modelOrMessage;
} else {
model = modelOrMessage;
userMessage = message;
}
const result = await this.chatCompletion(model, [
...(system ? [{ role: 'system', content: system }] : []),
{ role: 'user', content: userMessage },
], { maxTokens, responseFormat: json ? { type: 'json_object' } : undefined });
return result.choices[0]?.message?.content || '';
}
/**
* Full OpenAI-compatible chat completion.
* @param {string} model - Model ID
* @param {Array} messages - OpenAI-format messages
* @param {object} [options]
* @returns {Promise<object>} Full completion response with usage, operationId
*/
async chatCompletion(model, messages, { maxTokens = 1024, responseFormat } = {}) {
const body = {
model,
messages,
max_completion_tokens: maxTokens,
...(responseFormat ? { response_format: responseFormat } : {}),
};
const result = await this.#request('POST', '/v1/chat/completions', body, {
headers: { 'idempotency-key': randomUUID() },
});
return result;
}
/**
* Web search — returns structured results.
* @param {string} query - Search query
* @param {object} [options]
* @param {number} [options.maxResults=5] - Number of results (1-10)
* @returns {Promise<Array<{title: string, url: string, snippet: string}>>}
*/
async search(query, { maxResults = 5 } = {}) {
const result = await this.#request('POST', '/v1/search', { query, max_results: maxResults });
return result.results || [];
}
/**
* Scrape URL to markdown.
* @param {string} url - URL to scrape
* @returns {Promise<string>} Page content as markdown
*/
async scrape(url) {
const result = await this.#request('POST', '/v1/scrape', { url });
return result.content || '';
}
/**
* List available models.
* @returns {Promise<Array>} All models with metadata
*/
async models() {
const r = await fetch(`${this.#apiUrl}/v1/models`);
const j = await r.json();
return j.data || [];
}
/**
* Get free models only.
* @returns {Promise<Array>} Free models (no payment needed)
*/
async freeModels() {
const all = await this.models();
return all.filter(m => m.lifecycle === 'free_beta');
}
/**
* Get paid models only.
* @returns {Promise<Array>} Paid models with pricing
*/
async paidModels() {
const all = await this.models();
return all.filter(m => m.lifecycle === 'paid_beta');
}
/**
* Get operation status/receipt.
* @param {string} operationId - Operation ID from x-clervo-operation-id header
* @returns {Promise<object|null>}
*/
async operation(operationId) {
const r = await fetch(`${this.#apiUrl}/v1/operations/${operationId}`);
if (r.status === 404) return null;
return r.json();
}
/**
* Internal request helper with retries.
*/
async #request(method, path, body, { headers = {} } = {}) {
let lastError;
for (let attempt = 0; attempt <= this.#maxRetries; attempt++) {
if (attempt > 0) await new Promise(r => setTimeout(r, 1000 * attempt));
const r = await fetch(`${this.#apiUrl}${path}`, {
method,
headers: { 'content-type': 'application/json', ...headers },
body: JSON.stringify(body),
});
if (r.status === 402) {
const err = new Error(`Payment required. Use a free model or fund a wallet with USDC on Base.`);
err.code = 'PAYMENT_REQUIRED';
err.status = 402;
throw err;
}
if (r.status === 503 && attempt < this.#maxRetries) {
lastError = new Error('Service temporarily unavailable');
continue;
}
if (r.status !== 200) {
const j = await r.json().catch(() => ({}));
const err = new Error(j.error?.message || j.message || `API error: ${r.status}`);
err.code = j.error?.code || 'API_ERROR';
err.status = r.status;
throw err;
}
const result = await r.json();
result._operationId = r.headers.get('x-clervo-operation-id') || undefined;
result._replay = r.headers.get('x-idempotent-replay') === 'true';
return result;
}
throw lastError;
}
}
export default Clervo;