@inflowpayai/inflow 0.6.4 → 0.6.6

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.
Files changed (3) hide show
  1. package/README.md +95 -22
  2. package/dist/cli.js +1125 -706
  3. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -8837,19 +8837,19 @@ var require_range = __commonJS({
8837
8837
  var replaceCaret = (comp, options) => {
8838
8838
  debug("caret", comp, options);
8839
8839
  const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET];
8840
- const z7 = options.includePrerelease ? "-0" : "";
8840
+ const z8 = options.includePrerelease ? "-0" : "";
8841
8841
  return comp.replace(r, (_, M, m, p, pr) => {
8842
8842
  debug("caret", comp, _, M, m, p, pr);
8843
8843
  let ret;
8844
8844
  if (isX(M)) {
8845
8845
  ret = "";
8846
8846
  } else if (isX(m)) {
8847
- ret = `>=${M}.0.0${z7} <${+M + 1}.0.0-0`;
8847
+ ret = `>=${M}.0.0${z8} <${+M + 1}.0.0-0`;
8848
8848
  } else if (isX(p)) {
8849
8849
  if (M === "0") {
8850
- ret = `>=${M}.${m}.0${z7} <${M}.${+m + 1}.0-0`;
8850
+ ret = `>=${M}.${m}.0${z8} <${M}.${+m + 1}.0-0`;
8851
8851
  } else {
8852
- ret = `>=${M}.${m}.0${z7} <${+M + 1}.0.0-0`;
8852
+ ret = `>=${M}.${m}.0${z8} <${+M + 1}.0.0-0`;
8853
8853
  }
8854
8854
  } else if (pr) {
8855
8855
  debug("replaceCaret pr", pr);
@@ -8866,9 +8866,9 @@ var require_range = __commonJS({
8866
8866
  debug("no pr");
8867
8867
  if (M === "0") {
8868
8868
  if (m === "0") {
8869
- ret = `>=${M}.${m}.${p}${z7} <${M}.${m}.${+p + 1}-0`;
8869
+ ret = `>=${M}.${m}.${p}${z8} <${M}.${m}.${+p + 1}-0`;
8870
8870
  } else {
8871
- ret = `>=${M}.${m}.${p}${z7} <${M}.${+m + 1}.0-0`;
8871
+ ret = `>=${M}.${m}.${p}${z8} <${M}.${+m + 1}.0-0`;
8872
8872
  }
8873
8873
  } else {
8874
8874
  ret = `>=${M}.${m}.${p} <${+M + 1}.0.0-0`;
@@ -11023,12 +11023,14 @@ function stripAnsi(string) {
11023
11023
  }
11024
11024
 
11025
11025
  // ../core/dist/index.js
11026
+ import { fromFoundationRequirements as fromFoundationRequirements2 } from "@inflowpayai/x402-buyer";
11027
+ import { sellerProbe as sellerProbe5 } from "@inflowpayai/x402-buyer/probe";
11026
11028
  import {
11027
11029
  describeBody as describeBody2,
11028
11030
  parseHeaderFlag,
11029
11031
  parseHeaderFlags,
11030
11032
  replayWithPayment as replayWithPayment2,
11031
- sellerProbe as sellerProbe5,
11033
+ sellerProbe as sellerProbe6,
11032
11034
  X402HeaderFlagFormatError
11033
11035
  } from "@inflowpayai/x402-buyer/probe";
11034
11036
  var REDACTED_BODY_FIELDS = /* @__PURE__ */ new Set([
@@ -11731,6 +11733,15 @@ function buildNoFilteredMatchMessage(decoded, filters) {
11731
11733
  function isSuccessStatus(status) {
11732
11734
  return status >= 200 && status < 300;
11733
11735
  }
11736
+ function parseX402HeaderFromProbe(probe) {
11737
+ const headerValue = readHeader(Object.fromEntries(probe.headers.entries()), HEADERS.PAYMENT_REQUIRED);
11738
+ if (headerValue === void 0) return { kind: "absent" };
11739
+ try {
11740
+ return { kind: "parsed", decoded: decodePaymentRequiredHeader(headerValue) };
11741
+ } catch (err) {
11742
+ return { kind: "error", code: "DECODE_FAILED", message: err instanceof Error ? err.message : String(err) };
11743
+ }
11744
+ }
11734
11745
  function reduceX402Inspect(state, event) {
11735
11746
  switch (event.type) {
11736
11747
  case "accepts":
@@ -11775,8 +11786,8 @@ async function runInspectPipeline(deps, emit) {
11775
11786
  emit({ type: "no-payment", result: result2 });
11776
11787
  return;
11777
11788
  }
11778
- const headerValue = readHeader(Object.fromEntries(probe.headers.entries()), HEADERS.PAYMENT_REQUIRED);
11779
- if (headerValue === void 0) {
11789
+ const parse = parseX402HeaderFromProbe(probe);
11790
+ if (parse.kind === "absent") {
11780
11791
  emit({
11781
11792
  type: "errored",
11782
11793
  code: INVALID_402_CODE,
@@ -11784,17 +11795,11 @@ async function runInspectPipeline(deps, emit) {
11784
11795
  });
11785
11796
  return;
11786
11797
  }
11787
- let decoded;
11788
- try {
11789
- decoded = decodePaymentRequiredHeader(headerValue);
11790
- } catch (err) {
11791
- emit({
11792
- type: "errored",
11793
- code: "DECODE_FAILED",
11794
- message: err instanceof Error ? err.message : String(err)
11795
- });
11798
+ if (parse.kind === "error") {
11799
+ emit({ type: "errored", code: parse.code, message: parse.message });
11796
11800
  return;
11797
11801
  }
11802
+ const decoded = parse.decoded;
11798
11803
  const filters = {
11799
11804
  ...deps.schemeFilter !== void 0 ? { scheme: deps.schemeFilter } : {},
11800
11805
  ...deps.networkFilter !== void 0 ? { network: deps.networkFilter } : {},
@@ -12308,6 +12313,15 @@ function buildNoFilteredMatchMessage2(challenges, filters) {
12308
12313
  }).join(", ");
12309
12314
  return `Seller has no \`inflow\` challenge matching ${filterDescription}. Available: ${available || "(none)"}.`;
12310
12315
  }
12316
+ function parseMppHeaderFromProbe(probe) {
12317
+ const headerValues = readHeaderAll(probe.headers, HEADERS3.WWW_AUTHENTICATE);
12318
+ if (headerValues.length === 0) return { kind: "absent" };
12319
+ try {
12320
+ return { kind: "parsed", challenges: parseChallengeHeaders(headerValues) };
12321
+ } catch (err) {
12322
+ return { kind: "error", code: "DECODE_FAILED", message: err instanceof Error ? err.message : String(err) };
12323
+ }
12324
+ }
12311
12325
  function reduceMppInspect(state, event) {
12312
12326
  switch (event.type) {
12313
12327
  case "challenges":
@@ -12350,8 +12364,8 @@ async function runMppInspectPipeline(deps, emit) {
12350
12364
  });
12351
12365
  return;
12352
12366
  }
12353
- const headerValues = readHeaderAll(probe.headers, HEADERS3.WWW_AUTHENTICATE);
12354
- if (headerValues.length === 0) {
12367
+ const parse = parseMppHeaderFromProbe(probe);
12368
+ if (parse.kind === "absent") {
12355
12369
  emit({
12356
12370
  type: "errored",
12357
12371
  code: INVALID_402_CODE2,
@@ -12359,14 +12373,11 @@ async function runMppInspectPipeline(deps, emit) {
12359
12373
  });
12360
12374
  return;
12361
12375
  }
12362
- let challenges;
12363
- try {
12364
- challenges = parseChallengeHeaders(headerValues);
12365
- } catch (err) {
12366
- emit({ type: "errored", code: "DECODE_FAILED", message: err instanceof Error ? err.message : String(err) });
12376
+ if (parse.kind === "error") {
12377
+ emit({ type: "errored", code: parse.code, message: parse.message });
12367
12378
  return;
12368
12379
  }
12369
- const inflowChallenges = filterInflowChallenges(challenges);
12380
+ const inflowChallenges = filterInflowChallenges(parse.challenges);
12370
12381
  if (inflowChallenges.length === 0) {
12371
12382
  emit({ type: "errored", code: NO_INFLOW_MATCH_CODE2, message: NO_INFLOW_MATCH_MESSAGE2 });
12372
12383
  return;
@@ -12854,7 +12865,7 @@ function augmentMpp(mppResource, resolvedApiBaseUrl2) {
12854
12865
  var DEFAULT_RETRIES = 3;
12855
12866
  var DEFAULT_TIMEOUT_MS = 3e4;
12856
12867
  var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
12857
- var SDK_USER_AGENT = `@inflowpayai/inflow-core/${true ? "0.6.1" : "0.0.0"}`;
12868
+ var SDK_USER_AGENT = `@inflowpayai/inflow-core/${true ? "0.6.2" : "0.0.0"}`;
12858
12869
  function sleep2(ms, signal) {
12859
12870
  return new Promise((resolve, reject) => {
12860
12871
  const onAbort = () => {
@@ -13496,6 +13507,85 @@ async function probeSession(userResource, options) {
13496
13507
  function hasSession(storage2, hasApiKey) {
13497
13508
  return hasApiKey() || storage2.isAuthenticated();
13498
13509
  }
13510
+ function reduceCombinedInspect(state, event) {
13511
+ switch (event.type) {
13512
+ case "inspected":
13513
+ return { kind: "inspected", result: event.result };
13514
+ case "no-payment":
13515
+ return { kind: "no-payment", result: event.result };
13516
+ case "errored":
13517
+ return { kind: "error", code: event.code, message: event.message };
13518
+ default:
13519
+ return state;
13520
+ }
13521
+ }
13522
+ function buildMppSection(probe) {
13523
+ const parse = parseMppHeaderFromProbe(probe);
13524
+ if (parse.kind === "absent") return { kind: "absent" };
13525
+ if (parse.kind === "error") return { kind: "error", code: parse.code, message: parse.message };
13526
+ const inflowChallenges = filterInflowChallenges(parse.challenges);
13527
+ if (inflowChallenges.length === 0) {
13528
+ const methods = [...new Set(parse.challenges.map((c) => c.method))].sort((a, b) => a.localeCompare(b));
13529
+ return { kind: "none-inflow", methods };
13530
+ }
13531
+ const realm = inflowChallenges[0]?.realm ?? "";
13532
+ return { kind: "challenges", realm, challenges: inflowChallenges.map(summarizeChallenge) };
13533
+ }
13534
+ function buildX402Section(probe) {
13535
+ const parse = parseX402HeaderFromProbe(probe);
13536
+ if (parse.kind === "absent") return { kind: "absent" };
13537
+ if (parse.kind === "error") return { kind: "error", code: parse.code, message: parse.message };
13538
+ const decoded = parse.decoded;
13539
+ return {
13540
+ kind: "accepts",
13541
+ resource: decoded.resource.url,
13542
+ x402Version: decoded.x402Version,
13543
+ accepts: fromFoundationRequirements2(decoded.accepts),
13544
+ ...decoded.extensions !== void 0 ? { extensions: decoded.extensions } : {}
13545
+ };
13546
+ }
13547
+ async function runCombinedInspectPipeline(deps, emit) {
13548
+ let probe;
13549
+ try {
13550
+ probe = await sellerProbe5(deps.url, deps.probeOptions);
13551
+ } catch (err) {
13552
+ emit({ type: "errored", code: "INSPECT_FAILED", message: err instanceof Error ? err.message : String(err) });
13553
+ return;
13554
+ }
13555
+ if (probe.status !== 402) {
13556
+ if (!isSuccessStatus(probe.status)) {
13557
+ emit({
13558
+ type: "errored",
13559
+ code: UNEXPECTED_PROBE_STATUS_CODE,
13560
+ message: `Seller returned status ${String(probe.status)} during probe; expected 2xx (no payment) or 402 (payment required).`
13561
+ });
13562
+ return;
13563
+ }
13564
+ emit({
13565
+ type: "no-payment",
13566
+ result: {
13567
+ outcome: "no-payment-required",
13568
+ url: deps.url,
13569
+ method: deps.probeOptions.method,
13570
+ status: probe.status,
13571
+ contentType: probe.contentType,
13572
+ bodySizeBytes: probe.bytes.byteLength
13573
+ }
13574
+ });
13575
+ return;
13576
+ }
13577
+ emit({
13578
+ type: "inspected",
13579
+ result: {
13580
+ outcome: "inspected",
13581
+ url: deps.url,
13582
+ method: deps.probeOptions.method,
13583
+ status: probe.status,
13584
+ mpp: buildMppSection(probe),
13585
+ x402: buildX402Section(probe)
13586
+ }
13587
+ });
13588
+ }
13499
13589
  async function runBalancesList(input) {
13500
13590
  return input.balanceResource.list();
13501
13591
  }
@@ -14576,94 +14666,11 @@ function createDepositAddressesCli(depositAddressResource, authStorage2, inflow2
14576
14666
  return cli2;
14577
14667
  }
14578
14668
 
14579
- // src/commands/mpp/index.tsx
14580
- import { chmodSync, writeFileSync as writeFileSync2 } from "fs";
14581
- import { resolve as resolvePath2 } from "path";
14582
- import { Cli as Cli4 } from "incur";
14583
-
14584
- // src/commands/mpp/cancel.tsx
14669
+ // src/commands/mpp/inspect.tsx
14585
14670
  import { Box as Box9, Text as Text10 } from "ink";
14586
14671
  import Spinner7 from "ink-spinner";
14587
- import { useCallback as useCallback5 } from "react";
14588
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
14589
- var CancelView = ({ approvalId, cancel, onComplete }) => {
14590
- const action = useCallback5(() => cancel(), [cancel]);
14591
- const { finish } = useFlowExit(onComplete);
14592
- const { status } = useFlowState(action, finish);
14593
- if (status === "loading") {
14594
- return /* @__PURE__ */ jsx12(Box9, { children: /* @__PURE__ */ jsxs9(Text10, { color: "cyan", children: [
14595
- /* @__PURE__ */ jsx12(Spinner7, { type: "dots" }),
14596
- " Cancelling approval ",
14597
- approvalId,
14598
- "..."
14599
- ] }) });
14600
- }
14601
- return /* @__PURE__ */ jsx12(Box9, { children: /* @__PURE__ */ jsxs9(Text10, { color: "green", children: [
14602
- "\u2713 Cancelled approval ",
14603
- approvalId,
14604
- " (best-effort)"
14605
- ] }) });
14606
- };
14607
-
14608
- // src/commands/mpp/decode.tsx
14609
- import { Box as Box10, Text as Text11 } from "ink";
14610
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
14611
- function ChallengeBody({ challenge }) {
14612
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
14613
- /* @__PURE__ */ jsx13(Text11, { bold: true, children: "Challenge" }),
14614
- /* @__PURE__ */ jsxs10(Text11, { children: [
14615
- "method/intent: ",
14616
- /* @__PURE__ */ jsxs10(Text11, { color: "yellow", children: [
14617
- challenge.method,
14618
- " / ",
14619
- challenge.intent
14620
- ] })
14621
- ] }),
14622
- /* @__PURE__ */ jsx13(Text11, { children: `id: ${challenge.id}` }),
14623
- /* @__PURE__ */ jsx13(Text11, { children: `realm: ${challenge.realm}` }),
14624
- challenge.amount !== void 0 ? /* @__PURE__ */ jsx13(Text11, { children: `amount: ${challenge.amount}${challenge.currency !== void 0 ? ` ${challenge.currency}` : ""}` }) : null,
14625
- challenge.rail !== void 0 ? /* @__PURE__ */ jsx13(Text11, { children: `rail: ${challenge.rail}` }) : null,
14626
- challenge.expires !== void 0 ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `expires: ${challenge.expires}` }) : null
14627
- ] });
14628
- }
14629
- var DecodeView = ({ result }) => {
14630
- if (result.kind === "challenge") {
14631
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingY: 1, children: [
14632
- /* @__PURE__ */ jsx13(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text11, { bold: true, children: "Decoded WWW-Authenticate: Payment" }) }),
14633
- /* @__PURE__ */ jsx13(ChallengeBody, { challenge: result.challenge })
14634
- ] });
14635
- }
14636
- if (result.kind === "credential") {
14637
- const { credential } = result;
14638
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingY: 1, children: [
14639
- /* @__PURE__ */ jsx13(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text11, { bold: true, children: "Decoded Authorization: Payment credential" }) }),
14640
- /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
14641
- /* @__PURE__ */ jsx13(Text11, { children: `challenge id: ${credential.challenge.id}` }),
14642
- /* @__PURE__ */ jsx13(Text11, { children: `method: ${credential.challenge.method}` }),
14643
- /* @__PURE__ */ jsx13(Text11, { children: `source: ${credential.source}` }),
14644
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `payload keys: ${Object.keys(credential.payload).join(", ") || "(none)"}` })
14645
- ] })
14646
- ] });
14647
- }
14648
- const { receipt } = result;
14649
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingY: 1, children: [
14650
- /* @__PURE__ */ jsx13(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text11, { bold: true, children: "Decoded Payment-Receipt" }) }),
14651
- /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
14652
- /* @__PURE__ */ jsxs10(Text11, { children: [
14653
- "status: ",
14654
- /* @__PURE__ */ jsx13(Text11, { color: "green", children: receipt.status })
14655
- ] }),
14656
- /* @__PURE__ */ jsx13(Text11, { children: `reference: ${receipt.reference}` }),
14657
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `timestamp: ${receipt.timestamp}` })
14658
- ] })
14659
- ] });
14660
- };
14661
-
14662
- // src/commands/mpp/inspect.tsx
14663
- import { Box as Box11, Text as Text12 } from "ink";
14664
- import Spinner8 from "ink-spinner";
14665
14672
  import { useEffect as useEffect6, useReducer as useReducer4 } from "react";
14666
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
14673
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
14667
14674
  function orDash(value) {
14668
14675
  return value === void 0 || value === "" ? "\u2014" : value;
14669
14676
  }
@@ -14694,8 +14701,8 @@ var InspectView = ({ url, method, deps, onComplete }) => {
14694
14701
  }
14695
14702
  }, [phase, finish]);
14696
14703
  if (phase.kind === "probing") {
14697
- return /* @__PURE__ */ jsx14(Box11, { children: /* @__PURE__ */ jsxs11(Text12, { color: "cyan", children: [
14698
- /* @__PURE__ */ jsx14(Spinner8, { type: "dots" }),
14704
+ return /* @__PURE__ */ jsx12(Box9, { children: /* @__PURE__ */ jsxs9(Text10, { color: "cyan", children: [
14705
+ /* @__PURE__ */ jsx12(Spinner7, { type: "dots" }),
14699
14706
  " Probing ",
14700
14707
  method,
14701
14708
  " ",
@@ -14705,37 +14712,37 @@ var InspectView = ({ url, method, deps, onComplete }) => {
14705
14712
  }
14706
14713
  if (phase.kind === "no-payment") {
14707
14714
  const { result: result2 } = phase;
14708
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
14709
- /* @__PURE__ */ jsx14(Text12, { color: "green", children: "\u2713 Seller accepted without payment" }),
14710
- /* @__PURE__ */ jsx14(Text12, { children: `status: ${String(result2.status)}` }),
14711
- result2.contentType !== void 0 ? /* @__PURE__ */ jsx14(Text12, { children: `content-type: ${result2.contentType}` }) : null,
14712
- /* @__PURE__ */ jsx14(Text12, { children: `response size: ${String(result2.bodySizeBytes)} bytes` }),
14713
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Use `mpp pay` to fetch the body." })
14715
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
14716
+ /* @__PURE__ */ jsx12(Text10, { color: "green", children: "\u2713 Seller accepted without payment" }),
14717
+ /* @__PURE__ */ jsx12(Text10, { children: `status: ${String(result2.status)}` }),
14718
+ result2.contentType !== void 0 ? /* @__PURE__ */ jsx12(Text10, { children: `content-type: ${result2.contentType}` }) : null,
14719
+ /* @__PURE__ */ jsx12(Text10, { children: `response size: ${String(result2.bodySizeBytes)} bytes` }),
14720
+ /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: "Use `mpp pay` to fetch the body." })
14714
14721
  ] });
14715
14722
  }
14716
14723
  if (phase.kind === "error") {
14717
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
14718
- /* @__PURE__ */ jsxs11(Text12, { color: "red", children: [
14724
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
14725
+ /* @__PURE__ */ jsxs9(Text10, { color: "red", children: [
14719
14726
  "\u2717 ",
14720
14727
  phase.code
14721
14728
  ] }),
14722
- /* @__PURE__ */ jsx14(Text12, { color: "red", children: phase.message })
14729
+ /* @__PURE__ */ jsx12(Text10, { color: "red", children: phase.message })
14723
14730
  ] });
14724
14731
  }
14725
14732
  const { result } = phase;
14726
14733
  const count = result.challenges.length;
14727
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
14728
- /* @__PURE__ */ jsxs11(Text12, { children: [
14729
- /* @__PURE__ */ jsx14(Text12, { bold: true, children: "WWW-Authenticate: Payment" }),
14734
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
14735
+ /* @__PURE__ */ jsxs9(Text10, { children: [
14736
+ /* @__PURE__ */ jsx12(Text10, { bold: true, children: "WWW-Authenticate: Payment" }),
14730
14737
  " for ",
14731
- /* @__PURE__ */ jsx14(Text12, { color: "cyan", children: result.url }),
14738
+ /* @__PURE__ */ jsx12(Text10, { color: "cyan", children: result.url }),
14732
14739
  " \xB7 ",
14733
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `realm ${result.realm}` }),
14740
+ /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: `realm ${result.realm}` }),
14734
14741
  " \xB7 ",
14735
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `${String(count)} challenge${count === 1 ? "" : "s"}` })
14742
+ /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: `${String(count)} challenge${count === 1 ? "" : "s"}` })
14736
14743
  ] }),
14737
- /* @__PURE__ */ jsx14(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx14(Table, { columns: COLUMNS3, rows: [...result.challenges] }) }),
14738
- /* @__PURE__ */ jsx14(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Use --format json to see challenge ids and digests." }) })
14744
+ /* @__PURE__ */ jsx12(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx12(Table, { columns: COLUMNS3, rows: [...result.challenges] }) }),
14745
+ /* @__PURE__ */ jsx12(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: "Use --format json to see challenge ids and digests." }) })
14739
14746
  ] });
14740
14747
  };
