@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/dist/index.d.ts CHANGED
@@ -4365,6 +4365,55 @@ declare function register402Index(input: RegisterInput): Promise<RegisterOutcome
4365
4365
  declare function registerX402Scan(input: {
4366
4366
  url: string;
4367
4367
  }, signer: DiscoverySigner): Promise<RegisterOutcome>;
4368
+ /** What {@link claim402IndexDomain} returns — the proof to SERVE so 402 Index will
4369
+ * approve your domain (and flip your `pending-review` listings to searchable). */
4370
+ interface DomainClaim {
4371
+ ok: boolean;
4372
+ domain: string;
4373
+ /** The exact text to serve as the ENTIRE body of `verificationUrl` — this is what
4374
+ * 402 Index fetches and checks (the SHA-256 of the token). Always populated on
4375
+ * success: read from the response, or computed as `sha256(verificationToken)` if
4376
+ * the API returns only the token. Serve THIS. */
4377
+ verificationHash?: string;
4378
+ /** The raw 64-hex token 402 Index issued (the preimage of `verificationHash`). */
4379
+ verificationToken?: string;
4380
+ /** Where to serve `verificationHash` — your `https://<domain>/.well-known/402index-verify.txt`. */
4381
+ verificationUrl?: string;
4382
+ /** 402 Index's own human instructions. */
4383
+ instructions?: string;
4384
+ httpStatus?: number;
4385
+ /** Failure reason when `ok:false`. */
4386
+ detail?: string;
4387
+ }
4388
+ /** What {@link verify402IndexDomain} returns once the proof is in place. */
4389
+ interface DomainVerification {
4390
+ ok: boolean;
4391
+ domain: string;
4392
+ /** 402 Index's status string, e.g. `'verified'`. */
4393
+ status?: string;
4394
+ /** How many of your pending listings were approved by the verification. */
4395
+ servicesCount?: number;
4396
+ httpStatus?: number;
4397
+ detail?: string;
4398
+ }
4399
+ /**
4400
+ * Step 1 of 402 Index domain verification: claim the host of `domainOrUrl`. 402 Index
4401
+ * lists a self-registered resource as PENDING REVIEW; verifying the domain approves it
4402
+ * (and every other pending listing on that domain) so it becomes searchable. Returns the
4403
+ * `verificationHash` to serve as the entire body of `verificationUrl`
4404
+ * (`https://<domain>/.well-known/402index-verify.txt`). Then call {@link verify402IndexDomain}.
4405
+ * No funds move. Never throws.
4406
+ */
4407
+ declare function claim402IndexDomain(domainOrUrl: string, opts?: {
4408
+ contactEmail?: string;
4409
+ }): Promise<DomainClaim>;
4410
+ /**
4411
+ * Step 2 of 402 Index domain verification: after {@link claim402IndexDomain} and serving
4412
+ * the `verificationHash` at `verificationUrl`, tell 402 Index to re-fetch + approve. On
4413
+ * success, the domain's pending listings become searchable (`status:'verified'`,
4414
+ * `servicesCount` approved). No funds move. Never throws.
4415
+ */
4416
+ declare function verify402IndexDomain(domainOrUrl: string): Promise<DomainVerification>;
4368
4417
 
