@inflowpayai/inflow 0.5.2 → 0.6.1
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 +105 -37
- package/dist/cli.js +2111 -531
- package/package.json +6 -4
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 z7 = 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${z7} <${+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${z7} <${M}.${+m + 1}.0-0`;
|
|
8851
8851
|
} else {
|
|
8852
|
-
ret = `>=${M}.${m}.0${
|
|
8852
|
+
ret = `>=${M}.${m}.0${z7} <${+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}${z7} <${M}.${m}.${+p + 1}-0`;
|
|
8870
8870
|
} else {
|
|
8871
|
-
ret = `>=${M}.${m}.${p}${
|
|
8871
|
+
ret = `>=${M}.${m}.${p}${z7} <${M}.${+m + 1}.0-0`;
|
|
8872
8872
|
}
|
|
8873
8873
|
} else {
|
|
8874
8874
|
ret = `>=${M}.${m}.${p} <${+M + 1}.0.0-0`;
|
|
@@ -9687,6 +9687,10 @@ var require_semver2 = __commonJS({
|
|
|
9687
9687
|
import process9 from "process";
|
|
9688
9688
|
|
|
9689
9689
|
// ../core/dist/index.js
|
|
9690
|
+
import {
|
|
9691
|
+
MppClient
|
|
9692
|
+
} from "@inflowpayai/mpp";
|
|
9693
|
+
import { inflow as createInflowMppMethod } from "@inflowpayai/mpp-buyer";
|
|
9690
9694
|
import { createInflowClient } from "@inflowpayai/x402-buyer";
|
|
9691
9695
|
import { unlink } from "fs/promises";
|
|
9692
9696
|
import path5 from "path";
|
|
@@ -10959,6 +10963,7 @@ import { HEADERS, readHeader } from "@inflowpayai/x402";
|
|
|
10959
10963
|
import { fromFoundationRequirements } from "@inflowpayai/x402-buyer";
|
|
10960
10964
|
import { decodePaymentRequiredHeader } from "@x402/core/http";
|
|
10961
10965
|
import { sellerProbe } from "@inflowpayai/x402-buyer/probe";
|
|
10966
|
+
import { EXTRA_KEYS } from "@inflowpayai/x402";
|
|
10962
10967
|
import { writeFile } from "fs/promises";
|
|
10963
10968
|
import { resolve as resolvePath } from "path";
|
|
10964
10969
|
import { HEADERS as HEADERS2, readHeader as readHeader2 } from "@inflowpayai/x402";
|
|
@@ -10976,6 +10981,24 @@ import {
|
|
|
10976
10981
|
sellerProbe as sellerProbe2
|
|
10977
10982
|
} from "@inflowpayai/x402-buyer/probe";
|
|
10978
10983
|
import { decodePaymentRequiredHeader as decodePaymentRequiredHeader2 } from "@x402/core/http";
|
|
10984
|
+
import { HEADERS as HEADERS3, parseChallengeHeaders, readHeaderAll } from "@inflowpayai/mpp";
|
|
10985
|
+
import { sellerProbe as sellerProbe3 } from "@inflowpayai/x402-buyer/probe";
|
|
10986
|
+
import {
|
|
10987
|
+
decode,
|
|
10988
|
+
decodeCredential,
|
|
10989
|
+
decodeReceipt,
|
|
10990
|
+
parseChallengeHeader
|
|
10991
|
+
} from "@inflowpayai/mpp";
|
|
10992
|
+
import { METHOD_INFLOW } from "@inflowpayai/mpp";
|
|
10993
|
+
import {
|
|
10994
|
+
HEADERS as HEADERS4,
|
|
10995
|
+
parseChallengeHeaders as parseChallengeHeaders2,
|
|
10996
|
+
readHeader as readHeader3,
|
|
10997
|
+
readHeaderAll as readHeaderAll2,
|
|
10998
|
+
SCHEME_PAYMENT,
|
|
10999
|
+
decodeReceipt as decodeReceipt2
|
|
11000
|
+
} from "@inflowpayai/mpp";
|
|
11001
|
+
import { sellerProbe as sellerProbe4 } from "@inflowpayai/x402-buyer/probe";
|
|
10979
11002
|
import { hostname } from "os";
|
|
10980
11003
|
|
|
10981
11004
|
// ../../node_modules/.pnpm/ansi-regex@6.2.2/node_modules/ansi-regex/index.js
|
|
@@ -11005,7 +11028,7 @@ import {
|
|
|
11005
11028
|
parseHeaderFlag,
|
|
11006
11029
|
parseHeaderFlags,
|
|
11007
11030
|
replayWithPayment as replayWithPayment2,
|
|
11008
|
-
sellerProbe as
|
|
11031
|
+
sellerProbe as sellerProbe5,
|
|
11009
11032
|
X402HeaderFlagFormatError
|
|
11010
11033
|
} from "@inflowpayai/x402-buyer/probe";
|
|
11011
11034
|
var REDACTED_BODY_FIELDS = /* @__PURE__ */ new Set([
|
|
@@ -11668,11 +11691,11 @@ var NO_INFLOW_MATCH_MESSAGE = "Seller does not accept InFlow-signed payments. Us
|
|
|
11668
11691
|
var NO_FILTERED_MATCH_CODE = "NO_FILTERED_MATCH";
|
|
11669
11692
|
var PAYMENT_NOT_ACCEPTED_CODE = "PAYMENT_NOT_ACCEPTED";
|
|
11670
11693
|
var UNEXPECTED_PROBE_STATUS_CODE = "UNEXPECTED_PROBE_STATUS";
|
|
11671
|
-
function
|
|
11694
|
+
function extractAssetName(entry) {
|
|
11672
11695
|
const extra = entry.extra;
|
|
11673
11696
|
if (extra === void 0 || extra === null) return void 0;
|
|
11674
|
-
const
|
|
11675
|
-
return typeof
|
|
11697
|
+
const assetName = extra[EXTRA_KEYS.ASSET_NAME];
|
|
11698
|
+
return typeof assetName === "string" ? assetName : void 0;
|
|
11676
11699
|
}
|
|
11677
11700
|
function hasAnyFilter(filters) {
|
|
11678
11701
|
return filters.scheme !== void 0 || filters.network !== void 0 || filters.asset !== void 0 || filters.assetName !== void 0;
|
|
@@ -11683,7 +11706,7 @@ function filterAccepts(decoded, filters) {
|
|
|
11683
11706
|
return {
|
|
11684
11707
|
...decoded,
|
|
11685
11708
|
accepts: decoded.accepts.filter(
|
|
11686
|
-
(entry) => (scheme === void 0 || entry.scheme === scheme) && (network === void 0 || entry.network === network) && (asset === void 0 || entry.asset === asset) && (assetName === void 0 ||
|
|
11709
|
+
(entry) => (scheme === void 0 || entry.scheme === scheme) && (network === void 0 || entry.network === network) && (asset === void 0 || entry.asset === asset) && (assetName === void 0 || extractAssetName(entry) === assetName)
|
|
11687
11710
|
)
|
|
11688
11711
|
};
|
|
11689
11712
|
}
|
|
@@ -11698,8 +11721,8 @@ function buildNoFilteredMatchMessage(decoded, filters) {
|
|
|
11698
11721
|
const available = decoded.accepts.map((entry) => {
|
|
11699
11722
|
const parts = [`${entry.scheme}/${entry.network}`];
|
|
11700
11723
|
if (entry.asset !== void 0 && entry.asset !== "") parts.push(`asset=${entry.asset}`);
|
|
11701
|
-
const
|
|
11702
|
-
if (
|
|
11724
|
+
const assetName2 = extractAssetName(entry);
|
|
11725
|
+
if (assetName2 !== void 0 && assetName2 !== "") parts.push(`assetName=${assetName2}`);
|
|
11703
11726
|
return parts.join(" ");
|
|
11704
11727
|
}).join(", ");
|
|
11705
11728
|
return `Seller has no accepts[] entry matching ${filterDescription}. Available: ${available || "(none)"}.`;
|
|
@@ -11816,6 +11839,31 @@ function approvalUrlFor(apiBaseUrl2, approvalId) {
|
|
|
11816
11839
|
const host = dashboardHostFor(apiBaseUrl2);
|
|
11817
11840
|
return `https://${host}/approvals/${approvalId}/view/`;
|
|
11818
11841
|
}
|
|
11842
|
+
function userFacingApiError(err, fallbackCode) {
|
|
11843
|
+
if (isSdkApiError(err)) {
|
|
11844
|
+
const code = isMeaningfulCode(err.code) ? err.code : fallbackCode;
|
|
11845
|
+
return { code, message: stripDiagnosticPrefix(err.message) };
|
|
11846
|
+
}
|
|
11847
|
+
return { code: fallbackCode, message: err instanceof Error ? err.message : String(err) };
|
|
11848
|
+
}
|
|
11849
|
+
var UNCODED_SENTINEL = "UNEXPECTED_ERROR";
|
|
11850
|
+
function isMeaningfulCode(code) {
|
|
11851
|
+
return code.length > 0 && code !== UNCODED_SENTINEL;
|
|
11852
|
+
}
|
|
11853
|
+
function userFacingErrorMessage(err) {
|
|
11854
|
+
if (isSdkApiError(err)) return stripDiagnosticPrefix(err.message);
|
|
11855
|
+
return err instanceof Error ? err.message : String(err);
|
|
11856
|
+
}
|
|
11857
|
+
function isSdkApiError(err) {
|
|
11858
|
+
if (typeof err !== "object" || err === null) return false;
|
|
11859
|
+
const e = err;
|
|
11860
|
+
return typeof e.code === "string" && typeof e.endpoint === "string" && typeof e.message === "string";
|
|
11861
|
+
}
|
|
11862
|
+
function stripDiagnosticPrefix(message) {
|
|
11863
|
+
const separator = " \u2014 ";
|
|
11864
|
+
const index = message.indexOf(separator);
|
|
11865
|
+
return index >= 0 ? message.slice(index + separator.length) : message;
|
|
11866
|
+
}
|
|
11819
11867
|
function decodeHeader(raw) {
|
|
11820
11868
|
const parsed = decodePaymentRequiredHeader2(raw);
|
|
11821
11869
|
const decoded = {
|
|
@@ -11894,10 +11942,7 @@ function mapSdkError(err) {
|
|
|
11894
11942
|
if (err instanceof X402AdapterRoutingError) {
|
|
11895
11943
|
return { code: NO_INFLOW_MATCH_CODE, message: NO_INFLOW_MATCH_MESSAGE };
|
|
11896
11944
|
}
|
|
11897
|
-
return
|
|
11898
|
-
code: "PAY_FAILED",
|
|
11899
|
-
message: err instanceof Error ? err.message : String(err)
|
|
11900
|
-
};
|
|
11945
|
+
return userFacingApiError(err, "PAYMENT_FAILED");
|
|
11901
11946
|
}
|
|
11902
11947
|
function buildSettledMeta(headers) {
|
|
11903
11948
|
const responseHeader = readHeader2(Object.fromEntries(headers.entries()), HEADERS2.PAYMENT_RESPONSE);
|
|
@@ -12007,7 +12052,7 @@ async function runPayPipeline(deps, emit) {
|
|
|
12007
12052
|
});
|
|
12008
12053
|
return;
|
|
12009
12054
|
}
|
|
12010
|
-
const requirement = deps.client.selectInflowRequirement(filtered);
|
|
12055
|
+
const requirement = await deps.client.selectInflowRequirement(filtered);
|
|
12011
12056
|
if (requirement === null) {
|
|
12012
12057
|
emit({
|
|
12013
12058
|
type: "errored",
|
|
@@ -12159,7 +12204,7 @@ function runX402Status(input) {
|
|
|
12159
12204
|
return;
|
|
12160
12205
|
}
|
|
12161
12206
|
} catch (err) {
|
|
12162
|
-
yield { type: "crashed", message:
|
|
12207
|
+
yield { type: "crashed", message: userFacingErrorMessage(err) };
|
|
12163
12208
|
}
|
|
12164
12209
|
}
|
|
12165
12210
|
return { events: generate() };
|
|
@@ -12168,6 +12213,476 @@ async function runX402Supported(input) {
|
|
|
12168
12213
|
const client = await input.x402.client();
|
|
12169
12214
|
return client.getSupported();
|
|
12170
12215
|
}
|
|
12216
|
+
async function runMppCancel(input) {
|
|
12217
|
+
await input.mpp.cancelApproval(input.approvalId);
|
|
12218
|
+
return {
|
|
12219
|
+
approval_id: input.approvalId,
|
|
12220
|
+
cancelled: true,
|
|
12221
|
+
note: "best-effort; server-side state not verified"
|
|
12222
|
+
};
|
|
12223
|
+
}
|
|
12224
|
+
function decodeChallengeRequest(challenge) {
|
|
12225
|
+
if (challenge.request === "") return void 0;
|
|
12226
|
+
try {
|
|
12227
|
+
return decode(challenge.request, "challenge request");
|
|
12228
|
+
} catch {
|
|
12229
|
+
return void 0;
|
|
12230
|
+
}
|
|
12231
|
+
}
|
|
12232
|
+
function summarizeChallenge(challenge) {
|
|
12233
|
+
const out = {
|
|
12234
|
+
id: challenge.id,
|
|
12235
|
+
realm: challenge.realm,
|
|
12236
|
+
method: challenge.method,
|
|
12237
|
+
intent: challenge.intent
|
|
12238
|
+
};
|
|
12239
|
+
const request = decodeChallengeRequest(challenge);
|
|
12240
|
+
if (request !== void 0) {
|
|
12241
|
+
out.amount = request.amount;
|
|
12242
|
+
out.currency = request.currency;
|
|
12243
|
+
if (request.recipient !== void 0) out.recipient = request.recipient;
|
|
12244
|
+
if (request.methodDetails?.rail !== void 0) out.rail = request.methodDetails.rail;
|
|
12245
|
+
if (request.methodDetails?.instrumentId !== void 0) out.instrumentId = request.methodDetails.instrumentId;
|
|
12246
|
+
}
|
|
12247
|
+
if (challenge.expires !== void 0) out.expires = challenge.expires;
|
|
12248
|
+
if (challenge.description !== void 0) out.description = challenge.description;
|
|
12249
|
+
if (challenge.digest !== void 0) out.digest = challenge.digest;
|
|
12250
|
+
return out;
|
|
12251
|
+
}
|
|
12252
|
+
function decodeMppValue(raw) {
|
|
12253
|
+
const trimmed = raw.trim();
|
|
12254
|
+
if (/^payment\s+/i.test(trimmed) || /[a-zA-Z0-9-]+="/.test(trimmed)) {
|
|
12255
|
+
return { kind: "challenge", challenge: summarizeChallenge(parseChallengeHeader(trimmed)) };
|
|
12256
|
+
}
|
|
12257
|
+
const probe = decode(trimmed, "value");
|
|
12258
|
+
if ("challengeId" in probe) {
|
|
12259
|
+
return { kind: "receipt", receipt: decodeReceipt(trimmed) };
|
|
12260
|
+
}
|
|
12261
|
+
return { kind: "credential", credential: decodeCredential(trimmed) };
|
|
12262
|
+
}
|
|
12263
|
+
var INVALID_402_CODE2 = "INVALID_402";
|
|
12264
|
+
var NO_INFLOW_MATCH_CODE2 = "NO_INFLOW_MATCH";
|
|
12265
|
+
var NO_INFLOW_MATCH_MESSAGE2 = "Seller's 402 carries no `inflow`-method MPP challenge; the InFlow buyer cannot fulfil it.";
|
|
12266
|
+
var NO_FILTERED_MATCH_CODE2 = "NO_FILTERED_MATCH";
|
|
12267
|
+
var PAYMENT_NOT_ACCEPTED_CODE2 = "PAYMENT_NOT_ACCEPTED";
|
|
12268
|
+
var UNEXPECTED_PROBE_STATUS_CODE2 = "UNEXPECTED_PROBE_STATUS";
|
|
12269
|
+
function isSuccessStatus2(status) {
|
|
12270
|
+
return status >= 200 && status < 300;
|
|
12271
|
+
}
|
|
12272
|
+
function filterInflowChallenges(challenges) {
|
|
12273
|
+
return challenges.filter((challenge) => challenge.method === METHOD_INFLOW);
|
|
12274
|
+
}
|
|
12275
|
+
function hasAnyChallengeFilter(filters) {
|
|
12276
|
+
return filters.paymentMethod !== void 0 || filters.intent !== void 0 || filters.currency !== void 0 || filters.rail !== void 0;
|
|
12277
|
+
}
|
|
12278
|
+
function filterChallenges(challenges, filters) {
|
|
12279
|
+
if (!hasAnyChallengeFilter(filters)) return [...challenges];
|
|
12280
|
+
const { paymentMethod, intent, currency, rail } = filters;
|
|
12281
|
+
return challenges.filter((challenge) => {
|
|
12282
|
+
if (paymentMethod !== void 0 && challenge.method !== paymentMethod) return false;
|
|
12283
|
+
if (intent !== void 0 && challenge.intent !== intent) return false;
|
|
12284
|
+
if (currency !== void 0 || rail !== void 0) {
|
|
12285
|
+
const request = decodeChallengeRequest(challenge);
|
|
12286
|
+
if (currency !== void 0 && request?.currency !== currency) return false;
|
|
12287
|
+
if (rail !== void 0 && request?.methodDetails?.rail !== rail) return false;
|
|
12288
|
+
}
|
|
12289
|
+
return true;
|
|
12290
|
+
});
|
|
12291
|
+
}
|
|
12292
|
+
function buildNoFilteredMatchMessage2(challenges, filters) {
|
|
12293
|
+
const { paymentMethod, intent, currency, rail } = filters;
|
|
12294
|
+
const filterDescription = [
|
|
12295
|
+
paymentMethod !== void 0 ? `--payment-method=${paymentMethod}` : null,
|
|
12296
|
+
intent !== void 0 ? `--intent=${intent}` : null,
|
|
12297
|
+
currency !== void 0 ? `--currency=${currency}` : null,
|
|
12298
|
+
rail !== void 0 ? `--rail=${rail}` : null
|
|
12299
|
+
].filter((s) => s !== null).join(" ");
|
|
12300
|
+
const available = challenges.map((challenge) => {
|
|
12301
|
+
const request = decodeChallengeRequest(challenge);
|
|
12302
|
+
const parts = [`${challenge.method}/${challenge.intent}`];
|
|
12303
|
+
if (request?.currency !== void 0 && request.currency !== "") parts.push(`currency=${request.currency}`);
|
|
12304
|
+
const railValue = request?.methodDetails?.rail;
|
|
12305
|
+
if (railValue !== void 0 && railValue !== "") parts.push(`rail=${railValue}`);
|
|
12306
|
+
return parts.join(" ");
|
|
12307
|
+
}).join(", ");
|
|
12308
|
+
return `Seller has no \`inflow\` challenge matching ${filterDescription}. Available: ${available || "(none)"}.`;
|
|
12309
|
+
}
|
|
12310
|
+
function reduceMppInspect(state, event) {
|
|
12311
|
+
switch (event.type) {
|
|
12312
|
+
case "challenges":
|
|
12313
|
+
return { kind: "challenges", result: event.result };
|
|
12314
|
+
case "no-payment":
|
|
12315
|
+
return { kind: "no-payment", result: event.result };
|
|
12316
|
+
case "errored":
|
|
12317
|
+
return { kind: "error", code: event.code, message: event.message };
|
|
12318
|
+
default:
|
|
12319
|
+
return state;
|
|
12320
|
+
}
|
|
12321
|
+
}
|
|
12322
|
+
async function runMppInspectPipeline(deps, emit) {
|
|
12323
|
+
let probe;
|
|
12324
|
+
try {
|
|
12325
|
+
probe = await sellerProbe3(deps.url, deps.probeOptions);
|
|
12326
|
+
} catch (err) {
|
|
12327
|
+
emit({ type: "errored", code: "INSPECT_FAILED", message: err instanceof Error ? err.message : String(err) });
|
|
12328
|
+
return;
|
|
12329
|
+
}
|
|
12330
|
+
if (probe.status !== 402) {
|
|
12331
|
+
if (!isSuccessStatus2(probe.status)) {
|
|
12332
|
+
emit({
|
|
12333
|
+
type: "errored",
|
|
12334
|
+
code: UNEXPECTED_PROBE_STATUS_CODE2,
|
|
12335
|
+
message: `Seller returned status ${String(probe.status)} during probe; expected 2xx (no payment) or 402 (payment required).`
|
|
12336
|
+
});
|
|
12337
|
+
return;
|
|
12338
|
+
}
|
|
12339
|
+
emit({
|
|
12340
|
+
type: "no-payment",
|
|
12341
|
+
result: {
|
|
12342
|
+
outcome: "no-payment-required",
|
|
12343
|
+
url: deps.url,
|
|
12344
|
+
method: deps.probeOptions.method,
|
|
12345
|
+
status: probe.status,
|
|
12346
|
+
contentType: probe.contentType,
|
|
12347
|
+
bodySizeBytes: probe.bytes.byteLength
|
|
12348
|
+
}
|
|
12349
|
+
});
|
|
12350
|
+
return;
|
|
12351
|
+
}
|
|
12352
|
+
const headerValues = readHeaderAll(probe.headers, HEADERS3.WWW_AUTHENTICATE);
|
|
12353
|
+
if (headerValues.length === 0) {
|
|
12354
|
+
emit({
|
|
12355
|
+
type: "errored",
|
|
12356
|
+
code: INVALID_402_CODE2,
|
|
12357
|
+
message: "Seller returned 402 but did not include a WWW-Authenticate: Payment header."
|
|
12358
|
+
});
|
|
12359
|
+
return;
|
|
12360
|
+
}
|
|
12361
|
+
let challenges;
|
|
12362
|
+
try {
|
|
12363
|
+
challenges = parseChallengeHeaders(headerValues);
|
|
12364
|
+
} catch (err) {
|
|
12365
|
+
emit({ type: "errored", code: "DECODE_FAILED", message: err instanceof Error ? err.message : String(err) });
|
|
12366
|
+
return;
|
|
12367
|
+
}
|
|
12368
|
+
const inflowChallenges = filterInflowChallenges(challenges);
|
|
12369
|
+
if (inflowChallenges.length === 0) {
|
|
12370
|
+
emit({ type: "errored", code: NO_INFLOW_MATCH_CODE2, message: NO_INFLOW_MATCH_MESSAGE2 });
|
|
12371
|
+
return;
|
|
12372
|
+
}
|
|
12373
|
+
const filters = {
|
|
12374
|
+
...deps.paymentMethodFilter !== void 0 ? { paymentMethod: deps.paymentMethodFilter } : {},
|
|
12375
|
+
...deps.intentFilter !== void 0 ? { intent: deps.intentFilter } : {},
|
|
12376
|
+
...deps.currencyFilter !== void 0 ? { currency: deps.currencyFilter } : {},
|
|
12377
|
+
...deps.railFilter !== void 0 ? { rail: deps.railFilter } : {}
|
|
12378
|
+
};
|
|
12379
|
+
const filtered = filterChallenges(inflowChallenges, filters);
|
|
12380
|
+
if (hasAnyChallengeFilter(filters) && filtered.length === 0) {
|
|
12381
|
+
emit({
|
|
12382
|
+
type: "errored",
|
|
12383
|
+
code: NO_FILTERED_MATCH_CODE2,
|
|
12384
|
+
message: buildNoFilteredMatchMessage2(inflowChallenges, filters)
|
|
12385
|
+
});
|
|
12386
|
+
return;
|
|
12387
|
+
}
|
|
12388
|
+
const realm = filtered[0]?.realm ?? "";
|
|
12389
|
+
emit({
|
|
12390
|
+
type: "challenges",
|
|
12391
|
+
result: {
|
|
12392
|
+
outcome: "challenges",
|
|
12393
|
+
url: deps.url,
|
|
12394
|
+
method: deps.probeOptions.method,
|
|
12395
|
+
realm,
|
|
12396
|
+
challenges: filtered.map(summarizeChallenge)
|
|
12397
|
+
}
|
|
12398
|
+
});
|
|
12399
|
+
}
|
|
12400
|
+
function reduceMppPay(state, event) {
|
|
12401
|
+
switch (event.type) {
|
|
12402
|
+
case "decoded":
|
|
12403
|
+
return { kind: "decoded", challenge: event.challenge };
|
|
12404
|
+
case "created":
|
|
12405
|
+
return { kind: "created", created: event.created };
|
|
12406
|
+
case "replayed":
|
|
12407
|
+
return { kind: "success", result: event.result };
|
|
12408
|
+
case "rejected":
|
|
12409
|
+
return { kind: "seller-rejected", result: event.result };
|
|
12410
|
+
case "short-circuited":
|
|
12411
|
+
return { kind: "no-payment-final", result: event.result };
|
|
12412
|
+
case "errored":
|
|
12413
|
+
return { kind: "error", code: event.code, message: event.message };
|
|
12414
|
+
default:
|
|
12415
|
+
return state;
|
|
12416
|
+
}
|
|
12417
|
+
}
|
|
12418
|
+
function mapMppError(err) {
|
|
12419
|
+
return userFacingApiError(err, "PAYMENT_FAILED");
|
|
12420
|
+
}
|
|
12421
|
+
function buildSettlement(headers) {
|
|
12422
|
+
const raw = readHeader3(headers, HEADERS4.PAYMENT_RECEIPT);
|
|
12423
|
+
if (raw === void 0) return void 0;
|
|
12424
|
+
let receipt;
|
|
12425
|
+
try {
|
|
12426
|
+
receipt = decodeReceipt2(raw);
|
|
12427
|
+
} catch {
|
|
12428
|
+
return void 0;
|
|
12429
|
+
}
|
|
12430
|
+
const out = {};
|
|
12431
|
+
if (receipt.reference !== "") out.reference = receipt.reference;
|
|
12432
|
+
if (receipt.status !== "") out.status = receipt.status;
|
|
12433
|
+
if (receipt.timestamp !== "") out.timestamp = receipt.timestamp;
|
|
12434
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
12435
|
+
}
|
|
12436
|
+
async function resolveTransaction(client, transactionId, deps) {
|
|
12437
|
+
const generator = pollAsync({
|
|
12438
|
+
fn: () => client.getTransaction(transactionId),
|
|
12439
|
+
isTerminal: (response) => response.state !== "pending",
|
|
12440
|
+
isEqual: (a, b) => a.state === b.state,
|
|
12441
|
+
// A 0 interval reaches here only on the TTY path (the agent path gates inline polling on interval > 0); fall back
|
|
12442
|
+
// to a 5s cadence so the poll loop doesn't spin.
|
|
12443
|
+
interval: deps.interval > 0 ? deps.interval : 5,
|
|
12444
|
+
maxAttempts: deps.maxAttempts,
|
|
12445
|
+
timeout: deps.timeout,
|
|
12446
|
+
...deps.signal !== void 0 ? { signal: deps.signal } : {}
|
|
12447
|
+
});
|
|
12448
|
+
for await (const outcome of generator) {
|
|
12449
|
+
if (!outcome.terminal) continue;
|
|
12450
|
+
if (outcome.reason !== void 0) return { timedOut: true, latest: outcome.value };
|
|
12451
|
+
return { response: outcome.value };
|
|
12452
|
+
}
|
|
12453
|
+
return { timedOut: true };
|
|
12454
|
+
}
|
|
12455
|
+
async function runMppPayPipeline(deps, emit) {
|
|
12456
|
+
try {
|
|
12457
|
+
const probe = await sellerProbe4(deps.url, deps.probeOptions);
|
|
12458
|
+
if (probe.status !== 402) {
|
|
12459
|
+
if (!isSuccessStatus2(probe.status)) {
|
|
12460
|
+
emit({
|
|
12461
|
+
type: "errored",
|
|
12462
|
+
code: UNEXPECTED_PROBE_STATUS_CODE2,
|
|
12463
|
+
message: `Seller returned status ${String(probe.status)} during probe; expected 2xx (no payment) or 402 (payment required).`
|
|
12464
|
+
});
|
|
12465
|
+
return;
|
|
12466
|
+
}
|
|
12467
|
+
const attachment2 = await buildBodyAttachment(probe.bytes, deps.showBody, deps.outputFile);
|
|
12468
|
+
emit({
|
|
12469
|
+
type: "short-circuited",
|
|
12470
|
+
result: {
|
|
12471
|
+
outcome: "no-payment-required",
|
|
12472
|
+
url: deps.url,
|
|
12473
|
+
method: deps.probeOptions.method,
|
|
12474
|
+
status: probe.status,
|
|
12475
|
+
contentType: probe.contentType,
|
|
12476
|
+
...attachment2
|
|
12477
|
+
}
|
|
12478
|
+
});
|
|
12479
|
+
return;
|
|
12480
|
+
}
|
|
12481
|
+
const headerValues = readHeaderAll2(probe.headers, HEADERS4.WWW_AUTHENTICATE);
|
|
12482
|
+
if (headerValues.length === 0) {
|
|
12483
|
+
emit({
|
|
12484
|
+
type: "errored",
|
|
12485
|
+
code: INVALID_402_CODE2,
|
|
12486
|
+
message: "Seller returned 402 but did not include a WWW-Authenticate: Payment header."
|
|
12487
|
+
});
|
|
12488
|
+
return;
|
|
12489
|
+
}
|
|
12490
|
+
let challenges;
|
|
12491
|
+
try {
|
|
12492
|
+
challenges = parseChallengeHeaders2(headerValues);
|
|
12493
|
+
} catch (err) {
|
|
12494
|
+
emit({ type: "errored", code: "DECODE_FAILED", message: err instanceof Error ? err.message : String(err) });
|
|
12495
|
+
return;
|
|
12496
|
+
}
|
|
12497
|
+
const inflowChallenges = filterInflowChallenges(challenges);
|
|
12498
|
+
if (inflowChallenges.length === 0) {
|
|
12499
|
+
emit({ type: "errored", code: NO_INFLOW_MATCH_CODE2, message: NO_INFLOW_MATCH_MESSAGE2 });
|
|
12500
|
+
return;
|
|
12501
|
+
}
|
|
12502
|
+
const filters = {
|
|
12503
|
+
...deps.paymentMethodFilter !== void 0 ? { paymentMethod: deps.paymentMethodFilter } : {},
|
|
12504
|
+
...deps.intentFilter !== void 0 ? { intent: deps.intentFilter } : {},
|
|
12505
|
+
...deps.currencyFilter !== void 0 ? { currency: deps.currencyFilter } : {},
|
|
12506
|
+
...deps.railFilter !== void 0 ? { rail: deps.railFilter } : {}
|
|
12507
|
+
};
|
|
12508
|
+
const selected = filterChallenges(inflowChallenges, filters);
|
|
12509
|
+
if (hasAnyChallengeFilter(filters) && selected.length === 0) {
|
|
12510
|
+
emit({
|
|
12511
|
+
type: "errored",
|
|
12512
|
+
code: NO_FILTERED_MATCH_CODE2,
|
|
12513
|
+
message: buildNoFilteredMatchMessage2(inflowChallenges, filters)
|
|
12514
|
+
});
|
|
12515
|
+
return;
|
|
12516
|
+
}
|
|
12517
|
+
const challenge = selected[0];
|
|
12518
|
+
emit({ type: "decoded", challenge: summarizeChallenge(challenge) });
|
|
12519
|
+
const options = deps.instrumentId !== void 0 ? { instrumentId: deps.instrumentId } : {};
|
|
12520
|
+
let created;
|
|
12521
|
+
try {
|
|
12522
|
+
created = await deps.client.createTransaction({ challenge, options });
|
|
12523
|
+
} catch (err) {
|
|
12524
|
+
const mapped = mapMppError(err);
|
|
12525
|
+
emit({ type: "errored", code: mapped.code, message: mapped.message });
|
|
12526
|
+
return;
|
|
12527
|
+
}
|
|
12528
|
+
const createdFrame = {
|
|
12529
|
+
transactionId: created.transactionId ?? "",
|
|
12530
|
+
state: created.state,
|
|
12531
|
+
challenge: summarizeChallenge(challenge),
|
|
12532
|
+
...created.approvalId !== void 0 ? { approvalId: created.approvalId } : {},
|
|
12533
|
+
...created.approvalId !== void 0 ? { approvalUrl: approvalUrlFor(deps.apiBaseUrl, created.approvalId) } : {},
|
|
12534
|
+
...created.retryAfterSeconds !== void 0 ? { retryAfterSeconds: created.retryAfterSeconds } : {},
|
|
12535
|
+
...created.expires !== void 0 ? { expires: created.expires } : {}
|
|
12536
|
+
};
|
|
12537
|
+
emit({ type: "created", created: createdFrame });
|
|
12538
|
+
let resolved = created;
|
|
12539
|
+
if (created.state === "pending") {
|
|
12540
|
+
if (deps.awaitPayment === false) return;
|
|
12541
|
+
if (createdFrame.transactionId === "") {
|
|
12542
|
+
emit({
|
|
12543
|
+
type: "errored",
|
|
12544
|
+
code: "PAYMENT_FAILED",
|
|
12545
|
+
message: "Pending transaction carried no transactionId to poll."
|
|
12546
|
+
});
|
|
12547
|
+
return;
|
|
12548
|
+
}
|
|
12549
|
+
const outcome = await resolveTransaction(deps.client, createdFrame.transactionId, deps);
|
|
12550
|
+
if ("timedOut" in outcome) {
|
|
12551
|
+
emit({
|
|
12552
|
+
type: "errored",
|
|
12553
|
+
code: "POLLING_TIMEOUT",
|
|
12554
|
+
message: "Polling timed out before the transaction reached a ready state."
|
|
12555
|
+
});
|
|
12556
|
+
return;
|
|
12557
|
+
}
|
|
12558
|
+
resolved = outcome.response;
|
|
12559
|
+
}
|
|
12560
|
+
if (resolved.state === "failed") {
|
|
12561
|
+
emit({
|
|
12562
|
+
type: "errored",
|
|
12563
|
+
code: "PAYMENT_FAILED",
|
|
12564
|
+
message: resolved.problem?.detail ?? resolved.problem?.title ?? "MPP transaction failed."
|
|
12565
|
+
});
|
|
12566
|
+
return;
|
|
12567
|
+
}
|
|
12568
|
+
if (resolved.state === "expired") {
|
|
12569
|
+
emit({ type: "errored", code: "PAYMENT_EXPIRED", message: "MPP transaction expired before it was ready." });
|
|
12570
|
+
return;
|
|
12571
|
+
}
|
|
12572
|
+
if (resolved.state !== "ready" || resolved.credential === void 0) {
|
|
12573
|
+
emit({
|
|
12574
|
+
type: "errored",
|
|
12575
|
+
code: "PAYMENT_FAILED",
|
|
12576
|
+
message: "Transaction reached a ready state without a credential."
|
|
12577
|
+
});
|
|
12578
|
+
return;
|
|
12579
|
+
}
|
|
12580
|
+
const credential = resolved.credential;
|
|
12581
|
+
const replay = await sellerProbe4(deps.url, {
|
|
12582
|
+
method: deps.probeOptions.method,
|
|
12583
|
+
headers: { ...deps.probeOptions.headers, [HEADERS4.AUTHORIZATION]: `${SCHEME_PAYMENT} ${credential}` },
|
|
12584
|
+
...deps.probeOptions.data !== void 0 ? { data: deps.probeOptions.data } : {}
|
|
12585
|
+
});
|
|
12586
|
+
const attachment = await buildBodyAttachment(replay.bytes, deps.showBody, deps.outputFile);
|
|
12587
|
+
if (!isSuccessStatus2(replay.status)) {
|
|
12588
|
+
emit({
|
|
12589
|
+
type: "rejected",
|
|
12590
|
+
result: {
|
|
12591
|
+
outcome: "seller-rejected",
|
|
12592
|
+
url: deps.url,
|
|
12593
|
+
method: deps.probeOptions.method,
|
|
12594
|
+
transactionId: createdFrame.transactionId,
|
|
12595
|
+
challengeId: challenge.id,
|
|
12596
|
+
responseStatus: replay.status,
|
|
12597
|
+
responseContentType: replay.contentType,
|
|
12598
|
+
...attachment
|
|
12599
|
+
}
|
|
12600
|
+
});
|
|
12601
|
+
return;
|
|
12602
|
+
}
|
|
12603
|
+
const settled = buildSettlement(replay.headers);
|
|
12604
|
+
emit({
|
|
12605
|
+
type: "replayed",
|
|
12606
|
+
result: {
|
|
12607
|
+
outcome: "paid",
|
|
12608
|
+
url: deps.url,
|
|
12609
|
+
method: deps.probeOptions.method,
|
|
12610
|
+
transactionId: createdFrame.transactionId,
|
|
12611
|
+
challengeId: challenge.id,
|
|
12612
|
+
intent: challenge.intent,
|
|
12613
|
+
credential,
|
|
12614
|
+
responseStatus: replay.status,
|
|
12615
|
+
responseContentType: replay.contentType,
|
|
12616
|
+
...settled !== void 0 ? { settled } : {},
|
|
12617
|
+
...attachment
|
|
12618
|
+
}
|
|
12619
|
+
});
|
|
12620
|
+
} catch (err) {
|
|
12621
|
+
const mapped = mapMppError(err);
|
|
12622
|
+
emit({ type: "errored", code: mapped.code, message: mapped.message });
|
|
12623
|
+
}
|
|
12624
|
+
}
|
|
12625
|
+
var TERMINAL_STATES = /* @__PURE__ */ new Set(["expired", "failed", "ready"]);
|
|
12626
|
+
function reduceMppStatus(state, event) {
|
|
12627
|
+
switch (event.type) {
|
|
12628
|
+
case "snapshot":
|
|
12629
|
+
return { kind: "polling", latest: event.response };
|
|
12630
|
+
case "ready":
|
|
12631
|
+
return { kind: "ready", response: event.response };
|
|
12632
|
+
case "failed":
|
|
12633
|
+
return { kind: "failed", response: event.response };
|
|
12634
|
+
case "expired":
|
|
12635
|
+
return { kind: "expired", response: event.response };
|
|
12636
|
+
case "timedOut":
|
|
12637
|
+
return event.response !== void 0 ? { kind: "timeout", response: event.response } : { kind: "timeout" };
|
|
12638
|
+
case "crashed":
|
|
12639
|
+
return { kind: "error", message: event.message };
|
|
12640
|
+
default:
|
|
12641
|
+
return state;
|
|
12642
|
+
}
|
|
12643
|
+
}
|
|
12644
|
+
function runMppStatus(input) {
|
|
12645
|
+
async function* generate() {
|
|
12646
|
+
try {
|
|
12647
|
+
const generator = pollAsync({
|
|
12648
|
+
fn: input.fetchOnce,
|
|
12649
|
+
isTerminal: (response) => TERMINAL_STATES.has(response.state),
|
|
12650
|
+
isEqual: (a, b) => a.state === b.state && a.credential !== void 0 === (b.credential !== void 0),
|
|
12651
|
+
interval: input.interval,
|
|
12652
|
+
maxAttempts: input.maxAttempts,
|
|
12653
|
+
timeout: input.timeout
|
|
12654
|
+
});
|
|
12655
|
+
for await (const outcome of generator) {
|
|
12656
|
+
if (!outcome.terminal) {
|
|
12657
|
+
yield { type: "snapshot", response: outcome.value };
|
|
12658
|
+
continue;
|
|
12659
|
+
}
|
|
12660
|
+
if (outcome.reason !== void 0) {
|
|
12661
|
+
yield { type: "timedOut", response: outcome.value };
|
|
12662
|
+
return;
|
|
12663
|
+
}
|
|
12664
|
+
const state = outcome.value.state;
|
|
12665
|
+
if (state === "ready") {
|
|
12666
|
+
yield { type: "ready", response: outcome.value };
|
|
12667
|
+
return;
|
|
12668
|
+
}
|
|
12669
|
+
if (state === "expired") {
|
|
12670
|
+
yield { type: "expired", response: outcome.value };
|
|
12671
|
+
return;
|
|
12672
|
+
}
|
|
12673
|
+
yield { type: "failed", response: outcome.value };
|
|
12674
|
+
return;
|
|
12675
|
+
}
|
|
12676
|
+
} catch (err) {
|
|
12677
|
+
yield { type: "crashed", message: userFacingErrorMessage(err) };
|
|
12678
|
+
}
|
|
12679
|
+
}
|
|
12680
|
+
return { events: generate() };
|
|
12681
|
+
}
|
|
12682
|
+
async function runMppSupported(input) {
|
|
12683
|
+
const client = await input.mpp.client();
|
|
12684
|
+
return client.getSupported();
|
|
12685
|
+
}
|
|
12171
12686
|
function wrapEmittingPipeline(run) {
|
|
12172
12687
|
const buffer = [];
|
|
12173
12688
|
let started = false;
|
|
@@ -12308,16 +12823,43 @@ function augmentX402(x402Resource, resolvedApiBaseUrl2) {
|
|
|
12308
12823
|
augmented.cancel = async (input) => runX402Cancel({ x402: x402Resource, approvalId: input.approvalId });
|
|
12309
12824
|
return augmented;
|
|
12310
12825
|
}
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12826
|
+
function augmentMpp(mppResource, resolvedApiBaseUrl2) {
|
|
12827
|
+
const augmented = mppResource;
|
|
12828
|
+
augmented.inspect = (input) => wrapEmittingPipeline((emit) => runMppInspectPipeline(input, emit));
|
|
12829
|
+
augmented.supported = async () => runMppSupported({ mpp: mppResource });
|
|
12830
|
+
augmented.pay = (input) => wrapEmittingPipeline(async (emit) => {
|
|
12831
|
+
const client = await mppResource.client();
|
|
12832
|
+
return runMppPayPipeline(
|
|
12833
|
+
{
|
|
12834
|
+
...input,
|
|
12835
|
+
client,
|
|
12836
|
+
apiBaseUrl: input.apiBaseUrl ?? resolvedApiBaseUrl2
|
|
12837
|
+
},
|
|
12838
|
+
emit
|
|
12839
|
+
);
|
|
12840
|
+
});
|
|
12841
|
+
augmented.status = (input) => runMppStatus({
|
|
12842
|
+
fetchOnce: async () => {
|
|
12843
|
+
const client = await mppResource.client();
|
|
12844
|
+
return client.getTransaction(input.transactionId);
|
|
12845
|
+
},
|
|
12846
|
+
interval: input.interval,
|
|
12847
|
+
maxAttempts: input.maxAttempts,
|
|
12848
|
+
timeout: input.timeout
|
|
12849
|
+
});
|
|
12850
|
+
augmented.cancel = async (input) => runMppCancel({ mpp: mppResource, approvalId: input.approvalId });
|
|
12851
|
+
return augmented;
|
|
12852
|
+
}
|
|
12853
|
+
var DEFAULT_RETRIES = 3;
|
|
12854
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
12855
|
+
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
|
|
12856
|
+
var SDK_USER_AGENT = `@inflowpayai/inflow-core/${true ? "0.6.1" : "0.0.0"}`;
|
|
12857
|
+
function sleep2(ms, signal) {
|
|
12858
|
+
return new Promise((resolve, reject) => {
|
|
12859
|
+
const onAbort = () => {
|
|
12860
|
+
clearTimeout(handle);
|
|
12861
|
+
reject(new InflowTransportError("Request aborted."));
|
|
12862
|
+
};
|
|
12321
12863
|
if (signal?.aborted) {
|
|
12322
12864
|
reject(new InflowTransportError("Request aborted."));
|
|
12323
12865
|
return;
|
|
@@ -12827,12 +13369,33 @@ var X402Resource = class {
|
|
|
12827
13369
|
return this.cached;
|
|
12828
13370
|
}
|
|
12829
13371
|
};
|
|
13372
|
+
var MppResource = class {
|
|
13373
|
+
constructor(opts) {
|
|
13374
|
+
this.opts = opts;
|
|
13375
|
+
}
|
|
13376
|
+
opts;
|
|
13377
|
+
cachedClient;
|
|
13378
|
+
cachedMethod;
|
|
13379
|
+
client() {
|
|
13380
|
+
if (!this.cachedClient) {
|
|
13381
|
+
this.cachedClient = Promise.resolve(new MppClient(this.opts));
|
|
13382
|
+
}
|
|
13383
|
+
return this.cachedClient;
|
|
13384
|
+
}
|
|
13385
|
+
cancelApproval(approvalId) {
|
|
13386
|
+
if (!this.cachedMethod) {
|
|
13387
|
+
this.cachedMethod = createInflowMppMethod(this.opts);
|
|
13388
|
+
}
|
|
13389
|
+
return this.cachedMethod.cancelApproval(approvalId);
|
|
13390
|
+
}
|
|
13391
|
+
};
|
|
12830
13392
|
var Inflow = class {
|
|
12831
13393
|
auth;
|
|
12832
13394
|
balances;
|
|
12833
13395
|
depositAddresses;
|
|
12834
13396
|
user;
|
|
12835
13397
|
x402;
|
|
13398
|
+
mpp;
|
|
12836
13399
|
/**
|
|
12837
13400
|
* The effective API base URL this client will hit, after resolution against `options.apiBaseUrl`, `INFLOW_BASE_URL`,
|
|
12838
13401
|
* and the environment-derived default. Exposed for callers (CLI, MCP transports, etc.) that need to display "what URL
|
|
@@ -12855,6 +13418,8 @@ var Inflow = class {
|
|
|
12855
13418
|
this.user = augmentUser(rawUser);
|
|
12856
13419
|
const x402Internal = new X402Resource(this.resolveX402Options(options, dataOptions));
|
|
12857
13420
|
this.x402 = augmentX402(x402Internal, this.resolvedApiBaseUrl);
|
|
13421
|
+
const mppInternal = new MppResource(this.resolveMppOptions(options, dataOptions));
|
|
13422
|
+
this.mpp = augmentMpp(mppInternal, this.resolvedApiBaseUrl);
|
|
12858
13423
|
this.auth = augmentAuth(rawAuth, rawUser, options.authStorage);
|
|
12859
13424
|
}
|
|
12860
13425
|
/**
|
|
@@ -12891,6 +13456,20 @@ var Inflow = class {
|
|
|
12891
13456
|
}
|
|
12892
13457
|
return connection;
|
|
12893
13458
|
}
|
|
13459
|
+
resolveMppOptions(options, dataOptions) {
|
|
13460
|
+
const connection = {
|
|
13461
|
+
...options.environment !== void 0 ? { environment: options.environment } : {},
|
|
13462
|
+
...options.apiBaseUrl !== void 0 ? { baseUrl: options.apiBaseUrl } : {}
|
|
13463
|
+
};
|
|
13464
|
+
if (options.apiKey !== void 0 && options.apiKey.length > 0) {
|
|
13465
|
+
return { ...connection, apiKey: options.apiKey };
|
|
13466
|
+
}
|
|
13467
|
+
if (dataOptions.getAccessToken !== void 0) {
|
|
13468
|
+
const provider = dataOptions.getAccessToken;
|
|
13469
|
+
return { ...connection, getAccessToken: () => provider() };
|
|
13470
|
+
}
|
|
13471
|
+
return connection;
|
|
13472
|
+
}
|
|
12894
13473
|
};
|
|
12895
13474
|
async function probeSession(userResource, options) {
|
|
12896
13475
|
const controller = new AbortController();
|
|
@@ -12925,13 +13504,60 @@ async function runDepositAddressesList(input) {
|
|
|
12925
13504
|
}
|
|
12926
13505
|
|
|
12927
13506
|
// src/cli.tsx
|
|
12928
|
-
import { Cli as
|
|
13507
|
+
import { Cli as Cli7 } from "incur";
|
|
12929
13508
|
|
|
12930
13509
|
// src/commands/auth/index.tsx
|
|
12931
13510
|
import { Cli } from "incur";
|
|
12932
|
-
import { Text as Text6
|
|
13511
|
+
import { Text as Text6 } from "ink";
|
|
12933
13512
|
import { useEffect as useEffect5, useState as useState2 } from "react";
|
|
12934
13513
|
|
|
13514
|
+
// src/hooks/use-flow-exit.ts
|
|
13515
|
+
import { useApp } from "ink";
|
|
13516
|
+
import { useCallback, useRef } from "react";
|
|
13517
|
+
|
|
13518
|
+
// src/utils/best-effort-cancel.ts
|
|
13519
|
+
var CANCEL_GRACE_MS = 1500;
|
|
13520
|
+
function runBestEffortCancel(cancel, done, graceMs = CANCEL_GRACE_MS) {
|
|
13521
|
+
let finished = false;
|
|
13522
|
+
const finish = () => {
|
|
13523
|
+
if (finished) return;
|
|
13524
|
+
finished = true;
|
|
13525
|
+
done();
|
|
13526
|
+
};
|
|
13527
|
+
const timer = setTimeout(finish, graceMs);
|
|
13528
|
+
void Promise.resolve(cancel?.()).catch(() => void 0).finally(() => {
|
|
13529
|
+
clearTimeout(timer);
|
|
13530
|
+
finish();
|
|
13531
|
+
});
|
|
13532
|
+
}
|
|
13533
|
+
|
|
13534
|
+
// src/hooks/use-flow-exit.ts
|
|
13535
|
+
function useFlowExit(onComplete) {
|
|
13536
|
+
const { exit } = useApp();
|
|
13537
|
+
const onCompleteRef = useRef(onComplete);
|
|
13538
|
+
onCompleteRef.current = onComplete;
|
|
13539
|
+
const settledRef = useRef(false);
|
|
13540
|
+
const cancelStartedRef = useRef(false);
|
|
13541
|
+
const finish = useCallback(
|
|
13542
|
+
(...args) => {
|
|
13543
|
+
if (settledRef.current) return;
|
|
13544
|
+
settledRef.current = true;
|
|
13545
|
+
onCompleteRef.current(...args);
|
|
13546
|
+
exit();
|
|
13547
|
+
},
|
|
13548
|
+
[exit]
|
|
13549
|
+
);
|
|
13550
|
+
const cancelThenFinish = useCallback(
|
|
13551
|
+
(cancel, ...args) => {
|
|
13552
|
+
if (cancelStartedRef.current || settledRef.current) return;
|
|
13553
|
+
cancelStartedRef.current = true;
|
|
13554
|
+
runBestEffortCancel(cancel, () => finish(...args));
|
|
13555
|
+
},
|
|
13556
|
+
[finish]
|
|
13557
|
+
);
|
|
13558
|
+
return { finish, cancelThenFinish };
|
|
13559
|
+
}
|
|
13560
|
+
|
|
12935
13561
|
// src/utils/render-ink-until-exit.tsx
|
|
12936
13562
|
import { render } from "ink";
|
|
12937
13563
|
async function renderInkUntilExit(element, resolveResult) {
|
|
@@ -12944,7 +13570,7 @@ async function renderInkUntilExit(element, resolveResult) {
|
|
|
12944
13570
|
var NPM_INSTALL_COMMAND = "npm install -g @inflowpayai/inflow";
|
|
12945
13571
|
|
|
12946
13572
|
// src/commands/auth/login.tsx
|
|
12947
|
-
import { Box, Text,
|
|
13573
|
+
import { Box, Text, useInput } from "ink";
|
|
12948
13574
|
import Spinner from "ink-spinner";
|
|
12949
13575
|
import { useEffect, useReducer } from "react";
|
|
12950
13576
|
|
|
@@ -12985,7 +13611,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
12985
13611
|
var Login = ({ auth, clientName, connection, priorRefreshToken, onComplete }) => {
|
|
12986
13612
|
const initialPhase = { kind: "init" };
|
|
12987
13613
|
const [phase, dispatch] = useReducer(reduceAuthLogin, initialPhase);
|
|
12988
|
-
const {
|
|
13614
|
+
const { finish } = useFlowExit(onComplete);
|
|
12989
13615
|
useInput(
|
|
12990
13616
|
(_input, key) => {
|
|
12991
13617
|
if (key.return && phase.kind === "awaiting") {
|
|
@@ -13014,10 +13640,9 @@ var Login = ({ auth, clientName, connection, priorRefreshToken, onComplete }) =>
|
|
|
13014
13640
|
}, [auth, clientName, connection, priorRefreshToken]);
|
|
13015
13641
|
useEffect(() => {
|
|
13016
13642
|
if (phase.kind === "success" || phase.kind === "expired" || phase.kind === "denied" || phase.kind === "failed") {
|
|
13017
|
-
|
|
13018
|
-
exit();
|
|
13643
|
+
finish();
|
|
13019
13644
|
}
|
|
13020
|
-
}, [phase,
|
|
13645
|
+
}, [phase, finish]);
|
|
13021
13646
|
if (phase.kind === "init") {
|
|
13022
13647
|
return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
13023
13648
|
/* @__PURE__ */ jsx(Spinner, { type: "dots" }),
|
|
@@ -13069,14 +13694,14 @@ var Login = ({ auth, clientName, connection, priorRefreshToken, onComplete }) =>
|
|
|
13069
13694
|
};
|
|
13070
13695
|
|
|
13071
13696
|
// src/commands/auth/login-api-key.tsx
|
|
13072
|
-
import { Box as Box2, Text as Text2
|
|
13697
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
13073
13698
|
import Spinner2 from "ink-spinner";
|
|
13074
13699
|
import { useEffect as useEffect2, useReducer as useReducer2 } from "react";
|
|
13075
13700
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
13076
13701
|
var LoginApiKey = ({ apiKey: apiKey2, auth, connection, onComplete }) => {
|
|
13077
13702
|
const initial = { kind: "validating" };
|
|
13078
13703
|
const [phase, dispatch] = useReducer2(reduceAuthLoginApiKey, initial);
|
|
13079
|
-
const {
|
|
13704
|
+
const { finish } = useFlowExit(onComplete);
|
|
13080
13705
|
useEffect2(() => {
|
|
13081
13706
|
const run = auth.loginApiKey({ apiKey: apiKey2, connection });
|
|
13082
13707
|
let cancelled = false;
|
|
@@ -13092,9 +13717,8 @@ var LoginApiKey = ({ apiKey: apiKey2, auth, connection, onComplete }) => {
|
|
|
13092
13717
|
}, [apiKey2, auth, connection]);
|
|
13093
13718
|
useEffect2(() => {
|
|
13094
13719
|
if (phase.kind === "validating") return;
|
|
13095
|
-
|
|
13096
|
-
|
|
13097
|
-
}, [phase, onComplete, exit]);
|
|
13720
|
+
finish();
|
|
13721
|
+
}, [phase, finish]);
|
|
13098
13722
|
if (phase.kind === "validating") {
|
|
13099
13723
|
return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: "cyan", children: [
|
|
13100
13724
|
/* @__PURE__ */ jsx2(Spinner2, { type: "dots" }),
|
|
@@ -13136,19 +13760,19 @@ var LoginPrompt = ({ userDisplay, onAccept, onReject }) => {
|
|
|
13136
13760
|
};
|
|
13137
13761
|
|
|
13138
13762
|
// src/commands/auth/logout.tsx
|
|
13139
|
-
import { Box as Box4, Text as Text4
|
|
13763
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
13140
13764
|
import Spinner3 from "ink-spinner";
|
|
13141
|
-
import { useCallback } from "react";
|
|
13765
|
+
import { useCallback as useCallback2 } from "react";
|
|
13142
13766
|
|
|
13143
13767
|
// src/hooks/use-flow-state.ts
|
|
13144
|
-
import { useEffect as useEffect3, useRef, useState } from "react";
|
|
13768
|
+
import { useEffect as useEffect3, useRef as useRef2, useState } from "react";
|
|
13145
13769
|
function useFlowState(action, onComplete) {
|
|
13146
13770
|
const [status, setStatus] = useState("loading");
|
|
13147
13771
|
const [data, setData] = useState(null);
|
|
13148
13772
|
const [error, setError] = useState("");
|
|
13149
|
-
const onCompleteRef =
|
|
13773
|
+
const onCompleteRef = useRef2(onComplete);
|
|
13150
13774
|
onCompleteRef.current = onComplete;
|
|
13151
|
-
const completedRef =
|
|
13775
|
+
const completedRef = useRef2(false);
|
|
13152
13776
|
useEffect3(() => {
|
|
13153
13777
|
let cancelled = false;
|
|
13154
13778
|
const run = async () => {
|
|
@@ -13184,13 +13808,9 @@ function useFlowState(action, onComplete) {
|
|
|
13184
13808
|
// src/commands/auth/logout.tsx
|
|
13185
13809
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
13186
13810
|
var Logout = ({ auth, onComplete }) => {
|
|
13187
|
-
const
|
|
13188
|
-
const
|
|
13189
|
-
const
|
|
13190
|
-
onComplete();
|
|
13191
|
-
exit();
|
|
13192
|
-
}, [onComplete, exit]);
|
|
13193
|
-
const { status } = useFlowState(action, handleComplete);
|
|
13811
|
+
const action = useCallback2(() => auth.logout(), [auth]);
|
|
13812
|
+
const { finish } = useFlowExit(onComplete);
|
|
13813
|
+
const { status } = useFlowState(action, finish);
|
|
13194
13814
|
if (status === "loading") {
|
|
13195
13815
|
return /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: "cyan", children: [
|
|
13196
13816
|
/* @__PURE__ */ jsx4(Spinner3, { type: "dots" }),
|
|
@@ -13220,7 +13840,7 @@ var statusOptions = z.object({
|
|
|
13220
13840
|
});
|
|
13221
13841
|
|
|
13222
13842
|
// src/commands/auth/status.tsx
|
|
13223
|
-
import { Box as Box5, Text as Text5
|
|
13843
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
13224
13844
|
import Spinner4 from "ink-spinner";
|
|
13225
13845
|
import { useEffect as useEffect4, useReducer as useReducer3 } from "react";
|
|
13226
13846
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
@@ -13265,7 +13885,7 @@ var AuthStatus = ({
|
|
|
13265
13885
|
}) => {
|
|
13266
13886
|
const initial = { kind: "loading" };
|
|
13267
13887
|
const [view, dispatch] = useReducer3(reduce, initial);
|
|
13268
|
-
const {
|
|
13888
|
+
const { finish } = useFlowExit(onComplete);
|
|
13269
13889
|
useEffect4(() => {
|
|
13270
13890
|
let cancelled = false;
|
|
13271
13891
|
const options = composeOptions({ apiKey: apiKey2, displayConnection: displayConnection2, verbose: verbose2 });
|
|
@@ -13285,9 +13905,8 @@ var AuthStatus = ({
|
|
|
13285
13905
|
}, [auth, probe, apiKey2, displayConnection2, verbose2]);
|
|
13286
13906
|
useEffect4(() => {
|
|
13287
13907
|
if (view.kind === "loading" || view.kind === "probing") return;
|
|
13288
|
-
|
|
13289
|
-
|
|
13290
|
-
}, [view, onComplete, exit]);
|
|
13908
|
+
finish();
|
|
13909
|
+
}, [view, finish]);
|
|
13291
13910
|
if (view.kind === "loading") return null;
|
|
13292
13911
|
if (view.kind === "probing") {
|
|
13293
13912
|
return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsxs5(Text5, { color: "cyan", children: [
|
|
@@ -13422,7 +14041,7 @@ var InteractiveLoginShell = ({
|
|
|
13422
14041
|
}) => {
|
|
13423
14042
|
const initial = { kind: "probing" };
|
|
13424
14043
|
const [stage, setStage] = useState2(initial);
|
|
13425
|
-
const {
|
|
14044
|
+
const { finish } = useFlowExit(onComplete);
|
|
13426
14045
|
useEffect5(() => {
|
|
13427
14046
|
let cancelled = false;
|
|
13428
14047
|
const auth = authStorage2.getAuth();
|
|
@@ -13451,9 +14070,8 @@ var InteractiveLoginShell = ({
|
|
|
13451
14070
|
}, [authStorage2, userResource]);
|
|
13452
14071
|
useEffect5(() => {
|
|
13453
14072
|
if (stage.kind !== "declined") return;
|
|
13454
|
-
|
|
13455
|
-
|
|
13456
|
-
}, [stage, onComplete, exit]);
|
|
14073
|
+
finish();
|
|
14074
|
+
}, [stage, finish]);
|
|
13457
14075
|
if (stage.kind === "probing") return null;
|
|
13458
14076
|
if (stage.kind === "prompt") {
|
|
13459
14077
|
return /* @__PURE__ */ jsx6(
|
|
@@ -13741,9 +14359,9 @@ function assertSessionGuard(c, storage2, inflow2) {
|
|
|
13741
14359
|
}
|
|
13742
14360
|
|
|
13743
14361
|
// src/commands/balances/list.tsx
|
|
13744
|
-
import { Box as Box7, Text as Text8
|
|
14362
|
+
import { Box as Box7, Text as Text8 } from "ink";
|
|
13745
14363
|
import Spinner5 from "ink-spinner";
|
|
13746
|
-
import { useCallback as
|
|
14364
|
+
import { useCallback as useCallback3 } from "react";
|
|
13747
14365
|
|
|
13748
14366
|
// src/utils/table.tsx
|
|
13749
14367
|
import { Box as Box6, Text as Text7 } from "ink";
|
|
@@ -13814,16 +14432,9 @@ var COLUMNS = [
|
|
|
13814
14432
|
{ header: "Available", cell: (b) => b.available }
|
|
13815
14433
|
];
|
|
13816
14434
|
var BalancesList = ({ balanceResource, onComplete }) => {
|
|
13817
|
-
const
|
|
13818
|
-
const
|
|
13819
|
-
const
|
|
13820
|
-
(result) => {
|
|
13821
|
-
onComplete(result);
|
|
13822
|
-
exit();
|
|
13823
|
-
},
|
|
13824
|
-
[onComplete, exit]
|
|
13825
|
-
);
|
|
13826
|
-
const { status, data: balances, error } = useFlowState(action, handleLinger);
|
|
14435
|
+
const action = useCallback3(() => balanceResource.list(), [balanceResource]);
|
|
14436
|
+
const { finish } = useFlowExit(onComplete);
|
|
14437
|
+
const { status, data: balances, error } = useFlowState(action, finish);
|
|
13827
14438
|
if (status === "loading") {
|
|
13828
14439
|
return /* @__PURE__ */ jsx8(Box7, { children: /* @__PURE__ */ jsxs7(Text8, { color: "cyan", children: [
|
|
13829
14440
|
/* @__PURE__ */ jsx8(Spinner5, { type: "dots" }),
|
|
@@ -13887,9 +14498,9 @@ import { Cli as Cli3 } from "incur";
|
|
|
13887
14498
|
import "react";
|
|
13888
14499
|
|
|
13889
14500
|
// src/commands/deposit-addresses/list.tsx
|
|
13890
|
-
import { Box as Box8, Text as Text9
|
|
14501
|
+
import { Box as Box8, Text as Text9 } from "ink";
|
|
13891
14502
|
import Spinner6 from "ink-spinner";
|
|
13892
|
-
import { useCallback as
|
|
14503
|
+
import { useCallback as useCallback4 } from "react";
|
|
13893
14504
|
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
13894
14505
|
var COLUMNS2 = [
|
|
13895
14506
|
{ header: "Blockchain", cell: (a) => a.blockchain },
|
|
@@ -13897,16 +14508,9 @@ var COLUMNS2 = [
|
|
|
13897
14508
|
{ header: "Currencies", cell: (a) => a.currencies.join(", ") }
|
|
13898
14509
|
];
|
|
13899
14510
|
var DepositAddressesList = ({ depositAddressResource, onComplete }) => {
|
|
13900
|
-
const
|
|
13901
|
-
const
|
|
13902
|
-
const
|
|
13903
|
-
(result) => {
|
|
13904
|
-
onComplete(result);
|
|
13905
|
-
exit();
|
|
13906
|
-
},
|
|
13907
|
-
[onComplete, exit]
|
|
13908
|
-
);
|
|
13909
|
-
const { status, data: addresses, error } = useFlowState(action, handleLinger);
|
|
14511
|
+
const action = useCallback4(() => runDepositAddressesList({ depositAddressResource }), [depositAddressResource]);
|
|
14512
|
+
const { finish } = useFlowExit(onComplete);
|
|
14513
|
+
const { status, data: addresses, error } = useFlowState(action, finish);
|
|
13910
14514
|
if (status === "loading") {
|
|
13911
14515
|
return /* @__PURE__ */ jsx10(Box8, { children: /* @__PURE__ */ jsxs8(Text9, { color: "cyan", children: [
|
|
13912
14516
|
/* @__PURE__ */ jsx10(Spinner6, { type: "dots" }),
|
|
@@ -13971,204 +14575,112 @@ function createDepositAddressesCli(depositAddressResource, authStorage2, inflow2
|
|
|
13971
14575
|
return cli2;
|
|
13972
14576
|
}
|
|
13973
14577
|
|
|
13974
|
-
// src/commands/
|
|
13975
|
-
import { Cli as Cli4, Errors as Errors2 } from "incur";
|
|
13976
|
-
import "react";
|
|
13977
|
-
|
|
13978
|
-
// src/commands/user/get.tsx
|
|
13979
|
-
import { Box as Box9, Text as Text10, useApp as useApp8 } from "ink";
|
|
13980
|
-
import Spinner7 from "ink-spinner";
|
|
13981
|
-
import { useCallback as useCallback4, useRef as useRef2 } from "react";
|
|
13982
|
-
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
13983
|
-
var UserGet = ({ userResource, onComplete }) => {
|
|
13984
|
-
const { exit } = useApp8();
|
|
13985
|
-
const action = useCallback4(() => userResource.retrieve(), [userResource]);
|
|
13986
|
-
const lastErrorRef = useRef2("");
|
|
13987
|
-
const handleLinger = useCallback4(
|
|
13988
|
-
(result) => {
|
|
13989
|
-
if (result !== null) {
|
|
13990
|
-
onComplete({ kind: "success", user: result });
|
|
13991
|
-
} else {
|
|
13992
|
-
onComplete({
|
|
13993
|
-
kind: "error",
|
|
13994
|
-
message: lastErrorRef.current || "No result produced."
|
|
13995
|
-
});
|
|
13996
|
-
}
|
|
13997
|
-
exit();
|
|
13998
|
-
},
|
|
13999
|
-
[onComplete, exit]
|
|
14000
|
-
);
|
|
14001
|
-
const { status, data: user, error } = useFlowState(action, handleLinger);
|
|
14002
|
-
lastErrorRef.current = error;
|
|
14003
|
-
if (status === "loading") {
|
|
14004
|
-
return /* @__PURE__ */ jsx12(Box9, { children: /* @__PURE__ */ jsxs9(Text10, { color: "cyan", children: [
|
|
14005
|
-
/* @__PURE__ */ jsx12(Spinner7, { type: "dots" }),
|
|
14006
|
-
" Loading user..."
|
|
14007
|
-
] }) });
|
|
14008
|
-
}
|
|
14009
|
-
if (status === "error") {
|
|
14010
|
-
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
14011
|
-
/* @__PURE__ */ jsx12(Text10, { color: "red", children: "\u2717 Failed to retrieve user" }),
|
|
14012
|
-
/* @__PURE__ */ jsx12(Text10, { color: "red", children: error })
|
|
14013
|
-
] });
|
|
14014
|
-
}
|
|
14015
|
-
if (user === null) return null;
|
|
14016
|
-
const rows = buildProfileRows(user);
|
|
14017
|
-
return /* @__PURE__ */ jsx12(Box9, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: rows.map((row) => /* @__PURE__ */ jsxs9(Text10, { children: [
|
|
14018
|
-
`${row.label}: `,
|
|
14019
|
-
row.value !== null ? /* @__PURE__ */ jsx12(Text10, { bold: true, children: row.value }) : /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: "\u2014" })
|
|
14020
|
-
] }, row.label)) });
|
|
14021
|
-
};
|
|
14022
|
-
|
|
14023
|
-
// src/commands/user/schema.ts
|
|
14024
|
-
import { z as z4 } from "incur";
|
|
14025
|
-
var getOptions = z4.object({});
|
|
14026
|
-
|
|
14027
|
-
// src/commands/user/index.tsx
|
|
14028
|
-
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
14029
|
-
var TTY_NO_RESULT_MESSAGE = "inflow user get exited without producing a result.";
|
|
14030
|
-
async function runUserGet2(c, deps) {
|
|
14031
|
-
assertSessionGuard(c, deps.authStorage, deps.inflow);
|
|
14032
|
-
if (!c.agent && !c.formatExplicit) {
|
|
14033
|
-
let captured = null;
|
|
14034
|
-
const outcome = await renderInkUntilExit(
|
|
14035
|
-
/* @__PURE__ */ jsx13(
|
|
14036
|
-
UserGet,
|
|
14037
|
-
{
|
|
14038
|
-
userResource: deps.user,
|
|
14039
|
-
onComplete: (value) => {
|
|
14040
|
-
captured = value;
|
|
14041
|
-
}
|
|
14042
|
-
}
|
|
14043
|
-
),
|
|
14044
|
-
() => captured
|
|
14045
|
-
);
|
|
14046
|
-
if (outcome === null) {
|
|
14047
|
-
throw new Errors2.IncurError({
|
|
14048
|
-
code: "USER_GET_FAILED",
|
|
14049
|
-
message: TTY_NO_RESULT_MESSAGE
|
|
14050
|
-
});
|
|
14051
|
-
}
|
|
14052
|
-
if (outcome.kind === "error") {
|
|
14053
|
-
throw new Errors2.IncurError({
|
|
14054
|
-
code: "USER_GET_FAILED",
|
|
14055
|
-
message: outcome.message
|
|
14056
|
-
});
|
|
14057
|
-
}
|
|
14058
|
-
return projectUserPayload(outcome.user);
|
|
14059
|
-
}
|
|
14060
|
-
return deps.user.get();
|
|
14061
|
-
}
|
|
14062
|
-
function createUserCli(user, authStorage2, inflow2) {
|
|
14063
|
-
const cli2 = Cli4.create("user", { description: "User profile commands" });
|
|
14064
|
-
cli2.command("get", {
|
|
14065
|
-
description: "Retrieve the current authenticated user",
|
|
14066
|
-
options: getOptions,
|
|
14067
|
-
outputPolicy: "agent-only",
|
|
14068
|
-
async run(c) {
|
|
14069
|
-
return runUserGet2(c, { user, authStorage: authStorage2, inflow: inflow2 });
|
|
14070
|
-
}
|
|
14071
|
-
});
|
|
14072
|
-
return cli2;
|
|
14073
|
-
}
|
|
14074
|
-
|
|
14075
|
-
// src/commands/x402/index.tsx
|
|
14578
|
+
// src/commands/mpp/index.tsx
|
|
14076
14579
|
import { chmodSync, writeFileSync as writeFileSync2 } from "fs";
|
|
14077
14580
|
import { resolve as resolvePath2 } from "path";
|
|
14078
|
-
import { Cli as
|
|
14581
|
+
import { Cli as Cli4 } from "incur";
|
|
14079
14582
|
|
|
14080
|
-
// src/commands/
|
|
14081
|
-
import { Box as
|
|
14082
|
-
import
|
|
14583
|
+
// src/commands/mpp/cancel.tsx
|
|
14584
|
+
import { Box as Box9, Text as Text10 } from "ink";
|
|
14585
|
+
import Spinner7 from "ink-spinner";
|
|
14083
14586
|
import { useCallback as useCallback5 } from "react";
|
|
14084
|
-
import { jsx as
|
|
14587
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
14085
14588
|
var CancelView = ({ approvalId, cancel, onComplete }) => {
|
|
14086
|
-
const { exit } = useApp9();
|
|
14087
14589
|
const action = useCallback5(() => cancel(), [cancel]);
|
|
14088
|
-
const
|
|
14089
|
-
|
|
14090
|
-
exit();
|
|
14091
|
-
}, [onComplete, exit]);
|
|
14092
|
-
const { status } = useFlowState(action, handleComplete);
|
|
14590
|
+
const { finish } = useFlowExit(onComplete);
|
|
14591
|
+
const { status } = useFlowState(action, finish);
|
|
14093
14592
|
if (status === "loading") {
|
|
14094
|
-
return /* @__PURE__ */
|
|
14095
|
-
/* @__PURE__ */
|
|
14593
|
+
return /* @__PURE__ */ jsx12(Box9, { children: /* @__PURE__ */ jsxs9(Text10, { color: "cyan", children: [
|
|
14594
|
+
/* @__PURE__ */ jsx12(Spinner7, { type: "dots" }),
|
|
14096
14595
|
" Cancelling approval ",
|
|
14097
14596
|
approvalId,
|
|
14098
14597
|
"..."
|
|
14099
14598
|
] }) });
|
|
14100
14599
|
}
|
|
14101
|
-
return /* @__PURE__ */
|
|
14600
|
+
return /* @__PURE__ */ jsx12(Box9, { children: /* @__PURE__ */ jsxs9(Text10, { color: "green", children: [
|
|
14102
14601
|
"\u2713 Cancelled approval ",
|
|
14103
14602
|
approvalId,
|
|
14104
14603
|
" (best-effort)"
|
|
14105
14604
|
] }) });
|
|
14106
14605
|
};
|
|
14107
14606
|
|
|
14108
|
-
// src/commands/
|
|
14109
|
-
import {
|
|
14110
|
-
import {
|
|
14111
|
-
|
|
14112
|
-
|
|
14113
|
-
|
|
14114
|
-
|
|
14115
|
-
|
|
14116
|
-
|
|
14117
|
-
|
|
14118
|
-
"x402Version: ",
|
|
14119
|
-
/* @__PURE__ */ jsx15(Text12, { color: "cyan", children: String(decoded.x402Version) })
|
|
14120
|
-
] }),
|
|
14121
|
-
/* @__PURE__ */ jsxs11(Text12, { children: [
|
|
14122
|
-
"resource: ",
|
|
14123
|
-
/* @__PURE__ */ jsx15(Text12, { color: "cyan", children: decoded.resource.url })
|
|
14124
|
-
] }),
|
|
14125
|
-
/* @__PURE__ */ jsx15(Text12, { children: `accepts (${String(summary.length)}):` }),
|
|
14126
|
-
summary.map((entry, idx) => /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
14127
|
-
" ",
|
|
14128
|
-
/* @__PURE__ */ jsx15(Text12, { color: "yellow", children: entry.scheme }),
|
|
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,
|
|
14129
14617
|
" / ",
|
|
14130
|
-
|
|
14131
|
-
|
|
14132
|
-
|
|
14133
|
-
|
|
14134
|
-
|
|
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}` })
|
|
14135
14657
|
] })
|
|
14136
14658
|
] });
|
|
14137
14659
|
};
|
|
14138
14660
|
|
|
14139
|
-
// src/commands/
|
|
14140
|
-
import { Box as
|
|
14141
|
-
import
|
|
14661
|
+
// src/commands/mpp/inspect.tsx
|
|
14662
|
+
import { Box as Box11, Text as Text12 } from "ink";
|
|
14663
|
+
import Spinner8 from "ink-spinner";
|
|
14142
14664
|
import { useEffect as useEffect6, useReducer as useReducer4 } from "react";
|
|
14143
|
-
import { jsx as
|
|
14144
|
-
function
|
|
14145
|
-
|
|
14146
|
-
const keys = Object.keys(extra);
|
|
14147
|
-
if (keys.length === 0) return "\u2014";
|
|
14148
|
-
return [...keys].sort((a, b) => a.localeCompare(b)).join(", ");
|
|
14149
|
-
}
|
|
14150
|
-
function formatTimeout(seconds) {
|
|
14151
|
-
return `${String(seconds)}s`;
|
|
14152
|
-
}
|
|
14153
|
-
function formatAsset(asset) {
|
|
14154
|
-
return asset === "" ? "\u2014" : asset;
|
|
14665
|
+
import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
14666
|
+
function orDash(value) {
|
|
14667
|
+
return value === void 0 || value === "" ? "\u2014" : value;
|
|
14155
14668
|
}
|
|
14156
14669
|
var COLUMNS3 = [
|
|
14157
|
-
{ header: "
|
|
14158
|
-
{ header: "
|
|
14159
|
-
{ header: "Amount", cell: (
|
|
14160
|
-
{ header: "
|
|
14161
|
-
{ header: "
|
|
14162
|
-
{ header: "
|
|
14163
|
-
{ header: "Extra", cell: (r) => summarizeExtra(r.extra) }
|
|
14670
|
+
{ header: "Method", cell: (c) => c.method },
|
|
14671
|
+
{ header: "Intent", cell: (c) => c.intent },
|
|
14672
|
+
{ header: "Amount", cell: (c) => orDash(c.amount) },
|
|
14673
|
+
{ header: "Currency", cell: (c) => orDash(c.currency) },
|
|
14674
|
+
{ header: "Rail", cell: (c) => orDash(c.rail) },
|
|
14675
|
+
{ header: "Expires", cell: (c) => orDash(c.expires) }
|
|
14164
14676
|
];
|
|
14165
14677
|
var InspectView = ({ url, method, deps, onComplete }) => {
|
|
14166
|
-
const { exit } = useApp10();
|
|
14167
14678
|
const initial = { kind: "probing" };
|
|
14168
|
-
const [phase, dispatch] = useReducer4(
|
|
14679
|
+
const [phase, dispatch] = useReducer4(reduceMppInspect, initial);
|
|
14680
|
+
const { finish } = useFlowExit(onComplete);
|
|
14169
14681
|
useEffect6(() => {
|
|
14170
14682
|
let cancelled = false;
|
|
14171
|
-
void
|
|
14683
|
+
void runMppInspectPipeline(deps, (event) => {
|
|
14172
14684
|
if (!cancelled) dispatch(event);
|
|
14173
14685
|
});
|
|
14174
14686
|
return () => {
|
|
@@ -14176,14 +14688,13 @@ var InspectView = ({ url, method, deps, onComplete }) => {
|
|
|
14176
14688
|
};
|
|
14177
14689
|
}, [deps]);
|
|
14178
14690
|
useEffect6(() => {
|
|
14179
|
-
if (phase.kind === "
|
|
14180
|
-
|
|
14181
|
-
exit();
|
|
14691
|
+
if (phase.kind === "challenges" || phase.kind === "no-payment" || phase.kind === "error") {
|
|
14692
|
+
finish(phase);
|
|
14182
14693
|
}
|
|
14183
|
-
}, [phase,
|
|
14694
|
+
}, [phase, finish]);
|
|
14184
14695
|
if (phase.kind === "probing") {
|
|
14185
|
-
return /* @__PURE__ */
|
|
14186
|
-
/* @__PURE__ */
|
|
14696
|
+
return /* @__PURE__ */ jsx14(Box11, { children: /* @__PURE__ */ jsxs11(Text12, { color: "cyan", children: [
|
|
14697
|
+
/* @__PURE__ */ jsx14(Spinner8, { type: "dots" }),
|
|
14187
14698
|
" Probing ",
|
|
14188
14699
|
method,
|
|
14189
14700
|
" ",
|
|
@@ -14193,63 +14704,64 @@ var InspectView = ({ url, method, deps, onComplete }) => {
|
|
|
14193
14704
|
}
|
|
14194
14705
|
if (phase.kind === "no-payment") {
|
|
14195
14706
|
const { result: result2 } = phase;
|
|
14196
|
-
return /* @__PURE__ */
|
|
14197
|
-
/* @__PURE__ */
|
|
14198
|
-
/* @__PURE__ */
|
|
14199
|
-
result2.contentType !== void 0 ? /* @__PURE__ */
|
|
14200
|
-
/* @__PURE__ */
|
|
14201
|
-
/* @__PURE__ */
|
|
14707
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
14708
|
+
/* @__PURE__ */ jsx14(Text12, { color: "green", children: "\u2713 Seller accepted without payment" }),
|
|
14709
|
+
/* @__PURE__ */ jsx14(Text12, { children: `status: ${String(result2.status)}` }),
|
|
14710
|
+
result2.contentType !== void 0 ? /* @__PURE__ */ jsx14(Text12, { children: `content-type: ${result2.contentType}` }) : null,
|
|
14711
|
+
/* @__PURE__ */ jsx14(Text12, { children: `response size: ${String(result2.bodySizeBytes)} bytes` }),
|
|
14712
|
+
/* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Use `mpp pay` to fetch the body." })
|
|
14202
14713
|
] });
|
|
14203
14714
|
}
|
|
14204
14715
|
if (phase.kind === "error") {
|
|
14205
|
-
return /* @__PURE__ */
|
|
14206
|
-
/* @__PURE__ */
|
|
14716
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
14717
|
+
/* @__PURE__ */ jsxs11(Text12, { color: "red", children: [
|
|
14207
14718
|
"\u2717 ",
|
|
14208
14719
|
phase.code
|
|
14209
14720
|
] }),
|
|
14210
|
-
/* @__PURE__ */
|
|
14721
|
+
/* @__PURE__ */ jsx14(Text12, { color: "red", children: phase.message })
|
|
14211
14722
|
] });
|
|
14212
14723
|
}
|
|
14213
14724
|
const { result } = phase;
|
|
14214
|
-
const
|
|
14215
|
-
|
|
14216
|
-
|
|
14217
|
-
|
|
14218
|
-
/* @__PURE__ */ jsx16(Text13, { bold: true, children: "PAYMENT-REQUIRED" }),
|
|
14725
|
+
const count = result.challenges.length;
|
|
14726
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
14727
|
+
/* @__PURE__ */ jsxs11(Text12, { children: [
|
|
14728
|
+
/* @__PURE__ */ jsx14(Text12, { bold: true, children: "WWW-Authenticate: Payment" }),
|
|
14219
14729
|
" for ",
|
|
14220
|
-
/* @__PURE__ */
|
|
14730
|
+
/* @__PURE__ */ jsx14(Text12, { color: "cyan", children: result.url }),
|
|
14221
14731
|
" \xB7 ",
|
|
14222
|
-
/* @__PURE__ */
|
|
14732
|
+
/* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `realm ${result.realm}` }),
|
|
14223
14733
|
" \xB7 ",
|
|
14224
|
-
/* @__PURE__ */
|
|
14734
|
+
/* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `${String(count)} challenge${count === 1 ? "" : "s"}` })
|
|
14225
14735
|
] }),
|
|
14226
|
-
|
|
14227
|
-
/* @__PURE__ */
|
|
14228
|
-
/* @__PURE__ */ jsx16(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text13, { dimColor: true, children: "Use --format json to inspect extras values." }) })
|
|
14736
|
+
/* @__PURE__ */ jsx14(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx14(Table, { columns: COLUMNS3, rows: [...result.challenges] }) }),
|
|
14737
|
+
/* @__PURE__ */ jsx14(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Use --format json to see challenge ids and digests." }) })
|
|
14229
14738
|
] });
|
|
14230
14739
|
};
|
|
14231
|
-
function
|
|
14232
|
-
const
|
|
14233
|
-
|
|
14740
|
+
function challengeToFrame(challenge) {
|
|
14741
|
+
const row = {
|
|
14742
|
+
id: challenge.id,
|
|
14743
|
+
realm: challenge.realm,
|
|
14744
|
+
method: challenge.method,
|
|
14745
|
+
intent: challenge.intent
|
|
14746
|
+
};
|
|
14747
|
+
if (challenge.amount !== void 0) row.amount = challenge.amount;
|
|
14748
|
+
if (challenge.currency !== void 0) row.currency = challenge.currency;
|
|
14749
|
+
if (challenge.recipient !== void 0) row.recipient = challenge.recipient;
|
|
14750
|
+
if (challenge.rail !== void 0) row.rail = challenge.rail;
|
|
14751
|
+
if (challenge.instrumentId !== void 0) row.instrument_id = challenge.instrumentId;
|
|
14752
|
+
if (challenge.expires !== void 0) row.expires = challenge.expires;
|
|
14753
|
+
if (challenge.description !== void 0) row.description = challenge.description;
|
|
14754
|
+
if (challenge.digest !== void 0) row.digest = challenge.digest;
|
|
14755
|
+
return row;
|
|
14756
|
+
}
|
|
14757
|
+
function buildChallengesFrame(result) {
|
|
14758
|
+
return {
|
|
14759
|
+
outcome: "challenges",
|
|
14234
14760
|
url: result.url,
|
|
14235
14761
|
method: result.method,
|
|
14236
|
-
|
|
14237
|
-
|
|
14238
|
-
accepts: result.accepts.map((entry) => {
|
|
14239
|
-
const row = {
|
|
14240
|
-
scheme: entry.scheme,
|
|
14241
|
-
network: entry.network,
|
|
14242
|
-
amount: entry.amount,
|
|
14243
|
-
asset: entry.asset,
|
|
14244
|
-
pay_to: entry.payTo,
|
|
14245
|
-
max_timeout_seconds: entry.maxTimeoutSeconds
|
|
14246
|
-
};
|
|
14247
|
-
if (entry.extra !== void 0) row.extra = entry.extra;
|
|
14248
|
-
return row;
|
|
14249
|
-
})
|
|
14762
|
+
realm: result.realm,
|
|
14763
|
+
challenges: result.challenges.map(challengeToFrame)
|
|
14250
14764
|
};
|
|
14251
|
-
if (result.extensions !== void 0) frame.extensions = result.extensions;
|
|
14252
|
-
return frame;
|
|
14253
14765
|
}
|
|
14254
14766
|
function buildNoPaymentFrame(result) {
|
|
14255
14767
|
const frame = {
|
|
@@ -14263,41 +14775,1136 @@ function buildNoPaymentFrame(result) {
|
|
|
14263
14775
|
return frame;
|
|
14264
14776
|
}
|
|
14265
14777
|
|
|
14266
|
-
// src/commands/
|
|
14267
|
-
import { Box as
|
|
14268
|
-
import
|
|
14269
|
-
import { useEffect as useEffect7, useReducer as useReducer5 } from "react";
|
|
14270
|
-
import { jsx as
|
|
14271
|
-
var PayView = ({ url, method, deps, onComplete }) => {
|
|
14272
|
-
const { exit } = useApp11();
|
|
14778
|
+
// src/commands/mpp/pay.tsx
|
|
14779
|
+
import { Box as Box12, Text as Text13, useInput as useInput3 } from "ink";
|
|
14780
|
+
import Spinner9 from "ink-spinner";
|
|
14781
|
+
import { useEffect as useEffect7, useReducer as useReducer5, useState as useState3 } from "react";
|
|
14782
|
+
import { Fragment, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
14783
|
+
var PayView = ({ url, method, deps, onComplete, onCancel }) => {
|
|
14273
14784
|
const initial = { kind: "probing" };
|
|
14274
|
-
const [phase, dispatch] = useReducer5(
|
|
14785
|
+
const [phase, dispatch] = useReducer5(reduceMppPay, initial);
|
|
14786
|
+
const [cancelling, setCancelling] = useState3(false);
|
|
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;
|
|
14275
14791
|
useInput3(
|
|
14276
14792
|
(_input, key) => {
|
|
14277
|
-
if (
|
|
14278
|
-
|
|
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
|
+
});
|
|
14279
14805
|
}
|
|
14280
14806
|
},
|
|
14281
|
-
{ isActive:
|
|
14807
|
+
{ isActive: approvalUrl !== void 0 && !cancelling }
|
|
14282
14808
|
);
|
|
14283
14809
|
useEffect7(() => {
|
|
14810
|
+
const controller = new AbortController();
|
|
14284
14811
|
let cancelled = false;
|
|
14285
|
-
|
|
14812
|
+
const runDeps = { ...deps, signal: controller.signal };
|
|
14813
|
+
void runMppPayPipeline(runDeps, (event) => {
|
|
14286
14814
|
if (!cancelled) dispatch(event);
|
|
14287
14815
|
});
|
|
14288
14816
|
return () => {
|
|
14289
14817
|
cancelled = true;
|
|
14818
|
+
controller.abort();
|
|
14290
14819
|
};
|
|
14291
14820
|
}, [deps]);
|
|
14292
14821
|
useEffect7(() => {
|
|
14293
|
-
if (phase.kind === "success" || phase.kind === "
|
|
14294
|
-
|
|
14295
|
-
|
|
14822
|
+
if (phase.kind === "success" || phase.kind === "seller-rejected" || phase.kind === "no-payment-final" || phase.kind === "error") {
|
|
14823
|
+
finish(phase);
|
|
14824
|
+
}
|
|
14825
|
+
}, [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
|
+
if (phase.kind === "probing") {
|
|
14833
|
+
return /* @__PURE__ */ jsx15(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
|
|
14834
|
+
/* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
|
|
14835
|
+
" Probing ",
|
|
14836
|
+
method,
|
|
14837
|
+
" ",
|
|
14838
|
+
url,
|
|
14839
|
+
"..."
|
|
14840
|
+
] }) });
|
|
14841
|
+
}
|
|
14842
|
+
if (phase.kind === "no-payment") {
|
|
14843
|
+
return /* @__PURE__ */ jsx15(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
|
|
14844
|
+
/* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
|
|
14845
|
+
" Seller accepted without payment (status ",
|
|
14846
|
+
String(phase.probe.status),
|
|
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
|
+
] }) });
|
|
14860
|
+
}
|
|
14861
|
+
if (phase.kind === "created") {
|
|
14862
|
+
const { created: created2 } = phase;
|
|
14863
|
+
if (created2.state !== "pending") {
|
|
14864
|
+
return /* @__PURE__ */ jsx15(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
|
|
14865
|
+
/* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
|
|
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
|
|
14883
|
+
] }),
|
|
14884
|
+
/* @__PURE__ */ jsx15(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
|
|
14885
|
+
/* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
|
|
14886
|
+
" Waiting for approval..."
|
|
14887
|
+
] }) })
|
|
14888
|
+
] });
|
|
14889
|
+
}
|
|
14890
|
+
if (phase.kind === "replaying") {
|
|
14891
|
+
return /* @__PURE__ */ jsx15(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
|
|
14892
|
+
/* @__PURE__ */ jsx15(Spinner9, { type: "dots" }),
|
|
14893
|
+
" Replaying request with Authorization: Payment..."
|
|
14894
|
+
] }) });
|
|
14895
|
+
}
|
|
14896
|
+
if (phase.kind === "success") {
|
|
14897
|
+
const { result } = phase;
|
|
14898
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
14899
|
+
/* @__PURE__ */ jsxs12(Text13, { color: "green", children: [
|
|
14900
|
+
"\u2713 Paid (intent ",
|
|
14901
|
+
result.intent,
|
|
14902
|
+
")"
|
|
14903
|
+
] }),
|
|
14904
|
+
/* @__PURE__ */ jsx15(Text13, { children: `status: ${String(result.responseStatus)}` }),
|
|
14905
|
+
/* @__PURE__ */ jsx15(Text13, { children: `transaction: ${result.transactionId}` }),
|
|
14906
|
+
result.settled !== void 0 ? /* @__PURE__ */ jsx15(Text13, { children: `settled: ${result.settled.status ?? "success"} (ref ${result.settled.reference ?? "\u2014"})` }) : null,
|
|
14907
|
+
/* @__PURE__ */ jsx15(Text13, { children: `response size: ${String(result.bodySizeBytes)} bytes` }),
|
|
14908
|
+
result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs12(Text13, { children: [
|
|
14909
|
+
"Saved to: ",
|
|
14910
|
+
/* @__PURE__ */ jsx15(Text13, { bold: true, children: result.outputSavedTo })
|
|
14911
|
+
] }) : null,
|
|
14912
|
+
result.body !== void 0 ? /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
|
|
14913
|
+
/* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "response body:" }),
|
|
14914
|
+
/* @__PURE__ */ jsx15(Text13, { children: result.body })
|
|
14915
|
+
] }) : null
|
|
14916
|
+
] });
|
|
14917
|
+
}
|
|
14918
|
+
if (phase.kind === "seller-rejected") {
|
|
14919
|
+
const { result } = phase;
|
|
14920
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
14921
|
+
/* @__PURE__ */ jsx15(Text13, { color: "red", children: "\u2717 Payment not accepted by seller" }),
|
|
14922
|
+
/* @__PURE__ */ jsx15(Text13, { children: `status: ${String(result.responseStatus)}` }),
|
|
14923
|
+
/* @__PURE__ */ jsx15(Text13, { children: `transaction: ${result.transactionId}` }),
|
|
14924
|
+
/* @__PURE__ */ jsx15(Text13, { children: `response size: ${String(result.bodySizeBytes)} bytes` }),
|
|
14925
|
+
result.body !== void 0 ? /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
|
|
14926
|
+
/* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "response body:" }),
|
|
14927
|
+
/* @__PURE__ */ jsx15(Text13, { children: result.body })
|
|
14928
|
+
] }) : null
|
|
14929
|
+
] });
|
|
14930
|
+
}
|
|
14931
|
+
if (phase.kind === "no-payment-final") {
|
|
14932
|
+
const { result } = phase;
|
|
14933
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
14934
|
+
/* @__PURE__ */ jsx15(Text13, { color: "green", children: "\u2713 Seller accepted without payment" }),
|
|
14935
|
+
/* @__PURE__ */ jsx15(Text13, { children: `status: ${String(result.status)}` }),
|
|
14936
|
+
/* @__PURE__ */ jsx15(Text13, { children: `response size: ${String(result.bodySizeBytes)} bytes` }),
|
|
14937
|
+
result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs12(Text13, { children: [
|
|
14938
|
+
"Saved to: ",
|
|
14939
|
+
/* @__PURE__ */ jsx15(Text13, { bold: true, children: result.outputSavedTo })
|
|
14940
|
+
] }) : null,
|
|
14941
|
+
result.body !== void 0 ? /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
|
|
14942
|
+
/* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "response body:" }),
|
|
14943
|
+
/* @__PURE__ */ jsx15(Text13, { children: result.body })
|
|
14944
|
+
] }) : null
|
|
14945
|
+
] });
|
|
14946
|
+
}
|
|
14947
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
14948
|
+
/* @__PURE__ */ jsxs12(Text13, { color: "red", children: [
|
|
14949
|
+
"\u2717 ",
|
|
14950
|
+
phase.code
|
|
14951
|
+
] }),
|
|
14952
|
+
/* @__PURE__ */ jsx15(Text13, { color: "red", children: phase.message })
|
|
14953
|
+
] });
|
|
14954
|
+
};
|
|
14955
|
+
|
|
14956
|
+
// src/commands/mpp/schema.ts
|
|
14957
|
+
import { z as z4 } from "incur";
|
|
14958
|
+
var payArgs = z4.object({
|
|
14959
|
+
url: z4.string().describe("The MPP-protected resource URL to pay for.")
|
|
14960
|
+
});
|
|
14961
|
+
var payOptions = z4.object({
|
|
14962
|
+
paymentMethod: z4.string().optional().describe('Only consider challenges with this payment method (e.g. "inflow").'),
|
|
14963
|
+
intent: z4.string().optional().describe('Only consider challenges with this intent (e.g. "charge").'),
|
|
14964
|
+
currency: z4.string().optional().describe('Only consider challenges in this currency (e.g. "USDC").'),
|
|
14965
|
+
rail: z4.string().optional().describe('Only consider challenges on this settlement rail (e.g. "balance", "instrument").'),
|
|
14966
|
+
method: z4.string().default("GET").describe("HTTP method for the seller request."),
|
|
14967
|
+
data: z4.string().optional().describe(
|
|
14968
|
+
"Request body. JSON or raw text. Content-Type defaults to application/json when --data is set unless a --header overrides it."
|
|
14969
|
+
),
|
|
14970
|
+
header: z4.array(z4.string()).default([]).describe('Repeatable. "Name: Value" format.'),
|
|
14971
|
+
interval: z4.coerce.number().default(0).describe(
|
|
14972
|
+
"Inline poll cadence in seconds while a transaction is pending. 0 returns the transaction id and a follow-up command hint without blocking."
|
|
14973
|
+
),
|
|
14974
|
+
maxAttempts: z4.coerce.number().default(0).describe("Hard cap on poll attempts when --interval > 0. 0 means unlimited."),
|
|
14975
|
+
timeout: z4.coerce.number().default(900).describe("Polling deadline in seconds. Default 900s (matches the server-side approval expiry)."),
|
|
14976
|
+
instrumentId: z4.string().optional().describe(
|
|
14977
|
+
"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
|
+
),
|
|
14979
|
+
showBody: z4.boolean().default(true).describe(
|
|
14980
|
+
"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
|
+
),
|
|
14982
|
+
outputFile: z4.string().optional().describe(
|
|
14983
|
+
"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
|
+
),
|
|
14985
|
+
credentialFile: z4.string().optional().describe(
|
|
14986
|
+
"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
|
+
)
|
|
14988
|
+
});
|
|
14989
|
+
var statusArgs = z4.object({
|
|
14990
|
+
transactionId: z4.string().describe("The transaction id returned by `mpp pay`.")
|
|
14991
|
+
});
|
|
14992
|
+
var statusOptions2 = z4.object({
|
|
14993
|
+
interval: z4.coerce.number().default(0).describe(
|
|
14994
|
+
"Poll cadence in seconds. 0 returns the current snapshot; positive values yield on every change until ready or terminal."
|
|
14995
|
+
),
|
|
14996
|
+
maxAttempts: z4.coerce.number().default(0).describe("Hard cap on poll attempts. 0 means unlimited."),
|
|
14997
|
+
timeout: z4.coerce.number().default(900).describe("Polling deadline in seconds."),
|
|
14998
|
+
credentialFile: z4.string().optional().describe(
|
|
14999
|
+
"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
|
+
)
|
|
15001
|
+
});
|
|
15002
|
+
var cancelArgs = z4.object({
|
|
15003
|
+
approvalId: z4.string().describe("The approval id returned by `mpp pay` (on the pending frame).")
|
|
15004
|
+
});
|
|
15005
|
+
var decodeArgs = z4.object({
|
|
15006
|
+
value: z4.string().describe(
|
|
15007
|
+
"A raw `WWW-Authenticate: Payment` header value, or a base64url `Authorization: Payment` credential / `Payment-Receipt`. The kind is auto-detected."
|
|
15008
|
+
)
|
|
15009
|
+
});
|
|
15010
|
+
var inspectArgs = z4.object({
|
|
15011
|
+
url: z4.string().describe("The MPP-protected resource URL to probe. No payment is made.")
|
|
15012
|
+
});
|
|
15013
|
+
var inspectOptions = z4.object({
|
|
15014
|
+
paymentMethod: z4.string().optional().describe('Only show challenges with this payment method (e.g. "inflow").'),
|
|
15015
|
+
intent: z4.string().optional().describe('Only show challenges with this intent (e.g. "charge").'),
|
|
15016
|
+
currency: z4.string().optional().describe('Only show challenges in this currency (e.g. "USDC").'),
|
|
15017
|
+
rail: z4.string().optional().describe('Only show challenges on this settlement rail (e.g. "balance", "instrument").'),
|
|
15018
|
+
method: z4.string().default("GET").describe("HTTP method for the probe request."),
|
|
15019
|
+
data: z4.string().optional().describe(
|
|
15020
|
+
"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
|
+
),
|
|
15022
|
+
header: z4.array(z4.string()).default([]).describe('Repeatable. "Name: Value" format.')
|
|
15023
|
+
});
|
|
15024
|
+
|
|
15025
|
+
// src/commands/mpp/status.tsx
|
|
15026
|
+
import { Box as Box13, Text as Text14 } from "ink";
|
|
15027
|
+
import Spinner10 from "ink-spinner";
|
|
15028
|
+
import { useEffect as useEffect8, useReducer as useReducer6 } from "react";
|
|
15029
|
+
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
15030
|
+
var MppStatusView = ({
|
|
15031
|
+
transactionId,
|
|
15032
|
+
fetchOnce,
|
|
15033
|
+
interval,
|
|
15034
|
+
maxAttempts,
|
|
15035
|
+
timeout,
|
|
15036
|
+
onComplete
|
|
15037
|
+
}) => {
|
|
15038
|
+
const initial = { kind: "polling" };
|
|
15039
|
+
const [phase, dispatch] = useReducer6(reduceMppStatus, initial);
|
|
15040
|
+
const { finish } = useFlowExit(onComplete);
|
|
15041
|
+
useEffect8(() => {
|
|
15042
|
+
const run = runMppStatus({ fetchOnce, interval, maxAttempts, timeout });
|
|
15043
|
+
let cancelled = false;
|
|
15044
|
+
void (async () => {
|
|
15045
|
+
for await (const event of run.events) {
|
|
15046
|
+
if (cancelled) return;
|
|
15047
|
+
dispatch(event);
|
|
15048
|
+
}
|
|
15049
|
+
})();
|
|
15050
|
+
return () => {
|
|
15051
|
+
cancelled = true;
|
|
15052
|
+
};
|
|
15053
|
+
}, [fetchOnce, interval, maxAttempts, timeout]);
|
|
15054
|
+
useEffect8(() => {
|
|
15055
|
+
if (phase.kind === "ready" || phase.kind === "failed" || phase.kind === "expired" || phase.kind === "timeout" || phase.kind === "error") {
|
|
15056
|
+
finish(phase);
|
|
15057
|
+
}
|
|
15058
|
+
}, [phase, finish]);
|
|
15059
|
+
if (phase.kind === "polling") {
|
|
15060
|
+
const stateText = phase.latest?.state ?? "pending";
|
|
15061
|
+
return /* @__PURE__ */ jsx16(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsxs13(Text14, { color: "cyan", children: [
|
|
15062
|
+
/* @__PURE__ */ jsx16(Spinner10, { type: "dots" }),
|
|
15063
|
+
" Polling transaction ",
|
|
15064
|
+
transactionId,
|
|
15065
|
+
" (state: ",
|
|
15066
|
+
stateText,
|
|
15067
|
+
")..."
|
|
15068
|
+
] }) });
|
|
15069
|
+
}
|
|
15070
|
+
if (phase.kind === "ready") {
|
|
15071
|
+
const credential = phase.response.credential ?? "";
|
|
15072
|
+
const preview = credential.length > 32 ? `${credential.slice(0, 32)}...` : credential;
|
|
15073
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
15074
|
+
/* @__PURE__ */ jsx16(Text14, { color: "green", children: "\u2713 Ready" }),
|
|
15075
|
+
/* @__PURE__ */ jsx16(Text14, { children: `credential: ${preview}` }),
|
|
15076
|
+
phase.response.expires !== void 0 ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: `expires: ${phase.response.expires}` }) : null
|
|
15077
|
+
] });
|
|
15078
|
+
}
|
|
15079
|
+
if (phase.kind === "failed") {
|
|
15080
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
15081
|
+
/* @__PURE__ */ jsx16(Text14, { color: "red", children: "\u2717 Transaction failed" }),
|
|
15082
|
+
phase.response.problem !== void 0 ? /* @__PURE__ */ jsx16(Text14, { color: "red", children: phase.response.problem.detail ?? phase.response.problem.title }) : null
|
|
15083
|
+
] });
|
|
15084
|
+
}
|
|
15085
|
+
if (phase.kind === "expired") {
|
|
15086
|
+
return /* @__PURE__ */ jsx16(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsx16(Text14, { color: "yellow", children: "Transaction expired before it was ready." }) });
|
|
15087
|
+
}
|
|
15088
|
+
if (phase.kind === "timeout") {
|
|
15089
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
15090
|
+
/* @__PURE__ */ jsx16(Text14, { color: "yellow", children: "Polling timed out before the transaction reached a ready state." }),
|
|
15091
|
+
phase.response !== void 0 ? /* @__PURE__ */ jsx16(Text14, { children: `last state: ${phase.response.state}` }) : null
|
|
15092
|
+
] });
|
|
15093
|
+
}
|
|
15094
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
15095
|
+
/* @__PURE__ */ jsx16(Text14, { color: "red", children: "\u2717 Polling failed" }),
|
|
15096
|
+
/* @__PURE__ */ jsx16(Text14, { color: "red", children: phase.message })
|
|
15097
|
+
] });
|
|
15098
|
+
};
|
|
15099
|
+
|
|
15100
|
+
// src/commands/mpp/supported.tsx
|
|
15101
|
+
import { Box as Box14, Text as Text15 } from "ink";
|
|
15102
|
+
import Spinner11 from "ink-spinner";
|
|
15103
|
+
import { useCallback as useCallback6 } from "react";
|
|
15104
|
+
import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
15105
|
+
function flattenKinds(response) {
|
|
15106
|
+
const rows = [];
|
|
15107
|
+
for (const kind of response.kinds) {
|
|
15108
|
+
for (const intent of kind.intents) {
|
|
15109
|
+
for (const rail of intent.rails) {
|
|
15110
|
+
rows.push({
|
|
15111
|
+
method: kind.method,
|
|
15112
|
+
intent: intent.intent,
|
|
15113
|
+
rail: rail.rail,
|
|
15114
|
+
currencies: rail.currencies.join(", ") || "\u2014"
|
|
15115
|
+
});
|
|
15116
|
+
}
|
|
15117
|
+
}
|
|
15118
|
+
}
|
|
15119
|
+
return rows;
|
|
15120
|
+
}
|
|
15121
|
+
var COLUMNS4 = [
|
|
15122
|
+
{ header: "Method", cell: (r) => r.method },
|
|
15123
|
+
{ header: "Intent", cell: (r) => r.intent },
|
|
15124
|
+
{ header: "Rail", cell: (r) => r.rail },
|
|
15125
|
+
{ header: "Currencies", cell: (r) => r.currencies }
|
|
15126
|
+
];
|
|
15127
|
+
var SupportedView = ({ load, onComplete }) => {
|
|
15128
|
+
const action = useCallback6(() => load(), [load]);
|
|
15129
|
+
const { finish } = useFlowExit(onComplete);
|
|
15130
|
+
const { status, data, error } = useFlowState(action, finish);
|
|
15131
|
+
if (status === "loading") {
|
|
15132
|
+
return /* @__PURE__ */ jsx17(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { color: "cyan", children: [
|
|
15133
|
+
/* @__PURE__ */ jsx17(Spinner11, { type: "dots" }),
|
|
15134
|
+
" Loading supported MPP methods..."
|
|
15135
|
+
] }) });
|
|
15136
|
+
}
|
|
15137
|
+
if (status === "error") {
|
|
15138
|
+
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
|
|
15139
|
+
/* @__PURE__ */ jsx17(Text15, { color: "red", children: "Failed to load supported MPP methods" }),
|
|
15140
|
+
/* @__PURE__ */ jsx17(Text15, { color: "red", children: error })
|
|
15141
|
+
] });
|
|
15142
|
+
}
|
|
15143
|
+
const rows = data ? flattenKinds(data) : [];
|
|
15144
|
+
if (rows.length === 0) {
|
|
15145
|
+
return /* @__PURE__ */ jsx17(Box14, { flexDirection: "column", children: /* @__PURE__ */ jsx17(Text15, { children: "No supported MPP methods returned for this account." }) });
|
|
15146
|
+
}
|
|
15147
|
+
return /* @__PURE__ */ jsx17(Table, { columns: COLUMNS4, rows });
|
|
15148
|
+
};
|
|
15149
|
+
|
|
15150
|
+
// src/commands/mpp/index.tsx
|
|
15151
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
15152
|
+
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
|
+
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 parseHeaderFlagsOrFail(c, flags) {
|
|
15155
|
+
try {
|
|
15156
|
+
return parseHeaderFlags(flags);
|
|
15157
|
+
} catch (err) {
|
|
15158
|
+
c.error({ code: "INVALID_HEADER", message: err instanceof Error ? err.message : String(err) });
|
|
15159
|
+
}
|
|
15160
|
+
}
|
|
15161
|
+
function decorateCredentialField(frame, credential, credentialFile) {
|
|
15162
|
+
if (credentialFile !== void 0 && credentialFile.length > 0) {
|
|
15163
|
+
const absolute = resolvePath2(credentialFile);
|
|
15164
|
+
writeFileSync2(absolute, Buffer.from(credential, "utf-8"), { mode: 384 });
|
|
15165
|
+
chmodSync(absolute, 384);
|
|
15166
|
+
frame.credential_saved_to = absolute;
|
|
15167
|
+
return;
|
|
15168
|
+
}
|
|
15169
|
+
frame.credential = credential;
|
|
15170
|
+
}
|
|
15171
|
+
function probeOptionsFrom(c) {
|
|
15172
|
+
const headers = parseHeaderFlagsOrFail(c, c.options.header);
|
|
15173
|
+
return {
|
|
15174
|
+
method: c.options.method,
|
|
15175
|
+
headers,
|
|
15176
|
+
...c.options.data !== void 0 ? { data: c.options.data } : {}
|
|
15177
|
+
};
|
|
15178
|
+
}
|
|
15179
|
+
function buildPayPipelineInput(c) {
|
|
15180
|
+
return {
|
|
15181
|
+
probeOptions: probeOptionsFrom(c),
|
|
15182
|
+
url: c.args.url,
|
|
15183
|
+
showBody: c.options.showBody,
|
|
15184
|
+
interval: c.options.interval,
|
|
15185
|
+
maxAttempts: c.options.maxAttempts,
|
|
15186
|
+
timeout: c.options.timeout,
|
|
15187
|
+
...c.options.instrumentId !== void 0 ? { instrumentId: c.options.instrumentId } : {},
|
|
15188
|
+
...c.options.paymentMethod !== void 0 ? { paymentMethodFilter: c.options.paymentMethod } : {},
|
|
15189
|
+
...c.options.intent !== void 0 ? { intentFilter: c.options.intent } : {},
|
|
15190
|
+
...c.options.currency !== void 0 ? { currencyFilter: c.options.currency } : {},
|
|
15191
|
+
...c.options.rail !== void 0 ? { railFilter: c.options.rail } : {},
|
|
15192
|
+
...c.options.outputFile !== void 0 ? { outputFile: c.options.outputFile } : {}
|
|
15193
|
+
};
|
|
15194
|
+
}
|
|
15195
|
+
function attachBodyFields(frame, result) {
|
|
15196
|
+
frame.body_size_bytes = result.bodySizeBytes;
|
|
15197
|
+
if (result.body !== void 0) frame.body = result.body;
|
|
15198
|
+
if (result.bodyBase64 !== void 0) frame.body_base64 = result.bodyBase64;
|
|
15199
|
+
if (result.outputSavedTo !== void 0) frame.output_saved_to = result.outputSavedTo;
|
|
15200
|
+
}
|
|
15201
|
+
function challengeFields(challenge) {
|
|
15202
|
+
const out = {
|
|
15203
|
+
id: challenge.id,
|
|
15204
|
+
method: challenge.method,
|
|
15205
|
+
intent: challenge.intent
|
|
15206
|
+
};
|
|
15207
|
+
if (challenge.amount !== void 0) out.amount = challenge.amount;
|
|
15208
|
+
if (challenge.currency !== void 0) out.currency = challenge.currency;
|
|
15209
|
+
if (challenge.rail !== void 0) out.rail = challenge.rail;
|
|
15210
|
+
return out;
|
|
15211
|
+
}
|
|
15212
|
+
function noPaymentFrameFromResult(result) {
|
|
15213
|
+
const frame = { outcome: "no-payment-required", status: result.status };
|
|
15214
|
+
if (result.contentType !== void 0) frame.content_type = result.contentType;
|
|
15215
|
+
attachBodyFields(frame, result);
|
|
15216
|
+
return frame;
|
|
15217
|
+
}
|
|
15218
|
+
function createdFrameFromEvent(created, interval, maxAttempts) {
|
|
15219
|
+
const pending = created.state === "pending";
|
|
15220
|
+
const frame = {
|
|
15221
|
+
transaction_id: created.transactionId,
|
|
15222
|
+
state: created.state,
|
|
15223
|
+
challenge: challengeFields(created.challenge),
|
|
15224
|
+
instruction: interval > 0 ? POLLING_INSTRUCTION : POST_CREATE_INSTRUCTION
|
|
15225
|
+
};
|
|
15226
|
+
if (created.approvalId !== void 0) frame.approval_id = created.approvalId;
|
|
15227
|
+
if (created.approvalUrl !== void 0) frame.approval_url = created.approvalUrl;
|
|
15228
|
+
if (created.retryAfterSeconds !== void 0) frame.retry_after_seconds = created.retryAfterSeconds;
|
|
15229
|
+
if (created.expires !== void 0) frame.expires = created.expires;
|
|
15230
|
+
if (pending && interval <= 0) {
|
|
15231
|
+
const max = maxAttempts > 0 ? maxAttempts : 60;
|
|
15232
|
+
frame._next = {
|
|
15233
|
+
command: `mpp status ${created.transactionId} --interval 5 --max-attempts ${String(max)}`,
|
|
15234
|
+
poll_interval_seconds: 5,
|
|
15235
|
+
until: "state is ready (credential present)"
|
|
15236
|
+
};
|
|
15237
|
+
}
|
|
15238
|
+
return frame;
|
|
15239
|
+
}
|
|
15240
|
+
function paidFrameFromResult(result, credentialFile) {
|
|
15241
|
+
const frame = {
|
|
15242
|
+
outcome: "paid",
|
|
15243
|
+
transaction_id: result.transactionId,
|
|
15244
|
+
challenge_id: result.challengeId,
|
|
15245
|
+
intent: result.intent,
|
|
15246
|
+
response_status: result.responseStatus
|
|
15247
|
+
};
|
|
15248
|
+
decorateCredentialField(frame, result.credential, credentialFile);
|
|
15249
|
+
if (result.responseContentType !== void 0) frame.response_content_type = result.responseContentType;
|
|
15250
|
+
if (result.settled !== void 0) frame.settled = result.settled;
|
|
15251
|
+
attachBodyFields(frame, result);
|
|
15252
|
+
return frame;
|
|
15253
|
+
}
|
|
15254
|
+
function rejectedFrameFromResult(result) {
|
|
15255
|
+
const frame = {
|
|
15256
|
+
outcome: "seller-rejected",
|
|
15257
|
+
transaction_id: result.transactionId,
|
|
15258
|
+
challenge_id: result.challengeId,
|
|
15259
|
+
response_status: result.responseStatus
|
|
15260
|
+
};
|
|
15261
|
+
if (result.responseContentType !== void 0) frame.response_content_type = result.responseContentType;
|
|
15262
|
+
attachBodyFields(frame, result);
|
|
15263
|
+
return frame;
|
|
15264
|
+
}
|
|
15265
|
+
function toStatusFrame(response, credentialFile) {
|
|
15266
|
+
const frame = {
|
|
15267
|
+
transaction_id: response.transactionId ?? "",
|
|
15268
|
+
state: response.state
|
|
15269
|
+
};
|
|
15270
|
+
if (response.state === "ready" && response.credential !== void 0) {
|
|
15271
|
+
decorateCredentialField(frame, response.credential, credentialFile);
|
|
15272
|
+
if (response.expires !== void 0) frame.expires = response.expires;
|
|
15273
|
+
}
|
|
15274
|
+
if (response.state === "pending") {
|
|
15275
|
+
if (response.approvalId !== void 0) frame.approval_id = response.approvalId;
|
|
15276
|
+
if (response.retryAfterSeconds !== void 0) frame.retry_after_seconds = response.retryAfterSeconds;
|
|
15277
|
+
}
|
|
15278
|
+
if (response.problem !== void 0) frame.problem = response.problem;
|
|
15279
|
+
return frame;
|
|
15280
|
+
}
|
|
15281
|
+
async function* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2) {
|
|
15282
|
+
assertSessionGuard(c, authStorage2, inflow2);
|
|
15283
|
+
if (!c.agent && !c.formatExplicit) {
|
|
15284
|
+
const client = await inflow2.mpp.client();
|
|
15285
|
+
let finalPhase = null;
|
|
15286
|
+
await renderInkUntilExit(
|
|
15287
|
+
/* @__PURE__ */ jsx18(
|
|
15288
|
+
PayView,
|
|
15289
|
+
{
|
|
15290
|
+
url: c.args.url,
|
|
15291
|
+
method: c.options.method,
|
|
15292
|
+
deps: {
|
|
15293
|
+
...buildPayPipelineInput(c),
|
|
15294
|
+
client,
|
|
15295
|
+
apiBaseUrl: apiBaseUrl2,
|
|
15296
|
+
awaitPayment: true
|
|
15297
|
+
},
|
|
15298
|
+
onComplete: (phase) => {
|
|
15299
|
+
finalPhase = phase;
|
|
15300
|
+
},
|
|
15301
|
+
onCancel: (approvalId) => inflow2.mpp.cancel({ approvalId })
|
|
15302
|
+
}
|
|
15303
|
+
)
|
|
15304
|
+
);
|
|
15305
|
+
if (finalPhase !== null) {
|
|
15306
|
+
const phase = finalPhase;
|
|
15307
|
+
if (phase.kind === "seller-rejected") {
|
|
15308
|
+
c.error({
|
|
15309
|
+
code: PAYMENT_NOT_ACCEPTED_CODE2,
|
|
15310
|
+
message: `Seller rejected the credential with status ${String(phase.result.responseStatus)}. The transaction was ready but the seller did not honour the payment.`
|
|
15311
|
+
});
|
|
15312
|
+
}
|
|
15313
|
+
if (phase.kind === "error") {
|
|
15314
|
+
c.error({ code: phase.code, message: phase.message });
|
|
15315
|
+
}
|
|
15316
|
+
}
|
|
15317
|
+
return;
|
|
15318
|
+
}
|
|
15319
|
+
const run = inflow2.mpp.pay({
|
|
15320
|
+
...buildPayPipelineInput(c),
|
|
15321
|
+
awaitPayment: c.options.interval > 0
|
|
15322
|
+
});
|
|
15323
|
+
for await (const event of run.events) {
|
|
15324
|
+
if (event.type === "short-circuited") {
|
|
15325
|
+
yield sanitizeDeep(noPaymentFrameFromResult(event.result));
|
|
15326
|
+
return;
|
|
15327
|
+
}
|
|
15328
|
+
if (event.type === "created") {
|
|
15329
|
+
yield sanitizeDeep(createdFrameFromEvent(event.created, c.options.interval, c.options.maxAttempts));
|
|
15330
|
+
continue;
|
|
15331
|
+
}
|
|
15332
|
+
if (event.type === "replayed") {
|
|
15333
|
+
yield sanitizeDeep(paidFrameFromResult(event.result, c.options.credentialFile));
|
|
15334
|
+
return;
|
|
15335
|
+
}
|
|
15336
|
+
if (event.type === "rejected") {
|
|
15337
|
+
yield sanitizeDeep(rejectedFrameFromResult(event.result));
|
|
15338
|
+
c.error({
|
|
15339
|
+
code: PAYMENT_NOT_ACCEPTED_CODE2,
|
|
15340
|
+
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
|
+
});
|
|
15342
|
+
return;
|
|
15343
|
+
}
|
|
15344
|
+
if (event.type === "errored") {
|
|
15345
|
+
c.error({ code: event.code, message: event.message });
|
|
15346
|
+
}
|
|
15347
|
+
}
|
|
15348
|
+
}
|
|
15349
|
+
async function* runStatusCommand(c, inflow2, authStorage2) {
|
|
15350
|
+
assertSessionGuard(c, authStorage2, inflow2);
|
|
15351
|
+
if (!c.agent && !c.formatExplicit) {
|
|
15352
|
+
const client2 = await inflow2.mpp.client();
|
|
15353
|
+
await renderInkUntilExit(
|
|
15354
|
+
/* @__PURE__ */ jsx18(
|
|
15355
|
+
MppStatusView,
|
|
15356
|
+
{
|
|
15357
|
+
transactionId: c.args.transactionId,
|
|
15358
|
+
fetchOnce: () => client2.getTransaction(c.args.transactionId),
|
|
15359
|
+
interval: c.options.interval,
|
|
15360
|
+
maxAttempts: c.options.maxAttempts,
|
|
15361
|
+
timeout: c.options.timeout,
|
|
15362
|
+
onComplete: () => void 0
|
|
15363
|
+
}
|
|
15364
|
+
)
|
|
15365
|
+
);
|
|
15366
|
+
return;
|
|
15367
|
+
}
|
|
15368
|
+
const client = await inflow2.mpp.client();
|
|
15369
|
+
const fetchOnce = () => client.getTransaction(c.args.transactionId);
|
|
15370
|
+
if (c.options.interval <= 0) {
|
|
15371
|
+
const snapshot = await fetchOnce();
|
|
15372
|
+
yield sanitizeDeep(toStatusFrame(snapshot, c.options.credentialFile));
|
|
15373
|
+
return;
|
|
15374
|
+
}
|
|
15375
|
+
const run = runMppStatus({
|
|
15376
|
+
fetchOnce,
|
|
15377
|
+
interval: c.options.interval,
|
|
15378
|
+
maxAttempts: c.options.maxAttempts,
|
|
15379
|
+
timeout: c.options.timeout
|
|
15380
|
+
});
|
|
15381
|
+
for await (const event of run.events) {
|
|
15382
|
+
if (event.type === "snapshot") {
|
|
15383
|
+
yield sanitizeDeep(toStatusFrame(event.response, c.options.credentialFile));
|
|
15384
|
+
continue;
|
|
15385
|
+
}
|
|
15386
|
+
if (event.type === "ready") {
|
|
15387
|
+
yield sanitizeDeep(toStatusFrame(event.response, c.options.credentialFile));
|
|
15388
|
+
return;
|
|
15389
|
+
}
|
|
15390
|
+
if (event.type === "failed") {
|
|
15391
|
+
yield sanitizeDeep(toStatusFrame(event.response, c.options.credentialFile));
|
|
15392
|
+
c.error({
|
|
15393
|
+
code: "PAYMENT_FAILED",
|
|
15394
|
+
message: event.response.problem?.detail ?? event.response.problem?.title ?? "MPP transaction failed."
|
|
15395
|
+
});
|
|
15396
|
+
}
|
|
15397
|
+
if (event.type === "expired") {
|
|
15398
|
+
yield sanitizeDeep(toStatusFrame(event.response, c.options.credentialFile));
|
|
15399
|
+
c.error({ code: "PAYMENT_EXPIRED", message: "MPP transaction expired before it was ready." });
|
|
15400
|
+
}
|
|
15401
|
+
if (event.type === "timedOut") {
|
|
15402
|
+
if (event.response !== void 0) {
|
|
15403
|
+
yield sanitizeDeep(toStatusFrame(event.response, c.options.credentialFile));
|
|
15404
|
+
}
|
|
15405
|
+
c.error({
|
|
15406
|
+
code: "POLLING_TIMEOUT",
|
|
15407
|
+
message: "Polling timed out before the transaction reached a ready state.",
|
|
15408
|
+
retryable: true
|
|
15409
|
+
});
|
|
15410
|
+
}
|
|
15411
|
+
if (event.type === "crashed") {
|
|
15412
|
+
c.error({ code: "PAYMENT_FAILED", message: event.message });
|
|
15413
|
+
}
|
|
15414
|
+
}
|
|
15415
|
+
}
|
|
15416
|
+
async function runCancelCommand(c, inflow2, authStorage2) {
|
|
15417
|
+
assertSessionGuard(c, authStorage2, inflow2);
|
|
15418
|
+
if (!c.agent && !c.formatExplicit) {
|
|
15419
|
+
await renderInkUntilExit(
|
|
15420
|
+
/* @__PURE__ */ jsx18(
|
|
15421
|
+
CancelView,
|
|
15422
|
+
{
|
|
15423
|
+
approvalId: c.args.approvalId,
|
|
15424
|
+
cancel: () => inflow2.mpp.cancel({ approvalId: c.args.approvalId }).then(() => void 0),
|
|
15425
|
+
onComplete: () => void 0
|
|
15426
|
+
}
|
|
15427
|
+
)
|
|
15428
|
+
);
|
|
15429
|
+
return { approval_id: c.args.approvalId, cancelled: true, note: "best-effort; server-side state not verified" };
|
|
15430
|
+
}
|
|
15431
|
+
return inflow2.mpp.cancel({ approvalId: c.args.approvalId });
|
|
15432
|
+
}
|
|
15433
|
+
async function runDecodeCommand(c) {
|
|
15434
|
+
let result;
|
|
15435
|
+
try {
|
|
15436
|
+
result = decodeMppValue(c.args.value);
|
|
15437
|
+
} catch (err) {
|
|
15438
|
+
return c.error({ code: "DECODE_FAILED", message: err instanceof Error ? err.message : String(err) });
|
|
15439
|
+
}
|
|
15440
|
+
if (!c.agent && !c.formatExplicit) {
|
|
15441
|
+
await renderInkUntilExit(/* @__PURE__ */ jsx18(DecodeView, { result }));
|
|
15442
|
+
return void 0;
|
|
15443
|
+
}
|
|
15444
|
+
return sanitizeDeep(result);
|
|
15445
|
+
}
|
|
15446
|
+
async function runSupportedCommand(c, inflow2, authStorage2) {
|
|
15447
|
+
assertSessionGuard(c, authStorage2, inflow2);
|
|
15448
|
+
if (!c.agent && !c.formatExplicit) {
|
|
15449
|
+
await renderInkUntilExit(/* @__PURE__ */ jsx18(SupportedView, { load: () => inflow2.mpp.supported(), onComplete: () => void 0 }));
|
|
15450
|
+
return void 0;
|
|
15451
|
+
}
|
|
15452
|
+
const response = await inflow2.mpp.supported();
|
|
15453
|
+
return sanitizeDeep(response);
|
|
15454
|
+
}
|
|
15455
|
+
async function runInspectCommand(c) {
|
|
15456
|
+
const deps = {
|
|
15457
|
+
probeOptions: probeOptionsFrom(c),
|
|
15458
|
+
url: c.args.url,
|
|
15459
|
+
...c.options.paymentMethod !== void 0 ? { paymentMethodFilter: c.options.paymentMethod } : {},
|
|
15460
|
+
...c.options.intent !== void 0 ? { intentFilter: c.options.intent } : {},
|
|
15461
|
+
...c.options.currency !== void 0 ? { currencyFilter: c.options.currency } : {},
|
|
15462
|
+
...c.options.rail !== void 0 ? { railFilter: c.options.rail } : {}
|
|
15463
|
+
};
|
|
15464
|
+
if (!c.agent && !c.formatExplicit) {
|
|
15465
|
+
let finalPhase = null;
|
|
15466
|
+
await renderInkUntilExit(
|
|
15467
|
+
/* @__PURE__ */ jsx18(
|
|
15468
|
+
InspectView,
|
|
15469
|
+
{
|
|
15470
|
+
url: c.args.url,
|
|
15471
|
+
method: c.options.method,
|
|
15472
|
+
deps,
|
|
15473
|
+
onComplete: (phase) => {
|
|
15474
|
+
finalPhase = phase;
|
|
15475
|
+
}
|
|
15476
|
+
}
|
|
15477
|
+
)
|
|
15478
|
+
);
|
|
15479
|
+
if (finalPhase !== null) {
|
|
15480
|
+
const phase = finalPhase;
|
|
15481
|
+
if (phase.kind === "error") {
|
|
15482
|
+
c.error({ code: phase.code, message: phase.message });
|
|
15483
|
+
}
|
|
15484
|
+
}
|
|
15485
|
+
return void 0;
|
|
15486
|
+
}
|
|
15487
|
+
let finalEvent = null;
|
|
15488
|
+
await runMppInspectPipeline(deps, (event) => {
|
|
15489
|
+
if (event.type === "errored") {
|
|
15490
|
+
finalEvent = { kind: "error", payload: event };
|
|
15491
|
+
return;
|
|
15492
|
+
}
|
|
15493
|
+
if (event.type === "challenges") {
|
|
15494
|
+
finalEvent = { kind: "challenges", payload: event.result };
|
|
15495
|
+
return;
|
|
15496
|
+
}
|
|
15497
|
+
if (event.type === "no-payment") {
|
|
15498
|
+
finalEvent = { kind: "no-payment", payload: event.result };
|
|
15499
|
+
}
|
|
15500
|
+
});
|
|
15501
|
+
if (finalEvent === null) {
|
|
15502
|
+
return c.error({ code: "INSPECT_FAILED", message: "Inspect pipeline produced no result." });
|
|
15503
|
+
}
|
|
15504
|
+
const { kind, payload } = finalEvent;
|
|
15505
|
+
if (kind === "error") {
|
|
15506
|
+
const err = payload;
|
|
15507
|
+
return c.error({ code: err.code, message: err.message });
|
|
15508
|
+
}
|
|
15509
|
+
if (kind === "challenges") {
|
|
15510
|
+
return sanitizeDeep(buildChallengesFrame(payload));
|
|
15511
|
+
}
|
|
15512
|
+
return sanitizeDeep(buildNoPaymentFrame(payload));
|
|
15513
|
+
}
|
|
15514
|
+
function createMppCli(inflow2, authStorage2, apiBaseUrl2) {
|
|
15515
|
+
const cli2 = Cli4.create("mpp", {
|
|
15516
|
+
description: "MPP payment commands (pay, inspect, status, cancel, decode, supported)."
|
|
15517
|
+
});
|
|
15518
|
+
cli2.command("pay", {
|
|
15519
|
+
description: "Pay an MPP-protected resource and return the seller response.",
|
|
15520
|
+
args: payArgs,
|
|
15521
|
+
options: payOptions,
|
|
15522
|
+
outputPolicy: "agent-only",
|
|
15523
|
+
async *run(c) {
|
|
15524
|
+
yield* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2);
|
|
15525
|
+
}
|
|
15526
|
+
});
|
|
15527
|
+
cli2.command("status", {
|
|
15528
|
+
description: "Poll the buyer-side state of an in-flight MPP transaction.",
|
|
15529
|
+
args: statusArgs,
|
|
15530
|
+
options: statusOptions2,
|
|
15531
|
+
outputPolicy: "agent-only",
|
|
15532
|
+
async *run(c) {
|
|
15533
|
+
yield* runStatusCommand(c, inflow2, authStorage2);
|
|
15534
|
+
}
|
|
15535
|
+
});
|
|
15536
|
+
cli2.command("cancel", {
|
|
15537
|
+
description: "Best-effort cancel of an MPP approval.",
|
|
15538
|
+
args: cancelArgs,
|
|
15539
|
+
outputPolicy: "agent-only",
|
|
15540
|
+
async run(c) {
|
|
15541
|
+
return runCancelCommand(c, inflow2, authStorage2);
|
|
15542
|
+
}
|
|
15543
|
+
});
|
|
15544
|
+
cli2.command("decode", {
|
|
15545
|
+
description: "Decode a raw WWW-Authenticate: Payment header, or a base64url credential / receipt.",
|
|
15546
|
+
args: decodeArgs,
|
|
15547
|
+
outputPolicy: "agent-only",
|
|
15548
|
+
async run(c) {
|
|
15549
|
+
return runDecodeCommand(c);
|
|
15550
|
+
}
|
|
15551
|
+
});
|
|
15552
|
+
cli2.command("supported", {
|
|
15553
|
+
description: "List the methods the buyer can pay with \u2014 by intent, settlement rail, and currency.",
|
|
15554
|
+
outputPolicy: "agent-only",
|
|
15555
|
+
async run(c) {
|
|
15556
|
+
return runSupportedCommand(c, inflow2, authStorage2);
|
|
15557
|
+
}
|
|
15558
|
+
});
|
|
15559
|
+
cli2.command("inspect", {
|
|
15560
|
+
description: "Show the seller's MPP challenge(s) for a URL. Read-only probe \u2014 no auth, no payment.",
|
|
15561
|
+
args: inspectArgs,
|
|
15562
|
+
options: inspectOptions,
|
|
15563
|
+
outputPolicy: "agent-only",
|
|
15564
|
+
async run(c) {
|
|
15565
|
+
return runInspectCommand(c);
|
|
15566
|
+
}
|
|
15567
|
+
});
|
|
15568
|
+
return cli2;
|
|
15569
|
+
}
|
|
15570
|
+
|
|
15571
|
+
// src/commands/user/index.tsx
|
|
15572
|
+
import { Cli as Cli5, Errors as Errors2 } from "incur";
|
|
15573
|
+
import "react";
|
|
15574
|
+
|
|
15575
|
+
// src/commands/user/get.tsx
|
|
15576
|
+
import { Box as Box15, Text as Text16 } from "ink";
|
|
15577
|
+
import Spinner12 from "ink-spinner";
|
|
15578
|
+
import { useCallback as useCallback7, useRef as useRef3 } from "react";
|
|
15579
|
+
import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
15580
|
+
var UserGet = ({ userResource, onComplete }) => {
|
|
15581
|
+
const action = useCallback7(() => userResource.retrieve(), [userResource]);
|
|
15582
|
+
const { finish } = useFlowExit(onComplete);
|
|
15583
|
+
const lastErrorRef = useRef3("");
|
|
15584
|
+
const handleLinger = useCallback7(
|
|
15585
|
+
(result) => {
|
|
15586
|
+
if (result !== null) {
|
|
15587
|
+
finish({ kind: "success", user: result });
|
|
15588
|
+
} else {
|
|
15589
|
+
finish({
|
|
15590
|
+
kind: "error",
|
|
15591
|
+
message: lastErrorRef.current || "No result produced."
|
|
15592
|
+
});
|
|
15593
|
+
}
|
|
15594
|
+
},
|
|
15595
|
+
[finish]
|
|
15596
|
+
);
|
|
15597
|
+
const { status, data: user, error } = useFlowState(action, handleLinger);
|
|
15598
|
+
lastErrorRef.current = error;
|
|
15599
|
+
if (status === "loading") {
|
|
15600
|
+
return /* @__PURE__ */ jsx19(Box15, { children: /* @__PURE__ */ jsxs15(Text16, { color: "cyan", children: [
|
|
15601
|
+
/* @__PURE__ */ jsx19(Spinner12, { type: "dots" }),
|
|
15602
|
+
" Loading user..."
|
|
15603
|
+
] }) });
|
|
15604
|
+
}
|
|
15605
|
+
if (status === "error") {
|
|
15606
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
|
|
15607
|
+
/* @__PURE__ */ jsx19(Text16, { color: "red", children: "\u2717 Failed to retrieve user" }),
|
|
15608
|
+
/* @__PURE__ */ jsx19(Text16, { color: "red", children: error })
|
|
15609
|
+
] });
|
|
15610
|
+
}
|
|
15611
|
+
if (user === null) return null;
|
|
15612
|
+
const rows = buildProfileRows(user);
|
|
15613
|
+
return /* @__PURE__ */ jsx19(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: rows.map((row) => /* @__PURE__ */ jsxs15(Text16, { children: [
|
|
15614
|
+
`${row.label}: `,
|
|
15615
|
+
row.value !== null ? /* @__PURE__ */ jsx19(Text16, { bold: true, children: row.value }) : /* @__PURE__ */ jsx19(Text16, { dimColor: true, children: "\u2014" })
|
|
15616
|
+
] }, row.label)) });
|
|
15617
|
+
};
|
|
15618
|
+
|
|
15619
|
+
// src/commands/user/schema.ts
|
|
15620
|
+
import { z as z5 } from "incur";
|
|
15621
|
+
var getOptions = z5.object({});
|
|
15622
|
+
|
|
15623
|
+
// src/commands/user/index.tsx
|
|
15624
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
15625
|
+
var TTY_NO_RESULT_MESSAGE = "inflow user get exited without producing a result.";
|
|
15626
|
+
async function runUserGet2(c, deps) {
|
|
15627
|
+
assertSessionGuard(c, deps.authStorage, deps.inflow);
|
|
15628
|
+
if (!c.agent && !c.formatExplicit) {
|
|
15629
|
+
let captured = null;
|
|
15630
|
+
const outcome = await renderInkUntilExit(
|
|
15631
|
+
/* @__PURE__ */ jsx20(
|
|
15632
|
+
UserGet,
|
|
15633
|
+
{
|
|
15634
|
+
userResource: deps.user,
|
|
15635
|
+
onComplete: (value) => {
|
|
15636
|
+
captured = value;
|
|
15637
|
+
}
|
|
15638
|
+
}
|
|
15639
|
+
),
|
|
15640
|
+
() => captured
|
|
15641
|
+
);
|
|
15642
|
+
if (outcome === null) {
|
|
15643
|
+
throw new Errors2.IncurError({
|
|
15644
|
+
code: "USER_GET_FAILED",
|
|
15645
|
+
message: TTY_NO_RESULT_MESSAGE
|
|
15646
|
+
});
|
|
15647
|
+
}
|
|
15648
|
+
if (outcome.kind === "error") {
|
|
15649
|
+
throw new Errors2.IncurError({
|
|
15650
|
+
code: "USER_GET_FAILED",
|
|
15651
|
+
message: outcome.message
|
|
15652
|
+
});
|
|
15653
|
+
}
|
|
15654
|
+
return projectUserPayload(outcome.user);
|
|
15655
|
+
}
|
|
15656
|
+
return deps.user.get();
|
|
15657
|
+
}
|
|
15658
|
+
function createUserCli(user, authStorage2, inflow2) {
|
|
15659
|
+
const cli2 = Cli5.create("user", { description: "User profile commands" });
|
|
15660
|
+
cli2.command("get", {
|
|
15661
|
+
description: "Retrieve the current authenticated user",
|
|
15662
|
+
options: getOptions,
|
|
15663
|
+
outputPolicy: "agent-only",
|
|
15664
|
+
async run(c) {
|
|
15665
|
+
return runUserGet2(c, { user, authStorage: authStorage2, inflow: inflow2 });
|
|
15666
|
+
}
|
|
15667
|
+
});
|
|
15668
|
+
return cli2;
|
|
15669
|
+
}
|
|
15670
|
+
|
|
15671
|
+
// src/commands/x402/index.tsx
|
|
15672
|
+
import { chmodSync as chmodSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
15673
|
+
import { resolve as resolvePath3 } from "path";
|
|
15674
|
+
import { Cli as Cli6 } from "incur";
|
|
15675
|
+
|
|
15676
|
+
// src/commands/x402/cancel.tsx
|
|
15677
|
+
import { Box as Box16, Text as Text17 } from "ink";
|
|
15678
|
+
import Spinner13 from "ink-spinner";
|
|
15679
|
+
import { useCallback as useCallback8 } from "react";
|
|
15680
|
+
import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
15681
|
+
var CancelView2 = ({ approvalId, cancel, onComplete }) => {
|
|
15682
|
+
const action = useCallback8(() => cancel(), [cancel]);
|
|
15683
|
+
const { finish } = useFlowExit(onComplete);
|
|
15684
|
+
const { status } = useFlowState(action, finish);
|
|
15685
|
+
if (status === "loading") {
|
|
15686
|
+
return /* @__PURE__ */ jsx21(Box16, { children: /* @__PURE__ */ jsxs16(Text17, { color: "cyan", children: [
|
|
15687
|
+
/* @__PURE__ */ jsx21(Spinner13, { type: "dots" }),
|
|
15688
|
+
" Cancelling approval ",
|
|
15689
|
+
approvalId,
|
|
15690
|
+
"..."
|
|
15691
|
+
] }) });
|
|
15692
|
+
}
|
|
15693
|
+
return /* @__PURE__ */ jsx21(Box16, { children: /* @__PURE__ */ jsxs16(Text17, { color: "green", children: [
|
|
15694
|
+
"\u2713 Cancelled approval ",
|
|
15695
|
+
approvalId,
|
|
15696
|
+
" (best-effort)"
|
|
15697
|
+
] }) });
|
|
15698
|
+
};
|
|
15699
|
+
|
|
15700
|
+
// src/commands/x402/decode.tsx
|
|
15701
|
+
import { fromFoundationRequirements as fromFoundationRequirements2 } from "@inflowpayai/x402-buyer";
|
|
15702
|
+
import { Box as Box17, Text as Text18 } from "ink";
|
|
15703
|
+
import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
15704
|
+
var DecodeView2 = ({ decoded }) => {
|
|
15705
|
+
const summary = summarizeAccepts(fromFoundationRequirements2(decoded.accepts));
|
|
15706
|
+
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", paddingY: 1, children: [
|
|
15707
|
+
/* @__PURE__ */ jsx22(Box17, { marginBottom: 1, children: /* @__PURE__ */ jsx22(Text18, { bold: true, children: "Decoded PAYMENT-REQUIRED" }) }),
|
|
15708
|
+
/* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
|
|
15709
|
+
/* @__PURE__ */ jsxs17(Text18, { children: [
|
|
15710
|
+
"x402Version: ",
|
|
15711
|
+
/* @__PURE__ */ jsx22(Text18, { color: "cyan", children: String(decoded.x402Version) })
|
|
15712
|
+
] }),
|
|
15713
|
+
/* @__PURE__ */ jsxs17(Text18, { children: [
|
|
15714
|
+
"resource: ",
|
|
15715
|
+
/* @__PURE__ */ jsx22(Text18, { color: "cyan", children: decoded.resource.url })
|
|
15716
|
+
] }),
|
|
15717
|
+
/* @__PURE__ */ jsx22(Text18, { children: `accepts (${String(summary.length)}):` }),
|
|
15718
|
+
summary.map((entry, idx) => /* @__PURE__ */ jsxs17(Text18, { children: [
|
|
15719
|
+
" ",
|
|
15720
|
+
/* @__PURE__ */ jsx22(Text18, { color: "yellow", children: entry.scheme }),
|
|
15721
|
+
" / ",
|
|
15722
|
+
/* @__PURE__ */ jsx22(Text18, { color: "yellow", children: entry.network }),
|
|
15723
|
+
entry.amount !== void 0 ? ` \xB7 amount ${entry.amount}` : "",
|
|
15724
|
+
entry.asset !== void 0 ? ` \xB7 ${entry.asset}` : ""
|
|
15725
|
+
] }, `${entry.scheme}-${entry.network}-${String(idx)}`)),
|
|
15726
|
+
decoded.extensions !== void 0 ? /* @__PURE__ */ jsx22(Text18, { dimColor: true, children: `extensions: ${Object.keys(decoded.extensions).join(", ")}` }) : null
|
|
15727
|
+
] })
|
|
15728
|
+
] });
|
|
15729
|
+
};
|
|
15730
|
+
|
|
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
|
+
// src/commands/x402/pay.tsx
|
|
15854
|
+
import { Box as Box19, Text as Text20, useInput as useInput4 } from "ink";
|
|
15855
|
+
import Spinner15 from "ink-spinner";
|
|
15856
|
+
import { useEffect as useEffect10, useReducer as useReducer8, useState as useState4 } from "react";
|
|
15857
|
+
import { jsx as jsx24, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
15858
|
+
var PayView2 = ({ url, method, deps, onComplete, onCancel }) => {
|
|
15859
|
+
const initial = { kind: "probing" };
|
|
15860
|
+
const [phase, dispatch] = useReducer8(reducePay, initial);
|
|
15861
|
+
const [cancelling, setCancelling] = useState4(false);
|
|
15862
|
+
const { finish, cancelThenFinish } = useFlowExit(onComplete);
|
|
15863
|
+
useInput4(
|
|
15864
|
+
(_input, key) => {
|
|
15865
|
+
if (phase.kind !== "awaiting-approval") return;
|
|
15866
|
+
if (key.return) {
|
|
15867
|
+
openUrl(phase.approvalUrl);
|
|
15868
|
+
return;
|
|
15869
|
+
}
|
|
15870
|
+
if (key.escape) {
|
|
15871
|
+
const { approvalId } = phase.prepared;
|
|
15872
|
+
setCancelling(true);
|
|
15873
|
+
cancelThenFinish(() => onCancel?.(approvalId), {
|
|
15874
|
+
kind: "error",
|
|
15875
|
+
code: "APPROVAL_CANCELLED",
|
|
15876
|
+
message: `Approval ${approvalId} cancelled.`
|
|
15877
|
+
});
|
|
15878
|
+
}
|
|
15879
|
+
},
|
|
15880
|
+
{ isActive: phase.kind === "awaiting-approval" && !cancelling }
|
|
15881
|
+
);
|
|
15882
|
+
useEffect10(() => {
|
|
15883
|
+
const controller = new AbortController();
|
|
15884
|
+
let cancelled = false;
|
|
15885
|
+
const runDeps = { ...deps, signOptions: { ...deps.signOptions, signal: controller.signal } };
|
|
15886
|
+
void runPayPipeline(runDeps, (event) => {
|
|
15887
|
+
if (!cancelled) dispatch(event);
|
|
15888
|
+
});
|
|
15889
|
+
return () => {
|
|
15890
|
+
cancelled = true;
|
|
15891
|
+
controller.abort();
|
|
15892
|
+
};
|
|
15893
|
+
}, [deps]);
|
|
15894
|
+
useEffect10(() => {
|
|
15895
|
+
if (phase.kind === "success" || phase.kind === "replay-rejected" || phase.kind === "no-payment-final" || phase.kind === "error") {
|
|
15896
|
+
finish(phase);
|
|
14296
15897
|
}
|
|
14297
|
-
}, [phase,
|
|
15898
|
+
}, [phase, finish]);
|
|
15899
|
+
if (cancelling) {
|
|
15900
|
+
return /* @__PURE__ */ jsx24(Box19, { children: /* @__PURE__ */ jsxs19(Text20, { color: "yellow", children: [
|
|
15901
|
+
/* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
|
|
15902
|
+
" Cancelling approval..."
|
|
15903
|
+
] }) });
|
|
15904
|
+
}
|
|
14298
15905
|
if (phase.kind === "probing") {
|
|
14299
|
-
return /* @__PURE__ */
|
|
14300
|
-
/* @__PURE__ */
|
|
15906
|
+
return /* @__PURE__ */ jsx24(Box19, { children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
|
|
15907
|
+
/* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
|
|
14301
15908
|
" Probing ",
|
|
14302
15909
|
method,
|
|
14303
15910
|
" ",
|
|
@@ -14306,23 +15913,23 @@ var PayView = ({ url, method, deps, onComplete }) => {
|
|
|
14306
15913
|
] }) });
|
|
14307
15914
|
}
|
|
14308
15915
|
if (phase.kind === "no-payment") {
|
|
14309
|
-
return /* @__PURE__ */
|
|
14310
|
-
/* @__PURE__ */
|
|
15916
|
+
return /* @__PURE__ */ jsx24(Box19, { children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
|
|
15917
|
+
/* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
|
|
14311
15918
|
" Seller accepted without payment (status ",
|
|
14312
15919
|
String(phase.probe.status),
|
|
14313
15920
|
"); finalising..."
|
|
14314
15921
|
] }) });
|
|
14315
15922
|
}
|
|
14316
15923
|
if (phase.kind === "matching") {
|
|
14317
|
-
return /* @__PURE__ */
|
|
14318
|
-
/* @__PURE__ */
|
|
15924
|
+
return /* @__PURE__ */ jsx24(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
|
|
15925
|
+
/* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
|
|
14319
15926
|
" Decoding seller requirements..."
|
|
14320
15927
|
] }) });
|
|
14321
15928
|
}
|
|
14322
15929
|
if (phase.kind === "preparing") {
|
|
14323
15930
|
const summary = summarizeAccepts([phase.requirement]);
|
|
14324
|
-
return /* @__PURE__ */
|
|
14325
|
-
/* @__PURE__ */
|
|
15931
|
+
return /* @__PURE__ */ jsx24(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
|
|
15932
|
+
/* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
|
|
14326
15933
|
" Preparing payment (",
|
|
14327
15934
|
summary[0]?.scheme ?? phase.requirement.scheme,
|
|
14328
15935
|
" /",
|
|
@@ -14332,188 +15939,167 @@ var PayView = ({ url, method, deps, onComplete }) => {
|
|
|
14332
15939
|
] }) });
|
|
14333
15940
|
}
|
|
14334
15941
|
if (phase.kind === "awaiting-approval") {
|
|
14335
|
-
return /* @__PURE__ */
|
|
14336
|
-
/* @__PURE__ */
|
|
14337
|
-
/* @__PURE__ */
|
|
14338
|
-
/* @__PURE__ */
|
|
15942
|
+
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", paddingY: 1, children: [
|
|
15943
|
+
/* @__PURE__ */ jsx24(Box19, { marginBottom: 1, children: /* @__PURE__ */ jsx24(Text20, { bold: true, children: "Approval required" }) }),
|
|
15944
|
+
/* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
|
|
15945
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14339
15946
|
"Open: ",
|
|
14340
|
-
/* @__PURE__ */
|
|
15947
|
+
/* @__PURE__ */ jsx24(Text20, { bold: true, color: "cyan", children: phase.approvalUrl })
|
|
14341
15948
|
] }),
|
|
14342
|
-
/* @__PURE__ */
|
|
15949
|
+
/* @__PURE__ */ jsx24(Text20, { dimColor: true, children: "Press Enter to open in browser." }),
|
|
15950
|
+
/* @__PURE__ */ jsx24(Text20, { dimColor: true, children: "Press Escape to cancel." })
|
|
14343
15951
|
] }),
|
|
14344
|
-
/* @__PURE__ */
|
|
14345
|
-
/* @__PURE__ */
|
|
15952
|
+
/* @__PURE__ */ jsx24(Box19, { marginTop: 1, children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
|
|
15953
|
+
/* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
|
|
14346
15954
|
" Waiting for approval..."
|
|
14347
15955
|
] }) })
|
|
14348
15956
|
] });
|
|
14349
15957
|
}
|
|
14350
15958
|
if (phase.kind === "replaying") {
|
|
14351
|
-
return /* @__PURE__ */
|
|
14352
|
-
/* @__PURE__ */
|
|
15959
|
+
return /* @__PURE__ */ jsx24(Box19, { children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
|
|
15960
|
+
/* @__PURE__ */ jsx24(Spinner15, { type: "dots" }),
|
|
14353
15961
|
" Replaying request with PAYMENT-SIGNATURE..."
|
|
14354
15962
|
] }) });
|
|
14355
15963
|
}
|
|
14356
15964
|
if (phase.kind === "success") {
|
|
14357
15965
|
const { result } = phase;
|
|
14358
|
-
return /* @__PURE__ */
|
|
14359
|
-
/* @__PURE__ */
|
|
15966
|
+
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
|
|
15967
|
+
/* @__PURE__ */ jsxs19(Text20, { color: "green", children: [
|
|
14360
15968
|
"\u2713 Paid ",
|
|
14361
15969
|
result.scheme,
|
|
14362
15970
|
" / ",
|
|
14363
15971
|
result.network
|
|
14364
15972
|
] }),
|
|
14365
|
-
/* @__PURE__ */
|
|
14366
|
-
/* @__PURE__ */
|
|
14367
|
-
result.
|
|
14368
|
-
/* @__PURE__ */ jsx17(Text14, { children: `response size: ${String(result.bodySizeBytes)} bytes` }),
|
|
14369
|
-
result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs13(Text14, { children: [
|
|
15973
|
+
/* @__PURE__ */ jsx24(Text20, { children: `transaction: ${result.transactionId}` }),
|
|
15974
|
+
result.settled?.network !== void 0 ? /* @__PURE__ */ jsx24(Text20, { children: `settled via: ${result.settled.network}${result.settled.transaction !== void 0 ? ` (${result.settled.transaction})` : ""}` }) : null,
|
|
15975
|
+
result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14370
15976
|
"Saved to: ",
|
|
14371
|
-
/* @__PURE__ */
|
|
15977
|
+
/* @__PURE__ */ jsx24(Text20, { bold: true, children: result.outputSavedTo })
|
|
14372
15978
|
] }) : null,
|
|
14373
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
14374
|
-
/* @__PURE__ */
|
|
14375
|
-
/* @__PURE__ */
|
|
15979
|
+
result.body !== void 0 ? /* @__PURE__ */ jsxs19(Box19, { marginTop: 1, flexDirection: "column", children: [
|
|
15980
|
+
/* @__PURE__ */ jsx24(Text20, { dimColor: true, children: "response body:" }),
|
|
15981
|
+
/* @__PURE__ */ jsx24(Text20, { children: result.body })
|
|
14376
15982
|
] }) : null
|
|
14377
15983
|
] });
|
|
14378
15984
|
}
|
|
14379
15985
|
if (phase.kind === "replay-rejected") {
|
|
14380
15986
|
const { result } = phase;
|
|
14381
|
-
return /* @__PURE__ */
|
|
14382
|
-
/* @__PURE__ */
|
|
15987
|
+
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
|
|
15988
|
+
/* @__PURE__ */ jsxs19(Text20, { color: "red", children: [
|
|
14383
15989
|
"\u2717 Payment not accepted (",
|
|
14384
15990
|
result.scheme,
|
|
14385
15991
|
" / ",
|
|
14386
15992
|
result.network,
|
|
14387
15993
|
")"
|
|
14388
15994
|
] }),
|
|
14389
|
-
/* @__PURE__ */
|
|
14390
|
-
/* @__PURE__ */
|
|
14391
|
-
/* @__PURE__ */
|
|
14392
|
-
/* @__PURE__ */
|
|
14393
|
-
/* @__PURE__ */ jsx17(Text14, { children: `response size: ${String(result.bodySizeBytes)} bytes` }),
|
|
14394
|
-
result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs13(Text14, { children: [
|
|
15995
|
+
/* @__PURE__ */ jsx24(Text20, { children: `transaction: ${result.transactionId}` }),
|
|
15996
|
+
/* @__PURE__ */ jsx24(Text20, { children: `approval: ${result.approvalId}` }),
|
|
15997
|
+
/* @__PURE__ */ jsx24(Text20, { children: `approval url: ${result.approvalUrl}` }),
|
|
15998
|
+
result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14395
15999
|
"Saved to: ",
|
|
14396
|
-
/* @__PURE__ */
|
|
16000
|
+
/* @__PURE__ */ jsx24(Text20, { bold: true, children: result.outputSavedTo })
|
|
14397
16001
|
] }) : null,
|
|
14398
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
14399
|
-
/* @__PURE__ */
|
|
14400
|
-
/* @__PURE__ */
|
|
16002
|
+
result.body !== void 0 ? /* @__PURE__ */ jsxs19(Box19, { marginTop: 1, flexDirection: "column", children: [
|
|
16003
|
+
/* @__PURE__ */ jsx24(Text20, { dimColor: true, children: "response body:" }),
|
|
16004
|
+
/* @__PURE__ */ jsx24(Text20, { children: result.body })
|
|
14401
16005
|
] }) : null
|
|
14402
16006
|
] });
|
|
14403
16007
|
}
|
|
14404
16008
|
if (phase.kind === "no-payment-final") {
|
|
14405
16009
|
const { result } = phase;
|
|
14406
|
-
return /* @__PURE__ */
|
|
14407
|
-
/* @__PURE__ */
|
|
14408
|
-
/* @__PURE__ */
|
|
14409
|
-
/* @__PURE__ */ jsx17(Text14, { children: `response size: ${String(result.bodySizeBytes)} bytes` }),
|
|
14410
|
-
result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs13(Text14, { children: [
|
|
16010
|
+
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
|
|
16011
|
+
/* @__PURE__ */ jsx24(Text20, { color: "green", children: "\u2713 Seller accepted without payment" }),
|
|
16012
|
+
result.outputSavedTo !== void 0 ? /* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14411
16013
|
"Saved to: ",
|
|
14412
|
-
/* @__PURE__ */
|
|
16014
|
+
/* @__PURE__ */ jsx24(Text20, { bold: true, children: result.outputSavedTo })
|
|
14413
16015
|
] }) : null,
|
|
14414
|
-
result.body !== void 0 ? /* @__PURE__ */
|
|
14415
|
-
/* @__PURE__ */
|
|
14416
|
-
/* @__PURE__ */
|
|
16016
|
+
result.body !== void 0 ? /* @__PURE__ */ jsxs19(Box19, { marginTop: 1, flexDirection: "column", children: [
|
|
16017
|
+
/* @__PURE__ */ jsx24(Text20, { dimColor: true, children: "response body:" }),
|
|
16018
|
+
/* @__PURE__ */ jsx24(Text20, { children: result.body })
|
|
14417
16019
|
] }) : null
|
|
14418
16020
|
] });
|
|
14419
16021
|
}
|
|
14420
|
-
return /* @__PURE__ */
|
|
14421
|
-
/* @__PURE__ */
|
|
16022
|
+
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
|
|
16023
|
+
/* @__PURE__ */ jsxs19(Text20, { color: "red", children: [
|
|
14422
16024
|
"\u2717 ",
|
|
14423
16025
|
phase.code
|
|
14424
16026
|
] }),
|
|
14425
|
-
/* @__PURE__ */
|
|
16027
|
+
/* @__PURE__ */ jsx24(Text20, { color: "red", children: phase.message })
|
|
14426
16028
|
] });
|
|
14427
16029
|
};
|
|
14428
16030
|
|
|
14429
16031
|
// src/commands/x402/schema.ts
|
|
14430
|
-
import { z as
|
|
14431
|
-
var
|
|
14432
|
-
url:
|
|
16032
|
+
import { z as z6 } from "incur";
|
|
16033
|
+
var payArgs2 = z6.object({
|
|
16034
|
+
url: z6.string().describe("The x402-protected resource URL to pay for.")
|
|
14433
16035
|
});
|
|
14434
|
-
var
|
|
14435
|
-
|
|
14436
|
-
|
|
16036
|
+
var payOptions2 = z6.object({
|
|
16037
|
+
scheme: z6.string().optional().describe('Only consider `accepts[]` entries with this scheme (e.g. "exact", "balance").'),
|
|
16038
|
+
network: z6.string().optional().describe('Only consider entries on this network (e.g. "eip155:84532").'),
|
|
16039
|
+
asset: z6.string().optional().describe("Only consider entries with this on-chain asset id (ERC-20 address or SVM mint)."),
|
|
16040
|
+
assetName: z6.string().optional().describe('Only consider entries whose `extra.assetName` symbol matches (e.g. "USDC").'),
|
|
16041
|
+
method: z6.string().default("GET").describe("HTTP method for the seller request."),
|
|
16042
|
+
data: z6.string().optional().describe(
|
|
14437
16043
|
"Request body. JSON or raw text. Content-Type defaults to application/json when --data is set unless a --header overrides it."
|
|
14438
16044
|
),
|
|
14439
|
-
header:
|
|
14440
|
-
interval:
|
|
16045
|
+
header: z6.array(z6.string()).default([]).describe('Repeatable. "Name: Value" format.'),
|
|
16046
|
+
interval: z6.coerce.number().default(0).describe(
|
|
14441
16047
|
"Inline poll cadence in seconds while awaiting approval. 0 returns the approval URL and a follow-up command hint without blocking."
|
|
14442
16048
|
),
|
|
14443
|
-
maxAttempts:
|
|
14444
|
-
timeout:
|
|
14445
|
-
paymentId:
|
|
16049
|
+
maxAttempts: z6.coerce.number().default(0).describe("Hard cap on poll attempts when --interval > 0. 0 means unlimited."),
|
|
16050
|
+
timeout: z6.coerce.number().default(900).describe("Polling deadline in seconds. Default 900s (matches x402-buyer)."),
|
|
16051
|
+
paymentId: z6.string().min(16).max(128).regex(/^[a-zA-Z0-9_-]+$/).optional().describe(
|
|
14446
16052
|
"Caller-supplied payment identifier. 16-128 chars, ^[a-zA-Z0-9_-]+$. Forwarded to the server as remotePaymentId."
|
|
14447
16053
|
),
|
|
14448
|
-
showBody:
|
|
16054
|
+
showBody: z6.boolean().default(true).describe(
|
|
14449
16055
|
"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)."
|
|
14450
16056
|
),
|
|
14451
|
-
outputFile:
|
|
16057
|
+
outputFile: z6.string().optional().describe(
|
|
14452
16058
|
"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)."
|
|
14453
16059
|
),
|
|
14454
|
-
payloadFile:
|
|
16060
|
+
payloadFile: z6.string().optional().describe(
|
|
14455
16061
|
"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."
|
|
14456
|
-
),
|
|
14457
|
-
scheme: z5.string().optional().describe(
|
|
14458
|
-
'Constrain selection to seller `accepts[]` entries whose `scheme` matches exactly (e.g. "balance", "exact"). Combine with --network/--asset/--asset-name for a tighter filter; any flag can be set independently. When the filter empties the accepts list, the command fails with NO_FILTERED_MATCH instead of falling through to the buyer\'s prefer order.'
|
|
14459
|
-
),
|
|
14460
|
-
network: z5.string().optional().describe(
|
|
14461
|
-
'Constrain selection to seller `accepts[]` entries whose `network` matches exactly (e.g. "inflow:1", "eip155:84532", "solana:..."). Combine with --scheme/--asset/--asset-name for a tighter filter. When the filter empties the accepts list, the command fails with NO_FILTERED_MATCH.'
|
|
14462
|
-
),
|
|
14463
|
-
asset: z5.string().optional().describe(
|
|
14464
|
-
"Constrain selection to seller `accepts[]` entries whose `asset` matches exactly \u2014 the on-chain asset identifier (ERC-20 contract address for EVM, mint pubkey for SVM). When the filter empties the accepts list, the command fails with NO_FILTERED_MATCH."
|
|
14465
|
-
),
|
|
14466
|
-
assetName: z5.string().optional().describe(
|
|
14467
|
-
'Constrain selection to seller `accepts[]` entries whose `extra.name` matches exactly \u2014 the human-readable symbol/name the seller advertises (e.g. "USDC"). When the filter empties the accepts list, the command fails with NO_FILTERED_MATCH.'
|
|
14468
16062
|
)
|
|
14469
16063
|
});
|
|
14470
|
-
var
|
|
14471
|
-
transactionId:
|
|
16064
|
+
var statusArgs2 = z6.object({
|
|
16065
|
+
transactionId: z6.string().describe("The transaction id returned by `x402 pay`.")
|
|
14472
16066
|
});
|
|
14473
|
-
var
|
|
14474
|
-
interval:
|
|
16067
|
+
var statusOptions3 = z6.object({
|
|
16068
|
+
interval: z6.coerce.number().default(0).describe(
|
|
14475
16069
|
"Poll cadence in seconds. 0 returns the current snapshot; positive values yield on every change until signed or terminal."
|
|
14476
16070
|
),
|
|
14477
|
-
maxAttempts:
|
|
14478
|
-
timeout:
|
|
14479
|
-
payloadFile:
|
|
16071
|
+
maxAttempts: z6.coerce.number().default(0).describe("Hard cap on poll attempts. 0 means unlimited."),
|
|
16072
|
+
timeout: z6.coerce.number().default(900).describe("Polling deadline in seconds."),
|
|
16073
|
+
payloadFile: z6.string().optional().describe(
|
|
14480
16074
|
"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."
|
|
14481
16075
|
)
|
|
14482
16076
|
});
|
|
14483
|
-
var
|
|
14484
|
-
approvalId:
|
|
16077
|
+
var cancelArgs2 = z6.object({
|
|
16078
|
+
approvalId: z6.string().describe("The approval id returned by `x402 pay`.")
|
|
14485
16079
|
});
|
|
14486
|
-
var
|
|
14487
|
-
header:
|
|
16080
|
+
var decodeArgs2 = z6.object({
|
|
16081
|
+
header: z6.string().describe("Raw PAYMENT-REQUIRED header value (base64).")
|
|
14488
16082
|
});
|
|
14489
|
-
var
|
|
14490
|
-
url:
|
|
16083
|
+
var inspectArgs2 = z6.object({
|
|
16084
|
+
url: z6.string().describe("The x402-protected resource URL to probe. No payment is made.")
|
|
14491
16085
|
});
|
|
14492
|
-
var
|
|
14493
|
-
|
|
14494
|
-
|
|
16086
|
+
var inspectOptions2 = z6.object({
|
|
16087
|
+
scheme: z6.string().optional().describe('Only show `accepts[]` entries with this scheme (e.g. "exact", "balance").'),
|
|
16088
|
+
network: z6.string().optional().describe('Only show entries on this network (e.g. "eip155:84532").'),
|
|
16089
|
+
asset: z6.string().optional().describe("Only show entries with this on-chain asset id (ERC-20 address or SVM mint)."),
|
|
16090
|
+
assetName: z6.string().optional().describe('Only show entries whose `extra.assetName` symbol matches (e.g. "USDC").'),
|
|
16091
|
+
method: z6.string().default("GET").describe("HTTP method for the probe request."),
|
|
16092
|
+
data: z6.string().optional().describe(
|
|
14495
16093
|
"Request body for the probe. JSON or raw text. Content-Type defaults to application/json when --data is set unless a --header overrides it."
|
|
14496
16094
|
),
|
|
14497
|
-
header:
|
|
14498
|
-
scheme: z5.string().optional().describe(
|
|
14499
|
-
'Constrain the rendered accepts to entries whose `scheme` matches exactly (e.g. "balance", "exact"). When the filter empties the accepts list, the command fails with NO_FILTERED_MATCH.'
|
|
14500
|
-
),
|
|
14501
|
-
network: z5.string().optional().describe(
|
|
14502
|
-
'Constrain the rendered accepts to entries whose `network` matches exactly (e.g. "inflow:1", "eip155:84532"). When the filter empties the accepts list, the command fails with NO_FILTERED_MATCH.'
|
|
14503
|
-
),
|
|
14504
|
-
asset: z5.string().optional().describe(
|
|
14505
|
-
"Constrain the rendered accepts to entries whose `asset` matches exactly \u2014 the on-chain asset identifier (ERC-20 contract address for EVM, mint pubkey for SVM). When the filter empties the accepts list, the command fails with NO_FILTERED_MATCH."
|
|
14506
|
-
),
|
|
14507
|
-
assetName: z5.string().optional().describe(
|
|
14508
|
-
'Constrain the rendered accepts to entries whose `extra.name` matches exactly \u2014 the human-readable symbol/name the seller advertises (e.g. "USDC"). When the filter empties the accepts list, the command fails with NO_FILTERED_MATCH.'
|
|
14509
|
-
)
|
|
16095
|
+
header: z6.array(z6.string()).default([]).describe('Repeatable. "Name: Value" format.')
|
|
14510
16096
|
});
|
|
14511
16097
|
|
|
14512
16098
|
// src/commands/x402/status.tsx
|
|
14513
|
-
import { Box as
|
|
14514
|
-
import
|
|
14515
|
-
import { useEffect as
|
|
14516
|
-
import { jsx as
|
|
16099
|
+
import { Box as Box20, Text as Text21 } from "ink";
|
|
16100
|
+
import Spinner16 from "ink-spinner";
|
|
16101
|
+
import { useEffect as useEffect11, useReducer as useReducer9 } from "react";
|
|
16102
|
+
import { jsx as jsx25, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
14517
16103
|
var X402StatusView = ({
|
|
14518
16104
|
transactionId,
|
|
14519
16105
|
fetchOnce,
|
|
@@ -14522,10 +16108,10 @@ var X402StatusView = ({
|
|
|
14522
16108
|
timeout,
|
|
14523
16109
|
onComplete
|
|
14524
16110
|
}) => {
|
|
14525
|
-
const { exit } = useApp12();
|
|
14526
16111
|
const initial = { kind: "polling" };
|
|
14527
|
-
const [phase, dispatch] =
|
|
14528
|
-
|
|
16112
|
+
const [phase, dispatch] = useReducer9(reduceX402Status, initial);
|
|
16113
|
+
const { finish } = useFlowExit(onComplete);
|
|
16114
|
+
useEffect11(() => {
|
|
14529
16115
|
const run = runX402Status({ fetchOnce, interval, maxAttempts, timeout });
|
|
14530
16116
|
let cancelled = false;
|
|
14531
16117
|
void (async () => {
|
|
@@ -14538,16 +16124,15 @@ var X402StatusView = ({
|
|
|
14538
16124
|
cancelled = true;
|
|
14539
16125
|
};
|
|
14540
16126
|
}, [fetchOnce, interval, maxAttempts, timeout]);
|
|
14541
|
-
|
|
16127
|
+
useEffect11(() => {
|
|
14542
16128
|
if (phase.kind === "signed" || phase.kind === "failed" || phase.kind === "timeout" || phase.kind === "error") {
|
|
14543
|
-
|
|
14544
|
-
exit();
|
|
16129
|
+
finish(phase);
|
|
14545
16130
|
}
|
|
14546
|
-
}, [phase,
|
|
16131
|
+
}, [phase, finish]);
|
|
14547
16132
|
if (phase.kind === "polling") {
|
|
14548
16133
|
const statusText = phase.latest?.status ?? "pending";
|
|
14549
|
-
return /* @__PURE__ */
|
|
14550
|
-
/* @__PURE__ */
|
|
16134
|
+
return /* @__PURE__ */ jsx25(Box20, { flexDirection: "column", children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
|
|
16135
|
+
/* @__PURE__ */ jsx25(Spinner16, { type: "dots" }),
|
|
14551
16136
|
" Polling transaction ",
|
|
14552
16137
|
transactionId,
|
|
14553
16138
|
" (status: ",
|
|
@@ -14558,80 +16143,73 @@ var X402StatusView = ({
|
|
|
14558
16143
|
if (phase.kind === "signed") {
|
|
14559
16144
|
const encoded = phase.response.encodedPayload ?? "";
|
|
14560
16145
|
const preview = encoded.length > 32 ? `${encoded.slice(0, 32)}...` : encoded;
|
|
14561
|
-
return /* @__PURE__ */
|
|
14562
|
-
/* @__PURE__ */
|
|
14563
|
-
/* @__PURE__ */
|
|
14564
|
-
/* @__PURE__ */
|
|
16146
|
+
return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
|
|
16147
|
+
/* @__PURE__ */ jsx25(Text21, { color: "green", children: "\u2713 Signed" }),
|
|
16148
|
+
/* @__PURE__ */ jsx25(Text21, { children: `status: ${phase.response.status}` }),
|
|
16149
|
+
/* @__PURE__ */ jsx25(Text21, { children: `encodedPayload: ${preview}` })
|
|
14565
16150
|
] });
|
|
14566
16151
|
}
|
|
14567
16152
|
if (phase.kind === "failed") {
|
|
14568
|
-
return /* @__PURE__ */
|
|
14569
|
-
/* @__PURE__ */
|
|
14570
|
-
/* @__PURE__ */
|
|
16153
|
+
return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
|
|
16154
|
+
/* @__PURE__ */ jsx25(Text21, { color: "red", children: "\u2717 Approval did not settle" }),
|
|
16155
|
+
/* @__PURE__ */ jsx25(Text21, { color: "red", children: `status: ${phase.response.status}` })
|
|
14571
16156
|
] });
|
|
14572
16157
|
}
|
|
14573
16158
|
if (phase.kind === "timeout") {
|
|
14574
|
-
return /* @__PURE__ */
|
|
14575
|
-
/* @__PURE__ */
|
|
14576
|
-
phase.response !== void 0 ? /* @__PURE__ */
|
|
16159
|
+
return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
|
|
16160
|
+
/* @__PURE__ */ jsx25(Text21, { color: "yellow", children: "Polling timed out before the transaction reached a signed state." }),
|
|
16161
|
+
phase.response !== void 0 ? /* @__PURE__ */ jsx25(Text21, { children: `last status: ${phase.response.status}` }) : null
|
|
14577
16162
|
] });
|
|
14578
16163
|
}
|
|
14579
|
-
return /* @__PURE__ */
|
|
14580
|
-
/* @__PURE__ */
|
|
14581
|
-
/* @__PURE__ */
|
|
16164
|
+
return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
|
|
16165
|
+
/* @__PURE__ */ jsx25(Text21, { color: "red", children: "\u2717 Polling failed" }),
|
|
16166
|
+
/* @__PURE__ */ jsx25(Text21, { color: "red", children: phase.message })
|
|
14582
16167
|
] });
|
|
14583
16168
|
};
|
|
14584
16169
|
|
|
14585
16170
|
// src/commands/x402/supported.tsx
|
|
14586
|
-
import { Box as
|
|
14587
|
-
import
|
|
14588
|
-
import { useCallback as
|
|
14589
|
-
import { jsx as
|
|
14590
|
-
var
|
|
16171
|
+
import { Box as Box21, Text as Text22 } from "ink";
|
|
16172
|
+
import Spinner17 from "ink-spinner";
|
|
16173
|
+
import { useCallback as useCallback9 } from "react";
|
|
16174
|
+
import { jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
16175
|
+
var COLUMNS6 = [
|
|
14591
16176
|
{ header: "Scheme", cell: (k) => k.scheme },
|
|
14592
16177
|
{ header: "Network", cell: (k) => k.network }
|
|
14593
16178
|
];
|
|
14594
|
-
var
|
|
14595
|
-
const
|
|
14596
|
-
const
|
|
14597
|
-
const
|
|
14598
|
-
(result) => {
|
|
14599
|
-
onComplete(result);
|
|
14600
|
-
exit();
|
|
14601
|
-
},
|
|
14602
|
-
[onComplete, exit]
|
|
14603
|
-
);
|
|
14604
|
-
const { status, data, error } = useFlowState(action, handleComplete);
|
|
16179
|
+
var SupportedView2 = ({ load, onComplete }) => {
|
|
16180
|
+
const action = useCallback9(() => load(), [load]);
|
|
16181
|
+
const { finish } = useFlowExit(onComplete);
|
|
16182
|
+
const { status, data, error } = useFlowState(action, finish);
|
|
14605
16183
|
if (status === "loading") {
|
|
14606
|
-
return /* @__PURE__ */
|
|
14607
|
-
/* @__PURE__ */
|
|
16184
|
+
return /* @__PURE__ */ jsx26(Box21, { children: /* @__PURE__ */ jsxs21(Text22, { color: "cyan", children: [
|
|
16185
|
+
/* @__PURE__ */ jsx26(Spinner17, { type: "dots" }),
|
|
14608
16186
|
" Loading supported schemes..."
|
|
14609
16187
|
] }) });
|
|
14610
16188
|
}
|
|
14611
16189
|
if (status === "error") {
|
|
14612
|
-
return /* @__PURE__ */
|
|
14613
|
-
/* @__PURE__ */
|
|
14614
|
-
/* @__PURE__ */
|
|
16190
|
+
return /* @__PURE__ */ jsxs21(Box21, { flexDirection: "column", children: [
|
|
16191
|
+
/* @__PURE__ */ jsx26(Text22, { color: "red", children: "Failed to load supported schemes" }),
|
|
16192
|
+
/* @__PURE__ */ jsx26(Text22, { color: "red", children: error })
|
|
14615
16193
|
] });
|
|
14616
16194
|
}
|
|
14617
16195
|
const kinds = data?.kinds ?? [];
|
|
14618
16196
|
if (kinds.length === 0) {
|
|
14619
|
-
return /* @__PURE__ */
|
|
16197
|
+
return /* @__PURE__ */ jsx26(Box21, { flexDirection: "column", children: /* @__PURE__ */ jsx26(Text22, { children: "No supported (scheme, network) pairs returned for this account." }) });
|
|
14620
16198
|
}
|
|
14621
|
-
return /* @__PURE__ */
|
|
16199
|
+
return /* @__PURE__ */ jsx26(Table, { columns: COLUMNS6, rows: kinds });
|
|
14622
16200
|
};
|
|
14623
16201
|
|
|
14624
16202
|
// src/commands/x402/index.tsx
|
|
14625
|
-
import { jsx as
|
|
16203
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
14626
16204
|
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.";
|
|
14627
|
-
var
|
|
16205
|
+
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.";
|
|
14628
16206
|
function buildSignOptions(options) {
|
|
14629
16207
|
const out = { timeoutMs: options.timeout * 1e3 };
|
|
14630
16208
|
if (options.interval > 0) out.pollIntervalMs = options.interval * 1e3;
|
|
14631
16209
|
if (options.paymentId !== void 0) out.paymentId = options.paymentId;
|
|
14632
16210
|
return out;
|
|
14633
16211
|
}
|
|
14634
|
-
function
|
|
16212
|
+
function parseHeaderFlagsOrFail2(c, flags) {
|
|
14635
16213
|
try {
|
|
14636
16214
|
return parseHeaderFlags(flags);
|
|
14637
16215
|
} catch (err) {
|
|
@@ -14643,16 +16221,16 @@ function parseHeaderFlagsOrFail(c, flags) {
|
|
|
14643
16221
|
}
|
|
14644
16222
|
function decoratePayloadField(frame, encoded, payloadFile) {
|
|
14645
16223
|
if (payloadFile !== void 0 && payloadFile.length > 0) {
|
|
14646
|
-
const absolute =
|
|
14647
|
-
|
|
14648
|
-
|
|
16224
|
+
const absolute = resolvePath3(payloadFile);
|
|
16225
|
+
writeFileSync3(absolute, Buffer.from(encoded, "utf-8"), { mode: 384 });
|
|
16226
|
+
chmodSync2(absolute, 384);
|
|
14649
16227
|
frame.payload_saved_to = absolute;
|
|
14650
16228
|
return;
|
|
14651
16229
|
}
|
|
14652
16230
|
frame.encoded_payload = encoded;
|
|
14653
16231
|
}
|
|
14654
|
-
function
|
|
14655
|
-
const probeHeaders =
|
|
16232
|
+
function buildPayPipelineInput2(c) {
|
|
16233
|
+
const probeHeaders = parseHeaderFlagsOrFail2(c, c.options.header);
|
|
14656
16234
|
const probeOptions = {
|
|
14657
16235
|
method: c.options.method,
|
|
14658
16236
|
headers: probeHeaders,
|
|
@@ -14670,19 +16248,19 @@ function buildPayPipelineInput(c) {
|
|
|
14670
16248
|
...c.options.assetName !== void 0 ? { assetNameFilter: c.options.assetName } : {}
|
|
14671
16249
|
};
|
|
14672
16250
|
}
|
|
14673
|
-
function
|
|
16251
|
+
function attachBodyFields2(frame, result) {
|
|
14674
16252
|
frame.body_size_bytes = result.bodySizeBytes;
|
|
14675
16253
|
if (result.body !== void 0) frame.body = result.body;
|
|
14676
16254
|
if (result.bodyBase64 !== void 0) frame.body_base64 = result.bodyBase64;
|
|
14677
16255
|
if (result.outputSavedTo !== void 0) frame.output_saved_to = result.outputSavedTo;
|
|
14678
16256
|
}
|
|
14679
|
-
function
|
|
16257
|
+
function noPaymentFrameFromResult2(result) {
|
|
14680
16258
|
const frame = {
|
|
14681
16259
|
outcome: "no-payment-required",
|
|
14682
16260
|
status: result.status
|
|
14683
16261
|
};
|
|
14684
16262
|
if (result.contentType !== void 0) frame.content_type = result.contentType;
|
|
14685
|
-
|
|
16263
|
+
attachBodyFields2(frame, result);
|
|
14686
16264
|
return frame;
|
|
14687
16265
|
}
|
|
14688
16266
|
function initialPayFrame(event, interval, maxAttempts) {
|
|
@@ -14693,7 +16271,7 @@ function initialPayFrame(event, interval, maxAttempts) {
|
|
|
14693
16271
|
resource: event.decoded.resource.url,
|
|
14694
16272
|
scheme: event.requirement.scheme,
|
|
14695
16273
|
network: event.requirement.network,
|
|
14696
|
-
instruction: interval > 0 ?
|
|
16274
|
+
instruction: interval > 0 ? POLLING_INSTRUCTION2 : POST_PAY_INSTRUCTION
|
|
14697
16275
|
};
|
|
14698
16276
|
if (event.requirement.amount !== "") frame.amount = event.requirement.amount;
|
|
14699
16277
|
if (event.requirement.asset !== "") frame.asset = event.requirement.asset;
|
|
@@ -14707,7 +16285,7 @@ function initialPayFrame(event, interval, maxAttempts) {
|
|
|
14707
16285
|
}
|
|
14708
16286
|
return frame;
|
|
14709
16287
|
}
|
|
14710
|
-
function
|
|
16288
|
+
function paidFrameFromResult2(result, payloadFile) {
|
|
14711
16289
|
const frame = {
|
|
14712
16290
|
outcome: "paid",
|
|
14713
16291
|
transaction_id: result.transactionId,
|
|
@@ -14719,10 +16297,10 @@ function paidFrameFromResult(result, payloadFile) {
|
|
|
14719
16297
|
decoratePayloadField(frame, result.encodedPayload, payloadFile);
|
|
14720
16298
|
if (result.responseContentType !== void 0) frame.response_content_type = result.responseContentType;
|
|
14721
16299
|
if (result.settled !== void 0) frame.settled = result.settled;
|
|
14722
|
-
|
|
16300
|
+
attachBodyFields2(frame, result);
|
|
14723
16301
|
return frame;
|
|
14724
16302
|
}
|
|
14725
|
-
function
|
|
16303
|
+
function rejectedFrameFromResult2(result) {
|
|
14726
16304
|
const frame = {
|
|
14727
16305
|
outcome: "replay-rejected",
|
|
14728
16306
|
transaction_id: result.transactionId,
|
|
@@ -14733,14 +16311,14 @@ function rejectedFrameFromResult(result) {
|
|
|
14733
16311
|
response_status: result.responseStatus
|
|
14734
16312
|
};
|
|
14735
16313
|
if (result.responseContentType !== void 0) frame.response_content_type = result.responseContentType;
|
|
14736
|
-
|
|
16314
|
+
attachBodyFields2(frame, result);
|
|
14737
16315
|
return frame;
|
|
14738
16316
|
}
|
|
14739
|
-
async function*
|
|
16317
|
+
async function* runPayCommand2(c, inflow2, authStorage2, apiBaseUrl2) {
|
|
14740
16318
|
assertSessionGuard(c, authStorage2, inflow2);
|
|
14741
16319
|
if (!c.agent && !c.formatExplicit) {
|
|
14742
16320
|
const client = await inflow2.x402.client();
|
|
14743
|
-
const probeHeaders =
|
|
16321
|
+
const probeHeaders = parseHeaderFlagsOrFail2(c, c.options.header);
|
|
14744
16322
|
const probeOptions = {
|
|
14745
16323
|
method: c.options.method,
|
|
14746
16324
|
headers: probeHeaders,
|
|
@@ -14748,8 +16326,8 @@ async function* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2) {
|
|
|
14748
16326
|
};
|
|
14749
16327
|
let finalPhase = null;
|
|
14750
16328
|
await renderInkUntilExit(
|
|
14751
|
-
/* @__PURE__ */
|
|
14752
|
-
|
|
16329
|
+
/* @__PURE__ */ jsx27(
|
|
16330
|
+
PayView2,
|
|
14753
16331
|
{
|
|
14754
16332
|
url: c.args.url,
|
|
14755
16333
|
method: c.options.method,
|
|
@@ -14768,7 +16346,8 @@ async function* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2) {
|
|
|
14768
16346
|
},
|
|
14769
16347
|
onComplete: (phase) => {
|
|
14770
16348
|
finalPhase = phase;
|
|
14771
|
-
}
|
|
16349
|
+
},
|
|
16350
|
+
onCancel: (approvalId) => inflow2.x402.cancel({ approvalId })
|
|
14772
16351
|
}
|
|
14773
16352
|
)
|
|
14774
16353
|
);
|
|
@@ -14787,12 +16366,12 @@ async function* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2) {
|
|
|
14787
16366
|
return;
|
|
14788
16367
|
}
|
|
14789
16368
|
const run = inflow2.x402.pay({
|
|
14790
|
-
...
|
|
16369
|
+
...buildPayPipelineInput2(c),
|
|
14791
16370
|
awaitPayment: c.options.interval > 0
|
|
14792
16371
|
});
|
|
14793
16372
|
for await (const event of run.events) {
|
|
14794
16373
|
if (event.type === "short-circuited") {
|
|
14795
|
-
yield sanitizeDeep(
|
|
16374
|
+
yield sanitizeDeep(noPaymentFrameFromResult2(event.result));
|
|
14796
16375
|
return;
|
|
14797
16376
|
}
|
|
14798
16377
|
if (event.type === "prepared") {
|
|
@@ -14800,11 +16379,11 @@ async function* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2) {
|
|
|
14800
16379
|
continue;
|
|
14801
16380
|
}
|
|
14802
16381
|
if (event.type === "replayed") {
|
|
14803
|
-
yield sanitizeDeep(
|
|
16382
|
+
yield sanitizeDeep(paidFrameFromResult2(event.result, c.options.payloadFile));
|
|
14804
16383
|
return;
|
|
14805
16384
|
}
|
|
14806
16385
|
if (event.type === "rejected") {
|
|
14807
|
-
yield sanitizeDeep(
|
|
16386
|
+
yield sanitizeDeep(rejectedFrameFromResult2(event.result));
|
|
14808
16387
|
c.error({
|
|
14809
16388
|
code: PAYMENT_NOT_ACCEPTED_CODE,
|
|
14810
16389
|
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.`
|
|
@@ -14816,12 +16395,12 @@ async function* runPayCommand(c, inflow2, authStorage2, apiBaseUrl2) {
|
|
|
14816
16395
|
}
|
|
14817
16396
|
}
|
|
14818
16397
|
}
|
|
14819
|
-
async function*
|
|
16398
|
+
async function* runStatusCommand2(c, inflow2, authStorage2) {
|
|
14820
16399
|
assertSessionGuard(c, authStorage2, inflow2);
|
|
14821
16400
|
if (!c.agent && !c.formatExplicit) {
|
|
14822
16401
|
const client2 = await inflow2.x402.client();
|
|
14823
16402
|
await renderInkUntilExit(
|
|
14824
|
-
/* @__PURE__ */
|
|
16403
|
+
/* @__PURE__ */ jsx27(
|
|
14825
16404
|
X402StatusView,
|
|
14826
16405
|
{
|
|
14827
16406
|
transactionId: c.args.transactionId,
|
|
@@ -14839,7 +16418,7 @@ async function* runStatusCommand(c, inflow2, authStorage2) {
|
|
|
14839
16418
|
const fetchOnce = () => client.getX402Payload(c.args.transactionId);
|
|
14840
16419
|
if (c.options.interval <= 0) {
|
|
14841
16420
|
const snapshot = await fetchOnce();
|
|
14842
|
-
yield sanitizeDeep(
|
|
16421
|
+
yield sanitizeDeep(toStatusFrame2(c.args.transactionId, snapshot, c.options.payloadFile));
|
|
14843
16422
|
return;
|
|
14844
16423
|
}
|
|
14845
16424
|
const generator = pollAsync({
|
|
@@ -14851,7 +16430,7 @@ async function* runStatusCommand(c, inflow2, authStorage2) {
|
|
|
14851
16430
|
timeout: c.options.timeout
|
|
14852
16431
|
});
|
|
14853
16432
|
for await (const outcome of generator) {
|
|
14854
|
-
yield sanitizeDeep(
|
|
16433
|
+
yield sanitizeDeep(toStatusFrame2(c.args.transactionId, outcome.value, c.options.payloadFile));
|
|
14855
16434
|
if (!outcome.terminal) continue;
|
|
14856
16435
|
if (outcome.reason !== void 0) {
|
|
14857
16436
|
c.error({
|
|
@@ -14869,7 +16448,7 @@ async function* runStatusCommand(c, inflow2, authStorage2) {
|
|
|
14869
16448
|
return;
|
|
14870
16449
|
}
|
|
14871
16450
|
}
|
|
14872
|
-
function
|
|
16451
|
+
function toStatusFrame2(transactionId, response, payloadFile) {
|
|
14873
16452
|
const frame = {
|
|
14874
16453
|
transaction_id: transactionId,
|
|
14875
16454
|
status: response.status
|
|
@@ -14880,12 +16459,12 @@ function toStatusFrame(transactionId, response, payloadFile) {
|
|
|
14880
16459
|
if (response.paymentPayload !== void 0) frame.payment_payload = response.paymentPayload;
|
|
14881
16460
|
return frame;
|
|
14882
16461
|
}
|
|
14883
|
-
async function
|
|
16462
|
+
async function runCancelCommand2(c, inflow2, authStorage2) {
|
|
14884
16463
|
assertSessionGuard(c, authStorage2, inflow2);
|
|
14885
16464
|
if (!c.agent && !c.formatExplicit) {
|
|
14886
16465
|
await renderInkUntilExit(
|
|
14887
|
-
/* @__PURE__ */
|
|
14888
|
-
|
|
16466
|
+
/* @__PURE__ */ jsx27(
|
|
16467
|
+
CancelView2,
|
|
14889
16468
|
{
|
|
14890
16469
|
approvalId: c.args.approvalId,
|
|
14891
16470
|
cancel: () => inflow2.x402.cancel({ approvalId: c.args.approvalId }).then(() => void 0),
|
|
@@ -14901,7 +16480,7 @@ async function runCancelCommand(c, inflow2, authStorage2) {
|
|
|
14901
16480
|
}
|
|
14902
16481
|
return inflow2.x402.cancel({ approvalId: c.args.approvalId });
|
|
14903
16482
|
}
|
|
14904
|
-
async function
|
|
16483
|
+
async function runDecodeCommand2(c) {
|
|
14905
16484
|
let decoded;
|
|
14906
16485
|
try {
|
|
14907
16486
|
decoded = decodeHeader(c.args.header);
|
|
@@ -14912,22 +16491,22 @@ async function runDecodeCommand(c) {
|
|
|
14912
16491
|
});
|
|
14913
16492
|
}
|
|
14914
16493
|
if (!c.agent && !c.formatExplicit) {
|
|
14915
|
-
await renderInkUntilExit(/* @__PURE__ */
|
|
16494
|
+
await renderInkUntilExit(/* @__PURE__ */ jsx27(DecodeView2, { decoded }));
|
|
14916
16495
|
return void 0;
|
|
14917
16496
|
}
|
|
14918
16497
|
return sanitizeDeep(decoded);
|
|
14919
16498
|
}
|
|
14920
|
-
async function
|
|
16499
|
+
async function runSupportedCommand2(c, inflow2, authStorage2) {
|
|
14921
16500
|
assertSessionGuard(c, authStorage2, inflow2);
|
|
14922
16501
|
if (!c.agent && !c.formatExplicit) {
|
|
14923
|
-
await renderInkUntilExit(/* @__PURE__ */
|
|
16502
|
+
await renderInkUntilExit(/* @__PURE__ */ jsx27(SupportedView2, { load: () => inflow2.x402.supported(), onComplete: () => void 0 }));
|
|
14924
16503
|
return void 0;
|
|
14925
16504
|
}
|
|
14926
16505
|
const response = await inflow2.x402.supported();
|
|
14927
16506
|
return sanitizeDeep(response);
|
|
14928
16507
|
}
|
|
14929
|
-
async function
|
|
14930
|
-
const probeHeaders =
|
|
16508
|
+
async function runInspectCommand2(c) {
|
|
16509
|
+
const probeHeaders = parseHeaderFlagsOrFail2(c, c.options.header);
|
|
14931
16510
|
const probeOptions = {
|
|
14932
16511
|
method: c.options.method,
|
|
14933
16512
|
headers: probeHeaders,
|
|
@@ -14944,8 +16523,8 @@ async function runInspectCommand(c) {
|
|
|
14944
16523
|
if (!c.agent && !c.formatExplicit) {
|
|
14945
16524
|
let finalPhase = null;
|
|
14946
16525
|
await renderInkUntilExit(
|
|
14947
|
-
/* @__PURE__ */
|
|
14948
|
-
|
|
16526
|
+
/* @__PURE__ */ jsx27(
|
|
16527
|
+
InspectView2,
|
|
14949
16528
|
{
|
|
14950
16529
|
url: c.args.url,
|
|
14951
16530
|
method: c.options.method,
|
|
@@ -14989,60 +16568,60 @@ async function runInspectCommand(c) {
|
|
|
14989
16568
|
if (kind === "accepts") {
|
|
14990
16569
|
return sanitizeDeep(buildAcceptsFrame(payload));
|
|
14991
16570
|
}
|
|
14992
|
-
return sanitizeDeep(
|
|
16571
|
+
return sanitizeDeep(buildNoPaymentFrame2(payload));
|
|
14993
16572
|
}
|
|
14994
16573
|
function createX402Cli(inflow2, authStorage2, apiBaseUrl2) {
|
|
14995
|
-
const cli2 =
|
|
16574
|
+
const cli2 = Cli6.create("x402", {
|
|
14996
16575
|
description: "x402 payment commands (pay, inspect, status, cancel, decode, supported)."
|
|
14997
16576
|
});
|
|
14998
16577
|
cli2.command("pay", {
|
|
14999
16578
|
description: "Pay an x402-protected resource and return the seller response.",
|
|
15000
|
-
args:
|
|
15001
|
-
options:
|
|
16579
|
+
args: payArgs2,
|
|
16580
|
+
options: payOptions2,
|
|
15002
16581
|
outputPolicy: "agent-only",
|
|
15003
16582
|
async *run(c) {
|
|
15004
|
-
yield*
|
|
16583
|
+
yield* runPayCommand2(c, inflow2, authStorage2, apiBaseUrl2);
|
|
15005
16584
|
}
|
|
15006
16585
|
});
|
|
15007
16586
|
cli2.command("status", {
|
|
15008
16587
|
description: "Poll the signing state of an in-flight x402 transaction.",
|
|
15009
|
-
args:
|
|
15010
|
-
options:
|
|
16588
|
+
args: statusArgs2,
|
|
16589
|
+
options: statusOptions3,
|
|
15011
16590
|
outputPolicy: "agent-only",
|
|
15012
16591
|
async *run(c) {
|
|
15013
|
-
yield*
|
|
16592
|
+
yield* runStatusCommand2(c, inflow2, authStorage2);
|
|
15014
16593
|
}
|
|
15015
16594
|
});
|
|
15016
16595
|
cli2.command("cancel", {
|
|
15017
16596
|
description: "Best-effort cancel of an x402 approval.",
|
|
15018
|
-
args:
|
|
16597
|
+
args: cancelArgs2,
|
|
15019
16598
|
outputPolicy: "agent-only",
|
|
15020
16599
|
async run(c) {
|
|
15021
|
-
return
|
|
16600
|
+
return runCancelCommand2(c, inflow2, authStorage2);
|
|
15022
16601
|
}
|
|
15023
16602
|
});
|
|
15024
16603
|
cli2.command("decode", {
|
|
15025
16604
|
description: "Decode a raw PAYMENT-REQUIRED header value.",
|
|
15026
|
-
args:
|
|
16605
|
+
args: decodeArgs2,
|
|
15027
16606
|
outputPolicy: "agent-only",
|
|
15028
16607
|
async run(c) {
|
|
15029
|
-
return
|
|
16608
|
+
return runDecodeCommand2(c);
|
|
15030
16609
|
}
|
|
15031
16610
|
});
|
|
15032
16611
|
cli2.command("supported", {
|
|
15033
16612
|
description: "List the buyer-side capability cache (scheme x network).",
|
|
15034
16613
|
outputPolicy: "agent-only",
|
|
15035
16614
|
async run(c) {
|
|
15036
|
-
return
|
|
16615
|
+
return runSupportedCommand2(c, inflow2, authStorage2);
|
|
15037
16616
|
}
|
|
15038
16617
|
});
|
|
15039
16618
|
cli2.command("inspect", {
|
|
15040
16619
|
description: "Show the seller's PAYMENT-REQUIRED accepts for a URL. Read-only probe \u2014 no auth, no payment.",
|
|
15041
|
-
args:
|
|
15042
|
-
options:
|
|
16620
|
+
args: inspectArgs2,
|
|
16621
|
+
options: inspectOptions2,
|
|
15043
16622
|
outputPolicy: "agent-only",
|
|
15044
16623
|
async run(c) {
|
|
15045
|
-
return
|
|
16624
|
+
return runInspectCommand2(c);
|
|
15046
16625
|
}
|
|
15047
16626
|
});
|
|
15048
16627
|
return cli2;
|
|
@@ -15116,9 +16695,9 @@ function formatUpdateNotice(info) {
|
|
|
15116
16695
|
}
|
|
15117
16696
|
|
|
15118
16697
|
// src/cli.tsx
|
|
15119
|
-
var cliVersion = "0.
|
|
16698
|
+
var cliVersion = "0.6.1";
|
|
15120
16699
|
var cliName = "@inflowpayai/inflow";
|
|
15121
|
-
var skillBody = '# Agentic Payments\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 to your MCP client config:\n\n```json\n{\n "mcpServers": {\n "inflow": {\n "command": "npx",\n "args": ["-y", "@inflowpayai/inflow", "--mcp"]\n }\n }\n}\n```\n\nThe `-y` flag suppresses npx\'s confirmation prompt \u2014 without it 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- `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). `inflow <command> --schema` enumerates every option for the command.\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## Core flow\n\n**Sequencing.** Run the steps in order. Don\'t skip ahead \u2014 Step 3 fails or double-charges if Steps 1-2 didn\'t clear. `x402 inspect` and `x402 decode` are read-only and don\'t require auth, so they may run before Step 1 if useful (e.g. when sizing up a paywall before committing the user to a login).\n\nCopy this checklist and track progress:\n\n- Step 1: Authenticate with InFlow.\n- Step 2: Pre-flight evaluation (probe seller, check supported pairs, check balance).\n- Step 3: Pay via x402.\n\n### Step 1: Authenticate\n\nCheck the current state first \u2014 the user may already be logged in:\n\n```bash\ninflow auth status\n```\n\nAuthenticated response shape (`access_token` is a 20-char preview, not the full token):\n\n```json\n{\n "authenticated": true,\n "auth_method": "device_token",\n "access_token": "inf_3LtKpQ7nWxYzA1bC...",\n "credentials_path": "/Users/.../inflow/auth.json",\n "connection": { "environment": "production", "apiBaseUrl": "https://api.inflowpay.ai" },\n "update": { "current": "0.4.6", "latest": "0.5.1" }\n}\n```\n\n`auth_method` is `device_token` or `api_key`. For the user\'s identity (email, handle, account id), call `inflow user get` \u2014 `auth status` deliberately doesn\'t include it.\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 `_next.command` \u2014 run it immediately to poll until authenticated. **Do not wait for the user to respond before starting the poll.** Example response:\n\n```json\n{\n "verification_url": "https://app.inflowpay.ai/device/?code=ABCD-EFGH",\n "phrase": "ABCD-EFGH",\n "_next": {\n "command": "auth status --interval 5 --max-attempts 60",\n "poll_interval_seconds": 5,\n "until": "authenticated is true"\n }\n}\n```\n\nPresent `verification_url` to the user. Start polling with the `_next.command` immediately \u2014 don\'t wait for them to reply.\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### Step 2: Pre-flight evaluation\n\nThree commands, in this order:\n\n```bash\n# 1. Probe the seller without paying\ninflow x402 inspect <url>\n# Returns the seller\'s accepts[]: { scheme, network, asset, max_amount_required, extra.name, ... }\n\n# 2. List which scheme \xD7 network pairs the user\'s account supports\ninflow x402 supported\n# Returns: { "kinds": [{ "scheme": "exact", "network": "solana:mainnet" }, ...] }\n\n# 3. Check balances for the candidate asset(s)\ninflow balances list\n# Returns: [{ "available": "100.5", "currency": "USDC" }, ...]\n```\n\n**Shortcut:** If the agent already received a 402 with a `PAYMENT-REQUIRED` header from a prior HTTP call (e.g., the browsing tool hit a paywall), skip step 1 and decode the header directly \u2014 no second probe needed:\n\n```bash\ninflow x402 decode \'<PAYMENT-REQUIRED-header-value>\'\n# Returns the same accepts[] shape as `inspect`, parsed from the header you already have.\n```\n\nNow you have the three facts an agent needs: what the seller wants, what the account can pay with, and whether there\'s enough.\n\n- If `inspect.accepts \u2229 supported.kinds` is empty \u2192 stop with `NO_INFLOW_MATCH`. Tell the user the seller doesn\'t accept any scheme \xD7 network their account supports.\n- If the intersection exists but `balances.available < max_amount_required` for every match \u2192 stop and tell the user to fund the account on a matching network. Run `inflow deposit-addresses list` and surface the deposit address(es) in full.\n- Otherwise: proceed to Step 3.\n\n**Optional filters** to narrow the match before `x402 pay`:\n\n- `--scheme <s>` \u2014 e.g. `exact`, `balance`\n- `--network <n>` \u2014 e.g. `solana:mainnet`, `eip155:84532`, `inflow:1`\n- `--asset <a>` \u2014 on-chain asset identifier (ERC-20 contract address for EVM, mint pubkey for SVM)\n- `--asset-name <name>` \u2014 human-readable symbol the seller advertises (e.g. `USDC`)\n\nWhen any filter empties the accepts list, the command fails with `NO_FILTERED_MATCH` instead of falling through to the buyer\'s default prefer order.\n\n**Decimal precision.** `balances.available` and `max_amount_required` 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 3: Pay via x402\n\nDon\'t retry with the same `transaction_id` after `encoded_payload` is consumed \u2014 create a new transaction instead.\n\nBefore initiating the call, summarize the intent to the user in chat: amount, currency, resource URL, scheme, network. 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 on Solana mainnet to api.foo.dev for /article-3. Requesting approval next."\n\n**Fast path (recommended).** When the agent can block until the payment finishes, set `--interval N` and let the CLI handle the full flow in one call \u2014 probe, decode, prepare, await approval, replay against the seller, return the body. One tool call, one result:\n\n```bash\ninflow x402 pay <url> --interval 5 --max-attempts 60\n```\n\nThe result includes `outcome: "paid"`, `transaction_id`, `response_status`, `settled`, and the seller body inline (or `output_saved_to` if `--output-file` is set). To surface `approval_url` *before* the call returns (rather than at the end as part of the single result), 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**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 need to yield between turns). Drop `--interval`; the first call returns `approval_url` + a `_next.command` for the status poll, and the agent drives the replay itself after `encoded_payload` arrives.\n\n```bash\ninflow x402 pay <url>\n```\n\nFor non-GET requests, pass `--method`, `--data`, `--header` (repeatable):\n\n```bash\ninflow x402 pay https://seller.example.com/api/widgets \\\n --method POST \\\n --data \'{"sku":"widget-1"}\' \\\n --header "X-Custom: value" \\\n --interval 5 --max-attempts 60\n```\n\n**Retries and idempotency.** Set `--payment-id <id>` whenever a retry on transport failure is possible. The server treats two requests with the same `payment-id` as the same logical payment, so a retry after a network blip won\'t double-charge. Format: 16\u2013128 chars, `^[a-zA-Z0-9_-]+$`.\n\n**Discipline:** set `--payment-id` to 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.\n\n```bash\ninflow x402 pay <url> --payment-id "<stable-opaque-id>"\n```\n\nWithout `--payment-id`, the server generates one each call \u2014 fine for one-shots, unsafe for retries.\n\n**Sensitive / binary output.** `encoded_payload` (returned by `x402 status` after approval) is a one-time bearer credential \u2014 don\'t echo it back in chat. Use `--payload-file <path>` to write payload bytes to disk at mode `0o600`; the response then carries `payload_saved_to: <path>` in place of `encoded_payload`. 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 x402 pay https://api.foo.dev/report.pdf --interval 5 --max-attempts 60 \\\n --output-file /tmp/report.pdf --no-show-body\n```\n\n**Polling discipline.** Persist `transaction_id` as soon as `x402 pay` returns it. Then:\n\n- Run `_next.command` (or `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 x402 status <transaction_id> --interval 5 --max-attempts 60`. Only create a new transaction if the original expired (`APPROVAL_TIMEOUT`), was denied/cancelled, or its `encoded_payload` 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, surface that explicitly so they can act before `APPROVAL_TIMEOUT` lands.\n- If the user aborts ("nevermind", "cancel that"), call `inflow 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 `x402 status` returns `encoded_payload`, replay the original seller request with `PAYMENT-SIGNATURE: <encoded_payload>` (or `PAYMENT-SIGNATURE: $(cat <payload-file>)` if you used `--payload-file`). The seller\'s protected response comes back on the replay.\n\n## Worked example\n\nEnd-to-end: a user asks the agent to fetch a paywalled article at `https://api.foo.dev/article-3`.\n\nAfter running the Step 2 pre-flight commands, 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 \\\n --payment-id "<stable-opaque-id>" --interval 5 --max-attempts 60\n# Persist transaction_id from the response in case polling gets interrupted.\n# -> { "outcome": "paid", "transaction_id": "txn_abc", "response_status": 200,\n# "body": "{ \\"title\\": \\"How to brew coffee\\", ... }", "settled": { ... } }\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 long enough for approval): drop `--interval`, present `approval_url`, run the returned `_next.command`, then replay against the seller yourself \u2014 use `--payload-file <path>` on the status call and `PAYMENT-SIGNATURE: $(cat <path>)` on the replay.\n\n## What to surface when something goes wrong\n\nMatch each terminal failure to a clear user-facing prompt \u2014 don\'t dump the raw error.\n\n- **`APPROVAL_TIMEOUT`** \u2014 "You didn\'t approve within 15 minutes, so the request expired. Want me to start a new payment, or stop here?"\n- **`APPROVAL_FAILED`** (declined / insufficient funds / generic) \u2014 "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`** \u2014 "You cancelled the approval. Stopping here unless you want to start a new payment."\n- **`NO_INFLOW_MATCH`** \u2014 "The seller accepts `<scheme>` on `<network>`, but your account is funded on `<other-network>`. Either fund your account on `<network>`, or pick a different seller."\n- **`NO_FILTERED_MATCH`** \u2014 "Your filter (`--scheme/--network/--asset`) removed every option the seller accepts. Loosen the filter or check the seller\'s `accepts` list with `inflow x402 inspect`."\n- **`POLLING_TIMEOUT`** \u2014 "Still waiting on your approval \u2014 want me to keep polling, or cancel the request? (`inflow x402 cancel <approval_id>` cancels it.)"\n- **`VERSION_UNSUPPORTED` / HTTP 426** \u2014 "The installed `inflow` CLI is below the minimum supported version. Run `npm install -g @inflowpayai/inflow@latest` and re-try."\n\n## Limits\n\n| Limit | Value |\n| ------------------------------ | --------------------------------------------------------------------------------------------------- |\n| Approval window | 15 minutes from `x402 pay` creating the transaction (`--timeout` overrides the polling deadline) |\n| Default polling max-attempts | Unlimited (`--max-attempts 0`). Set a positive cap when you need a hard stop |\n| `--payment-id` format | 16\u2013128 chars, `^[a-zA-Z0-9_-]+$` |\n| `encoded_payload` reuse | One-time. Consumed by the first seller replay. Not reusable \u2014 failed seller calls require a new pay |\n\n## Important\n\n- Treat OAuth tokens and API keys as secrets \u2014 never echo them. The `encoded_payload` returned by `x402 status` is a one-time bearer credential; replay it directly against the seller and discard, don\'t paste it 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 wildly 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## Out of scope\n\nThis skill covers programmatic HTTP 402 (x402) payments only. It does NOT handle:\n\n- **Traditional merchant checkouts** (card forms, Stripe Elements, hosted checkouts). No PANs.\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 `accepts[]` against the account\'s supported assets; if no overlap, fund or use a different source.\n- **Subscriptions / recurring payments.** Each `x402 pay` is one-shot. Schedule externally.\n\nFor any of the above, point the user to https://app.inflowpay.ai or support.\n\n## Errors\n\nAll errors in agent mode are JSON with `code` and `message` fields and exit code 1.\n\n| Error code | Meaning | Recovery |\n| ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `APPROVAL_CANCELLED` / `APPROVAL_FAILED` / `APPROVAL_TIMEOUT` | Approval did not produce an `encoded_payload` \u2014 cancelled via `x402 cancel` or server-side / declined or insufficient funds or generic error / 15-minute window elapsed. | Call `inflow x402 status <transaction_id>` for the precise reason; create a new transaction. User-facing prompts for each variant are in "What to surface." |\n| `INVALID_402` / `DECODE_FAILED` | Seller returned 402 but the `PAYMENT-REQUIRED` header was missing (`INVALID_402`) or unparseable (`DECODE_FAILED`). | Verify the URL is x402-protected. Pass the raw header to `inflow x402 decode` for the detailed parse error. |\n| `INVALID_PAYMENT_ID` | `--payment-id` doesn\'t match `^[a-zA-Z0-9_-]+$` and 16\u2013128 chars. | Adjust or omit the payment id. |\n| `NO_FILTERED_MATCH` | A `--scheme` / `--network` / `--asset` / `--asset-name` filter emptied the candidate `accepts[]` list. | Loosen the filter or call `inflow x402 inspect <url>` to see what the seller actually accepts. |\n| `NO_INFLOW_MATCH` | Seller doesn\'t accept any scheme \xD7 network the user\'s InFlow account supports. | Use a different buyer or fund the user\'s account on a chain the seller accepts. |\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. |\n| `POLLING_TIMEOUT` | `--interval` polling reached its max-attempts or timeout. Retryable. | Resume with `inflow x402 status <transaction_id> --interval 5 --max-attempts 60`. |\n| `api_error` | Non-2xx from the InFlow API. Discriminate on `httpStatus`. | `401` \u2014 saved auth rejected; run `inflow auth login` again. `426` (`VERSION_UNSUPPORTED`) \u2014 upgrade with `npm install -g @inflowpayai/inflow@latest` and re-try; don\'t retry on the old version. `5xx` \u2014 server-side; wait and retry. |\n| `transport_error` | Network failure. | Check connectivity; retry. |\n\n## Further docs\n\n- MPP protocol: https://mpp.dev\n- x402 protocol: https://x402.io\n- InFlow: https://app.inflowpay.ai\n';
|
|
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';
|
|
15122
16701
|
if (process9.argv.includes("--skill")) {
|
|
15123
16702
|
process9.stdout.write(skillBody.endsWith("\n") ? skillBody : `${skillBody}
|
|
15124
16703
|
`);
|
|
@@ -15215,7 +16794,7 @@ Received ${signal}; exiting.
|
|
|
15215
16794
|
process9.on("SIGINT", onSignal);
|
|
15216
16795
|
process9.on("SIGTERM", onSignal);
|
|
15217
16796
|
}
|
|
15218
|
-
var cli =
|
|
16797
|
+
var cli = Cli7.create("inflow", {
|
|
15219
16798
|
description: "InFlow \u2014 agentic MPP / x402 payments from your machine.",
|
|
15220
16799
|
version: cliVersion
|
|
15221
16800
|
});
|
|
@@ -15244,6 +16823,7 @@ cli.command(createUserCli(inflow.user, authStorage, inflow));
|
|
|
15244
16823
|
cli.command(createBalancesCli(inflow.balances, authStorage, inflow));
|
|
15245
16824
|
cli.command(createDepositAddressesCli(inflow.depositAddresses, authStorage, inflow));
|
|
15246
16825
|
cli.command(createX402Cli(inflow, authStorage, resolvedApiBaseUrl));
|
|
16826
|
+
cli.command(createMppCli(inflow, authStorage, resolvedApiBaseUrl));
|
|
15247
16827
|
await cli.serve();
|
|
15248
16828
|
var cli_default = cli;
|
|
15249
16829
|
export {
|