14741
14748
  function challengeToFrame(challenge) {
@@ -14776,63 +14783,53 @@ function buildNoPaymentFrame(result) {
14776
14783
  return frame;
14777
14784
  }
14778
14785
 
14779
- // src/commands/mpp/pay.tsx
14780
- import { Box as Box12, Text as Text13, useInput as useInput3 } from "ink";
14781
- import Spinner9 from "ink-spinner";
14782
- import { useEffect as useEffect7, useReducer as useReducer5, useState as useState3 } from "react";
14783
- import { Fragment, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
14784
- var PayView = ({ url, method, deps, onComplete, onCancel }) => {
14786
+ // src/commands/x402/inspect.tsx
14787
+ import { Box as Box10, Text as Text11 } from "ink";
14788
+ import Spinner8 from "ink-spinner";
14789
+ import { useEffect as useEffect7, useReducer as useReducer5 } from "react";
14790
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
14791
+ function summarizeExtra(extra) {
14792
+ if (extra === void 0) return "\u2014";
14793
+ const keys = Object.keys(extra);
14794
+ if (keys.length === 0) return "\u2014";
14795
+ return [...keys].sort((a, b) => a.localeCompare(b)).join(", ");
14796
+ }
14797
+ function formatTimeout(seconds) {
14798
+ return `${String(seconds)}s`;
14799
+ }
14800
+ function formatAsset(asset) {
14801
+ return asset === "" ? "\u2014" : asset;
14802
+ }
14803
+ var COLUMNS4 = [
14804
+ { header: "Scheme", cell: (r) => r.scheme },
14805
+ { header: "Network", cell: (r) => r.network },
14806
+ { header: "Amount", cell: (r) => r.amount },
14807
+ { header: "Asset", cell: (r) => formatAsset(r.asset) },
14808
+ { header: "Pay To", cell: (r) => r.payTo },
14809
+ { header: "Timeout", cell: (r) => formatTimeout(r.maxTimeoutSeconds) },
14810
+ { header: "Extra", cell: (r) => summarizeExtra(r.extra) }
14811
+ ];
14812
+ var InspectView2 = ({ url, method, deps, onComplete }) => {
14785
14813
  const initial = { kind: "probing" };
14786
- const [phase, dispatch] = useReducer5(reduceMppPay, initial);
14787
- const [cancelling, setCancelling] = useState3(false);
14788
- const { finish, cancelThenFinish } = useFlowExit(onComplete);
14789
- const created = phase.kind === "created" ? phase.created : void 0;
14790
- const approvalUrl = created?.approvalUrl;
14791
- const approvalId = created?.approvalId;
14792
- useInput3(
14793
- (_input, key) => {
14794
- if (approvalUrl === void 0) return;
14795
- if (key.return) {
14796
- openUrl(approvalUrl);
14797
- return;
14798
- }
14799
- if (key.escape && approvalId !== void 0) {
14800
- setCancelling(true);
14801
- cancelThenFinish(() => onCancel?.(approvalId), {
14802
- kind: "error",
14803
- code: "APPROVAL_CANCELLED",
14804
- message: `Approval ${approvalId} cancelled.`
14805
- });
14806
- }
14807
- },
14808
- { isActive: approvalUrl !== void 0 && !cancelling }
14809
- );
14814
+ const [phase, dispatch] = useReducer5(reduceX402Inspect, initial);
14815
+ const { finish } = useFlowExit(onComplete);
14810
14816
  useEffect7(() => {
14811
- const controller = new AbortController();
14812
14817
  let cancelled = false;
14813
- const runDeps = { ...deps, signal: controller.signal };
14814
- void runMppPayPipeline(runDeps, (event) => {
14818
+ void runInspectPipeline(deps, (event) => {
14815
14819
  if (!cancelled) dispatch(event);
14816
14820
  });
14817
14821
  return () => {
14818
14822
  cancelled = true;
14819
- controller.abort();
14820
14823
  };
14821
14824
  }, [deps]);
14822
14825
  useEffect7(() => {
14823
- if (phase.kind === "success" || phase.kind === "seller-rejected" || phase.kind === "no-payment-final" || phase.kind === "error") {
14826
+ if (phase.kind === "accepts" || phase.kind === "no-payment" || phase.kind === "error") {
14824
14827
  finish(phase);
14825
14828
  }
14826
14829
  }, [phase, finish]);
14827
- if (cancelling) {
14828
- return /* @__PURE__ */ jsx15(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "yellow", children: [
14829
- /* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
14830
- " Cancelling approval..."
14831
- ] }) });
14832
- }
14833
14830
  if (phase.kind === "probing") {
14834
- return /* @__PURE__ */ jsx15(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
14835
- /* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
14831
+ return /* @__PURE__ */ jsx13(Box10, { children: /* @__PURE__ */ jsxs10(Text11, { color: "cyan", children: [
14832
+ /* @__PURE__ */ jsx13(Spinner8, { type: "dots" }),
14836
14833
  " Probing ",
14837
14834
  method,
14838
14835
  " ",
@@ -14841,193 +14838,720 @@ var PayView = ({ url, method, deps, onComplete, onCancel }) => {
14841
14838
  ] }) });
14842
14839
  }
14843
14840
  if (phase.kind === "no-payment") {
14844
- return /* @__PURE__ */ jsx15(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
14845
- /* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
14846
- " Seller accepted without payment (status ",
14847
- String(phase.probe.status),
14848
- "); finalising..."
14849
- ] }) });
14850
- }
14851
- if (phase.kind === "decoded") {
14852
- return /* @__PURE__ */ jsx15(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
14853
- /* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
14854
- " Fulfilling ",
14855
- phase.challenge.amount ?? "",
14856
- " ",
14857
- phase.challenge.currency ?? "",
14858
- " ",
14859
- "challenge..."
14860
- ] }) });
14841
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
14842
+ /* @__PURE__ */ jsx13(Text11, { color: "green", children: "\u2713 Seller accepted without payment" }),
14843
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Use `x402 pay` to fetch the body." })
14844
+ ] });
14861
14845
  }
14862
- if (phase.kind === "created") {
14863
- const { created: created2 } = phase;
14864
- if (created2.state !== "pending") {
14865
- return /* @__PURE__ */ jsx15(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
14866
- /* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
14867
- " Transaction ",
14868
- created2.transactionId,
14869
- " ready; replaying..."
14870
- ] }) });
14871
- }
14872
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingY: 1, children: [
14873
- /* @__PURE__ */ jsx15(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text13, { bold: true, children: "Approval required" }) }),
14874
- /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
14875
- /* @__PURE__ */ jsx15(Text13, { children: `transaction: ${created2.transactionId}` }),
14876
- created2.approvalUrl !== void 0 ? /* @__PURE__ */ jsxs12(Fragment, { children: [
14877
- /* @__PURE__ */ jsxs12(Text13, { children: [
14878
- "Open: ",
14879
- /* @__PURE__ */ jsx15(Text13, { bold: true, color: "cyan", children: created2.approvalUrl })
14880
- ] }),
14881
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Press Enter to open in browser." }),
14882
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Press Escape to cancel." })
14883
- ] }) : null
14846
+ if (phase.kind === "error") {
14847
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
14848
+ /* @__PURE__ */ jsxs10(Text11, { color: "red", children: [
14849
+ "\u2717 ",
14850
+ phase.code
14884
14851
  ] }),
14885
- /* @__PURE__ */ jsx15(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
14886
- /* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
14887
- " Waiting for approval..."
14888
- ] }) })
14852
+ /* @__PURE__ */ jsx13(Text11, { color: "red", children: phase.message })
14889
14853
  ] });
14890
14854
  }
14891
- if (phase.kind === "replaying") {
14892
- return /* @__PURE__ */ jsx15(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
14893
- /* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
14894
- " Replaying request with Authorization: Payment..."
14895
- ] }) });
14896
- }
14897
- if (phase.kind === "success") {
14898
- const { result } = phase;
14899
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
14900
- /* @__PURE__ */ jsxs12(Text13, { color: "green", children: [
14901
- "\u2713 Paid (intent ",
14902
- result.intent,
14903
- ")"
14855
+ const { result } = phase;
14856
+ const acceptsCount = result.accepts.length;
14857
+ const extensionsLine = result.extensions !== void 0 ? Object.keys(result.extensions).join(", ") : null;
14858
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
14859
+ /* @__PURE__ */ jsxs10(Text11, { children: [
14860
+ /* @__PURE__ */ jsx13(Text11, { bold: true, children: "PAYMENT-REQUIRED" }),
14861
+ " for ",
14862
+ /* @__PURE__ */ jsx13(Text11, { color: "cyan", children: result.resource }),
14863
+ " \xB7 ",
14864
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `x402Version ${String(result.x402Version)}` }),
14865
+ " \xB7 ",
14866
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `${String(acceptsCount)} accept${acceptsCount === 1 ? "" : "s"}` })
14867
+ ] }),
14868
+ extensionsLine !== null ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `extensions: ${extensionsLine}` }) : null,
14869
+ /* @__PURE__ */ jsx13(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx13(Table, { columns: COLUMNS4, rows: result.accepts }) }),
14870
+ /* @__PURE__ */ jsx13(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Use --format json to inspect extras values." }) })
14871
+ ] });
14872
+ };
14873
+ function acceptToFrame(entry) {
14874
+ const row = {
14875
+ scheme: entry.scheme,
14876
+ network: entry.network,
14877
+ amount: entry.amount,
14878
+ asset: entry.asset,
14879
+ pay_to: entry.payTo,
14880
+ max_timeout_seconds: entry.maxTimeoutSeconds
14881
+ };
14882
+ if (entry.extra !== void 0) row.extra = entry.extra;
14883
+ return row;
14884
+ }
14885
+ function buildAcceptsFrame(result) {
14886
+ const frame = {
14887
+ outcome: "accepts",
14888
+ url: result.url,
14889
+ method: result.method,
14890
+ resource: result.resource,
14891
+ x402_version: result.x402Version,
14892
+ accepts: result.accepts.map(acceptToFrame)
14893
+ };
14894
+ if (result.extensions !== void 0) frame.extensions = result.extensions;
14895
+ return frame;
14896
+ }
14897
+ function buildNoPaymentFrame2(result) {
14898
+ const frame = {
14899
+ outcome: "no-payment-required",
14900
+ url: result.url,
14901
+ method: result.method,
14902
+ status: result.status,
14903
+ body_size_bytes: result.bodySizeBytes
14904
+ };
14905
+ if (result.contentType !== void 0) frame.content_type = result.contentType;
14906
+ return frame;
14907
+ }
14908
+
14909
+ // src/commands/inspect/combined-inspect-view.tsx
14910
+ import { Box as Box11, Text as Text12 } from "ink";
14911
+ import Spinner9 from "ink-spinner";
14912
+ import { useEffect as useEffect8, useReducer as useReducer6 } from "react";
14913
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
14914
+ function orDash2(value) {
14915
+ return value === void 0 || value === "" ? "\u2014" : value;
14916
+ }
14917
+ var MPP_TRIAGE_COLUMNS = [
14918
+ { header: "Method", cell: (c) => c.method },
14919
+ { header: "Intent", cell: (c) => c.intent },
14920
+ { header: "Amount", cell: (c) => orDash2(c.amount) },
14921
+ { header: "Currency", cell: (c) => orDash2(c.currency) },
14922
+ { header: "Rail", cell: (c) => orDash2(c.rail) }
14923
+ ];
14924
+ var X402_TRIAGE_COLUMNS = [
14925
+ { header: "Scheme", cell: (r) => r.scheme },
14926
+ { header: "Network", cell: (r) => r.network },
14927
+ { header: "Amount", cell: (r) => r.amount },
14928
+ { header: "Asset", cell: (r) => orDash2(r.asset) }
14929
+ ];
14930
+ function detectedProtocols(mpp, x402) {
14931
+ const out = [];
14932
+ if (mpp.kind === "challenges" && mpp.challenges.length > 0) out.push("mpp");
14933
+ if (x402.kind === "accepts" && x402.accepts.length > 0) out.push("x402");
14934
+ return out;
14935
+ }
14936
+ var MppSectionView = ({ section }) => {
14937
+ if (section.kind === "absent") {
14938
+ return /* @__PURE__ */ jsxs11(Text12, { children: [
14939
+ /* @__PURE__ */ jsx14(Text12, { bold: true, children: "\u2500\u2500 MPP \u2500\u2500" }),
14940
+ " ",
14941
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "none advertised" })
14942
+ ] });
14943
+ }
14944
+ if (section.kind === "none-inflow") {
14945
+ const methods = section.methods.length > 0 ? section.methods.join(", ") : "(unknown)";
14946
+ return /* @__PURE__ */ jsxs11(Text12, { children: [
14947
+ /* @__PURE__ */ jsx14(Text12, { bold: true, children: "\u2500\u2500 MPP \u2500\u2500" }),
14948
+ " ",
14949
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `advertised method(s) not payable by InFlow: ${methods} (only \`inflow\` is supported)` })
14950
+ ] });
14951
+ }
14952
+ if (section.kind === "error") {
14953
+ return /* @__PURE__ */ jsxs11(Text12, { children: [
14954
+ /* @__PURE__ */ jsx14(Text12, { bold: true, children: "\u2500\u2500 MPP \u2500\u2500" }),
14955
+ " ",
14956
+ /* @__PURE__ */ jsx14(Text12, { color: "yellow", children: `header present but undecodable (${section.code})` })
14957
+ ] });
14958
+ }
14959
+ const count = section.challenges.length;
14960
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
14961
+ /* @__PURE__ */ jsxs11(Text12, { children: [
14962
+ /* @__PURE__ */ jsx14(Text12, { bold: true, children: "\u2500\u2500 MPP \u2500\u2500" }),
14963
+ " ",
14964
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "WWW-Authenticate: Payment" }),
14965
+ " \xB7 ",
14966
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `realm ${section.realm}` }),
14967
+ " \xB7 ",
14968
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `${String(count)} challenge${count === 1 ? "" : "s"}` })
14969
+ ] }),
14970
+ /* @__PURE__ */ jsx14(Table, { columns: MPP_TRIAGE_COLUMNS, rows: [...section.challenges] })
14971
+ ] });
14972
+ };
14973
+ var X402SectionView = ({ section }) => {
14974
+ if (section.kind === "absent") {
14975
+ return /* @__PURE__ */ jsxs11(Text12, { children: [
14976
+ /* @__PURE__ */ jsx14(Text12, { bold: true, children: "\u2500\u2500 x402 \u2500\u2500" }),
14977
+ " ",
14978
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "none advertised" })
14979
+ ] });
14980
+ }
14981
+ if (section.kind === "error") {
14982
+ return /* @__PURE__ */ jsxs11(Text12, { children: [
14983
+ /* @__PURE__ */ jsx14(Text12, { bold: true, children: "\u2500\u2500 x402 \u2500\u2500" }),
14984
+ " ",
14985
+ /* @__PURE__ */ jsx14(Text12, { color: "yellow", children: `header present but undecodable (${section.code})` })
14986
+ ] });
14987
+ }
14988
+ const count = section.accepts.length;
14989
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
14990
+ /* @__PURE__ */ jsxs11(Text12, { children: [
14991
+ /* @__PURE__ */ jsx14(Text12, { bold: true, children: "\u2500\u2500 x402 \u2500\u2500" }),
14992
+ " ",
14993
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "PAYMENT-REQUIRED" }),
14994
+ " \xB7 ",
14995
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `x402Version ${String(section.x402Version)}` }),
14996
+ " \xB7 ",
14997
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `${String(count)} accept${count === 1 ? "" : "s"}` })
14998
+ ] }),
14999
+ /* @__PURE__ */ jsx14(Table, { columns: X402_TRIAGE_COLUMNS, rows: [...section.accepts] })
15000
+ ] });
15001
+ };
15002
+ var CombinedInspectView = ({ url, method, deps, onComplete }) => {
15003
+ const initial = { kind: "probing" };
15004
+ const [phase, dispatch] = useReducer6(reduceCombinedInspect, initial);
15005
+ const { finish } = useFlowExit(onComplete);
15006
+ useEffect8(() => {
15007
+ let cancelled = false;
15008
+ void runCombinedInspectPipeline(deps, (event) => {
15009
+ if (!cancelled) dispatch(event);
15010
+ });
15011
+ return () => {
15012
+ cancelled = true;
15013
+ };
15014
+ }, [deps]);
15015
+ useEffect8(() => {
15016
+ if (phase.kind === "inspected" || phase.kind === "no-payment" || phase.kind === "error") {
15017
+ finish(phase);
15018
+ }
15019
+ }, [phase, finish]);
15020
+ if (phase.kind === "probing") {
15021
+ return /* @__PURE__ */ jsx14(Box11, { children: /* @__PURE__ */ jsxs11(Text12, { color: "cyan", children: [
15022
+ /* @__PURE__ */ jsx14(Spinner9, { type: "dots" }),
15023
+ " Probing ",
15024
+ method,
15025
+ " ",
15026
+ url,
15027
+ "..."
15028
+ ] }) });
15029
+ }
15030
+ if (phase.kind === "no-payment") {
15031
+ const { result: result2 } = phase;
15032
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
15033
+ /* @__PURE__ */ jsx14(Text12, { color: "green", children: "\u2713 Seller accepted without payment" }),
15034
+ /* @__PURE__ */ jsx14(Text12, { children: `status: ${String(result2.status)}` }),
15035
+ result2.contentType !== void 0 ? /* @__PURE__ */ jsx14(Text12, { children: `content-type: ${result2.contentType}` }) : null,
15036
+ /* @__PURE__ */ jsx14(Text12, { children: `response size: ${String(result2.bodySizeBytes)} bytes` }),
15037
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "No InFlow-payable challenge advertised." })
15038
+ ] });
15039
+ }
15040
+ if (phase.kind === "error") {
15041
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
15042
+ /* @__PURE__ */ jsxs11(Text12, { color: "red", children: [
15043
+ "\u2717 ",
15044
+ phase.code
15045
+ ] }),
15046
+ /* @__PURE__ */ jsx14(Text12, { color: "red", children: phase.message })
15047
+ ] });
15048
+ }
15049
+ const { result } = phase;
15050
+ const detected = detectedProtocols(result.mpp, result.x402);
15051
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
15052
+ /* @__PURE__ */ jsxs11(Text12, { children: [
15053
+ /* @__PURE__ */ jsx14(Text12, { bold: true, children: "PAYMENT-REQUIRED" }),
15054
+ " for ",
15055
+ /* @__PURE__ */ jsx14(Text12, { color: "cyan", children: result.url }),
15056
+ " \xB7 ",
15057
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `detected: ${detected.length > 0 ? detected.join(", ") : "none"}` })
15058
+ ] }),
15059
+ /* @__PURE__ */ jsx14(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx14(MppSectionView, { section: result.mpp }) }),
15060
+ /* @__PURE__ */ jsx14(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx14(X402SectionView, { section: result.x402 }) }),
15061
+ /* @__PURE__ */ jsx14(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Full detail (pay-to, timeout, extras, ids/digests): `inflow mpp inspect` / `inflow x402 inspect`, or --format json." }) })
15062
+ ] });
15063
+ };
15064
+
15065
+ // src/commands/inspect/schema.ts
15066
+ import { z as z4 } from "incur";
15067
+ var inspectArgs = z4.object({
15068
+ url: z4.string().describe("The resource URL to probe for MPP and/or x402 payment challenges. No payment is made.")
15069
+ });
15070
+ var inspectOptions = z4.object({
15071
+ method: z4.string().default("GET").describe("HTTP method for the probe request."),
15072
+ data: z4.string().optional().describe(
15073
+ "Request body for the probe. JSON or raw text. Content-Type defaults to application/json when --data is set unless a --header overrides it."
15074
+ ),
15075
+ header: z4.array(z4.string()).default([]).describe('Repeatable. "Name: Value" format.')
15076
+ });
15077
+
15078
+ // src/commands/inspect/index.tsx
15079
+ import { jsx as jsx15 } from "react/jsx-runtime";
15080
+ function parseHeaderFlagsOrFail(c, flags) {
15081
+ try {
15082
+ return parseHeaderFlags(flags);
15083
+ } catch (err) {
15084
+ c.error({ code: "INVALID_HEADER", message: err instanceof Error ? err.message : String(err) });
15085
+ }
15086
+ }
15087
+ function buildCombinedFrame(result) {
15088
+ const warnings = [];
15089
+ const mppRows = result.mpp.kind === "challenges" ? result.mpp.challenges.map(challengeToFrame) : [];
15090
+ if (result.mpp.kind === "none-inflow") {
15091
+ const offered = result.mpp.methods.length > 0 ? result.mpp.methods.join(", ") : "(unknown)";
15092
+ warnings.push({
15093
+ protocol: "mpp",
15094
+ code: "NO_INFLOW_MATCH",
15095
+ message: `WWW-Authenticate: Payment present, but no challenge uses the \`inflow\` method (only one the InFlow buyer can pay). Method(s) advertised: ${offered}.`,
15096
+ methods: result.mpp.methods
15097
+ });
15098
+ } else if (result.mpp.kind === "error") {
15099
+ warnings.push({ protocol: "mpp", code: result.mpp.code, message: result.mpp.message });
15100
+ }
15101
+ const x402Rows = result.x402.kind === "accepts" ? result.x402.accepts.map(acceptToFrame) : [];
15102
+ if (result.x402.kind === "error") {
15103
+ warnings.push({ protocol: "x402", code: result.x402.code, message: result.x402.message });
15104
+ }
15105
+ if (result.mpp.kind === "absent" && result.x402.kind === "absent") {
15106
+ warnings.push({
15107
+ protocol: "none",
15108
+ code: "NO_PAYMENT_CHALLENGE",
15109
+ message: "Seller returned 402 but carried neither a WWW-Authenticate: Payment nor a PAYMENT-REQUIRED header."
15110
+ });
15111
+ }
15112
+ const frame = {
15113
+ outcome: "inspected",
15114
+ url: result.url,
15115
+ method: result.method,
15116
+ detected: detectedProtocols(result.mpp, result.x402),
15117
+ mpp: mppRows,
15118
+ x402: x402Rows
15119
+ };
15120
+ if (result.x402.kind === "accepts") {
15121
+ frame.x402_resource = result.x402.resource;
15122
+ frame.x402_version = result.x402.x402Version;
15123
+ if (result.x402.extensions !== void 0) frame.x402_extensions = result.x402.extensions;
15124
+ }
15125
+ if (warnings.length > 0) frame.warnings = warnings;
15126
+ return frame;
15127
+ }
15128
+ function buildNoPaymentFrame3(result) {
15129
+ const frame = {
15130
+ outcome: "no-payment-required",
15131
+ url: result.url,
15132
+ method: result.method,
15133
+ status: result.status,
15134
+ body_size_bytes: result.bodySizeBytes
15135
+ };
15136
+ if (result.contentType !== void 0) frame.content_type = result.contentType;
15137
+ return frame;
15138
+ }
15139
+ async function runCombinedInspectCommand(c) {
15140
+ const probeHeaders = parseHeaderFlagsOrFail(c, c.options.header);
15141
+ const probeOptions = {
15142
+ method: c.options.method,
15143
+ headers: probeHeaders,
15144
+ ...c.options.data !== void 0 ? { data: c.options.data } : {}
15145
+ };
15146
+ const deps = { probeOptions, url: c.args.url };
15147
+ if (!c.agent && !c.formatExplicit) {
15148
+ let finalPhase = null;
15149
+ await renderInkUntilExit(
15150
+ /* @__PURE__ */ jsx15(
15151
+ CombinedInspectView,
15152
+ {
15153
+ url: c.args.url,
15154
+ method: c.options.method,
15155
+ deps,
15156
+ onComplete: (phase) => {
15157
+ finalPhase = phase;
15158
+ }
15159
+ }
15160
+ )
15161
+ );
15162
+ if (finalPhase !== null) {
15163
+ const phase = finalPhase;
15164
+ if (phase.kind === "error") {
15165
+ c.error({ code: phase.code, message: phase.message });
15166
+ }
15167
+ }
15168
+ return void 0;
15169
+ }
15170
+ let finalEvent = null;
15171
+ await runCombinedInspectPipeline(deps, (event) => {
15172
+ if (event.type === "errored") {
15173
+ finalEvent = { kind: "error", payload: event };
15174
+ return;
15175
+ }
15176
+ if (event.type === "inspected") {
15177
+ finalEvent = { kind: "inspected", payload: event.result };
15178
+ return;
15179
+ }
15180
+ if (event.type === "no-payment") {
15181
+ finalEvent = { kind: "no-payment", payload: event.result };
15182
+ }
15183
+ });
15184
+ if (finalEvent === null) {
15185
+ return c.error({ code: "INSPECT_FAILED", message: "Inspect pipeline produced no result." });
15186
+ }
15187
+ const { kind, payload } = finalEvent;
15188
+ if (kind === "error") {
15189
+ const err = payload;
15190
+ return c.error({ code: err.code, message: err.message });
15191
+ }
15192
+ if (kind === "inspected") {
15193
+ return sanitizeDeep(buildCombinedFrame(payload));
15194
+ }
15195
+ return sanitizeDeep(buildNoPaymentFrame3(payload));
15196
+ }
15197
+ function createInspectCommand() {
15198
+ return {
15199
+ description: "Detect a URL's payment protocol(s) and show MPP and x402 challenges together. Read-only probe \u2014 no auth, no payment. Read `detected` to choose a pay rail (MPP wins when both are present).",
15200
+ args: inspectArgs,
15201
+ options: inspectOptions,
15202
+ outputPolicy: "agent-only",
15203
+ examples: [
15204
+ {
15205
+ args: { url: "https://api.foo.dev/dataset.csv" },
15206
+ description: "Probe a URL and show every MPP and x402 challenge it advertises."
15207
+ },
15208
+ {
15209
+ args: { url: "https://api.foo.dev/widgets" },
15210
+ options: { method: "POST", data: '{"sku":"widget-1"}' },
15211
+ description: "Probe a POST-only paywalled endpoint."
15212
+ }
15213
+ ],
15214
+ async run(c) {
15215
+ return runCombinedInspectCommand(c);
15216
+ }
15217
+ };
15218
+ }
15219
+
15220
+ // src/commands/mpp/index.tsx
15221
+ import { chmodSync, writeFileSync as writeFileSync2 } from "fs";
15222
+ import { resolve as resolvePath2 } from "path";
15223
+ import { Cli as Cli4 } from "incur";
15224
+
15225
+ // src/commands/mpp/cancel.tsx
15226
+ import { Box as Box12, Text as Text13 } from "ink";
15227
+ import Spinner10 from "ink-spinner";
15228
+ import { useCallback as useCallback5 } from "react";
15229
+ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
15230
+ var CancelView = ({ approvalId, cancel, onComplete }) => {
15231
+ const action = useCallback5(() => cancel(), [cancel]);
15232
+ const { finish } = useFlowExit(onComplete);
15233
+ const { status } = useFlowState(action, finish);
15234
+ if (status === "loading") {
15235
+ return /* @__PURE__ */ jsx16(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
15236
+ /* @__PURE__ */ jsx16(Spinner10, { type: "dots" }),
15237
+ " Cancelling approval ",
15238
+ approvalId,
15239
+ "..."
15240
+ ] }) });
15241
+ }
15242
+ return /* @__PURE__ */ jsx16(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "green", children: [
15243
+ "\u2713 Cancelled approval ",
15244
+ approvalId,
15245
+ " (best-effort)"
15246
+ ] }) });
15247
+ };
15248
+
15249
+ // src/commands/mpp/decode.tsx
15250
+ import { Box as Box13, Text as Text14 } from "ink";
15251
+ import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
15252
+ function ChallengeBody({ challenge }) {
15253
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
15254
+ /* @__PURE__ */ jsx17(Text14, { bold: true, children: "Challenge" }),
15255
+ /* @__PURE__ */ jsxs13(Text14, { children: [
15256
+ "method/intent: ",
15257
+ /* @__PURE__ */ jsxs13(Text14, { color: "yellow", children: [
15258
+ challenge.method,
15259
+ " / ",
15260
+ challenge.intent
15261
+ ] })
15262
+ ] }),
15263
+ /* @__PURE__ */ jsx17(Text14, { children: `id: ${challenge.id}` }),
15264
+ /* @__PURE__ */ jsx17(Text14, { children: `realm: ${challenge.realm}` }),
15265
+ challenge.amount !== void 0 ? /* @__PURE__ */ jsx17(Text14, { children: `amount: ${challenge.amount}${challenge.currency !== void 0 ? ` ${challenge.currency}` : ""}` }) : null,
15266
+ challenge.rail !== void 0 ? /* @__PURE__ */ jsx17(Text14, { children: `rail: ${challenge.rail}` }) : null,
15267
+ challenge.expires !== void 0 ? /* @__PURE__ */ jsx17(Text14, { dimColor: true, children: `expires: ${challenge.expires}` }) : null
15268
+ ] });
15269
+ }
15270
+ var DecodeView = ({ result }) => {
15271
+ if (result.kind === "challenge") {
15272
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingY: 1, children: [
15273
+ /* @__PURE__ */ jsx17(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text14, { bold: true, children: "Decoded WWW-Authenticate: Payment" }) }),
15274
+ /* @__PURE__ */ jsx17(ChallengeBody, { challenge: result.challenge })
15275
+ ] });
15276
+ }
15277
+ if (result.kind === "credential") {
15278
+ const { credential } = result;
15279
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingY: 1, children: [
15280
+ /* @__PURE__ */ jsx17(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text14, { bold: true, children: "Decoded Authorization: Payment credential" }) }),
15281
+ /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
15282
+ /* @__PURE__ */ jsx17(Text14, { children: `challenge id: ${credential.challenge.id}` }),
15283
+ /* @__PURE__ */ jsx17(Text14, { children: `method: ${credential.challenge.method}` }),
15284
+ /* @__PURE__ */ jsx17(Text14, { children: `source: ${credential.source}` }),
15285
+ /* @__PURE__ */ jsx17(Text14, { dimColor: true, children: `payload keys: ${Object.keys(credential.payload).join(", ") || "(none)"}` })
15286
+ ] })
15287
+ ] });
15288
+ }
15289
+ const { receipt } = result;
15290
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingY: 1, children: [
15291
+ /* @__PURE__ */ jsx17(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text14, { bold: true, children: "Decoded Payment-Receipt" }) }),
15292
+ /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
15293
+ /* @__PURE__ */ jsxs13(Text14, { children: [
15294
+ "status: ",
15295
+ /* @__PURE__ */ jsx17(Text14, { color: "green", children: receipt.status })
15296
+ ] }),
15297
+ /* @__PURE__ */ jsx17(Text14, { children: `reference: ${receipt.reference}` }),
15298
+ /* @__PURE__ */ jsx17(Text14, { dimColor: true, children: `timestamp: ${receipt.timestamp}` })
15299
+ ] })
15300
+ ] });
15301
+ };
15302
+
15303
+ // src/commands/mpp/pay.tsx
15304
+ import { Box as Box14, Text as Text15, useInput as useInput3 } from "ink";
15305
+ import Spinner11 from "ink-spinner";
15306
+ import { useEffect as useEffect9, useReducer as useReducer7, useState as useState3 } from "react";
15307
+ import { Fragment, jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
15308
+ var PayView = ({ url, method, deps, onComplete, onCancel }) => {
15309
+ const initial = { kind: "probing" };
15310
+ const [phase, dispatch] = useReducer7(reduceMppPay, initial);
15311
+ const [cancelling, setCancelling] = useState3(false);
15312
+ const { finish, cancelThenFinish } = useFlowExit(onComplete);
15313
+ const created = phase.kind === "created" ? phase.created : void 0;
15314
+ const approvalUrl = created?.approvalUrl;
15315
+ const approvalId = created?.approvalId;
15316
+ useInput3(
15317
+ (_input, key) => {
15318
+ if (approvalUrl === void 0) return;
15319
+ if (key.return) {
15320
+ openUrl(approvalUrl);
15321
+ return;
15322
+ }
15323
+ if (key.escape && approvalId !== void 0) {
15324
+ setCancelling(true);
15325
+ cancelThenFinish(() => onCancel?.(approvalId), {
15326
+ kind: "error",
15327
+ code: "APPROVAL_CANCELLED",
15328
+ message: `Approval ${approvalId} cancelled.`
15329
+ });
15330
+ }
15331
+ },
15332
+ { isActive: approvalUrl !== void 0 && !cancelling }
15333
+ );
15334
+ useEffect9(() => {
15335
+ const controller = new AbortController();
15336
+ let cancelled = false;
15337
+ const runDeps = { ...deps, signal: controller.signal };
15338
+ void runMppPayPipeline(runDeps, (event) => {
15339
+ if (!cancelled) dispatch(event);
15340
+ });
15341
+ return () => {
15342
+ cancelled = true;
15343
+ controller.abort();
15344
+ };
15345
+ }, [deps]);
15346
+ useEffect9(() => {
15347
+ if (phase.kind === "success" || phase.kind === "seller-rejected" || phase.kind === "no-payment-final" || phase.kind === "error") {
15348
+ finish(phase);
15349
+ }
15350
+ }, [phase, finish]);
15351
+ if (cancelling) {
15352
+ return /* @__PURE__ */ jsx18(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { color: "yellow", children: [
15353
+ /* @__PURE__ */ jsx18(Spinner11, { type: "dots" }),
15354
+ " Cancelling approval..."
15355
+ ] }) });
15356
+ }
15357
+ if (phase.kind === "probing") {
15358
+ return /* @__PURE__ */ jsx18(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { color: "cyan", children: [
15359
+ /* @__PURE__ */ jsx18(Spinner11, { type: "dots" }),
15360
+ " Probing ",
15361
+ method,
15362
+ " ",
15363
+ url,
15364
+ "..."
15365
+ ] }) });
15366
+ }
15367
+ if (phase.kind === "no-payment") {
15368
+ return /* @__PURE__ */ jsx18(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { color: "cyan", children: [
15369
+ /* @__PURE__ */ jsx18(Spinner11, { type: "dots" }),
15370
+ " Seller accepted without payment (status ",
15371
+ String(phase.probe.status),
15372
+ "); finalising..."
15373
+ ] }) });
15374
+ }
15375
+ if (phase.kind === "decoded") {
15376
+ return /* @__PURE__ */ jsx18(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { color: "cyan", children: [
15377
+ /* @__PURE__ */ jsx18(Spinner11, { type: "dots" }),
15378
+ " Fulfilling ",
15379
+ phase.challenge.amount ?? "",
15380
+ " ",
15381
+ phase.challenge.currency ?? "",
15382
+ " ",
15383
+ "challenge..."
15384
+ ] }) });
15385
+ }
15386
+ if (phase.kind === "created") {
15387
+ const { created: created2 } = phase;
15388
+ if (created2.state !== "pending") {
15389
+ return /* @__PURE__ */ jsx18(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { color: "cyan", children: [
15390
+ /* @__PURE__ */ jsx18(Spinner11, { type: "dots" }),
15391
+ " Transaction ",
15392
+ created2.transactionId,
15393
+ " ready; replaying..."
15394
+ ] }) });
15395
+ }
15396
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", paddingY: 1, children: [
15397
+ /* @__PURE__ */ jsx18(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx18(Text15, { bold: true, children: "Approval required" }) }),
15398
+ /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
15399
+ /* @__PURE__ */ jsx18(Text15, { children: `transaction: ${created2.transactionId}` }),
15400
+ created2.approvalUrl !== void 0 ? /* @__PURE__ */ jsxs14(Fragment, { children: [
15401
+ /* @__PURE__ */ jsxs14(Text15, { children: [
15402
+ "Open: ",
15403
+ /* @__PURE__ */ jsx18(Text15, { bold: true, color: "cyan", children: created2.approvalUrl })
15404
+ ] }),
15405
+ /* @__PURE__ */ jsx18(Text15, { dimColor: true, children: "Press Enter to open in browser." }),
15406
+ /* @__PURE__ */ jsx18(Text15, { dimColor: true, children: "Press Escape to cancel." })
15407
+ ] }) : null
15408
+ ] }),
15409
+ /* @__PURE__ */ jsx18(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text15, { color: "cyan", children: [
15410
+ /* @__PURE__ */ jsx18(Spinner11, { type: "dots" }),
15411
+ " Waiting for approval..."
15412
+ ] }) })
15413
+ ] });
15414
+ }
15415
+ if (phase.kind === "replaying") {
15416
+ return /* @__PURE__ */ jsx18(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { color: "cyan", children: [
15417
+ /* @__PURE__ */ jsx18(Spinner11, { type: "dots" }),
15418
+ " Replaying request with Authorization: Payment..."
15419
+ ] }) });
15420
+ }
15421
+ if (phase.kind === "success") {
15422
+ const { result } = phase;
15423
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
15424
+ /* @__PURE__ */ jsxs14(Text15, { color: "green", children: [
15425
+ "\u2713 Paid (intent ",
15426
+ result.intent,
15427
+ ")"
14904
15428
  ] }),
14905
- /* @__PURE__ */ jsx15(Text13, { children: `status: ${String(result.responseStatus)}` }),
14906
- /* @__PURE__ */ jsx15(Text13, { children: `transaction: ${result.transactionId}` }),
14907
- result.settled !== void 0 ? /* @__PURE__ */ jsx15(Text13, { children: `settled: ${result.settled.status ?? "success"} (ref ${result.settled.reference ?? "\u2014"})` }) : null,
14908
- /* @__PURE__ */ jsx15(Text13, { children: `response size: ${String(result.bodySizeBytes)} bytes` }),
14909
- result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs12(Text13, { children: [
15429
+ /* @__PURE__ */ jsx18(Text15, { children: `status: ${String(result.responseStatus)}` }),
15430
+ /* @__PURE__ */ jsx18(Text15, { children: `transaction: ${result.transactionId}` }),
15431
+ result.settled !== void 0 ? /* @__PURE__ */ jsx18(Text15, { children: `settled: ${result.settled.status ?? "success"} (ref ${result.settled.reference ?? "\u2014"})` }) : null,
15432
+ /* @__PURE__ */ jsx18(Text15, { children: `response size: ${String(result.bodySizeBytes)} bytes` }),
15433
+ result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs14(Text15, { children: [
14910
15434
  "Saved to: ",
14911
- /* @__PURE__ */ jsx15(Text13, { bold: true, children: result.outputSavedTo })
15435
+ /* @__PURE__ */ jsx18(Text15, { bold: true, children: result.outputSavedTo })
14912
15436
  ] }) : null,
14913
- result.body !== void 0 ? /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
14914
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "response body:" }),
14915
- /* @__PURE__ */ jsx15(Text13, { children: result.body })
15437
+ result.body !== void 0 ? /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, flexDirection: "column", children: [
15438
+ /* @__PURE__ */ jsx18(Text15, { dimColor: true, children: "response body:" }),
15439
+ /* @__PURE__ */ jsx18(Text15, { children: result.body })
14916
15440
  ] }) : null