4369
4418
  interface PaymentPolicy {
4370
4419
  /** Per-payment ceiling, human-readable (e.g. '0.10'). Compared using the
@@ -4849,6 +4898,30 @@ declare class PipRailClient {
4849
4898
  * on them before calling.
4850
4899
  */
4851
4900
  register(url: string, opts?: RegisterOptions): Promise<RegisterOutcome[]>;
4901
+ /**
4902
+ * **402 Index domain verification, step 1 of 2.** A self-registered 402 Index
4903
+ * listing is `pending-review` (see {@link register}); verifying your domain flips
4904
+ * it — and every other pending listing on that domain — to APPROVED/searchable.
4905
+ * Pass the resource URL or a bare domain; returns the `verificationHash` to serve
4906
+ * as the entire body of `verificationUrl` (your `/.well-known/402index-verify.txt`).
4907
+ * Then serve it and call {@link verifyDomain}. Moves no funds; never throws.
4908
+ *
4909
+ * ```ts
4910
+ * const claim = await client.claimDomain('https://api.example.com/report')
4911
+ * // serve claim.verificationHash at claim.verificationUrl, then:
4912
+ * const res = await client.verifyDomain('api.example.com') // → { ok:true, status:'verified' }
4913
+ * ```
4914
+ */
4915
+ claimDomain(urlOrDomain: string, opts?: {
4916
+ contactEmail?: string;
4917
+ }): Promise<DomainClaim>;
4918
+ /**
4919
+ * **402 Index domain verification, step 2 of 2.** After {@link claimDomain} and
4920
+ * serving the hash at your `/.well-known/402index-verify.txt`, this tells 402 Index
4921
+ * to re-fetch + approve. On success the domain's pending listings become searchable
4922
+ * (`{ ok:true, status:'verified', servicesCount }`). Moves no funds; never throws.
4923
+ */
4924
+ verifyDomain(urlOrDomain: string): Promise<DomainVerification>;
4852
4925
  /**
4853
4926
  * The discovery signer for the bound wallet (its address + a message signer),
4854
4927
  * or `null` if the chain family doesn't support it (EVM does today). For
@@ -5061,10 +5134,6 @@ interface OpenApiOperation {
5061
5134
  'x-payment-info': {
5062
5135
  x402Version: 2;
5063
5136
  accepts: PaymentRail[];
5064
- /** Bazaar-style input schema marker so a strict index doesn't "skip" the op. */
5065
- bazaar: {
5066
- discoverable: true;
5067
- };
5068
5137
  };
5069
5138
  }
5070
5139
  /** x402scan's legacy origin file (`/.well-known/x402`). */
@@ -5073,6 +5142,46 @@ interface WellKnownX402 {
5073
5142
  resources: string[];
5074
5143
  ownershipProofs?: string[];
5075
5144
  }
5145
+ /**
5146
+ * Describes a resource's INPUT for discovery. The open indexes that REQUIRE an
5147
+ * input schema (x402scan rejects a listing without one) read this from a
5148
+ * `extensions.bazaar` block. Pass it to a gate's `discovery` option (emits the
5149
+ * block in the 402 challenge) or build it directly with {@link buildBazaarExtension}.
5150
+ */
5151
+ interface DiscoveryDescriptor {
5152
+ /** HTTP method the resource answers. Default `'GET'`. */
5153
+ method?: string;
5154
+ /** Query params the resource reads, as a JSON-Schema `properties` object
5155
+ * (name → schema). Default `{}` — a no-input GET. */
5156
+ queryParams?: Record<string, unknown>;
5157
+ /** Optional output hint (shape/example) for a richer listing. */
5158
+ output?: {
5159
+ type?: string;
5160
+ example?: unknown;
5161
+ };
5162
+ }
5163
+ /** The `extensions.bazaar` discovery block (the x402 "bazaar" convention the open
5164
+ * indexes parse: `info.input` describes the request, `schema` is its JSON Schema). */
5165
+ interface BazaarExtension {
5166
+ info: {
5167
+ input: {
5168
+ type: 'http';
5169
+ method: string;
5170
+ queryParams: Record<string, unknown>;
5171
+ };
5172
+ output?: {
5173
+ type?: string;
5174
+ example?: unknown;
5175
+ };
5176
+ };
5177
+ schema: Record<string, unknown>;
5178
+ }
5179
+ /**
5180
+ * Build the `extensions.bazaar` block that satisfies x402scan's mandatory input-schema
5181
+ * check, from a {@link DiscoveryDescriptor}. Pure. Defaults to a no-input GET — the
5182
+ * minimal shape a live x402scan listing accepts.
5183
+ */
5184
+ declare function buildBazaarExtension(descriptor?: DiscoveryDescriptor): BazaarExtension;
5076
5185
  /** The `_x402` DNS TXT pointer record (experimental draft). */
