@inflowpayai/inflow 0.6.3 → 0.6.5
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 +1122 -702
- 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([
|
|
@@ -11589,6 +11591,7 @@ function runAuthLogin(input) {
|
|
|
11589
11591
|
input.authStorage.setAuth(outcome.value);
|
|
11590
11592
|
input.authStorage.clearApiKey();
|
|
11591
11593
|
input.authStorage.setConnection(input.connection);
|
|
11594
|
+
input.authStorage.clearPendingDeviceAuth();
|
|
11592
11595
|
if (input.priorRefreshToken !== void 0) {
|
|
11593
11596
|
input.authResource.revokeToken(input.priorRefreshToken).catch(() => {
|
|
11594
11597
|
});
|
|
@@ -11730,6 +11733,15 @@ function buildNoFilteredMatchMessage(decoded, filters) {
|
|
|
11730
11733
|
function isSuccessStatus(status) {
|
|
11731
11734
|
return status >= 200 && status < 300;
|
|
11732
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
|
+
}
|
|
11733
11745
|
function reduceX402Inspect(state, event) {
|
|
11734
11746
|
switch (event.type) {
|
|
11735
11747
|
case "accepts":
|
|
@@ -11774,8 +11786,8 @@ async function runInspectPipeline(deps, emit) {
|
|
|
11774
11786
|
emit({ type: "no-payment", result: result2 });
|
|
11775
11787
|
return;
|
|
11776
11788
|
}
|
|
11777
|
-
const
|
|
11778
|
-
if (
|
|
11789
|
+
const parse = parseX402HeaderFromProbe(probe);
|
|
11790
|
+
if (parse.kind === "absent") {
|
|
11779
11791
|
emit({
|
|
11780
11792
|
type: "errored",
|
|
11781
11793
|
code: INVALID_402_CODE,
|
|
@@ -11783,17 +11795,11 @@ async function runInspectPipeline(deps, emit) {
|
|
|
11783
11795
|
});
|
|
11784
11796
|
return;
|
|
11785
11797
|
}
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
decoded = decodePaymentRequiredHeader(headerValue);
|
|
11789
|
-
} catch (err) {
|
|
11790
|
-
emit({
|
|
11791
|
-
type: "errored",
|
|
11792
|
-
code: "DECODE_FAILED",
|
|
11793
|
-
message: err instanceof Error ? err.message : String(err)
|
|
11794
|
-
});
|
|
11798
|
+
if (parse.kind === "error") {
|
|
11799
|
+
emit({ type: "errored", code: parse.code, message: parse.message });
|
|
11795
11800
|
return;
|
|
11796
11801
|
}
|
|
11802
|
+
const decoded = parse.decoded;
|
|
11797
11803
|
const filters = {
|
|
11798
11804
|
...deps.schemeFilter !== void 0 ? { scheme: deps.schemeFilter } : {},
|
|
11799
11805
|
...deps.networkFilter !== void 0 ? { network: deps.networkFilter } : {},
|
|
@@ -12307,6 +12313,15 @@ function buildNoFilteredMatchMessage2(challenges, filters) {
|
|
|
12307
12313
|
}).join(", ");
|
|
12308
12314
|
return `Seller has no \`inflow\` challenge matching ${filterDescription}. Available: ${available || "(none)"}.`;
|
|
12309
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
|
+
}
|
|
12310
12325
|
function reduceMppInspect(state, event) {
|
|
12311
12326
|
switch (event.type) {
|
|
12312
12327
|
case "challenges":
|
|
@@ -12349,8 +12364,8 @@ async function runMppInspectPipeline(deps, emit) {
|
|
|
12349
12364
|
});
|
|
12350
12365
|
return;
|
|
12351
12366
|
}
|
|
12352
|
-
const
|
|
12353
|
-
if (
|
|
12367
|
+
const parse = parseMppHeaderFromProbe(probe);
|
|
12368
|
+
if (parse.kind === "absent") {
|
|
12354
12369
|
emit({
|
|
12355
12370
|
type: "errored",
|
|
12356
12371
|
code: INVALID_402_CODE2,
|
|
@@ -12358,14 +12373,11 @@ async function runMppInspectPipeline(deps, emit) {
|
|
|
12358
12373
|
});
|
|
12359
12374
|
return;
|
|
12360
12375
|
}
|
|
12361
|
-
|
|
12362
|
-
|
|
12363
|
-
challenges = parseChallengeHeaders(headerValues);
|
|
12364
|
-
} catch (err) {
|
|
12365
|
-
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 });
|
|
12366
12378
|
return;
|
|
12367
12379
|
}
|
|
12368
|
-
const inflowChallenges = filterInflowChallenges(challenges);
|
|
12380
|
+
const inflowChallenges = filterInflowChallenges(parse.challenges);
|
|
12369
12381
|
if (inflowChallenges.length === 0) {
|
|
12370
12382
|
emit({ type: "errored", code: NO_INFLOW_MATCH_CODE2, message: NO_INFLOW_MATCH_MESSAGE2 });
|
|
12371
12383
|
return;
|
|
@@ -12853,7 +12865,7 @@ function augmentMpp(mppResource, resolvedApiBaseUrl2) {
|
|
|
12853
12865
|
var DEFAULT_RETRIES = 3;
|
|
12854
12866
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
12855
12867
|
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
|
|
12856
|
-
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"}`;
|
|
12857
12869
|
function sleep2(ms, signal) {
|
|
12858
12870
|
return new Promise((resolve, reject) => {
|
|
12859
12871
|
const onAbort = () => {
|
|
@@ -13495,6 +13507,85 @@ async function probeSession(userResource, options) {
|
|
|
13495
13507
|
function hasSession(storage2, hasApiKey) {
|
|
13496
13508
|
return hasApiKey() || storage2.isAuthenticated();
|
|
13497
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
|
+
}
|
|
13498
13589
|
async function runBalancesList(input) {
|
|
13499
13590
|
return input.balanceResource.list();
|
|
13500
13591
|
}
|
|
@@ -14575,94 +14666,11 @@ function createDepositAddressesCli(depositAddressResource, authStorage2, inflow2
|
|
|
14575
14666
|
return cli2;
|
|
14576
14667
|
}
|
|
14577
14668
|
|
|
14578
|
-
// src/commands/mpp/
|
|
14579
|
-
import { chmodSync, writeFileSync as writeFileSync2 } from "fs";
|
|
14580
|
-
import { resolve as resolvePath2 } from "path";
|
|
14581
|
-
import { Cli as Cli4 } from "incur";
|
|
14582
|
-
|
|
14583
|
-
// src/commands/mpp/cancel.tsx
|
|
14669
|
+
// src/commands/mpp/inspect.tsx
|
|
14584
14670
|
import { Box as Box9, Text as Text10 } from "ink";
|
|
14585
14671
|
import Spinner7 from "ink-spinner";
|
|
14586
|
-
import { useCallback as useCallback5 } from "react";
|
|
14587
|
-
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
14588
|
-
var CancelView = ({ approvalId, cancel, onComplete }) => {
|
|
14589
|
-
const action = useCallback5(() => cancel(), [cancel]);
|
|
14590
|
-
const { finish } = useFlowExit(onComplete);
|
|
14591
|
-
const { status } = useFlowState(action, finish);
|
|
14592
|
-
if (status === "loading") {
|
|
14593
|
-
return /* @__PURE__ */ jsx12(Box9, { children: /* @__PURE__ */ jsxs9(Text10, { color: "cyan", children: [
|
|
14594
|
-
/* @__PURE__ */ jsx12(Spinner7, { type: "dots" }),
|
|
14595
|
-
" Cancelling approval ",
|
|
14596
|
-
approvalId,
|
|
14597
|
-
"..."
|
|
14598
|
-
] }) });
|
|
14599
|
-
}
|
|
14600
|
-
return /* @__PURE__ */ jsx12(Box9, { children: /* @__PURE__ */ jsxs9(Text10, { color: "green", children: [
|
|
14601
|
-
"\u2713 Cancelled approval ",
|
|
14602
|
-
approvalId,
|
|
14603
|
-
" (best-effort)"
|
|
14604
|
-
] }) });
|
|
14605
|
-
};
|
|
14606
|
-
|
|
14607
|
-
// src/commands/mpp/decode.tsx
|
|
14608
|
-
import { Box as Box10, Text as Text11 } from "ink";
|
|
14609
|
-
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
14610
|
-
function ChallengeBody({ challenge }) {
|
|
14611
|
-
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
|
|
14612
|
-
/* @__PURE__ */ jsx13(Text11, { bold: true, children: "Challenge" }),
|
|
14613
|
-
/* @__PURE__ */ jsxs10(Text11, { children: [
|
|
14614
|
-
"method/intent: ",
|
|
14615
|
-
/* @__PURE__ */ jsxs10(Text11, { color: "yellow", children: [
|
|
14616
|
-
challenge.method,
|
|
14617
|
-
" / ",
|
|
14618
|
-
challenge.intent
|
|
14619
|
-
] })
|
|
14620
|
-
] }),
|
|
14621
|
-
/* @__PURE__ */ jsx13(Text11, { children: `id: ${challenge.id}` }),
|
|
14622
|
-
/* @__PURE__ */ jsx13(Text11, { children: `realm: ${challenge.realm}` }),
|
|
14623
|
-
challenge.amount !== void 0 ? /* @__PURE__ */ jsx13(Text11, { children: `amount: ${challenge.amount}${challenge.currency !== void 0 ? ` ${challenge.currency}` : ""}` }) : null,
|
|
14624
|
-
challenge.rail !== void 0 ? /* @__PURE__ */ jsx13(Text11, { children: `rail: ${challenge.rail}` }) : null,
|
|
14625
|
-
challenge.expires !== void 0 ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `expires: ${challenge.expires}` }) : null
|
|
14626
|
-
] });
|
|
14627
|
-
}
|
|
14628
|
-
var DecodeView = ({ result }) => {
|
|
14629
|
-
if (result.kind === "challenge") {
|
|
14630
|
-
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingY: 1, children: [
|
|
14631
|
-
/* @__PURE__ */ jsx13(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text11, { bold: true, children: "Decoded WWW-Authenticate: Payment" }) }),
|
|
14632
|
-
/* @__PURE__ */ jsx13(ChallengeBody, { challenge: result.challenge })
|
|
14633
|
-
] });
|
|
14634
|
-
}
|
|
14635
|
-
if (result.kind === "credential") {
|
|
14636
|
-
const { credential } = result;
|
|
14637
|
-
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingY: 1, children: [
|
|
14638
|
-
/* @__PURE__ */ jsx13(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text11, { bold: true, children: "Decoded Authorization: Payment credential" }) }),
|
|
14639
|
-
/* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
|
|
14640
|
-
/* @__PURE__ */ jsx13(Text11, { children: `challenge id: ${credential.challenge.id}` }),
|
|
14641
|
-
/* @__PURE__ */ jsx13(Text11, { children: `method: ${credential.challenge.method}` }),
|
|
14642
|
-
/* @__PURE__ */ jsx13(Text11, { children: `source: ${credential.source}` }),
|
|
14643
|
-
/* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `payload keys: ${Object.keys(credential.payload).join(", ") || "(none)"}` })
|
|
14644
|
-
] })
|
|
14645
|
-
] });
|
|
14646
|
-
}
|
|
14647
|
-
const { receipt } = result;
|
|
14648
|
-
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingY: 1, children: [
|
|
14649
|
-
/* @__PURE__ */ jsx13(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text11, { bold: true, children: "Decoded Payment-Receipt" }) }),
|
|
14650
|
-
/* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
|
|
14651
|
-
/* @__PURE__ */ jsxs10(Text11, { children: [
|
|
14652
|
-
"status: ",
|
|
14653
|
-
/* @__PURE__ */ jsx13(Text11, { color: "green", children: receipt.status })
|
|
14654
|
-
] }),
|
|
14655
|
-
/* @__PURE__ */ jsx13(Text11, { children: `reference: ${receipt.reference}` }),
|
|
14656
|
-
/* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `timestamp: ${receipt.timestamp}` })
|
|
14657
|
-
] })
|
|
14658
|
-
] });
|
|
14659
|
-
};
|
|
14660
|
-
|
|
14661
|
-
// src/commands/mpp/inspect.tsx
|
|
14662
|
-
import { Box as Box11, Text as Text12 } from "ink";
|
|
14663
|
-
import Spinner8 from "ink-spinner";
|
|
14664
14672
|
import { useEffect as useEffect6, useReducer as useReducer4 } from "react";
|
|
14665
|
-
import { jsx as
|
|
14673
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
14666
14674
|
function orDash(value) {
|
|
14667
14675
|
return value === void 0 || value === "" ? "\u2014" : value;
|
|
14668
14676
|
}
|
|
@@ -14693,8 +14701,8 @@ var InspectView = ({ url, method, deps, onComplete }) => {
|
|
|
14693
14701
|
}
|
|
14694
14702
|
}, [phase, finish]);
|
|
14695
14703
|
if (phase.kind === "probing") {
|
|
14696
|
-
return /* @__PURE__ */
|
|
14697
|
-
/* @__PURE__ */
|
|
14704
|
+
return /* @__PURE__ */ jsx12(Box9, { children: /* @__PURE__ */ jsxs9(Text10, { color: "cyan", children: [
|
|
14705
|
+
/* @__PURE__ */ jsx12(Spinner7, { type: "dots" }),
|
|
14698
14706
|
" Probing ",
|
|
14699
14707
|
method,
|
|
14700
14708
|
" ",
|
|
@@ -14704,37 +14712,37 @@ var InspectView = ({ url, method, deps, onComplete }) => {
|
|
|
14704
14712
|
}
|
|
14705
14713
|
if (phase.kind === "no-payment") {
|
|
14706
14714
|
const { result: result2 } = phase;
|
|
14707
|
-
return /* @__PURE__ */
|
|
14708
|
-
/* @__PURE__ */
|
|
14709
|
-
/* @__PURE__ */
|
|
14710
|
-
result2.contentType !== void 0 ? /* @__PURE__ */
|
|
14711
|
-
/* @__PURE__ */
|
|
14712
|
-
/* @__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." })
|
|
14713
14721
|
] });
|
|
14714
14722
|
}
|
|
14715
14723
|
if (phase.kind === "error") {
|
|
14716
|
-
return /* @__PURE__ */
|
|
14717
|
-
/* @__PURE__ */
|
|
14724
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
14725
|
+
/* @__PURE__ */ jsxs9(Text10, { color: "red", children: [
|
|
14718
14726
|
"\u2717 ",
|
|
14719
14727
|
phase.code
|
|
14720
14728
|
] }),
|
|
14721
|
-
/* @__PURE__ */
|
|
14729
|
+
/* @__PURE__ */ jsx12(Text10, { color: "red", children: phase.message })
|
|
14722
14730
|
] });
|
|
14723
14731
|
}
|
|
14724
14732
|
const { result } = phase;
|
|
14725
14733
|
const count = result.challenges.length;
|
|
14726
|
-
return /* @__PURE__ */
|
|
14727
|
-
/* @__PURE__ */
|
|
14728
|
-
/* @__PURE__ */
|
|
14734
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
14735
|
+
/* @__PURE__ */ jsxs9(Text10, { children: [
|
|
14736
|
+
/* @__PURE__ */ jsx12(Text10, { bold: true, children: "WWW-Authenticate: Payment" }),
|
|
14729
14737
|
" for ",
|
|
14730
|
-
/* @__PURE__ */
|
|
14738
|
+
/* @__PURE__ */ jsx12(Text10, { color: "cyan", children: result.url }),
|
|
14731
14739
|
" \xB7 ",
|
|
14732
|
-
/* @__PURE__ */
|
|
14740
|
+
/* @__PURE__ */ jsx12(Text10, { dimColor: true, children: `realm ${result.realm}` }),
|
|
14733
14741
|
" \xB7 ",
|
|
14734
|
-
/* @__PURE__ */
|
|
14742
|
+
/* @__PURE__ */ jsx12(Text10, { dimColor: true, children: `${String(count)} challenge${count === 1 ? "" : "s"}` })
|
|
14735
14743
|
] }),
|
|
14736
|
-
/* @__PURE__ */
|
|
14737
|
-
/* @__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." }) })
|
|
14738
14746
|
] });
|
|
14739
14747
|
};
|
|
14740
14748
|
function challengeToFrame(challenge) {
|
|
@@ -14775,63 +14783,53 @@ function buildNoPaymentFrame(result) {
|
|
|
14775
14783
|
return frame;
|
|
14776
14784
|
}
|
|
14777
14785
|
|
|
14778
|
-
// src/commands/
|
|
14779
|
-
import { Box as
|
|
14780
|
-
import
|
|
14781
|
-
import { useEffect as useEffect7, useReducer as useReducer5
|
|
14782
|
-
import {
|
|
14783
|
-
|
|
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 }) => {
|
|
14784
14813
|
const initial = { kind: "probing" };
|
|
14785
|
-
const [phase, dispatch] = useReducer5(
|
|
14786
|
-
const
|
|
14787
|
-
const { finish, cancelThenFinish } = useFlowExit(onComplete);
|
|
14788
|
-
const created = phase.kind === "created" ? phase.created : void 0;
|
|
14789
|
-
const approvalUrl = created?.approvalUrl;
|
|
14790
|
-
const approvalId = created?.approvalId;
|
|
14791
|
-
useInput3(
|
|
14792
|
-
(_input, key) => {
|
|
14793
|
-
if (approvalUrl === void 0) return;
|
|
14794
|
-
if (key.return) {
|
|
14795
|
-
openUrl(approvalUrl);
|
|
14796
|
-
return;
|
|
14797
|
-
}
|
|
14798
|
-
if (key.escape && approvalId !== void 0) {
|
|
14799
|
-
setCancelling(true);
|
|
14800
|
-
cancelThenFinish(() => onCancel?.(approvalId), {
|
|
14801
|
-
kind: "error",
|
|
14802
|
-
code: "APPROVAL_CANCELLED",
|
|
14803
|
-
message: `Approval ${approvalId} cancelled.`
|
|
14804
|
-
});
|
|
14805
|
-
}
|
|
14806
|
-
},
|
|
14807
|
-
{ isActive: approvalUrl !== void 0 && !cancelling }
|
|
14808
|
-
);
|
|
14814
|
+
const [phase, dispatch] = useReducer5(reduceX402Inspect, initial);
|
|
14815
|
+
const { finish } = useFlowExit(onComplete);
|
|
14809
14816
|
useEffect7(() => {
|
|
14810
|
-
const controller = new AbortController();
|
|
14811
14817
|
let cancelled = false;
|
|
14812
|
-
|
|
14813
|
-
void runMppPayPipeline(runDeps, (event) => {
|
|
14818
|
+
void runInspectPipeline(deps, (event) => {
|
|
14814
14819
|
if (!cancelled) dispatch(event);
|
|
14815
14820
|
});
|
|
14816
14821
|
return () => {
|
|
14817
14822
|
cancelled = true;
|
|
14818
|
-
controller.abort();
|
|
14819
14823
|
};
|
|
14820
14824
|
}, [deps]);
|
|
14821
14825
|
useEffect7(() => {
|
|
14822
|
-
if (phase.kind === "
|
|
14826
|
+
if (phase.kind === "accepts" || phase.kind === "no-payment" || phase.kind === "error") {
|
|
14823
14827
|
finish(phase);
|
|
14824
14828
|
}
|
|
14825
14829
|
}, [phase, finish]);
|
|
14826
|
-
if (cancelling) {
|
|
14827
|
-
return /* @__PURE__ */ jsx15(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "yellow", children: [
|
|
14828
|
-
/* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
|
|
14829
|
-
" Cancelling approval..."
|
|
14830
|
-
] }) });
|
|
14831
|
-
}
|
|
14832
14830
|
if (phase.kind === "probing") {
|
|
14833
|
-
return /* @__PURE__ */
|
|
14834
|
-
/* @__PURE__ */
|
|
14831
|
+
return /* @__PURE__ */ jsx13(Box10, { children: /* @__PURE__ */ jsxs10(Text11, { color: "cyan", children: [
|
|
14832
|
+
/* @__PURE__ */ jsx13(Spinner8, { type: "dots" }),
|
|
14835
14833
|
" Probing ",
|
|
14836
14834
|
method,
|
|
14837
14835
|
" ",
|
|
@@ -14840,193 +14838,720 @@ var PayView = ({ url, method, deps, onComplete, onCancel }) => {
|
|
|
14840
14838
|
] }) });
|
|
14841
14839
|
}
|
|
14842
14840
|
if (phase.kind === "no-payment") {
|
|
14843
|
-
return /* @__PURE__ */
|
|
14844
|
-
/* @__PURE__ */
|
|
14845
|
-
"
|
|
14846
|
-
|
|
14847
|
-
"); finalising..."
|
|
14848
|
-
] }) });
|
|
14849
|
-
}
|
|
14850
|
-
if (phase.kind === "decoded") {
|
|
14851
|
-
return /* @__PURE__ */ jsx15(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
|
|
14852
|
-
/* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
|
|
14853
|
-
" Fulfilling ",
|
|
14854
|
-
phase.challenge.amount ?? "",
|
|
14855
|
-
" ",
|
|
14856
|
-
phase.challenge.currency ?? "",
|
|
14857
|
-
" ",
|
|
14858
|
-
"challenge..."
|
|
14859
|
-
] }) });
|
|
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
|
+
] });
|
|
14860
14845
|
}
|
|
14861
|
-
if (phase.kind === "
|
|
14862
|
-
|
|
14863
|
-
|
|
14864
|
-
|
|
14865
|
-
|
|
14866
|
-
" Transaction ",
|
|
14867
|
-
created2.transactionId,
|
|
14868
|
-
" ready; replaying..."
|
|
14869
|
-
] }) });
|
|
14870
|
-
}
|
|
14871
|
-
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingY: 1, children: [
|
|
14872
|
-
/* @__PURE__ */ jsx15(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text13, { bold: true, children: "Approval required" }) }),
|
|
14873
|
-
/* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
|
|
14874
|
-
/* @__PURE__ */ jsx15(Text13, { children: `transaction: ${created2.transactionId}` }),
|
|
14875
|
-
created2.approvalUrl !== void 0 ? /* @__PURE__ */ jsxs12(Fragment, { children: [
|
|
14876
|
-
/* @__PURE__ */ jsxs12(Text13, { children: [
|
|
14877
|
-
"Open: ",
|
|
14878
|
-
/* @__PURE__ */ jsx15(Text13, { bold: true, color: "cyan", children: created2.approvalUrl })
|
|
14879
|
-
] }),
|
|
14880
|
-
/* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Press Enter to open in browser." }),
|
|
14881
|
-
/* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Press Escape to cancel." })
|
|
14882
|
-
] }) : 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
|
|
14883
14851
|
] }),
|
|
14884
|
-
/* @__PURE__ */
|
|
14885
|
-
/* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
|
|
14886
|
-
" Waiting for approval..."
|
|
14887
|
-
] }) })
|
|
14852
|
+
/* @__PURE__ */ jsx13(Text11, { color: "red", children: phase.message })
|
|
14888
14853
|
] });
|
|
14889
14854
|
}
|
|
14890
|
-
|
|
14891
|
-
|
|
14892
|
-
|
|
14893
|
-
|
|
14894
|
-
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
|
|
14898
|
-
|
|
14899
|
-
/* @__PURE__ */
|
|
14900
|
-
|
|
14901
|
-
|
|
14902
|
-
|
|
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
|
+
")"
|
|
14903
15428
|
] }),
|
|
14904
|
-
/* @__PURE__ */
|
|
14905
|
-
/* @__PURE__ */
|
|
14906
|
-
result.settled !== void 0 ? /* @__PURE__ */
|
|
14907
|
-
/* @__PURE__ */
|
|
14908
|
-
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: [
|
|
14909
15434
|
"Saved to: ",
|
|
14910
|
-
/* @__PURE__ */
|
|
15435
|
+
/* @__PURE__ */ jsx18(Text15, { bold: true, children: result.outputSavedTo })
|
|
14911
15436
|
] }) : null,
|
|
14912
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
14913
|
-
/* @__PURE__ */
|
|
14914
|
-
/* @__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 })
|
|
14915
15440
|
] }) : null
|
|
14916
15441
|
] });
|
|
14917
15442
|
}
|
|
14918
15443
|
if (phase.kind === "seller-rejected") {
|
|
14919
15444
|
const { result } = phase;
|
|
14920
|
-
return /* @__PURE__ */
|
|
14921
|
-
/* @__PURE__ */
|
|
14922
|
-
/* @__PURE__ */
|
|
14923
|
-
/* @__PURE__ */
|
|
14924
|
-
/* @__PURE__ */
|
|
14925
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
14926
|
-
/* @__PURE__ */
|
|
14927
|
-
/* @__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 })
|
|
14928
15453
|
] }) : null
|
|
14929
15454
|
] });
|
|
14930
15455
|
}
|
|
14931
15456
|
if (phase.kind === "no-payment-final") {
|
|
14932
15457
|
const { result } = phase;
|
|
14933
|
-
return /* @__PURE__ */
|
|
14934
|
-
/* @__PURE__ */
|
|
14935
|
-
/* @__PURE__ */
|
|
14936
|
-
/* @__PURE__ */
|
|
14937
|
-
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: [
|
|
14938
15463
|
"Saved to: ",
|
|
14939
|
-
/* @__PURE__ */
|
|
15464
|
+
/* @__PURE__ */ jsx18(Text15, { bold: true, children: result.outputSavedTo })
|
|
14940
15465
|
] }) : null,
|
|
14941
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
14942
|
-
/* @__PURE__ */
|
|
14943
|
-
/* @__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 })
|
|
14944
15469
|
] }) : null
|
|
14945
15470
|
] });
|
|
14946
15471
|
}
|
|
14947
|
-
return /* @__PURE__ */
|
|
14948
|
-
/* @__PURE__ */
|
|
15472
|
+
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
|
|
15473
|
+
/* @__PURE__ */ jsxs14(Text15, { color: "red", children: [
|
|
14949
15474
|
"\u2717 ",
|
|
14950
15475
|
phase.code
|
|
14951
15476
|
] }),
|
|
14952
|
-
/* @__PURE__ */
|
|
15477
|
+
/* @__PURE__ */ jsx18(Text15, { color: "red", children: phase.message })
|
|
14953
15478
|
] });
|
|
14954
15479
|
};
|
|
14955
15480
|
|
|
14956
15481
|
// src/commands/mpp/schema.ts
|
|
14957
|
-
import { z as
|
|
14958
|
-
var payArgs =
|
|
14959
|
-
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.")
|
|
14960
15485
|
});
|
|
14961
|
-
var payOptions =
|
|
14962
|
-
paymentMethod:
|
|
14963
|
-
intent:
|
|
14964
|
-
currency:
|
|
14965
|
-
rail:
|
|
14966
|
-
method:
|
|
14967
|
-
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(
|
|
14968
15493
|
"Request body. JSON or raw text. Content-Type defaults to application/json when --data is set unless a --header overrides it."
|
|
14969
15494
|
),
|
|
14970
|
-
header:
|
|
14971
|
-
interval:
|
|
15495
|
+
header: z5.array(z5.string()).default([]).describe('Repeatable. "Name: Value" format.'),
|
|
15496
|
+
interval: z5.coerce.number().default(0).describe(
|
|
14972
15497
|
"Inline poll cadence in seconds while a transaction is pending. 0 returns the transaction id and a follow-up command hint without blocking."
|
|
14973
15498
|
),
|
|
14974
|
-
maxAttempts:
|
|
14975
|
-
timeout:
|
|
14976
|
-
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(
|
|
14977
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."
|
|
14978
15503
|
),
|
|
14979
|
-
showBody:
|
|
15504
|
+
showBody: z5.boolean().default(true).describe(
|
|
14980
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)."
|
|
14981
15506
|
),
|
|
14982
|
-
outputFile:
|
|
15507
|
+
outputFile: z5.string().optional().describe(
|
|
14983
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)."
|
|
14984
15509
|
),
|
|
14985
|
-
credentialFile:
|
|
15510
|
+
credentialFile: z5.string().optional().describe(
|
|
14986
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."
|
|
14987
15512
|
)
|
|
14988
15513
|
});
|
|
14989
|
-
var statusArgs =
|
|
14990
|
-
transactionId:
|
|
15514
|
+
var statusArgs = z5.object({
|
|
15515
|
+
transactionId: z5.string().describe("The transaction id returned by `mpp pay`.")
|
|
14991
15516
|
});
|
|
14992
|
-
var statusOptions2 =
|
|
14993
|
-
interval:
|
|
15517
|
+
var statusOptions2 = z5.object({
|
|
15518
|
+
interval: z5.coerce.number().default(0).describe(
|
|
14994
15519
|
"Poll cadence in seconds. 0 returns the current snapshot; positive values yield on every change until ready or terminal."
|
|
14995
15520
|
),
|
|
14996
|
-
maxAttempts:
|
|
14997
|
-
timeout:
|
|
14998
|
-
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(
|
|
14999
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."
|
|
15000
15525
|
)
|
|
15001
15526
|
});
|
|
15002
|
-
var cancelArgs =
|
|
15003
|
-
approvalId:
|
|
15527
|
+
var cancelArgs = z5.object({
|
|
15528
|
+
approvalId: z5.string().describe("The approval id returned by `mpp pay` (on the pending frame).")
|
|
15004
15529
|
});
|
|
15005
|
-
var decodeArgs =
|
|
15006
|
-
value:
|
|
15530
|
+
var decodeArgs = z5.object({
|
|
15531
|
+
value: z5.string().describe(
|
|
15007
15532
|
"A raw `WWW-Authenticate: Payment` header value, or a base64url `Authorization: Payment` credential / `Payment-Receipt`. The kind is auto-detected."
|
|
15008
15533
|
)
|
|
15009
15534
|
});
|
|
15010
|
-
var
|
|
15011
|
-
url:
|
|
15535
|
+
var inspectArgs2 = z5.object({
|
|
15536
|
+
url: z5.string().describe("The MPP-protected resource URL to probe. No payment is made.")
|
|
15012
15537
|
});
|
|
15013
|
-
var
|
|
15014
|
-
paymentMethod:
|
|
15015
|
-
intent:
|
|
15016
|
-
currency:
|
|
15017
|
-
rail:
|
|
15018
|
-
method:
|
|
15019
|
-
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(
|
|
15020
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."
|
|
15021
15546
|
),
|
|
15022
|
-
header:
|
|
15547
|
+
header: z5.array(z5.string()).default([]).describe('Repeatable. "Name: Value" format.')
|
|
15023
15548
|
});
|
|
15024
15549
|
|
|
15025
15550
|
// src/commands/mpp/status.tsx
|
|
15026
|
-
import { Box as
|
|
15027
|
-
import
|
|
15028
|
-
import { useEffect as
|
|
15029
|
-
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";
|
|
15030
15555
|
var MppStatusView = ({
|
|
15031
15556
|
transactionId,
|
|
15032
15557
|
fetchOnce,
|
|
@@ -15036,9 +15561,9 @@ var MppStatusView = ({
|
|
|
15036
15561
|
onComplete
|
|
15037
15562
|
}) => {
|
|
15038
15563
|
const initial = { kind: "polling" };
|
|
15039
|
-
const [phase, dispatch] =
|
|
15564
|
+
const [phase, dispatch] = useReducer8(reduceMppStatus, initial);
|
|
15040
15565
|
const { finish } = useFlowExit(onComplete);
|
|
15041
|
-
|
|
15566
|
+
useEffect10(() => {
|
|
15042
15567
|
const run = runMppStatus({ fetchOnce, interval, maxAttempts, timeout });
|
|
15043
15568
|
let cancelled = false;
|
|
15044
15569
|
void (async () => {
|
|
@@ -15051,15 +15576,15 @@ var MppStatusView = ({
|
|
|
15051
15576
|
cancelled = true;
|
|
15052
15577
|
};
|
|
15053
15578
|
}, [fetchOnce, interval, maxAttempts, timeout]);
|
|
15054
|
-
|
|
15579
|
+
useEffect10(() => {
|
|
15055
15580
|
if (phase.kind === "ready" || phase.kind === "failed" || phase.kind === "expired" || phase.kind === "timeout" || phase.kind === "error") {
|
|
15056
15581
|
finish(phase);
|
|
15057
15582
|
}
|
|
15058
15583
|
}, [phase, finish]);
|
|
15059
15584
|
if (phase.kind === "polling") {
|
|
15060
15585
|
const stateText = phase.latest?.state ?? "pending";
|
|
15061
|
-
return /* @__PURE__ */
|
|
15062
|
-
/* @__PURE__ */
|
|
15586
|
+
return /* @__PURE__ */ jsx19(Box15, { flexDirection: "column", children: /* @__PURE__ */ jsxs15(Text16, { color: "cyan", children: [
|
|
15587
|
+
/* @__PURE__ */ jsx19(Spinner12, { type: "dots" }),
|
|
15063
15588
|
" Polling transaction ",
|
|
15064
15589
|
transactionId,
|
|
15065
15590
|
" (state: ",
|
|
@@ -15070,38 +15595,38 @@ var MppStatusView = ({
|
|
|
15070
15595
|
if (phase.kind === "ready") {
|
|
15071
15596
|
const credential = phase.response.credential ?? "";
|
|
15072
15597
|
const preview = credential.length > 32 ? `${credential.slice(0, 32)}...` : credential;
|
|
15073
|
-
return /* @__PURE__ */
|
|
15074
|
-
/* @__PURE__ */
|
|
15075
|
-
/* @__PURE__ */
|
|
15076
|
-
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
|
|
15077
15602
|
] });
|
|
15078
15603
|
}
|
|
15079
15604
|
if (phase.kind === "failed") {
|
|
15080
|
-
return /* @__PURE__ */
|
|
15081
|
-
/* @__PURE__ */
|
|
15082
|
-
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
|
|
15083
15608
|
] });
|
|
15084
15609
|
}
|
|
15085
15610
|
if (phase.kind === "expired") {
|
|
15086
|
-
return /* @__PURE__ */
|
|
15611
|
+
return /* @__PURE__ */ jsx19(Box15, { flexDirection: "column", children: /* @__PURE__ */ jsx19(Text16, { color: "yellow", children: "Transaction expired before it was ready." }) });
|
|
15087
15612
|
}
|
|
15088
15613
|
if (phase.kind === "timeout") {
|
|
15089
|
-
return /* @__PURE__ */
|
|
15090
|
-
/* @__PURE__ */
|
|
15091
|
-
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
|
|
15092
15617
|
] });
|
|
15093
15618
|
}
|
|
15094
|
-
return /* @__PURE__ */
|
|
15095
|
-
/* @__PURE__ */
|
|
15096
|
-
/* @__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 })
|
|
15097
15622
|
] });
|
|
15098
15623
|
};
|
|
15099
15624
|
|
|
15100
15625
|
// src/commands/mpp/supported.tsx
|
|
15101
|
-
import { Box as
|
|
15102
|
-
import
|
|
15626
|
+
import { Box as Box16, Text as Text17 } from "ink";
|
|
15627
|
+
import Spinner13 from "ink-spinner";
|
|
15103
15628
|
import { useCallback as useCallback6 } from "react";
|
|
15104
|
-
import { jsx as
|
|
15629
|
+
import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
15105
15630
|
function flattenKinds(response) {
|
|
15106
15631
|
const rows = [];
|
|
15107
15632
|
for (const kind of response.kinds) {
|
|
@@ -15118,7 +15643,7 @@ function flattenKinds(response) {
|
|
|
15118
15643
|
}
|
|
15119
15644
|
return rows;
|
|
15120
15645
|
}
|
|
15121
|
-
var
|
|
15646
|
+
var COLUMNS5 = [
|
|
15122
15647
|
{ header: "Method", cell: (r) => r.method },
|
|
15123
15648
|
{ header: "Intent", cell: (r) => r.intent },
|
|
15124
15649
|
{ header: "Rail", cell: (r) => r.rail },
|
|
@@ -15129,34 +15654,33 @@ var SupportedView = ({ load, onComplete }) => {
|
|
|
15129
15654
|
const { finish } = useFlowExit(onComplete);
|
|
15130
15655
|
const { status, data, error } = useFlowState(action, finish);
|
|
15131
15656
|
if (status === "loading") {
|
|
15132
|
-
return /* @__PURE__ */
|
|
15133
|
-
/* @__PURE__ */
|
|
15657
|
+
return /* @__PURE__ */ jsx20(Box16, { children: /* @__PURE__ */ jsxs16(Text17, { color: "cyan", children: [
|
|
15658
|
+
/* @__PURE__ */ jsx20(Spinner13, { type: "dots" }),
|
|
15134
15659
|
" Loading supported MPP methods..."
|
|
15135
15660
|
] }) });
|
|
15136
15661
|
}
|
|
15137
15662
|
if (status === "error") {
|
|
15138
|
-
return /* @__PURE__ */
|
|
15139
|
-
/* @__PURE__ */
|
|
15140
|
-
/* @__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 })
|
|
15141
15666
|
] });
|
|
15142
15667
|
}
|
|
15143
15668
|
const rows = data ? flattenKinds(data) : [];
|
|
15144
15669
|
if (rows.length === 0) {
|
|
15145
|
-
return /* @__PURE__ */
|
|
15670
|
+
return /* @__PURE__ */ jsx20(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx20(Text17, { children: "No supported MPP methods returned for this account." }) });
|
|
15146
15671
|
}
|
|
15147
|
-
return /* @__PURE__ */
|
|
15672
|
+
return /* @__PURE__ */ jsx20(Table, { columns: COLUMNS5, rows });
|
|
15148
15673
|
};
|
|
15149
15674
|
|
|
15150
15675
|
// src/commands/mpp/index.tsx
|
|
15151
|
-
import { jsx as
|
|
15676
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
15152
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.";
|
|
15153
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.";
|
|
15154
|
-
function
|
|
15155
|
-
|
|
15156
|
-
|
|
15157
|
-
|
|
15158
|
-
|
|
15159
|
-
}
|
|
15679
|
+
function invalidHeaderError(err) {
|
|
15680
|
+
return {
|
|
15681
|
+
code: "INVALID_HEADER",
|
|
15682
|
+
message: err instanceof Error ? err.message : String(err)
|
|
15683
|
+
};
|
|
15160
15684
|
}
|
|
15161
15685
|
function decorateCredentialField(frame, credential, credentialFile) {
|
|
15162
15686
|
if (credentialFile !== void 0 && credentialFile.length > 0) {
|
|
@@ -15169,16 +15693,15 @@ function decorateCredentialField(frame, credential, credentialFile) {
|
|
|
15169
15693
|
frame.credential = credential;
|
|
15170
15694
|
}
|
|
15171
15695
|
function probeOptionsFrom(c) {
|
|
15172
|
-
const headers = parseHeaderFlagsOrFail(c, c.options.header);
|
|
15173
15696
|
return {
|
|
15174
15697
|
method: c.options.method,
|
|
15175
|
-
headers,
|
|
15698
|
+
headers: parseHeaderFlags(c.options.header),
|
|
15176
15699
|
...c.options.data !== void 0 ? { data: c.options.data } : {}
|
|
15177
15700
|
};
|
|
15178
15701
|
}
|
|
15179
|
-
function buildPayPipelineInput(c) {
|
|
15702
|
+
function buildPayPipelineInput(c, probeOptions) {
|
|
15180
15703
|
return {
|
|
15181
|
-
probeOptions
|
|
15704
|
+
probeOptions,
|
|
15182
15705
|
url: c.args.url,
|
|
15183
15706
|
showBody: c.options.showBody,
|
|
15184
15707
|
interval: c.options.interval,
|
|
@@ -15280,17 +15803,23 @@ function toStatusFrame(response, credentialFile) {
|
|
|
15280
15803
|
}
|
|
15281
15804
|
async function* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2) {
|
|
15282
15805
|
assertSessionGuard(c, authStorage2, inflow2);
|
|
15806
|
+
let probeOptions;
|
|
15807
|
+
try {
|
|
15808
|
+
probeOptions = probeOptionsFrom(c);
|
|
15809
|
+
} catch (err) {
|
|
15810
|
+
return c.error(invalidHeaderError(err));
|
|
15811
|
+
}
|
|
15283
15812
|
if (!c.agent && !c.formatExplicit) {
|
|
15284
15813
|
const client = await inflow2.mpp.client();
|
|
15285
15814
|
let finalPhase = null;
|
|
15286
15815
|
await renderInkUntilExit(
|
|
15287
|
-
/* @__PURE__ */
|
|
15816
|
+
/* @__PURE__ */ jsx21(
|
|
15288
15817
|
PayView,
|
|
15289
15818
|
{
|
|
15290
15819
|
url: c.args.url,
|
|
15291
15820
|
method: c.options.method,
|
|
15292
15821
|
deps: {
|
|
15293
|
-
...buildPayPipelineInput(c),
|
|
15822
|
+
...buildPayPipelineInput(c, probeOptions),
|
|
15294
15823
|
client,
|
|
15295
15824
|
apiBaseUrl: apiBaseUrl2,
|
|
15296
15825
|
awaitPayment: true
|
|
@@ -15305,19 +15834,19 @@ async function* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2) {
|
|
|
15305
15834
|
if (finalPhase !== null) {
|
|
15306
15835
|
const phase = finalPhase;
|
|
15307
15836
|
if (phase.kind === "seller-rejected") {
|
|
15308
|
-
c.error({
|
|
15837
|
+
return c.error({
|
|
15309
15838
|
code: PAYMENT_NOT_ACCEPTED_CODE2,
|
|
15310
15839
|
message: `Seller rejected the credential with status ${String(phase.result.responseStatus)}. The transaction was ready but the seller did not honour the payment.`
|
|
15311
15840
|
});
|
|
15312
15841
|
}
|
|
15313
15842
|
if (phase.kind === "error") {
|
|
15314
|
-
c.error({ code: phase.code, message: phase.message });
|
|
15843
|
+
return c.error({ code: phase.code, message: phase.message });
|
|
15315
15844
|
}
|
|
15316
15845
|
}
|
|
15317
15846
|
return;
|
|
15318
15847
|
}
|
|
15319
15848
|
const run = inflow2.mpp.pay({
|
|
15320
|
-
...buildPayPipelineInput(c),
|
|
15849
|
+
...buildPayPipelineInput(c, probeOptions),
|
|
15321
15850
|
awaitPayment: c.options.interval > 0
|
|
15322
15851
|
});
|
|
15323
15852
|
for await (const event of run.events) {
|
|
@@ -15335,14 +15864,13 @@ async function* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2) {
|
|
|
15335
15864
|
}
|
|
15336
15865
|
if (event.type === "rejected") {
|
|
15337
15866
|
yield sanitizeDeep(rejectedFrameFromResult(event.result));
|
|
15338
|
-
c.error({
|
|
15867
|
+
return c.error({
|
|
15339
15868
|
code: PAYMENT_NOT_ACCEPTED_CODE2,
|
|
15340
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.`
|
|
15341
15870
|
});
|
|
15342
|
-
return;
|
|
15343
15871
|
}
|
|
15344
15872
|
if (event.type === "errored") {
|
|
15345
|
-
c.error({ code: event.code, message: event.message });
|
|
15873
|
+
return c.error({ code: event.code, message: event.message });
|
|
15346
15874
|
}
|
|
15347
15875
|
}
|
|
15348
15876
|
}
|
|
@@ -15351,7 +15879,7 @@ async function* runStatusCommand(c, inflow2, authStorage2) {
|
|
|
15351
15879
|
if (!c.agent && !c.formatExplicit) {
|
|
15352
15880
|
const client2 = await inflow2.mpp.client();
|
|
15353
15881
|
await renderInkUntilExit(
|
|
15354
|
-
/* @__PURE__ */
|
|
15882
|
+
/* @__PURE__ */ jsx21(
|
|
15355
15883
|
MppStatusView,
|
|
15356
15884
|
{
|
|
15357
15885
|
transactionId: c.args.transactionId,
|
|
@@ -15389,27 +15917,27 @@ async function* runStatusCommand(c, inflow2, authStorage2) {
|
|
|
15389
15917
|
}
|
|
15390
15918
|
if (event.type === "failed") {
|
|
15391
15919
|
yield sanitizeDeep(toStatusFrame(event.response, c.options.credentialFile));
|
|
15392
|
-
c.error({
|
|
15920
|
+
return c.error({
|
|
15393
15921
|
code: "PAYMENT_FAILED",
|
|
15394
15922
|
message: event.response.problem?.detail ?? event.response.problem?.title ?? "MPP transaction failed."
|
|
15395
15923
|
});
|
|
15396
15924
|
}
|
|
15397
15925
|
if (event.type === "expired") {
|
|
15398
15926
|
yield sanitizeDeep(toStatusFrame(event.response, c.options.credentialFile));
|
|
15399
|
-
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." });
|
|
15400
15928
|
}
|
|
15401
15929
|
if (event.type === "timedOut") {
|
|
15402
15930
|
if (event.response !== void 0) {
|
|
15403
15931
|
yield sanitizeDeep(toStatusFrame(event.response, c.options.credentialFile));
|
|
15404
15932
|
}
|
|
15405
|
-
c.error({
|
|
15933
|
+
return c.error({
|
|
15406
15934
|
code: "POLLING_TIMEOUT",
|
|
15407
15935
|
message: "Polling timed out before the transaction reached a ready state.",
|
|
15408
15936
|
retryable: true
|
|
15409
15937
|
});
|
|
15410
15938
|
}
|
|
15411
15939
|
if (event.type === "crashed") {
|
|
15412
|
-
c.error({ code: "PAYMENT_FAILED", message: event.message });
|
|
15940
|
+
return c.error({ code: "PAYMENT_FAILED", message: event.message });
|
|
15413
15941
|
}
|
|
15414
15942
|
}
|
|
15415
15943
|
}
|
|
@@ -15417,7 +15945,7 @@ async function runCancelCommand(c, inflow2, authStorage2) {
|
|
|
15417
15945
|
assertSessionGuard(c, authStorage2, inflow2);
|
|
15418
15946
|
if (!c.agent && !c.formatExplicit) {
|
|
15419
15947
|
await renderInkUntilExit(
|
|
15420
|
-
/* @__PURE__ */
|
|
15948
|
+
/* @__PURE__ */ jsx21(
|
|
15421
15949
|
CancelView,
|
|
15422
15950
|
{
|
|
15423
15951
|
approvalId: c.args.approvalId,
|
|
@@ -15438,7 +15966,7 @@ async function runDecodeCommand(c) {
|
|
|
15438
15966
|
return c.error({ code: "DECODE_FAILED", message: err instanceof Error ? err.message : String(err) });
|
|
15439
15967
|
}
|
|
15440
15968
|
if (!c.agent && !c.formatExplicit) {
|
|
15441
|
-
await renderInkUntilExit(/* @__PURE__ */
|
|
15969
|
+
await renderInkUntilExit(/* @__PURE__ */ jsx21(DecodeView, { result }));
|
|
15442
15970
|
return void 0;
|
|
15443
15971
|
}
|
|
15444
15972
|
return sanitizeDeep(result);
|
|
@@ -15446,15 +15974,21 @@ async function runDecodeCommand(c) {
|
|
|
15446
15974
|
async function runSupportedCommand(c, inflow2, authStorage2) {
|
|
15447
15975
|
assertSessionGuard(c, authStorage2, inflow2);
|
|
15448
15976
|
if (!c.agent && !c.formatExplicit) {
|
|
15449
|
-
await renderInkUntilExit(/* @__PURE__ */
|
|
15977
|
+
await renderInkUntilExit(/* @__PURE__ */ jsx21(SupportedView, { load: () => inflow2.mpp.supported(), onComplete: () => void 0 }));
|
|
15450
15978
|
return void 0;
|
|
15451
15979
|
}
|
|
15452
15980
|
const response = await inflow2.mpp.supported();
|
|
15453
15981
|
return sanitizeDeep(response);
|
|
15454
15982
|
}
|
|
15455
15983
|
async function runInspectCommand(c) {
|
|
15984
|
+
let probeOptions;
|
|
15985
|
+
try {
|
|
15986
|
+
probeOptions = probeOptionsFrom(c);
|
|
15987
|
+
} catch (err) {
|
|
15988
|
+
return c.error(invalidHeaderError(err));
|
|
15989
|
+
}
|
|
15456
15990
|
const deps = {
|
|
15457
|
-
probeOptions
|
|
15991
|
+
probeOptions,
|
|
15458
15992
|
url: c.args.url,
|
|
15459
15993
|
...c.options.paymentMethod !== void 0 ? { paymentMethodFilter: c.options.paymentMethod } : {},
|
|
15460
15994
|
...c.options.intent !== void 0 ? { intentFilter: c.options.intent } : {},
|
|
@@ -15464,7 +15998,7 @@ async function runInspectCommand(c) {
|
|
|
15464
15998
|
if (!c.agent && !c.formatExplicit) {
|
|
15465
15999
|
let finalPhase = null;
|
|
15466
16000
|
await renderInkUntilExit(
|
|
15467
|
-
/* @__PURE__ */
|
|
16001
|
+
/* @__PURE__ */ jsx21(
|
|
15468
16002
|
InspectView,
|
|
15469
16003
|
{
|
|
15470
16004
|
url: c.args.url,
|
|
@@ -15479,7 +16013,7 @@ async function runInspectCommand(c) {
|
|
|
15479
16013
|
if (finalPhase !== null) {
|
|
15480
16014
|
const phase = finalPhase;
|
|
15481
16015
|
if (phase.kind === "error") {
|
|
15482
|
-
c.error({ code: phase.code, message: phase.message });
|
|
16016
|
+
return c.error({ code: phase.code, message: phase.message });
|
|
15483
16017
|
}
|
|
15484
16018
|
}
|
|
15485
16019
|
return void 0;
|
|
@@ -15558,8 +16092,8 @@ function createMppCli(inflow2, authStorage2, apiBaseUrl2) {
|
|
|
15558
16092
|
});
|
|
15559
16093
|
cli2.command("inspect", {
|
|
15560
16094
|
description: "Show the seller's MPP challenge(s) for a URL. Read-only probe \u2014 no auth, no payment.",
|
|
15561
|
-
args:
|
|
15562
|
-
options:
|
|
16095
|
+
args: inspectArgs2,
|
|
16096
|
+
options: inspectOptions2,
|
|
15563
16097
|
outputPolicy: "agent-only",
|
|
15564
16098
|
async run(c) {
|
|
15565
16099
|
return runInspectCommand(c);
|
|
@@ -15573,10 +16107,10 @@ import { Cli as Cli5, Errors as Errors2 } from "incur";
|
|
|
15573
16107
|
import "react";
|
|
15574
16108
|
|
|
15575
16109
|
// src/commands/user/get.tsx
|
|
15576
|
-
import { Box as
|
|
15577
|
-
import
|
|
16110
|
+
import { Box as Box17, Text as Text18 } from "ink";
|
|
16111
|
+
import Spinner14 from "ink-spinner";
|
|
15578
16112
|
import { useCallback as useCallback7, useRef as useRef3 } from "react";
|
|
15579
|
-
import { jsx as
|
|
16113
|
+
import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
15580
16114
|
var UserGet = ({ userResource, onComplete }) => {
|
|
15581
16115
|
const action = useCallback7(() => userResource.retrieve(), [userResource]);
|
|
15582
16116
|
const { finish } = useFlowExit(onComplete);
|
|
@@ -15597,38 +16131,38 @@ var UserGet = ({ userResource, onComplete }) => {
|
|
|
15597
16131
|
const { status, data: user, error } = useFlowState(action, handleLinger);
|
|
15598
16132
|
lastErrorRef.current = error;
|
|
15599
16133
|
if (status === "loading") {
|
|
15600
|
-
return /* @__PURE__ */
|
|
15601
|
-
/* @__PURE__ */
|
|
16134
|
+
return /* @__PURE__ */ jsx22(Box17, { children: /* @__PURE__ */ jsxs17(Text18, { color: "cyan", children: [
|
|
16135
|
+
/* @__PURE__ */ jsx22(Spinner14, { type: "dots" }),
|
|
15602
16136
|
" Loading user..."
|
|
15603
16137
|
] }) });
|
|
15604
16138
|
}
|
|
15605
16139
|
if (status === "error") {
|
|
15606
|
-
return /* @__PURE__ */
|
|
15607
|
-
/* @__PURE__ */
|
|
15608
|
-
/* @__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 })
|
|
15609
16143
|
] });
|
|
15610
16144
|
}
|
|
15611
16145
|
if (user === null) return null;
|
|
15612
16146
|
const rows = buildProfileRows(user);
|
|
15613
|
-
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: [
|
|
15614
16148
|
`${row.label}: `,
|
|
15615
|
-
row.value !== null ? /* @__PURE__ */
|
|
16149
|
+
row.value !== null ? /* @__PURE__ */ jsx22(Text18, { bold: true, children: row.value }) : /* @__PURE__ */ jsx22(Text18, { dimColor: true, children: "\u2014" })
|
|
15616
16150
|
] }, row.label)) });
|
|
15617
16151
|
};
|
|
15618
16152
|
|
|
15619
16153
|
// src/commands/user/schema.ts
|
|
15620
|
-
import { z as
|
|
15621
|
-
var getOptions =
|
|
16154
|
+
import { z as z6 } from "incur";
|
|
16155
|
+
var getOptions = z6.object({});
|
|
15622
16156
|
|
|
15623
16157
|
// src/commands/user/index.tsx
|
|
15624
|
-
import { jsx as
|
|
16158
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
15625
16159
|
var TTY_NO_RESULT_MESSAGE = "inflow user get exited without producing a result.";
|
|
15626
16160
|
async function runUserGet2(c, deps) {
|
|
15627
16161
|
assertSessionGuard(c, deps.authStorage, deps.inflow);
|
|
15628
16162
|
if (!c.agent && !c.formatExplicit) {
|
|
15629
16163
|
let captured = null;
|
|
15630
16164
|
const outcome = await renderInkUntilExit(
|
|
15631
|
-
/* @__PURE__ */
|
|
16165
|
+
/* @__PURE__ */ jsx23(
|
|
15632
16166
|
UserGet,
|
|
15633
16167
|
{
|
|
15634
16168
|
userResource: deps.user,
|
|
@@ -15674,23 +16208,23 @@ import { resolve as resolvePath3 } from "path";
|
|
|
15674
16208
|
import { Cli as Cli6 } from "incur";
|
|
15675
16209
|
|
|
15676
16210
|
// src/commands/x402/cancel.tsx
|
|
15677
|
-
import { Box as
|
|
15678
|
-
import
|
|
16211
|
+
import { Box as Box18, Text as Text19 } from "ink";
|
|
16212
|
+
import Spinner15 from "ink-spinner";
|
|
15679
16213
|
import { useCallback as useCallback8 } from "react";
|
|
15680
|
-
import { jsx as
|
|
16214
|
+
import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
15681
16215
|
var CancelView2 = ({ approvalId, cancel, onComplete }) => {
|
|
15682
16216
|
const action = useCallback8(() => cancel(), [cancel]);
|
|
15683
16217
|
const { finish } = useFlowExit(onComplete);
|
|
15684
16218
|
const { status } = useFlowState(action, finish);
|
|
15685
16219
|
if (status === "loading") {
|
|
15686
|
-
return /* @__PURE__ */
|
|
15687
|
-
/* @__PURE__ */
|
|
16220
|
+
return /* @__PURE__ */ jsx24(Box18, { children: /* @__PURE__ */ jsxs18(Text19, { color: "cyan", children: [
|
|
16221
|
+
/* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
|
|
15688
16222
|
" Cancelling approval ",
|
|
15689
16223
|
approvalId,
|
|
15690
16224
|
"..."
|
|
15691
16225
|
] }) });
|
|
15692
16226
|
}
|
|
15693
|
-
return /* @__PURE__ */
|
|
16227
|
+
return /* @__PURE__ */ jsx24(Box18, { children: /* @__PURE__ */ jsxs18(Text19, { color: "green", children: [
|
|
15694
16228
|
"\u2713 Cancelled approval ",
|
|
15695
16229
|
approvalId,
|
|
15696
16230
|
" (best-effort)"
|
|
@@ -15698,166 +16232,44 @@ var CancelView2 = ({ approvalId, cancel, onComplete }) => {
|
|
|
15698
16232
|
};
|
|
15699
16233
|
|
|
15700
16234
|
// src/commands/x402/decode.tsx
|
|
15701
|
-
import { fromFoundationRequirements as
|
|
15702
|
-
import { Box as
|
|
15703
|
-
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";
|
|
15704
16238
|
var DecodeView2 = ({ decoded }) => {
|
|
15705
|
-
const summary = summarizeAccepts(
|
|
15706
|
-
return /* @__PURE__ */
|
|
15707
|
-
/* @__PURE__ */
|
|
15708
|
-
/* @__PURE__ */
|
|
15709
|
-
/* @__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: [
|
|
15710
16244
|
"x402Version: ",
|
|
15711
|
-
/* @__PURE__ */
|
|
16245
|
+
/* @__PURE__ */ jsx25(Text20, { color: "cyan", children: String(decoded.x402Version) })
|
|
15712
16246
|
] }),
|
|
15713
|
-
/* @__PURE__ */
|
|
16247
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
15714
16248
|
"resource: ",
|
|
15715
|
-
/* @__PURE__ */
|
|
16249
|
+
/* @__PURE__ */ jsx25(Text20, { color: "cyan", children: decoded.resource.url })
|
|
15716
16250
|
] }),
|
|
15717
|
-
/* @__PURE__ */
|
|
15718
|
-
summary.map((entry, idx) => /* @__PURE__ */
|
|
16251
|
+
/* @__PURE__ */ jsx25(Text20, { children: `accepts (${String(summary.length)}):` }),
|
|
16252
|
+
summary.map((entry, idx) => /* @__PURE__ */ jsxs19(Text20, { children: [
|
|
15719
16253
|
" ",
|
|
15720
|
-
/* @__PURE__ */
|
|
16254
|
+
/* @__PURE__ */ jsx25(Text20, { color: "yellow", children: entry.scheme }),
|
|
15721
16255
|
" / ",
|
|
15722
|
-
/* @__PURE__ */
|
|
16256
|
+
/* @__PURE__ */ jsx25(Text20, { color: "yellow", children: entry.network }),
|
|
15723
16257
|
entry.amount !== void 0 ? ` \xB7 amount ${entry.amount}` : "",
|
|
15724
16258
|
entry.asset !== void 0 ? ` \xB7 ${entry.asset}` : ""
|
|
15725
16259
|
] }, `${entry.scheme}-${entry.network}-${String(idx)}`)),
|
|
15726
|
-
decoded.extensions !== void 0 ? /* @__PURE__ */
|
|
16260
|
+
decoded.extensions !== void 0 ? /* @__PURE__ */ jsx25(Text20, { dimColor: true, children: `extensions: ${Object.keys(decoded.extensions).join(", ")}` }) : null
|
|
15727
16261
|
] })
|
|
15728
16262
|
] });
|
|
15729
16263
|
};
|
|
15730
16264
|
|
|
15731
|
-
// src/commands/x402/inspect.tsx
|
|
15732
|
-
import { Box as Box18, Text as Text19 } from "ink";
|
|
15733
|
-
import Spinner14 from "ink-spinner";
|
|
15734
|
-
import { useEffect as useEffect9, useReducer as useReducer7 } from "react";
|
|
15735
|
-
import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
15736
|
-
function summarizeExtra(extra) {
|
|
15737
|
-
if (extra === void 0) return "\u2014";
|
|
15738
|
-
const keys = Object.keys(extra);
|
|
15739
|
-
if (keys.length === 0) return "\u2014";
|
|
15740
|
-
return [...keys].sort((a, b) => a.localeCompare(b)).join(", ");
|
|
15741
|
-
}
|
|
15742
|
-
function formatTimeout(seconds) {
|
|
15743
|
-
return `${String(seconds)}s`;
|
|
15744
|
-
}
|
|
15745
|
-
function formatAsset(asset) {
|
|
15746
|
-
return asset === "" ? "\u2014" : asset;
|
|
15747
|
-
}
|
|
15748
|
-
var COLUMNS5 = [
|
|
15749
|
-
{ header: "Scheme", cell: (r) => r.scheme },
|
|
15750
|
-
{ header: "Network", cell: (r) => r.network },
|
|
15751
|
-
{ header: "Amount", cell: (r) => r.amount },
|
|
15752
|
-
{ header: "Asset", cell: (r) => formatAsset(r.asset) },
|
|
15753
|
-
{ header: "Pay To", cell: (r) => r.payTo },
|
|
15754
|
-
{ header: "Timeout", cell: (r) => formatTimeout(r.maxTimeoutSeconds) },
|
|
15755
|
-
{ header: "Extra", cell: (r) => summarizeExtra(r.extra) }
|
|
15756
|
-
];
|
|
15757
|
-
var InspectView2 = ({ url, method, deps, onComplete }) => {
|
|
15758
|
-
const initial = { kind: "probing" };
|
|
15759
|
-
const [phase, dispatch] = useReducer7(reduceX402Inspect, initial);
|
|
15760
|
-
const { finish } = useFlowExit(onComplete);
|
|
15761
|
-
useEffect9(() => {
|
|
15762
|
-
let cancelled = false;
|
|
15763
|
-
void runInspectPipeline(deps, (event) => {
|
|
15764
|
-
if (!cancelled) dispatch(event);
|
|
15765
|
-
});
|
|
15766
|
-
return () => {
|
|
15767
|
-
cancelled = true;
|
|
15768
|
-
};
|
|
15769
|
-
}, [deps]);
|
|
15770
|
-
useEffect9(() => {
|
|
15771
|
-
if (phase.kind === "accepts" || phase.kind === "no-payment" || phase.kind === "error") {
|
|
15772
|
-
finish(phase);
|
|
15773
|
-
}
|
|
15774
|
-
}, [phase, finish]);
|
|
15775
|
-
if (phase.kind === "probing") {
|
|
15776
|
-
return /* @__PURE__ */ jsx23(Box18, { children: /* @__PURE__ */ jsxs18(Text19, { color: "cyan", children: [
|
|
15777
|
-
/* @__PURE__ */ jsx23(Spinner14, { type: "dots" }),
|
|
15778
|
-
" Probing ",
|
|
15779
|
-
method,
|
|
15780
|
-
" ",
|
|
15781
|
-
url,
|
|
15782
|
-
"..."
|
|
15783
|
-
] }) });
|
|
15784
|
-
}
|
|
15785
|
-
if (phase.kind === "no-payment") {
|
|
15786
|
-
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
|
|
15787
|
-
/* @__PURE__ */ jsx23(Text19, { color: "green", children: "\u2713 Seller accepted without payment" }),
|
|
15788
|
-
/* @__PURE__ */ jsx23(Text19, { dimColor: true, children: "Use `x402 pay` to fetch the body." })
|
|
15789
|
-
] });
|
|
15790
|
-
}
|
|
15791
|
-
if (phase.kind === "error") {
|
|
15792
|
-
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
|
|
15793
|
-
/* @__PURE__ */ jsxs18(Text19, { color: "red", children: [
|
|
15794
|
-
"\u2717 ",
|
|
15795
|
-
phase.code
|
|
15796
|
-
] }),
|
|
15797
|
-
/* @__PURE__ */ jsx23(Text19, { color: "red", children: phase.message })
|
|
15798
|
-
] });
|
|
15799
|
-
}
|
|
15800
|
-
const { result } = phase;
|
|
15801
|
-
const acceptsCount = result.accepts.length;
|
|
15802
|
-
const extensionsLine = result.extensions !== void 0 ? Object.keys(result.extensions).join(", ") : null;
|
|
15803
|
-
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
|
|
15804
|
-
/* @__PURE__ */ jsxs18(Text19, { children: [
|
|
15805
|
-
/* @__PURE__ */ jsx23(Text19, { bold: true, children: "PAYMENT-REQUIRED" }),
|
|
15806
|
-
" for ",
|
|
15807
|
-
/* @__PURE__ */ jsx23(Text19, { color: "cyan", children: result.resource }),
|
|
15808
|
-
" \xB7 ",
|
|
15809
|
-
/* @__PURE__ */ jsx23(Text19, { dimColor: true, children: `x402Version ${String(result.x402Version)}` }),
|
|
15810
|
-
" \xB7 ",
|
|
15811
|
-
/* @__PURE__ */ jsx23(Text19, { dimColor: true, children: `${String(acceptsCount)} accept${acceptsCount === 1 ? "" : "s"}` })
|
|
15812
|
-
] }),
|
|
15813
|
-
extensionsLine !== null ? /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: `extensions: ${extensionsLine}` }) : null,
|
|
15814
|
-
/* @__PURE__ */ jsx23(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx23(Table, { columns: COLUMNS5, rows: result.accepts }) }),
|
|
15815
|
-
/* @__PURE__ */ jsx23(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: "Use --format json to inspect extras values." }) })
|
|
15816
|
-
] });
|
|
15817
|
-
};
|
|
15818
|
-
function buildAcceptsFrame(result) {
|
|
15819
|
-
const frame = {
|
|
15820
|
-
outcome: "accepts",
|
|
15821
|
-
url: result.url,
|
|
15822
|
-
method: result.method,
|
|
15823
|
-
resource: result.resource,
|
|
15824
|
-
x402_version: result.x402Version,
|
|
15825
|
-
accepts: result.accepts.map((entry) => {
|
|
15826
|
-
const row = {
|
|
15827
|
-
scheme: entry.scheme,
|
|
15828
|
-
network: entry.network,
|
|
15829
|
-
amount: entry.amount,
|
|
15830
|
-
asset: entry.asset,
|
|
15831
|
-
pay_to: entry.payTo,
|
|
15832
|
-
max_timeout_seconds: entry.maxTimeoutSeconds
|
|
15833
|
-
};
|
|
15834
|
-
if (entry.extra !== void 0) row.extra = entry.extra;
|
|
15835
|
-
return row;
|
|
15836
|
-
})
|
|
15837
|
-
};
|
|
15838
|
-
if (result.extensions !== void 0) frame.extensions = result.extensions;
|
|
15839
|
-
return frame;
|
|
15840
|
-
}
|
|
15841
|
-
function buildNoPaymentFrame2(result) {
|
|
15842
|
-
const frame = {
|
|
15843
|
-
outcome: "no-payment-required",
|
|
15844
|
-
url: result.url,
|
|
15845
|
-
method: result.method,
|
|
15846
|
-
status: result.status,
|
|
15847
|
-
body_size_bytes: result.bodySizeBytes
|
|
15848
|
-
};
|
|
15849
|
-
if (result.contentType !== void 0) frame.content_type = result.contentType;
|
|
15850
|
-
return frame;
|
|
15851
|
-
}
|
|
15852
|
-
|
|
15853
16265
|
// src/commands/x402/pay.tsx
|
|
15854
|
-
import { Box as
|
|
15855
|
-
import
|
|
15856
|
-
import { useEffect as
|
|
15857
|
-
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";
|
|
15858
16270
|
var PayView2 = ({ url, method, deps, onComplete, onCancel }) => {
|
|
15859
16271
|
const initial = { kind: "probing" };
|
|
15860
|
-
const [phase, dispatch] =
|
|
16272
|
+
const [phase, dispatch] = useReducer9(reducePay, initial);
|
|
15861
16273
|
const [cancelling, setCancelling] = useState4(false);
|
|
15862
16274
|
const { finish, cancelThenFinish } = useFlowExit(onComplete);
|
|
15863
16275
|
useInput4(
|
|
@@ -15879,7 +16291,7 @@ var PayView2 = ({ url, method, deps, onComplete, onCancel }) => {
|
|
|
15879
16291
|
},
|
|
15880
16292
|
{ isActive: phase.kind === "awaiting-approval" && !cancelling }
|
|
15881
16293
|
);
|
|
15882
|
-
|
|
16294
|
+
useEffect11(() => {
|
|
15883
16295
|
const controller = new AbortController();
|
|
15884
16296
|
let cancelled = false;
|
|
15885
16297
|
const runDeps = { ...deps, signOptions: { ...deps.signOptions, signal: controller.signal } };
|
|
@@ -15891,20 +16303,20 @@ var PayView2 = ({ url, method, deps, onComplete, onCancel }) => {
|
|
|
15891
16303
|
controller.abort();
|
|
15892
16304
|
};
|
|
15893
16305
|
}, [deps]);
|
|
15894
|
-
|
|
16306
|
+
useEffect11(() => {
|
|
15895
16307
|
if (phase.kind === "success" || phase.kind === "replay-rejected" || phase.kind === "no-payment-final" || phase.kind === "error") {
|
|
15896
16308
|
finish(phase);
|
|
15897
16309
|
}
|
|
15898
16310
|
}, [phase, finish]);
|
|
15899
16311
|
if (cancelling) {
|
|
15900
|
-
return /* @__PURE__ */
|
|
15901
|
-
/* @__PURE__ */
|
|
16312
|
+
return /* @__PURE__ */ jsx26(Box20, { children: /* @__PURE__ */ jsxs20(Text21, { color: "yellow", children: [
|
|
16313
|
+
/* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
|
|
15902
16314
|
" Cancelling approval..."
|
|
15903
16315
|
] }) });
|
|
15904
16316
|
}
|
|
15905
16317
|
if (phase.kind === "probing") {
|
|
15906
|
-
return /* @__PURE__ */
|
|
15907
|
-
/* @__PURE__ */
|
|
16318
|
+
return /* @__PURE__ */ jsx26(Box20, { children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
|
|
16319
|
+
/* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
|
|
15908
16320
|
" Probing ",
|
|
15909
16321
|
method,
|
|
15910
16322
|
" ",
|
|
@@ -15913,23 +16325,23 @@ var PayView2 = ({ url, method, deps, onComplete, onCancel }) => {
|
|
|
15913
16325
|
] }) });
|
|
15914
16326
|
}
|
|
15915
16327
|
if (phase.kind === "no-payment") {
|
|
15916
|
-
return /* @__PURE__ */
|
|
15917
|
-
/* @__PURE__ */
|
|
16328
|
+
return /* @__PURE__ */ jsx26(Box20, { children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
|
|
16329
|
+
/* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
|
|
15918
16330
|
" Seller accepted without payment (status ",
|
|
15919
16331
|
String(phase.probe.status),
|
|
15920
16332
|
"); finalising..."
|
|
15921
16333
|
] }) });
|
|
15922
16334
|
}
|
|
15923
16335
|
if (phase.kind === "matching") {
|
|
15924
|
-
return /* @__PURE__ */
|
|
15925
|
-
/* @__PURE__ */
|
|
16336
|
+
return /* @__PURE__ */ jsx26(Box20, { flexDirection: "column", children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
|
|
16337
|
+
/* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
|
|
15926
16338
|
" Decoding seller requirements..."
|
|
15927
16339
|
] }) });
|
|
15928
16340
|
}
|
|
15929
16341
|
if (phase.kind === "preparing") {
|
|
15930
16342
|
const summary = summarizeAccepts([phase.requirement]);
|
|
15931
|
-
return /* @__PURE__ */
|
|
15932
|
-
/* @__PURE__ */
|
|
16343
|
+
return /* @__PURE__ */ jsx26(Box20, { flexDirection: "column", children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
|
|
16344
|
+
/* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
|
|
15933
16345
|
" Preparing payment (",
|
|
15934
16346
|
summary[0]?.scheme ?? phase.requirement.scheme,
|
|
15935
16347
|
" /",
|
|
@@ -15939,167 +16351,167 @@ var PayView2 = ({ url, method, deps, onComplete, onCancel }) => {
|
|
|
15939
16351
|
] }) });
|
|
15940
16352
|
}
|
|
15941
16353
|
if (phase.kind === "awaiting-approval") {
|
|
15942
|
-
return /* @__PURE__ */
|
|
15943
|
-
/* @__PURE__ */
|
|
15944
|
-
/* @__PURE__ */
|
|
15945
|
-
/* @__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: [
|
|
15946
16358
|
"Open: ",
|
|
15947
|
-
/* @__PURE__ */
|
|
16359
|
+
/* @__PURE__ */ jsx26(Text21, { bold: true, color: "cyan", children: phase.approvalUrl })
|
|
15948
16360
|
] }),
|
|
15949
|
-
/* @__PURE__ */
|
|
15950
|
-
/* @__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." })
|
|
15951
16363
|
] }),
|
|
15952
|
-
/* @__PURE__ */
|
|
15953
|
-
/* @__PURE__ */
|
|
16364
|
+
/* @__PURE__ */ jsx26(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
|
|
16365
|
+
/* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
|
|
15954
16366
|
" Waiting for approval..."
|
|
15955
16367
|
] }) })
|
|
15956
16368
|
] });
|
|
15957
16369
|
}
|
|
15958
16370
|
if (phase.kind === "replaying") {
|
|
15959
|
-
return /* @__PURE__ */
|
|
15960
|
-
/* @__PURE__ */
|
|
16371
|
+
return /* @__PURE__ */ jsx26(Box20, { children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
|
|
16372
|
+
/* @__PURE__ */ jsx26(Spinner16, { type: "dots" }),
|
|
15961
16373
|
" Replaying request with PAYMENT-SIGNATURE..."
|
|
15962
16374
|
] }) });
|
|
15963
16375
|
}
|
|
15964
16376
|
if (phase.kind === "success") {
|
|
15965
16377
|
const { result } = phase;
|
|
15966
|
-
return /* @__PURE__ */
|
|
15967
|
-
/* @__PURE__ */
|
|
16378
|
+
return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
|
|
16379
|
+
/* @__PURE__ */ jsxs20(Text21, { color: "green", children: [
|
|
15968
16380
|
"\u2713 Paid ",
|
|
15969
16381
|
result.scheme,
|
|
15970
16382
|
" / ",
|
|
15971
16383
|
result.network
|
|
15972
16384
|
] }),
|
|
15973
|
-
/* @__PURE__ */
|
|
15974
|
-
result.settled?.network !== void 0 ? /* @__PURE__ */
|
|
15975
|
-
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: [
|
|
15976
16388
|
"Saved to: ",
|
|
15977
|
-
/* @__PURE__ */
|
|
16389
|
+
/* @__PURE__ */ jsx26(Text21, { bold: true, children: result.outputSavedTo })
|
|
15978
16390
|
] }) : null,
|
|
15979
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
15980
|
-
/* @__PURE__ */
|
|
15981
|
-
/* @__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 })
|
|
15982
16394
|
] }) : null
|
|
15983
16395
|
] });
|
|
15984
16396
|
}
|
|
15985
16397
|
if (phase.kind === "replay-rejected") {
|
|
15986
16398
|
const { result } = phase;
|
|
15987
|
-
return /* @__PURE__ */
|
|
15988
|
-
/* @__PURE__ */
|
|
16399
|
+
return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
|
|
16400
|
+
/* @__PURE__ */ jsxs20(Text21, { color: "red", children: [
|
|
15989
16401
|
"\u2717 Payment not accepted (",
|
|
15990
16402
|
result.scheme,
|
|
15991
16403
|
" / ",
|
|
15992
16404
|
result.network,
|
|
15993
16405
|
")"
|
|
15994
16406
|
] }),
|
|
15995
|
-
/* @__PURE__ */
|
|
15996
|
-
/* @__PURE__ */
|
|
15997
|
-
/* @__PURE__ */
|
|
15998
|
-
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: [
|
|
15999
16411
|
"Saved to: ",
|
|
16000
|
-
/* @__PURE__ */
|
|
16412
|
+
/* @__PURE__ */ jsx26(Text21, { bold: true, children: result.outputSavedTo })
|
|
16001
16413
|
] }) : null,
|
|
16002
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
16003
|
-
/* @__PURE__ */
|
|
16004
|
-
/* @__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 })
|
|
16005
16417
|
] }) : null
|
|
16006
16418
|
] });
|
|
16007
16419
|
}
|
|
16008
16420
|
if (phase.kind === "no-payment-final") {
|
|
16009
16421
|
const { result } = phase;
|
|
16010
|
-
return /* @__PURE__ */
|
|
16011
|
-
/* @__PURE__ */
|
|
16012
|
-
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: [
|
|
16013
16425
|
"Saved to: ",
|
|
16014
|
-
/* @__PURE__ */
|
|
16426
|
+
/* @__PURE__ */ jsx26(Text21, { bold: true, children: result.outputSavedTo })
|
|
16015
16427
|
] }) : null,
|
|
16016
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
16017
|
-
/* @__PURE__ */
|
|
16018
|
-
/* @__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 })
|
|
16019
16431
|
] }) : null
|
|
16020
16432
|
] });
|
|
16021
16433
|
}
|
|
16022
|
-
return /* @__PURE__ */
|
|
16023
|
-
/* @__PURE__ */
|
|
16434
|
+
return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
|
|
16435
|
+
/* @__PURE__ */ jsxs20(Text21, { color: "red", children: [
|
|
16024
16436
|
"\u2717 ",
|
|
16025
16437
|
phase.code
|
|
16026
16438
|
] }),
|
|
16027
|
-
/* @__PURE__ */
|
|
16439
|
+
/* @__PURE__ */ jsx26(Text21, { color: "red", children: phase.message })
|
|
16028
16440
|
] });
|
|
16029
16441
|
};
|
|
16030
16442
|
|
|
16031
16443
|
// src/commands/x402/schema.ts
|
|
16032
|
-
import { z as
|
|
16033
|
-
var payArgs2 =
|
|
16034
|
-
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.")
|
|
16035
16447
|
});
|
|
16036
|
-
var payOptions2 =
|
|
16037
|
-
scheme:
|
|
16038
|
-
network:
|
|
16039
|
-
asset:
|
|
16040
|
-
assetName:
|
|
16041
|
-
method:
|
|
16042
|
-
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(
|
|
16043
16455
|
"Request body. JSON or raw text. Content-Type defaults to application/json when --data is set unless a --header overrides it."
|
|
16044
16456
|
),
|
|
16045
|
-
header:
|
|
16046
|
-
interval:
|
|
16457
|
+
header: z7.array(z7.string()).default([]).describe('Repeatable. "Name: Value" format.'),
|
|
16458
|
+
interval: z7.coerce.number().default(0).describe(
|
|
16047
16459
|
"Inline poll cadence in seconds while awaiting approval. 0 returns the approval URL and a follow-up command hint without blocking."
|
|
16048
16460
|
),
|
|
16049
|
-
maxAttempts:
|
|
16050
|
-
timeout:
|
|
16051
|
-
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(
|
|
16052
16464
|
"Caller-supplied payment identifier. 16-128 chars, ^[a-zA-Z0-9_-]+$. Forwarded to the server as remotePaymentId."
|
|
16053
16465
|
),
|
|
16054
|
-
showBody:
|
|
16466
|
+
showBody: z7.boolean().default(true).describe(
|
|
16055
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)."
|
|
16056
16468
|
),
|
|
16057
|
-
outputFile:
|
|
16469
|
+
outputFile: z7.string().optional().describe(
|
|
16058
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)."
|
|
16059
16471
|
),
|
|
16060
|
-
payloadFile:
|
|
16472
|
+
payloadFile: z7.string().optional().describe(
|
|
16061
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."
|
|
16062
16474
|
)
|
|
16063
16475
|
});
|
|
16064
|
-
var statusArgs2 =
|
|
16065
|
-
transactionId:
|
|
16476
|
+
var statusArgs2 = z7.object({
|
|
16477
|
+
transactionId: z7.string().describe("The transaction id returned by `x402 pay`.")
|
|
16066
16478
|
});
|
|
16067
|
-
var statusOptions3 =
|
|
16068
|
-
interval:
|
|
16479
|
+
var statusOptions3 = z7.object({
|
|
16480
|
+
interval: z7.coerce.number().default(0).describe(
|
|
16069
16481
|
"Poll cadence in seconds. 0 returns the current snapshot; positive values yield on every change until signed or terminal."
|
|
16070
16482
|
),
|
|
16071
|
-
maxAttempts:
|
|
16072
|
-
timeout:
|
|
16073
|
-
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(
|
|
16074
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."
|
|
16075
16487
|
)
|
|
16076
16488
|
});
|
|
16077
|
-
var cancelArgs2 =
|
|
16078
|
-
approvalId:
|
|
16489
|
+
var cancelArgs2 = z7.object({
|
|
16490
|
+
approvalId: z7.string().describe("The approval id returned by `x402 pay`.")
|
|
16079
16491
|
});
|
|
16080
|
-
var decodeArgs2 =
|
|
16081
|
-
header:
|
|
16492
|
+
var decodeArgs2 = z7.object({
|
|
16493
|
+
header: z7.string().describe("Raw PAYMENT-REQUIRED header value (base64).")
|
|
16082
16494
|
});
|
|
16083
|
-
var
|
|
16084
|
-
url:
|
|
16495
|
+
var inspectArgs3 = z7.object({
|
|
16496
|
+
url: z7.string().describe("The x402-protected resource URL to probe. No payment is made.")
|
|
16085
16497
|
});
|
|
16086
|
-
var
|
|
16087
|
-
scheme:
|
|
16088
|
-
network:
|
|
16089
|
-
asset:
|
|
16090
|
-
assetName:
|
|
16091
|
-
method:
|
|
16092
|
-
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(
|
|
16093
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."
|
|
16094
16506
|
),
|
|
16095
|
-
header:
|
|
16507
|
+
header: z7.array(z7.string()).default([]).describe('Repeatable. "Name: Value" format.')
|
|
16096
16508
|
});
|
|
16097
16509
|
|
|
16098
16510
|
// src/commands/x402/status.tsx
|
|
16099
|
-
import { Box as
|
|
16100
|
-
import
|
|
16101
|
-
import { useEffect as
|
|
16102
|
-
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";
|
|
16103
16515
|
var X402StatusView = ({
|
|
16104
16516
|
transactionId,
|
|
16105
16517
|
fetchOnce,
|
|
@@ -16109,9 +16521,9 @@ var X402StatusView = ({
|
|
|
16109
16521
|
onComplete
|
|
16110
16522
|
}) => {
|
|
16111
16523
|
const initial = { kind: "polling" };
|
|
16112
|
-
const [phase, dispatch] =
|
|
16524
|
+
const [phase, dispatch] = useReducer10(reduceX402Status, initial);
|
|
16113
16525
|
const { finish } = useFlowExit(onComplete);
|
|
16114
|
-
|
|
16526
|
+
useEffect12(() => {
|
|
16115
16527
|
const run = runX402Status({ fetchOnce, interval, maxAttempts, timeout });
|
|
16116
16528
|
let cancelled = false;
|
|
16117
16529
|
void (async () => {
|
|
@@ -16124,15 +16536,15 @@ var X402StatusView = ({
|
|
|
16124
16536
|
cancelled = true;
|
|
16125
16537
|
};
|
|
16126
16538
|
}, [fetchOnce, interval, maxAttempts, timeout]);
|
|
16127
|
-
|
|
16539
|
+
useEffect12(() => {
|
|
16128
16540
|
if (phase.kind === "signed" || phase.kind === "failed" || phase.kind === "timeout" || phase.kind === "error") {
|
|
16129
16541
|
finish(phase);
|
|
16130
16542
|
}
|
|
16131
16543
|
}, [phase, finish]);
|
|
16132
16544
|
if (phase.kind === "polling") {
|
|
16133
16545
|
const statusText = phase.latest?.status ?? "pending";
|
|
16134
|
-
return /* @__PURE__ */
|
|
16135
|
-
/* @__PURE__ */
|
|
16546
|
+
return /* @__PURE__ */ jsx27(Box21, { flexDirection: "column", children: /* @__PURE__ */ jsxs21(Text22, { color: "cyan", children: [
|
|
16547
|
+
/* @__PURE__ */ jsx27(Spinner17, { type: "dots" }),
|
|
16136
16548
|
" Polling transaction ",
|
|
16137
16549
|
transactionId,
|
|
16138
16550
|
" (status: ",
|
|
@@ -16143,35 +16555,35 @@ var X402StatusView = ({
|
|
|
16143
16555
|
if (phase.kind === "signed") {
|
|
16144
16556
|
const encoded = phase.response.encodedPayload ?? "";
|
|
16145
16557
|
const preview = encoded.length > 32 ? `${encoded.slice(0, 32)}...` : encoded;
|
|
16146
|
-
return /* @__PURE__ */
|
|
16147
|
-
/* @__PURE__ */
|
|
16148
|
-
/* @__PURE__ */
|
|
16149
|
-
/* @__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}` })
|
|
16150
16562
|
] });
|
|
16151
16563
|
}
|
|
16152
16564
|
if (phase.kind === "failed") {
|
|
16153
|
-
return /* @__PURE__ */
|
|
16154
|
-
/* @__PURE__ */
|
|
16155
|
-
/* @__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}` })
|
|
16156
16568
|
] });
|
|
16157
16569
|
}
|
|
16158
16570
|
if (phase.kind === "timeout") {
|
|
16159
|
-
return /* @__PURE__ */
|
|
16160
|
-
/* @__PURE__ */
|
|
16161
|
-
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
|
|
16162
16574
|
] });
|
|
16163
16575
|
}
|
|
16164
|
-
return /* @__PURE__ */
|
|
16165
|
-
/* @__PURE__ */
|
|
16166
|
-
/* @__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 })
|
|
16167
16579
|
] });
|
|
16168
16580
|
};
|
|
16169
16581
|
|
|
16170
16582
|
// src/commands/x402/supported.tsx
|
|
16171
|
-
import { Box as
|
|
16172
|
-
import
|
|
16583
|
+
import { Box as Box22, Text as Text23 } from "ink";
|
|
16584
|
+
import Spinner18 from "ink-spinner";
|
|
16173
16585
|
import { useCallback as useCallback9 } from "react";
|
|
16174
|
-
import { jsx as
|
|
16586
|
+
import { jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
16175
16587
|
var COLUMNS6 = [
|
|
16176
16588
|
{ header: "Scheme", cell: (k) => k.scheme },
|
|
16177
16589
|
{ header: "Network", cell: (k) => k.network }
|
|
@@ -16181,26 +16593,26 @@ var SupportedView2 = ({ load, onComplete }) => {
|
|
|
16181
16593
|
const { finish } = useFlowExit(onComplete);
|
|
16182
16594
|
const { status, data, error } = useFlowState(action, finish);
|
|
16183
16595
|
if (status === "loading") {
|
|
16184
|
-
return /* @__PURE__ */
|
|
16185
|
-
/* @__PURE__ */
|
|
16596
|
+
return /* @__PURE__ */ jsx28(Box22, { children: /* @__PURE__ */ jsxs22(Text23, { color: "cyan", children: [
|
|
16597
|
+
/* @__PURE__ */ jsx28(Spinner18, { type: "dots" }),
|
|
16186
16598
|
" Loading supported schemes..."
|
|
16187
16599
|
] }) });
|
|
16188
16600
|
}
|
|
16189
16601
|
if (status === "error") {
|
|
16190
|
-
return /* @__PURE__ */
|
|
16191
|
-
/* @__PURE__ */
|
|
16192
|
-
/* @__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 })
|
|
16193
16605
|
] });
|
|
16194
16606
|
}
|
|
16195
16607
|
const kinds = data?.kinds ?? [];
|
|
16196
16608
|
if (kinds.length === 0) {
|
|
16197
|
-
return /* @__PURE__ */
|
|
16609
|
+
return /* @__PURE__ */ jsx28(Box22, { flexDirection: "column", children: /* @__PURE__ */ jsx28(Text23, { children: "No supported (scheme, network) pairs returned for this account." }) });
|
|
16198
16610
|
}
|
|
16199
|
-
return /* @__PURE__ */
|
|
16611
|
+
return /* @__PURE__ */ jsx28(Table, { columns: COLUMNS6, rows: kinds });
|
|
16200
16612
|
};
|
|
16201
16613
|
|
|
16202
16614
|
// src/commands/x402/index.tsx
|
|
16203
|
-
import { jsx as
|
|
16615
|
+
import { jsx as jsx29 } from "react/jsx-runtime";
|
|
16204
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.";
|
|
16205
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.";
|
|
16206
16618
|
function buildSignOptions(options) {
|
|
@@ -16209,15 +16621,11 @@ function buildSignOptions(options) {
|
|
|
16209
16621
|
if (options.paymentId !== void 0) out.paymentId = options.paymentId;
|
|
16210
16622
|
return out;
|
|
16211
16623
|
}
|
|
16212
|
-
function
|
|
16213
|
-
|
|
16214
|
-
|
|
16215
|
-
|
|
16216
|
-
|
|
16217
|
-
code: "INVALID_HEADER",
|
|
16218
|
-
message: err instanceof Error ? err.message : String(err)
|
|
16219
|
-
});
|
|
16220
|
-
}
|
|
16624
|
+
function invalidHeaderError2(err) {
|
|
16625
|
+
return {
|
|
16626
|
+
code: "INVALID_HEADER",
|
|
16627
|
+
message: err instanceof Error ? err.message : String(err)
|
|
16628
|
+
};
|
|
16221
16629
|
}
|
|
16222
16630
|
function decoratePayloadField(frame, encoded, payloadFile) {
|
|
16223
16631
|
if (payloadFile !== void 0 && payloadFile.length > 0) {
|
|
@@ -16229,13 +16637,14 @@ function decoratePayloadField(frame, encoded, payloadFile) {
|
|
|
16229
16637
|
}
|
|
16230
16638
|
frame.encoded_payload = encoded;
|
|
16231
16639
|
}
|
|
16232
|
-
function
|
|
16233
|
-
|
|
16234
|
-
const probeOptions = {
|
|
16640
|
+
function probeOptionsFrom2(c) {
|
|
16641
|
+
return {
|
|
16235
16642
|
method: c.options.method,
|
|
16236
|
-
headers:
|
|
16643
|
+
headers: parseHeaderFlags(c.options.header),
|
|
16237
16644
|
...c.options.data !== void 0 ? { data: c.options.data } : {}
|
|
16238
16645
|
};
|
|
16646
|
+
}
|
|
16647
|
+
function buildPayPipelineInput2(c, probeOptions) {
|
|
16239
16648
|
return {
|
|
16240
16649
|
probeOptions,
|
|
16241
16650
|
url: c.args.url,
|
|
@@ -16316,17 +16725,17 @@ function rejectedFrameFromResult2(result) {
|
|
|
16316
16725
|
}
|
|
16317
16726
|
async function* runPayCommand2(c, inflow2, authStorage2, apiBaseUrl2) {
|
|
16318
16727
|
assertSessionGuard(c, authStorage2, inflow2);
|
|
16728
|
+
let probeOptions;
|
|
16729
|
+
try {
|
|
16730
|
+
probeOptions = probeOptionsFrom2(c);
|
|
16731
|
+
} catch (err) {
|
|
16732
|
+
return c.error(invalidHeaderError2(err));
|
|
16733
|
+
}
|
|
16319
16734
|
if (!c.agent && !c.formatExplicit) {
|
|
16320
16735
|
const client = await inflow2.x402.client();
|
|
16321
|
-
const probeHeaders = parseHeaderFlagsOrFail2(c, c.options.header);
|
|
16322
|
-
const probeOptions = {
|
|
16323
|
-
method: c.options.method,
|
|
16324
|
-
headers: probeHeaders,
|
|
16325
|
-
...c.options.data !== void 0 ? { data: c.options.data } : {}
|
|
16326
|
-
};
|
|
16327
16736
|
let finalPhase = null;
|
|
16328
16737
|
await renderInkUntilExit(
|
|
16329
|
-
/* @__PURE__ */
|
|
16738
|
+
/* @__PURE__ */ jsx29(
|
|
16330
16739
|
PayView2,
|
|
16331
16740
|
{
|
|
16332
16741
|
url: c.args.url,
|
|
@@ -16354,19 +16763,19 @@ async function* runPayCommand2(c, inflow2, authStorage2, apiBaseUrl2) {
|
|
|
16354
16763
|
if (finalPhase !== null) {
|
|
16355
16764
|
const phase = finalPhase;
|
|
16356
16765
|
if (phase.kind === "replay-rejected") {
|
|
16357
|
-
c.error({
|
|
16766
|
+
return c.error({
|
|
16358
16767
|
code: PAYMENT_NOT_ACCEPTED_CODE,
|
|
16359
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.`
|
|
16360
16769
|
});
|
|
16361
16770
|
}
|
|
16362
16771
|
if (phase.kind === "error") {
|
|
16363
|
-
c.error({ code: phase.code, message: phase.message });
|
|
16772
|
+
return c.error({ code: phase.code, message: phase.message });
|
|
16364
16773
|
}
|
|
16365
16774
|
}
|
|
16366
16775
|
return;
|
|
16367
16776
|
}
|
|
16368
16777
|
const run = inflow2.x402.pay({
|
|
16369
|
-
...buildPayPipelineInput2(c),
|
|
16778
|
+
...buildPayPipelineInput2(c, probeOptions),
|
|
16370
16779
|
awaitPayment: c.options.interval > 0
|
|
16371
16780
|
});
|
|
16372
16781
|
for await (const event of run.events) {
|
|
@@ -16384,14 +16793,13 @@ async function* runPayCommand2(c, inflow2, authStorage2, apiBaseUrl2) {
|
|
|
16384
16793
|
}
|
|
16385
16794
|
if (event.type === "rejected") {
|
|
16386
16795
|
yield sanitizeDeep(rejectedFrameFromResult2(event.result));
|
|
16387
|
-
c.error({
|
|
16796
|
+
return c.error({
|
|
16388
16797
|
code: PAYMENT_NOT_ACCEPTED_CODE,
|
|
16389
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.`
|
|
16390
16799
|
});
|
|
16391
|
-
return;
|
|
16392
16800
|
}
|
|
16393
16801
|
if (event.type === "errored") {
|
|
16394
|
-
c.error({ code: event.code, message: event.message });
|
|
16802
|
+
return c.error({ code: event.code, message: event.message });
|
|
16395
16803
|
}
|
|
16396
16804
|
}
|
|
16397
16805
|
}
|
|
@@ -16400,7 +16808,7 @@ async function* runStatusCommand2(c, inflow2, authStorage2) {
|
|
|
16400
16808
|
if (!c.agent && !c.formatExplicit) {
|
|
16401
16809
|
const client2 = await inflow2.x402.client();
|
|
16402
16810
|
await renderInkUntilExit(
|
|
16403
|
-
/* @__PURE__ */
|
|
16811
|
+
/* @__PURE__ */ jsx29(
|
|
16404
16812
|
X402StatusView,
|
|
16405
16813
|
{
|
|
16406
16814
|
transactionId: c.args.transactionId,
|
|
@@ -16433,14 +16841,14 @@ async function* runStatusCommand2(c, inflow2, authStorage2) {
|
|
|
16433
16841
|
yield sanitizeDeep(toStatusFrame2(c.args.transactionId, outcome.value, c.options.payloadFile));
|
|
16434
16842
|
if (!outcome.terminal) continue;
|
|
16435
16843
|
if (outcome.reason !== void 0) {
|
|
16436
|
-
c.error({
|
|
16844
|
+
return c.error({
|
|
16437
16845
|
code: "POLLING_TIMEOUT",
|
|
16438
16846
|
message: outcome.reason === "timeout" ? "Polling timed out before the transaction reached a signed state." : "Reached the configured maximum poll attempts before signed state.",
|
|
16439
16847
|
retryable: true
|
|
16440
16848
|
});
|
|
16441
16849
|
}
|
|
16442
16850
|
if (classifyPayloadResponse(outcome.value) === "failed") {
|
|
16443
|
-
c.error({
|
|
16851
|
+
return c.error({
|
|
16444
16852
|
code: "APPROVAL_FAILED",
|
|
16445
16853
|
message: `Transaction ${c.args.transactionId} terminated as ${outcome.value.status} with no payload.`
|
|
16446
16854
|
});
|
|
@@ -16463,7 +16871,7 @@ async function runCancelCommand2(c, inflow2, authStorage2) {
|
|
|
16463
16871
|
assertSessionGuard(c, authStorage2, inflow2);
|
|
16464
16872
|
if (!c.agent && !c.formatExplicit) {
|
|
16465
16873
|
await renderInkUntilExit(
|
|
16466
|
-
/* @__PURE__ */
|
|
16874
|
+
/* @__PURE__ */ jsx29(
|
|
16467
16875
|
CancelView2,
|
|
16468
16876
|
{
|
|
16469
16877
|
approvalId: c.args.approvalId,
|
|
@@ -16485,13 +16893,13 @@ async function runDecodeCommand2(c) {
|
|
|
16485
16893
|
try {
|
|
16486
16894
|
decoded = decodeHeader(c.args.header);
|
|
16487
16895
|
} catch (err) {
|
|
16488
|
-
c.error({
|
|
16896
|
+
return c.error({
|
|
16489
16897
|
code: "DECODE_FAILED",
|
|
16490
16898
|
message: err instanceof Error ? err.message : String(err)
|
|
16491
16899
|
});
|
|
16492
16900
|
}
|
|
16493
16901
|
if (!c.agent && !c.formatExplicit) {
|
|
16494
|
-
await renderInkUntilExit(/* @__PURE__ */
|
|
16902
|
+
await renderInkUntilExit(/* @__PURE__ */ jsx29(DecodeView2, { decoded }));
|
|
16495
16903
|
return void 0;
|
|
16496
16904
|
}
|
|
16497
16905
|
return sanitizeDeep(decoded);
|
|
@@ -16499,19 +16907,19 @@ async function runDecodeCommand2(c) {
|
|
|
16499
16907
|
async function runSupportedCommand2(c, inflow2, authStorage2) {
|
|
16500
16908
|
assertSessionGuard(c, authStorage2, inflow2);
|
|
16501
16909
|
if (!c.agent && !c.formatExplicit) {
|
|
16502
|
-
await renderInkUntilExit(/* @__PURE__ */
|
|
16910
|
+
await renderInkUntilExit(/* @__PURE__ */ jsx29(SupportedView2, { load: () => inflow2.x402.supported(), onComplete: () => void 0 }));
|
|
16503
16911
|
return void 0;
|
|
16504
16912
|
}
|
|
16505
16913
|
const response = await inflow2.x402.supported();
|
|
16506
16914
|
return sanitizeDeep(response);
|
|
16507
16915
|
}
|
|
16508
16916
|
async function runInspectCommand2(c) {
|
|
16509
|
-
|
|
16510
|
-
|
|
16511
|
-
|
|
16512
|
-
|
|
16513
|
-
|
|
16514
|
-
}
|
|
16917
|
+
let probeOptions;
|
|
16918
|
+
try {
|
|
16919
|
+
probeOptions = probeOptionsFrom2(c);
|
|
16920
|
+
} catch (err) {
|
|
16921
|
+
return c.error(invalidHeaderError2(err));
|
|
16922
|
+
}
|
|
16515
16923
|
const deps = {
|
|
16516
16924
|
probeOptions,
|
|
16517
16925
|
url: c.args.url,
|
|
@@ -16523,7 +16931,7 @@ async function runInspectCommand2(c) {
|
|
|
16523
16931
|
if (!c.agent && !c.formatExplicit) {
|
|
16524
16932
|
let finalPhase = null;
|
|
16525
16933
|
await renderInkUntilExit(
|
|
16526
|
-
/* @__PURE__ */
|
|
16934
|
+
/* @__PURE__ */ jsx29(
|
|
16527
16935
|
InspectView2,
|
|
16528
16936
|
{
|
|
16529
16937
|
url: c.args.url,
|
|
@@ -16538,7 +16946,7 @@ async function runInspectCommand2(c) {
|
|
|
16538
16946
|
if (finalPhase !== null) {
|
|
16539
16947
|
const phase = finalPhase;
|
|
16540
16948
|
if (phase.kind === "error") {
|
|
16541
|
-
c.error({ code: phase.code, message: phase.message });
|
|
16949
|
+
return c.error({ code: phase.code, message: phase.message });
|
|
16542
16950
|
}
|
|
16543
16951
|
}
|
|
16544
16952
|
return void 0;
|
|
@@ -16558,12 +16966,12 @@ async function runInspectCommand2(c) {
|
|
|
16558
16966
|
}
|
|
16559
16967
|
});
|
|
16560
16968
|
if (finalEvent === null) {
|
|
16561
|
-
c.error({ code: "INSPECT_FAILED", message: "Inspect pipeline produced no result." });
|
|
16969
|
+
return c.error({ code: "INSPECT_FAILED", message: "Inspect pipeline produced no result." });
|
|
16562
16970
|
}
|
|
16563
16971
|
const { kind, payload } = finalEvent;
|
|
16564
16972
|
if (kind === "error") {
|
|
16565
16973
|
const err = payload;
|
|
16566
|
-
c.error({ code: err.code, message: err.message });
|
|
16974
|
+
return c.error({ code: err.code, message: err.message });
|
|
16567
16975
|
}
|
|
16568
16976
|
if (kind === "accepts") {
|
|
16569
16977
|
return sanitizeDeep(buildAcceptsFrame(payload));
|
|
@@ -16617,8 +17025,8 @@ function createX402Cli(inflow2, authStorage2, apiBaseUrl2) {
|
|
|
16617
17025
|
});
|
|
16618
17026
|
cli2.command("inspect", {
|
|
16619
17027
|
description: "Show the seller's PAYMENT-REQUIRED accepts for a URL. Read-only probe \u2014 no auth, no payment.",
|
|
16620
|
-
args:
|
|
16621
|
-
options:
|
|
17028
|
+
args: inspectArgs3,
|
|
17029
|
+
options: inspectOptions3,
|
|
16622
17030
|
outputPolicy: "agent-only",
|
|
16623
17031
|
async run(c) {
|
|
16624
17032
|
return runInspectCommand2(c);
|
|
@@ -16695,9 +17103,9 @@ function formatUpdateNotice(info) {
|
|
|
16695
17103
|
}
|
|
16696
17104
|
|
|
16697
17105
|
// src/cli.tsx
|
|
16698
|
-
var cliVersion = "0.6.
|
|
17106
|
+
var cliVersion = "0.6.5";
|
|
16699
17107
|
var cliName = "@inflowpayai/inflow";
|
|
16700
|
-
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';
|
|
16701
17109
|
if (process9.argv.includes("--skill")) {
|
|
16702
17110
|
process9.stdout.write(skillBody.endsWith("\n") ? skillBody : `${skillBody}
|
|
16703
17111
|
`);
|
|
@@ -16717,9 +17125,20 @@ function extractFlag(name) {
|
|
|
16717
17125
|
}
|
|
16718
17126
|
function extractBooleanFlag(name) {
|
|
16719
17127
|
const idx = process9.argv.indexOf(name);
|
|
16720
|
-
if (idx
|
|
16721
|
-
|
|
16722
|
-
|
|
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);
|
|
16723
17142
|
}
|
|
16724
17143
|
var credentialFilePath = extractFlag("--auth") ?? process9.env.INFLOW_AUTH_FILE;
|
|
16725
17144
|
var baseUrlFromFlag = extractFlag("--base-url");
|
|
@@ -16729,7 +17148,7 @@ var authBaseUrlFromFlag = extractFlag("--auth-base-url");
|
|
|
16729
17148
|
var environmentFromFlag = extractFlag("--environment");
|
|
16730
17149
|
var sandboxFlag = extractBooleanFlag("--sandbox");
|
|
16731
17150
|
var apiKeyFromFlag = extractFlag("--api-key");
|
|
16732
|
-
var verbose =
|
|
17151
|
+
var verbose = extractBooleanFlag("--verbose");
|
|
16733
17152
|
var authStorage = credentialFilePath ? new Storage({ configPath: credentialFilePath }) : storage;
|
|
16734
17153
|
var apiKeyFromEnv = process9.env.INFLOW_API_KEY;
|
|
16735
17154
|
function readSavedApiKey() {
|
|
@@ -16824,6 +17243,7 @@ cli.command(createBalancesCli(inflow.balances, authStorage, inflow));
|
|
|
16824
17243
|
cli.command(createDepositAddressesCli(inflow.depositAddresses, authStorage, inflow));
|
|
16825
17244
|
cli.command(createX402Cli(inflow, authStorage, resolvedApiBaseUrl));
|
|
16826
17245
|
cli.command(createMppCli(inflow, authStorage, resolvedApiBaseUrl));
|
|
17246
|
+
cli.command("inspect", createInspectCommand());
|
|
16827
17247
|
await cli.serve();
|
|
16828
17248
|
var cli_default = cli;
|
|
16829
17249
|
export {
|