14917
15441
  ] });
14918
15442
  }
14919
15443
  if (phase.kind === "seller-rejected") {
14920
15444
  const { result } = phase;
14921
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
14922
- /* @__PURE__ */ jsx15(Text13, { color: "red", children: "\u2717 Payment not accepted by seller" }),
14923
- /* @__PURE__ */ jsx15(Text13, { children: `status: ${String(result.responseStatus)}` }),
14924
- /* @__PURE__ */ jsx15(Text13, { children: `transaction: ${result.transactionId}` }),
14925
- /* @__PURE__ */ jsx15(Text13, { children: `response size: ${String(result.bodySizeBytes)} bytes` }),
14926
- result.body !== void 0 ? /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
14927
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "response body:" }),
14928
- /* @__PURE__ */ jsx15(Text13, { children: result.body })
15445
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
15446
+ /* @__PURE__ */ jsx18(Text15, { color: "red", children: "\u2717 Payment not accepted by seller" }),
15447
+ /* @__PURE__ */ jsx18(Text15, { children: `status: ${String(result.responseStatus)}` }),
15448
+ /* @__PURE__ */ jsx18(Text15, { children: `transaction: ${result.transactionId}` }),
15449
+ /* @__PURE__ */ jsx18(Text15, { children: `response size: ${String(result.bodySizeBytes)} bytes` }),
15450
+ result.body !== void 0 ? /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, flexDirection: "column", children: [
15451
+ /* @__PURE__ */ jsx18(Text15, { dimColor: true, children: "response body:" }),
15452
+ /* @__PURE__ */ jsx18(Text15, { children: result.body })
14929
15453
  ] }) : null
14930
15454
  ] });
14931
15455
  }
14932
15456
  if (phase.kind === "no-payment-final") {
14933
15457
  const { result } = phase;
14934
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
14935
- /* @__PURE__ */ jsx15(Text13, { color: "green", children: "\u2713 Seller accepted without payment" }),
14936
- /* @__PURE__ */ jsx15(Text13, { children: `status: ${String(result.status)}` }),
14937
- /* @__PURE__ */ jsx15(Text13, { children: `response size: ${String(result.bodySizeBytes)} bytes` }),
14938
- result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs12(Text13, { children: [
15458
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
15459
+ /* @__PURE__ */ jsx18(Text15, { color: "green", children: "\u2713 Seller accepted without payment" }),
15460
+ /* @__PURE__ */ jsx18(Text15, { children: `status: ${String(result.status)}` }),
15461
+ /* @__PURE__ */ jsx18(Text15, { children: `response size: ${String(result.bodySizeBytes)} bytes` }),
15462
+ result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs14(Text15, { children: [
14939
15463
  "Saved to: ",
14940
- /* @__PURE__ */ jsx15(Text13, { bold: true, children: result.outputSavedTo })
15464
+ /* @__PURE__ */ jsx18(Text15, { bold: true, children: result.outputSavedTo })
14941
15465
  ] }) : null,
14942
- result.body !== void 0 ? /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
14943
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "response body:" }),
14944
- /* @__PURE__ */ jsx15(Text13, { children: result.body })
15466
+ result.body !== void 0 ? /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, flexDirection: "column", children: [
15467
+ /* @__PURE__ */ jsx18(Text15, { dimColor: true, children: "response body:" }),
15468
+ /* @__PURE__ */ jsx18(Text15, { children: result.body })
14945
15469
  ] }) : null
14946
15470
  ] });
14947
15471
  }
14948
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
14949
- /* @__PURE__ */ jsxs12(Text13, { color: "red", children: [
15472
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
15473
+ /* @__PURE__ */ jsxs14(Text15, { color: "red", children: [
14950
15474
  "\u2717 ",
14951
15475
  phase.code
14952
15476
  ] }),
14953
- /* @__PURE__ */ jsx15(Text13, { color: "red", children: phase.message })
15477
+ /* @__PURE__ */ jsx18(Text15, { color: "red", children: phase.message })
14954
15478
  ] });
14955
15479
  };
14956
15480
 
14957
15481
  // src/commands/mpp/schema.ts