5077
5186
  interface X402DnsRecord {
5078
5187
  name: string;
@@ -5224,6 +5333,14 @@ interface RequirePaymentOptions {
5224
5333
  * Omit to keep the gate exactly as today (`onchain-proof` only).
5225
5334
  */
5226
5335
  exact?: ExactRailOption;
5336
+ /**
5337
+ * Make this gate's 402 self-describing for the open indexes — **x402scan REQUIRES
5338
+ * an input schema or it won't list the resource.** Set `true` for a no-input GET,
5339
+ * or pass a {@link DiscoveryDescriptor} to describe the request. Emits an
5340
+ * `extensions.bazaar` block in the 402 challenge. Opt-in; omitting it leaves the
5341
+ * challenge byte-identical to before.
5342
+ */
5343
+ discovery?: boolean | DiscoveryDescriptor;
5227
5344
  }
5228
5345
  type VerifyPaymentResult = {
5229
5346
  kind: 'paid';
@@ -5830,4 +5947,4 @@ declare function readExactDomain(publicClient: PublicClient, asset: string): Pro
5830
5947
  version: string;
5831
5948
  } | null>;
5832
5949
 
5833
- export { type AcceptOption, type AddressId, type AgentTool, type AlgorandToken, type AptosToken, type AssetId, type BuildExactParams, CHAINS, type Caip2, type ChainFamily, type ChainInput, type ChainName, type ChainPreset, type ChainSelector, type ConfirmInfo, ConfirmationTimeoutError, type CostEstimate, DIRECTORY_INFO, type DirectoryInfo, type DiscoverOptions, type DiscoveredRail, type DiscoveredResource, type DiscoverySigner, type DiscoverySource, EIP3009_TYPES, EXACT_NETWORK_SLUGS, type EvmToken, type ExactAccept, type ExactAuthorization, type ExactAuthorizationWire, type ExactPaymentPayload, type ExactRailOption, type ExpressLikeMiddleware, type ExpressLikeNext, type ExpressLikeRequest, type ExpressLikeResponse, type FacilitatorConfig, type FacilitatorPaymentRequirements, GENERATOR, HEADER_REQUIRED, HEADER_RESPONSE, HEADER_RESPONSE_V1, HEADER_SIGNATURE, HEADER_SIGNATURE_V1, InsufficientFundsError, InvalidEnvelopeError, type ListingVisibility, type ManifestInput, MaxRetriesExceededError, MissingDriverError, type NearToken, NoCompatibleAcceptError, NonReplayableBodyError, type OpenApiDocument, type OpenApiOperation, type ParsedExactPayment, type PayBlocker, type PayOption, type PayWarning, PaymentDeclinedError, type PaymentDriver, type PaymentGate, type PaymentIntent, type PaymentPlan, type PaymentPolicy, type PaymentRail, PaymentTimeoutError, PipRailClient, type PipRailClientOptions, type PipRailCostQuote, PipRailError, type PipRailEvent, type PipRailQuote, type PolicyDecision, RecipientNotReadyError, type RecipientReason, type RegisterInput, type RegisterOptions, type RegisterOutcome, type RequirePaymentOptions, type ResolveOptions, type ResolvedChain, type ResolvedNetwork, type ResolvedToken, type ResourceDescription, type SearchOpenIndexesOptions, type SettleViaFacilitatorInput, SettlementError, type SolanaToken, type SpendAssetTotal, type SpendRecord, type SpendSummary, type StellarToken, type SuiToken, type TokenInfo, type TokenInput, type TonToken, type ToolAnnotations, type TronToken, UnknownTokenError, UnsupportedNetworkError, type VerifyErrorCode, type VerifyPaymentResult, type VerifyResult, type WalletBalance, type WalletHandle, type WalletInput, type WellKnownX402, WrongChainError, WrongFamilyError, type X402AcceptEntry, type X402AnyAccept, type X402Challenge, type X402DnsRecord, type X402ExactAcceptEntry, type X402InvalidBody, type X402PaymentSignature, type X402Receipt, type X402ResourceObject, type XrplToken, buildChallengeHeader, buildExactAuthorization, buildOpenApi, buildReceiptHeader, buildSignatureHeader, buildWellKnownX402, buildX402DnsTxt, chainIdForExactNetwork, createPaymentGate, decorateOutcome, eip3009Abi, encodeXPaymentHeader, evaluatePolicy, getDirectoryInfo, normalizeNetwork, parseChallenge, parseExactPaymentHeader, parseExactRequirements, parseReceipt, parseSignatureHeader, paymentTools, pickAccept, planAcross, readExactDomain, register402Index, registerDriver, registerX402Scan, requirePayment, resolveChain, searchOpenIndexes, settleViaFacilitator, toInsufficientFundsError, toInvalidBody };
5950
+ export { type AcceptOption, type AddressId, type AgentTool, type AlgorandToken, type AptosToken, type AssetId, type BazaarExtension, type BuildExactParams, CHAINS, type Caip2, type ChainFamily, type ChainInput, type ChainName, type ChainPreset, type ChainSelector, type ConfirmInfo, ConfirmationTimeoutError, type CostEstimate, DIRECTORY_INFO, type DirectoryInfo, type DiscoverOptions, type DiscoveredRail, type DiscoveredResource, type DiscoveryDescriptor, type DiscoverySigner, type DiscoverySource, type DomainClaim, type DomainVerification, EIP3009_TYPES, EXACT_NETWORK_SLUGS, type EvmToken, type ExactAccept, type ExactAuthorization, type ExactAuthorizationWire, type ExactPaymentPayload, type ExactRailOption, type ExpressLikeMiddleware, type ExpressLikeNext, type ExpressLikeRequest, type ExpressLikeResponse, type FacilitatorConfig, type FacilitatorPaymentRequirements, GENERATOR, HEADER_REQUIRED, HEADER_RESPONSE, HEADER_RESPONSE_V1, HEADER_SIGNATURE, HEADER_SIGNATURE_V1, InsufficientFundsError, InvalidEnvelopeError, type ListingVisibility, type ManifestInput, MaxRetriesExceededError, MissingDriverError, type NearToken, NoCompatibleAcceptError, NonReplayableBodyError, type OpenApiDocument, type OpenApiOperation, type ParsedExactPayment, type PayBlocker, type PayOption, type PayWarning, PaymentDeclinedError, type PaymentDriver, type PaymentGate, type PaymentIntent, type PaymentPlan, type PaymentPolicy, type PaymentRail, PaymentTimeoutError, PipRailClient, type PipRailClientOptions, type PipRailCostQuote, PipRailError, type PipRailEvent, type PipRailQuote, type PolicyDecision, RecipientNotReadyError, type RecipientReason, type RegisterInput, type RegisterOptions, type RegisterOutcome, type RequirePaymentOptions, type ResolveOptions, type ResolvedChain, type ResolvedNetwork, type ResolvedToken, type ResourceDescription, type SearchOpenIndexesOptions, type SettleViaFacilitatorInput, SettlementError, type SolanaToken, type SpendAssetTotal, type SpendRecord, type SpendSummary, type StellarToken, type SuiToken, type TokenInfo, type TokenInput, type TonToken, type ToolAnnotations, type TronToken, UnknownTokenError, UnsupportedNetworkError, type VerifyErrorCode, type VerifyPaymentResult, type VerifyResult, type WalletBalance, type WalletHandle, type WalletInput, type WellKnownX402, WrongChainError, WrongFamilyError, type X402AcceptEntry, type X402AnyAccept, type X402Challenge, type X402DnsRecord, type X402ExactAcceptEntry, type X402InvalidBody, type X402PaymentSignature, type X402Receipt, type X402ResourceObject, type XrplToken, buildBazaarExtension, buildChallengeHeader, buildExactAuthorization, buildOpenApi, buildReceiptHeader, buildSignatureHeader, buildWellKnownX402, buildX402DnsTxt, chainIdForExactNetwork, claim402IndexDomain, createPaymentGate, decorateOutcome, eip3009Abi, encodeXPaymentHeader, evaluatePolicy, getDirectoryInfo, normalizeNetwork, parseChallenge, parseExactPaymentHeader, parseExactRequirements, parseReceipt, parseSignatureHeader, paymentTools, pickAccept, planAcross, readExactDomain, register402Index, registerDriver, registerX402Scan, requirePayment, resolveChain, searchOpenIndexes, settleViaFacilitator, toInsufficientFundsError, toInvalidBody, verify402IndexDomain };
package/dist/index.js CHANGED
@@ -1344,6 +1344,8 @@ function decorateOutcome(o) {
1344
1344
  var BAZAAR_URL = "https://api.cdp.coinbase.com/platform/v2/x402/discovery/resources";
1345
1345
  var INDEX402_SEARCH = "https://402index.io/api/v1/services";
1346
1346
  var INDEX402_REGISTER = "https://402index.io/api/v1/register";
1347
+ var INDEX402_CLAIM = "https://402index.io/api/v1/claim";
1348
+ var INDEX402_VERIFY = "https://402index.io/api/v1/claim/verify";
1347
1349
  var X402SCAN_REGISTER = "https://www.x402scan.com/api/x402/registry/register";
1348
1350
  var USER_AGENT = "@piprail/sdk (+https://piprail.com)";
1349
1351
  function clientHeaders(extra = {}) {
@@ -1579,12 +1581,72 @@ async function registerX402Scan(input, signer) {
1579
1581
  return { source: "x402scan", ok: false, detail: errMsg(err) };
1580
1582
  }
1581
1583
  }
1584
+ async function claim402IndexDomain(domainOrUrl, opts = {}) {
1585
+ const domain = hostOf(domainOrUrl);
1586
+ try {
1587
+ const res = await fetch(INDEX402_CLAIM, {
1588
+ method: "POST",
1589
+ headers: clientHeaders({ "content-type": "application/json", accept: "application/json" }),
1590
+ body: JSON.stringify({ domain, ...opts.contactEmail ? { contact_email: opts.contactEmail } : {} })
1591
+ });
1592
+ const body = await res.json().catch(() => ({}));
1593
+ if (!res.ok) {
1594
+ return { ok: false, domain, httpStatus: res.status, detail: pickString(body, "error", "detail", "message") ?? `402 Index claim returned HTTP ${res.status}.` };
1595
+ }
1596
+ const verificationToken = pickString(body, "verification_token");
1597
+ const verificationHash = pickString(body, "verification_hash") ?? (verificationToken ? await sha256Hex(verificationToken) : void 0);
1598
+ return {
1599
+ ok: true,
1600
+ domain,
1601
+ httpStatus: res.status,
1602
+ ...optionalString("verificationHash", verificationHash),
1603
+ ...optionalString("verificationToken", verificationToken),
1604
+ ...optionalString("verificationUrl", pickString(body, "verification_url")),
1605
+ ...optionalString("instructions", pickString(body, "instructions"))
1606
+ };
1607
+ } catch (err) {
1608
+ return { ok: false, domain, detail: errMsg(err) };
1609
+ }
1610
+ }
1611
+ async function sha256Hex(input) {
1612
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
1613
+ return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
1614
+ }
1615
+ async function verify402IndexDomain(domainOrUrl) {
1616
+ const domain = hostOf(domainOrUrl);
1617
+ try {
1618
+ const res = await fetch(INDEX402_VERIFY, {
1619
+ method: "POST",
1620
+ headers: clientHeaders({ "content-type": "application/json", accept: "application/json" }),
1621
+ body: JSON.stringify({ domain })
1622
+ });
1623
+ const body = await res.json().catch(() => ({}));
1624
+ if (!res.ok) {
1625
+ return { ok: false, domain, httpStatus: res.status, detail: pickString(body, "error", "detail", "message") ?? `402 Index verify returned HTTP ${res.status}.` };
1626
+ }
1627
+ return {
1628
+ ok: true,
1629
+ domain,
1630
+ httpStatus: res.status,
1631
+ ...optionalString("status", pickString(body, "status")),
1632
+ ...typeof body.services_count === "number" ? { servicesCount: body.services_count } : {}
1633
+ };
1634
+ } catch (err) {
1635
+ return { ok: false, domain, detail: errMsg(err) };
1636
+ }
1637
+ }
1582
1638
  async function readSiwxInfo(res) {
1583
1639
  try {
1584
1640
  const body = await res.json();
1585
1641
  const ext = body.extensions;
1586
1642
  const siwx = ext?.["sign-in-with-x"];
1587
1643
  const info = siwx?.info ?? siwx;
1644
+ if (info && info.chainId == null && Array.isArray(siwx?.supportedChains)) {
1645
+ const evm = siwx.supportedChains.find(
1646
+ (c) => typeof c?.chainId === "string" && c.chainId.startsWith("eip155:")
1647
+ );
1648
+ if (evm && typeof evm.chainId === "string") info.chainId = evm.chainId;
1649
+ }
1588
1650
  if (info && typeof info.domain === "string" && info.domain.length > 0 && typeof info.nonce === "string" && info.nonce.length > 0 && typeof info.uri === "string" && info.uri.length > 0) {
1589
1651
  return info;
1590
1652
  }
@@ -1668,7 +1730,8 @@ function firstArray(o, ...keys) {
1668
1730
  }
1669
1731
  function hostOf(url) {
1670
1732
  try {
1671
- return new URL(url).hostname;
1733
+ const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(url) ? url : `https://${url}`;
1734
+ return new URL(withScheme).hostname || url;
1672
1735
  } catch {
1673
1736
  return url;
1674
1737
  }
@@ -1677,8 +1740,13 @@ function errMsg(err) {
1677
1740
  return err instanceof Error ? err.message : String(err);
1678
1741
  }
1679
1742
  function encodeBase642(str) {
1680
- if (typeof btoa === "function") return btoa(str);
1681
1743
  if (typeof Buffer !== "undefined") return Buffer.from(str, "utf8").toString("base64");
1744
+ if (typeof btoa === "function" && typeof TextEncoder !== "undefined") {
1745
+ const bytes = new TextEncoder().encode(str);
1746
+ let binary = "";
1747
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
1748
+ return btoa(binary);
1749
+ }
1682
1750
  throw new Error("No base64 encoder available in this runtime.");
1683
1751
  }
1684
1752
 
@@ -2052,6 +2120,32 @@ var PipRailClient = class {
2052
2120
  }
2053
2121
  return outcomes.map(decorateOutcome);
2054
2122
  }
2123
+ /**
2124
+ * **402 Index domain verification, step 1 of 2.** A self-registered 402 Index
2125
+ * listing is `pending-review` (see {@link register}); verifying your domain flips
2126
+ * it — and every other pending listing on that domain — to APPROVED/searchable.
2127
+ * Pass the resource URL or a bare domain; returns the `verificationHash` to serve
2128
+ * as the entire body of `verificationUrl` (your `/.well-known/402index-verify.txt`).
2129
+ * Then serve it and call {@link verifyDomain}. Moves no funds; never throws.
2130
+ *
2131
+ * ```ts
2132
+ * const claim = await client.claimDomain('https://api.example.com/report')
2133
+ * // serve claim.verificationHash at claim.verificationUrl, then:
2134
+ * const res = await client.verifyDomain('api.example.com') // → { ok:true, status:'verified' }
2135
+ * ```
2136
+ */
2137
+ async claimDomain(urlOrDomain, opts = {}) {
2138
+ return claim402IndexDomain(urlOrDomain, opts);
2139
+ }
2140
+ /**
2141
+ * **402 Index domain verification, step 2 of 2.** After {@link claimDomain} and
2142
+ * serving the hash at your `/.well-known/402index-verify.txt`, this tells 402 Index
2143
+ * to re-fetch + approve. On success the domain's pending listings become searchable
2144
+ * (`{ ok:true, status:'verified', servicesCount }`). Moves no funds; never throws.
2145
+ */
2146
+ async verifyDomain(urlOrDomain) {
2147
+ return verify402IndexDomain(urlOrDomain);
2148
+ }
2055
2149
  /**
2056
2150
  * The discovery signer for the bound wallet (its address + a message signer),
2057
2151
  * or `null` if the chain family doesn't support it (EVM does today). For
@@ -2698,7 +2792,7 @@ function paymentTools(client) {
2698
2792
  },
2699
2793
  {
2700
2794
  name: "piprail_register",
2701
- description: "List an x402 payment-gated resource YOU run on the open indexes so other agents can discover it. Default target is 402 Index \u2014 no auth, no signature, no payment; a self-registered listing is pending review (verify your domain on 402index.io for instant approval). Returns one outcome per index ({ source, ok, detail, visibility, note }); a step the chain can't satisfy comes back ok:false with the reason. Moves no funds; nothing is PipRail-hosted.",
2795
+ description: "List an x402 payment-gated resource YOU run on the open indexes so other agents can discover it. Default target is 402 Index \u2014 no auth, no signature, no payment; a self-registered listing is pending review (verify your domain on 402index.io for instant approval). Returns one outcome per index ({ source, ok, detail, visibility, note }); a step the chain can't satisfy comes back ok:false with the reason. Moves no funds; nothing is PipRail-hosted. NOTE: index/agent payers are overwhelmingly standard `exact` clients \u2014 a default onchain-proof-only gate gets listed but they cannot pay it, so add an `exact` rail (and set the gate's `discovery` option, required for x402scan) to be usefully discoverable AND payable.",
2702
2796
  annotations: {
2703
2797
  title: "Register an x402 endpoint",
2704
2798
  readOnlyHint: false,
@@ -2732,6 +2826,87 @@ function paymentTools(client) {
2732
2826
  ];
2733
2827
  }
2734
2828
 
2829
+ // src/discovery.ts
2830
+ var GENERATOR = "@piprail/sdk \xB7 https://piprail.com";
2831
+ function buildBazaarExtension(descriptor = {}) {
2832
+ const method = (descriptor.method ?? "GET").toUpperCase();
2833
+ const queryParams = descriptor.queryParams ?? {};
2834
+ return {
2835
+ info: {
2836
+ input: { type: "http", method, queryParams },
2837
+ output: descriptor.output ?? { type: "json" }
2838
+ },
2839
+ schema: {
2840
+ $schema: "https://json-schema.org/draft/2020-12/schema",
2841
+ type: "object",
2842
+ properties: {
2843
+ input: {
2844
+ type: "object",
2845
+ properties: {
2846
+ type: { type: "string", const: "http" },
2847
+ method: { type: "string", enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"] },
2848
+ queryParams: { type: "object", properties: queryParams, additionalProperties: false }
2849
+ },
2850
+ required: ["type", "method"],
2851
+ additionalProperties: false
2852
+ }
2853
+ },
2854
+ required: ["input"]
2855
+ }
2856
+ };
2857
+ }
2858
+ function pathOf(url) {
2859
+ try {
2860
+ return new URL(url).pathname || "/";
2861
+ } catch {
2862
+ return url.startsWith("/") ? url : `/${url}`;
2863
+ }
2864
+ }
2865
+ function buildOpenApi(input) {
2866
+ const paths = {};
2867
+ for (const r of input.resources) {
2868
+ const path = pathOf(r.url);
2869
+ const method = (r.method ?? "GET").toLowerCase();
2870
+ const op = {
2871
+ ...r.description ? { summary: r.description } : {},
2872
+ responses: {
2873
+ "200": { description: "Paid \u2014 the resource." },
2874
+ "402": { description: "Payment required (x402)." }
2875
+ },
2876
+ "x-payment-info": {
2877
+ x402Version: 2,
2878
+ accepts: r.accepts
2879
+ }
2880
+ };
2881
+ paths[path] = { ...paths[path] ?? {}, [method]: op };
2882
+ }
2883
+ return {
2884
+ openapi: "3.1.0",
2885
+ info: { title: input.title ?? "PipRail x402 resources", version: input.version ?? "1.0.0" },
2886
+ servers: [{ url: input.origin }],
2887
+ paths,
2888
+ // "Built with @piprail/sdk" — default on (opt out with attribution:false). At the
2889
+ // document ROOT so `info` stays exactly { title, version }.
2890
+ ...input.attribution === false ? {} : { "x-generator": GENERATOR },
2891
+ ...input.ownershipProofs && input.ownershipProofs.length > 0 ? { "x-agentcash-provenance": { ownershipProofs: input.ownershipProofs } } : {}
2892
+ };
2893
+ }
2894
+ function buildWellKnownX402(input) {
2895
+ return {
2896
+ version: 1,
2897
+ resources: input.resources.map((r) => r.url),
2898
+ ...input.ownershipProofs && input.ownershipProofs.length > 0 ? { ownershipProofs: input.ownershipProofs } : {}
2899
+ };
2900
+ }
2901
+ function buildX402DnsTxt(input) {
2902
+ const descriptor = input.descriptor ? `descriptor=${input.descriptor};` : "";
2903
+ return {
2904
+ name: `_x402.${input.host}`,
2905
+ type: "TXT",
2906
+ value: `v=x4021;${descriptor}url=${input.discoveryUrl}`
2907
+ };
2908
+ }
2909
+
2735
2910
  // src/facilitator.ts
2736
2911
  function safeStringify(value) {
2737
2912
  return JSON.stringify(value, (_k, v) => typeof v === "bigint" ? v.toString() : v);
@@ -2980,6 +3155,8 @@ function createPaymentGate(options) {
2980
3155
  async function makeChallenge(resourceUrl, opts) {
2981
3156
  const specs = await ready();
2982
3157
  const nonce = genNonce();
3158
+ const bazaar = options.discovery ? { bazaar: buildBazaarExtension(options.discovery === true ? {} : options.discovery) } : void 0;
3159
+ const extensions = { ...bazaar, ...opts?.extensions };
2983
3160
  const challenge2 = {
2984
3161
  x402Version: 2,
2985
3162
  resource: {
@@ -2988,7 +3165,7 @@ function createPaymentGate(options) {
2988
3165
  },
2989
3166
  accepts: buildAccepts(specs, nonce),
2990
3167
  ...opts?.error ? { error: opts.error } : {},
2991
- ...opts?.extensions ? { extensions: opts.extensions } : {}
3168
+ ...Object.keys(extensions).length > 0 ? { extensions } : {}
2992
3169
  };
2993
3170
  return { challenge: challenge2, requiredHeader: buildChallengeHeader(challenge2) };
2994
3171
  }
@@ -3174,61 +3351,6 @@ function normaliseHeader(value) {
3174
3351
  if (Array.isArray(value)) return value[0];
3175
3352
  return value;
3176
3353
  }
3177
-
3178
- // src/discovery.ts
3179
- var GENERATOR = "@piprail/sdk \xB7 https://piprail.com";
3180
- function pathOf(url) {
3181
- try {
3182
- return new URL(url).pathname || "/";
3183
- } catch {
3184
- return url.startsWith("/") ? url : `/${url}`;
3185
- }
3186
- }
3187
- function buildOpenApi(input) {
3188
- const paths = {};
3189
- for (const r of input.resources) {
3190
- const path = pathOf(r.url);
3191
- const method = (r.method ?? "GET").toLowerCase();
3192
- const op = {
3193
- ...r.description ? { summary: r.description } : {},
3194
- responses: {
3195
- "200": { description: "Paid \u2014 the resource." },
3196
- "402": { description: "Payment required (x402)." }
3197
- },
3198
- "x-payment-info": {
3199
- x402Version: 2,
3200
- accepts: r.accepts,
3201
- bazaar: { discoverable: true }
3202
- }
3203
- };
3204
- paths[path] = { ...paths[path] ?? {}, [method]: op };
3205
- }
3206
- return {
3207
- openapi: "3.1.0",
3208
- info: { title: input.title ?? "PipRail x402 resources", version: input.version ?? "1.0.0" },
3209
- servers: [{ url: input.origin }],
3210
- paths,
3211
- // "Built with @piprail/sdk" — default on (opt out with attribution:false). At the
3212
- // document ROOT so `info` stays exactly { title, version }.
3213
- ...input.attribution === false ? {} : { "x-generator": GENERATOR },
3214
- ...input.ownershipProofs && input.ownershipProofs.length > 0 ? { "x-agentcash-provenance": { ownershipProofs: input.ownershipProofs } } : {}
3215
- };
3216
- }
3217
- function buildWellKnownX402(input) {
3218
- return {
3219
- version: 1,
3220
- resources: input.resources.map((r) => r.url),
3221
- ...input.ownershipProofs && input.ownershipProofs.length > 0 ? { ownershipProofs: input.ownershipProofs } : {}
3222
- };
3223
- }
3224
- function buildX402DnsTxt(input) {
3225
- const descriptor = input.descriptor ? `descriptor=${input.descriptor};` : "";
3226
- return {
3227
- name: `_x402.${input.host}`,
3228
- type: "TXT",
3229
- value: `v=x4021;${descriptor}url=${input.discoveryUrl}`
3230
- };
3231
- }
3232
3354
  export {
3233
3355
  CHAINS,
3234
3356
  ConfirmationTimeoutError,
@@ -3257,6 +3379,7 @@ export {
3257
3379
  UnsupportedNetworkError,
3258
3380
  WrongChainError,
3259
3381
  WrongFamilyError,
3382
+ buildBazaarExtension,
3260
3383
  buildChallengeHeader,
3261
3384
  buildExactAuthorization,
3262
3385
  buildOpenApi,
@@ -3265,6 +3388,7 @@ export {
3265
3388
  buildWellKnownX402,
3266
3389
  buildX402DnsTxt,
3267
3390
  chainIdForExactNetwork,
3391
+ claim402IndexDomain,
3268
3392
  createPaymentGate,
3269
3393
  decorateOutcome,
3270
3394
  eip3009Abi,
@@ -3289,5 +3413,6 @@ export {
3289
3413
  searchOpenIndexes,
3290
3414
  settleViaFacilitator,
3291
3415
  toInsufficientFundsError,
3292
- toInvalidBody
3416
+ toInvalidBody,
3417
+ verify402IndexDomain
3293
3418
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piprail/sdk",
3
- "version": "1.11.0",
3
+ "version": "1.13.0",
4
4
  "description": "Accept x402 crypto payments across 29 chains — every major EVM chain plus Solana, TON, Tron, NEAR, Sui, Aptos, Algorand, Stellar & XRPL — in a couple of lines. No backend, no database, no fee; payments settle straight to your wallet.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",