@piprail/sdk 1.11.0 → 1.13.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,45 @@ All notable changes to `@piprail/sdk` are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
5
5
  versions follow [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [1.13.0] — 2026-06-10
8
+
9
+ ### Added — gate `discovery` option (one flag → x402scan-listable)
10
+ - **`createPaymentGate`/`requirePayment` now take an opt-in `discovery` option** that emits an
11
+ `extensions.bazaar` block **in the 402 challenge itself** — so the gate alone satisfies x402scan's
12
+ mandatory input-schema check (no separately-served file needed). `discovery: true` for a no-input
13
+ GET, or a `DiscoveryDescriptor` (`{ method, queryParams, output }`). Omitting it leaves the challenge
14
+ byte-identical. New export `buildBazaarExtension` + the `DiscoveryDescriptor`/`BazaarExtension` types.
15
+ - `DomainClaim` now also surfaces `verificationToken`, and `verificationHash` is **always** populated —
16
+ from the API, or computed as `sha256(verificationToken)` — so an agent always has the exact bytes to serve.
17
+
18
+ ### Fixed — conformance bug hunt (20-agent audit, every finding verified against the live API)
19
+ - **`hostOf` returned `''` for a bare `host:port`** (`new URL('x.com:8080')` parses the host as a scheme) —
20
+ `claimDomain`/`verifyDomain` now extract the host correctly.
21
+ - **`indexes.ts` base64 was Latin1-only** (`btoa`) — now UTF-8-safe, matching `x402.ts` (a non-ASCII SIWX
22
+ field could have thrown in the browser).
23
+ - **SIWX message** now reads `chainId` from `supportedChains[]` as a fallback (not only `info.chainId`),
24
+ so it always signs the correct `Chain ID`.
25
+ - Removed an invented `x-payment-info.bazaar:{discoverable:true}` marker from `buildOpenApi` (no index read it).
26
+ - Documented the caveat that the open indexes' agents are standard `exact` clients — advertise an `exact`
27
+ rail to be *payable*, not just listed (README + the `piprail_register` MCP tool).
28
+
29
+ ## [1.12.0] — 2026-06-09
30
+
31
+ ### Added — One-call domain verification (pending-review → searchable)
32
+ - **`client.verifyDomain()` takes a 402 Index listing all the way to searchable.** A self-registered
33
+ 402 Index listing is `pending-review`; verifying the domain you control approves it (and every other
34
+ pending listing on it). `client.claimDomain(urlOrDomain, { contactEmail? })` returns the
35
+ `verificationHash` to serve at your `/.well-known/402index-verify.txt`; `client.verifyDomain(urlOrDomain)`
36
+ then flips it live. Standalone forms `claim402IndexDomain` / `verify402IndexDomain` + the
37
+ `DomainClaim` / `DomainVerification` types are exported. Never throws; moves no funds.
38
+
39
+ ### Docs — a complete, agent-followable discovery playbook
40
+ - Rewrote the **"Be discoverable"** README section into a top-to-bottom **4-step playbook** an agent can
41
+ follow (list → verify domain → discover → self-describe), with the corrected lifecycle output
42
+ (`visibility` + `note`), a `DIRECTORY_INFO` reference table, and the caveats inline (402 Index is
43
+ pending-review; `discover()` doesn't read x402scan; x402scan needs an input schema). Updated
44
+ `llms-full.txt` to the same four moves (EMIT · REGISTER · VERIFY · DISCOVER).
45
+
7
46
  ## [1.11.0] — 2026-06-09
8
47
 
9
48
  ### Added — Agent-friendly discovery lifecycle
@@ -576,6 +615,8 @@ straight into your wallet. The API is small and self-contained.
576
615
  to your wallet; PipRail never holds funds.
577
616
  - `viem ^2.21` is a peer dependency. Node 20+ or a modern browser.
578
617
 
618
+ [1.13.0]: https://www.npmjs.com/package/@piprail/sdk
619
+ [1.12.0]: https://www.npmjs.com/package/@piprail/sdk
579
620
  [1.11.0]: https://www.npmjs.com/package/@piprail/sdk
580
621
  [1.10.0]: https://www.npmjs.com/package/@piprail/sdk
581
622
  [1.9.0]: https://www.npmjs.com/package/@piprail/sdk
package/README.md CHANGED
@@ -148,24 +148,38 @@ See [`examples/agent-tools.mjs`](../examples/agent-tools.mjs) for MCP / AI-SDK w
148
148
  A 402 endpoint is payable, but nobody can *find* it. PipRail closes that gap by building on the
149
149
  **open** x402 indexes that already exist (402 Index, the CDP Bazaar read API, x402scan) — **nothing
150
150
  PipRail-hosted, no registry, no database.** All opt-in; the pay path is untouched. There's **no PipRail
151
- account and no x402 sign-up anywhere** — the only thing ever registered is a merchant's own URL, by one
152
- call. The complete reference including a **step-by-step walkthrough of exactly what each step needs**
153
- (wallet? signing? sign-up? spoiler: 402 Index needs none) is **[DISCOVERY.md](./DISCOVERY.md)** (§7).
151
+ account and no x402 sign-up anywhere** — the only thing ever registered is a merchant's own URL.
152
+ **The four steps below are the whole playbook** an agent can follow them top to bottom (every method
153
+ never throws and returns a typed result that says what to do next); [DISCOVERY.md](./DISCOVERY.md) is
154
+ the deep reference.
154
155
 
155
156
  > **Experimental.** Discovery integrates with third-party open indexes whose conventions are young
156
157
  > and moving — treat this layer as experimental. The read path + 402 Index register are live-verified;
157
158
  > x402scan SIWX isn't yet. Note **402 Index probes your URL and only lists endpoints that actually
158
159
  > return a `402`** — so register a *deployed* gate, not a marketing page. (DISCOVERY.md §10 has the log.)
159
160
 
160
- **1) List a resource you run** — one call, no auth, no signature:
161
+ **1) List a resource you run** — one call, no auth, no signature, no funds:
161
162
 