14958
- import { z as z4 } from "incur";
14959
- var payArgs = z4.object({
14960
- url: z4.string().describe("The MPP-protected resource URL to pay for.")
15482
+ import { z as z5 } from "incur";
15483
+ var payArgs = z5.object({
15484
+ url: z5.string().describe("The MPP-protected resource URL to pay for.")
14961
15485
  });
14962
- var payOptions = z4.object({
14963
- paymentMethod: z4.string().optional().describe('Only consider challenges with this payment method (e.g. "inflow").'),
14964
- intent: z4.string().optional().describe('Only consider challenges with this intent (e.g. "charge").'),
14965
- currency: z4.string().optional().describe('Only consider challenges in this currency (e.g. "USDC").'),
14966
- rail: z4.string().optional().describe('Only consider challenges on this settlement rail (e.g. "balance", "instrument").'),
14967
- method: z4.string().default("GET").describe("HTTP method for the seller request."),
14968
- data: z4.string().optional().describe(
15486
+ var payOptions = z5.object({
15487
+ paymentMethod: z5.string().optional().describe('Only consider challenges with this payment method (e.g. "inflow").'),
15488
+ intent: z5.string().optional().describe('Only consider challenges with this intent (e.g. "charge").'),
15489
+ currency: z5.string().optional().describe('Only consider challenges in this currency (e.g. "USDC").'),
15490
+ rail: z5.string().optional().describe('Only consider challenges on this settlement rail (e.g. "balance", "instrument").'),
15491
+ method: z5.string().default("GET").describe("HTTP method for the seller request."),
15492
+ data: z5.string().optional().describe(
14969
15493
  "Request body. JSON or raw text. Content-Type defaults to application/json when --data is set unless a --header overrides it."
14970
15494
  ),
14971
- header: z4.array(z4.string()).default([]).describe('Repeatable. "Name: Value" format.'),
14972
- interval: z4.coerce.number().default(0).describe(
15495
+ header: z5.array(z5.string()).default([]).describe('Repeatable. "Name: Value" format.'),
15496
+ interval: z5.coerce.number().default(0).describe(
14973
15497
  "Inline poll cadence in seconds while a transaction is pending. 0 returns the transaction id and a follow-up command hint without blocking."
14974
15498
  ),
14975
- maxAttempts: z4.coerce.number().default(0).describe("Hard cap on poll attempts when --interval > 0. 0 means unlimited."),
14976
- timeout: z4.coerce.number().default(900).describe("Polling deadline in seconds. Default 900s (matches the server-side approval expiry)."),
14977
- instrumentId: z4.string().optional().describe(
15499
+ maxAttempts: z5.coerce.number().default(0).describe("Hard cap on poll attempts when --interval > 0. 0 means unlimited."),
15500
+ timeout: z5.coerce.number().default(900).describe("Polling deadline in seconds. Default 900s (matches the server-side approval expiry)."),
15501
+ instrumentId: z5.string().optional().describe(
14978
15502
  "Funding instrument id (UUID) for an instrument-rail challenge. The buyer does not choose the rail \u2014 it is derived from the seller challenge; this is the only buyer-supplied payment option."
14979
15503
  ),
14980
- showBody: z4.boolean().default(true).describe(
15504
+ showBody: z5.boolean().default(true).describe(
14981
15505
  "Include the seller response body in the result. Default true so AI assistants paying for content receive the deliverable. Pass --no-show-body to suppress (e.g. for binary downloads paired with --output-file)."
14982
15506
  ),
14983
- outputFile: z4.string().optional().describe(
15507
+ outputFile: z5.string().optional().describe(
14984
15508
  "Write the seller response body bytes to this file path (overwrites silently). When set, the result frame includes `output_saved_to: <absolute_path>` instead of `body` / `body_base64`. Natural choice for binary content (PDFs, images, downloads)."
14985
15509
  ),
14986
- credentialFile: z4.string().optional().describe(
15510
+ credentialFile: z5.string().optional().describe(
14987
15511
  "Write the base64url `Authorization: Payment` credential to this file path (mode 0o600, overwrites silently). When set, the result frame includes `credential_saved_to: <absolute_path>` instead of `credential`. Use to keep one-time payment credentials out of chat transcripts and logs."
14988
15512
  )
14989
15513
  });
14990
- var statusArgs = z4.object({
14991
- transactionId: z4.string().describe("The transaction id returned by `mpp pay`.")
15514
+ var statusArgs = z5.object({
15515
+ transactionId: z5.string().describe("The transaction id returned by `mpp pay`.")
14992
15516
  });
14993
- var statusOptions2 = z4.object({
14994
- interval: z4.coerce.number().default(0).describe(
15517
+ var statusOptions2 = z5.object({
15518
+ interval: z5.coerce.number().default(0).describe(
14995
15519
  "Poll cadence in seconds. 0 returns the current snapshot; positive values yield on every change until ready or terminal."
14996
15520
  ),
14997
- maxAttempts: z4.coerce.number().default(0).describe("Hard cap on poll attempts. 0 means unlimited."),
14998
- timeout: z4.coerce.number().default(900).describe("Polling deadline in seconds."),
14999
- credentialFile: z4.string().optional().describe(
15521
+ maxAttempts: z5.coerce.number().default(0).describe("Hard cap on poll attempts. 0 means unlimited."),
15522
+ timeout: z5.coerce.number().default(900).describe("Polling deadline in seconds."),
15523
+ credentialFile: z5.string().optional().describe(
15000
15524
  "Write the base64url `Authorization: Payment` credential to this file path (mode 0o600, overwrites silently). When set, the ready frame includes `credential_saved_to: <absolute_path>` instead of `credential`. Use to keep one-time payment credentials out of chat transcripts and logs."
15001
15525
  )
15002
15526
  });
15003
- var cancelArgs = z4.object({
15004
- approvalId: z4.string().describe("The approval id returned by `mpp pay` (on the pending frame).")
15527
+ var cancelArgs = z5.object({
15528
+ approvalId: z5.string().describe("The approval id returned by `mpp pay` (on the pending frame).")
15005
15529
  });
15006
- var decodeArgs = z4.object({
15007
- value: z4.string().describe(
15530
+ var decodeArgs = z5.object({
15531
+ value: z5.string().describe(
15008
15532
  "A raw `WWW-Authenticate: Payment` header value, or a base64url `Authorization: Payment` credential / `Payment-Receipt`. The kind is auto-detected."
15009
15533
  )
15010
15534
  });
15011
- var inspectArgs = z4.object({
15012
- url: z4.string().describe("The MPP-protected resource URL to probe. No payment is made.")
15535
+ var inspectArgs2 = z5.object({
15536
+ url: z5.string().describe("The MPP-protected resource URL to probe. No payment is made.")
15013
15537
  });
15014
- var inspectOptions = z4.object({
15015
- paymentMethod: z4.string().optional().describe('Only show challenges with this payment method (e.g. "inflow").'),
15016
- intent: z4.string().optional().describe('Only show challenges with this intent (e.g. "charge").'),
15017
- currency: z4.string().optional().describe('Only show challenges in this currency (e.g. "USDC").'),
15018
- rail: z4.string().optional().describe('Only show challenges on this settlement rail (e.g. "balance", "instrument").'),
15019
- method: z4.string().default("GET").describe("HTTP method for the probe request."),
15020
- data: z4.string().optional().describe(
15538
+ var inspectOptions2 = z5.object({
15539
+ paymentMethod: z5.string().optional().describe('Only show challenges with this payment method (e.g. "inflow").'),
15540
+ intent: z5.string().optional().describe('Only show challenges with this intent (e.g. "charge").'),
15541
+ currency: z5.string().optional().describe('Only show challenges in this currency (e.g. "USDC").'),
15542
+ rail: z5.string().optional().describe('Only show challenges on this settlement rail (e.g. "balance", "instrument").'),
15543
+ method: z5.string().default("GET").describe("HTTP method for the probe request."),
15544
+ data: z5.string().optional().describe(
15021
15545
  "Request body for the probe. JSON or raw text. Content-Type defaults to application/json when --data is set unless a --header overrides it."
15022
15546
  ),
15023
- header: z4.array(z4.string()).default([]).describe('Repeatable. "Name: Value" format.')
15547
+ header: z5.array(z5.string()).default([]).describe('Repeatable. "Name: Value" format.')
15024
15548
  });
15025
15549
 
15026
15550
  // src/commands/mpp/status.tsx
15027
- import { Box as Box13, Text as Text14 } from "ink";
15028
- import Spinner10 from "ink-spinner";
15029
- import { useEffect as useEffect8, useReducer as useReducer6 } from "react";
15030
- import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
15551
+ import { Box as Box15, Text as Text16 } from "ink";
15552
+ import Spinner12 from "ink-spinner";
15553
+ import { useEffect as useEffect10, useReducer as useReducer8 } from "react";
15554
+ import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
15031
15555
  var MppStatusView = ({
15032
15556
  transactionId,
15033
15557
  fetchOnce,
@@ -15037,9 +15561,9 @@ var MppStatusView = ({
15037
15561
  onComplete
15038
15562
  }) => {
15039
15563
  const initial = { kind: "polling" };
15040
- const [phase, dispatch] = useReducer6(reduceMppStatus, initial);
15564
+ const [phase, dispatch] = useReducer8(reduceMppStatus, initial);
15041
15565
  const { finish } = useFlowExit(onComplete);
15042
- useEffect8(() => {
15566
+ useEffect10(() => {
15043
15567
  const run = runMppStatus({ fetchOnce, interval, maxAttempts, timeout });
15044
15568
  let cancelled = false;
15045
15569
  void (async () => {
@@ -15052,15 +15576,15 @@ var MppStatusView = ({
15052
15576
  cancelled = true;
15053
15577
  };
15054
15578
  }, [fetchOnce, interval, maxAttempts, timeout]);
15055
- useEffect8(() => {
15579
+ useEffect10(() => {
15056
15580
  if (phase.kind === "ready" || phase.kind === "failed" || phase.kind === "expired" || phase.kind === "timeout" || phase.kind === "error") {
15057
15581
  finish(phase);
15058
15582
  }
15059
15583
  }, [phase, finish]);
15060
15584
  if (phase.kind === "polling") {
15061
15585
  const stateText = phase.latest?.state ?? "pending";
15062
- return /* @__PURE__ */ jsx16(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsxs13(Text14, { color: "cyan", children: [
15063
- /* @__PURE__ */ jsx16(Spinner10, { type: "dots" }),
15586
+ return /* @__PURE__ */ jsx19(Box15, { flexDirection: "column", children: /* @__PURE__ */ jsxs15(Text16, { color: "cyan", children: [
15587
+ /* @__PURE__ */ jsx19(Spinner12, { type: "dots" }),
15064
15588
  " Polling transaction ",
15065
15589
  transactionId,
15066
15590
  " (state: ",
@@ -15071,38 +15595,38 @@ var MppStatusView = ({
15071
15595
  if (phase.kind === "ready") {
15072
15596
  const credential = phase.response.credential ?? "";
15073
15597
  const preview = credential.length > 32 ? `${credential.slice(0, 32)}...` : credential;
15074
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
15075
- /* @__PURE__ */ jsx16(Text14, { color: "green", children: "\u2713 Ready" }),
15076
- /* @__PURE__ */ jsx16(Text14, { children: `credential: ${preview}` }),
15077
- phase.response.expires !== void 0 ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: `expires: ${phase.response.expires}` }) : null
15598
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
15599
+ /* @__PURE__ */ jsx19(Text16, { color: "green", children: "\u2713 Ready" }),
15600
+ /* @__PURE__ */ jsx19(Text16, { children: `credential: ${preview}` }),
15601
+ phase.response.expires !== void 0 ? /* @__PURE__ */ jsx19(Text16, { dimColor: true, children: `expires: ${phase.response.expires}` }) : null
15078
15602
  ] });
15079
15603
  }
15080
15604
  if (phase.kind === "failed") {
15081
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
15082
- /* @__PURE__ */ jsx16(Text14, { color: "red", children: "\u2717 Transaction failed" }),
15083
- phase.response.problem !== void 0 ? /* @__PURE__ */ jsx16(Text14, { color: "red", children: phase.response.problem.detail ?? phase.response.problem.title }) : null
15605
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
15606
+ /* @__PURE__ */ jsx19(Text16, { color: "red", children: "\u2717 Transaction failed" }),
15607
+ phase.response.problem !== void 0 ? /* @__PURE__ */ jsx19(Text16, { color: "red", children: phase.response.problem.detail ?? phase.response.problem.title }) : null
15084
15608
  ] });
15085
15609
  }
15086
15610
  if (phase.kind === "expired") {
15087
- return /* @__PURE__ */ jsx16(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsx16(Text14, { color: "yellow", children: "Transaction expired before it was ready." }) });
15611
+ return /* @__PURE__ */ jsx19(Box15, { flexDirection: "column", children: /* @__PURE__ */ jsx19(Text16, { color: "yellow", children: "Transaction expired before it was ready." }) });
15088
15612
  }
15089
15613
  if (phase.kind === "timeout") {
15090
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
15091
- /* @__PURE__ */ jsx16(Text14, { color: "yellow", children: "Polling timed out before the transaction reached a ready state." }),
15092
- phase.response !== void 0 ? /* @__PURE__ */ jsx16(Text14, { children: `last state: ${phase.response.state}` }) : null
15614
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
15615
+ /* @__PURE__ */ jsx19(Text16, { color: "yellow", children: "Polling timed out before the transaction reached a ready state." }),
15616
+ phase.response !== void 0 ? /* @__PURE__ */ jsx19(Text16, { children: `last state: ${phase.response.state}` }) : null
15093
15617
  ] });
15094
15618
  }
15095
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
15096
- /* @__PURE__ */ jsx16(Text14, { color: "red", children: "\u2717 Polling failed" }),
15097
- /* @__PURE__ */ jsx16(Text14, { color: "red", children: phase.message })
15619
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
15620
+ /* @__PURE__ */ jsx19(Text16, { color: "red", children: "\u2717 Polling failed" }),
15621
+ /* @__PURE__ */ jsx19(Text16, { color: "red", children: phase.message })
15098
15622
  ] });
15099
15623
  };
15100
15624
 
15101
15625
  // src/commands/mpp/supported.tsx
15102
- import { Box as Box14, Text as Text15 } from "ink";
15103
- import Spinner11 from "ink-spinner";
15626
+ import { Box as Box16, Text as Text17 } from "ink";
15627
+ import Spinner13 from "ink-spinner";
15104
15628
  import { useCallback as useCallback6 } from "react";
15105
- import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
15629
+ import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
15106
15630
  function flattenKinds(response) {
15107
15631
  const rows = [];
15108
15632
  for (const kind of response.kinds) {
@@ -15119,7 +15643,7 @@ function flattenKinds(response) {
15119
15643
  }
15120
15644
  return rows;
15121
15645
  }
15122
- var COLUMNS4 = [
15646
+ var COLUMNS5 = [
15123
15647
  { header: "Method", cell: (r) => r.method },
15124
15648
  { header: "Intent", cell: (r) => r.intent },
15125
15649
  { header: "Rail", cell: (r) => r.rail },
@@ -15130,34 +15654,33 @@ var SupportedView = ({ load, onComplete }) => {
15130
15654
  const { finish } = useFlowExit(onComplete);
15131
15655
  const { status, data, error } = useFlowState(action, finish);
15132
15656
  if (status === "loading") {
15133
- return /* @__PURE__ */ jsx17(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { color: "cyan", children: [
15134
- /* @__PURE__ */ jsx17(Spinner11, { type: "dots" }),
15657
+ return /* @__PURE__ */ jsx20(Box16, { children: /* @__PURE__ */ jsxs16(Text17, { color: "cyan", children: [
15658
+ /* @__PURE__ */ jsx20(Spinner13, { type: "dots" }),
15135
15659
  " Loading supported MPP methods..."
15136
15660
  ] }) });
15137
15661
  }
15138
15662
  if (status === "error") {
15139
- return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
15140
- /* @__PURE__ */ jsx17(Text15, { color: "red", children: "Failed to load supported MPP methods" }),
15141
- /* @__PURE__ */ jsx17(Text15, { color: "red", children: error })
15663
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
15664
+ /* @__PURE__ */ jsx20(Text17, { color: "red", children: "Failed to load supported MPP methods" }),
15665
+ /* @__PURE__ */ jsx20(Text17, { color: "red", children: error })
15142
15666
  ] });
15143
15667
  }
15144
15668
  const rows = data ? flattenKinds(data) : [];
15145
15669
  if (rows.length === 0) {
15146
- return /* @__PURE__ */ jsx17(Box14, { flexDirection: "column", children: /* @__PURE__ */ jsx17(Text15, { children: "No supported MPP methods returned for this account." }) });
15670
+ return /* @__PURE__ */ jsx20(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx20(Text17, { children: "No supported MPP methods returned for this account." }) });
15147
15671
  }
15148
- return /* @__PURE__ */ jsx17(Table, { columns: COLUMNS4, rows });
15672
+ return /* @__PURE__ */ jsx20(Table, { columns: COLUMNS5, rows });
15149
15673
  };
15150
15674
 
15151
15675
  // src/commands/mpp/index.tsx
15152
- import { jsx as jsx18 } from "react/jsx-runtime";
15676
+ import { jsx as jsx21 } from "react/jsx-runtime";
15153
15677
  var POST_CREATE_INSTRUCTION = "Present the approval_url to the user and ask them to approve in the InFlow mobile app or dashboard. Then call `mpp status <transaction_id> --interval 5 --max-attempts 60` to poll until ready. Once ready, replay the request manually with the credential as the `Authorization: Payment <credential>` header.";
15154
15678
  var POLLING_INSTRUCTION = "Approval polling is happening inline. The yield stream emits each state change; the final frame includes the result once the transaction is ready and replayed.";
15155
- function parseHeaderFlagsOrFail(c, flags) {
15156
- try {
15157
- return parseHeaderFlags(flags);
15158
- } catch (err) {
15159
- c.error({ code: "INVALID_HEADER", message: err instanceof Error ? err.message : String(err) });
15160
- }
15679
+ function invalidHeaderError(err) {
15680
+ return {
15681
+ code: "INVALID_HEADER",
15682
+ message: err instanceof Error ? err.message : String(err)
15683
+ };
15161
15684
  }
15162
15685
  function decorateCredentialField(frame, credential, credentialFile) {
15163
15686
  if (credentialFile !== void 0 && credentialFile.length > 0) {
@@ -15170,16 +15693,15 @@ function decorateCredentialField(frame, credential, credentialFile) {
15170
15693
  frame.credential = credential;
15171
15694
  }
15172
15695
  function probeOptionsFrom(c) {
15173
- const headers = parseHeaderFlagsOrFail(c, c.options.header);
15174
15696
  return {
15175
15697
  method: c.options.method,
15176
- headers,
15698
+ headers: parseHeaderFlags(c.options.header),
15177
15699
  ...c.options.data !== void 0 ? { data: c.options.data } : {}
15178
15700
  };
15179
15701
  }
15180
- function buildPayPipelineInput(c) {
15702
+ function buildPayPipelineInput(c, probeOptions) {
15181
15703
  return {
15182
- probeOptions: probeOptionsFrom(c),
15704
+ probeOptions,
15183
15705
  url: c.args.url,
15184
15706
  showBody: c.options.showBody,
15185
15707
  interval: c.options.interval,
@@ -15281,17 +15803,23 @@ function toStatusFrame(response, credentialFile) {
15281
15803
  }
15282
15804
  async function* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2) {
15283
15805
  assertSessionGuard(c, authStorage2, inflow2);
15806
+ let probeOptions;
15807
+ try {
15808
+ probeOptions = probeOptionsFrom(c);
15809
+ } catch (err) {
15810
+ return c.error(invalidHeaderError(err));
15811
+ }
15284
15812
  if (!c.agent && !c.formatExplicit) {
15285
15813
  const client = await inflow2.mpp.client();
15286
15814
  let finalPhase = null;
15287
15815
  await renderInkUntilExit(
15288
- /* @__PURE__ */ jsx18(
15816
+ /* @__PURE__ */ jsx21(
15289
15817
  PayView,
15290
15818
  {
15291
15819
  url: c.args.url,
15292
15820
  method: c.options.method,
15293
15821
  deps: {
15294
- ...buildPayPipelineInput(c),
15822
+ ...buildPayPipelineInput(c, probeOptions),
15295
15823
  client,
15296
15824
  apiBaseUrl: apiBaseUrl2,
15297
15825
  awaitPayment: true
@@ -15306,19 +15834,19 @@ async function* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2) {
15306
15834
  if (finalPhase !== null) {
15307
15835
  const phase = finalPhase;
15308
15836
  if (phase.kind === "seller-rejected") {
15309
- c.error({
15837
+ return c.error({
15310
15838
  code: PAYMENT_NOT_ACCEPTED_CODE2,
15311
15839
  message: `Seller rejected the credential with status ${String(phase.result.responseStatus)}. The transaction was ready but the seller did not honour the payment.`
15312
15840
  });
15313
15841
  }
15314
15842
  if (phase.kind === "error") {
15315
- c.error({ code: phase.code, message: phase.message });
15843
+ return c.error({ code: phase.code, message: phase.message });
15316
15844
  }
15317
15845
  }
15318
15846
  return;
15319
15847
  }
15320
15848
  const run = inflow2.mpp.pay({
15321
- ...buildPayPipelineInput(c),
15849
+ ...buildPayPipelineInput(c, probeOptions),
15322
15850
  awaitPayment: c.options.interval > 0
15323
15851
  });
15324
15852
  for await (const event of run.events) {
@@ -15336,14 +15864,13 @@ async function* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2) {
15336
15864
  }
15337
15865
  if (event.type === "rejected") {
15338
15866
  yield sanitizeDeep(rejectedFrameFromResult(event.result));
15339
- c.error({
15867
+ return c.error({
15340
15868
  code: PAYMENT_NOT_ACCEPTED_CODE2,
15341
15869
  message: `Seller rejected the credential with status ${String(event.result.responseStatus)}. The transaction was ready but the seller did not honour the payment; see the previous frame for details.`
15342
15870
  });
15343
- return;
15344
15871
  }
15345
15872
  if (event.type === "errored") {
15346
- c.error({ code: event.code, message: event.message });
15873
+ return c.error({ code: event.code, message: event.message });
15347
15874
  }
15348
15875
  }
15349
15876
  }
@@ -15352,7 +15879,7 @@ async function* runStatusCommand(c, inflow2, authStorage2) {
15352
15879
  if (!c.agent && !c.formatExplicit) {
15353
15880
  const client2 = await inflow2.mpp.client();
15354
15881
  await renderInkUntilExit(
15355
- /* @__PURE__ */ jsx18(
15882
+ /* @__PURE__ */ jsx21(
15356
15883
  MppStatusView,
15357
15884
  {
15358
15885
  transactionId: c.args.transactionId,
@@ -15390,27 +15917,27 @@ async function* runStatusCommand(c, inflow2, authStorage2) {
15390
15917
  }
15391
15918
  if (event.type === "failed") {
15392
15919
  yield sanitizeDeep(toStatusFrame(event.response, c.options.credentialFile));
15393
- c.error({
15920
+ return c.error({
15394
15921
  code: "PAYMENT_FAILED",
15395
15922
  message: event.response.problem?.detail ?? event.response.problem?.title ?? "MPP transaction failed."
15396
15923
  });
15397
15924
  }
15398
15925
  if (event.type === "expired") {
15399
15926
  yield sanitizeDeep(toStatusFrame(event.response, c.options.credentialFile));
15400
- c.error({ code: "PAYMENT_EXPIRED", message: "MPP transaction expired before it was ready." });
15927
+ return c.error({ code: "PAYMENT_EXPIRED", message: "MPP transaction expired before it was ready." });
15401
15928
  }
15402
15929
  if (event.type === "timedOut") {
15403
15930
  if (event.response !== void 0) {
15404
15931
  yield sanitizeDeep(toStatusFrame(event.response, c.options.credentialFile));
15405
15932
  }
15406
- c.error({
15933
+ return c.error({
15407
15934
  code: "POLLING_TIMEOUT",
15408
15935
  message: "Polling timed out before the transaction reached a ready state.",
15409
15936
  retryable: true
15410
15937
  });
15411
15938
  }
15412
15939
  if (event.type === "crashed") {
15413
- c.error({ code: "PAYMENT_FAILED", message: event.message });
15940
+ return c.error({ code: "PAYMENT_FAILED", message: event.message });
15414
15941
  }
15415
15942
  }
15416
15943
  }
@@ -15418,7 +15945,7 @@ async function runCancelCommand(c, inflow2, authStorage2) {
15418
15945
  assertSessionGuard(c, authStorage2, inflow2);
15419
15946
  if (!c.agent && !c.formatExplicit) {
15420
15947
  await renderInkUntilExit(
15421
- /* @__PURE__ */ jsx18(
15948
+ /* @__PURE__ */ jsx21(
15422
15949
  CancelView,
15423
15950
  {
15424
15951
  approvalId: c.args.approvalId,
@@ -15439,7 +15966,7 @@ async function runDecodeCommand(c) {
15439
15966
  return c.error({ code: "DECODE_FAILED", message: err instanceof Error ? err.message : String(err) });
15440
15967
  }
15441
15968
  if (!c.agent && !c.formatExplicit) {
15442
- await renderInkUntilExit(/* @__PURE__ */ jsx18(DecodeView, { result }));
15969
+ await renderInkUntilExit(/* @__PURE__ */ jsx21(DecodeView, { result }));
15443
15970
  return void 0;
15444
15971
  }
15445
15972
  return sanitizeDeep(result);
@@ -15447,15 +15974,21 @@ async function runDecodeCommand(c) {
15447
15974
  async function runSupportedCommand(c, inflow2, authStorage2) {
15448
15975
  assertSessionGuard(c, authStorage2, inflow2);
15449
15976
  if (!c.agent && !c.formatExplicit) {
15450
- await renderInkUntilExit(/* @__PURE__ */ jsx18(SupportedView, { load: () => inflow2.mpp.supported(), onComplete: () => void 0 }));
15977
+ await renderInkUntilExit(/* @__PURE__ */ jsx21(SupportedView, { load: () => inflow2.mpp.supported(), onComplete: () => void 0 }));
15451
15978
  return void 0;
15452
15979
  }
15453
15980
  const response = await inflow2.mpp.supported();
15454
15981
  return sanitizeDeep(response);
15455
15982
  }
15456
15983
  async function runInspectCommand(c) {
15984
+ let probeOptions;
15985
+ try {
15986
+ probeOptions = probeOptionsFrom(c);
15987
+ } catch (err) {
15988
+ return c.error(invalidHeaderError(err));
15989
+ }
15457
15990
  const deps = {
15458
- probeOptions: probeOptionsFrom(c),
15991
+ probeOptions,
15459
15992
  url: c.args.url,
15460
15993
  ...c.options.paymentMethod !== void 0 ? { paymentMethodFilter: c.options.paymentMethod } : {},
15461
15994
  ...c.options.intent !== void 0 ? { intentFilter: c.options.intent } : {},
@@ -15465,7 +15998,7 @@ async function runInspectCommand(c) {
15465
15998
  if (!c.agent && !c.formatExplicit) {
15466
15999
  let finalPhase = null;
15467
16000
  await renderInkUntilExit(
15468
- /* @__PURE__ */ jsx18(
16001
+ /* @__PURE__ */ jsx21(
15469
16002
  InspectView,
15470
16003
  {
15471
16004
  url: c.args.url,
@@ -15480,7 +16013,7 @@ async function runInspectCommand(c) {
15480
16013
  if (finalPhase !== null) {
15481
16014
  const phase = finalPhase;
15482
16015
  if (phase.kind === "error") {
15483
- c.error({ code: phase.code, message: phase.message });
16016
+ return c.error({ code: phase.code, message: phase.message });
15484
16017
  }
15485
16018
  }
15486
16019
  return void 0;
@@ -15522,7 +16055,7 @@ function createMppCli(inflow2, authStorage2, apiBaseUrl2) {
15522
16055
  options: payOptions,
15523
16056
  outputPolicy: "agent-only",
15524
16057
  async *run(c) {
15525
- yield* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2);
16058
+ return yield* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2);
15526
16059
  }
15527
16060
  });
15528
16061
  cli2.command("status", {
@@ -15531,7 +16064,7 @@ function createMppCli(inflow2, authStorage2, apiBaseUrl2) {
15531
16064
  options: statusOptions2,
15532
16065
  outputPolicy: "agent-only",
15533
16066
  async *run(c) {
15534
- yield* runStatusCommand(c, inflow2, authStorage2);
16067
+ return yield* runStatusCommand(c, inflow2, authStorage2);
15535
16068
  }
15536
16069
  });
15537
16070
  cli2.command("cancel", {
@@ -15559,8 +16092,8 @@ function createMppCli(inflow2, authStorage2, apiBaseUrl2) {
15559
16092
  });
15560
16093
  cli2.command("inspect", {
15561
16094
  description: "Show the seller's MPP challenge(s) for a URL. Read-only probe \u2014 no auth, no payment.",
15562
- args: inspectArgs,
15563
- options: inspectOptions,
16095
+ args: inspectArgs2,
16096
+ options: inspectOptions2,
15564
16097
  outputPolicy: "agent-only",
15565
16098
  async run(c) {
15566
16099
  return runInspectCommand(c);
@@ -15574,10 +16107,10 @@ import { Cli as Cli5, Errors as Errors2 } from "incur";
15574
16107
  import "react";
15575
16108
 
15576
16109
  // src/commands/user/get.tsx
15577
- import { Box as Box15, Text as Text16 } from "ink";
15578
- import Spinner12 from "ink-spinner";
16110
+ import { Box as Box17, Text as Text18 } from "ink";
16111
+ import Spinner14 from "ink-spinner";
15579
16112
  import { useCallback as useCallback7, useRef as useRef3 } from "react";
15580
- import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
16113
+ import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
15581
16114
  var UserGet = ({ userResource, onComplete }) => {
15582
16115
  const action = useCallback7(() => userResource.retrieve(), [userResource]);
15583
16116
  const { finish } = useFlowExit(onComplete);
@@ -15598,38 +16131,38 @@ var UserGet = ({ userResource, onComplete }) => {
15598
16131
  const { status, data: user, error } = useFlowState(action, handleLinger);
15599
16132
  lastErrorRef.current = error;
15600
16133
  if (status === "loading") {
15601
- return /* @__PURE__ */ jsx19(Box15, { children: /* @__PURE__ */ jsxs15(Text16, { color: "cyan", children: [
15602
- /* @__PURE__ */ jsx19(Spinner12, { type: "dots" }),
16134
+ return /* @__PURE__ */ jsx22(Box17, { children: /* @__PURE__ */ jsxs17(Text18, { color: "cyan", children: [
16135
+ /* @__PURE__ */ jsx22(Spinner14, { type: "dots" }),
15603
16136
  " Loading user..."
15604
16137
  ] }) });
15605
16138
  }
15606
16139
  if (status === "error") {
15607
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
15608
- /* @__PURE__ */ jsx19(Text16, { color: "red", children: "\u2717 Failed to retrieve user" }),
15609
- /* @__PURE__ */ jsx19(Text16, { color: "red", children: error })
16140
+ return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
16141
+ /* @__PURE__ */ jsx22(Text18, { color: "red", children: "\u2717 Failed to retrieve user" }),
16142
+ /* @__PURE__ */ jsx22(Text18, { color: "red", children: error })
15610
16143
  ] });
15611
16144
  }
15612
16145
  if (user === null) return null;
15613
16146
  const rows = buildProfileRows(user);
15614
- return /* @__PURE__ */ jsx19(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: rows.map((row) => /* @__PURE__ */ jsxs15(Text16, { children: [
16147
+ return /* @__PURE__ */ jsx22(Box17, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: rows.map((row) => /* @__PURE__ */ jsxs17(Text18, { children: [
15615
16148
  `${row.label}: `,
15616
- row.value !== null ? /* @__PURE__ */ jsx19(Text16, { bold: true, children: row.value }) : /* @__PURE__ */ jsx19(Text16, { dimColor: true, children: "\u2014" })
16149
+ row.value !== null ? /* @__PURE__ */ jsx22(Text18, { bold: true, children: row.value }) : /* @__PURE__ */ jsx22(Text18, { dimColor: true, children: "\u2014" })
15617
16150
  ] }, row.label)) });
15618
16151
  };
15619
16152
 
15620
16153
  // src/commands/user/schema.ts
15621
- import { z as z5 } from "incur";
15622
- var getOptions = z5.object({});
16154
+ import { z as z6 } from "incur";
16155
+ var getOptions = z6.object({});
15623
16156
 
15624
16157
  // src/commands/user/index.tsx
15625
- import { jsx as jsx20 } from "react/jsx-runtime";
16158
+ import { jsx as jsx23 } from "react/jsx-runtime";
15626
16159
  var TTY_NO_RESULT_MESSAGE = "inflow user get exited without producing a result.";
15627
16160
  async function runUserGet2(c, deps) {
15628
16161
  assertSessionGuard(c, deps.authStorage, deps.inflow);
15629
16162
  if (!c.agent && !c.formatExplicit) {
15630
16163
  let captured = null;
15631
16164
  const outcome = await renderInkUntilExit(
15632
- /* @__PURE__ */ jsx20(
16165
+ /* @__PURE__ */ jsx23(
15633
16166
  UserGet,
15634
16167
  {
15635
16168
  userResource: deps.user,
@@ -15675,23 +16208,23 @@ import { resolve as resolvePath3 } from "path";
15675
16208
  import { Cli as Cli6 } from "incur";
15676
16209
 
15677
16210
  // src/commands/x402/cancel.tsx
15678
- import { Box as Box16, Text as Text17 } from "ink";
15679
- import Spinner13 from "ink-spinner";
16211
+ import { Box as Box18, Text as Text19 } from "ink";
16212
+ import Spinner15 from "ink-spinner";
15680
16213
  import { useCallback as useCallback8 } from "react";
15681
- import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
16214
+ import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
15682
16215
  var CancelView2 = ({ approvalId, cancel, onComplete }) => {
15683
16216
  const action = useCallback8(() => cancel(), [cancel]);
15684
16217
  const { finish } = useFlowExit(onComplete);
15685
16218
  const { status } = useFlowState(action, finish);
15686
16219
  if (status === "loading") {
15687
- return /* @__PURE__ */ jsx21(Box16, { children: /* @__PURE__ */ jsxs16(Text17, { color: "cyan", children: [
15688
- /* @__PURE__ */ jsx21(Spinner13, { type: "dots" }),
16220
+ return /* @__PURE__ */ jsx24(Box18, { children: /* @__PURE__ */ jsxs18(Text19, { color: "cyan", children: [
16221
+ /* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
15689
16222
  " Cancelling approval ",
15690
16223
  approvalId,
15691
16224
  "..."
15692
16225
  ] }) });
15693
16226
  }
15694
- return /* @__PURE__ */ jsx21(Box16, { children: /* @__PURE__ */ jsxs16(Text17, { color: "green", children: [
16227
+ return /* @__PURE__ */ jsx24(Box18, { children: /* @__PURE__ */ jsxs18(Text19, { color: "green", children: [
15695
16228
  "\u2713 Cancelled approval ",
15696
16229
  approvalId,
15697
16230
  " (best-effort)"
@@ -15699,166 +16232,44 @@ var CancelView2 = ({ approvalId, cancel, onComplete }) => {
15699
16232
  };
15700
16233
 
15701
16234
  // src/commands/x402/decode.tsx
15702
- import { fromFoundationRequirements as fromFoundationRequirements2 } from "@inflowpayai/x402-buyer";
15703
- import { Box as Box17, Text as Text18 } from "ink";
15704
- import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
16235
+ import { fromFoundationRequirements as fromFoundationRequirements3 } from "@inflowpayai/x402-buyer";
16236
+ import { Box as Box19, Text as Text20 } from "ink";
16237
+ import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
15705
16238
  var DecodeView2 = ({ decoded }) => {
15706
- const summary = summarizeAccepts(fromFoundationRequirements2(decoded.accepts));
15707
- return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", paddingY: 1, children: [
15708
- /* @__PURE__ */ jsx22(Box17, { marginBottom: 1, children: /* @__PURE__ */ jsx22(Text18, { bold: true, children: "Decoded PAYMENT-REQUIRED" }) }),
15709
- /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
15710
- /* @__PURE__ */ jsxs17(Text18, { children: [
16239
+ const summary = summarizeAccepts(fromFoundationRequirements3(decoded.accepts));
16240
+ return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", paddingY: 1, children: [
16241
+ /* @__PURE__ */ jsx25(Box19, { marginBottom: 1, children: /* @__PURE__ */ jsx25(Text20, { bold: true, children: "Decoded PAYMENT-REQUIRED" }) }),
16242
+ /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
16243
+ /* @__PURE__ */ jsxs19(Text20, { children: [
15711
16244
  "x402Version: ",
15712
- /* @__PURE__ */ jsx22(Text18, { color: "cyan", children: String(decoded.x402Version) })
16245
+ /* @__PURE__ */ jsx25(Text20, { color: "cyan", children: String(decoded.x402Version) })
15713
16246
  ] }),
15714
- /* @__PURE__ */ jsxs17(Text18, { children: [
16247
+ /* @__PURE__ */ jsxs19(Text20, { children: [
15715
16248
  "resource: ",
15716
- /* @__PURE__ */ jsx22(Text18, { color: "cyan", children: decoded.resource.url })
16249
+ /* @__PURE__ */ jsx25(Text20, { color: "cyan", children: decoded.resource.url })
15717
16250
  ] }),
15718
- /* @__PURE__ */ jsx22(Text18, { children: `accepts (${String(summary.length)}):` }),
15719
- summary.map((entry, idx) => /* @__PURE__ */ jsxs17(Text18, { children: [
16251
+ /* @__PURE__ */ jsx25(Text20, { children: `accepts (${String(summary.length)}):` }),
16252
+ summary.map((entry, idx) => /* @__PURE__ */ jsxs19(Text20, { children: [
15720
16253
  " ",
15721
- /* @__PURE__ */ jsx22(Text18, { color: "yellow", children: entry.scheme }),
16254
+ /* @__PURE__ */ jsx25(Text20, { color: "yellow", children: entry.scheme }),
15722
16255
  " / ",
15723
- /* @__PURE__ */ jsx22(Text18, { color: "yellow", children: entry.network }),
16256
+ /* @__PURE__ */ jsx25(Text20, { color: "yellow", children: entry.network }),
15724
16257
  entry.amount !== void 0 ? ` \xB7 amount ${entry.amount}` : "",
15725
16258
  entry.asset !== void 0 ? ` \xB7 ${entry.asset}` : ""
15726
16259
  ] }, `${entry.scheme}-${entry.network}-${String(idx)}`)),
15727
- decoded.extensions !== void 0 ? /* @__PURE__ */ jsx22(Text18, { dimColor: true, children: `extensions: ${Object.keys(decoded.extensions).join(", ")}` }) : null
16260
+ decoded.extensions !== void 0 ? /* @__PURE__ */ jsx25(Text20, { dimColor: true, children: `extensions: ${Object.keys(decoded.extensions).join(", ")}` }) : null
15728
16261
  ] })
15729
16262
  ] });
15730
16263
  };
15731
16264
 
15732
- // src/commands/x402/inspect.tsx
15733
- import { Box as Box18, Text as Text19 } from "ink";
15734
- import Spinner14 from "ink-spinner";
15735
- import { useEffect as useEffect9, useReducer as useReducer7 } from "react";
15736
- import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
15737
- function summarizeExtra(extra) {
15738
- if (extra === void 0) return "\u2014";
15739
- const keys = Object.keys(extra);
15740
- if (keys.length === 0) return "\u2014";
15741
- return [...keys].sort((a, b) => a.localeCompare(b)).join(", ");
15742
- }
15743
- function formatTimeout(seconds) {
15744
- return `${String(seconds)}s`;
15745
- }
15746
- function formatAsset(asset) {
15747
- return asset === "" ? "\u2014" : asset;
15748
- }
15749
- var COLUMNS5 = [
15750
- { header: "Scheme", cell: (r) => r.scheme },
15751
- { header: "Network", cell: (r) => r.network },
15752
- { header: "Amount", cell: (r) => r.amount },
15753
- { header: "Asset", cell: (r) => formatAsset(r.asset) },
15754
- { header: "Pay To", cell: (r) => r.payTo },
15755
- { header: "Timeout", cell: (r) => formatTimeout(r.maxTimeoutSeconds) },
15756
- { header: "Extra", cell: (r) => summarizeExtra(r.extra) }
15757
- ];
15758
- var InspectView2 = ({ url, method, deps, onComplete }) => {
15759
- const initial = { kind: "probing" };
15760
- const [phase, dispatch] = useReducer7(reduceX402Inspect, initial);
15761
- const { finish } = useFlowExit(onComplete);
15762
- useEffect9(() => {
15763
- let cancelled = false;
15764
- void runInspectPipeline(deps, (event) => {
15765
- if (!cancelled) dispatch(event);
15766
- });
15767
- return () => {
15768
- cancelled = true;
15769
- };
15770
- }, [deps]);
15771
- useEffect9(() => {
15772
- if (phase.kind === "accepts" || phase.kind === "no-payment" || phase.kind === "error") {
15773
- finish(phase);
15774
- }
15775
- }, [phase, finish]);
15776
- if (phase.kind === "probing") {
15777
- return /* @__PURE__ */ jsx23(Box18, { children: /* @__PURE__ */ jsxs18(Text19, { color: "cyan", children: [
15778
- /* @__PURE__ */ jsx23(Spinner14, { type: "dots" }),
15779
- " Probing ",
15780
- method,
15781
- " ",
15782
- url,
15783
- "..."
15784
- ] }) });
15785
- }
15786
- if (phase.kind === "no-payment") {
15787
- return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
15788
- /* @__PURE__ */ jsx23(Text19, { color: "green", children: "\u2713 Seller accepted without payment" }),
15789
- /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: "Use `x402 pay` to fetch the body." })
15790
- ] });
15791
- }
15792
- if (phase.kind === "error") {
15793
- return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
15794
- /* @__PURE__ */ jsxs18(Text19, { color: "red", children: [
15795
- "\u2717 ",
15796
- phase.code
15797
- ] }),
15798
- /* @__PURE__ */ jsx23(Text19, { color: "red", children: phase.message })
15799
- ] });
15800
- }
15801
- const { result } = phase;
15802
- const acceptsCount = result.accepts.length;
15803
- const extensionsLine = result.extensions !== void 0 ? Object.keys(result.extensions).join(", ") : null;
15804
- return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
15805
- /* @__PURE__ */ jsxs18(Text19, { children: [
15806
- /* @__PURE__ */ jsx23(Text19, { bold: true, children: "PAYMENT-REQUIRED" }),
15807
- " for ",
15808
- /* @__PURE__ */ jsx23(Text19, { color: "cyan", children: result.resource }),
15809
- " \xB7 ",
15810
- /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: `x402Version ${String(result.x402Version)}` }),
15811
- " \xB7 ",
15812
- /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: `${String(acceptsCount)} accept${acceptsCount === 1 ? "" : "s"}` })
15813
- ] }),
15814
- extensionsLine !== null ? /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: `extensions: ${extensionsLine}` }) : null,
15815
- /* @__PURE__ */ jsx23(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx23(Table, { columns: COLUMNS5, rows: result.accepts }) }),
15816
- /* @__PURE__ */ jsx23(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: "Use --format json to inspect extras values." }) })
15817
- ] });
15818
- };
15819
- function buildAcceptsFrame(result) {
15820
- const frame = {
15821
- outcome: "accepts",
15822
- url: result.url,
15823
- method: result.method,
15824
- resource: result.resource,
15825
- x402_version: result.x402Version,
15826
- accepts: result.accepts.map((entry) => {
15827
- const row = {
15828
- scheme: entry.scheme,
15829
- network: entry.network,
15830
- amount: entry.amount,
15831
- asset: entry.asset,
15832
- pay_to: entry.payTo,
15833
- max_timeout_seconds: entry.maxTimeoutSeconds
15834
- };
15835
- if (entry.extra !== void 0) row.extra = entry.extra;
15836
- return row;
15837
- })
15838
- };
15839
- if (result.extensions !== void 0) frame.extensions = result.extensions;
15840
- return frame;
15841
- }
15842
- function buildNoPaymentFrame2(result) {
15843
- const frame = {
15844
- outcome: "no-payment-required",
15845
- url: result.url,
15846
- method: result.method,
15847
- status: result.status,
15848
- body_size_bytes: result.bodySizeBytes
15849
- };
15850
- if (result.contentType !== void 0) frame.content_type = result.contentType;
15851
- return frame;
15852
- }
15853
-
15854
16265
  // src/commands/x402/pay.tsx
15855
- import { Box as Box19, Text as Text20, useInput as useInput4 } from "ink";
15856
- import Spinner15 from "ink-spinner";
15857
- import { useEffect as useEffect10, useReducer as useReducer8, useState as useState4 } from "react";
15858
- import { jsx as jsx24, jsxs as jsxs19 } from "react/jsx-runtime";
16266
+ import { Box as Box20, Text as Text21, useInput as useInput4 } from "ink";
16267
+ import Spinner16 from "ink-spinner";
16268
+ import { useEffect as useEffect11, useReducer as useReducer9, useState as useState4 } from "react";
16269
+ import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
15859
16270
  var PayView2 = ({ url, method, deps, onComplete, onCancel }) => {
15860
16271
  const initial = { kind: "probing" };
15861
- const [phase, dispatch] = useReducer8(reducePay, initial);
16272
+ const [phase, dispatch] = useReducer9(reducePay, initial);
15862
16273
  const [cancelling, setCancelling] = useState4(false);
15863
16274
  const { finish, cancelThenFinish } = useFlowExit(onComplete);
15864
16275
  useInput4(
@@ -15880,7 +16291,7 @@ var PayView2 = ({ url, method, deps, onComplete, onCancel }) => {
15880
16291
  },
15881
16292
  { isActive: phase.kind === "awaiting-approval" && !cancelling }
15882
16293
  );
15883
- useEffect10(() => {
16294
+ useEffect11(() => {
15884
16295
  const controller = new AbortController();
15885
16296
  let cancelled = false;
15886
16297
  const runDeps = { ...deps, signOptions: { ...deps.signOptions, signal: controller.signal } };
@@ -15892,20 +16303,20 @@ var PayView2 = ({ url, method, deps, onComplete, onCancel }) => {
15892
16303
  controller.abort();
15893
16304
  };
15894
16305
  }, [deps]);
15895
- useEffect10(() => {
16306
+ useEffect11(() => {
15896
16307
  if (phase.kind === "success" || phase.kind === "replay-rejected" || phase.kind === "no-payment-final" || phase.kind === "error") {
15897
16308
  finish(phase);
15898
16309
  }
15899
16310
  }, [phase, finish]);
15900
16311
  if (cancelling) {
15901
- return /* @__PURE__ */ jsx24(Box19, { children: /* @__PURE__ */ jsxs19(Text20, { color: "yellow", children: [
15902
- /* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
16312
+ return /* @__PURE__ */ jsx26(Box20, { children: /* @__PURE__ */ jsxs20(Text21, { color: "yellow", children: [
16313
+ /* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
15903
16314
  " Cancelling approval..."
15904
16315
  ] }) });
15905
16316
  }
15906
16317
  if (phase.kind === "probing") {
15907
- return /* @__PURE__ */ jsx24(Box19, { children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
15908
- /* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
16318
+ return /* @__PURE__ */ jsx26(Box20, { children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
16319
+ /* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
15909
16320
  " Probing ",
15910
16321
  method,
15911
16322
  " ",
@@ -15914,23 +16325,23 @@ var PayView2 = ({ url, method, deps, onComplete, onCancel }) => {
15914
16325
  ] }) });
15915
16326
  }
15916
16327
  if (phase.kind === "no-payment") {
15917
- return /* @__PURE__ */ jsx24(Box19, { children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
15918
- /* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
16328
+ return /* @__PURE__ */ jsx26(Box20, { children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
16329
+ /* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
15919
16330
  " Seller accepted without payment (status ",
15920
16331
  String(phase.probe.status),
15921
16332
  "); finalising..."
15922
16333
  ] }) });
15923
16334
  }
15924
16335
  if (phase.kind === "matching") {
15925
- return /* @__PURE__ */ jsx24(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
15926
- /* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
16336
+ return /* @__PURE__ */ jsx26(Box20, { flexDirection: "column", children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
16337
+ /* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
15927
16338
  " Decoding seller requirements..."
15928
16339
  ] }) });
15929
16340
  }
15930
16341
  if (phase.kind === "preparing") {
15931
16342
  const summary = summarizeAccepts([phase.requirement]);
15932
- return /* @__PURE__ */ jsx24(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
15933
- /* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
16343
+ return /* @__PURE__ */ jsx26(Box20, { flexDirection: "column", children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
16344
+ /* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
15934
16345
  " Preparing payment (",
15935
16346
  summary[0]?.scheme ?? phase.requirement.scheme,
15936
16347
  " /",
@@ -15940,167 +16351,167 @@ var PayView2 = ({ url, method, deps, onComplete, onCancel }) => {
15940
16351
  ] }) });
15941
16352
  }
15942
16353
  if (phase.kind === "awaiting-approval") {
15943
- return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", paddingY: 1, children: [
15944
- /* @__PURE__ */ jsx24(Box19, { marginBottom: 1, children: /* @__PURE__ */ jsx24(Text20, { bold: true, children: "Approval required" }) }),
15945
- /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
15946
- /* @__PURE__ */ jsxs19(Text20, { children: [
16354
+ return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", paddingY: 1, children: [
16355
+ /* @__PURE__ */ jsx26(Box20, { marginBottom: 1, children: /* @__PURE__ */ jsx26(Text21, { bold: true, children: "Approval required" }) }),
16356
+ /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
16357
+ /* @__PURE__ */ jsxs20(Text21, { children: [
15947
16358
  "Open: ",
15948
- /* @__PURE__ */ jsx24(Text20, { bold: true, color: "cyan", children: phase.approvalUrl })
16359
+ /* @__PURE__ */ jsx26(Text21, { bold: true, color: "cyan", children: phase.approvalUrl })
15949
16360
  ] }),
15950
- /* @__PURE__ */ jsx24(Text20, { dimColor: true, children: "Press Enter to open in browser." }),
15951
- /* @__PURE__ */ jsx24(Text20, { dimColor: true, children: "Press Escape to cancel." })
16361
+ /* @__PURE__ */ jsx26(Text21, { dimColor: true, children: "Press Enter to open in browser." }),
16362
+ /* @__PURE__ */ jsx26(Text21, { dimColor: true, children: "Press Escape to cancel." })
15952
16363
  ] }),
15953
- /* @__PURE__ */ jsx24(Box19, { marginTop: 1, children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
15954
- /* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
16364
+ /* @__PURE__ */ jsx26(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
16365
+ /* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
15955
16366
  " Waiting for approval..."
15956
16367
  ] }) })
15957
16368
  ] });
15958
16369
  }
15959
16370
  if (phase.kind === "replaying") {
15960
- return /* @__PURE__ */ jsx24(Box19, { children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
15961
- /* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
16371
+ return /* @__PURE__ */ jsx26(Box20, { children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
16372
+ /* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
15962
16373
  " Replaying request with PAYMENT-SIGNATURE..."
15963
16374
  ] }) });
15964
16375
  }
15965
16376
  if (phase.kind === "success") {
15966
16377
  const { result } = phase;
15967
- return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
15968
- /* @__PURE__ */ jsxs19(Text20, { color: "green", children: [
16378
+ return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
16379
+ /* @__PURE__ */ jsxs20(Text21, { color: "green", children: [
15969
16380
  "\u2713 Paid ",
15970
16381
  result.scheme,
15971
16382
  " / ",
15972
16383
  result.network
15973
16384
  ] }),
15974
- /* @__PURE__ */ jsx24(Text20, { children: `transaction: ${result.transactionId}` }),
15975
- result.settled?.network !== void 0 ? /* @__PURE__ */ jsx24(Text20, { children: `settled via: ${result.settled.network}${result.settled.transaction !== void 0 ? ` (${result.settled.transaction})` : ""}` }) : null,
15976
- result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs19(Text20, { children: [
16385
+ /* @__PURE__ */ jsx26(Text21, { children: `transaction: ${result.transactionId}` }),
16386
+ result.settled?.network !== void 0 ? /* @__PURE__ */ jsx26(Text21, { children: `settled via: ${result.settled.network}${result.settled.transaction !== void 0 ? ` (${result.settled.transaction})` : ""}` }) : null,
16387
+ result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs20(Text21, { children: [
15977
16388
  "Saved to: ",
15978
- /* @__PURE__ */ jsx24(Text20, { bold: true, children: result.outputSavedTo })
16389
+ /* @__PURE__ */ jsx26(Text21, { bold: true, children: result.outputSavedTo })
15979
16390
  ] }) : null,
15980
- result.body !== void 0 ? /* @__PURE__ */ jsxs19(Box19, { marginTop: 1, flexDirection: "column", children: [
15981
- /* @__PURE__ */ jsx24(Text20, { dimColor: true, children: "response body:" }),
15982
- /* @__PURE__ */ jsx24(Text20, { children: result.body })
16391
+ result.body !== void 0 ? /* @__PURE__ */ jsxs20(Box20, { marginTop: 1, flexDirection: "column", children: [
16392
+ /* @__PURE__ */ jsx26(Text21, { dimColor: true, children: "response body:" }),
16393
+ /* @__PURE__ */ jsx26(Text21, { children: result.body })
15983
16394
  ] }) : null
15984
16395
  ] });
15985
16396
  }
15986
16397
  if (phase.kind === "replay-rejected") {
15987
16398
  const { result } = phase;
15988
- return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
15989
- /* @__PURE__ */ jsxs19(Text20, { color: "red", children: [
16399
+ return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
16400
+ /* @__PURE__ */ jsxs20(Text21, { color: "red", children: [
15990
16401
  "\u2717 Payment not accepted (",
15991
16402
  result.scheme,
15992
16403
  " / ",
15993
16404
  result.network,
15994
16405
  ")"
15995
16406
  ] }),
15996
- /* @__PURE__ */ jsx24(Text20, { children: `transaction: ${result.transactionId}` }),
15997
- /* @__PURE__ */ jsx24(Text20, { children: `approval: ${result.approvalId}` }),
15998
- /* @__PURE__ */ jsx24(Text20, { children: `approval url: ${result.approvalUrl}` }),
15999
- result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs19(Text20, { children: [
16407
+ /* @__PURE__ */ jsx26(Text21, { children: `transaction: ${result.transactionId}` }),
16408
+ /* @__PURE__ */ jsx26(Text21, { children: `approval: ${result.approvalId}` }),
16409
+ /* @__PURE__ */ jsx26(Text21, { children: `approval url: ${result.approvalUrl}` }),
16410
+ result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs20(Text21, { children: [
16000
16411
  "Saved to: ",
16001
- /* @__PURE__ */ jsx24(Text20, { bold: true, children: result.outputSavedTo })
16412
+ /* @__PURE__ */ jsx26(Text21, { bold: true, children: result.outputSavedTo })
16002
16413
  ] }) : null,
16003
- result.body !== void 0 ? /* @__PURE__ */ jsxs19(Box19, { marginTop: 1, flexDirection: "column", children: [
16004
- /* @__PURE__ */ jsx24(Text20, { dimColor: true, children: "response body:" }),
16005
- /* @__PURE__ */ jsx24(Text20, { children: result.body })
16414
+ result.body !== void 0 ? /* @__PURE__ */ jsxs20(Box20, { marginTop: 1, flexDirection: "column", children: [
16415
+ /* @__PURE__ */ jsx26(Text21, { dimColor: true, children: "response body:" }),
16416
+ /* @__PURE__ */ jsx26(Text21, { children: result.body })
16006
16417
  ] }) : null
16007
16418
  ] });
16008
16419
  }
16009
16420
  if (phase.kind === "no-payment-final") {
16010
16421
  const { result } = phase;
16011
- return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
16012
- /* @__PURE__ */ jsx24(Text20, { color: "green", children: "\u2713 Seller accepted without payment" }),
16013
- result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs19(Text20, { children: [
16422
+ return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
16423
+ /* @__PURE__ */ jsx26(Text21, { color: "green", children: "\u2713 Seller accepted without payment" }),
16424
+ result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs20(Text21, { children: [
16014
16425
  "Saved to: ",
16015
- /* @__PURE__ */ jsx24(Text20, { bold: true, children: result.outputSavedTo })
16426
+ /* @__PURE__ */ jsx26(Text21, { bold: true, children: result.outputSavedTo })
16016
16427
  ] }) : null,
16017
- result.body !== void 0 ? /* @__PURE__ */ jsxs19(Box19, { marginTop: 1, flexDirection: "column", children: [
16018
- /* @__PURE__ */ jsx24(Text20, { dimColor: true, children: "response body:" }),
16019
- /* @__PURE__ */ jsx24(Text20, { children: result.body })
16428
+ result.body !== void 0 ? /* @__PURE__ */ jsxs20(Box20, { marginTop: 1, flexDirection: "column", children: [
16429
+ /* @__PURE__ */ jsx26(Text21, { dimColor: true, children: "response body:" }),
16430
+ /* @__PURE__ */ jsx26(Text21, { children: result.body })
16020
16431
  ] }) : null
16021
16432
  ] });
16022
16433
  }
16023
- return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
16024
- /* @__PURE__ */ jsxs19(Text20, { color: "red", children: [
16434
+ return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
16435
+ /* @__PURE__ */ jsxs20(Text21, { color: "red", children: [
16025
16436
  "\u2717 ",
16026
16437
  phase.code
16027
16438
  ] }),
16028
- /* @__PURE__ */ jsx24(Text20, { color: "red", children: phase.message })
16439
+ /* @__PURE__ */ jsx26(Text21, { color: "red", children: phase.message })
16029
16440
  ] });
16030
16441
  };
16031
16442
 
16032
16443
  // src/commands/x402/schema.ts
16033
- import { z as z6 } from "incur";
16034
- var payArgs2 = z6.object({
16035
- url: z6.string().describe("The x402-protected resource URL to pay for.")
16444
+ import { z as z7 } from "incur";
16445
+ var payArgs2 = z7.object({
16446
+ url: z7.string().describe("The x402-protected resource URL to pay for.")
16036
16447
  });
16037
- var payOptions2 = z6.object({
16038
- scheme: z6.string().optional().describe('Only consider `accepts[]` entries with this scheme (e.g. "exact", "balance").'),
16039
- network: z6.string().optional().describe('Only consider entries on this network (e.g. "eip155:84532").'),
16040
- asset: z6.string().optional().describe("Only consider entries with this on-chain asset id (ERC-20 address or SVM mint)."),
16041
- assetName: z6.string().optional().describe('Only consider entries whose `extra.assetName` symbol matches (e.g. "USDC").'),
16042
- method: z6.string().default("GET").describe("HTTP method for the seller request."),
16043
- data: z6.string().optional().describe(
16448
+ var payOptions2 = z7.object({
16449
+ scheme: z7.string().optional().describe('Only consider `accepts[]` entries with this scheme (e.g. "exact", "balance").'),
16450
+ network: z7.string().optional().describe('Only consider entries on this network (e.g. "eip155:84532").'),
16451
+ asset: z7.string().optional().describe("Only consider entries with this on-chain asset id (ERC-20 address or SVM mint)."),
16452
+ assetName: z7.string().optional().describe('Only consider entries whose `extra.assetName` symbol matches (e.g. "USDC").'),
16453
+ method: z7.string().default("GET").describe("HTTP method for the seller request."),
16454
+ data: z7.string().optional().describe(
16044
16455
  "Request body. JSON or raw text. Content-Type defaults to application/json when --data is set unless a --header overrides it."
16045
16456
  ),
16046
- header: z6.array(z6.string()).default([]).describe('Repeatable. "Name: Value" format.'),
16047
- interval: z6.coerce.number().default(0).describe(
16457
+ header: z7.array(z7.string()).default([]).describe('Repeatable. "Name: Value" format.'),
16458
+ interval: z7.coerce.number().default(0).describe(
16048
16459
  "Inline poll cadence in seconds while awaiting approval. 0 returns the approval URL and a follow-up command hint without blocking."
16049
16460
  ),
16050
- maxAttempts: z6.coerce.number().default(0).describe("Hard cap on poll attempts when --interval > 0. 0 means unlimited."),
16051
- timeout: z6.coerce.number().default(900).describe("Polling deadline in seconds. Default 900s (matches x402-buyer)."),
16052
- paymentId: z6.string().min(16).max(128).regex(/^[a-zA-Z0-9_-]+$/).optional().describe(
16461
+ maxAttempts: z7.coerce.number().default(0).describe("Hard cap on poll attempts when --interval > 0. 0 means unlimited."),
16462
+ timeout: z7.coerce.number().default(900).describe("Polling deadline in seconds. Default 900s (matches x402-buyer)."),
16463
+ paymentId: z7.string().min(16).max(128).regex(/^[a-zA-Z0-9_-]+$/).optional().describe(
16053
16464
  "Caller-supplied payment identifier. 16-128 chars, ^[a-zA-Z0-9_-]+$. Forwarded to the server as remotePaymentId."
16054
16465
  ),
16055
- showBody: z6.boolean().default(true).describe(
16466
+ showBody: z7.boolean().default(true).describe(
16056
16467
  "Include the seller response body in the result. Default true so AI assistants paying for content receive the deliverable. Pass --no-show-body to suppress (e.g. for binary downloads paired with --output-file)."
16057
16468
  ),
16058
- outputFile: z6.string().optional().describe(
16469
+ outputFile: z7.string().optional().describe(
16059
16470
  "Write the seller response body bytes to this file path (overwrites silently). When set, the result frame includes `output_saved_to: <absolute_path>` instead of `body` / `body_base64`. Natural choice for binary content (PDFs, images, downloads)."
16060
16471
  ),
16061
- payloadFile: z6.string().optional().describe(
16472
+ payloadFile: z7.string().optional().describe(
16062
16473
  "Write the signed `encoded_payload` bytes to this file path (mode 0o600, overwrites silently). When set, the result frame includes `payload_saved_to: <absolute_path>` instead of `encoded_payload`. Use to keep one-time payment credentials out of chat transcripts and logs."
16063
16474
  )
16064
16475
  });
16065
- var statusArgs2 = z6.object({
16066
- transactionId: z6.string().describe("The transaction id returned by `x402 pay`.")
16476
+ var statusArgs2 = z7.object({
16477
+ transactionId: z7.string().describe("The transaction id returned by `x402 pay`.")
16067
16478
  });
16068
- var statusOptions3 = z6.object({
16069
- interval: z6.coerce.number().default(0).describe(
16479
+ var statusOptions3 = z7.object({
16480
+ interval: z7.coerce.number().default(0).describe(
16070
16481
  "Poll cadence in seconds. 0 returns the current snapshot; positive values yield on every change until signed or terminal."
16071
16482
  ),
16072
- maxAttempts: z6.coerce.number().default(0).describe("Hard cap on poll attempts. 0 means unlimited."),
16073
- timeout: z6.coerce.number().default(900).describe("Polling deadline in seconds."),
16074
- payloadFile: z6.string().optional().describe(
16483
+ maxAttempts: z7.coerce.number().default(0).describe("Hard cap on poll attempts. 0 means unlimited."),
16484
+ timeout: z7.coerce.number().default(900).describe("Polling deadline in seconds."),
16485
+ payloadFile: z7.string().optional().describe(
16075
16486
  "Write the signed `encoded_payload` bytes to this file path (mode 0o600, overwrites silently). When set, status frames include `payload_saved_to: <absolute_path>` instead of `encoded_payload`. Use to keep one-time payment credentials out of chat transcripts and logs."
16076
16487
  )
16077
16488
  });
16078
- var cancelArgs2 = z6.object({
16079
- approvalId: z6.string().describe("The approval id returned by `x402 pay`.")
16489
+ var cancelArgs2 = z7.object({
16490
+ approvalId: z7.string().describe("The approval id returned by `x402 pay`.")
16080
16491
  });
16081
- var decodeArgs2 = z6.object({
16082
- header: z6.string().describe("Raw PAYMENT-REQUIRED header value (base64).")
16492
+ var decodeArgs2 = z7.object({
16493
+ header: z7.string().describe("Raw PAYMENT-REQUIRED header value (base64).")
16083
16494
  });
16084
- var inspectArgs2 = z6.object({
16085
- url: z6.string().describe("The x402-protected resource URL to probe. No payment is made.")
16495
+ var inspectArgs3 = z7.object({
16496
+ url: z7.string().describe("The x402-protected resource URL to probe. No payment is made.")
16086
16497
  });
16087
- var inspectOptions2 = z6.object({
16088
- scheme: z6.string().optional().describe('Only show `accepts[]` entries with this scheme (e.g. "exact", "balance").'),
16089
- network: z6.string().optional().describe('Only show entries on this network (e.g. "eip155:84532").'),
16090
- asset: z6.string().optional().describe("Only show entries with this on-chain asset id (ERC-20 address or SVM mint)."),
16091
- assetName: z6.string().optional().describe('Only show entries whose `extra.assetName` symbol matches (e.g. "USDC").'),
16092
- method: z6.string().default("GET").describe("HTTP method for the probe request."),
16093
- data: z6.string().optional().describe(
16498
+ var inspectOptions3 = z7.object({
16499
+ scheme: z7.string().optional().describe('Only show `accepts[]` entries with this scheme (e.g. "exact", "balance").'),
16500
+ network: z7.string().optional().describe('Only show entries on this network (e.g. "eip155:84532").'),
16501
+ asset: z7.string().optional().describe("Only show entries with this on-chain asset id (ERC-20 address or SVM mint)."),
16502
+ assetName: z7.string().optional().describe('Only show entries whose `extra.assetName` symbol matches (e.g. "USDC").'),
16503
+ method: z7.string().default("GET").describe("HTTP method for the probe request."),
16504
+ data: z7.string().optional().describe(
16094
16505
  "Request body for the probe. JSON or raw text. Content-Type defaults to application/json when --data is set unless a --header overrides it."
16095
16506
  ),
16096
- header: z6.array(z6.string()).default([]).describe('Repeatable. "Name: Value" format.')
16507
+ header: z7.array(z7.string()).default([]).describe('Repeatable. "Name: Value" format.')
16097
16508
  });
16098
16509
 
16099
16510
  // src/commands/x402/status.tsx
16100
- import { Box as Box20, Text as Text21 } from "ink";
16101
- import Spinner16 from "ink-spinner";
16102
- import { useEffect as useEffect11, useReducer as useReducer9 } from "react";
16103
- import { jsx as jsx25, jsxs as jsxs20 } from "react/jsx-runtime";
16511
+ import { Box as Box21, Text as Text22 } from "ink";
16512
+ import Spinner17 from "ink-spinner";
16513
+ import { useEffect as useEffect12, useReducer as useReducer10 } from "react";
16514
+ import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
16104
16515
  var X402StatusView = ({
16105
16516
  transactionId,
16106
16517
  fetchOnce,
@@ -16110,9 +16521,9 @@ var X402StatusView = ({
16110
16521
  onComplete
16111
16522
  }) => {
16112
16523
  const initial = { kind: "polling" };
16113
- const [phase, dispatch] = useReducer9(reduceX402Status, initial);
16524
+ const [phase, dispatch] = useReducer10(reduceX402Status, initial);
16114
16525
  const { finish } = useFlowExit(onComplete);
16115
- useEffect11(() => {
16526
+ useEffect12(() => {
16116
16527
  const run = runX402Status({ fetchOnce, interval, maxAttempts, timeout });
16117
16528
  let cancelled = false;
16118
16529
  void (async () => {
@@ -16125,15 +16536,15 @@ var X402StatusView = ({
16125
16536
  cancelled = true;
16126
16537
  };
16127
16538
  }, [fetchOnce, interval, maxAttempts, timeout]);
16128
- useEffect11(() => {
16539
+ useEffect12(() => {
16129
16540
  if (phase.kind === "signed" || phase.kind === "failed" || phase.kind === "timeout" || phase.kind === "error") {
16130
16541
  finish(phase);
16131
16542
  }
16132
16543
  }, [phase, finish]);
16133
16544
  if (phase.kind === "polling") {
16134
16545
  const statusText = phase.latest?.status ?? "pending";
16135
- return /* @__PURE__ */ jsx25(Box20, { flexDirection: "column", children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
16136
- /* @__PURE__ */ jsx25(Spinner16, { type: "dots" }),
16546
+ return /* @__PURE__ */ jsx27(Box21, { flexDirection: "column", children: /* @__PURE__ */ jsxs21(Text22, { color: "cyan", children: [
16547
+ /* @__PURE__ */ jsx27(Spinner17, { type: "dots" }),
16137
16548
  " Polling transaction ",
16138
16549
  transactionId,
16139
16550
  " (status: ",
@@ -16144,35 +16555,35 @@ var X402StatusView = ({
16144
16555
  if (phase.kind === "signed") {
16145
16556
  const encoded = phase.response.encodedPayload ?? "";
16146
16557
  const preview = encoded.length > 32 ? `${encoded.slice(0, 32)}...` : encoded;
16147
- return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
16148
- /* @__PURE__ */ jsx25(Text21, { color: "green", children: "\u2713 Signed" }),
16149
- /* @__PURE__ */ jsx25(Text21, { children: `status: ${phase.response.status}` }),
16150
- /* @__PURE__ */ jsx25(Text21, { children: `encodedPayload: ${preview}` })
16558
+ return /* @__PURE__ */ jsxs21(Box21, { flexDirection: "column", children: [
16559
+ /* @__PURE__ */ jsx27(Text22, { color: "green", children: "\u2713 Signed" }),
16560
+ /* @__PURE__ */ jsx27(Text22, { children: `status: ${phase.response.status}` }),
16561
+ /* @__PURE__ */ jsx27(Text22, { children: `encodedPayload: ${preview}` })
16151
16562
  ] });
16152
16563
  }
16153
16564
  if (phase.kind === "failed") {
16154
- return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
16155
- /* @__PURE__ */ jsx25(Text21, { color: "red", children: "\u2717 Approval did not settle" }),
16156
- /* @__PURE__ */ jsx25(Text21, { color: "red", children: `status: ${phase.response.status}` })
16565
+ return /* @__PURE__ */ jsxs21(Box21, { flexDirection: "column", children: [
16566
+ /* @__PURE__ */ jsx27(Text22, { color: "red", children: "\u2717 Approval did not settle" }),
16567
+ /* @__PURE__ */ jsx27(Text22, { color: "red", children: `status: ${phase.response.status}` })
16157
16568
  ] });
16158
16569
  }
16159
16570
  if (phase.kind === "timeout") {
16160
- return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
16161
- /* @__PURE__ */ jsx25(Text21, { color: "yellow", children: "Polling timed out before the transaction reached a signed state." }),
16162
- phase.response !== void 0 ? /* @__PURE__ */ jsx25(Text21, { children: `last status: ${phase.response.status}` }) : null
16571
+ return /* @__PURE__ */ jsxs21(Box21, { flexDirection: "column", children: [
16572
+ /* @__PURE__ */ jsx27(Text22, { color: "yellow", children: "Polling timed out before the transaction reached a signed state." }),
16573
+ phase.response !== void 0 ? /* @__PURE__ */ jsx27(Text22, { children: `last status: ${phase.response.status}` }) : null
16163
16574
  ] });
16164
16575
  }
16165
- return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
16166
- /* @__PURE__ */ jsx25(Text21, { color: "red", children: "\u2717 Polling failed" }),
16167
- /* @__PURE__ */ jsx25(Text21, { color: "red", children: phase.message })
16576
+ return /* @__PURE__ */ jsxs21(Box21, { flexDirection: "column", children: [
16577
+ /* @__PURE__ */ jsx27(Text22, { color: "red", children: "\u2717 Polling failed" }),
16578
+ /* @__PURE__ */ jsx27(Text22, { color: "red", children: phase.message })
16168
16579
  ] });
16169
16580
  };
16170
16581
 
16171
16582
  // src/commands/x402/supported.tsx
16172
- import { Box as Box21, Text as Text22 } from "ink";
16173
- import Spinner17 from "ink-spinner";
16583
+ import { Box as Box22, Text as Text23 } from "ink";
16584
+ import Spinner18 from "ink-spinner";
16174
16585
  import { useCallback as useCallback9 } from "react";
16175
- import { jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
16586
+ import { jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
16176
16587
  var COLUMNS6 = [
16177
16588
  { header: "Scheme", cell: (k) => k.scheme },
16178
16589
  { header: "Network", cell: (k) => k.network }
@@ -16182,26 +16593,26 @@ var SupportedView2 = ({ load, onComplete }) => {
16182
16593
  const { finish } = useFlowExit(onComplete);
16183
16594
  const { status, data, error } = useFlowState(action, finish);
16184
16595
  if (status === "loading") {
16185
- return /* @__PURE__ */ jsx26(Box21, { children: /* @__PURE__ */ jsxs21(Text22, { color: "cyan", children: [
16186
- /* @__PURE__ */ jsx26(Spinner17, { type: "dots" }),
16596
+ return /* @__PURE__ */ jsx28(Box22, { children: /* @__PURE__ */ jsxs22(Text23, { color: "cyan", children: [
16597
+ /* @__PURE__ */ jsx28(Spinner18, { type: "dots" }),
16187
16598
  " Loading supported schemes..."
16188
16599
  ] }) });
16189
16600
  }
16190
16601
  if (status === "error") {
16191
- return /* @__PURE__ */ jsxs21(Box21, { flexDirection: "column", children: [
16192
- /* @__PURE__ */ jsx26(Text22, { color: "red", children: "Failed to load supported schemes" }),
16193
- /* @__PURE__ */ jsx26(Text22, { color: "red", children: error })
16602
+ return /* @__PURE__ */ jsxs22(Box22, { flexDirection: "column", children: [
16603
+ /* @__PURE__ */ jsx28(Text23, { color: "red", children: "Failed to load supported schemes" }),
16604
+ /* @__PURE__ */ jsx28(Text23, { color: "red", children: error })
16194
16605
  ] });
16195
16606
  }
16196
16607
  const kinds = data?.kinds ?? [];
16197
16608
  if (kinds.length === 0) {
16198
- return /* @__PURE__ */ jsx26(Box21, { flexDirection: "column", children: /* @__PURE__ */ jsx26(Text22, { children: "No supported (scheme, network) pairs returned for this account." }) });
16609
+ return /* @__PURE__ */ jsx28(Box22, { flexDirection: "column", children: /* @__PURE__ */ jsx28(Text23, { children: "No supported (scheme, network) pairs returned for this account." }) });
16199
16610
  }
16200
- return /* @__PURE__ */ jsx26(Table, { columns: COLUMNS6, rows: kinds });
16611
+ return /* @__PURE__ */ jsx28(Table, { columns: COLUMNS6, rows: kinds });
16201
16612
  };
16202
16613
 
16203
16614
  // src/commands/x402/index.tsx
16204
- import { jsx as jsx27 } from "react/jsx-runtime";
16615
+ import { jsx as jsx29 } from "react/jsx-runtime";
16205
16616
  var POST_PAY_INSTRUCTION = "Present the approval_url to the user and ask them to approve in the InFlow mobile app or dashboard. Then call `x402 status <transaction_id> --interval 5 --max-attempts 60` to poll until signed. Once the transaction is signed, replay the request manually using the encoded_payload from the status response as the PAYMENT-SIGNATURE header.";
16206
16617
  var POLLING_INSTRUCTION2 = "Approval polling is happening inline. The yield stream emits each status change; the final frame includes the encoded_payload when signing completes.";
16207
16618
  function buildSignOptions(options) {
@@ -16210,15 +16621,11 @@ function buildSignOptions(options) {
16210
16621
  if (options.paymentId !== void 0) out.paymentId = options.paymentId;
16211
16622
  return out;
16212
16623
  }
16213
- function parseHeaderFlagsOrFail2(c, flags) {
16214
- try {
16215
- return parseHeaderFlags(flags);
16216
- } catch (err) {
16217
- c.error({
16218
- code: "INVALID_HEADER",
16219
- message: err instanceof Error ? err.message : String(err)
16220
- });
16221
- }
16624
+ function invalidHeaderError2(err) {
16625
+ return {
16626
+ code: "INVALID_HEADER",
16627
+ message: err instanceof Error ? err.message : String(err)
16628
+ };
16222
16629
  }
16223
16630
  function decoratePayloadField(frame, encoded, payloadFile) {
16224
16631
  if (payloadFile !== void 0 && payloadFile.length > 0) {
@@ -16230,13 +16637,14 @@ function decoratePayloadField(frame, encoded, payloadFile) {
16230
16637
  }
16231
16638
  frame.encoded_payload = encoded;
16232
16639
  }
16233
- function buildPayPipelineInput2(c) {
16234
- const probeHeaders = parseHeaderFlagsOrFail2(c, c.options.header);
16235
- const probeOptions = {
16640
+ function probeOptionsFrom2(c) {
16641
+ return {
16236
16642
  method: c.options.method,
16237
- headers: probeHeaders,
16643
+ headers: parseHeaderFlags(c.options.header),
16238
16644
  ...c.options.data !== void 0 ? { data: c.options.data } : {}
16239
16645
  };
16646
+ }
16647
+ function buildPayPipelineInput2(c, probeOptions) {
16240
16648
  return {
16241
16649
  probeOptions,
16242
16650
  url: c.args.url,
@@ -16317,17 +16725,17 @@ function rejectedFrameFromResult2(result) {
16317
16725
  }
16318
16726
  async function* runPayCommand2(c, inflow2, authStorage2, apiBaseUrl2) {
16319
16727
  assertSessionGuard(c, authStorage2, inflow2);
16728
+ let probeOptions;
16729
+ try {
16730
+ probeOptions = probeOptionsFrom2(c);
16731
+ } catch (err) {
16732
+ return c.error(invalidHeaderError2(err));
16733
+ }
16320
16734
  if (!c.agent && !c.formatExplicit) {
16321
16735
  const client = await inflow2.x402.client();
16322
- const probeHeaders = parseHeaderFlagsOrFail2(c, c.options.header);
16323
- const probeOptions = {
16324
- method: c.options.method,
16325
- headers: probeHeaders,
16326
- ...c.options.data !== void 0 ? { data: c.options.data } : {}
16327
- };
16328
16736
  let finalPhase = null;
16329
16737
  await renderInkUntilExit(
16330
- /* @__PURE__ */ jsx27(
16738
+ /* @__PURE__ */ jsx29(
16331
16739
  PayView2,
16332
16740
  {
16333
16741
  url: c.args.url,
@@ -16355,19 +16763,19 @@ async function* runPayCommand2(c, inflow2, authStorage2, apiBaseUrl2) {
16355
16763
  if (finalPhase !== null) {
16356
16764
  const phase = finalPhase;
16357
16765
  if (phase.kind === "replay-rejected") {
16358
- c.error({
16766
+ return c.error({
16359
16767
  code: PAYMENT_NOT_ACCEPTED_CODE,
16360
16768
  message: `Seller rejected the signed payment with status ${String(phase.result.responseStatus)}. The approval was completed but the seller did not honour the payment.`
16361
16769
  });
16362
16770
  }
16363
16771
  if (phase.kind === "error") {
16364
- c.error({ code: phase.code, message: phase.message });
16772
+ return c.error({ code: phase.code, message: phase.message });
16365
16773
  }
16366
16774
  }
16367
16775
  return;
16368
16776
  }
16369
16777
  const run = inflow2.x402.pay({
16370
- ...buildPayPipelineInput2(c),
16778
+ ...buildPayPipelineInput2(c, probeOptions),
16371
16779
  awaitPayment: c.options.interval > 0
16372
16780
  });
16373
16781
  for await (const event of run.events) {
@@ -16385,14 +16793,13 @@ async function* runPayCommand2(c, inflow2, authStorage2, apiBaseUrl2) {
16385
16793
  }
16386
16794
  if (event.type === "rejected") {
16387
16795
  yield sanitizeDeep(rejectedFrameFromResult2(event.result));
16388
- c.error({
16796
+ return c.error({
16389
16797
  code: PAYMENT_NOT_ACCEPTED_CODE,
16390
16798
  message: `Seller rejected the signed payment with status ${String(event.result.responseStatus)}. The approval was completed but the seller did not honour the payment; see approval_url in the previous frame for details.`
16391
16799
  });
16392
- return;
16393
16800
  }
16394
16801
  if (event.type === "errored") {
16395
- c.error({ code: event.code, message: event.message });
16802
+ return c.error({ code: event.code, message: event.message });
16396
16803
  }
16397
16804
  }
16398
16805
  }
@@ -16401,7 +16808,7 @@ async function* runStatusCommand2(c, inflow2, authStorage2) {
16401
16808
  if (!c.agent && !c.formatExplicit) {
16402
16809
  const client2 = await inflow2.x402.client();
16403
16810
  await renderInkUntilExit(
16404
- /* @__PURE__ */ jsx27(
16811
+ /* @__PURE__ */ jsx29(
16405
16812
  X402StatusView,
16406
16813
  {
16407
16814
  transactionId: c.args.transactionId,
@@ -16434,14 +16841,14 @@ async function* runStatusCommand2(c, inflow2, authStorage2) {
16434
16841
  yield sanitizeDeep(toStatusFrame2(c.args.transactionId, outcome.value, c.options.payloadFile));
16435
16842
  if (!outcome.terminal) continue;
16436
16843
  if (outcome.reason !== void 0) {
16437
- c.error({
16844
+ return c.error({
16438
16845
  code: "POLLING_TIMEOUT",
16439
16846
  message: outcome.reason === "timeout" ? "Polling timed out before the transaction reached a signed state." : "Reached the configured maximum poll attempts before signed state.",
16440
16847
  retryable: true
16441
16848
  });
16442
16849
  }
16443
16850
  if (classifyPayloadResponse(outcome.value) === "failed") {
16444
- c.error({
16851
+ return c.error({
16445
16852
  code: "APPROVAL_FAILED",
16446
16853
  message: `Transaction ${c.args.transactionId} terminated as ${outcome.value.status} with no payload.`
16447
16854
  });
@@ -16464,7 +16871,7 @@ async function runCancelCommand2(c, inflow2, authStorage2) {
16464
16871
  assertSessionGuard(c, authStorage2, inflow2);
16465
16872
  if (!c.agent && !c.formatExplicit) {
16466
16873
  await renderInkUntilExit(
16467
- /* @__PURE__ */ jsx27(
16874
+ /* @__PURE__ */ jsx29(
16468
16875
  CancelView2,
16469
16876
  {
16470
16877
  approvalId: c.args.approvalId,
@@ -16486,13 +16893,13 @@ async function runDecodeCommand2(c) {
16486
16893
  try {
16487
16894
  decoded = decodeHeader(c.args.header);
16488
16895
  } catch (err) {
16489
- c.error({
16896
+ return c.error({
16490
16897
  code: "DECODE_FAILED",
16491
16898
  message: err instanceof Error ? err.message : String(err)
16492
16899
  });
16493
16900
  }
16494
16901
  if (!c.agent && !c.formatExplicit) {
16495
- await renderInkUntilExit(/* @__PURE__ */ jsx27(DecodeView2, { decoded }));
16902
+ await renderInkUntilExit(/* @__PURE__ */ jsx29(DecodeView2, { decoded }));
16496
16903
  return void 0;
16497
16904
  }
16498
16905
  return sanitizeDeep(decoded);
@@ -16500,19 +16907,19 @@ async function runDecodeCommand2(c) {
16500
16907
  async function runSupportedCommand2(c, inflow2, authStorage2) {
16501
16908
  assertSessionGuard(c, authStorage2, inflow2);
16502
16909
  if (!c.agent && !c.formatExplicit) {
16503
- await renderInkUntilExit(/* @__PURE__ */ jsx27(SupportedView2, { load: () => inflow2.x402.supported(), onComplete: () => void 0 }));
16910
+ await renderInkUntilExit(/* @__PURE__ */ jsx29(SupportedView2, { load: () => inflow2.x402.supported(), onComplete: () => void 0 }));
16504
16911
  return void 0;
16505
16912
  }
16506
16913
  const response = await inflow2.x402.supported();
16507
16914
  return sanitizeDeep(response);
16508
16915
  }
16509
16916
  async function runInspectCommand2(c) {
16510
- const probeHeaders = parseHeaderFlagsOrFail2(c, c.options.header);
16511
- const probeOptions = {
16512
- method: c.options.method,
16513
- headers: probeHeaders,
16514
- ...c.options.data !== void 0 ? { data: c.options.data } : {}
16515
- };
16917
+ let probeOptions;
16918
+ try {
16919
+ probeOptions = probeOptionsFrom2(c);
16920
+ } catch (err) {
16921
+ return c.error(invalidHeaderError2(err));
16922
+ }
16516
16923
  const deps = {
16517
16924
  probeOptions,
16518
16925
  url: c.args.url,
@@ -16524,7 +16931,7 @@ async function runInspectCommand2(c) {
16524
16931
  if (!c.agent && !c.formatExplicit) {
16525
16932
  let finalPhase = null;
16526
16933
  await renderInkUntilExit(
16527
- /* @__PURE__ */ jsx27(
16934
+ /* @__PURE__ */ jsx29(
16528
16935
  InspectView2,
16529
16936
  {
16530
16937
  url: c.args.url,
@@ -16539,7 +16946,7 @@ async function runInspectCommand2(c) {
16539
16946
  if (finalPhase !== null) {
16540
16947
  const phase = finalPhase;
16541
16948
  if (phase.kind === "error") {
16542
- c.error({ code: phase.code, message: phase.message });
16949
+ return c.error({ code: phase.code, message: phase.message });
16543
16950
  }
16544
16951
  }
16545
16952
  return void 0;
@@ -16559,12 +16966,12 @@ async function runInspectCommand2(c) {
16559
16966
  }
16560
16967
  });
16561
16968
  if (finalEvent === null) {
16562
- c.error({ code: "INSPECT_FAILED", message: "Inspect pipeline produced no result." });
16969
+ return c.error({ code: "INSPECT_FAILED", message: "Inspect pipeline produced no result." });
16563
16970
  }
16564
16971
  const { kind, payload } = finalEvent;
16565
16972
  if (kind === "error") {
16566
16973
  const err = payload;
16567
- c.error({ code: err.code, message: err.message });
16974
+ return c.error({ code: err.code, message: err.message });
16568
16975
  }
16569
16976
  if (kind === "accepts") {
16570
16977
  return sanitizeDeep(buildAcceptsFrame(payload));
@@ -16581,7 +16988,7 @@ function createX402Cli(inflow2, authStorage2, apiBaseUrl2) {
16581
16988
  options: payOptions2,
16582
16989
  outputPolicy: "agent-only",
16583
16990
  async *run(c) {
16584
- yield* runPayCommand2(c, inflow2, authStorage2, apiBaseUrl2);
16991
+ return yield* runPayCommand2(c, inflow2, authStorage2, apiBaseUrl2);
16585
16992
  }
16586
16993
  });
16587
16994
  cli2.command("status", {
@@ -16590,7 +16997,7 @@ function createX402Cli(inflow2, authStorage2, apiBaseUrl2) {
16590
16997
  options: statusOptions3,
16591
16998
  outputPolicy: "agent-only",
16592
16999
  async *run(c) {
16593
- yield* runStatusCommand2(c, inflow2, authStorage2);
17000
+ return yield* runStatusCommand2(c, inflow2, authStorage2);
16594
17001
  }
16595
17002
  });
16596
17003
  cli2.command("cancel", {
@@ -16618,8 +17025,8 @@ function createX402Cli(inflow2, authStorage2, apiBaseUrl2) {
16618
17025
  });
16619
17026
  cli2.command("inspect", {
16620
17027
  description: "Show the seller's PAYMENT-REQUIRED accepts for a URL. Read-only probe \u2014 no auth, no payment.",
16621
- args: inspectArgs2,
16622
- options: inspectOptions2,
17028
+ args: inspectArgs3,
17029
+ options: inspectOptions3,
16623
17030
  outputPolicy: "agent-only",
16624
17031
  async run(c) {
16625
17032
  return runInspectCommand2(c);
@@ -16696,9 +17103,9 @@ function formatUpdateNotice(info) {
16696
17103
  }
16697
17104
 
16698
17105
  // src/cli.tsx
16699
- var cliVersion = "0.6.4";
17106
+ var cliVersion = "0.6.6";
16700
17107
  var cliName = "@inflowpayai/inflow";
16701
- var skillBody = '# Agentic Payments\n\nPay HTTP 402-protected resources on the user\'s behalf. InFlow speaks two payment protocols \u2014 **MPP** and **x402** \u2014 but the flow is the same for both: shared setup (install, run, authenticate), then a **router** that picks the protocol from the seller\'s 402 header, then one **Paying a 402 resource** section that covers both. A per-protocol **delta table** at the top of that section lists the handful of real differences (header name, credential name, filters, error codes); read your row, then follow the shared steps.\n\n## Installing\n\nInstall with `npm install -g @inflowpayai/inflow`. Or run directly with `npx @inflowpayai/inflow`.\n\n## Running\n\nInFlow runs as a **standalone CLI** or an **MCP server**.\n\n**MCP**: add an `inflow` server to your MCP client config that runs `npx -y @inflowpayai/inflow --mcp`. Keep the `-y` flag \u2014 it suppresses npx\'s confirmation prompt, without which the MCP host can stall on first run.\n\n**MCP mode** exposes every CLI command as a tool. Call `tools/list` on the MCP server for the authoritative inventory; arguments mirror the CLI flags one-to-one.\n\n### Common commands / options\n\n**The CLI is the source of truth for exact flags, enums, and output shapes** \u2014 run `inflow <command> --schema` for one command, or `inflow --llms-full` for everything. This playbook covers *when and why*, not exhaustive parameter lists; when you need a precise flag name, value set, or response shape, query the CLI rather than guessing.\n\n- `inflow --llms` (or `--llms-full` for parameter detail) \u2014 discover all commands. `inflow <command> --schema` for a single command\'s JSON Schema.\n- `inflow --skill` \u2014 print this playbook (no frontmatter) to stdout. Use it to paste into the system-prompt field of an MCP host that doesn\'t natively load skills: `inflow --skill | pbcopy`.\n- Default output is `toon`. Override with `--format <fmt>`; for programmatic parsing prefer `json` (single document) or `jsonl` (line-delimited).\n- Multi-step flows return `_next.command` \u2014 run it to continue.\n- `--auth <path>` overrides the credentials file location.\n- `--api-key <key>` or `INFLOW_API_KEY=<key>` is an alternative to device-flow auth.\n\n## Authenticate\n\nAuthentication is shared by both protocols \u2014 do it once, before either payment flow. **Don\'t start a payment until the user is authenticated.**\n\nCheck the current state first \u2014 the user may already be logged in:\n\n```bash\ninflow auth status\n```\n\nA successful `auth status` returns `authenticated: true` plus `auth_method` (`device_token` or `api_key`), a truncated `access_token` preview (never the full token), `credentials_path`, `connection`, and possibly an `update` field. For the user\'s identity (email, handle, account id), call `inflow user get` \u2014 `auth status` deliberately omits it. Run the command to see the full shape.\n\nIf the response includes an `update` field, a newer version of `inflow` is published.\n\n**Surface and defer.** Tell the user a newer version is available and how to upgrade \u2014 `npm install -g @inflowpayai/inflow@latest` (or `npx @inflowpayai/inflow@latest`). Then **proceed with the current version**. Only block on the upgrade if a subsequent command fails with `VERSION_UNSUPPORTED` (or an HTTP 426 from the API), at which point the upgrade is mandatory and you should not retry until it lands.\n\nIf `authenticated` is `false`, start the device flow:\n\n```bash\ninflow auth login --client-name "<your-agent-name>"\n```\n\nReplace `<your-agent-name>` with the name of your agent or application (for example `"Personal Assistant"`, `"Shopping Bot"`). The device-authorization page in the user\'s browser displays this name when they approve the connection. Use a clear, unique, identifiable name.\n\nThe response includes a `verification_url` (present this to the user), a `phrase`, and a `_next.command`. Run that command immediately to poll until authenticated. **Do not wait for the user to respond before starting the poll.**\n\nIf your environment can\'t relay the verification phrase to the user while a separate polling command blocks I/O, use inline polling instead:\n\n```bash\ninflow auth login --client-name "<name>" --interval 5 --timeout 300\n```\n\n**API key alternative:** if the user provides an API key, set `INFLOW_API_KEY=<key>` in the environment (or pass `--api-key <key>` to any command) instead of running `auth login`. The API key takes precedence over a saved device token.\n\n## Which protocol? \u2014 start here\n\nBefore paying, decide which protocol the resource uses. **You do not choose it \u2014 the seller\'s 402 challenge header decides.** Detection is read-only and needs no auth.\n\n1. Get the 402 challenge header. If a prior HTTP call already returned a 402 (e.g. the browsing tool hit a paywall), use that response. Otherwise make a plain, **unauthenticated GET** to the URL and read the headers of the 402.\n2. Branch on the header \u2014 **check for MPP first:**\n\n| 402 carries\u2026 | Protocol | Then |\n| --- | --- | --- |\n| `WWW-Authenticate: Payment` | **MPP** | Go to [\xA7 Paying a 402 resource](#paying-a-402-resource); use the **MPP** column of the delta table |\n| `WWW-Authenticate: Payment` **and** `PAYMENT-REQUIRED` | **MPP** (MPP wins when both are present) | Go to [\xA7 Paying a 402 resource](#paying-a-402-resource); use the **MPP** column |\n| `PAYMENT-REQUIRED` only | **x402** | Go to [\xA7 Paying a 402 resource](#paying-a-402-resource); use the **x402** column |\n| neither header, or the response isn\'t a 402 | not InFlow-payable | Stop. Tell the user the resource isn\'t a supported 402 endpoint. |\n\nNote: the `inspect` and `decode` commands are protocol-specific (`inflow mpp \u2026` vs `inflow x402 \u2026`), which is why you detect the header *first*, then use that protocol\'s tools.\n\n---\n\n## Paying a 402 resource\n\nOne flow for both protocols. Prerequisite: you are authenticated (see [Authenticate](#authenticate)). First find your protocol\'s row in the **Protocol deltas** table below \u2014 it names the 402 header that selected it, the matching model, the filter flags, and the credential and replay header you\'ll use. Everything else in this section applies to both protocols.\n\n**Sequencing.** Run pre-flight before pay \u2014 `pay` fails or double-charges if the pre-flight checks didn\'t clear. `inspect` and `decode` are read-only and need no auth, so they may run before you authenticate if useful (e.g. sizing up a paywall first).\n\n### Protocol deltas\n\n| Aspect | MPP | x402 |\n| --- | --- | --- |\n| Selected when the 402 carries | `WWW-Authenticate: Payment` | `PAYMENT-REQUIRED` (and no `WWW-Authenticate: Payment`) |\n| Command prefix | `inflow mpp \u2026` | `inflow x402 \u2026` |\n| Matching model | The seller\'s challenge **pins the rail** \u2014 the buyer does not choose scheme/network/asset | Pay where `inspect.accepts \u2229 supported.kinds` is non-empty |\n| Filter flags | `--payment-method`, `--intent`, `--currency`, `--rail`, `--instrument-id` | `--scheme`, `--network`, `--asset`, `--asset-name` |\n| Credential field (after approval) | `credential` (from `mpp status` when `state` is `ready`) | `encoded_payload` (from `x402 status` after approval) |\n| Replay header | `Authorization: Payment <credential>` | `PAYMENT-SIGNATURE: <encoded_payload>` |\n| Write-credential-to-disk flag | `--credential-file <path>` | `--payload-file <path>` |\n| Idempotency | \u2014 | `--payment-id` (see Step 2) |\n| Cancel uses | `approval_id` | `approval_id` |\n| Protocol-specific error codes | `PAYMENT_FAILED`, `PAYMENT_EXPIRED`, `PAYMENT_NOT_ACCEPTED` | `APPROVAL_TIMEOUT`, `APPROVAL_FAILED`, `APPROVAL_CANCELLED` |\n\nThroughout this section `<mpp|x402>` means "use your protocol\'s prefix." For the exact parameters and output shape of any command below, run `inflow <command> --schema`.\n\n### Step 1: Pre-flight evaluation\n\n```bash\n# 1. Parse what the seller will accept \u2014 read-only, no auth\ninflow <mpp|x402> inspect <url>\n\n# (Already have the 402 header from a prior response? Decode it directly instead of re-probing:)\ninflow <mpp|x402> decode \'<402 header value>\'\n\n# 2. List what the buyer\'s account can pay with\ninflow <mpp|x402> supported\n\n# 3. Check balances for the candidate currency/asset(s)\ninflow balances list\n```\n\n`inspect` / `decode` return what the seller accepts \u2014 the price is the `amount` field (for x402 the human-readable symbol is `extra.assetName`); `decode` also accepts a base64url credential / receipt. `supported` returns what the account can pay with; `balances list` returns `available` per currency. Run the commands to see the exact shapes.\n\nDecide whether you can pay (apply your protocol\'s matching model from the delta table):\n\n| Condition | Meaning | Action |\n| --- | --- | --- |\n| No payable match between the seller and the buyer\'s `supported` methods | No payable rail | Stop \u2192 `NO_INFLOW_MATCH`. Tell the user the seller\'s rails aren\'t supported by their account. |\n| A match exists, but `balances.available < amount` for every match | Right rail, not enough funds | Stop \u2192 run `inflow deposit-addresses list`, surface the address(es) in full, ask the user to fund a matching network. |\n| A match exists **and** \u22651 match has `balances.available \u2265 amount` | Payable | Proceed to Step 2. |\n\n**Optional filters** narrow *which* offer to fulfil \u2014 optional, AND-combined, applied on both `pay` and `inspect`, and an empty result fails with `NO_FILTERED_MATCH` (it does not fall through to a default order). One non-obvious case: MPP\'s `--instrument-id` picks *how* to fund (an instrument-rail / fiat challenge), not which challenge. For the exact filter flags and accepted values per protocol, run `inflow <mpp|x402> pay --schema`.\n\n**Decimal precision.** `balances.available` and the challenge/`amount` value are decimal strings preserving BigDecimal precision. **Never parse them to a JS `Number`** \u2014 that drops precision. Compare as strings, or use a `BigInt` / `decimal.js`-style library.\n\n### Step 2: Pay\n\nBefore initiating the call, summarize the intent to the user in chat: amount, currency, resource URL, and the method/rail (MPP) or scheme/network (x402). The user verifies the canonical details on the approval screen; the chat summary is what they read first. Example:\n\n> "I\'m about to pay 0.10 USDC to api.foo.dev for /dataset.csv. Requesting approval next."\n\n**Fast path (recommended).** When the agent can block until the payment finishes, set `--interval N` and let the CLI run the whole flow in one call \u2014 probe, decode, prepare, await approval, replay against the seller, return the body:\n\n```bash\ninflow <mpp|x402> pay <url> --interval 5 --max-attempts 180\n```\n\nThe result includes `outcome`, `transaction_id`, `response_status`, `settled`, the seller body inline (or `output_saved_to` if `--output-file` is set), and the now-consumed credential (`credential` for MPP, `encoded_payload` for x402). On the fast path the CLI has already replayed that credential to fetch the body \u2014 it appears in the result for reference only; **do not replay it yourself.** To surface `approval_url` *before* the call returns, add `--format jsonl` \u2014 frames stream line-by-line. With the default `json` (or `toon`), the agent only sees the final buffered result.\n\n**`outcome` values.** A completed `pay` returns one of three terminal outcomes \u2014 branch on it, don\'t assume `paid`:\n\n| `outcome` | Meaning | What to do |\n| --- | --- | --- |\n| `paid` | Settled and the seller returned 2xx | Deliver the body to the user |\n| `no-payment-required` | The resource wasn\'t paywalled, or was already paid | Tell the user nothing was charged; return the body |\n| `replay-rejected` | Payment was approved (funds in transit) but the seller replied non-2xx on the replay | Do NOT report success. Tell the user the seller\'s response failed; because the payment didn\'t complete, the in-transit funds are reverted to their InFlow balance. Offer to retry |\n\n**Two-step path.** Use this when the agent\'s host can\'t block I/O long enough for the user to approve (chat UIs that yield between turns). Drop `--interval`; the first call returns `transaction_id` + `approval_id` + `approval_url` + a `_next` `status` command, and the agent drives the replay itself once a credential arrives.\n\n```bash\ninflow <mpp|x402> pay <url>\n# -> { "transaction_id": "txn_abc", "approval_id": "appr_xyz", "approval_url": "https://app.inflowpay.ai/approvals/appr_xyz", "_next": { "command": "<mpp|x402> status txn_abc --interval 5 --max-attempts 180" } }\n```\n\nMind the two distinct ids: poll, replay, and resume all use `transaction_id`; **cancel uses `approval_id`** (`inflow <mpp|x402> cancel <approval_id>`). Both are returned by `pay`.\n\nFor non-GET requests, pass `--method`, `--data`, `--header` (repeatable):\n\n```bash\ninflow <mpp|x402> pay https://seller.example.com/api/widgets --method POST --data \'{"sku":"widget-1"}\' --header "X-Custom: value" --interval 5 --max-attempts 180\n```\n\n**Idempotency (x402 only).** Set `--payment-id <id>` whenever a retry on transport failure is possible \u2014 the server treats two requests with the same id as the same logical payment, so a retry after a network blip won\'t double-charge. Use a stable random opaque value generated once per intent; reuse the same id on transport retry; regenerate only when the user explicitly wants a fresh charge. Don\'t tie the id to wall-clock time \u2014 a date-based id silently double-charges on next-day "buy this again" requests. Without `--payment-id`, the server generates one each call \u2014 fine for one-shots, unsafe for retries. (Format constraints: `inflow x402 pay --schema`.)\n\n```bash\ninflow x402 pay <url> --payment-id "<stable-opaque-id>"\n```\n\n**Sensitive / binary output.** The one-time bearer credential (`credential` for MPP, `encoded_payload` for x402; returned after approval and echoed in the fast-path result) must not be echoed back in chat. Write it to disk at mode `0o600` with your protocol\'s flag (`--credential-file <path>` for MPP, `--payload-file <path>` for x402); replay then reads from that file. For the seller\'s response body, `--output-file <path>` writes bytes to disk and replaces `body` / `body_base64` with `output_saved_to: <path>` \u2014 pair with `--no-show-body` for binary content (PDFs, images, audio, datasets) so bytes never appear inline as base64:\n\n```bash\ninflow <mpp|x402> pay https://api.foo.dev/dataset.csv --interval 5 --max-attempts 180 --output-file /tmp/dataset.csv --no-show-body\n```\n\n**Polling discipline.** Persist `transaction_id` as soon as `pay` returns it. Then:\n\n- Run `_next.command` (or `<mpp|x402> status <transaction_id> --interval N`) immediately. Don\'t wait for the user to confirm before polling starts.\n- If polling is interrupted \u2014 network drop, session bounce, user kills the agent \u2014 resume with `inflow <mpp|x402> status <transaction_id> --interval 5 --max-attempts 180`. Only create a new transaction if the original expired (`PAYMENT_EXPIRED` for MPP, `APPROVAL_TIMEOUT` for x402), was denied/cancelled, or its credential is already consumed.\n- If `POLLING_TIMEOUT` fires before approval, ask the user whether to keep waiting or cancel \u2014 don\'t silently restart the poll.\n- If >12 minutes elapsed without a user response (\u22483 min before the 15-minute approval window closes), surface that explicitly so they can act before the window closes.\n- If the user aborts ("nevermind", "cancel that"), call `inflow <mpp|x402> cancel <approval_id>` before exiting. Otherwise the approval sits pending for 15 minutes and triggers phantom notifications in the user\'s InFlow app.\n\nOnce `status` reports the credential (MPP: `state: ready` with `credential`; x402: `encoded_payload`), replay the original seller request with your protocol\'s replay header from the delta table \u2014 `Authorization: Payment <credential>` (MPP) or `PAYMENT-SIGNATURE: <encoded_payload>` (x402); use `$(cat <file>)` if you wrote it to disk with `--credential-file` / `--payload-file`. The seller\'s protected response comes back on the replay.\n\n### Limits\n\n| Limit | Value |\n| --- | --- |\n| Approval window | 15 minutes from `pay` creating the transaction (`--timeout` overrides the polling deadline) |\n| Polling stop condition | Polling ends at whichever fires first: `--max-attempts` (count, default `0` = unlimited) or `--timeout` (seconds, default `900` = the full 15-min window). The examples use `--interval 5 --max-attempts 180` (= 900 s) so a copied command covers the whole window \u2014 `--interval 5 --max-attempts 60` (= 300 s) would stop polling at 5 min, well before approval can land |\n| Credential reuse | One-time. The credential (`credential` for MPP, `encoded_payload` for x402) is consumed by the first seller replay \u2014 not reusable; a failed seller call requires a new `pay` |\n\n### Worked example (MPP)\n\nA user asks the agent to fetch a paywalled dataset at `https://api.foo.dev/dataset.csv` that answered 402 with `WWW-Authenticate: Payment`.\n\nPre-flight: `inflow mpp inspect <url>` (the seller\'s challenges), `inflow mpp supported` (methods the buyer can pay with), `inflow balances list`. The seller offers the `inflow` method in USDC; the user\'s 100.5 USDC balance covers the 0.10 USDC price. Summarize intent, then pay:\n\n```bash\ninflow mpp pay https://api.foo.dev/dataset.csv --interval 5 --max-attempts 180 --output-file /tmp/dataset.csv --no-show-body\n# Persist transaction_id from the response in case polling is interrupted.\n# Returns outcome "paid" with output_saved_to /tmp/dataset.csv.\n```\n\n> "Approval requested \u2014 confirm in the InFlow app: https://app.inflowpay.ai/approvals/appr_xyz\n> I\'ll keep polling. 15-min window."\n\nOnce the result arrives:\n\n> "Paid 0.10 USDC. Transaction txn_abc. Saved the dataset to /tmp/dataset.csv."\n\n**Two-step variant** (host can\'t block): follow Step 2\'s two-step path; once `mpp status` reports `state: ready`, replay with `Authorization: Payment <credential>` (or `$(cat <path>)` via `--credential-file` to keep it out of chat).\n\n### Worked example (x402)\n\nA user asks the agent to fetch a paywalled article at `https://api.foo.dev/article-3` that answered 402 with `PAYMENT-REQUIRED`.\n\nPre-flight: the intersection lands on `exact` \xD7 `solana:mainnet`, and the user\'s 100.5 USDC balance easily covers the 0.10 USDC the seller requires. Proceed.\n\n> "I\'m about to pay 0.10 USDC on Solana mainnet to api.foo.dev for /article-3.\n> Your balance is 100.5 USDC \u2014 plenty. Requesting approval next."\n\n```bash\ninflow x402 pay https://api.foo.dev/article-3 --payment-id "<stable-opaque-id>" --interval 5 --max-attempts 180\n# Persist transaction_id from the response in case polling gets interrupted.\n# Returns outcome "paid"; body contains the article JSON.\n```\n\n> "Approval requested \u2014 confirm in the InFlow app: https://app.inflowpay.ai/approvals/appr_xyz\n> I\'ll keep polling. 15-min window."\n\nOnce the result arrives:\n\n> "Paid 0.10 USDC. Transaction txn_abc. Server returned: \'How to brew coffee \u2014 ...\'"\n\n**Two-step variant** (host can\'t block): follow Step 2\'s two-step path; once `x402 status` returns the `encoded_payload`, replay with `PAYMENT-SIGNATURE: <encoded_payload>` (use `--payload-file` to keep it out of chat).\n\n### MPP errors\n\nAll errors in agent mode are JSON with `code` and `message` fields and exit code 1. MPP-specific codes (shared codes are in [\xA7 Shared errors](#shared-errors)). "What to tell the user" is the prompt to surface \u2014 don\'t dump the raw error:\n\n| Error code | Recovery | What to tell the user |\n| --- | --- | --- |\n| `PAYMENT_FAILED` | `inflow mpp status <transaction_id>` for the precise state, then create a new transaction with `inflow mpp pay`. (Terminal `failed` state, or no credential produced.) | "The payment didn\'t go through \u2014 it was declined, underfunded, or the transaction failed. Want me to try again, switch funding, or stop?" |\n| `PAYMENT_EXPIRED` | Start a new `inflow mpp pay`. | "The payment window expired before it was ready to settle. Want me to start a new one, or stop here?" |\n| `PAYMENT_NOT_ACCEPTED` | `inflow mpp inspect <url>` to re-check the challenge; adjust and retry. | \u2014 |\n\n### x402 errors\n\nAll errors in agent mode are JSON with `code` and `message` fields and exit code 1. x402-specific codes (shared codes are in [\xA7 Shared errors](#shared-errors)). "What to tell the user" is the prompt to surface \u2014 don\'t dump the raw error:\n\n| Error code | Recovery | What to tell the user |\n| --- | --- | --- |\n| `APPROVAL_TIMEOUT` | `inflow x402 status <transaction_id>` for the precise reason, then create a new transaction. | "You didn\'t approve within 15 minutes, so the request expired. Want me to start a new payment, or stop here?" |\n| `APPROVAL_FAILED` | Same recovery as `APPROVAL_TIMEOUT` (declined / insufficient funds in the matched asset / generic). | "Approval didn\'t go through (declined or insufficient funds in the matched asset). Want me to try a different funding source, top up, or stop?" |\n| `APPROVAL_CANCELLED` | Same recovery (cancelled via `x402 cancel` or server-side). | "You cancelled the approval. Stopping here unless you want to start a new payment." |\n| `INVALID_PAYMENT_ID` | `--payment-id` violated the format (see `inflow x402 pay --schema`). Adjust or omit the payment id. | \u2014 |\n\n---\n\n## Security & data handling\n\nApplies to both protocols.\n\n- Treat OAuth tokens and API keys as secrets \u2014 never echo them. The one-time bearer credential (`encoded_payload` for x402, `credential` for MPP) returned after approval should be replayed directly against the seller and discarded, not pasted back to the user.\n- Respect `/agents.txt` and `/llm.txt` on sites you browse.\n- Avoid suspicious 402 endpoints \u2014 if the domain doesn\'t match what the user asked to pay, or the price is different from expectation, stop and ask.\n- When displaying deposit addresses to the user, print the full address (don\'t truncate). Truncating breaks copy-paste.\n\n## Shared errors\n\nThese apply to both protocols (in addition to each section\'s protocol-specific codes). All are JSON with `code` and `message` and exit code 1. Where a command is protocol-specific, use your prefix (`<mpp|x402>`). "What to tell the user" is the prompt to surface \u2014 don\'t dump the raw error:\n\n| Error code | Recovery | What to tell the user |\n| --- | --- | --- |\n| `NOT_AUTHENTICATED` | No saved device token and no `--api-key` / `INFLOW_API_KEY` configured. Run `inflow auth login` or set the API key env var. | \u2014 |\n| `NO_INFLOW_MATCH` | Seller\'s rails aren\'t supported by the account. Fund a matching method/chain, or use a different seller. | "The seller wants `<method/rail or scheme\xD7network>`, but your account can\'t pay on that rail. Either fund a matching method, or pick a different seller." |\n| `NO_FILTERED_MATCH` | A filter emptied the candidate list. Loosen it, or re-check with `inflow <mpp|x402> inspect <url>` (filter flags per the delta table). | "Your filter removed every option the seller accepts. Loosen it or check the seller\'s options with `inflow <mpp|x402> inspect`." |\n| `INVALID_402` / `DECODE_FAILED` | Seller returned 402 but the protocol\'s header was missing (`INVALID_402`) or unparseable (`DECODE_FAILED`). Verify the URL is payable; pass the raw header to `inflow <mpp|x402> decode` for the detailed parse error. | \u2014 |\n| `POLLING_TIMEOUT` | `--interval` polling reached its max-attempts or timeout. Retryable \u2014 resume with `inflow <mpp|x402> status <transaction_id> --interval 5 --max-attempts 180`. | "Still waiting on your approval \u2014 want me to keep polling, or cancel the request? (`inflow <mpp|x402> cancel <approval_id>` cancels it.)" |\n| `api_error` | Non-2xx from the InFlow API on the plain data calls (`user`, `balances`, `deposit-addresses`); discriminate on `httpStatus`. `401` \u2014 saved auth rejected, re-run `inflow auth login`. `426` (`VERSION_UNSUPPORTED`) \u2014 upgrade and retry. `5xx` \u2014 server-side; wait and retry. (Note: `pay`/`status` rejections instead surface the server\'s own code, e.g. `INSUFFICIENT_FUNDS`, or the protocol\'s terminal code \u2014 not `api_error`.) | \u2014 |\n| `VERSION_UNSUPPORTED` / HTTP 426 | Installed `inflow` CLI is below the minimum supported version. `npm install -g @inflowpayai/inflow@latest`, then retry; don\'t retry on the old version. | \u2014 |\n| `transport_error` | Network failure \u2014 check connectivity; retry. | \u2014 |\n\n## Out of scope\n\nThis skill covers programmatic HTTP 402 payments (MPP and x402) only. It does NOT handle:\n\n- **Traditional merchant checkouts** No PANs (credit card forms, hosted checkouts).\n- **Card issuance** or wallet management beyond `balances list` and `deposit-addresses list`.\n- **Refunds, disputes, chargebacks** \u2014 handled out of band via support.\n- **Peer-to-peer transfers** between users or wallets.\n- **FX / currency conversion.** Buyer logic matches the seller\'s accepted rails against the account\'s supported assets.\n- **Subscriptions / recurring payments.** Each `pay` is one-shot.\n\nFor any of the above, point the user to https://app.inflowpay.ai or support.\n\n## Further docs\n\n- MPP protocol: https://mpp.dev\n- x402 protocol: https://x402.org\n- InFlow: https://app.inflowpay.ai\n';
17108
+ var skillBody = '# Agentic Payments\n\nPay HTTP 402-protected resources on the user\'s behalf. InFlow speaks two payment protocols \u2014 **MPP** and **x402** \u2014 but the flow is the same for both: shared setup (install, run, authenticate), then a **router** that picks the protocol from the seller\'s 402 header, then one **Paying a 402 resource** section that covers both. A per-protocol **delta table** at the top of that section lists the handful of real differences (header name, credential name, filters, error codes); read your row, then follow the shared steps.\n\n## Installing\n\nInstall with `npm install -g @inflowpayai/inflow`. Or run directly with `npx @inflowpayai/inflow`.\n\n## Running\n\nInFlow runs as a **standalone CLI** or an **MCP server**.\n\n**MCP**: add an `inflow` server to your MCP client config that runs `npx -y @inflowpayai/inflow --mcp`. Keep the `-y` flag \u2014 it suppresses npx\'s confirmation prompt, without which the MCP host can stall on first run.\n\n**MCP mode** exposes every CLI command as a tool. Call `tools/list` on the MCP server for the authoritative inventory; arguments mirror the CLI flags one-to-one.\n\n### Common commands / options\n\n**The CLI is the source of truth for exact flags, enums, and output shapes** \u2014 run `inflow <command> --schema` for one command, or `inflow --llms-full` for everything. This playbook covers *when and why*, not exhaustive parameter lists; when you need a precise flag name, value set, or response shape, query the CLI rather than guessing.\n\n- `inflow --llms` (or `--llms-full` for parameter detail) \u2014 discover all commands. `inflow <command> --schema` for a single command\'s JSON Schema.\n- `inflow --skill` \u2014 print this playbook (no frontmatter) to stdout. Use it to paste into the system-prompt field of an MCP host that doesn\'t natively load skills: `inflow --skill | pbcopy`.\n- Default output is `toon`. Override with `--format <fmt>`; for programmatic parsing prefer `json` (single document) or `jsonl` (line-delimited).\n- Multi-step flows return `_next.command` \u2014 run it to continue.\n- `--auth <path>` overrides the credentials file location.\n- `--api-key <key>` or `INFLOW_API_KEY=<key>` is an alternative to device-flow auth.\n\n## Authenticate\n\nAuthentication is shared by both protocols \u2014 do it once, before either payment flow. **Don\'t start a payment until the user is authenticated.**\n\nCheck the current state first \u2014 the user may already be logged in:\n\n```bash\ninflow auth status\n```\n\nA successful `auth status` returns `authenticated: true` plus `auth_method` (`device_token` or `api_key`), a truncated `access_token` preview (never the full token), `credentials_path`, `connection`, and possibly an `update` field. For the user\'s identity (email, handle, account id), call `inflow user get` \u2014 `auth status` deliberately omits it. Run the command to see the full shape.\n\nIf the response includes an `update` field, a newer version of `inflow` is published.\n\n**Surface and defer.** Tell the user a newer version is available and how to upgrade \u2014 `npm install -g @inflowpayai/inflow@latest` (or `npx @inflowpayai/inflow@latest`). Then **proceed with the current version**. Only block on the upgrade if a subsequent command fails with `VERSION_UNSUPPORTED` (or an HTTP 426 from the API), at which point the upgrade is mandatory and you should not retry until it lands.\n\nIf `authenticated` is `false`, start the device flow:\n\n```bash\ninflow auth login --client-name "<your-agent-name>"\n```\n\nReplace `<your-agent-name>` with the name of your agent or application (for example `"Personal Assistant"`, `"Shopping Bot"`). The device-authorization page in the user\'s browser displays this name when they approve the connection. Use a clear, unique, identifiable name.\n\nThe response includes a `verification_url` (present this to the user), a `phrase`, and a `_next.command`. Run that command immediately to poll until authenticated. **Do not wait for the user to respond before starting the poll.**\n\nIf your environment can\'t relay the verification phrase to the user while a separate polling command blocks I/O, use inline polling instead:\n\n```bash\ninflow auth login --client-name "<name>" --interval 5 --timeout 300\n```\n\n**API key alternative:** if the user provides an API key, set `INFLOW_API_KEY=<key>` in the environment (or pass `--api-key <key>` to any command) instead of running `auth login`. The API key takes precedence over a saved device token.\n\n## Which protocol? \u2014 start here\n\nBefore paying, decide which protocol the resource uses. **You do not choose it \u2014 the seller\'s 402 challenge decides.** Run one read-only, no-auth command and let it detect both:\n\n```bash\ninflow inspect <url>\n```\n\n`inflow inspect` probes the URL **once** and decodes both MPP and x402 challenges from the same 402. Read its `detected` array to pick the pay rail:\n\n| `detected` | Pay with |\n| --- | --- |\n| `["mpp"]` | `inflow mpp pay <url>` |\n| `["x402"]` | `inflow x402 pay <url>` |\n| `["mpp", "x402"]` | `inflow mpp pay <url>` \u2014 **MPP wins when both are present** |\n| `[]` (seller still returned 402) | Not InFlow-payable on this account. Stop and tell the user; check `warnings` for why. |\n\nIf `inspect` returns `outcome: "no-payment-required"`, the URL isn\'t paywalled \u2014 there\'s nothing to pay.\n\n---\n\n## Paying a 402 resource\n\nOne flow for both protocols. Prerequisite: you are authenticated (see [Authenticate](#authenticate)). First find your protocol\'s row in the **Protocol deltas** table below \u2014 it names the 402 header that selected it, the matching model, the filter flags, and the credential and replay header you\'ll use. Everything else in this section applies to both protocols.\n\n**Sequencing.** Run pre-flight before pay \u2014 `pay` fails or double-charges if the pre-flight checks didn\'t clear. `inspect` and `decode` are read-only and need no auth, so they may run before you authenticate if useful (e.g. sizing up a paywall first).\n\n### Protocol deltas\n\n| Aspect | MPP | x402 |\n| --- | --- | --- |\n| Selected when the 402 carries | `WWW-Authenticate: Payment` | `PAYMENT-REQUIRED` (and no `WWW-Authenticate: Payment`) |\n| Command prefix | `inflow mpp \u2026` | `inflow x402 \u2026` |\n| Matching model | The seller\'s challenge **pins the rail** \u2014 the buyer does not choose scheme/network/asset | Pay where the x402 `accepts` \u2229 `supported.kinds` is non-empty |\n| Filter flags | `--payment-method`, `--intent`, `--currency`, `--rail`, `--instrument-id` | `--scheme`, `--network`, `--asset`, `--asset-name` |\n| Credential field (after approval) | `credential` (from `mpp status` when `state` is `ready`) | `encoded_payload` (from `x402 status` after approval) |\n| Replay header | `Authorization: Payment <credential>` | `PAYMENT-SIGNATURE: <encoded_payload>` |\n| Write-credential-to-disk flag | `--credential-file <path>` | `--payload-file <path>` |\n| Idempotency | \u2014 | `--payment-id` (see Step 2) |\n| Cancel uses | `approval_id` | `approval_id` |\n| Protocol-specific error codes | `PAYMENT_FAILED`, `PAYMENT_EXPIRED`, `PAYMENT_NOT_ACCEPTED` | `APPROVAL_TIMEOUT`, `APPROVAL_FAILED`, `APPROVAL_CANCELLED` |\n\nThroughout this section `<mpp|x402>` means "use your protocol\'s prefix." For the exact parameters and output shape of any command below, run `inflow <command> --schema`.\n\n### Step 1: Pre-flight evaluation\n\n```bash\n# 1. Parse what the seller will accept \u2014 read-only, no auth (both protocols in one probe)\ninflow inspect <url>\n\n# (Already have the raw 402 header from a prior response? Decode it directly instead of re-probing:)\ninflow <mpp|x402> decode \'<402 header value>\'\n\n# 2. List what the buyer\'s account can pay with (use the protocol from `detected`)\ninflow <mpp|x402> supported\n\n# 3. Check balances for the candidate currency/asset(s)\ninflow balances list\n```\n\n`inflow inspect` returns what the seller accepts under its `mpp` and `x402` keys \u2014 the price is each challenge\'s `amount` field (raw atomic units for x402; the asset is the on-chain contract address, not a symbol). `decode` parses a single raw header you already hold (and also accepts a base64url credential / receipt). `supported` returns what the account can pay with; `balances list` returns `available` per currency. Run the commands to see the exact shapes.\n\nDecide whether you can pay (apply your protocol\'s matching model from the delta table):\n\n| Condition | Meaning | Action |\n| --- | --- | --- |\n| No payable match between the seller and the buyer\'s `supported` methods | No payable rail | Stop \u2192 `NO_INFLOW_MATCH`. Tell the user the seller\'s rails aren\'t supported by their account. |\n| A match exists, but `balances.available < amount` for every match | Right rail, not enough funds | Stop \u2192 run `inflow deposit-addresses list`, surface the address(es) in full, ask the user to fund a matching network. |\n| A match exists **and** \u22651 match has `balances.available \u2265 amount` | Payable | Proceed to Step 2. |\n\n**Optional filters** narrow *which* offer to fulfil \u2014 optional, AND-combined, applied on `pay`, and an empty result fails with `NO_FILTERED_MATCH` (it does not fall through to a default order). One non-obvious case: MPP\'s `--instrument-id` picks *how* to fund (an instrument-rail / fiat challenge), not which challenge. For the exact filter flags and accepted values per protocol, run `inflow <mpp|x402> pay --schema`.\n\n**Decimal precision.** `balances.available` and the challenge/`amount` value are decimal strings preserving BigDecimal precision. **Never parse them to a JS `Number`** \u2014 that drops precision. Compare as strings, or use a `BigInt` / `decimal.js`-style library.\n\n### Step 2: Pay\n\nBefore initiating the call, summarize the intent to the user in chat: amount, currency, resource URL, and the method/rail (MPP) or scheme/network (x402). The user verifies the canonical details on the approval screen; the chat summary is what they read first. Example:\n\n> "I\'m about to pay 0.10 USDC to api.foo.dev for /dataset.csv. Requesting approval next."\n\n**Fast path (recommended).** When the agent can block until the payment finishes, set `--interval N` and let the CLI run the whole flow in one call \u2014 probe, decode, prepare, await approval, replay against the seller, return the body:\n\n```bash\ninflow <mpp|x402> pay <url> --interval 5 --max-attempts 180\n```\n\nThe result includes `outcome`, `transaction_id`, `response_status`, `settled`, the seller body inline (or `output_saved_to` if `--output-file` is set), and the now-consumed credential (`credential` for MPP, `encoded_payload` for x402). On the fast path the CLI has already replayed that credential to fetch the body \u2014 it appears in the result for reference only; **do not replay it yourself.** To surface `approval_url` *before* the call returns, add `--format jsonl` \u2014 frames stream line-by-line. With the default `json` (or `toon`), the agent only sees the final buffered result.\n\n**`outcome` values.** A completed `pay` returns one of three terminal outcomes \u2014 branch on it, don\'t assume `paid`:\n\n| `outcome` | Meaning | What to do |\n| --- | --- | --- |\n| `paid` | Settled and the seller returned 2xx | Deliver the body to the user |\n| `no-payment-required` | The resource wasn\'t paywalled, or was already paid | Tell the user nothing was charged; return the body |\n| `replay-rejected` | Payment was approved (funds in transit) but the seller replied non-2xx on the replay | Do NOT report success. Tell the user the seller\'s response failed; because the payment didn\'t complete, the in-transit funds are reverted to their InFlow balance. Offer to retry |\n\n**Two-step path.** Use this when the agent\'s host can\'t block I/O long enough for the user to approve (chat UIs that yield between turns). Drop `--interval`; the first call returns `transaction_id` + `approval_id` + `approval_url` + a `_next` `status` command, and the agent drives the replay itself once a credential arrives.\n\n```bash\ninflow <mpp|x402> pay <url>\n# -> { "transaction_id": "txn_abc", "approval_id": "appr_xyz", "approval_url": "https://app.inflowpay.ai/approvals/appr_xyz", "_next": { "command": "<mpp|x402> status txn_abc --interval 5 --max-attempts 180" } }\n```\n\nMind the two distinct ids: poll, replay, and resume all use `transaction_id`; **cancel uses `approval_id`** (`inflow <mpp|x402> cancel <approval_id>`). Both are returned by `pay`.\n\nFor non-GET requests, pass `--method`, `--data`, `--header` (repeatable):\n\n```bash\ninflow <mpp|x402> pay https://seller.example.com/api/widgets --method POST --data \'{"sku":"widget-1"}\' --header "X-Custom: value" --interval 5 --max-attempts 180\n```\n\n**Idempotency (x402 only).** Set `--payment-id <id>` whenever a retry on transport failure is possible \u2014 the server treats two requests with the same id as the same logical payment, so a retry after a network blip won\'t double-charge. Use a stable random opaque value generated once per intent; reuse the same id on transport retry; regenerate only when the user explicitly wants a fresh charge. Don\'t tie the id to wall-clock time \u2014 a date-based id silently double-charges on next-day "buy this again" requests. Without `--payment-id`, the server generates one each call \u2014 fine for one-shots, unsafe for retries. (Format constraints: `inflow x402 pay --schema`.)\n\n```bash\ninflow x402 pay <url> --payment-id "<stable-opaque-id>"\n```\n\n**Sensitive / binary output.** The one-time bearer credential (`credential` for MPP, `encoded_payload` for x402; returned after approval and echoed in the fast-path result) must not be echoed back in chat. Write it to disk at mode `0o600` with your protocol\'s flag (`--credential-file <path>` for MPP, `--payload-file <path>` for x402); replay then reads from that file. For the seller\'s response body, `--output-file <path>` writes bytes to disk and replaces `body` / `body_base64` with `output_saved_to: <path>` \u2014 pair with `--no-show-body` for binary content (PDFs, images, audio, datasets) so bytes never appear inline as base64:\n\n```bash\ninflow <mpp|x402> pay https://api.foo.dev/dataset.csv --interval 5 --max-attempts 180 --output-file /tmp/dataset.csv --no-show-body\n```\n\n**Polling discipline.** Persist `transaction_id` as soon as `pay` returns it. Then:\n\n- Run `_next.command` (or `<mpp|x402> status <transaction_id> --interval N`) immediately. Don\'t wait for the user to confirm before polling starts.\n- If polling is interrupted \u2014 network drop, session bounce, user kills the agent \u2014 resume with `inflow <mpp|x402> status <transaction_id> --interval 5 --max-attempts 180`. Only create a new transaction if the original expired (`PAYMENT_EXPIRED` for MPP, `APPROVAL_TIMEOUT` for x402), was denied/cancelled, or its credential is already consumed.\n- If `POLLING_TIMEOUT` fires before approval, ask the user whether to keep waiting or cancel \u2014 don\'t silently restart the poll.\n- If >12 minutes elapsed without a user response (\u22483 min before the 15-minute approval window closes), surface that explicitly so they can act before the window closes.\n- If the user aborts ("nevermind", "cancel that"), call `inflow <mpp|x402> cancel <approval_id>` before exiting. Otherwise the approval sits pending for 15 minutes and triggers phantom notifications in the user\'s InFlow app.\n\nOnce `status` reports the credential (MPP: `state: ready` with `credential`; x402: `encoded_payload`), replay the original seller request with your protocol\'s replay header from the delta table \u2014 `Authorization: Payment <credential>` (MPP) or `PAYMENT-SIGNATURE: <encoded_payload>` (x402); use `$(cat <file>)` if you wrote it to disk with `--credential-file` / `--payload-file`. The seller\'s protected response comes back on the replay.\n\n### Limits\n\n| Limit | Value |\n| --- | --- |\n| Approval window | 15 minutes from `pay` creating the transaction (`--timeout` overrides the polling deadline) |\n| Polling stop condition | Polling ends at whichever fires first: `--max-attempts` (count, default `0` = unlimited) or `--timeout` (seconds, default `900` = the full 15-min window). The examples use `--interval 5 --max-attempts 180` (= 900 s) so a copied command covers the whole window \u2014 `--interval 5 --max-attempts 60` (= 300 s) would stop polling at 5 min, well before approval can land |\n| Credential reuse | One-time. The credential (`credential` for MPP, `encoded_payload` for x402) is consumed by the first seller replay \u2014 not reusable; a failed seller call requires a new `pay` |\n\n### Worked example (MPP)\n\nA user asks the agent to fetch a paywalled dataset at `https://api.foo.dev/dataset.csv`.\n\nPre-flight: `inflow inspect <url>` reports `detected: ["mpp"]` with the seller\'s challenges; then `inflow mpp supported` (methods the buyer can pay with) and `inflow balances list`. The seller offers the `inflow` method in USDC; the user\'s 100.5 USDC balance covers the 0.10 USDC price. Summarize intent, then pay:\n\n```bash\ninflow mpp pay https://api.foo.dev/dataset.csv --interval 5 --max-attempts 180 --output-file /tmp/dataset.csv --no-show-body\n# Persist transaction_id from the response in case polling is interrupted.\n# Returns outcome "paid" with output_saved_to /tmp/dataset.csv.\n```\n\n> "Approval requested \u2014 confirm in the InFlow app: https://app.inflowpay.ai/approvals/appr_xyz\n> I\'ll keep polling. 15-min window."\n\nOnce the result arrives:\n\n> "Paid 0.10 USDC. Transaction txn_abc. Saved the dataset to /tmp/dataset.csv."\n\n**Two-step variant** (host can\'t block): follow Step 2\'s two-step path; once `mpp status` reports `state: ready`, replay with `Authorization: Payment <credential>` (or `$(cat <path>)` via `--credential-file` to keep it out of chat).\n\n### Worked example (x402)\n\nA user asks the agent to fetch a paywalled article at `https://api.foo.dev/article-3`.\n\nPre-flight: `inflow inspect <url>` reports `detected: ["x402"]`; the intersection lands on `exact` \xD7 `solana:mainnet`, and the user\'s 100.5 USDC balance easily covers the 0.10 USDC the seller requires. Proceed.\n\n> "I\'m about to pay 0.10 USDC on Solana mainnet to api.foo.dev for /article-3.\n> Your balance is 100.5 USDC \u2014 plenty. Requesting approval next."\n\n```bash\ninflow x402 pay https://api.foo.dev/article-3 --payment-id "<stable-opaque-id>" --interval 5 --max-attempts 180\n# Persist transaction_id from the response in case polling gets interrupted.\n# Returns outcome "paid"; body contains the article JSON.\n```\n\n> "Approval requested \u2014 confirm in the InFlow app: https://app.inflowpay.ai/approvals/appr_xyz\n> I\'ll keep polling. 15-min window."\n\nOnce the result arrives:\n\n> "Paid 0.10 USDC. Transaction txn_abc. Server returned: \'How to brew coffee \u2014 ...\'"\n\n**Two-step variant** (host can\'t block): follow Step 2\'s two-step path; once `x402 status` returns the `encoded_payload`, replay with `PAYMENT-SIGNATURE: <encoded_payload>` (use `--payload-file` to keep it out of chat).\n\n### MPP errors\n\nAll errors in agent mode are JSON with `code` and `message` fields and exit code 1. MPP-specific codes (shared codes are in [\xA7 Shared errors](#shared-errors)). "What to tell the user" is the prompt to surface \u2014 don\'t dump the raw error:\n\n| Error code | Recovery | What to tell the user |\n| --- | --- | --- |\n| `PAYMENT_FAILED` | `inflow mpp status <transaction_id>` for the precise state, then create a new transaction with `inflow mpp pay`. (Terminal `failed` state, or no credential produced.) | "The payment didn\'t go through \u2014 it was declined, underfunded, or the transaction failed. Want me to try again, switch funding, or stop?" |\n| `PAYMENT_EXPIRED` | Start a new `inflow mpp pay`. | "The payment window expired before it was ready to settle. Want me to start a new one, or stop here?" |\n| `PAYMENT_NOT_ACCEPTED` | `inflow inspect <url>` to re-check the challenge; adjust and retry. | \u2014 |\n\n### x402 errors\n\nAll errors in agent mode are JSON with `code` and `message` fields and exit code 1. x402-specific codes (shared codes are in [\xA7 Shared errors](#shared-errors)). "What to tell the user" is the prompt to surface \u2014 don\'t dump the raw error:\n\n| Error code | Recovery | What to tell the user |\n| --- | --- | --- |\n| `APPROVAL_TIMEOUT` | `inflow x402 status <transaction_id>` for the precise reason, then create a new transaction. | "You didn\'t approve within 15 minutes, so the request expired. Want me to start a new payment, or stop here?" |\n| `APPROVAL_FAILED` | Same recovery as `APPROVAL_TIMEOUT` (declined / insufficient funds in the matched asset / generic). | "Approval didn\'t go through (declined or insufficient funds in the matched asset). Want me to try a different funding source, top up, or stop?" |\n| `APPROVAL_CANCELLED` | Same recovery (cancelled via `x402 cancel` or server-side). | "You cancelled the approval. Stopping here unless you want to start a new payment." |\n| `INVALID_PAYMENT_ID` | `--payment-id` violated the format (see `inflow x402 pay --schema`). Adjust or omit the payment id. | \u2014 |\n\n---\n\n## Security & data handling\n\nApplies to both protocols.\n\n- Treat OAuth tokens and API keys as secrets \u2014 never echo them. The one-time bearer credential (`encoded_payload` for x402, `credential` for MPP) returned after approval should be replayed directly against the seller and discarded, not pasted back to the user.\n- Respect `/agents.txt` and `/llm.txt` on sites you browse.\n- Avoid suspicious 402 endpoints \u2014 if the domain doesn\'t match what the user asked to pay, or the price is different from expectation, stop and ask.\n- When displaying deposit addresses to the user, print the full address (don\'t truncate). Truncating breaks copy-paste.\n\n## Shared errors\n\nThese apply to both protocols (in addition to each section\'s protocol-specific codes). All are JSON with `code` and `message` and exit code 1. Where a command is protocol-specific, use your prefix (`<mpp|x402>`). "What to tell the user" is the prompt to surface \u2014 don\'t dump the raw error:\n\n| Error code | Recovery | What to tell the user |\n| --- | --- | --- |\n| `NOT_AUTHENTICATED` | No saved device token and no `--api-key` / `INFLOW_API_KEY` configured. Run `inflow auth login` or set the API key env var. | \u2014 |\n| `NO_INFLOW_MATCH` | Seller\'s rails aren\'t supported by the account. Fund a matching method/chain, or use a different seller. | "The seller wants `<method/rail or scheme\xD7network>`, but your account can\'t pay on that rail. Either fund a matching method, or pick a different seller." |\n| `NO_FILTERED_MATCH` | A `pay` filter emptied the candidate list. Loosen the filter (flags per the delta table), or re-check the seller\'s unfiltered options with `inflow inspect <url>`. | "Your filter removed every option the seller accepts. Loosen it or re-check the seller\'s options with `inflow inspect`." |\n| `INVALID_402` / `DECODE_FAILED` | Seller returned 402 but the protocol\'s header was missing (`INVALID_402`) or unparseable (`DECODE_FAILED`). Verify the URL is payable; pass the raw header to `inflow <mpp|x402> decode` for the detailed parse error. | \u2014 |\n| `POLLING_TIMEOUT` | `--interval` polling reached its max-attempts or timeout. Retryable \u2014 resume with `inflow <mpp|x402> status <transaction_id> --interval 5 --max-attempts 180`. | "Still waiting on your approval \u2014 want me to keep polling, or cancel the request? (`inflow <mpp|x402> cancel <approval_id>` cancels it.)" |\n| `api_error` | Non-2xx from the InFlow API on the plain data calls (`user`, `balances`, `deposit-addresses`); discriminate on `httpStatus`. `401` \u2014 saved auth rejected, re-run `inflow auth login`. `426` (`VERSION_UNSUPPORTED`) \u2014 upgrade and retry. `5xx` \u2014 server-side; wait and retry. (Note: `pay`/`status` rejections instead surface the server\'s own code, e.g. `INSUFFICIENT_FUNDS`, or the protocol\'s terminal code \u2014 not `api_error`.) | \u2014 |\n| `VERSION_UNSUPPORTED` / HTTP 426 | Installed `inflow` CLI is below the minimum supported version. `npm install -g @inflowpayai/inflow@latest`, then retry; don\'t retry on the old version. | \u2014 |\n| `transport_error` | Network failure \u2014 check connectivity; retry. | \u2014 |\n\n## Out of scope\n\nThis skill covers programmatic HTTP 402 payments (MPP and x402) only. It does NOT handle:\n\n- **Traditional merchant checkouts** No PANs (credit card forms, hosted checkouts).\n- **Card issuance** or wallet management beyond `balances list` and `deposit-addresses list`.\n- **Refunds, disputes, chargebacks** \u2014 handled out of band via support.\n- **Peer-to-peer transfers** between users or wallets.\n- **FX / currency conversion.** Buyer logic matches the seller\'s accepted rails against the account\'s supported assets.\n- **Subscriptions / recurring payments.** Each `pay` is one-shot.\n\nFor any of the above, point the user to https://app.inflowpay.ai or support.\n\n## Further docs\n\n- MPP protocol: https://mpp.dev\n- x402 protocol: https://x402.org\n- InFlow: https://app.inflowpay.ai\n';
16702
17109
  if (process9.argv.includes("--skill")) {
16703
17110
  process9.stdout.write(skillBody.endsWith("\n") ? skillBody : `${skillBody}
16704
17111
  `);
@@ -16718,9 +17125,20 @@ function extractFlag(name) {
16718
17125
  }
16719
17126
  function extractBooleanFlag(name) {
16720
17127
  const idx = process9.argv.indexOf(name);
16721
- if (idx === -1) return false;
16722
- process9.argv.splice(idx, 1);
16723
- return true;
17128
+ if (idx !== -1) {
17129
+ process9.argv.splice(idx, 1);
17130
+ return true;
17131
+ }
17132
+ const prefix = `${name}=`;
17133
+ const assignmentIdx = process9.argv.findIndex((arg2) => arg2.startsWith(prefix));
17134
+ if (assignmentIdx === -1) return false;
17135
+ const [arg] = process9.argv.splice(assignmentIdx, 1);
17136
+ const value = arg?.slice(prefix.length) ?? "";
17137
+ if (value === "true") return true;
17138
+ if (value === "false") return false;
17139
+ process9.stderr.write(`Invalid ${name} value: ${value}. Expected 'true' or 'false'.
17140
+ `);
17141
+ process9.exit(2);
16724
17142
  }
16725
17143
  var credentialFilePath = extractFlag("--auth") ?? process9.env.INFLOW_AUTH_FILE;
16726
17144
  var baseUrlFromFlag = extractFlag("--base-url");
@@ -16730,7 +17148,7 @@ var authBaseUrlFromFlag = extractFlag("--auth-base-url");
16730
17148
  var environmentFromFlag = extractFlag("--environment");
16731
17149
  var sandboxFlag = extractBooleanFlag("--sandbox");
16732
17150
  var apiKeyFromFlag = extractFlag("--api-key");
16733
- var verbose = process9.argv.includes("--verbose");
17151
+ var verbose = extractBooleanFlag("--verbose");
16734
17152
  var authStorage = credentialFilePath ? new Storage({ configPath: credentialFilePath }) : storage;
16735
17153
  var apiKeyFromEnv = process9.env.INFLOW_API_KEY;
16736
17154
  function readSavedApiKey() {
@@ -16825,6 +17243,7 @@ cli.command(createBalancesCli(inflow.balances, authStorage, inflow));
16825
17243
  cli.command(createDepositAddressesCli(inflow.depositAddresses, authStorage, inflow));
16826
17244
  cli.command(createX402Cli(inflow, authStorage, resolvedApiBaseUrl));
16827
17245
  cli.command(createMppCli(inflow, authStorage, resolvedApiBaseUrl));
17246
+ cli.command("inspect", createInspectCommand());
16828
17247
  await cli.serve();
16829
17248
  var cli_default = cli;
16830
17249
  export {