@piprail/sdk 1.10.0 → 1.12.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.js CHANGED
@@ -1305,9 +1305,47 @@ async function resolveNetwork2(opts) {
1305
1305
  }
1306
1306
 
1307
1307
  // src/indexes.ts
1308
+ var DIRECTORY_INFO = {
1309
+ "402index": {
1310
+ source: "402index",
1311
+ review: "probe-sync",
1312
+ auth: "none",
1313
+ chains: null,
1314
+ onSuccess: "pending-review",
1315
+ readByDiscover: true,
1316
+ caveat: "402 Index probes your URL on submit, then lists it as PENDING REVIEW \u2014 a self-registered resource is NOT in search until approved. Verify your domain on 402index.io for instant approval; otherwise it appears after manual review, so retry discover() later."
1317
+ },
1318
+ x402scan: {
1319
+ source: "x402scan",
1320
+ review: "probe-sync",
1321
+ auth: "siwx",
1322
+ chains: ["eip155:8453", "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"],
1323
+ onSuccess: "live",
1324
+ readByDiscover: false,
1325
+ caveat: "x402scan lists Base/Solana only, needs one wallet signature (SIWX), and requires a resolvable input schema (from /openapi.json or the bazaar extension in the 402 body). It goes live on x402scan.com immediately on success \u2014 but discover() does NOT read x402scan, so the listing won't appear in discover() results."
1326
+ },
1327
+ bazaar: {
1328
+ source: "bazaar",
1329
+ review: "settle-coupled",
1330
+ auth: "facilitator-only",
1331
+ chains: null,
1332
+ onSuccess: "not-listable",
1333
+ readByDiscover: true,
1334
+ caveat: "CDP Bazaar has no register endpoint \u2014 it catalogs a resource only when its own facilitator settles a payment. PipRail verifies locally with no facilitator, so a PipRail resource cannot be listed here (you can still READ Bazaar to find others). List on 402 Index or x402scan instead."
1335
+ }
1336
+ };
1337
+ function getDirectoryInfo(source) {
1338
+ return DIRECTORY_INFO[source];
1339
+ }
1340
+ function decorateOutcome(o) {
1341
+ const info = DIRECTORY_INFO[o.source];
1342
+ return { ...o, visibility: o.ok ? info.onSuccess : "not-listable", note: info.caveat };
1343
+ }
1308
1344
  var BAZAAR_URL = "https://api.cdp.coinbase.com/platform/v2/x402/discovery/resources";
1309
1345
  var INDEX402_SEARCH = "https://402index.io/api/v1/services";
1310
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";
1311
1349
  var X402SCAN_REGISTER = "https://www.x402scan.com/api/x402/registry/register";
1312
1350
  var USER_AGENT = "@piprail/sdk (+https://piprail.com)";
1313
1351
  function clientHeaders(extra = {}) {
@@ -1457,7 +1495,13 @@ async function register402Index(input) {
1457
1495
  body: JSON.stringify(payload)
1458
1496
  });
1459
1497
  if (res.ok) {
1460
- return { source: "402index", ok: true, status: res.status, detail: "Listed on 402 Index (searchable at 402index.io)." };
1498
+ const msg = await readIndexMessage(res);
1499
+ return {
1500
+ source: "402index",
1501
+ ok: true,
1502
+ status: res.status,
1503
+ detail: msg ?? "Registered on 402 Index \u2014 pending review (verify your domain on 402index.io for instant approval)."
1504
+ };
1461
1505
  }
1462
1506
  const why = await readIndexError(res);
1463
1507
  return {
@@ -1470,6 +1514,14 @@ async function register402Index(input) {
1470
1514
  return { source: "402index", ok: false, detail: errMsg(err) };
1471
1515
  }
1472
1516
  }