162
163
  ```ts
163
164
  const client = new PipRailClient({ wallet: { privateKey: KEY }, chain: 'base' })
164
165
 
165
- await client.register('https://api.example.com/report', { name: 'Market Report', priceUsd: 0.05 })
166
- // → [{ source: '402index', ok: true, detail: 'Listed on 402 Index (searchable at 402index.io).' }]
167
- // Add targets: ['402index', 'x402scan'] to also register on x402scan via one wallet signature
168
- // (SIWX — Base/Solana only). It moves no funds; you're listing on third-party open directories.
166
+ const outcomes = await client.register('https://api.example.com/report', {
167
+ name: 'Market Report', priceUsd: 0.05, asset: 'USDC',
168
+ targets: ['402index', 'x402scan'], // 402index is the default; x402scan adds SIWX (Base/Solana)
169
+ })
170
+ // Each outcome carries its LIFECYCLE — read `visibility` + `note`. "ok:true" ≠ "searchable now":
171
+ // • 402index → { ok:true, visibility:'pending-review', note:'… verify your domain for instant approval' }
172
+ // • x402scan → { ok:true, visibility:'live', note:"… discover() does NOT read x402scan" }
173
+ ```
174
+
175
+ **2) Flip 402 Index `pending-review` → searchable** — verify the domain you control (no funds, no sign-up):
176
+
177
+ ```ts
178
+ const claim = await client.claimDomain('https://api.example.com/report', { contactEmail: 'you@example.com' })
179
+ // Serve claim.verificationHash as the ENTIRE body of claim.verificationUrl
180
+ // (https://api.example.com/.well-known/402index-verify.txt) — then:
181
+ await client.verifyDomain('api.example.com') // → { ok:true, status:'verified', servicesCount }
182
+ // Now every pending listing on that domain is approved + searchable.
169
183
  ```
170
184
 
171
185
  **Works on every chain.** 402 Index needs no signature and has no chain allowlist, so *any* chain —
@@ -179,7 +193,7 @@ request sends a `User-Agent: @piprail/sdk` — so the tech spreads through the f
179
193
  the logs operators read, never by spamming listings. An opt-in `register(url, { attribution: true })`
180
194
  adds a best-effort `via` tag; it's off by default (it's your listing).
181
195
 
182
- **2) Find resources to pay** — read the open indexes (free), filtered to your chain by default:
196
+ **3) Find resources to pay** — read the open indexes (free), filtered to your chain by default:
183
197
 
184
198
  ```ts
185
199
  const hits = await client.discover({ query: 'weather', maxPrice: 0.01 })
@@ -187,25 +201,51 @@ const hits = await client.discover({ query: 'weather', maxPrice: 0.01 })
187
201
  const res = await client.fetch(hits[0].resource) // then quote → plan → pay as usual
188
202
  ```
189
203
 
