@zeroxyz/sdk 0.5.0 → 0.6.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/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@ All notable changes to `@zeroxyz/sdk` will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) — with the pre-1.0 caveat that minor versions may include breaking changes.
6
6
 
7
+ ## 0.6.0
8
+
9
+ ### Added
10
+
11
+ - **Envelope-aware `client.fetch()` routing.** When a request body is Zero's HTTP-transport envelope (`{ input: { type:"http", method, queryParams, body } }` — how many caps store their request contract), `fetch()` now normalizes it into a real wire request: the verb is **carried** (explicit `method` → the envelope's `input.method` → body-presence, in that order) rather than inferred from body-presence, and a GET's `queryParams` are moved onto the URL query string with the body dropped. This stops the SDK from POSTing the ~6,614 indexed GET capabilities that store an MPP-style envelope on an x402 GET endpoint (which 404'd with "Cannot POST"). The envelope is never a wire format, so the normalization is unconditional and protocol-agnostic. (#677)
12
+ - New pure exports for envelope/schema handling, lifted out of the CLI so SDK-direct consumers get them too: `extractInputEnvelope`, `unwrapTransportEnvelope`, `normalizeTransportEnvelopeRequest`, `asSchemaNode`, and the `JsonSchemaNode` type. (#677)
13
+ - **`client.runs.get(runId)`** — fetch a single run's detail. New exports `RunDetail` and `runDetailSchema`. (#667)
14
+
15
+ ## 0.5.0
16
+
17
+ ### Added
18
+
19
+ - Direct-URL `client.fetch()` calls now record a run via a server-side URL→capability resolve, so a bare URL that missed the local cache is still attributable without the caller supplying a `capabilityId`. The resolve runs concurrently with the probe (no added latency) and is fail-open. (#558, ZERO-335)
20
+
7
21
  ## 0.4.0
8
22
 
9
23
  ### Added
package/README.md CHANGED
@@ -104,6 +104,18 @@ Endpoints that don't charge pass straight through: a `200` on the first request
104
104
 
105
105
  **Building the request — search → get → fetch.** A `search()` result carries the `url` and `method`, so a no-body `GET` capability can go straight to `fetch()`. For anything that takes a request body, call `capabilities.get()` first: its `bodySchema`, `method`, and `headers` are what tell you how to construct the call. Don't guess the body from the search summary.
106
106
 
107
+ **The HTTP-transport envelope.** Some capabilities store their request contract as Zero's HTTP-transport envelope — `{ input: { type: "http", method, queryParams, body } }` — rather than a bare body. You don't have to special-case it: `fetch()` detects an envelope body and normalizes it to the real wire request — it **carries the method** (from the envelope's `input.method`, never inferring `POST` from body-presence) and moves a `GET`'s `queryParams` onto the URL query string. So passing the envelope straight through works:
108
+
109
+ ```ts
110
+ // A GET capability whose schema is the envelope — fetch() routes it to
111
+ // GET https://…/weather/current?city=tokyo (not a POST).
112
+ await client.fetch(cap.url, {
113
+ body: JSON.stringify({ input: { type: "http", method: "GET", queryParams: { city: "tokyo" } } }),
114
+ });
115
+ ```
116
+
117
+ If you'd rather inspect the schema yourself, the SDK exports the pure helpers it uses: `extractInputEnvelope(bodySchema, method)` (the query-param / body schema nodes), `unwrapTransportEnvelope(exampleRequest)` (the inner request from a stored example), and `normalizeTransportEnvelopeRequest(url, { method, body })` (the `{ url, method, body }` the wire request should use).
118
+
107
119
  ```ts
108
120
  const result = await client.fetch(url, {
109
121
  method: "POST",
@@ -126,6 +126,78 @@ var ZeroConfigurationError = class extends ZeroError {
126
126
  }
127
127
  };
128
128
 
129
+ // src/input-envelope.ts
130
+ var asSchemaNode = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : null;
131
+ var extractInputEnvelope = (bodySchema, method) => {
132
+ const root = asSchemaNode(bodySchema);
133
+ if (!root) return {};
134
+ const props = asSchemaNode(root.properties);
135
+ if (!props) return {};
136
+ const inputProps = asSchemaNode(asSchemaNode(props.input)?.properties);
137
+ if (inputProps) {
138
+ return {
139
+ queryParams: asSchemaNode(inputProps.queryParams) ?? void 0,
140
+ body: asSchemaNode(inputProps.body) ?? void 0
141
+ };
142
+ }
143
+ const upperMethod = method.toUpperCase();
144
+ const isGetLike = upperMethod === "GET" || upperMethod === "DELETE";
145
+ return isGetLike ? { queryParams: root } : { body: root };
146
+ };
147
+ var unwrapTransportEnvelope = (request2) => {
148
+ const input = asSchemaNode(asSchemaNode(request2)?.input);
149
+ if (!input || input.type !== "http") return request2;
150
+ const body = input.body;
151
+ const hasBody = body !== void 0 && body !== null && !(typeof body === "object" && Object.keys(body).length === 0);
152
+ if (hasBody) return body;
153
+ const qp = asSchemaNode(input.queryParams);
154
+ if (qp && Object.keys(qp).length > 0) return qp;
155
+ return null;
156
+ };
157
+ var GET_LIKE = /* @__PURE__ */ new Set(["GET", "HEAD", "DELETE"]);
158
+ var appendQueryParams = (url, params) => {
159
+ const entries = Object.entries(params).filter(([, v]) => v != null);
160
+ if (entries.length === 0) return url;
161
+ try {
162
+ const u = new URL(url);
163
+ for (const [k, v] of entries) u.searchParams.set(k, String(v));
164
+ return u.toString();
165
+ } catch {
166
+ const qs = entries.map(
167
+ ([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`
168
+ ).join("&");
169
+ return url.includes("?") ? `${url}&${qs}` : `${url}?${qs}`;
170
+ }
171
+ };
172
+ var normalizeTransportEnvelopeRequest = (url, opts) => {
173
+ const { method, body } = opts;
174
+ if (typeof body !== "string") return { url, method, body };
175
+ let parsed;
176
+ try {
177
+ parsed = JSON.parse(body);
178
+ } catch {
179
+ return { url, method, body };
180
+ }
181
+ const input = asSchemaNode(asSchemaNode(parsed)?.input);
182
+ if (!input || input.type !== "http") return { url, method, body };
183
+ const innerBody = input.body;
184
+ const hasInnerBody = innerBody !== void 0 && innerBody !== null && !(typeof innerBody === "object" && Object.keys(innerBody).length === 0);
185
+ const verb = (method ?? (typeof input.method === "string" ? input.method : void 0) ?? (hasInnerBody ? "POST" : "GET")).toUpperCase();
186
+ if (GET_LIKE.has(verb)) {
187
+ const qp = asSchemaNode(input.queryParams);
188
+ return {
189
+ url: qp ? appendQueryParams(url, qp) : url,
190
+ method: verb,
191
+ body: void 0
192
+ };
193
+ }
194
+ return {
195
+ url,
196
+ method: verb,
197
+ body: hasInnerBody ? JSON.stringify(innerBody) : void 0
198
+ };
199
+ };
200
+
129
201
  // src/chains/eip712.ts
130
202
  var KNOWN_EIP712_DOMAINS = {
131
203
  // Base mainnet USDC. On-chain `name()` is "USD Coin" (NOT "USDC")
@@ -1144,7 +1216,7 @@ var Payments = class {
1144
1216
 
1145
1217
  // package.json
1146
1218
  var package_default = {
1147
- version: "0.5.0"};
1219
+ version: "0.6.0"};
1148
1220
 
1149
1221
  // src/version.ts
1150
1222
  var SDK_VERSION = package_default.version;
@@ -1872,6 +1944,30 @@ var listRunsResponseSchema = z.object({
1872
1944
  runs: z.array(runListItemSchema),
1873
1945
  nextCursor: z.string().nullable()
1874
1946
  });
1947
+ var runDetailSchema = z.object({
1948
+ runId: z.string(),
1949
+ status: z.number().nullable(),
1950
+ latencyMs: z.number().nullable(),
1951
+ errorClass: z.string().nullable(),
1952
+ createdAt: z.coerce.date(),
1953
+ cost: z.object({ amount: z.string(), asset: z.string().nullable() }).nullable(),
1954
+ payment: z.object({
1955
+ protocol: z.string(),
1956
+ chain: z.string().nullable(),
1957
+ txHash: z.string().nullable(),
1958
+ mode: z.string().nullable()
1959
+ }).nullable(),
1960
+ capability: z.object({
1961
+ uid: z.string(),
1962
+ slug: z.string(),
1963
+ name: z.string(),
1964
+ url: z.string(),
1965
+ method: z.string(),
1966
+ whatItDoes: z.string().nullable(),
1967
+ tags: z.array(z.string())
1968
+ }),
1969
+ reviewed: z.boolean()
1970
+ });
1875
1971
  var createReviewResponseSchema = z.object({
1876
1972
  reviewId: z.string(),
1877
1973
  recorded: z.boolean(),
@@ -1953,6 +2049,15 @@ var Runs = class {
1953
2049
  listRunsResponseSchema
1954
2050
  );
1955
2051
  };
2052
+ get = (runId, opts = {}) => request(
2053
+ this.client,
2054
+ {
2055
+ method: "GET",
2056
+ path: `/v1/runs/${runId}`,
2057
+ signal: opts.signal
2058
+ },
2059
+ runDetailSchema
2060
+ );
1956
2061
  review = (input, opts = {}) => request(
1957
2062
  this.client,
1958
2063
  {
@@ -2483,17 +2588,23 @@ var withDefaultContentType = (headers, body) => {
2483
2588
  var clientFetch = async (client, url, opts = {}) => {
2484
2589
  const startedAt = Date.now();
2485
2590
  const capabilityIdRequested = opts.capabilityId !== void 0;
2486
- const method = opts.method ?? (opts.body !== void 0 ? "POST" : "GET");
2591
+ const normalized = normalizeTransportEnvelopeRequest(url, {
2592
+ method: opts.method,
2593
+ body: opts.body
2594
+ });
2595
+ const requestUrl = normalized.url;
2596
+ const requestBody = normalized.body;
2597
+ const method = normalized.method ?? (requestBody !== void 0 ? "POST" : "GET");
2487
2598
  const { canRecord: canRecordForResolve } = resolveRecordingClient(
2488
2599
  client,
2489
2600
  opts
2490
2601
  );
2491
- const resolvePromise = !capabilityIdRequested && canRecordForResolve ? client.capabilities.resolveByUrl(redactUrl(url), method).catch(() => null) : Promise.resolve(null);
2492
- const probeHeaders = withDefaultContentType(opts.headers, opts.body);
2602
+ const resolvePromise = !capabilityIdRequested && canRecordForResolve ? client.capabilities.resolveByUrl(redactUrl(requestUrl), method).catch(() => null) : Promise.resolve(null);
2603
+ const probeHeaders = withDefaultContentType(opts.headers, requestBody);
2493
2604
  const requestInit = {
2494
2605
  method,
2495
2606
  headers: probeHeaders,
2496
- body: opts.body,
2607
+ body: requestBody,
2497
2608
  signal: opts.signal
2498
2609
  };
2499
2610
  const configuredFetch = buildConfiguredFetch(client, {
@@ -2504,7 +2615,7 @@ var clientFetch = async (client, url, opts = {}) => {
2504
2615
  const skipReasons = [];
2505
2616
  const warnings = [];
2506
2617
  try {
2507
- response = await configuredFetch(url, requestInit);
2618
+ response = await configuredFetch(requestUrl, requestInit);
2508
2619
  } catch (err) {
2509
2620
  if (err instanceof ZeroTimeoutError) {
2510
2621
  await recordTimeoutRun(
@@ -2534,13 +2645,13 @@ var clientFetch = async (client, url, opts = {}) => {
2534
2645
  const payCall = requirement.protocol === "x402" ? client.payments.pay : client.payments.payMpp;
2535
2646
  try {
2536
2647
  const paid = await payCall({
2537
- url,
2648
+ url: requestUrl,
2538
2649
  method,
2539
2650
  // Fresh clone for the paid retry — same rationale as
2540
2651
  // the probe-side clone above. Re-derives the default
2541
2652
  // Content-Type so the paid leg matches the probe.
2542
- headers: withDefaultContentType(opts.headers, opts.body),
2543
- body: opts.body,
2653
+ headers: withDefaultContentType(opts.headers, requestBody),
2654
+ body: requestBody,
2544
2655
  maxPay: opts.maxPay,
2545
2656
  account: opts.account,
2546
2657
  signal: opts.signal,
@@ -3188,6 +3299,6 @@ var ZeroClient = class _ZeroClient {
3188
3299
  };
3189
3300
  };
3190
3301
 
3191
- export { Auth, AuthDevice, BUG_REPORT_CATEGORIES, BugReports, Capabilities, DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT_MS, FETCH_SKIP_REASONS, FETCH_WARNINGS, Payments, Runs, SDK_VERSION, TEMPO_CHAIN_ID, TEMPO_TESTNET_CHAIN_ID, Wallet, ZeroApiError, ZeroAuthError, ZeroClient, ZeroConfigurationError, ZeroError, ZeroPaymentError, ZeroSessionCloseFailedError, ZeroTimeoutError, ZeroValidationError, ZeroWalletError, coerceTempoChainId, createManagedAccount, paymentHasAnchor, tempoChainLabelFromId };
3192
- //# sourceMappingURL=chunk-RY3VFSIE.js.map
3193
- //# sourceMappingURL=chunk-RY3VFSIE.js.map
3302
+ export { Auth, AuthDevice, BUG_REPORT_CATEGORIES, BugReports, Capabilities, DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT_MS, FETCH_SKIP_REASONS, FETCH_WARNINGS, Payments, Runs, SDK_VERSION, TEMPO_CHAIN_ID, TEMPO_TESTNET_CHAIN_ID, Wallet, ZeroApiError, ZeroAuthError, ZeroClient, ZeroConfigurationError, ZeroError, ZeroPaymentError, ZeroSessionCloseFailedError, ZeroTimeoutError, ZeroValidationError, ZeroWalletError, asSchemaNode, coerceTempoChainId, createManagedAccount, extractInputEnvelope, normalizeTransportEnvelopeRequest, paymentHasAnchor, tempoChainLabelFromId, unwrapTransportEnvelope };
3303
+ //# sourceMappingURL=chunk-3YKWO4B2.js.map
3304
+ //# sourceMappingURL=chunk-3YKWO4B2.js.map