1517
+ async function readIndexMessage(res) {
1518
+ try {
1519
+ const body = await res.json();
1520
+ return typeof body.message === "string" && body.message.length > 0 ? body.message : void 0;
1521
+ } catch {
1522
+ return void 0;
1523
+ }
1524
+ }
1473
1525
  async function readIndexError(res) {
1474
1526
  try {
1475
1527
  const body = await res.json();
@@ -1529,6 +1581,53 @@ async function registerX402Scan(input, signer) {
1529
1581
  return { source: "x402scan", ok: false, detail: errMsg(err) };
1530
1582
  }
1531
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
+ return {
1597
+ ok: true,
1598
+ domain,
1599
+ httpStatus: res.status,
1600
+ ...optionalString("verificationHash", pickString(body, "verification_hash")),
1601
+ ...optionalString("verificationUrl", pickString(body, "verification_url")),
1602
+ ...optionalString("instructions", pickString(body, "instructions"))
1603
+ };
1604
+ } catch (err) {
1605
+ return { ok: false, domain, detail: errMsg(err) };
1606
+ }
1607
+ }
1608
+ async function verify402IndexDomain(domainOrUrl) {
1609
+ const domain = hostOf(domainOrUrl);
1610
+ try {
1611
+ const res = await fetch(INDEX402_VERIFY, {
1612
+ method: "POST",
1613
+ headers: clientHeaders({ "content-type": "application/json", accept: "application/json" }),
1614
+ body: JSON.stringify({ domain })
1615
+ });
1616
+ const body = await res.json().catch(() => ({}));
1617
+ if (!res.ok) {
1618
+ return { ok: false, domain, httpStatus: res.status, detail: pickString(body, "error", "detail", "message") ?? `402 Index verify returned HTTP ${res.status}.` };
1619
+ }
1620
+ return {
1621
+ ok: true,
1622
+ domain,
1623
+ httpStatus: res.status,
1624
+ ...optionalString("status", pickString(body, "status")),
1625
+ ...typeof body.services_count === "number" ? { servicesCount: body.services_count } : {}
1626
+ };
1627
+ } catch (err) {
1628
+ return { ok: false, domain, detail: errMsg(err) };
1629
+ }
1630
+ }
1532
1631
  async function readSiwxInfo(res) {
1533
1632
  try {
1534
1633
  const body = await res.json();
@@ -1908,9 +2007,15 @@ var PipRailClient = class {
1908
2007
  *
1909
2008
  * Nothing PipRail-hosted: these are third-party open directories. Never throws
1910
2009
  * for a read problem — an index that's down or changed simply contributes
1911
- * nothing. Honest caveat: index results are cross-scheme (mostly the
1912
- * mainstream `exact` scheme); `fetch()` pays only `onchain-proof` rails
1913
- * directly (pay `exact` resources with the experimental `drivers/evm/exact.ts`).
2010
+ * nothing. Honest caveats (see {@link DIRECTORY_INFO}):
2011
+ * - Reads **`bazaar` + `402index`** only — **NOT `x402scan`** (its reads are paid). A
2012
+ * resource you registered on x402scan is live there but will NOT appear here; don't
2013
+ * read that absence as failure. (Passing `sources:['x402scan']` explicitly yields `[]`.)
2014
+ * - A resource just listed via {@link register} may not appear yet — 402 Index reviews
2015
+ * before publishing, so retry with a brief backoff if a fresh listing is missing.
2016
+ * - Results are cross-scheme (mostly the mainstream `exact` scheme); `fetch()` pays
2017
+ * only `onchain-proof` rails directly (pay `exact` resources with the experimental
2018
+ * `drivers/evm/exact.ts`).
1914
2019
  */
1915
2020
  async discover(opts = {}) {
1916
2021
  const found = await searchOpenIndexes({
@@ -1935,12 +2040,27 @@ var PipRailClient = class {
1935
2040
  }
1936
2041
  /**
1937
2042
  * List a resource you run on the OPEN x402 registries, so agents can find it.
1938
- * Default target is **402 Index** — one POST, no auth, no signature, no payment
1939
- * (searchable within seconds). Add `'x402scan'` to also register via SIWX (one
1940
- * wallet signature; EVM + a Base/Solana rail). Returns one {@link RegisterOutcome}
1941
- * per target — a target the chain can't satisfy comes back `{ ok:false, detail }`,
1942
- * never a throw. An explicit, developer-invoked action; it moves no funds, and
1943
- * nothing is PipRail-hosted — you're listing on third-party open directories.
2043
+ * Default target is **402 Index** — one POST, no auth, no signature, no payment.
2044
+ * Add `'x402scan'` to also register via SIWX (one wallet signature; EVM + a
2045
+ * Base/Solana rail). Returns one {@link RegisterOutcome} per target — a target the
2046
+ * chain can't satisfy comes back `{ ok:false, detail }`, never a throw. An explicit,
2047
+ * developer-invoked action; it moves no funds, and nothing is PipRail-hosted —
2048
+ * you're listing on third-party open directories.
2049
+ *
2050
+ * **Listing is asynchronous — each outcome carries a `visibility` + `note` so an
2051
+ * agent knows when/where the resource is findable (don't assume `ok:true` means
2052
+ * "searchable now"):**
2053
+ * - **402 Index** → `visibility:'pending-review'`. It probes your URL on submit, then lists it
2054
+ * PENDING REVIEW — not searchable until approved (verify your domain on 402index.io for instant
2055
+ * approval), so `discover()` returns nothing for a fresh listing until then. Retry later.
2056
+ * - **x402scan** → `visibility:'live'`, but **`discover()` does NOT read x402scan** — the
2057
+ * listing is real on x402scan.com yet won't show up in `discover()`. Base/Solana only;
2058
+ * needs a resolvable input schema (`/openapi.json` or the `extensions.bazaar` block).
2059
+ * - **Bazaar** → `visibility:'not-listable'` for PipRail (it lists only what its facilitator
2060
+ * settles; PipRail uses none). You can still READ Bazaar via {@link discover} to find others.
2061
+ *
2062
+ * The per-source facts live in {@link DIRECTORY_INFO} (importable) if you'd rather branch
2063
+ * on them before calling.
1944
2064
  */
1945
2065
  async register(url, opts = {}) {
1946
2066
  const targets = opts.targets ?? ["402index"];
@@ -1979,7 +2099,33 @@ var PipRailClient = class {
1979
2099
  });
1980
2100
  }
1981
2101
  }
1982
- return outcomes;
2102
+ return outcomes.map(decorateOutcome);
2103
+ }
2104
+ /**
2105
+ * **402 Index domain verification, step 1 of 2.** A self-registered 402 Index
2106
+ * listing is `pending-review` (see {@link register}); verifying your domain flips
2107
+ * it — and every other pending listing on that domain — to APPROVED/searchable.
2108
+ * Pass the resource URL or a bare domain; returns the `verificationHash` to serve
2109
+ * as the entire body of `verificationUrl` (your `/.well-known/402index-verify.txt`).
2110
+ * Then serve it and call {@link verifyDomain}. Moves no funds; never throws.
2111
+ *
2112
+ * ```ts
2113
+ * const claim = await client.claimDomain('https://api.example.com/report')
2114
+ * // serve claim.verificationHash at claim.verificationUrl, then:
2115
+ * const res = await client.verifyDomain('api.example.com') // → { ok:true, status:'verified' }
2116
+ * ```
2117
+ */
2118
+ async claimDomain(urlOrDomain, opts = {}) {
2119
+ return claim402IndexDomain(urlOrDomain, opts);
2120
+ }
2121
+ /**
2122
+ * **402 Index domain verification, step 2 of 2.** After {@link claimDomain} and
2123
+ * serving the hash at your `/.well-known/402index-verify.txt`, this tells 402 Index
2124
+ * to re-fetch + approve. On success the domain's pending listings become searchable
2125
+ * (`{ ok:true, status:'verified', servicesCount }`). Moves no funds; never throws.
2126
+ */
2127
+ async verifyDomain(urlOrDomain) {
2128
+ return verify402IndexDomain(urlOrDomain);
1983
2129
  }
1984
2130
  /**
1985
2131
  * The discovery signer for the bound wallet (its address + a message signer),
@@ -2627,7 +2773,7 @@ function paymentTools(client) {
2627
2773
  },
2628
2774
  {
2629
2775
  name: "piprail_register",
2630
- 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; searchable within seconds. Returns one outcome per index ({ source, ok, detail }); a step the chain can't satisfy comes back ok:false with the reason. Moves no funds; nothing is PipRail-hosted.",
2776
+ 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.",
2631
2777
  annotations: {
2632
2778
  title: "Register an x402 endpoint",
2633
2779
  readOnlyHint: false,
@@ -3161,6 +3307,7 @@ function buildX402DnsTxt(input) {
3161
3307
  export {
3162
3308
  CHAINS,
3163
3309
  ConfirmationTimeoutError,
3310
+ DIRECTORY_INFO,
3164
3311
  EIP3009_TYPES,
3165
3312
  EXACT_NETWORK_SLUGS,
3166
3313
  GENERATOR,
@@ -3193,10 +3340,13 @@ export {
3193
3340
  buildWellKnownX402,
3194
3341
  buildX402DnsTxt,
3195
3342
  chainIdForExactNetwork,
3343
+ claim402IndexDomain,
3196
3344
  createPaymentGate,
3345
+ decorateOutcome,
3197
3346
  eip3009Abi,
3198
3347
  encodeXPaymentHeader,
3199
3348
  evaluatePolicy,
3349
+ getDirectoryInfo,
3200
3350
  normalizeNetwork,
3201
3351
  parseChallenge,
3202
3352
  parseExactPaymentHeader,
@@ -3215,5 +3365,6 @@ export {
3215
3365
  searchOpenIndexes,
3216
3366
  settleViaFacilitator,
3217
3367
  toInsufficientFundsError,
3218
- toInvalidBody
3368
+ toInvalidBody,
3369
+ verify402IndexDomain
3219
3370
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piprail/sdk",
3
- "version": "1.10.0",
3
+ "version": "1.12.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",