@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.
- package/README.md +95 -22
- package/dist/cli.js +1125 -706
- 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
|
|
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${
|
|
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${
|
|
8850
|
+
ret = `>=${M}.${m}.0${z8} <${M}.${+m + 1}.0-0`;
|
|
8851
8851
|
} else {
|
|
8852
|
-
ret = `>=${M}.${m}.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}${
|
|
8869
|
+
ret = `>=${M}.${m}.${p}${z8} <${M}.${m}.${+p + 1}-0`;
|
|
8870
8870
|
} else {
|
|
8871
|
-
ret = `>=${M}.${m}.${p}${
|
|
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
|
|
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
|
|
11779
|
-
if (
|
|
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
|
-
|
|
11788
|
-
|
|
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
|
|
12354
|
-
if (
|
|
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
|
-
|
|
12363
|
-
|
|
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.
|
|
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/
|
|
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
|
|
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__ */
|
|
14698
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14709
|
-
/* @__PURE__ */
|
|
14710
|
-
/* @__PURE__ */
|
|
14711
|
-
result2.contentType !== void 0 ? /* @__PURE__ */
|
|
14712
|
-
/* @__PURE__ */
|
|
14713
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14718
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
14728
|
-
/* @__PURE__ */
|
|
14729
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14738
|
+
/* @__PURE__ */ jsx12(Text10, { color: "cyan", children: result.url }),
|
|
14732
14739
|
" \xB7 ",
|
|
14733
|
-
/* @__PURE__ */
|
|
14740
|
+
/* @__PURE__ */ jsx12(Text10, { dimColor: true, children: `realm ${result.realm}` }),
|
|
14734
14741
|
" \xB7 ",
|
|
14735
|
-
/* @__PURE__ */
|
|
14742
|
+
/* @__PURE__ */ jsx12(Text10, { dimColor: true, children: `${String(count)} challenge${count === 1 ? "" : "s"}` })
|
|
14736
14743
|
] }),
|
|
14737
|
-
/* @__PURE__ */
|
|
14738
|
-
/* @__PURE__ */
|
|
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/
|
|
14780
|
-
import { Box as
|
|
14781
|
-
import
|
|
14782
|
-
import { useEffect as useEffect7, useReducer as useReducer5
|
|
14783
|
-
import {
|
|
14784
|
-
|
|
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(
|
|
14787
|
-
const
|
|
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
|
-
|
|
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 === "
|
|
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__ */
|
|
14835
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14845
|
-
/* @__PURE__ */
|
|
14846
|
-
"
|
|
14847
|
-
|
|
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 === "
|
|
14863
|
-
|
|
14864
|
-
|
|
14865
|
-
|
|
14866
|
-
|
|
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__ */
|
|
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
|
-
|
|
14892
|
-
|
|
14893
|
-
|
|
14894
|
-
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
|
|
14898
|
-
|
|
14899
|
-
|
|
14900
|
-
/* @__PURE__ */
|
|
14901
|
-
|
|
14902
|
-
|
|
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__ */
|
|
14906
|
-
/* @__PURE__ */
|
|
14907
|
-
result.settled !== void 0 ? /* @__PURE__ */
|
|
14908
|
-
/* @__PURE__ */
|
|
14909
|
-
result.outputSavedTo !== void 0 ? /* @__PURE__ */
|
|
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__ */
|
|
15435
|
+
/* @__PURE__ */ jsx18(Text15, { bold: true, children: result.outputSavedTo })
|
|
14912
15436
|
] }) : null,
|
|
14913
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
14914
|
-
/* @__PURE__ */
|
|
14915
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14922
|
-
/* @__PURE__ */
|
|
14923
|
-
/* @__PURE__ */
|
|
14924
|
-
/* @__PURE__ */
|
|
14925
|
-
/* @__PURE__ */
|
|
14926
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
14927
|
-
/* @__PURE__ */
|
|
14928
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14935
|
-
/* @__PURE__ */
|
|
14936
|
-
/* @__PURE__ */
|
|
14937
|
-
/* @__PURE__ */
|
|
14938
|
-
result.outputSavedTo !== void 0 ? /* @__PURE__ */
|
|
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__ */
|
|
15464
|
+
/* @__PURE__ */ jsx18(Text15, { bold: true, children: result.outputSavedTo })
|
|
14941
15465
|
] }) : null,
|
|
14942
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
14943
|
-
/* @__PURE__ */
|
|
14944
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14949
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
14959
|
-
var payArgs =
|
|
14960
|
-
url:
|
|
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 =
|
|
14963
|
-
paymentMethod:
|
|
14964
|
-
intent:
|
|
14965
|
-
currency:
|
|
14966
|
-
rail:
|
|
14967
|
-
method:
|
|
14968
|
-
data:
|
|
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:
|
|
14972
|
-
interval:
|
|
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:
|
|
14976
|
-
timeout:
|
|
14977
|
-
instrumentId:
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
14991
|
-
transactionId:
|
|
15514
|
+
var statusArgs = z5.object({
|
|
15515
|
+
transactionId: z5.string().describe("The transaction id returned by `mpp pay`.")
|
|
14992
15516
|
});
|
|
14993
|
-
var statusOptions2 =
|
|
14994
|
-
interval:
|
|
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:
|
|
14998
|
-
timeout:
|
|
14999
|
-
credentialFile:
|
|
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 =
|
|
15004
|
-
approvalId:
|
|
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 =
|
|
15007
|
-
value:
|
|
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
|
|
15012
|
-
url:
|
|
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
|
|
15015
|
-
paymentMethod:
|
|
15016
|
-
intent:
|
|
15017
|
-
currency:
|
|
15018
|
-
rail:
|
|
15019
|
-
method:
|
|
15020
|
-
data:
|
|
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:
|
|
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
|
|
15028
|
-
import
|
|
15029
|
-
import { useEffect as
|
|
15030
|
-
import { jsx as
|
|
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] =
|
|
15564
|
+
const [phase, dispatch] = useReducer8(reduceMppStatus, initial);
|
|
15041
15565
|
const { finish } = useFlowExit(onComplete);
|
|
15042
|
-
|
|
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
|
-
|
|
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__ */
|
|
15063
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15075
|
-
/* @__PURE__ */
|
|
15076
|
-
/* @__PURE__ */
|
|
15077
|
-
phase.response.expires !== void 0 ? /* @__PURE__ */
|
|
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__ */
|
|
15082
|
-
/* @__PURE__ */
|
|
15083
|
-
phase.response.problem !== void 0 ? /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
15091
|
-
/* @__PURE__ */
|
|
15092
|
-
phase.response !== void 0 ? /* @__PURE__ */
|
|
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__ */
|
|
15096
|
-
/* @__PURE__ */
|
|
15097
|
-
/* @__PURE__ */
|
|
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
|
|
15103
|
-
import
|
|
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
|
|
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
|
|
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__ */
|
|
15134
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15140
|
-
/* @__PURE__ */
|
|
15141
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
15672
|
+
return /* @__PURE__ */ jsx20(Table, { columns: COLUMNS5, rows });
|
|
15149
15673
|
};
|
|
15150
15674
|
|
|
15151
15675
|
// src/commands/mpp/index.tsx
|
|
15152
|
-
import { jsx as
|
|
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
|
|
15156
|
-
|
|
15157
|
-
|
|
15158
|
-
|
|
15159
|
-
|
|
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
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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:
|
|
15563
|
-
options:
|
|
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
|
|
15578
|
-
import
|
|
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
|
|
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__ */
|
|
15602
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15608
|
-
/* @__PURE__ */
|
|
15609
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
15622
|
-
var getOptions =
|
|
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
|
|
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__ */
|
|
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
|
|
15679
|
-
import
|
|
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
|
|
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__ */
|
|
15688
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
15703
|
-
import { Box as
|
|
15704
|
-
import { jsx as
|
|
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(
|
|
15707
|
-
return /* @__PURE__ */
|
|
15708
|
-
/* @__PURE__ */
|
|
15709
|
-
/* @__PURE__ */
|
|
15710
|
-
/* @__PURE__ */
|
|
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__ */
|
|
16245
|
+
/* @__PURE__ */ jsx25(Text20, { color: "cyan", children: String(decoded.x402Version) })
|
|
15713
16246
|
] }),
|
|
15714
|
-
/* @__PURE__ */
|
|
16247
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
15715
16248
|
"resource: ",
|
|
15716
|
-
/* @__PURE__ */
|
|
16249
|
+
/* @__PURE__ */ jsx25(Text20, { color: "cyan", children: decoded.resource.url })
|
|
15717
16250
|
] }),
|
|
15718
|
-
/* @__PURE__ */
|
|
15719
|
-
summary.map((entry, idx) => /* @__PURE__ */
|
|
16251
|
+
/* @__PURE__ */ jsx25(Text20, { children: `accepts (${String(summary.length)}):` }),
|
|
16252
|
+
summary.map((entry, idx) => /* @__PURE__ */ jsxs19(Text20, { children: [
|
|
15720
16253
|
" ",
|
|
15721
|
-
/* @__PURE__ */
|
|
16254
|
+
/* @__PURE__ */ jsx25(Text20, { color: "yellow", children: entry.scheme }),
|
|
15722
16255
|
" / ",
|
|
15723
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
15856
|
-
import
|
|
15857
|
-
import { useEffect as
|
|
15858
|
-
import { jsx as
|
|
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] =
|
|
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
|
-
|
|
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
|
-
|
|
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__ */
|
|
15902
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15908
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15918
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15926
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15933
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15944
|
-
/* @__PURE__ */
|
|
15945
|
-
/* @__PURE__ */
|
|
15946
|
-
/* @__PURE__ */
|
|
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__ */
|
|
16359
|
+
/* @__PURE__ */ jsx26(Text21, { bold: true, color: "cyan", children: phase.approvalUrl })
|
|
15949
16360
|
] }),
|
|
15950
|
-
/* @__PURE__ */
|
|
15951
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15954
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15961
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15968
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15975
|
-
result.settled?.network !== void 0 ? /* @__PURE__ */
|
|
15976
|
-
result.outputSavedTo !== void 0 ? /* @__PURE__ */
|
|
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__ */
|
|
16389
|
+
/* @__PURE__ */ jsx26(Text21, { bold: true, children: result.outputSavedTo })
|
|
15979
16390
|
] }) : null,
|
|
15980
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
15981
|
-
/* @__PURE__ */
|
|
15982
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15989
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15997
|
-
/* @__PURE__ */
|
|
15998
|
-
/* @__PURE__ */
|
|
15999
|
-
result.outputSavedTo !== void 0 ? /* @__PURE__ */
|
|
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__ */
|
|
16412
|
+
/* @__PURE__ */ jsx26(Text21, { bold: true, children: result.outputSavedTo })
|
|
16002
16413
|
] }) : null,
|
|
16003
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
16004
|
-
/* @__PURE__ */
|
|
16005
|
-
/* @__PURE__ */
|
|
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__ */
|
|
16012
|
-
/* @__PURE__ */
|
|
16013
|
-
result.outputSavedTo !== void 0 ? /* @__PURE__ */
|
|
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__ */
|
|
16426
|
+
/* @__PURE__ */ jsx26(Text21, { bold: true, children: result.outputSavedTo })
|
|
16016
16427
|
] }) : null,
|
|
16017
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
16018
|
-
/* @__PURE__ */
|
|
16019
|
-
/* @__PURE__ */
|
|
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__ */
|
|
16024
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
16034
|
-
var payArgs2 =
|
|
16035
|
-
url:
|
|
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 =
|
|
16038
|
-
scheme:
|
|
16039
|
-
network:
|
|
16040
|
-
asset:
|
|
16041
|
-
assetName:
|
|
16042
|
-
method:
|
|
16043
|
-
data:
|
|
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:
|
|
16047
|
-
interval:
|
|
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:
|
|
16051
|
-
timeout:
|
|
16052
|
-
paymentId:
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
16066
|
-
transactionId:
|
|
16476
|
+
var statusArgs2 = z7.object({
|
|
16477
|
+
transactionId: z7.string().describe("The transaction id returned by `x402 pay`.")
|
|
16067
16478
|
});
|
|
16068
|
-
var statusOptions3 =
|
|
16069
|
-
interval:
|
|
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:
|
|
16073
|
-
timeout:
|
|
16074
|
-
payloadFile:
|
|
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 =
|
|
16079
|
-
approvalId:
|
|
16489
|
+
var cancelArgs2 = z7.object({
|
|
16490
|
+
approvalId: z7.string().describe("The approval id returned by `x402 pay`.")
|
|
16080
16491
|
});
|
|
16081
|
-
var decodeArgs2 =
|
|
16082
|
-
header:
|
|
16492
|
+
var decodeArgs2 = z7.object({
|
|
16493
|
+
header: z7.string().describe("Raw PAYMENT-REQUIRED header value (base64).")
|
|
16083
16494
|
});
|
|
16084
|
-
var
|
|
16085
|
-
url:
|
|
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
|
|
16088
|
-
scheme:
|
|
16089
|
-
network:
|
|
16090
|
-
asset:
|
|
16091
|
-
assetName:
|
|
16092
|
-
method:
|
|
16093
|
-
data:
|
|
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:
|
|
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
|
|
16101
|
-
import
|
|
16102
|
-
import { useEffect as
|
|
16103
|
-
import { jsx as
|
|
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] =
|
|
16524
|
+
const [phase, dispatch] = useReducer10(reduceX402Status, initial);
|
|
16114
16525
|
const { finish } = useFlowExit(onComplete);
|
|
16115
|
-
|
|
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
|
-
|
|
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__ */
|
|
16136
|
-
/* @__PURE__ */
|
|
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__ */
|
|
16148
|
-
/* @__PURE__ */
|
|
16149
|
-
/* @__PURE__ */
|
|
16150
|
-
/* @__PURE__ */
|
|
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__ */
|
|
16155
|
-
/* @__PURE__ */
|
|
16156
|
-
/* @__PURE__ */
|
|
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__ */
|
|
16161
|
-
/* @__PURE__ */
|
|
16162
|
-
phase.response !== void 0 ? /* @__PURE__ */
|
|
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__ */
|
|
16166
|
-
/* @__PURE__ */
|
|
16167
|
-
/* @__PURE__ */
|
|
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
|
|
16173
|
-
import
|
|
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
|
|
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__ */
|
|
16186
|
-
/* @__PURE__ */
|
|
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__ */
|
|
16192
|
-
/* @__PURE__ */
|
|
16193
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
16611
|
+
return /* @__PURE__ */ jsx28(Table, { columns: COLUMNS6, rows: kinds });
|
|
16201
16612
|
};
|
|
16202
16613
|
|
|
16203
16614
|
// src/commands/x402/index.tsx
|
|
16204
|
-
import { jsx as
|
|
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
|
|
16214
|
-
|
|
16215
|
-
|
|
16216
|
-
|
|
16217
|
-
|
|
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
|
|
16234
|
-
|
|
16235
|
-
const probeOptions = {
|
|
16640
|
+
function probeOptionsFrom2(c) {
|
|
16641
|
+
return {
|
|
16236
16642
|
method: c.options.method,
|
|
16237
|
-
headers:
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
-
|
|
16511
|
-
|
|
16512
|
-
|
|
16513
|
-
|
|
16514
|
-
|
|
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__ */
|
|
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:
|
|
16622
|
-
options:
|
|
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.
|
|
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
|
|
16722
|
-
|
|
16723
|
-
|
|
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 =
|
|
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 {
|