190
- `network` defaults to `'self'` (your chain only); pass `'any'` to search every chain, or a CAIP-2 id
191
- (`'eip155:8453'`) for a specific one. Chain slugs map to CAIP-2 through the `SLUG_TO_CAIP2` table —
192
- adding a chain adds one entry there (see [DISCOVERY.md](./DISCOVERY.md) §2.5), and an unresolved
193
- network is kept, never hidden.
204
+ `discover()` reads **402 Index + CDP Bazaar**, **not x402scan** (its reads are paid) a live x402scan
205
+ listing won't appear here, so don't read that absence as failure. `network` defaults to `'self'` (your
206
+ chain); pass `'any'` for every chain, or a CAIP-2 id (`'eip155:8453'`). Slugs map to CAIP-2 via
207
+ `SLUG_TO_CAIP2`; an unresolved network is kept, never hidden.
194
208
 
195
- **3) Emit a discovery file** turn your gate's config into the artifacts a crawler reads (pure, no
196
- I/O); serve the result as a static file on **your own** origin:
209
+ **4) Make your endpoint self-describing.** **x402scan REQUIRES an input schema or it won't list you.**
210
+ The simplest path: set the gate's `discovery` option it emits an `extensions.bazaar` block **in the 402
211
+ itself**, so the challenge alone is x402scan-listable (no extra file to serve):
197
212
 
198
213
  ```ts
199
- import { createPaymentGate, buildOpenApi, buildX402DnsTxt } from '@piprail/sdk'
200
-
201
- const gate = createPaymentGate({ chain: 'base', token: 'USDC', amount: '0.05', payTo })
202
- const openapi = buildOpenApi({
203
- origin: 'https://api.example.com',
204
- resources: [await gate.describe('https://api.example.com/report')],
214
+ // `discovery: true` for a no-input GET, or describe the request:
215
+ const gate = createPaymentGate({
216
+ chain: 'base', token: 'USDC', amount: '0.05', payTo,
217
+ exact: { settle: { facilitator: 'https://facilitator.payai.network' } }, // be payable by exact clients (see caveat)
218
+ discovery: { method: 'GET', output: { type: 'json', example: { ok: true } } },
205
219
  })
206
- // serve `openapi` at https://api.example.com/openapi.json — each priced op carries an `x-payment-info`
207
- // block (the field indexes crawl) plus a default root `x-generator` attribution stamp.
208
- // buildWellKnownX402(...) emits the legacy /.well-known/x402 file; buildX402DnsTxt(...) the _x402 DNS line
220
+ ```
221
+
222
+ Optionally also serve the static discovery files (richer listings; OpenAPI carries the `x-generator`
223
+ attribution stamp) from your own origin — all pure, no I/O:
224
+
225
+ ```ts
226
+ import { buildOpenApi, buildWellKnownX402, buildX402DnsTxt } from '@piprail/sdk'
227
+ const desc = await gate.describe('https://api.example.com/report')
228
+ const openapi = buildOpenApi({ origin: 'https://api.example.com', resources: [desc] }) // → /openapi.json
229
+ const wellKnown = buildWellKnownX402({ resources: [desc] }) // → /.well-known/x402
230
+ // buildX402DnsTxt(...) emits the _x402 DNS line too.
231
+ ```
232
+
233
+ > **Caveat — be *payable*, not just listed.** The open indexes' agents are overwhelmingly standard
234
+ > `exact` clients; a default `onchain-proof`-only gate gets listed but they can't pay it. Advertise an
235
+ > `exact` rail (above) so a discovered resource is actually payable.
236
+
237
+ **Know each index before you call** — the facts are one import, `DIRECTORY_INFO`, and `register()`
238
+ projects them onto every outcome (`visibility` + `note`), so an agent never has to guess:
239
+
240
+ | Index | Write auth | Chains | On a successful register | Read by `discover()`? |
241
+ |---|---|---|---|---|
242
+ | **402 Index** (default) | none | any | `pending-review` → `verifyDomain()` for instant approval | ✅ yes |
243
+ | **x402scan** | one wallet sig (SIWX) | Base / Solana | `live` on x402scan.com | ❌ no (paid reads) |
244
+ | **CDP Bazaar** | — (facilitator-only) | — | `not-listable` for PipRail (backendless) | ✅ read-only |
245
+
246
+ ```ts
247
+ import { DIRECTORY_INFO } from '@piprail/sdk'
248
+ DIRECTORY_INFO['x402scan'].readByDiscover // false — branch on this, don't guess
209
249
  ```
210
250
 
211
251
  For an LLM/MCP these are two more tools — **`piprail_discover`** (find) and **`piprail_register`**