@stripe/link-cli 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -1
- package/dist/cli.js +1748 -422
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8704,19 +8704,19 @@ var require_range = __commonJS({
|
|
|
8704
8704
|
var replaceCaret = (comp, options) => {
|
|
8705
8705
|
debug("caret", comp, options);
|
|
8706
8706
|
const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET];
|
|
8707
|
-
const
|
|
8707
|
+
const z8 = options.includePrerelease ? "-0" : "";
|
|
8708
8708
|
return comp.replace(r, (_, M, m, p, pr) => {
|
|
8709
8709
|
debug("caret", comp, _, M, m, p, pr);
|
|
8710
8710
|
let ret;
|
|
8711
8711
|
if (isX(M)) {
|
|
8712
8712
|
ret = "";
|
|
8713
8713
|
} else if (isX(m)) {
|
|
8714
|
-
ret = `>=${M}.0.0${
|
|
8714
|
+
ret = `>=${M}.0.0${z8} <${+M + 1}.0.0-0`;
|
|
8715
8715
|
} else if (isX(p)) {
|
|
8716
8716
|
if (M === "0") {
|
|
8717
|
-
ret = `>=${M}.${m}.0${
|
|
8717
|
+
ret = `>=${M}.${m}.0${z8} <${M}.${+m + 1}.0-0`;
|
|
8718
8718
|
} else {
|
|
8719
|
-
ret = `>=${M}.${m}.0${
|
|
8719
|
+
ret = `>=${M}.${m}.0${z8} <${+M + 1}.0.0-0`;
|
|
8720
8720
|
}
|
|
8721
8721
|
} else if (pr) {
|
|
8722
8722
|
debug("replaceCaret pr", pr);
|
|
@@ -8733,9 +8733,9 @@ var require_range = __commonJS({
|
|
|
8733
8733
|
debug("no pr");
|
|
8734
8734
|
if (M === "0") {
|
|
8735
8735
|
if (m === "0") {
|
|
8736
|
-
ret = `>=${M}.${m}.${p}${
|
|
8736
|
+
ret = `>=${M}.${m}.${p}${z8} <${M}.${m}.${+p + 1}-0`;
|
|
8737
8737
|
} else {
|
|
8738
|
-
ret = `>=${M}.${m}.${p}${
|
|
8738
|
+
ret = `>=${M}.${m}.${p}${z8} <${M}.${+m + 1}.0-0`;
|
|
8739
8739
|
}
|
|
8740
8740
|
} else {
|
|
8741
8741
|
ret = `>=${M}.${m}.${p} <${+M + 1}.0.0-0`;
|
|
@@ -9549,7 +9549,7 @@ var require_semver2 = __commonJS({
|
|
|
9549
9549
|
});
|
|
9550
9550
|
|
|
9551
9551
|
// src/cli.tsx
|
|
9552
|
-
import { Cli as
|
|
9552
|
+
import { Cli as Cli7 } from "incur";
|
|
9553
9553
|
import updateNotifier from "update-notifier";
|
|
9554
9554
|
|
|
9555
9555
|
// ../sdk/dist/index.js
|
|
@@ -11580,10 +11580,686 @@ function createAuthCli(authResource, updateInfo) {
|
|
|
11580
11580
|
return cli2;
|
|
11581
11581
|
}
|
|
11582
11582
|
|
|
11583
|
-
// src/commands/
|
|
11584
|
-
import { Cli as Cli2, z as
|
|
11583
|
+
// src/commands/demo/index.tsx
|
|
11584
|
+
import { Cli as Cli2, z as z2 } from "incur";
|
|
11585
11585
|
import { render as render2 } from "ink";
|
|
11586
11586
|
|
|
11587
|
+
// src/commands/demo/demo-runner.tsx
|
|
11588
|
+
import { Box as Box8, Text as Text9, useInput as useInput4 } from "ink";
|
|
11589
|
+
import { useCallback, useState as useState6 } from "react";
|
|
11590
|
+
|
|
11591
|
+
// src/utils/markdown-text.tsx
|
|
11592
|
+
import { Text as Text3 } from "ink";
|
|
11593
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
11594
|
+
var MarkdownText = ({
|
|
11595
|
+
children,
|
|
11596
|
+
dimColor
|
|
11597
|
+
}) => {
|
|
11598
|
+
const parts = tokenize(children);
|
|
11599
|
+
return /* @__PURE__ */ jsx4(Text3, { dimColor, children: parts.map((part) => {
|
|
11600
|
+
if (part.type === "bold") {
|
|
11601
|
+
return /* @__PURE__ */ jsx4(Text3, { bold: true, children: part.text }, part.key);
|
|
11602
|
+
}
|
|
11603
|
+
if (part.type === "code") {
|
|
11604
|
+
return /* @__PURE__ */ jsx4(Text3, { color: "yellow", children: part.text }, part.key);
|
|
11605
|
+
}
|
|
11606
|
+
return /* @__PURE__ */ jsx4(Text3, { children: part.text }, part.key);
|
|
11607
|
+
}) });
|
|
11608
|
+
};
|
|
11609
|
+
function tokenize(input) {
|
|
11610
|
+
const tokens = [];
|
|
11611
|
+
const re = /\*\*([^*]+)\*\*|`([^`]+)`/g;
|
|
11612
|
+
let last = 0;
|
|
11613
|
+
for (; ; ) {
|
|
11614
|
+
const match = re.exec(input);
|
|
11615
|
+
if (match === null) break;
|
|
11616
|
+
if (match.index > last) {
|
|
11617
|
+
tokens.push({
|
|
11618
|
+
type: "text",
|
|
11619
|
+
key: `t${last}`,
|
|
11620
|
+
text: input.slice(last, match.index)
|
|
11621
|
+
});
|
|
11622
|
+
}
|
|
11623
|
+
if (match[1] !== void 0) {
|
|
11624
|
+
tokens.push({ type: "bold", key: `b${match.index}`, text: match[1] });
|
|
11625
|
+
} else if (match[2] !== void 0) {
|
|
11626
|
+
tokens.push({
|
|
11627
|
+
type: "code",
|
|
11628
|
+
key: `c${match.index}`,
|
|
11629
|
+
text: `\`${match[2]}\``
|
|
11630
|
+
});
|
|
11631
|
+
}
|
|
11632
|
+
last = match.index + match[0].length;
|
|
11633
|
+
}
|
|
11634
|
+
if (last < input.length) {
|
|
11635
|
+
tokens.push({ type: "text", key: `t${last}`, text: input.slice(last) });
|
|
11636
|
+
}
|
|
11637
|
+
return tokens;
|
|
11638
|
+
}
|
|
11639
|
+
|
|
11640
|
+
// src/commands/spend-request/app-download-qr-codes.tsx
|
|
11641
|
+
import { Box as Box3, Text as Text4 } from "ink";
|
|
11642
|
+
import { useMemo } from "react";
|
|
11643
|
+
|
|
11644
|
+
// src/utils/render-qr-matrix.ts
|
|
11645
|
+
import QRCode from "qrcode";
|
|
11646
|
+
function renderQrMatrix(url) {
|
|
11647
|
+
const qr = QRCode.create(url, { errorCorrectionLevel: "L" });
|
|
11648
|
+
const size = qr.modules.size;
|
|
11649
|
+
const data = qr.modules.data;
|
|
11650
|
+
const quiet = 1;
|
|
11651
|
+
const total = size + quiet * 2;
|
|
11652
|
+
const matrix = Array.from(
|
|
11653
|
+
{ length: total },
|
|
11654
|
+
(_, r) => Array.from({ length: total }, (_2, c) => {
|
|
11655
|
+
if (r < quiet || r >= size + quiet || c < quiet || c >= size + quiet) {
|
|
11656
|
+
return false;
|
|
11657
|
+
}
|
|
11658
|
+
return data[(r - quiet) * size + (c - quiet)] === 1;
|
|
11659
|
+
})
|
|
11660
|
+
);
|
|
11661
|
+
const lines = [];
|
|
11662
|
+
for (let r = 0; r < total; r += 2) {
|
|
11663
|
+
let line = "";
|
|
11664
|
+
for (let c = 0; c < total; c++) {
|
|
11665
|
+
const top = matrix[r][c];
|
|
11666
|
+
const bottom = r + 1 < total ? matrix[r + 1][c] : false;
|
|
11667
|
+
if (top && bottom) line += "\u2588";
|
|
11668
|
+
else if (top) line += "\u2580";
|
|
11669
|
+
else if (bottom) line += "\u2584";
|
|
11670
|
+
else line += " ";
|
|
11671
|
+
}
|
|
11672
|
+
lines.push(line);
|
|
11673
|
+
}
|
|
11674
|
+
return lines;
|
|
11675
|
+
}
|
|
11676
|
+
|
|
11677
|
+
// src/commands/spend-request/app-download-qr-codes.tsx
|
|
11678
|
+
import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
11679
|
+
var DOWNLOAD_URL = "https://link.com/download";
|
|
11680
|
+
var AppDownloadQrCodes = () => {
|
|
11681
|
+
const qrLines = useMemo(() => renderQrMatrix(DOWNLOAD_URL), []);
|
|
11682
|
+
return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", marginTop: 1, children: [
|
|
11683
|
+
/* @__PURE__ */ jsx5(Text4, { dimColor: true, children: "Get the Link app to approve spend requests from your phone" }),
|
|
11684
|
+
/* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", alignItems: "flex-start", marginTop: 1, children: [
|
|
11685
|
+
qrLines.map((line, i) => (
|
|
11686
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: stable static array
|
|
11687
|
+
/* @__PURE__ */ jsx5(Text4, { children: line }, i)
|
|
11688
|
+
)),
|
|
11689
|
+
/* @__PURE__ */ jsx5(Text4, { dimColor: true, children: DOWNLOAD_URL })
|
|
11690
|
+
] })
|
|
11691
|
+
] });
|
|
11692
|
+
};
|
|
11693
|
+
|
|
11694
|
+
// src/commands/demo/card-flow.tsx
|
|
11695
|
+
import { Box as Box5, Text as Text6, useInput as useInput2 } from "ink";
|
|
11696
|
+
import { useEffect as useEffect3, useRef, useState as useState3 } from "react";
|
|
11697
|
+
|
|
11698
|
+
// src/utils/poll-until-approved.ts
|
|
11699
|
+
function pollUntilApproved(repository, id, options = {}) {
|
|
11700
|
+
const pollIntervalMs = options.pollIntervalMs ?? 2e3;
|
|
11701
|
+
const timeoutMs = options.timeoutMs ?? 3e5;
|
|
11702
|
+
const startTime = Date.now();
|
|
11703
|
+
const poll = async () => {
|
|
11704
|
+
const elapsed = Date.now() - startTime;
|
|
11705
|
+
if (elapsed > timeoutMs) {
|
|
11706
|
+
throw new Error("Approval polling timed out");
|
|
11707
|
+
}
|
|
11708
|
+
const request = await repository.getSpendRequest(id);
|
|
11709
|
+
if (!request) {
|
|
11710
|
+
throw new Error(`Spend request ${id} not found`);
|
|
11711
|
+
}
|
|
11712
|
+
if (request.status !== "created" && request.status !== "pending_approval") {
|
|
11713
|
+
return request;
|
|
11714
|
+
}
|
|
11715
|
+
options.onProgress?.(Math.floor(elapsed / 1e3));
|
|
11716
|
+
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
11717
|
+
return poll();
|
|
11718
|
+
};
|
|
11719
|
+
return poll();
|
|
11720
|
+
}
|
|
11721
|
+
|
|
11722
|
+
// src/commands/demo/constants.ts
|
|
11723
|
+
var DEMO_MERCHANT_NAME = "Galtee Outdoor";
|
|
11724
|
+
var DEMO_MERCHANT_URL = "https://galtee.stripedemos.com/";
|
|
11725
|
+
var DEMO_MPP_DEV_URL = "https://mpp.dev";
|
|
11726
|
+
var DEMO_CLIMATE_API_URL = "https://climate.stripe.dev/api/test/contribute";
|
|
11727
|
+
var DEMO_CARD_AMOUNT = 12900;
|
|
11728
|
+
var DEMO_SPT_AMOUNT = 100;
|
|
11729
|
+
var DEMO_CARD_CONTEXT = "Link CLI onboarding demo: Merino Trail Jacket (size M, Forest) from Galtee Outdoor. Agent completing checkout using a virtual card credential.";
|
|
11730
|
+
var DEMO_SPT_CONTEXT = "Demo purchase: Testing the Link CLI machine payment flow by making an automated HTTP 402 payment to Stripe Climate. This demonstrates the shared payment token credential and machine payment protocol.";
|
|
11731
|
+
|
|
11732
|
+
// src/commands/demo/content.ts
|
|
11733
|
+
var cardAmount = `$${(DEMO_CARD_AMOUNT / 100).toFixed(2)}`;
|
|
11734
|
+
var sptAmount = `$${(DEMO_SPT_AMOUNT / 100).toFixed(2)}`;
|
|
11735
|
+
var CARD_FLOW = {
|
|
11736
|
+
title: "Flow 1: Virtual Card",
|
|
11737
|
+
intro: {
|
|
11738
|
+
description: `An agent buys a Merino Trail Jacket (${cardAmount}) from Galtee Outdoor using a **virtual card** \u2014 a one-time card number issued from your Link wallet. Your real payment details are never shared with the agent or the business.`,
|
|
11739
|
+
steps: [
|
|
11740
|
+
`The agent calls \`spend-request create\` to request ${cardAmount} at Galtee Outdoor`,
|
|
11741
|
+
"You approve in the Link app",
|
|
11742
|
+
"Link issues a one-time virtual card",
|
|
11743
|
+
"Open the checkout page and enter the card details"
|
|
11744
|
+
],
|
|
11745
|
+
prompt: "Press [Enter] to start"
|
|
11746
|
+
},
|
|
11747
|
+
createSpend: {
|
|
11748
|
+
description: `\`spend-request create\` sends the business name, URL, amount (${cardAmount}), and purchase description to Link. No credentials are issued until you approve.`,
|
|
11749
|
+
loading: "Creating spend request..."
|
|
11750
|
+
},
|
|
11751
|
+
approval: {
|
|
11752
|
+
description: "Open the URL to approve the spend request. The CLI polls and continues once approved.",
|
|
11753
|
+
loading: "Waiting for approval...",
|
|
11754
|
+
browserHint: "Press [Enter] to open in browser"
|
|
11755
|
+
},
|
|
11756
|
+
showCard: {
|
|
11757
|
+
description: "Link issued a one-time virtual card. An agent fills these into the checkout form. Single-use \u2014 expires in minutes:",
|
|
11758
|
+
openUrl: "Open Galtee Outdoor and enter these details at checkout.",
|
|
11759
|
+
prompt: "Press [Enter] to open Galtee Outdoor"
|
|
11760
|
+
},
|
|
11761
|
+
done: {
|
|
11762
|
+
success: "Opened Galtee Outdoor in your browser",
|
|
11763
|
+
detail: "Enter the card details above at checkout. Galtee Outdoor runs in testmode \u2014 no real charge."
|
|
11764
|
+
}
|
|
11765
|
+
};
|
|
11766
|
+
var SPT_FLOW = {
|
|
11767
|
+
title: "Flow 2: Machine Payment (SPT)",
|
|
11768
|
+
intro: {
|
|
11769
|
+
description: "Some APIs accept payment without a checkout form. When called without credentials, the server responds with **HTTP 402** and a payment challenge. The agent signs that challenge with a **shared payment token** (SPT) and retries \u2014 this is the Machine Payment Protocol (MPP).",
|
|
11770
|
+
preamble: `A ${sptAmount} donation to Stripe Climate (climate.stripe.dev) demonstrates:`,
|
|
11771
|
+
steps: [
|
|
11772
|
+
"Probe the API \u2014 server responds HTTP 402 with a challenge",
|
|
11773
|
+
"Decode the challenge to extract the `network_id`",
|
|
11774
|
+
"Call `spend-request create` for an SPT credential",
|
|
11775
|
+
"Approve the spend request",
|
|
11776
|
+
"`mpp pay` signs the challenge and retries with an Authorization header"
|
|
11777
|
+
],
|
|
11778
|
+
prompt: "Press [Enter] to start"
|
|
11779
|
+
},
|
|
11780
|
+
probe: {
|
|
11781
|
+
description: `The agent POSTs to ${DEMO_CLIMATE_API_URL}. Without a payment credential, the server returns HTTP 402 with a \`WWW-Authenticate\` challenge header.`,
|
|
11782
|
+
loading: "Probing...",
|
|
11783
|
+
detail: "The challenge contains a **network_id** \u2014 the business identifier on the Stripe network. The agent uses it to request the matching SPT credential."
|
|
11784
|
+
},
|
|
11785
|
+
createSpend: {
|
|
11786
|
+
description: `\`spend-request create\` with \`credential_type: "shared_payment_token"\`, the decoded \`network_id\`, and amount (${sptAmount}). The \`network_id\` identifies the business \u2014 no name or URL needed.`,
|
|
11787
|
+
loading: "Creating spend request..."
|
|
11788
|
+
},
|
|
11789
|
+
approval: {
|
|
11790
|
+
description: "Approve the spend request. Once approved, Link issues an SPT the agent uses to sign the 402 challenge.",
|
|
11791
|
+
loading: "Waiting for approval...",
|
|
11792
|
+
browserHint: "Press [Enter] to open in browser"
|
|
11793
|
+
},
|
|
11794
|
+
mppPay: {
|
|
11795
|
+
description: "`mpp pay` retrieves the SPT, re-probes the API, signs the 402 challenge, and retries with an `Authorization: Payment` header.",
|
|
11796
|
+
loading: "Completing payment...",
|
|
11797
|
+
prompt: "Press [Enter] to pay"
|
|
11798
|
+
},
|
|
11799
|
+
done: {
|
|
11800
|
+
success: "Payment complete",
|
|
11801
|
+
detail: `The ${sptAmount} donation went through entirely via API \u2014 no forms, no browser.`
|
|
11802
|
+
}
|
|
11803
|
+
};
|
|
11804
|
+
var DEMO_MENU = {
|
|
11805
|
+
title: "Link CLI Demo",
|
|
11806
|
+
subtitle: "Two flows showing how agents request and use payment credentials with Link.",
|
|
11807
|
+
question: "Which flow would you like to run?",
|
|
11808
|
+
hint: "Use \u2191\u2193 to select, [Enter] to confirm",
|
|
11809
|
+
options: [
|
|
11810
|
+
{
|
|
11811
|
+
key: "both",
|
|
11812
|
+
label: "Both flows",
|
|
11813
|
+
description: "Walk through both flows end-to-end."
|
|
11814
|
+
},
|
|
11815
|
+
{
|
|
11816
|
+
key: "card",
|
|
11817
|
+
label: "Virtual card",
|
|
11818
|
+
description: "The agent requests a one-time card number and fills it into a checkout form."
|
|
11819
|
+
},
|
|
11820
|
+
{
|
|
11821
|
+
key: "spt",
|
|
11822
|
+
label: "Machine payment (SPT)",
|
|
11823
|
+
description: "The agent pays via API using the Machine Payment Protocol \u2014 no browser, no forms."
|
|
11824
|
+
}
|
|
11825
|
+
],
|
|
11826
|
+
transition: "Virtual card flow done. Next: **machine payment** \u2014 the agent pays an API directly, no checkout form needed.",
|
|
11827
|
+
transitionPrompt: "Press [Enter] to continue to Flow 2"
|
|
11828
|
+
};
|
|
11829
|
+
var ONBOARD = {
|
|
11830
|
+
title: "Welcome to Link CLI",
|
|
11831
|
+
subtitle: "Set up Link CLI to let agents make secure payments on your behalf.",
|
|
11832
|
+
auth: {
|
|
11833
|
+
alreadyLoggedIn: "Already logged in",
|
|
11834
|
+
authenticated: "Authenticated",
|
|
11835
|
+
clientName: "Link CLI Onboard"
|
|
11836
|
+
},
|
|
11837
|
+
paymentMethods: {
|
|
11838
|
+
loading: "Checking payment methods...",
|
|
11839
|
+
pickPrompt: "Which payment method should we use for the demo?",
|
|
11840
|
+
pickHint: "Use \u2191\u2193 to select, [Enter] to confirm",
|
|
11841
|
+
missing: "No payment methods found in your Link wallet.",
|
|
11842
|
+
missingSteps: [
|
|
11843
|
+
"Open the Link app or visit link.com",
|
|
11844
|
+
"Add a payment method",
|
|
11845
|
+
"Come back here and press [Enter] to retry"
|
|
11846
|
+
],
|
|
11847
|
+
retryPrompt: "Press [Enter] to retry"
|
|
11848
|
+
},
|
|
11849
|
+
appTip: {
|
|
11850
|
+
title: "Get the Link app",
|
|
11851
|
+
description: "Approve spend requests from your phone. Push notifications let you approve or deny instantly.",
|
|
11852
|
+
url: "https://link.com/download"
|
|
11853
|
+
}
|
|
11854
|
+
};
|
|
11855
|
+
|
|
11856
|
+
// src/commands/demo/step-data.tsx
|
|
11857
|
+
import { Box as Box4, Text as Text5 } from "ink";
|
|
11858
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
11859
|
+
var StepData = ({ data }) => {
|
|
11860
|
+
const entries = Object.entries(data).filter(
|
|
11861
|
+
([, v]) => v !== void 0 && v !== null
|
|
11862
|
+
);
|
|
11863
|
+
const maxKeyLen = Math.max(...entries.map(([k]) => k.length));
|
|
11864
|
+
return /* @__PURE__ */ jsx6(
|
|
11865
|
+
Box4,
|
|
11866
|
+
{
|
|
11867
|
+
flexDirection: "column",
|
|
11868
|
+
paddingX: 2,
|
|
11869
|
+
marginTop: 1,
|
|
11870
|
+
borderStyle: "single",
|
|
11871
|
+
borderColor: "gray",
|
|
11872
|
+
children: entries.map(([key, value]) => /* @__PURE__ */ jsxs3(Text5, { children: [
|
|
11873
|
+
/* @__PURE__ */ jsx6(Text5, { dimColor: true, children: key.padEnd(maxKeyLen) }),
|
|
11874
|
+
" ",
|
|
11875
|
+
/* @__PURE__ */ jsx6(Text5, { children: typeof value === "object" ? JSON.stringify(value) : String(value) })
|
|
11876
|
+
] }, key))
|
|
11877
|
+
}
|
|
11878
|
+
);
|
|
11879
|
+
};
|
|
11880
|
+
|
|
11881
|
+
// src/commands/demo/card-flow.tsx
|
|
11882
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
11883
|
+
function formatPmLabel(pm) {
|
|
11884
|
+
return `${pm.card_details?.brand ?? pm.type} ****${pm.card_details?.last4 ?? ""}`;
|
|
11885
|
+
}
|
|
11886
|
+
function formatCardNumber(num) {
|
|
11887
|
+
return num.replace(/(.{4})/g, "$1 ").trim();
|
|
11888
|
+
}
|
|
11889
|
+
function formatExpiry(month, year) {
|
|
11890
|
+
return `${String(month).padStart(2, "0")}/${String(year).slice(-2)}`;
|
|
11891
|
+
}
|
|
11892
|
+
var CardFlow = ({
|
|
11893
|
+
spendRequestRepo: spendRequestRepo2,
|
|
11894
|
+
paymentMethodsResource,
|
|
11895
|
+
paymentMethodId: initialPaymentMethodId,
|
|
11896
|
+
onComplete
|
|
11897
|
+
}) => {
|
|
11898
|
+
const [step, setStep] = useState3("intro");
|
|
11899
|
+
const [paymentMethod, setPaymentMethod] = useState3(
|
|
11900
|
+
null
|
|
11901
|
+
);
|
|
11902
|
+
const [paymentMethods, setPaymentMethods] = useState3([]);
|
|
11903
|
+
const [selectedPmIndex, setSelectedPmIndex] = useState3(0);
|
|
11904
|
+
const [spendRequest, setSpendRequest] = useState3(null);
|
|
11905
|
+
const [spendRequestPayload, setSpendRequestPayload] = useState3(null);
|
|
11906
|
+
const [error, setError] = useState3("");
|
|
11907
|
+
const approvalUrl = spendRequest?.approval_url ?? "";
|
|
11908
|
+
const enterResolver = useRef(null);
|
|
11909
|
+
const pmResolver = useRef(null);
|
|
11910
|
+
const retryChoiceResolver = useRef(null);
|
|
11911
|
+
function waitForEnter() {
|
|
11912
|
+
return new Promise((resolve) => {
|
|
11913
|
+
enterResolver.current = resolve;
|
|
11914
|
+
});
|
|
11915
|
+
}
|
|
11916
|
+
function waitForPmSelection() {
|
|
11917
|
+
return new Promise((resolve) => {
|
|
11918
|
+
pmResolver.current = resolve;
|
|
11919
|
+
});
|
|
11920
|
+
}
|
|
11921
|
+
function waitForRetryChoice() {
|
|
11922
|
+
return new Promise((resolve) => {
|
|
11923
|
+
retryChoiceResolver.current = resolve;
|
|
11924
|
+
});
|
|
11925
|
+
}
|
|
11926
|
+
useInput2((input, key) => {
|
|
11927
|
+
if (step === "pick-pm") {
|
|
11928
|
+
if (key.upArrow) {
|
|
11929
|
+
setSelectedPmIndex((i) => i > 0 ? i - 1 : paymentMethods.length - 1);
|
|
11930
|
+
} else if (key.downArrow) {
|
|
11931
|
+
setSelectedPmIndex((i) => i < paymentMethods.length - 1 ? i + 1 : 0);
|
|
11932
|
+
} else if (key.return && pmResolver.current) {
|
|
11933
|
+
const pm = paymentMethods[selectedPmIndex];
|
|
11934
|
+
const resolve = pmResolver.current;
|
|
11935
|
+
pmResolver.current = null;
|
|
11936
|
+
resolve(pm.id);
|
|
11937
|
+
}
|
|
11938
|
+
} else if (step === "approval-timeout" && retryChoiceResolver.current) {
|
|
11939
|
+
if (input === "r") {
|
|
11940
|
+
const resolve = retryChoiceResolver.current;
|
|
11941
|
+
retryChoiceResolver.current = null;
|
|
11942
|
+
resolve("retry");
|
|
11943
|
+
} else if (input === "q") {
|
|
11944
|
+
const resolve = retryChoiceResolver.current;
|
|
11945
|
+
retryChoiceResolver.current = null;
|
|
11946
|
+
resolve("exit");
|
|
11947
|
+
}
|
|
11948
|
+
} else if (key.return) {
|
|
11949
|
+
if (enterResolver.current) {
|
|
11950
|
+
const resolve = enterResolver.current;
|
|
11951
|
+
enterResolver.current = null;
|
|
11952
|
+
resolve();
|
|
11953
|
+
} else if ((step === "await-approval" || step === "approval-timeout") && approvalUrl) {
|
|
11954
|
+
openUrl(approvalUrl);
|
|
11955
|
+
}
|
|
11956
|
+
}
|
|
11957
|
+
});
|
|
11958
|
+
const started = useRef(false);
|
|
11959
|
+
useEffect3(() => {
|
|
11960
|
+
if (started.current) return;
|
|
11961
|
+
started.current = true;
|
|
11962
|
+
const run = async () => {
|
|
11963
|
+
try {
|
|
11964
|
+
await waitForEnter();
|
|
11965
|
+
let pmId = initialPaymentMethodId;
|
|
11966
|
+
if (!pmId) {
|
|
11967
|
+
setStep("fetch-pm");
|
|
11968
|
+
const methods = await paymentMethodsResource.listPaymentMethods();
|
|
11969
|
+
if (methods.length === 0) {
|
|
11970
|
+
setError(
|
|
11971
|
+
"No payment methods found. Open the Link app (link.com) and add a card to your wallet, then run the demo again."
|
|
11972
|
+
);
|
|
11973
|
+
setStep("error");
|
|
11974
|
+
onComplete({ paymentMethodId: "", success: false });
|
|
11975
|
+
return;
|
|
11976
|
+
}
|
|
11977
|
+
if (methods.length === 1) {
|
|
11978
|
+
const pm = methods[0];
|
|
11979
|
+
setPaymentMethod(pm);
|
|
11980
|
+
setPaymentMethods(methods);
|
|
11981
|
+
pmId = pm.id;
|
|
11982
|
+
} else {
|
|
11983
|
+
setPaymentMethods(methods);
|
|
11984
|
+
const defaultIdx = methods.findIndex((m) => m.is_default);
|
|
11985
|
+
setSelectedPmIndex(defaultIdx >= 0 ? defaultIdx : 0);
|
|
11986
|
+
setStep("pick-pm");
|
|
11987
|
+
pmId = await waitForPmSelection();
|
|
11988
|
+
const pm = methods.find((m) => m.id === pmId) ?? methods[0];
|
|
11989
|
+
setPaymentMethod(pm);
|
|
11990
|
+
}
|
|
11991
|
+
setStep("explain-pm");
|
|
11992
|
+
await waitForEnter();
|
|
11993
|
+
}
|
|
11994
|
+
setStep("create-spend");
|
|
11995
|
+
const payload = {
|
|
11996
|
+
payment_details: pmId,
|
|
11997
|
+
credential_type: "card",
|
|
11998
|
+
amount: DEMO_CARD_AMOUNT,
|
|
11999
|
+
context: DEMO_CARD_CONTEXT,
|
|
12000
|
+
merchant_name: DEMO_MERCHANT_NAME,
|
|
12001
|
+
merchant_url: DEMO_MERCHANT_URL,
|
|
12002
|
+
request_approval: true,
|
|
12003
|
+
test: true
|
|
12004
|
+
};
|
|
12005
|
+
setSpendRequestPayload(payload);
|
|
12006
|
+
const result = await spendRequestRepo2.createSpendRequest(payload);
|
|
12007
|
+
setSpendRequest(result);
|
|
12008
|
+
setStep("await-approval");
|
|
12009
|
+
for (; ; ) {
|
|
12010
|
+
try {
|
|
12011
|
+
await pollUntilApproved(spendRequestRepo2, result.id);
|
|
12012
|
+
break;
|
|
12013
|
+
} catch (err) {
|
|
12014
|
+
if (err.message === "Approval polling timed out") {
|
|
12015
|
+
setStep("approval-timeout");
|
|
12016
|
+
const choice = await waitForRetryChoice();
|
|
12017
|
+
if (choice === "exit") {
|
|
12018
|
+
onComplete({ paymentMethodId: pmId ?? "", success: false });
|
|
12019
|
+
return;
|
|
12020
|
+
}
|
|
12021
|
+
setStep("await-approval");
|
|
12022
|
+
} else {
|
|
12023
|
+
throw err;
|
|
12024
|
+
}
|
|
12025
|
+
}
|
|
12026
|
+
}
|
|
12027
|
+
const approved = await spendRequestRepo2.getSpendRequest(result.id, {
|
|
12028
|
+
include: ["card"]
|
|
12029
|
+
});
|
|
12030
|
+
if (approved) setSpendRequest(approved);
|
|
12031
|
+
setStep("show-card");
|
|
12032
|
+
await waitForEnter();
|
|
12033
|
+
setStep("open-url");
|
|
12034
|
+
openUrl(DEMO_MERCHANT_URL);
|
|
12035
|
+
setStep("done");
|
|
12036
|
+
onComplete({ paymentMethodId: pmId, success: true });
|
|
12037
|
+
} catch (err) {
|
|
12038
|
+
setError(err.message);
|
|
12039
|
+
setStep("error");
|
|
12040
|
+
onComplete({
|
|
12041
|
+
paymentMethodId: paymentMethod?.id ?? "",
|
|
12042
|
+
success: false
|
|
12043
|
+
});
|
|
12044
|
+
}
|
|
12045
|
+
};
|
|
12046
|
+
run();
|
|
12047
|
+
}, []);
|
|
12048
|
+
const pmLabel = paymentMethod ? formatPmLabel(paymentMethod) : "";
|
|
12049
|
+
const card = spendRequest?.card;
|
|
12050
|
+
const pastStep = (target) => {
|
|
12051
|
+
const order = [
|
|
12052
|
+
"intro",
|
|
12053
|
+
"fetch-pm",
|
|
12054
|
+
"pick-pm",
|
|
12055
|
+
"explain-pm",
|
|
12056
|
+
"create-spend",
|
|
12057
|
+
"await-approval",
|
|
12058
|
+
"show-card",
|
|
12059
|
+
"open-url",
|
|
12060
|
+
"done"
|
|
12061
|
+
];
|
|
12062
|
+
return order.indexOf(step) > order.indexOf(target);
|
|
12063
|
+
};
|
|
12064
|
+
const prompt = (label = "Press [Enter] to continue") => /* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
|
|
12065
|
+
"\n",
|
|
12066
|
+
">",
|
|
12067
|
+
" ",
|
|
12068
|
+
label
|
|
12069
|
+
] });
|
|
12070
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, children: [
|
|
12071
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "cyan", children: CARD_FLOW.title }),
|
|
12072
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
12073
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "row", gap: 1, children: [
|
|
12074
|
+
/* @__PURE__ */ jsx7(Text6, { color: "yellow", children: "[testmode]" }),
|
|
12075
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: DEMO_MERCHANT_URL })
|
|
12076
|
+
] }),
|
|
12077
|
+
/* @__PURE__ */ jsx7(MarkdownText, { children: CARD_FLOW.intro.description }),
|
|
12078
|
+
/* @__PURE__ */ jsx7(Box5, { marginTop: 1, children: /* @__PURE__ */ jsxs4(Text6, { children: [
|
|
12079
|
+
"What happens:",
|
|
12080
|
+
"\n",
|
|
12081
|
+
CARD_FLOW.intro.steps.map((s, i) => ` ${i + 1}. ${s}`).join("\n")
|
|
12082
|
+
] }) }),
|
|
12083
|
+
step === "intro" && prompt(CARD_FLOW.intro.prompt)
|
|
12084
|
+
] }),
|
|
12085
|
+
step === "fetch-pm" && /* @__PURE__ */ jsx7(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "Fetching payment methods from your Link wallet..." }) }),
|
|
12086
|
+
step === "pick-pm" && paymentMethods.length > 1 && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
12087
|
+
/* @__PURE__ */ jsx7(Text6, { children: "Which payment method should we use for the demo?" }),
|
|
12088
|
+
/* @__PURE__ */ jsx7(Box5, { flexDirection: "column", marginTop: 1, children: paymentMethods.map((pm, i) => /* @__PURE__ */ jsx7(Text6, { children: i === selectedPmIndex ? /* @__PURE__ */ jsxs4(Text6, { color: "cyan", bold: true, children: [
|
|
12089
|
+
">",
|
|
12090
|
+
" ",
|
|
12091
|
+
formatPmLabel(pm),
|
|
12092
|
+
pm.is_default ? " (default)" : ""
|
|
12093
|
+
] }) : /* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
|
|
12094
|
+
" ",
|
|
12095
|
+
formatPmLabel(pm),
|
|
12096
|
+
pm.is_default ? " (default)" : ""
|
|
12097
|
+
] }) }, pm.id)) }),
|
|
12098
|
+
/* @__PURE__ */ jsx7(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "Use \u2191\u2193 to select, [Enter] to confirm" }) })
|
|
12099
|
+
] }),
|
|
12100
|
+
pastStep("fetch-pm") && paymentMethod && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
12101
|
+
/* @__PURE__ */ jsxs4(Text6, { color: "green", children: [
|
|
12102
|
+
"\u2713 Using ",
|
|
12103
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, children: pmLabel }),
|
|
12104
|
+
paymentMethod.is_default ? " (default)" : ""
|
|
12105
|
+
] }),
|
|
12106
|
+
step === "explain-pm" && prompt()
|
|
12107
|
+
] }),
|
|
12108
|
+
pastStep("explain-pm") && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
12109
|
+
/* @__PURE__ */ jsx7(MarkdownText, { children: CARD_FLOW.createSpend.description }),
|
|
12110
|
+
spendRequestPayload && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
12111
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "spend-request create" }),
|
|
12112
|
+
/* @__PURE__ */ jsx7(
|
|
12113
|
+
Box5,
|
|
12114
|
+
{
|
|
12115
|
+
flexDirection: "column",
|
|
12116
|
+
borderStyle: "single",
|
|
12117
|
+
borderColor: "gray",
|
|
12118
|
+
paddingX: 2,
|
|
12119
|
+
children: /* @__PURE__ */ jsx7(Text6, { children: JSON.stringify(
|
|
12120
|
+
spendRequestPayload,
|
|
12121
|
+
null,
|
|
12122
|
+
2
|
|
12123
|
+
) })
|
|
12124
|
+
}
|
|
12125
|
+
)
|
|
12126
|
+
] }),
|
|
12127
|
+
step === "create-spend" && /* @__PURE__ */ jsx7(Box5, { marginY: 1, children: /* @__PURE__ */ jsx7(Text6, { color: "cyan", children: CARD_FLOW.createSpend.loading }) })
|
|
12128
|
+
] }),
|
|
12129
|
+
(step === "await-approval" || step === "approval-timeout" || pastStep("await-approval")) && spendRequest && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
12130
|
+
/* @__PURE__ */ jsxs4(Text6, { color: "green", children: [
|
|
12131
|
+
"\u2713 Spend request created (ID: ",
|
|
12132
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, children: spendRequest.id }),
|
|
12133
|
+
")"
|
|
12134
|
+
] }),
|
|
12135
|
+
/* @__PURE__ */ jsx7(
|
|
12136
|
+
StepData,
|
|
12137
|
+
{
|
|
12138
|
+
data: {
|
|
12139
|
+
id: spendRequest.id,
|
|
12140
|
+
status: spendRequest.status,
|
|
12141
|
+
credential_type: spendRequest.credential_type,
|
|
12142
|
+
amount: spendRequest.amount,
|
|
12143
|
+
merchant_name: spendRequest.merchant_name,
|
|
12144
|
+
merchant_url: spendRequest.merchant_url,
|
|
12145
|
+
context: spendRequest.context,
|
|
12146
|
+
approval_url: spendRequest.approval_url
|
|
12147
|
+
}
|
|
12148
|
+
}
|
|
12149
|
+
)
|
|
12150
|
+
] }),
|
|
12151
|
+
step === "await-approval" && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
12152
|
+
/* @__PURE__ */ jsx7(Text6, { children: CARD_FLOW.approval.description }),
|
|
12153
|
+
/* @__PURE__ */ jsxs4(
|
|
12154
|
+
Box5,
|
|
12155
|
+
{
|
|
12156
|
+
flexDirection: "column",
|
|
12157
|
+
borderStyle: "round",
|
|
12158
|
+
borderColor: "cyan",
|
|
12159
|
+
paddingX: 2,
|
|
12160
|
+
paddingY: 1,
|
|
12161
|
+
marginTop: 1,
|
|
12162
|
+
children: [
|
|
12163
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
12164
|
+
"Approve at:",
|
|
12165
|
+
" ",
|
|
12166
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "cyan", children: approvalUrl })
|
|
12167
|
+
] }),
|
|
12168
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: CARD_FLOW.approval.browserHint })
|
|
12169
|
+
]
|
|
12170
|
+
}
|
|
12171
|
+
),
|
|
12172
|
+
/* @__PURE__ */ jsx7(Box5, { marginY: 1, children: /* @__PURE__ */ jsx7(Text6, { color: "cyan", children: CARD_FLOW.approval.loading }) })
|
|
12173
|
+
] }),
|
|
12174
|
+
step === "approval-timeout" && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
12175
|
+
/* @__PURE__ */ jsx7(Text6, { color: "yellow", children: "\u26A0 Approval timed out (5 min). The spend request is still pending \u2014 you can still approve it." }),
|
|
12176
|
+
/* @__PURE__ */ jsxs4(
|
|
12177
|
+
Box5,
|
|
12178
|
+
{
|
|
12179
|
+
flexDirection: "column",
|
|
12180
|
+
borderStyle: "round",
|
|
12181
|
+
borderColor: "yellow",
|
|
12182
|
+
paddingX: 2,
|
|
12183
|
+
paddingY: 1,
|
|
12184
|
+
marginTop: 1,
|
|
12185
|
+
children: [
|
|
12186
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
12187
|
+
"Approve at:",
|
|
12188
|
+
" ",
|
|
12189
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "cyan", children: approvalUrl })
|
|
12190
|
+
] }),
|
|
12191
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "Press [Enter] to open in browser" })
|
|
12192
|
+
]
|
|
12193
|
+
}
|
|
12194
|
+
),
|
|
12195
|
+
/* @__PURE__ */ jsx7(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "r Retry polling q Quit demo" }) })
|
|
12196
|
+
] }),
|
|
12197
|
+
pastStep("await-approval") && card && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
12198
|
+
/* @__PURE__ */ jsx7(Text6, { color: "green", children: "\u2713 Approved!" }),
|
|
12199
|
+
/* @__PURE__ */ jsx7(MarkdownText, { children: CARD_FLOW.showCard.description }),
|
|
12200
|
+
/* @__PURE__ */ jsxs4(
|
|
12201
|
+
Box5,
|
|
12202
|
+
{
|
|
12203
|
+
flexDirection: "column",
|
|
12204
|
+
borderStyle: "round",
|
|
12205
|
+
borderColor: "green",
|
|
12206
|
+
paddingX: 2,
|
|
12207
|
+
paddingY: 1,
|
|
12208
|
+
marginTop: 1,
|
|
12209
|
+
children: [
|
|
12210
|
+
/* @__PURE__ */ jsx7(Text6, { color: "yellow", dimColor: true, children: "[testmode card]" }),
|
|
12211
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
12212
|
+
"Number: ",
|
|
12213
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, children: formatCardNumber(card.number) })
|
|
12214
|
+
] }),
|
|
12215
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
12216
|
+
"Exp:",
|
|
12217
|
+
" ",
|
|
12218
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, children: formatExpiry(card.exp_month, card.exp_year) })
|
|
12219
|
+
] }),
|
|
12220
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
12221
|
+
"CVC: ",
|
|
12222
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, children: card.cvc })
|
|
12223
|
+
] }),
|
|
12224
|
+
card.billing_address?.postal_code && /* @__PURE__ */ jsxs4(Text6, { children: [
|
|
12225
|
+
"Zip: ",
|
|
12226
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, children: card.billing_address.postal_code })
|
|
12227
|
+
] }),
|
|
12228
|
+
card.valid_until && /* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
|
|
12229
|
+
"Expires:",
|
|
12230
|
+
" ",
|
|
12231
|
+
new Date(card.valid_until).toLocaleTimeString([], {
|
|
12232
|
+
hour: "2-digit",
|
|
12233
|
+
minute: "2-digit"
|
|
12234
|
+
})
|
|
12235
|
+
] })
|
|
12236
|
+
]
|
|
12237
|
+
}
|
|
12238
|
+
),
|
|
12239
|
+
/* @__PURE__ */ jsx7(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx7(MarkdownText, { children: CARD_FLOW.showCard.openUrl }) }),
|
|
12240
|
+
step === "show-card" && prompt(CARD_FLOW.showCard.prompt)
|
|
12241
|
+
] }),
|
|
12242
|
+
step === "done" && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
12243
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "row", gap: 1, children: [
|
|
12244
|
+
/* @__PURE__ */ jsx7(Text6, { color: "yellow", children: "[testmode]" }),
|
|
12245
|
+
/* @__PURE__ */ jsxs4(Text6, { color: "green", children: [
|
|
12246
|
+
"\u2713 ",
|
|
12247
|
+
CARD_FLOW.done.success
|
|
12248
|
+
] })
|
|
12249
|
+
] }),
|
|
12250
|
+
/* @__PURE__ */ jsx7(Text6, { children: CARD_FLOW.done.detail })
|
|
12251
|
+
] }),
|
|
12252
|
+
step === "error" && /* @__PURE__ */ jsxs4(Text6, { color: "red", children: [
|
|
12253
|
+
"Error: ",
|
|
12254
|
+
error
|
|
12255
|
+
] })
|
|
12256
|
+
] });
|
|
12257
|
+
};
|
|
12258
|
+
|
|
12259
|
+
// src/commands/demo/spt-flow.tsx
|
|
12260
|
+
import { Box as Box7, Text as Text8, useInput as useInput3 } from "ink";
|
|
12261
|
+
import { useEffect as useEffect5, useRef as useRef2, useState as useState5 } from "react";
|
|
12262
|
+
|
|
11587
12263
|
// src/commands/mpp/decode.ts
|
|
11588
12264
|
import { Challenge } from "mppx";
|
|
11589
12265
|
function getString(value, path5, required = true) {
|
|
@@ -11662,41 +12338,14 @@ function decodeStripeChallenge(challengeHeader) {
|
|
|
11662
12338
|
};
|
|
11663
12339
|
}
|
|
11664
12340
|
|
|
11665
|
-
// src/commands/mpp/decode-view.tsx
|
|
11666
|
-
import { Box as Box3, Text as Text3 } from "ink";
|
|
11667
|
-
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
11668
|
-
function DecodeChallengeView({
|
|
11669
|
-
decoded
|
|
11670
|
-
}) {
|
|
11671
|
-
return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", children: [
|
|
11672
|
-
/* @__PURE__ */ jsx4(Text3, { color: "green", children: "\u2713 Stripe challenge decoded" }),
|
|
11673
|
-
/* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
11674
|
-
/* @__PURE__ */ jsxs2(Text3, { children: [
|
|
11675
|
-
"ID: ",
|
|
11676
|
-
/* @__PURE__ */ jsx4(Text3, { bold: true, children: decoded.id })
|
|
11677
|
-
] }),
|
|
11678
|
-
/* @__PURE__ */ jsxs2(Text3, { children: [
|
|
11679
|
-
"Realm: ",
|
|
11680
|
-
/* @__PURE__ */ jsx4(Text3, { bold: true, children: decoded.realm })
|
|
11681
|
-
] }),
|
|
11682
|
-
/* @__PURE__ */ jsxs2(Text3, { children: [
|
|
11683
|
-
"Network ID: ",
|
|
11684
|
-
/* @__PURE__ */ jsx4(Text3, { bold: true, children: decoded.network_id })
|
|
11685
|
-
] }),
|
|
11686
|
-
/* @__PURE__ */ jsx4(Text3, { children: "Request JSON:" }),
|
|
11687
|
-
/* @__PURE__ */ jsx4(Text3, { children: JSON.stringify(decoded.request_json, null, 2) })
|
|
11688
|
-
] })
|
|
11689
|
-
] });
|
|
11690
|
-
}
|
|
11691
|
-
|
|
11692
12341
|
// src/commands/mpp/pay.tsx
|
|
11693
|
-
import { Box as
|
|
12342
|
+
import { Box as Box6, Text as Text7 } from "ink";
|
|
11694
12343
|
import Spinner2 from "ink-spinner";
|
|
11695
12344
|
import { Credential, Method } from "mppx";
|
|
11696
12345
|
import { Mppx, Transport } from "mppx/client";
|
|
11697
12346
|
import { Methods as StripeMethods } from "mppx/stripe";
|
|
11698
|
-
import { useEffect as
|
|
11699
|
-
import { jsx as
|
|
12347
|
+
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
12348
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
11700
12349
|
function buildHeaders(data, headers) {
|
|
11701
12350
|
const result = {};
|
|
11702
12351
|
if (data !== void 0) {
|
|
@@ -11801,10 +12450,10 @@ function MppPay({
|
|
|
11801
12450
|
repository,
|
|
11802
12451
|
onComplete
|
|
11803
12452
|
}) {
|
|
11804
|
-
const [step, setStep] =
|
|
11805
|
-
const [result, setResult] =
|
|
11806
|
-
const [error, setError] =
|
|
11807
|
-
|
|
12453
|
+
const [step, setStep] = useState4("retrieving");
|
|
12454
|
+
const [result, setResult] = useState4(null);
|
|
12455
|
+
const [error, setError] = useState4(null);
|
|
12456
|
+
useEffect4(() => {
|
|
11808
12457
|
(async () => {
|
|
11809
12458
|
try {
|
|
11810
12459
|
setStep("retrieving");
|
|
@@ -11873,54 +12522,640 @@ function MppPay({
|
|
|
11873
12522
|
done: "Done"
|
|
11874
12523
|
};
|
|
11875
12524
|
if (error) {
|
|
11876
|
-
return /* @__PURE__ */
|
|
12525
|
+
return /* @__PURE__ */ jsxs5(Text7, { color: "red", children: [
|
|
11877
12526
|
"Error: ",
|
|
11878
12527
|
error
|
|
11879
12528
|
] });
|
|
11880
12529
|
}
|
|
11881
|
-
return /* @__PURE__ */
|
|
11882
|
-
step !== "done" && /* @__PURE__ */
|
|
11883
|
-
/* @__PURE__ */
|
|
12530
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
12531
|
+
step !== "done" && /* @__PURE__ */ jsx8(Box6, { children: /* @__PURE__ */ jsxs5(Text7, { color: "cyan", children: [
|
|
12532
|
+
/* @__PURE__ */ jsx8(Spinner2, { type: "dots" }),
|
|
11884
12533
|
" ",
|
|
11885
12534
|
stepLabels[step],
|
|
11886
12535
|
"..."
|
|
11887
12536
|
] }) }),
|
|
11888
|
-
result && /* @__PURE__ */
|
|
11889
|
-
/* @__PURE__ */
|
|
12537
|
+
result && /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
12538
|
+
/* @__PURE__ */ jsxs5(Text7, { color: "green", children: [
|
|
11890
12539
|
"HTTP ",
|
|
11891
12540
|
result.status
|
|
11892
12541
|
] }),
|
|
11893
|
-
/* @__PURE__ */
|
|
12542
|
+
/* @__PURE__ */ jsx8(Text7, { children: result.body })
|
|
12543
|
+
] })
|
|
12544
|
+
] });
|
|
12545
|
+
}
|
|
12546
|
+
|
|
12547
|
+
// src/commands/demo/spt-flow.tsx
|
|
12548
|
+
import { Fragment, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
12549
|
+
var SptFlow = ({
|
|
12550
|
+
spendRequestRepo: spendRequestRepo2,
|
|
12551
|
+
paymentMethodsResource,
|
|
12552
|
+
paymentMethodId: initialPaymentMethodId,
|
|
12553
|
+
onComplete
|
|
12554
|
+
}) => {
|
|
12555
|
+
const [step, setStep] = useState5("intro");
|
|
12556
|
+
const [networkId, setNetworkId] = useState5("");
|
|
12557
|
+
const [paymentMethods, setPaymentMethods] = useState5([]);
|
|
12558
|
+
const [selectedPmIndex, setSelectedPmIndex] = useState5(0);
|
|
12559
|
+
const [spendRequest, setSpendRequest] = useState5(null);
|
|
12560
|
+
const [spendRequestPayload, setSpendRequestPayload] = useState5(null);
|
|
12561
|
+
const [payResult, setPayResult] = useState5(null);
|
|
12562
|
+
const [challengeData, setChallengeData] = useState5(null);
|
|
12563
|
+
const [error, setError] = useState5("");
|
|
12564
|
+
const approvalUrl = spendRequest?.approval_url ?? "";
|
|
12565
|
+
const enterResolver = useRef2(null);
|
|
12566
|
+
const pmResolver = useRef2(null);
|
|
12567
|
+
const retryChoiceResolver = useRef2(null);
|
|
12568
|
+
function waitForEnter() {
|
|
12569
|
+
return new Promise((resolve) => {
|
|
12570
|
+
enterResolver.current = resolve;
|
|
12571
|
+
});
|
|
12572
|
+
}
|
|
12573
|
+
function waitForPmSelection() {
|
|
12574
|
+
return new Promise((resolve) => {
|
|
12575
|
+
pmResolver.current = resolve;
|
|
12576
|
+
});
|
|
12577
|
+
}
|
|
12578
|
+
function waitForRetryChoice() {
|
|
12579
|
+
return new Promise((resolve) => {
|
|
12580
|
+
retryChoiceResolver.current = resolve;
|
|
12581
|
+
});
|
|
12582
|
+
}
|
|
12583
|
+
useInput3((input, key) => {
|
|
12584
|
+
if (step === "pick-pm") {
|
|
12585
|
+
if (key.upArrow) {
|
|
12586
|
+
setSelectedPmIndex((i) => i > 0 ? i - 1 : paymentMethods.length - 1);
|
|
12587
|
+
} else if (key.downArrow) {
|
|
12588
|
+
setSelectedPmIndex((i) => i < paymentMethods.length - 1 ? i + 1 : 0);
|
|
12589
|
+
} else if (key.return && pmResolver.current) {
|
|
12590
|
+
const pm = paymentMethods[selectedPmIndex];
|
|
12591
|
+
const resolve = pmResolver.current;
|
|
12592
|
+
pmResolver.current = null;
|
|
12593
|
+
resolve(pm.id);
|
|
12594
|
+
}
|
|
12595
|
+
} else if (step === "approval-timeout" && retryChoiceResolver.current) {
|
|
12596
|
+
if (input === "r") {
|
|
12597
|
+
const resolve = retryChoiceResolver.current;
|
|
12598
|
+
retryChoiceResolver.current = null;
|
|
12599
|
+
resolve("retry");
|
|
12600
|
+
} else if (input === "q") {
|
|
12601
|
+
const resolve = retryChoiceResolver.current;
|
|
12602
|
+
retryChoiceResolver.current = null;
|
|
12603
|
+
resolve("exit");
|
|
12604
|
+
}
|
|
12605
|
+
} else if (key.return) {
|
|
12606
|
+
if (enterResolver.current) {
|
|
12607
|
+
const resolve = enterResolver.current;
|
|
12608
|
+
enterResolver.current = null;
|
|
12609
|
+
resolve();
|
|
12610
|
+
} else if ((step === "await-approval" || step === "approval-timeout") && approvalUrl) {
|
|
12611
|
+
openUrl(approvalUrl);
|
|
12612
|
+
}
|
|
12613
|
+
}
|
|
12614
|
+
});
|
|
12615
|
+
const started = useRef2(false);
|
|
12616
|
+
useEffect5(() => {
|
|
12617
|
+
if (started.current) return;
|
|
12618
|
+
started.current = true;
|
|
12619
|
+
const run = async () => {
|
|
12620
|
+
try {
|
|
12621
|
+
await waitForEnter();
|
|
12622
|
+
let pmId = initialPaymentMethodId;
|
|
12623
|
+
if (!pmId) {
|
|
12624
|
+
setStep("fetch-pm");
|
|
12625
|
+
const methods = await paymentMethodsResource.listPaymentMethods();
|
|
12626
|
+
if (methods.length === 0) {
|
|
12627
|
+
throw new Error(
|
|
12628
|
+
"No payment methods found. Open the Link app (link.com) and add a card to your wallet, then run the demo again."
|
|
12629
|
+
);
|
|
12630
|
+
}
|
|
12631
|
+
if (methods.length === 1) {
|
|
12632
|
+
pmId = methods[0].id;
|
|
12633
|
+
} else {
|
|
12634
|
+
setPaymentMethods(methods);
|
|
12635
|
+
const defaultIdx = methods.findIndex((m) => m.is_default);
|
|
12636
|
+
setSelectedPmIndex(defaultIdx >= 0 ? defaultIdx : 0);
|
|
12637
|
+
setStep("pick-pm");
|
|
12638
|
+
pmId = await waitForPmSelection();
|
|
12639
|
+
}
|
|
12640
|
+
}
|
|
12641
|
+
setStep("probe");
|
|
12642
|
+
const probeResponse = await fetch(DEMO_CLIMATE_API_URL, {
|
|
12643
|
+
method: "POST",
|
|
12644
|
+
headers: { "Content-Type": "application/json" },
|
|
12645
|
+
body: JSON.stringify({ amount: DEMO_SPT_AMOUNT })
|
|
12646
|
+
});
|
|
12647
|
+
if (probeResponse.status !== 402) {
|
|
12648
|
+
throw new Error(
|
|
12649
|
+
`Expected 402 from ${DEMO_CLIMATE_API_URL}, got ${probeResponse.status}`
|
|
12650
|
+
);
|
|
12651
|
+
}
|
|
12652
|
+
const wwwAuth = probeResponse.headers.get("www-authenticate") ?? "";
|
|
12653
|
+
const decoded = decodeStripeChallenge(wwwAuth);
|
|
12654
|
+
setNetworkId(decoded.network_id);
|
|
12655
|
+
setChallengeData({
|
|
12656
|
+
status: probeResponse.status,
|
|
12657
|
+
method: decoded.method,
|
|
12658
|
+
intent: decoded.intent,
|
|
12659
|
+
network_id: decoded.network_id,
|
|
12660
|
+
realm: decoded.realm
|
|
12661
|
+
});
|
|
12662
|
+
setStep("explain-402");
|
|
12663
|
+
await waitForEnter();
|
|
12664
|
+
setStep("create-spend");
|
|
12665
|
+
const payload = {
|
|
12666
|
+
payment_details: pmId,
|
|
12667
|
+
credential_type: "shared_payment_token",
|
|
12668
|
+
network_id: decoded.network_id,
|
|
12669
|
+
amount: DEMO_SPT_AMOUNT,
|
|
12670
|
+
context: DEMO_SPT_CONTEXT,
|
|
12671
|
+
request_approval: true,
|
|
12672
|
+
test: true
|
|
12673
|
+
};
|
|
12674
|
+
setSpendRequestPayload(payload);
|
|
12675
|
+
const result = await spendRequestRepo2.createSpendRequest(payload);
|
|
12676
|
+
setSpendRequest(result);
|
|
12677
|
+
setStep("await-approval");
|
|
12678
|
+
for (; ; ) {
|
|
12679
|
+
try {
|
|
12680
|
+
const approved = await pollUntilApproved(
|
|
12681
|
+
spendRequestRepo2,
|
|
12682
|
+
result.id
|
|
12683
|
+
);
|
|
12684
|
+
setSpendRequest(approved);
|
|
12685
|
+
break;
|
|
12686
|
+
} catch (err) {
|
|
12687
|
+
if (err.message === "Approval polling timed out") {
|
|
12688
|
+
setStep("approval-timeout");
|
|
12689
|
+
const choice = await waitForRetryChoice();
|
|
12690
|
+
if (choice === "exit") {
|
|
12691
|
+
onComplete(false);
|
|
12692
|
+
return;
|
|
12693
|
+
}
|
|
12694
|
+
setStep("await-approval");
|
|
12695
|
+
} else {
|
|
12696
|
+
throw err;
|
|
12697
|
+
}
|
|
12698
|
+
}
|
|
12699
|
+
}
|
|
12700
|
+
setStep("mpp-pay-gate");
|
|
12701
|
+
await waitForEnter();
|
|
12702
|
+
setStep("mpp-pay");
|
|
12703
|
+
const payResponse = await runMppPay(
|
|
12704
|
+
DEMO_CLIMATE_API_URL,
|
|
12705
|
+
result.id,
|
|
12706
|
+
"POST",
|
|
12707
|
+
JSON.stringify({ amount: DEMO_SPT_AMOUNT }),
|
|
12708
|
+
void 0,
|
|
12709
|
+
spendRequestRepo2
|
|
12710
|
+
);
|
|
12711
|
+
setPayResult(payResponse);
|
|
12712
|
+
setStep("done");
|
|
12713
|
+
onComplete(true);
|
|
12714
|
+
} catch (err) {
|
|
12715
|
+
setError(err.message);
|
|
12716
|
+
setStep("error");
|
|
12717
|
+
onComplete(false);
|
|
12718
|
+
}
|
|
12719
|
+
};
|
|
12720
|
+
run();
|
|
12721
|
+
}, []);
|
|
12722
|
+
const pastStep = (target) => {
|
|
12723
|
+
const order = [
|
|
12724
|
+
"intro",
|
|
12725
|
+
"fetch-pm",
|
|
12726
|
+
"pick-pm",
|
|
12727
|
+
"probe",
|
|
12728
|
+
"explain-402",
|
|
12729
|
+
"create-spend",
|
|
12730
|
+
"await-approval",
|
|
12731
|
+
"mpp-pay-gate",
|
|
12732
|
+
"mpp-pay",
|
|
12733
|
+
"done"
|
|
12734
|
+
];
|
|
12735
|
+
return order.indexOf(step) > order.indexOf(target);
|
|
12736
|
+
};
|
|
12737
|
+
const prompt = (label = "Press [Enter] to continue") => /* @__PURE__ */ jsxs6(Text8, { dimColor: true, children: [
|
|
12738
|
+
"\n",
|
|
12739
|
+
">",
|
|
12740
|
+
" ",
|
|
12741
|
+
label
|
|
12742
|
+
] });
|
|
12743
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, children: [
|
|
12744
|
+
/* @__PURE__ */ jsx9(Text8, { bold: true, color: "cyan", children: SPT_FLOW.title }),
|
|
12745
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
12746
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 1, children: [
|
|
12747
|
+
/* @__PURE__ */ jsx9(Text8, { color: "yellow", children: "[testmode]" }),
|
|
12748
|
+
/* @__PURE__ */ jsxs6(Text8, { dimColor: true, children: [
|
|
12749
|
+
DEMO_CLIMATE_API_URL,
|
|
12750
|
+
" ",
|
|
12751
|
+
DEMO_MPP_DEV_URL
|
|
12752
|
+
] })
|
|
12753
|
+
] }),
|
|
12754
|
+
/* @__PURE__ */ jsx9(MarkdownText, { children: SPT_FLOW.intro.description }),
|
|
12755
|
+
/* @__PURE__ */ jsxs6(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
12756
|
+
/* @__PURE__ */ jsx9(Text8, { children: SPT_FLOW.intro.preamble }),
|
|
12757
|
+
SPT_FLOW.intro.steps.map((s, i) => /* @__PURE__ */ jsx9(MarkdownText, { children: ` ${i + 1}. ${s}` }, s))
|
|
12758
|
+
] }),
|
|
12759
|
+
step === "intro" && prompt(SPT_FLOW.intro.prompt)
|
|
12760
|
+
] }),
|
|
12761
|
+
step === "fetch-pm" && /* @__PURE__ */ jsx9(Box7, { marginY: 1, children: /* @__PURE__ */ jsx9(Text8, { color: "cyan", children: "Fetching payment methods..." }) }),
|
|
12762
|
+
step === "pick-pm" && paymentMethods.length > 1 && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
12763
|
+
/* @__PURE__ */ jsx9(Text8, { children: "Which payment method should we use for the demo?" }),
|
|
12764
|
+
/* @__PURE__ */ jsx9(Box7, { flexDirection: "column", marginTop: 1, children: paymentMethods.map((pm, i) => /* @__PURE__ */ jsx9(Text8, { children: i === selectedPmIndex ? /* @__PURE__ */ jsxs6(Text8, { color: "cyan", bold: true, children: [
|
|
12765
|
+
">",
|
|
12766
|
+
" ",
|
|
12767
|
+
pm.card_details ? `${pm.card_details.brand} ****${pm.card_details.last4}` : pm.type,
|
|
12768
|
+
pm.is_default ? " (default)" : ""
|
|
12769
|
+
] }) : /* @__PURE__ */ jsxs6(Text8, { dimColor: true, children: [
|
|
12770
|
+
" ",
|
|
12771
|
+
pm.card_details ? `${pm.card_details.brand} ****${pm.card_details.last4}` : pm.type,
|
|
12772
|
+
pm.is_default ? " (default)" : ""
|
|
12773
|
+
] }) }, pm.id)) }),
|
|
12774
|
+
/* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text8, { dimColor: true, children: "Use \u2191\u2193 to select, [Enter] to confirm" }) })
|
|
12775
|
+
] }),
|
|
12776
|
+
pastStep("fetch-pm") && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
12777
|
+
/* @__PURE__ */ jsx9(MarkdownText, { children: SPT_FLOW.probe.description }),
|
|
12778
|
+
step === "probe" && /* @__PURE__ */ jsx9(Box7, { marginY: 1, children: /* @__PURE__ */ jsx9(Text8, { color: "cyan", children: SPT_FLOW.probe.loading }) })
|
|
12779
|
+
] }),
|
|
12780
|
+
pastStep("probe") && networkId && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
12781
|
+
/* @__PURE__ */ jsxs6(Text8, { color: "green", children: [
|
|
12782
|
+
"\u2713 Got HTTP 402 with a ",
|
|
12783
|
+
/* @__PURE__ */ jsx9(Text8, { bold: true, children: "WWW-Authenticate" }),
|
|
12784
|
+
" challenge"
|
|
12785
|
+
] }),
|
|
12786
|
+
challengeData && /* @__PURE__ */ jsx9(StepData, { data: challengeData }),
|
|
12787
|
+
/* @__PURE__ */ jsx9(MarkdownText, { children: SPT_FLOW.probe.detail }),
|
|
12788
|
+
step === "explain-402" && prompt()
|
|
12789
|
+
] }),
|
|
12790
|
+
pastStep("explain-402") && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
12791
|
+
/* @__PURE__ */ jsx9(MarkdownText, { children: SPT_FLOW.createSpend.description }),
|
|
12792
|
+
spendRequestPayload && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", marginTop: 1, children: [
|
|
12793
|
+
/* @__PURE__ */ jsx9(Text8, { dimColor: true, children: "spend-request create" }),
|
|
12794
|
+
/* @__PURE__ */ jsx9(
|
|
12795
|
+
Box7,
|
|
12796
|
+
{
|
|
12797
|
+
flexDirection: "column",
|
|
12798
|
+
borderStyle: "single",
|
|
12799
|
+
borderColor: "gray",
|
|
12800
|
+
paddingX: 2,
|
|
12801
|
+
children: /* @__PURE__ */ jsx9(Text8, { children: JSON.stringify(
|
|
12802
|
+
spendRequestPayload,
|
|
12803
|
+
null,
|
|
12804
|
+
2
|
|
12805
|
+
) })
|
|
12806
|
+
}
|
|
12807
|
+
)
|
|
12808
|
+
] }),
|
|
12809
|
+
step === "create-spend" && /* @__PURE__ */ jsx9(Box7, { marginY: 1, children: /* @__PURE__ */ jsx9(Text8, { color: "cyan", children: SPT_FLOW.createSpend.loading }) })
|
|
12810
|
+
] }),
|
|
12811
|
+
(step === "await-approval" || step === "approval-timeout" || pastStep("await-approval")) && spendRequest && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
12812
|
+
/* @__PURE__ */ jsxs6(Text8, { color: "green", children: [
|
|
12813
|
+
"\u2713 Spend request created (ID: ",
|
|
12814
|
+
/* @__PURE__ */ jsx9(Text8, { bold: true, children: spendRequest.id }),
|
|
12815
|
+
")"
|
|
12816
|
+
] }),
|
|
12817
|
+
/* @__PURE__ */ jsx9(
|
|
12818
|
+
StepData,
|
|
12819
|
+
{
|
|
12820
|
+
data: {
|
|
12821
|
+
id: spendRequest.id,
|
|
12822
|
+
status: spendRequest.status,
|
|
12823
|
+
credential_type: spendRequest.credential_type,
|
|
12824
|
+
network_id: spendRequest.network_id,
|
|
12825
|
+
amount: spendRequest.amount,
|
|
12826
|
+
context: spendRequest.context,
|
|
12827
|
+
approval_url: spendRequest.approval_url
|
|
12828
|
+
}
|
|
12829
|
+
}
|
|
12830
|
+
)
|
|
12831
|
+
] }),
|
|
12832
|
+
step === "await-approval" && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
12833
|
+
/* @__PURE__ */ jsx9(Text8, { children: SPT_FLOW.approval.description }),
|
|
12834
|
+
/* @__PURE__ */ jsxs6(
|
|
12835
|
+
Box7,
|
|
12836
|
+
{
|
|
12837
|
+
flexDirection: "column",
|
|
12838
|
+
borderStyle: "round",
|
|
12839
|
+
borderColor: "cyan",
|
|
12840
|
+
paddingX: 2,
|
|
12841
|
+
paddingY: 1,
|
|
12842
|
+
marginTop: 1,
|
|
12843
|
+
children: [
|
|
12844
|
+
/* @__PURE__ */ jsxs6(Text8, { children: [
|
|
12845
|
+
"Approve at:",
|
|
12846
|
+
" ",
|
|
12847
|
+
/* @__PURE__ */ jsx9(Text8, { bold: true, color: "cyan", children: approvalUrl })
|
|
12848
|
+
] }),
|
|
12849
|
+
/* @__PURE__ */ jsx9(Text8, { dimColor: true, children: SPT_FLOW.approval.browserHint })
|
|
12850
|
+
]
|
|
12851
|
+
}
|
|
12852
|
+
),
|
|
12853
|
+
/* @__PURE__ */ jsx9(Box7, { marginY: 1, children: /* @__PURE__ */ jsx9(Text8, { color: "cyan", children: SPT_FLOW.approval.loading }) })
|
|
12854
|
+
] }),
|
|
12855
|
+
step === "approval-timeout" && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
12856
|
+
/* @__PURE__ */ jsx9(Text8, { color: "yellow", children: "\u26A0 Approval timed out (5 min). The spend request is still pending \u2014 you can still approve it." }),
|
|
12857
|
+
/* @__PURE__ */ jsxs6(
|
|
12858
|
+
Box7,
|
|
12859
|
+
{
|
|
12860
|
+
flexDirection: "column",
|
|
12861
|
+
borderStyle: "round",
|
|
12862
|
+
borderColor: "yellow",
|
|
12863
|
+
paddingX: 2,
|
|
12864
|
+
paddingY: 1,
|
|
12865
|
+
marginTop: 1,
|
|
12866
|
+
children: [
|
|
12867
|
+
/* @__PURE__ */ jsxs6(Text8, { children: [
|
|
12868
|
+
"Approve at:",
|
|
12869
|
+
" ",
|
|
12870
|
+
/* @__PURE__ */ jsx9(Text8, { bold: true, color: "cyan", children: approvalUrl })
|
|
12871
|
+
] }),
|
|
12872
|
+
/* @__PURE__ */ jsx9(Text8, { dimColor: true, children: "Press [Enter] to open in browser" })
|
|
12873
|
+
]
|
|
12874
|
+
}
|
|
12875
|
+
),
|
|
12876
|
+
/* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text8, { dimColor: true, children: "r Retry polling q Quit demo" }) })
|
|
12877
|
+
] }),
|
|
12878
|
+
pastStep("await-approval") && step !== "error" && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
12879
|
+
(step === "mpp-pay-gate" || pastStep("mpp-pay-gate")) && /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
12880
|
+
/* @__PURE__ */ jsx9(Text8, { color: "green", children: "\u2713 Approved!" }),
|
|
12881
|
+
/* @__PURE__ */ jsx9(MarkdownText, { children: SPT_FLOW.mppPay.description })
|
|
12882
|
+
] }),
|
|
12883
|
+
step === "mpp-pay-gate" && prompt(SPT_FLOW.mppPay.prompt),
|
|
12884
|
+
step === "mpp-pay" && /* @__PURE__ */ jsx9(Box7, { marginY: 1, children: /* @__PURE__ */ jsx9(Text8, { color: "cyan", children: SPT_FLOW.mppPay.loading }) })
|
|
12885
|
+
] }),
|
|
12886
|
+
step === "done" && payResult && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
12887
|
+
/* @__PURE__ */ jsxs6(
|
|
12888
|
+
Box7,
|
|
12889
|
+
{
|
|
12890
|
+
flexDirection: "column",
|
|
12891
|
+
borderStyle: "round",
|
|
12892
|
+
borderColor: "green",
|
|
12893
|
+
paddingX: 2,
|
|
12894
|
+
paddingY: 1,
|
|
12895
|
+
children: [
|
|
12896
|
+
/* @__PURE__ */ jsx9(Text8, { bold: true, color: "green", children: SPT_FLOW.done.success }),
|
|
12897
|
+
/* @__PURE__ */ jsxs6(Text8, { children: [
|
|
12898
|
+
"Status: ",
|
|
12899
|
+
/* @__PURE__ */ jsxs6(Text8, { bold: true, children: [
|
|
12900
|
+
"HTTP ",
|
|
12901
|
+
payResult.status
|
|
12902
|
+
] })
|
|
12903
|
+
] }),
|
|
12904
|
+
payResult.body && (() => {
|
|
12905
|
+
const body = payResult.body;
|
|
12906
|
+
try {
|
|
12907
|
+
return /* @__PURE__ */ jsx9(Text8, { children: JSON.stringify(JSON.parse(body), null, 2) });
|
|
12908
|
+
} catch {
|
|
12909
|
+
return /* @__PURE__ */ jsx9(Text8, { children: body });
|
|
12910
|
+
}
|
|
12911
|
+
})()
|
|
12912
|
+
]
|
|
12913
|
+
}
|
|
12914
|
+
),
|
|
12915
|
+
/* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx9(MarkdownText, { children: SPT_FLOW.done.detail }) })
|
|
12916
|
+
] }),
|
|
12917
|
+
step === "error" && /* @__PURE__ */ jsxs6(Text8, { color: "red", children: [
|
|
12918
|
+
"Error: ",
|
|
12919
|
+
error
|
|
12920
|
+
] })
|
|
12921
|
+
] });
|
|
12922
|
+
};
|
|
12923
|
+
|
|
12924
|
+
// src/commands/demo/demo-runner.tsx
|
|
12925
|
+
import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
12926
|
+
var DemoRunner = ({
|
|
12927
|
+
authRepo: authRepo2,
|
|
12928
|
+
spendRequestRepo: spendRequestRepo2,
|
|
12929
|
+
paymentMethodsResource,
|
|
12930
|
+
paymentMethodId: preselectedPmId,
|
|
12931
|
+
onlyCard,
|
|
12932
|
+
onlySpt,
|
|
12933
|
+
onComplete
|
|
12934
|
+
}) => {
|
|
12935
|
+
const preselected = onlyCard ? "card" : onlySpt ? "spt" : null;
|
|
12936
|
+
const [choice, setChoice] = useState6(preselected);
|
|
12937
|
+
const [menuIndex, setMenuIndex] = useState6(0);
|
|
12938
|
+
const postAuthPhase = preselected === "spt" ? "spt-flow" : preselected ? "card-flow" : "menu";
|
|
12939
|
+
const [phase, setPhase] = useState6(
|
|
12940
|
+
storage.isAuthenticated() ? postAuthPhase : "auth"
|
|
12941
|
+
);
|
|
12942
|
+
const [paymentMethodId, setPaymentMethodId] = useState6(
|
|
12943
|
+
preselectedPmId ?? ""
|
|
12944
|
+
);
|
|
12945
|
+
const [cardSuccess, setCardSuccess] = useState6(null);
|
|
12946
|
+
const [sptSuccess, setSptSuccess] = useState6(null);
|
|
12947
|
+
const runCard = choice === "card" || choice === "both";
|
|
12948
|
+
const runSpt = choice === "spt" || choice === "both";
|
|
12949
|
+
useInput4((_input, key) => {
|
|
12950
|
+
if (phase === "menu") {
|
|
12951
|
+
if (key.upArrow) {
|
|
12952
|
+
setMenuIndex((i) => i > 0 ? i - 1 : DEMO_MENU.options.length - 1);
|
|
12953
|
+
} else if (key.downArrow) {
|
|
12954
|
+
setMenuIndex((i) => i < DEMO_MENU.options.length - 1 ? i + 1 : 0);
|
|
12955
|
+
} else if (key.return) {
|
|
12956
|
+
const selected = DEMO_MENU.options[menuIndex].key;
|
|
12957
|
+
setChoice(selected);
|
|
12958
|
+
setPhase(selected === "spt" ? "spt-flow" : "card-flow");
|
|
12959
|
+
}
|
|
12960
|
+
} else if (phase === "card-done" && key.return) {
|
|
12961
|
+
setPhase("spt-flow");
|
|
12962
|
+
}
|
|
12963
|
+
});
|
|
12964
|
+
const onCardComplete = useCallback(
|
|
12965
|
+
(result) => {
|
|
12966
|
+
setPaymentMethodId(result.paymentMethodId);
|
|
12967
|
+
setCardSuccess(result.success);
|
|
12968
|
+
if (!runSpt) {
|
|
12969
|
+
setPhase("summary");
|
|
12970
|
+
setTimeout(onComplete, 1500);
|
|
12971
|
+
} else {
|
|
12972
|
+
setPhase("card-done");
|
|
12973
|
+
}
|
|
12974
|
+
},
|
|
12975
|
+
[runSpt, onComplete]
|
|
12976
|
+
);
|
|
12977
|
+
const onSptComplete = useCallback(
|
|
12978
|
+
(success) => {
|
|
12979
|
+
setSptSuccess(success);
|
|
12980
|
+
setPhase("summary");
|
|
12981
|
+
setTimeout(onComplete, 1500);
|
|
12982
|
+
},
|
|
12983
|
+
[onComplete]
|
|
12984
|
+
);
|
|
12985
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 1, children: [
|
|
12986
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
12987
|
+
/* @__PURE__ */ jsx10(Text9, { bold: true, children: DEMO_MENU.title }),
|
|
12988
|
+
/* @__PURE__ */ jsx10(Text9, { children: DEMO_MENU.subtitle })
|
|
12989
|
+
] }),
|
|
12990
|
+
phase === "auth" && /* @__PURE__ */ jsx10(
|
|
12991
|
+
Login,
|
|
12992
|
+
{
|
|
12993
|
+
authResource: authRepo2,
|
|
12994
|
+
clientName: ONBOARD.auth.clientName,
|
|
12995
|
+
onComplete: () => setPhase(postAuthPhase)
|
|
12996
|
+
}
|
|
12997
|
+
),
|
|
12998
|
+
phase === "menu" && /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
12999
|
+
/* @__PURE__ */ jsx10(Text9, { children: DEMO_MENU.question }),
|
|
13000
|
+
/* @__PURE__ */ jsx10(Box8, { flexDirection: "column", marginTop: 1, gap: 1, children: DEMO_MENU.options.map((opt, i) => /* @__PURE__ */ jsx10(Box8, { flexDirection: "column", children: i === menuIndex ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
13001
|
+
/* @__PURE__ */ jsxs7(Text9, { color: "cyan", bold: true, children: [
|
|
13002
|
+
">",
|
|
13003
|
+
" ",
|
|
13004
|
+
opt.label
|
|
13005
|
+
] }),
|
|
13006
|
+
/* @__PURE__ */ jsxs7(Text9, { color: "cyan", children: [
|
|
13007
|
+
" ",
|
|
13008
|
+
opt.description
|
|
13009
|
+
] })
|
|
13010
|
+
] }) : /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
|
|
13011
|
+
" ",
|
|
13012
|
+
opt.label
|
|
13013
|
+
] }) }, opt.key)) }),
|
|
13014
|
+
/* @__PURE__ */ jsx10(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: DEMO_MENU.hint }) })
|
|
13015
|
+
] }),
|
|
13016
|
+
runCard && phase !== "menu" && /* @__PURE__ */ jsx10(
|
|
13017
|
+
CardFlow,
|
|
13018
|
+
{
|
|
13019
|
+
spendRequestRepo: spendRequestRepo2,
|
|
13020
|
+
paymentMethodsResource,
|
|
13021
|
+
paymentMethodId: preselectedPmId,
|
|
13022
|
+
onComplete: onCardComplete
|
|
13023
|
+
}
|
|
13024
|
+
),
|
|
13025
|
+
phase === "card-done" && /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
13026
|
+
/* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u2500\u2500\u2500" }),
|
|
13027
|
+
/* @__PURE__ */ jsx10(MarkdownText, { children: DEMO_MENU.transition }),
|
|
13028
|
+
/* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
|
|
13029
|
+
"\n",
|
|
13030
|
+
">",
|
|
13031
|
+
" ",
|
|
13032
|
+
DEMO_MENU.transitionPrompt
|
|
13033
|
+
] })
|
|
13034
|
+
] }),
|
|
13035
|
+
runSpt && (phase === "spt-flow" || phase === "summary") && /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
13036
|
+
runCard && /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u2500\u2500\u2500" }),
|
|
13037
|
+
/* @__PURE__ */ jsx10(
|
|
13038
|
+
SptFlow,
|
|
13039
|
+
{
|
|
13040
|
+
spendRequestRepo: spendRequestRepo2,
|
|
13041
|
+
paymentMethodsResource,
|
|
13042
|
+
paymentMethodId: paymentMethodId || void 0,
|
|
13043
|
+
onComplete: onSptComplete
|
|
13044
|
+
}
|
|
13045
|
+
)
|
|
13046
|
+
] }),
|
|
13047
|
+
phase === "summary" && /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
13048
|
+
/* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u2500\u2500\u2500" }),
|
|
13049
|
+
/* @__PURE__ */ jsx10(Text9, { bold: true, children: "Done!" }),
|
|
13050
|
+
cardSuccess !== null && /* @__PURE__ */ jsxs7(Text9, { color: cardSuccess ? "green" : "red", children: [
|
|
13051
|
+
cardSuccess ? "\u2713" : "\u2717",
|
|
13052
|
+
" Virtual card flow"
|
|
13053
|
+
] }),
|
|
13054
|
+
sptSuccess !== null && /* @__PURE__ */ jsxs7(Text9, { color: sptSuccess ? "green" : "red", children: [
|
|
13055
|
+
sptSuccess ? "\u2713" : "\u2717",
|
|
13056
|
+
" Machine payment flow"
|
|
13057
|
+
] }),
|
|
13058
|
+
/* @__PURE__ */ jsx10(AppDownloadQrCodes, {})
|
|
13059
|
+
] })
|
|
13060
|
+
] });
|
|
13061
|
+
};
|
|
13062
|
+
|
|
13063
|
+
// src/commands/demo/index.tsx
|
|
13064
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
13065
|
+
var demoOptions = z2.object({
|
|
13066
|
+
onlyCard: z2.boolean().default(false).describe("Run only the virtual card flow"),
|
|
13067
|
+
onlySpt: z2.boolean().default(false).describe("Run only the machine payment (SPT) flow")
|
|
13068
|
+
});
|
|
13069
|
+
function createDemoCli(authRepo2, spendRequestRepo2, createPaymentMethodsResource) {
|
|
13070
|
+
return Cli2.create("demo", {
|
|
13071
|
+
description: "Run an interactive demo of both Link payment flows (virtual card + machine payment)",
|
|
13072
|
+
options: demoOptions,
|
|
13073
|
+
outputPolicy: "agent-only",
|
|
13074
|
+
async run(c) {
|
|
13075
|
+
if (c.agent || c.formatExplicit) {
|
|
13076
|
+
return c.error({
|
|
13077
|
+
code: "REQUIRES_TTY",
|
|
13078
|
+
message: "The demo command requires an interactive terminal."
|
|
13079
|
+
});
|
|
13080
|
+
}
|
|
13081
|
+
const paymentMethodsResource = createPaymentMethodsResource();
|
|
13082
|
+
return new Promise((resolve) => {
|
|
13083
|
+
const { waitUntilExit, unmount } = render2(
|
|
13084
|
+
/* @__PURE__ */ jsx11(
|
|
13085
|
+
DemoRunner,
|
|
13086
|
+
{
|
|
13087
|
+
authRepo: authRepo2,
|
|
13088
|
+
spendRequestRepo: spendRequestRepo2,
|
|
13089
|
+
paymentMethodsResource,
|
|
13090
|
+
onlyCard: c.options.onlyCard,
|
|
13091
|
+
onlySpt: c.options.onlySpt,
|
|
13092
|
+
onComplete: () => unmount()
|
|
13093
|
+
}
|
|
13094
|
+
)
|
|
13095
|
+
);
|
|
13096
|
+
waitUntilExit().then(() => resolve({}));
|
|
13097
|
+
});
|
|
13098
|
+
}
|
|
13099
|
+
});
|
|
13100
|
+
}
|
|
13101
|
+
|
|
13102
|
+
// src/commands/mpp/index.tsx
|
|
13103
|
+
import { Cli as Cli3, z as z4 } from "incur";
|
|
13104
|
+
import { render as render3 } from "ink";
|
|
13105
|
+
|
|
13106
|
+
// src/commands/mpp/decode-view.tsx
|
|
13107
|
+
import { Box as Box9, Text as Text10 } from "ink";
|
|
13108
|
+
import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
13109
|
+
function DecodeChallengeView({
|
|
13110
|
+
decoded
|
|
13111
|
+
}) {
|
|
13112
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", children: [
|
|
13113
|
+
/* @__PURE__ */ jsx12(Text10, { color: "green", children: "\u2713 Stripe challenge decoded" }),
|
|
13114
|
+
/* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
13115
|
+
/* @__PURE__ */ jsxs8(Text10, { children: [
|
|
13116
|
+
"ID: ",
|
|
13117
|
+
/* @__PURE__ */ jsx12(Text10, { bold: true, children: decoded.id })
|
|
13118
|
+
] }),
|
|
13119
|
+
/* @__PURE__ */ jsxs8(Text10, { children: [
|
|
13120
|
+
"Realm: ",
|
|
13121
|
+
/* @__PURE__ */ jsx12(Text10, { bold: true, children: decoded.realm })
|
|
13122
|
+
] }),
|
|
13123
|
+
/* @__PURE__ */ jsxs8(Text10, { children: [
|
|
13124
|
+
"Network ID: ",
|
|
13125
|
+
/* @__PURE__ */ jsx12(Text10, { bold: true, children: decoded.network_id })
|
|
13126
|
+
] }),
|
|
13127
|
+
/* @__PURE__ */ jsx12(Text10, { children: "Request JSON:" }),
|
|
13128
|
+
/* @__PURE__ */ jsx12(Text10, { children: JSON.stringify(decoded.request_json, null, 2) })
|
|
11894
13129
|
] })
|
|
11895
13130
|
] });
|
|
11896
13131
|
}
|
|
11897
13132
|
|
|
11898
13133
|
// src/commands/mpp/schema.ts
|
|
11899
|
-
import { z as
|
|
11900
|
-
var payOptions =
|
|
11901
|
-
spendRequestId:
|
|
13134
|
+
import { z as z3 } from "incur";
|
|
13135
|
+
var payOptions = z3.object({
|
|
13136
|
+
spendRequestId: z3.string().describe(
|
|
11902
13137
|
'Approved spend request ID with credential_type "shared_payment_token"'
|
|
11903
13138
|
),
|
|
11904
|
-
method:
|
|
11905
|
-
data:
|
|
11906
|
-
header:
|
|
13139
|
+
method: z3.string().optional().describe("HTTP method (default: GET, or POST if --data is provided)"),
|
|
13140
|
+
data: z3.string().optional().describe("Request body (implies POST if --method is not set)"),
|
|
13141
|
+
header: z3.array(z3.string()).default([]).describe('Request header in "Name: Value" format (repeatable)')
|
|
11907
13142
|
});
|
|
11908
|
-
var decodeOptions =
|
|
11909
|
-
challenge:
|
|
13143
|
+
var decodeOptions = z3.object({
|
|
13144
|
+
challenge: z3.string().describe(
|
|
11910
13145
|
"Raw WWW-Authenticate header value; may include multiple payment challenges"
|
|
11911
13146
|
)
|
|
11912
13147
|
});
|
|
11913
13148
|
|
|
11914
13149
|
// src/commands/mpp/index.tsx
|
|
11915
|
-
import { jsx as
|
|
13150
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
11916
13151
|
function createMppCli(repository) {
|
|
11917
|
-
const cli2 =
|
|
13152
|
+
const cli2 = Cli3.create("mpp", {
|
|
11918
13153
|
description: "Machine payment protocol (MPP) commands"
|
|
11919
13154
|
});
|
|
11920
13155
|
cli2.command("pay", {
|
|
11921
13156
|
description: "Complete a machine payment protocol (MPP) payment using an approved spend request",
|
|
11922
|
-
args:
|
|
11923
|
-
url:
|
|
13157
|
+
args: z4.object({
|
|
13158
|
+
url: z4.string().describe("URL to pay")
|
|
11924
13159
|
}),
|
|
11925
13160
|
options: payOptions,
|
|
11926
13161
|
alias: { method: "X", data: "d", header: "H" },
|
|
@@ -11944,8 +13179,8 @@ function createMppCli(repository) {
|
|
|
11944
13179
|
const headers = opts.header?.length ? opts.header : void 0;
|
|
11945
13180
|
if (!c.agent && !c.formatExplicit) {
|
|
11946
13181
|
return new Promise((resolve) => {
|
|
11947
|
-
const { waitUntilExit } =
|
|
11948
|
-
/* @__PURE__ */
|
|
13182
|
+
const { waitUntilExit } = render3(
|
|
13183
|
+
/* @__PURE__ */ jsx13(
|
|
11949
13184
|
MppPay,
|
|
11950
13185
|
{
|
|
11951
13186
|
url,
|
|
@@ -11991,8 +13226,8 @@ function createMppCli(repository) {
|
|
|
11991
13226
|
const decoded = decodeStripeChallenge(c.options.challenge);
|
|
11992
13227
|
if (!c.agent && !c.formatExplicit) {
|
|
11993
13228
|
return new Promise((resolve) => {
|
|
11994
|
-
const { waitUntilExit } =
|
|
11995
|
-
/* @__PURE__ */
|
|
13229
|
+
const { waitUntilExit } = render3(
|
|
13230
|
+
/* @__PURE__ */ jsx13(DecodeChallengeView, { decoded })
|
|
11996
13231
|
);
|
|
11997
13232
|
waitUntilExit().then(() => resolve(decoded));
|
|
11998
13233
|
});
|
|
@@ -12003,26 +13238,183 @@ function createMppCli(repository) {
|
|
|
12003
13238
|
return cli2;
|
|
12004
13239
|
}
|
|
12005
13240
|
|
|
13241
|
+
// src/commands/onboard/index.tsx
|
|
13242
|
+
import { Cli as Cli4 } from "incur";
|
|
13243
|
+
import { render as render4 } from "ink";
|
|
13244
|
+
|
|
13245
|
+
// src/commands/onboard/onboard-runner.tsx
|
|
13246
|
+
import { Box as Box10, Text as Text11, useInput as useInput5 } from "ink";
|
|
13247
|
+
import { useEffect as useEffect6, useRef as useRef3, useState as useState7 } from "react";
|
|
13248
|
+
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
13249
|
+
var OnboardRunner = ({
|
|
13250
|
+
authRepo: authRepo2,
|
|
13251
|
+
spendRequestRepo: spendRequestRepo2,
|
|
13252
|
+
paymentMethodsResource,
|
|
13253
|
+
onComplete
|
|
13254
|
+
}) => {
|
|
13255
|
+
const [phase, setPhase] = useState7("welcome");
|
|
13256
|
+
const [authSkipped, setAuthSkipped] = useState7(false);
|
|
13257
|
+
const [pmMissing, setPmMissing] = useState7(false);
|
|
13258
|
+
const [error, setError] = useState7("");
|
|
13259
|
+
const enterResolver = useRef3(null);
|
|
13260
|
+
function waitForEnter() {
|
|
13261
|
+
return new Promise((resolve) => {
|
|
13262
|
+
enterResolver.current = resolve;
|
|
13263
|
+
});
|
|
13264
|
+
}
|
|
13265
|
+
const authResolver = useRef3(null);
|
|
13266
|
+
function waitForAuth() {
|
|
13267
|
+
return new Promise((resolve) => {
|
|
13268
|
+
authResolver.current = resolve;
|
|
13269
|
+
});
|
|
13270
|
+
}
|
|
13271
|
+
useInput5((_input, key) => {
|
|
13272
|
+
if (key.return && enterResolver.current) {
|
|
13273
|
+
const resolve = enterResolver.current;
|
|
13274
|
+
enterResolver.current = null;
|
|
13275
|
+
resolve();
|
|
13276
|
+
}
|
|
13277
|
+
});
|
|
13278
|
+
const started = useRef3(false);
|
|
13279
|
+
useEffect6(() => {
|
|
13280
|
+
if (started.current) return;
|
|
13281
|
+
started.current = true;
|
|
13282
|
+
const run = async () => {
|
|
13283
|
+
try {
|
|
13284
|
+
setPhase("auth");
|
|
13285
|
+
if (storage.isAuthenticated()) {
|
|
13286
|
+
setAuthSkipped(true);
|
|
13287
|
+
} else {
|
|
13288
|
+
setAuthSkipped(false);
|
|
13289
|
+
await waitForAuth();
|
|
13290
|
+
}
|
|
13291
|
+
setPhase("payment-methods");
|
|
13292
|
+
while (true) {
|
|
13293
|
+
const methods = await paymentMethodsResource.listPaymentMethods();
|
|
13294
|
+
if (methods.length > 0) break;
|
|
13295
|
+
setPmMissing(true);
|
|
13296
|
+
await waitForEnter();
|
|
13297
|
+
setPmMissing(false);
|
|
13298
|
+
}
|
|
13299
|
+
setPhase("demo");
|
|
13300
|
+
} catch (err) {
|
|
13301
|
+
setError(err.message);
|
|
13302
|
+
}
|
|
13303
|
+
};
|
|
13304
|
+
run();
|
|
13305
|
+
}, []);
|
|
13306
|
+
const pastPhase = (target) => {
|
|
13307
|
+
const order = ["welcome", "auth", "payment-methods", "demo"];
|
|
13308
|
+
return order.indexOf(phase) > order.indexOf(target);
|
|
13309
|
+
};
|
|
13310
|
+
const prompt = (label = "Press [Enter] to continue") => /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
|
|
13311
|
+
"\n",
|
|
13312
|
+
">",
|
|
13313
|
+
" ",
|
|
13314
|
+
label
|
|
13315
|
+
] });
|
|
13316
|
+
return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", gap: 1, children: [
|
|
13317
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
|
|
13318
|
+
/* @__PURE__ */ jsx14(Text11, { bold: true, children: ONBOARD.title }),
|
|
13319
|
+
/* @__PURE__ */ jsx14(Text11, { children: ONBOARD.subtitle })
|
|
13320
|
+
] }),
|
|
13321
|
+
/* @__PURE__ */ jsx14(Box10, { flexDirection: "column", children: authSkipped || pastPhase("auth") ? /* @__PURE__ */ jsxs9(Text11, { color: "green", children: [
|
|
13322
|
+
"\u2713 ",
|
|
13323
|
+
authSkipped ? ONBOARD.auth.alreadyLoggedIn : ONBOARD.auth.authenticated
|
|
13324
|
+
] }) : phase === "auth" && !storage.isAuthenticated() ? /* @__PURE__ */ jsx14(
|
|
13325
|
+
Login,
|
|
13326
|
+
{
|
|
13327
|
+
authResource: authRepo2,
|
|
13328
|
+
clientName: ONBOARD.auth.clientName,
|
|
13329
|
+
onComplete: () => authResolver.current?.()
|
|
13330
|
+
}
|
|
13331
|
+
) : null }),
|
|
13332
|
+
pastPhase("auth") && /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
|
|
13333
|
+
phase === "payment-methods" && !pmMissing && /* @__PURE__ */ jsx14(Text11, { color: "cyan", children: ONBOARD.paymentMethods.loading }),
|
|
13334
|
+
pmMissing && /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
|
|
13335
|
+
/* @__PURE__ */ jsx14(Text11, { color: "yellow", children: ONBOARD.paymentMethods.missing }),
|
|
13336
|
+
/* @__PURE__ */ jsx14(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text11, { children: [
|
|
13337
|
+
"Visit",
|
|
13338
|
+
" ",
|
|
13339
|
+
/* @__PURE__ */ jsx14(Text11, { bold: true, color: "cyan", children: "app.link.com/wallet" }),
|
|
13340
|
+
" ",
|
|
13341
|
+
"to add a payment method, then press [Enter] to continue."
|
|
13342
|
+
] }) }),
|
|
13343
|
+
prompt(ONBOARD.paymentMethods.retryPrompt)
|
|
13344
|
+
] }),
|
|
13345
|
+
pastPhase("payment-methods") && /* @__PURE__ */ jsx14(Text11, { color: "green", children: "\u2713 Payment method found" })
|
|
13346
|
+
] }),
|
|
13347
|
+
phase === "demo" && /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
|
|
13348
|
+
/* @__PURE__ */ jsx14(Text11, { dimColor: true, children: "\u2500\u2500\u2500" }),
|
|
13349
|
+
/* @__PURE__ */ jsx14(
|
|
13350
|
+
DemoRunner,
|
|
13351
|
+
{
|
|
13352
|
+
authRepo: authRepo2,
|
|
13353
|
+
spendRequestRepo: spendRequestRepo2,
|
|
13354
|
+
paymentMethodsResource,
|
|
13355
|
+
onComplete
|
|
13356
|
+
}
|
|
13357
|
+
)
|
|
13358
|
+
] }),
|
|
13359
|
+
error && /* @__PURE__ */ jsxs9(Text11, { color: "red", children: [
|
|
13360
|
+
"Error: ",
|
|
13361
|
+
error
|
|
13362
|
+
] })
|
|
13363
|
+
] });
|
|
13364
|
+
};
|
|
13365
|
+
|
|
13366
|
+
// src/commands/onboard/index.tsx
|
|
13367
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
13368
|
+
function createOnboardCli(authRepo2, spendRequestRepo2, createPaymentMethodsResource) {
|
|
13369
|
+
return Cli4.create("onboard", {
|
|
13370
|
+
description: "Guided setup: authenticate, verify payment methods, and demo both payment flows",
|
|
13371
|
+
outputPolicy: "agent-only",
|
|
13372
|
+
async run(c) {
|
|
13373
|
+
if (c.agent || c.formatExplicit) {
|
|
13374
|
+
return c.error({
|
|
13375
|
+
code: "REQUIRES_TTY",
|
|
13376
|
+
message: "The onboard command requires an interactive terminal."
|
|
13377
|
+
});
|
|
13378
|
+
}
|
|
13379
|
+
const paymentMethodsResource = createPaymentMethodsResource();
|
|
13380
|
+
return new Promise((resolve) => {
|
|
13381
|
+
const { waitUntilExit, unmount } = render4(
|
|
13382
|
+
/* @__PURE__ */ jsx15(
|
|
13383
|
+
OnboardRunner,
|
|
13384
|
+
{
|
|
13385
|
+
authRepo: authRepo2,
|
|
13386
|
+
spendRequestRepo: spendRequestRepo2,
|
|
13387
|
+
paymentMethodsResource,
|
|
13388
|
+
onComplete: () => unmount()
|
|
13389
|
+
}
|
|
13390
|
+
)
|
|
13391
|
+
);
|
|
13392
|
+
waitUntilExit().then(() => resolve({}));
|
|
13393
|
+
});
|
|
13394
|
+
}
|
|
13395
|
+
});
|
|
13396
|
+
}
|
|
13397
|
+
|
|
12006
13398
|
// src/commands/payment-methods/index.tsx
|
|
12007
|
-
import { Cli as
|
|
12008
|
-
import { render as
|
|
13399
|
+
import { Cli as Cli5 } from "incur";
|
|
13400
|
+
import { render as render5 } from "ink";
|
|
12009
13401
|
|
|
12010
13402
|
// src/commands/payment-methods/add.tsx
|
|
12011
|
-
import { Box as
|
|
12012
|
-
import { jsx as
|
|
13403
|
+
import { Box as Box11, Text as Text12, useApp, useInput as useInput6 } from "ink";
|
|
13404
|
+
import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
12013
13405
|
var WALLET_URL = "https://app.link.com/wallet";
|
|
12014
13406
|
var AddPaymentMethod = () => {
|
|
12015
13407
|
const { exit } = useApp();
|
|
12016
|
-
|
|
13408
|
+
useInput6((_input, key) => {
|
|
12017
13409
|
if (key.return) {
|
|
12018
13410
|
openUrl(WALLET_URL);
|
|
12019
13411
|
exit();
|
|
12020
13412
|
}
|
|
12021
13413
|
});
|
|
12022
|
-
return /* @__PURE__ */
|
|
12023
|
-
/* @__PURE__ */
|
|
12024
|
-
/* @__PURE__ */
|
|
12025
|
-
|
|
13414
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingY: 1, children: [
|
|
13415
|
+
/* @__PURE__ */ jsx16(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text12, { bold: true, children: "Add Payment Method" }) }),
|
|
13416
|
+
/* @__PURE__ */ jsxs10(
|
|
13417
|
+
Box11,
|
|
12026
13418
|
{
|
|
12027
13419
|
flexDirection: "column",
|
|
12028
13420
|
borderStyle: "round",
|
|
@@ -12030,12 +13422,12 @@ var AddPaymentMethod = () => {
|
|
|
12030
13422
|
paddingX: 2,
|
|
12031
13423
|
paddingY: 1,
|
|
12032
13424
|
children: [
|
|
12033
|
-
/* @__PURE__ */
|
|
13425
|
+
/* @__PURE__ */ jsxs10(Text12, { children: [
|
|
12034
13426
|
"Open:",
|
|
12035
13427
|
" ",
|
|
12036
|
-
/* @__PURE__ */
|
|
13428
|
+
/* @__PURE__ */ jsx16(Text12, { bold: true, color: "cyan", children: WALLET_URL })
|
|
12037
13429
|
] }),
|
|
12038
|
-
/* @__PURE__ */
|
|
13430
|
+
/* @__PURE__ */ jsx16(Text12, { dimColor: true, children: "Press Enter to open in browser" })
|
|
12039
13431
|
]
|
|
12040
13432
|
}
|
|
12041
13433
|
)
|
|
@@ -12043,20 +13435,20 @@ var AddPaymentMethod = () => {
|
|
|
12043
13435
|
};
|
|
12044
13436
|
|
|
12045
13437
|
// src/commands/payment-methods/list.tsx
|
|
12046
|
-
import { Box as
|
|
13438
|
+
import { Box as Box12, Text as Text13 } from "ink";
|
|
12047
13439
|
import Spinner3 from "ink-spinner";
|
|
12048
|
-
import { useEffect as
|
|
12049
|
-
import { jsx as
|
|
13440
|
+
import { useEffect as useEffect7, useState as useState8 } from "react";
|
|
13441
|
+
import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
12050
13442
|
var PaymentMethodsList = ({
|
|
12051
13443
|
resource,
|
|
12052
13444
|
onComplete
|
|
12053
13445
|
}) => {
|
|
12054
|
-
const [status, setStatus] =
|
|
13446
|
+
const [status, setStatus] = useState8(
|
|
12055
13447
|
"loading"
|
|
12056
13448
|
);
|
|
12057
|
-
const [methods, setMethods] =
|
|
12058
|
-
const [error, setError] =
|
|
12059
|
-
|
|
13449
|
+
const [methods, setMethods] = useState8([]);
|
|
13450
|
+
const [error, setError] = useState8("");
|
|
13451
|
+
useEffect7(() => {
|
|
12060
13452
|
const fetch2 = async () => {
|
|
12061
13453
|
try {
|
|
12062
13454
|
const result = await resource.listPaymentMethods();
|
|
@@ -12072,43 +13464,43 @@ var PaymentMethodsList = ({
|
|
|
12072
13464
|
fetch2();
|
|
12073
13465
|
}, [resource, onComplete]);
|
|
12074
13466
|
if (status === "loading") {
|
|
12075
|
-
return /* @__PURE__ */
|
|
12076
|
-
/* @__PURE__ */
|
|
13467
|
+
return /* @__PURE__ */ jsx17(Box12, { children: /* @__PURE__ */ jsxs11(Text13, { color: "cyan", children: [
|
|
13468
|
+
/* @__PURE__ */ jsx17(Spinner3, { type: "dots" }),
|
|
12077
13469
|
" Loading payment methods..."
|
|
12078
13470
|
] }) });
|
|
12079
13471
|
}
|
|
12080
13472
|
if (status === "error") {
|
|
12081
|
-
return /* @__PURE__ */
|
|
12082
|
-
/* @__PURE__ */
|
|
12083
|
-
/* @__PURE__ */
|
|
13473
|
+
return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
|
|
13474
|
+
/* @__PURE__ */ jsx17(Text13, { color: "red", children: "\u2717 Failed to load payment methods" }),
|
|
13475
|
+
/* @__PURE__ */ jsx17(Text13, { color: "red", children: error })
|
|
12084
13476
|
] });
|
|
12085
13477
|
}
|
|
12086
13478
|
if (methods.length === 0) {
|
|
12087
|
-
return /* @__PURE__ */
|
|
13479
|
+
return /* @__PURE__ */ jsx17(Box12, { children: /* @__PURE__ */ jsx17(Text13, { dimColor: true, children: "No payment methods found" }) });
|
|
12088
13480
|
}
|
|
12089
|
-
return /* @__PURE__ */
|
|
12090
|
-
/* @__PURE__ */
|
|
12091
|
-
/* @__PURE__ */
|
|
13481
|
+
return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
|
|
13482
|
+
/* @__PURE__ */ jsx17(Text13, { bold: true, children: "Payment Methods" }),
|
|
13483
|
+
/* @__PURE__ */ jsx17(Box12, { flexDirection: "column", marginTop: 1, children: methods.map((pm) => {
|
|
12092
13484
|
const label = pm.card_details?.brand ?? pm.bank_account_details?.bank_name ?? "Bank account";
|
|
12093
13485
|
const last4 = pm.card_details?.last4 ?? pm.bank_account_details?.last4;
|
|
12094
13486
|
const suffix = [pm.nickname ? `(${pm.nickname})` : ""].filter(Boolean).join(" ");
|
|
12095
|
-
return /* @__PURE__ */
|
|
12096
|
-
/* @__PURE__ */
|
|
13487
|
+
return /* @__PURE__ */ jsx17(Box12, { paddingX: 2, children: /* @__PURE__ */ jsxs11(Text13, { children: [
|
|
13488
|
+
/* @__PURE__ */ jsx17(Text13, { dimColor: true, children: pm.id }),
|
|
12097
13489
|
" ",
|
|
12098
13490
|
label,
|
|
12099
13491
|
" ****",
|
|
12100
13492
|
last4,
|
|
12101
13493
|
suffix ? ` ${suffix}` : "",
|
|
12102
|
-
pm.is_default ? /* @__PURE__ */
|
|
13494
|
+
pm.is_default ? /* @__PURE__ */ jsx17(Text13, { color: "green", children: " (default)" }) : ""
|
|
12103
13495
|
] }) }, pm.id);
|
|
12104
13496
|
}) })
|
|
12105
13497
|
] });
|
|
12106
13498
|
};
|
|
12107
13499
|
|
|
12108
13500
|
// src/commands/payment-methods/index.tsx
|
|
12109
|
-
import { jsx as
|
|
13501
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
12110
13502
|
function createPaymentMethodsCli(createResource) {
|
|
12111
|
-
const cli2 =
|
|
13503
|
+
const cli2 = Cli5.create("payment-methods", {
|
|
12112
13504
|
description: "Payment methods management commands"
|
|
12113
13505
|
});
|
|
12114
13506
|
cli2.command("list", {
|
|
@@ -12129,8 +13521,8 @@ function createPaymentMethodsCli(createResource) {
|
|
|
12129
13521
|
const resource = createResource();
|
|
12130
13522
|
if (!c.agent && !c.formatExplicit) {
|
|
12131
13523
|
return new Promise((resolve) => {
|
|
12132
|
-
const { waitUntilExit } =
|
|
12133
|
-
/* @__PURE__ */
|
|
13524
|
+
const { waitUntilExit } = render5(
|
|
13525
|
+
/* @__PURE__ */ jsx18(PaymentMethodsList, { resource, onComplete: () => {
|
|
12134
13526
|
} })
|
|
12135
13527
|
);
|
|
12136
13528
|
waitUntilExit().then(async () => {
|
|
@@ -12158,7 +13550,7 @@ function createPaymentMethodsCli(createResource) {
|
|
|
12158
13550
|
}
|
|
12159
13551
|
if (!c.agent && !c.formatExplicit) {
|
|
12160
13552
|
return new Promise((resolve) => {
|
|
12161
|
-
const { waitUntilExit } =
|
|
13553
|
+
const { waitUntilExit } = render5(/* @__PURE__ */ jsx18(AddPaymentMethod, {}));
|
|
12162
13554
|
waitUntilExit().then(() => resolve({ url: WALLET_URL }));
|
|
12163
13555
|
});
|
|
12164
13556
|
}
|
|
@@ -12169,25 +13561,25 @@ function createPaymentMethodsCli(createResource) {
|
|
|
12169
13561
|
}
|
|
12170
13562
|
|
|
12171
13563
|
// src/commands/spend-request/index.tsx
|
|
12172
|
-
import { Cli as
|
|
12173
|
-
import { render as
|
|
13564
|
+
import { Cli as Cli6, z as z7 } from "incur";
|
|
13565
|
+
import { render as render6 } from "ink";
|
|
12174
13566
|
|
|
12175
13567
|
// src/utils/line-item-parser.ts
|
|
12176
|
-
import { z as
|
|
12177
|
-
var LineItemSchema =
|
|
12178
|
-
name:
|
|
12179
|
-
url:
|
|
12180
|
-
image_url:
|
|
12181
|
-
description:
|
|
12182
|
-
sku:
|
|
12183
|
-
quantity:
|
|
12184
|
-
unit_amount:
|
|
12185
|
-
product_url:
|
|
13568
|
+
import { z as z5 } from "zod";
|
|
13569
|
+
var LineItemSchema = z5.object({
|
|
13570
|
+
name: z5.string(),
|
|
13571
|
+
url: z5.string().optional(),
|
|
13572
|
+
image_url: z5.string().optional(),
|
|
13573
|
+
description: z5.string().optional(),
|
|
13574
|
+
sku: z5.string().optional(),
|
|
13575
|
+
quantity: z5.coerce.number().optional(),
|
|
13576
|
+
unit_amount: z5.coerce.number().optional(),
|
|
13577
|
+
product_url: z5.string().optional()
|
|
12186
13578
|
}).strict();
|
|
12187
|
-
var TotalSchema =
|
|
12188
|
-
type:
|
|
12189
|
-
display_text:
|
|
12190
|
-
amount:
|
|
13579
|
+
var TotalSchema = z5.object({
|
|
13580
|
+
type: z5.string(),
|
|
13581
|
+
display_text: z5.string(),
|
|
13582
|
+
amount: z5.coerce.number()
|
|
12191
13583
|
}).strict();
|
|
12192
13584
|
function parseKvString(raw) {
|
|
12193
13585
|
const result = {};
|
|
@@ -12212,7 +13604,7 @@ function parseLineItemFlag(raw) {
|
|
|
12212
13604
|
try {
|
|
12213
13605
|
return LineItemSchema.parse(obj);
|
|
12214
13606
|
} catch (err) {
|
|
12215
|
-
if (err instanceof
|
|
13607
|
+
if (err instanceof z5.ZodError) throw formatZodError(err, "Line item");
|
|
12216
13608
|
throw err;
|
|
12217
13609
|
}
|
|
12218
13610
|
}
|
|
@@ -12221,80 +13613,26 @@ function parseTotalFlag(raw) {
|
|
|
12221
13613
|
try {
|
|
12222
13614
|
return TotalSchema.parse(obj);
|
|
12223
13615
|
} catch (err) {
|
|
12224
|
-
if (err instanceof
|
|
13616
|
+
if (err instanceof z5.ZodError) throw formatZodError(err, "Total");
|
|
12225
13617
|
throw err;
|
|
12226
13618
|
}
|
|
12227
13619
|
}
|
|
12228
13620
|
|
|
12229
13621
|
// src/commands/spend-request/create.tsx
|
|
12230
|
-
import { Box as
|
|
13622
|
+
import { Box as Box14, Text as Text15 } from "ink";
|
|
12231
13623
|
import Spinner5 from "ink-spinner";
|
|
12232
|
-
import { useCallback, useEffect as
|
|
12233
|
-
|
|
12234
|
-
// src/commands/spend-request/app-download-qr-codes.tsx
|
|
12235
|
-
import { Box as Box7, Text as Text7 } from "ink";
|
|
12236
|
-
import { useMemo } from "react";
|
|
12237
|
-
|
|
12238
|
-
// src/utils/render-qr-matrix.ts
|
|
12239
|
-
import QRCode from "qrcode";
|
|
12240
|
-
function renderQrMatrix(url) {
|
|
12241
|
-
const qr = QRCode.create(url, { errorCorrectionLevel: "L" });
|
|
12242
|
-
const size = qr.modules.size;
|
|
12243
|
-
const data = qr.modules.data;
|
|
12244
|
-
const quiet = 1;
|
|
12245
|
-
const total = size + quiet * 2;
|
|
12246
|
-
const matrix = Array.from(
|
|
12247
|
-
{ length: total },
|
|
12248
|
-
(_, r) => Array.from({ length: total }, (_2, c) => {
|
|
12249
|
-
if (r < quiet || r >= size + quiet || c < quiet || c >= size + quiet) {
|
|
12250
|
-
return false;
|
|
12251
|
-
}
|
|
12252
|
-
return data[(r - quiet) * size + (c - quiet)] === 1;
|
|
12253
|
-
})
|
|
12254
|
-
);
|
|
12255
|
-
const lines = [];
|
|
12256
|
-
for (let r = 0; r < total; r += 2) {
|
|
12257
|
-
let line = "";
|
|
12258
|
-
for (let c = 0; c < total; c++) {
|
|
12259
|
-
const top = matrix[r][c];
|
|
12260
|
-
const bottom = r + 1 < total ? matrix[r + 1][c] : false;
|
|
12261
|
-
if (top && bottom) line += "\u2588";
|
|
12262
|
-
else if (top) line += "\u2580";
|
|
12263
|
-
else if (bottom) line += "\u2584";
|
|
12264
|
-
else line += " ";
|
|
12265
|
-
}
|
|
12266
|
-
lines.push(line);
|
|
12267
|
-
}
|
|
12268
|
-
return lines;
|
|
12269
|
-
}
|
|
12270
|
-
|
|
12271
|
-
// src/commands/spend-request/app-download-qr-codes.tsx
|
|
12272
|
-
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
12273
|
-
var DOWNLOAD_URL = "https://link.com/download";
|
|
12274
|
-
var AppDownloadQrCodes = () => {
|
|
12275
|
-
const qrLines = useMemo(() => renderQrMatrix(DOWNLOAD_URL), []);
|
|
12276
|
-
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", marginTop: 1, children: [
|
|
12277
|
-
/* @__PURE__ */ jsx10(Text7, { dimColor: true, children: "New! Get the Link app to approve spend requests easily" }),
|
|
12278
|
-
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", alignItems: "flex-start", marginTop: 1, children: [
|
|
12279
|
-
qrLines.map((line, i) => (
|
|
12280
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: stable static array
|
|
12281
|
-
/* @__PURE__ */ jsx10(Text7, { children: line }, i)
|
|
12282
|
-
)),
|
|
12283
|
-
/* @__PURE__ */ jsx10(Text7, { dimColor: true, children: DOWNLOAD_URL })
|
|
12284
|
-
] })
|
|
12285
|
-
] });
|
|
12286
|
-
};
|
|
13624
|
+
import { useCallback as useCallback2, useEffect as useEffect9, useState as useState9 } from "react";
|
|
12287
13625
|
|
|
12288
13626
|
// src/commands/spend-request/approval-waiting-view.tsx
|
|
12289
|
-
import { Box as
|
|
13627
|
+
import { Box as Box13, Text as Text14 } from "ink";
|
|
12290
13628
|
import Spinner4 from "ink-spinner";
|
|
12291
|
-
import { jsx as
|
|
13629
|
+
import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
12292
13630
|
var ApprovalWaitingView = ({
|
|
12293
13631
|
status,
|
|
12294
13632
|
approvalUrl
|
|
12295
|
-
}) => /* @__PURE__ */
|
|
12296
|
-
/* @__PURE__ */
|
|
12297
|
-
|
|
13633
|
+
}) => /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingY: 1, children: [
|
|
13634
|
+
/* @__PURE__ */ jsxs12(
|
|
13635
|
+
Box13,
|
|
12298
13636
|
{
|
|
12299
13637
|
flexDirection: "column",
|
|
12300
13638
|
borderStyle: "round",
|
|
@@ -12302,51 +13640,25 @@ var ApprovalWaitingView = ({
|
|
|
12302
13640
|
paddingX: 2,
|
|
12303
13641
|
paddingY: 1,
|
|
12304
13642
|
children: [
|
|
12305
|
-
/* @__PURE__ */
|
|
13643
|
+
/* @__PURE__ */ jsxs12(Text14, { children: [
|
|
12306
13644
|
"Approve at:",
|
|
12307
13645
|
" ",
|
|
12308
|
-
/* @__PURE__ */
|
|
13646
|
+
/* @__PURE__ */ jsx19(Text14, { bold: true, color: "cyan", children: approvalUrl })
|
|
12309
13647
|
] }),
|
|
12310
|
-
/* @__PURE__ */
|
|
13648
|
+
/* @__PURE__ */ jsx19(Text14, { dimColor: true, children: "Press Enter to open in browser" })
|
|
12311
13649
|
]
|
|
12312
13650
|
}
|
|
12313
13651
|
),
|
|
12314
|
-
/* @__PURE__ */
|
|
12315
|
-
/* @__PURE__ */
|
|
12316
|
-
/* @__PURE__ */
|
|
13652
|
+
/* @__PURE__ */ jsx19(AppDownloadQrCodes, {}),
|
|
13653
|
+
/* @__PURE__ */ jsx19(Box13, { marginTop: 1, children: status === "polling" ? /* @__PURE__ */ jsxs12(Text14, { color: "cyan", children: [
|
|
13654
|
+
/* @__PURE__ */ jsx19(Spinner4, { type: "dots" }),
|
|
12317
13655
|
" Waiting for approval..."
|
|
12318
|
-
] }) : /* @__PURE__ */
|
|
13656
|
+
] }) : /* @__PURE__ */ jsx19(Text14, { dimColor: true, children: "Waiting..." }) })
|
|
12319
13657
|
] });
|
|
12320
13658
|
|
|
12321
13659
|
// src/commands/spend-request/use-approval-polling.ts
|
|
12322
|
-
import { useInput as
|
|
12323
|
-
import { useEffect as
|
|
12324
|
-
|
|
12325
|
-
// src/utils/poll-until-approved.ts
|
|
12326
|
-
function pollUntilApproved(repository, id, options = {}) {
|
|
12327
|
-
const pollIntervalMs = options.pollIntervalMs ?? 2e3;
|
|
12328
|
-
const timeoutMs = options.timeoutMs ?? 3e5;
|
|
12329
|
-
const startTime = Date.now();
|
|
12330
|
-
const poll = async () => {
|
|
12331
|
-
const elapsed = Date.now() - startTime;
|
|
12332
|
-
if (elapsed > timeoutMs) {
|
|
12333
|
-
throw new Error("Approval polling timed out");
|
|
12334
|
-
}
|
|
12335
|
-
const request = await repository.getSpendRequest(id);
|
|
12336
|
-
if (!request) {
|
|
12337
|
-
throw new Error(`Spend request ${id} not found`);
|
|
12338
|
-
}
|
|
12339
|
-
if (request.status !== "created" && request.status !== "pending_approval") {
|
|
12340
|
-
return request;
|
|
12341
|
-
}
|
|
12342
|
-
options.onProgress?.(Math.floor(elapsed / 1e3));
|
|
12343
|
-
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
12344
|
-
return poll();
|
|
12345
|
-
};
|
|
12346
|
-
return poll();
|
|
12347
|
-
}
|
|
12348
|
-
|
|
12349
|
-
// src/commands/spend-request/use-approval-polling.ts
|
|
13660
|
+
import { useInput as useInput7 } from "ink";
|
|
13661
|
+
import { useEffect as useEffect8 } from "react";
|
|
12350
13662
|
function useApprovalPolling({
|
|
12351
13663
|
status,
|
|
12352
13664
|
setStatus,
|
|
@@ -12358,18 +13670,18 @@ function useApprovalPolling({
|
|
|
12358
13670
|
onError
|
|
12359
13671
|
}) {
|
|
12360
13672
|
const isWaiting = status === "waiting" || status === "polling";
|
|
12361
|
-
|
|
13673
|
+
useInput7(
|
|
12362
13674
|
(_input, key) => {
|
|
12363
13675
|
if (key.return && approvalUrl) openUrl(approvalUrl);
|
|
12364
13676
|
},
|
|
12365
13677
|
{ isActive: isWaiting }
|
|
12366
13678
|
);
|
|
12367
|
-
|
|
13679
|
+
useEffect8(() => {
|
|
12368
13680
|
if (status !== "waiting") return;
|
|
12369
13681
|
const timeout = setTimeout(() => setStatus("polling"), 1e3);
|
|
12370
13682
|
return () => clearTimeout(timeout);
|
|
12371
13683
|
}, [status, setStatus]);
|
|
12372
|
-
|
|
13684
|
+
useEffect8(() => {
|
|
12373
13685
|
if (status !== "polling" || !requestId) return;
|
|
12374
13686
|
let cancelled = false;
|
|
12375
13687
|
const poll = async () => {
|
|
@@ -12403,22 +13715,22 @@ function useApprovalPolling({
|
|
|
12403
13715
|
}
|
|
12404
13716
|
|
|
12405
13717
|
// src/commands/spend-request/create.tsx
|
|
12406
|
-
import { Fragment, jsx as
|
|
13718
|
+
import { Fragment as Fragment3, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
12407
13719
|
var CreateSpendRequest = ({
|
|
12408
13720
|
repository,
|
|
12409
13721
|
params,
|
|
12410
13722
|
requestApproval = false,
|
|
12411
13723
|
onComplete
|
|
12412
13724
|
}) => {
|
|
12413
|
-
const [status, setStatus] =
|
|
12414
|
-
const [request, setRequest] =
|
|
12415
|
-
const [error, setError] =
|
|
13725
|
+
const [status, setStatus] = useState9("creating");
|
|
13726
|
+
const [request, setRequest] = useState9(null);
|
|
13727
|
+
const [error, setError] = useState9("");
|
|
12416
13728
|
const approvalUrl = request?.approval_url ?? "";
|
|
12417
|
-
const onSuccess =
|
|
13729
|
+
const onSuccess = useCallback2(
|
|
12418
13730
|
(result) => setRequest(result),
|
|
12419
13731
|
[]
|
|
12420
13732
|
);
|
|
12421
|
-
const onError =
|
|
13733
|
+
const onError = useCallback2((msg) => setError(msg), []);
|
|
12422
13734
|
useApprovalPolling({
|
|
12423
13735
|
status,
|
|
12424
13736
|
setStatus,
|
|
@@ -12429,7 +13741,7 @@ var CreateSpendRequest = ({
|
|
|
12429
13741
|
onSuccess,
|
|
12430
13742
|
onError
|
|
12431
13743
|
});
|
|
12432
|
-
|
|
13744
|
+
useEffect9(() => {
|
|
12433
13745
|
const create = async () => {
|
|
12434
13746
|
try {
|
|
12435
13747
|
const result = await repository.createSpendRequest(params);
|
|
@@ -12449,57 +13761,57 @@ var CreateSpendRequest = ({
|
|
|
12449
13761
|
create();
|
|
12450
13762
|
}, [repository, params, requestApproval, onComplete]);
|
|
12451
13763
|
if (status === "creating") {
|
|
12452
|
-
return /* @__PURE__ */
|
|
12453
|
-
/* @__PURE__ */
|
|
13764
|
+
return /* @__PURE__ */ jsx20(Box14, { children: /* @__PURE__ */ jsxs13(Text15, { color: "cyan", children: [
|
|
13765
|
+
/* @__PURE__ */ jsx20(Spinner5, { type: "dots" }),
|
|
12454
13766
|
" Creating spend request..."
|
|
12455
13767
|
] }) });
|
|
12456
13768
|
}
|
|
12457
13769
|
if (status === "error") {
|
|
12458
|
-
return /* @__PURE__ */
|
|
12459
|
-
/* @__PURE__ */
|
|
12460
|
-
/* @__PURE__ */
|
|
13770
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
|
|
13771
|
+
/* @__PURE__ */ jsx20(Text15, { color: "red", children: "\u2717 Failed to create spend request" }),
|
|
13772
|
+
/* @__PURE__ */ jsx20(Text15, { color: "red", children: error })
|
|
12461
13773
|
] });
|
|
12462
13774
|
}
|
|
12463
13775
|
if (status === "success") {
|
|
12464
|
-
return /* @__PURE__ */
|
|
12465
|
-
/* @__PURE__ */
|
|
12466
|
-
/* @__PURE__ */
|
|
12467
|
-
/* @__PURE__ */
|
|
13776
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
|
|
13777
|
+
/* @__PURE__ */ jsx20(Text15, { color: "green", children: "\u2713 Spend request created" }),
|
|
13778
|
+
/* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
13779
|
+
/* @__PURE__ */ jsxs13(Text15, { children: [
|
|
12468
13780
|
"ID: ",
|
|
12469
|
-
/* @__PURE__ */
|
|
13781
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.id })
|
|
12470
13782
|
] }),
|
|
12471
|
-
/* @__PURE__ */
|
|
13783
|
+
/* @__PURE__ */ jsxs13(Text15, { children: [
|
|
12472
13784
|
"Status: ",
|
|
12473
|
-
/* @__PURE__ */
|
|
13785
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.status })
|
|
12474
13786
|
] }),
|
|
12475
|
-
/* @__PURE__ */
|
|
13787
|
+
/* @__PURE__ */ jsxs13(Text15, { children: [
|
|
12476
13788
|
"Amount:",
|
|
12477
13789
|
" ",
|
|
12478
|
-
/* @__PURE__ */
|
|
13790
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: (() => {
|
|
12479
13791
|
const t = request?.totals.find((t2) => t2.type === "total");
|
|
12480
13792
|
return t ? String(t.amount) : "N/A";
|
|
12481
13793
|
})() })
|
|
12482
13794
|
] }),
|
|
12483
|
-
/* @__PURE__ */
|
|
13795
|
+
/* @__PURE__ */ jsxs13(Text15, { children: [
|
|
12484
13796
|
"Merchant: ",
|
|
12485
|
-
/* @__PURE__ */
|
|
13797
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.merchant_name })
|
|
12486
13798
|
] }),
|
|
12487
|
-
/* @__PURE__ */
|
|
13799
|
+
/* @__PURE__ */ jsxs13(Text15, { children: [
|
|
12488
13800
|
"Line Items:",
|
|
12489
13801
|
" ",
|
|
12490
|
-
/* @__PURE__ */
|
|
13802
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") || "N/A" })
|
|
12491
13803
|
] })
|
|
12492
13804
|
] }),
|
|
12493
|
-
/* @__PURE__ */
|
|
13805
|
+
/* @__PURE__ */ jsx20(AppDownloadQrCodes, {})
|
|
12494
13806
|
] });
|
|
12495
13807
|
}
|
|
12496
|
-
return /* @__PURE__ */
|
|
12497
|
-
/* @__PURE__ */
|
|
13808
|
+
return /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
13809
|
+
/* @__PURE__ */ jsx20(Box14, { children: /* @__PURE__ */ jsxs13(Text15, { color: "green", children: [
|
|
12498
13810
|
"\u2713 Spend request created (ID: ",
|
|
12499
|
-
/* @__PURE__ */
|
|
13811
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.id }),
|
|
12500
13812
|
")"
|
|
12501
13813
|
] }) }),
|
|
12502
|
-
/* @__PURE__ */
|
|
13814
|
+
/* @__PURE__ */ jsx20(
|
|
12503
13815
|
ApprovalWaitingView,
|
|
12504
13816
|
{
|
|
12505
13817
|
status,
|
|
@@ -12510,21 +13822,21 @@ var CreateSpendRequest = ({
|
|
|
12510
13822
|
};
|
|
12511
13823
|
|
|
12512
13824
|
// src/commands/spend-request/request-approval.tsx
|
|
12513
|
-
import { Box as
|
|
13825
|
+
import { Box as Box15, Text as Text16 } from "ink";
|
|
12514
13826
|
import Spinner6 from "ink-spinner";
|
|
12515
|
-
import { useCallback as
|
|
12516
|
-
import { jsx as
|
|
13827
|
+
import { useCallback as useCallback3, useEffect as useEffect10, useState as useState10 } from "react";
|
|
13828
|
+
import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
12517
13829
|
var RequestApproval = ({
|
|
12518
13830
|
repository,
|
|
12519
13831
|
id,
|
|
12520
13832
|
onComplete
|
|
12521
13833
|
}) => {
|
|
12522
|
-
const [status, setStatus] =
|
|
12523
|
-
const [approvalUrl, setApprovalUrl] =
|
|
12524
|
-
const [result, setResult] =
|
|
12525
|
-
const [error, setError] =
|
|
12526
|
-
const onSuccess =
|
|
12527
|
-
const onError =
|
|
13834
|
+
const [status, setStatus] = useState10("requesting");
|
|
13835
|
+
const [approvalUrl, setApprovalUrl] = useState10("");
|
|
13836
|
+
const [result, setResult] = useState10(null);
|
|
13837
|
+
const [error, setError] = useState10("");
|
|
13838
|
+
const onSuccess = useCallback3((r) => setResult(r), []);
|
|
13839
|
+
const onError = useCallback3((msg) => setError(msg), []);
|
|
12528
13840
|
useApprovalPolling({
|
|
12529
13841
|
status,
|
|
12530
13842
|
setStatus,
|
|
@@ -12535,7 +13847,7 @@ var RequestApproval = ({
|
|
|
12535
13847
|
onSuccess,
|
|
12536
13848
|
onError
|
|
12537
13849
|
});
|
|
12538
|
-
|
|
13850
|
+
useEffect10(() => {
|
|
12539
13851
|
const request = async () => {
|
|
12540
13852
|
try {
|
|
12541
13853
|
const res = await repository.requestApproval(id);
|
|
@@ -12549,45 +13861,45 @@ var RequestApproval = ({
|
|
|
12549
13861
|
request();
|
|
12550
13862
|
}, [repository, id]);
|
|
12551
13863
|
if (status === "requesting") {
|
|
12552
|
-
return /* @__PURE__ */
|
|
12553
|
-
/* @__PURE__ */
|
|
13864
|
+
return /* @__PURE__ */ jsx21(Box15, { children: /* @__PURE__ */ jsxs14(Text16, { color: "cyan", children: [
|
|
13865
|
+
/* @__PURE__ */ jsx21(Spinner6, { type: "dots" }),
|
|
12554
13866
|
" Requesting approval..."
|
|
12555
13867
|
] }) });
|
|
12556
13868
|
}
|
|
12557
13869
|
if (status === "error") {
|
|
12558
|
-
return /* @__PURE__ */
|
|
12559
|
-
/* @__PURE__ */
|
|
12560
|
-
/* @__PURE__ */
|
|
13870
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
|
|
13871
|
+
/* @__PURE__ */ jsx21(Text16, { color: "red", children: "\u2717 Failed to request approval" }),
|
|
13872
|
+
/* @__PURE__ */ jsx21(Text16, { color: "red", children: error })
|
|
12561
13873
|
] });
|
|
12562
13874
|
}
|
|
12563
13875
|
if (status === "success") {
|
|
12564
|
-
return /* @__PURE__ */
|
|
12565
|
-
/* @__PURE__ */
|
|
12566
|
-
/* @__PURE__ */
|
|
12567
|
-
/* @__PURE__ */
|
|
13876
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
|
|
13877
|
+
/* @__PURE__ */ jsx21(Text16, { color: "green", children: "\u2713 Approval completed" }),
|
|
13878
|
+
/* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
13879
|
+
/* @__PURE__ */ jsxs14(Text16, { children: [
|
|
12568
13880
|
"ID: ",
|
|
12569
|
-
/* @__PURE__ */
|
|
13881
|
+
/* @__PURE__ */ jsx21(Text16, { bold: true, children: result?.id })
|
|
12570
13882
|
] }),
|
|
12571
|
-
/* @__PURE__ */
|
|
13883
|
+
/* @__PURE__ */ jsxs14(Text16, { children: [
|
|
12572
13884
|
"Status: ",
|
|
12573
|
-
/* @__PURE__ */
|
|
13885
|
+
/* @__PURE__ */ jsx21(Text16, { bold: true, children: result?.status })
|
|
12574
13886
|
] }),
|
|
12575
|
-
/* @__PURE__ */
|
|
13887
|
+
/* @__PURE__ */ jsxs14(Text16, { children: [
|
|
12576
13888
|
"Amount:",
|
|
12577
13889
|
" ",
|
|
12578
|
-
/* @__PURE__ */
|
|
13890
|
+
/* @__PURE__ */ jsx21(Text16, { bold: true, children: (() => {
|
|
12579
13891
|
const t = result?.totals.find((t2) => t2.type === "total");
|
|
12580
13892
|
return t ? String(t.amount) : "N/A";
|
|
12581
13893
|
})() })
|
|
12582
13894
|
] }),
|
|
12583
|
-
/* @__PURE__ */
|
|
13895
|
+
/* @__PURE__ */ jsxs14(Text16, { children: [
|
|
12584
13896
|
"Merchant: ",
|
|
12585
|
-
/* @__PURE__ */
|
|
13897
|
+
/* @__PURE__ */ jsx21(Text16, { bold: true, children: result?.merchant_name })
|
|
12586
13898
|
] })
|
|
12587
13899
|
] })
|
|
12588
13900
|
] });
|
|
12589
13901
|
}
|
|
12590
|
-
return /* @__PURE__ */
|
|
13902
|
+
return /* @__PURE__ */ jsx21(
|
|
12591
13903
|
ApprovalWaitingView,
|
|
12592
13904
|
{
|
|
12593
13905
|
status,
|
|
@@ -12597,10 +13909,10 @@ var RequestApproval = ({
|
|
|
12597
13909
|
};
|
|
12598
13910
|
|
|
12599
13911
|
// src/commands/spend-request/retrieve.tsx
|
|
12600
|
-
import { Box as
|
|
13912
|
+
import { Box as Box16, Text as Text17 } from "ink";
|
|
12601
13913
|
import Spinner7 from "ink-spinner";
|
|
12602
|
-
import { useEffect as
|
|
12603
|
-
import { jsx as
|
|
13914
|
+
import { useEffect as useEffect11, useRef as useRef4, useState as useState11 } from "react";
|
|
13915
|
+
import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
12604
13916
|
var RetrieveSpendRequest = ({
|
|
12605
13917
|
repository,
|
|
12606
13918
|
id,
|
|
@@ -12608,20 +13920,20 @@ var RetrieveSpendRequest = ({
|
|
|
12608
13920
|
include,
|
|
12609
13921
|
onComplete
|
|
12610
13922
|
}) => {
|
|
12611
|
-
const [phase, setPhase] =
|
|
12612
|
-
const [request, setRequest] =
|
|
12613
|
-
const [error, setError] =
|
|
12614
|
-
const [elapsed, setElapsed] =
|
|
12615
|
-
const startTimeRef =
|
|
12616
|
-
const pollRef =
|
|
12617
|
-
const timerRef =
|
|
12618
|
-
|
|
13923
|
+
const [phase, setPhase] = useState11("fetching");
|
|
13924
|
+
const [request, setRequest] = useState11(null);
|
|
13925
|
+
const [error, setError] = useState11("");
|
|
13926
|
+
const [elapsed, setElapsed] = useState11(0);
|
|
13927
|
+
const startTimeRef = useRef4(Date.now());
|
|
13928
|
+
const pollRef = useRef4(null);
|
|
13929
|
+
const timerRef = useRef4(null);
|
|
13930
|
+
useEffect11(() => {
|
|
12619
13931
|
return () => {
|
|
12620
13932
|
if (pollRef.current) clearInterval(pollRef.current);
|
|
12621
13933
|
if (timerRef.current) clearInterval(timerRef.current);
|
|
12622
13934
|
};
|
|
12623
13935
|
}, []);
|
|
12624
|
-
|
|
13936
|
+
useEffect11(() => {
|
|
12625
13937
|
const fetch2 = async () => {
|
|
12626
13938
|
try {
|
|
12627
13939
|
const result = await repository.getSpendRequest(id, { include });
|
|
@@ -12650,7 +13962,7 @@ var RetrieveSpendRequest = ({
|
|
|
12650
13962
|
};
|
|
12651
13963
|
fetch2();
|
|
12652
13964
|
}, [repository, id, include, onComplete]);
|
|
12653
|
-
|
|
13965
|
+
useEffect11(() => {
|
|
12654
13966
|
if (phase !== "polling") return;
|
|
12655
13967
|
timerRef.current = setInterval(() => {
|
|
12656
13968
|
const secs = Math.floor((Date.now() - startTimeRef.current) / 1e3);
|
|
@@ -12689,169 +14001,169 @@ var RetrieveSpendRequest = ({
|
|
|
12689
14001
|
};
|
|
12690
14002
|
}, [phase, repository, id, include, timeout, onComplete]);
|
|
12691
14003
|
if (phase === "fetching") {
|
|
12692
|
-
return /* @__PURE__ */
|
|
12693
|
-
/* @__PURE__ */
|
|
14004
|
+
return /* @__PURE__ */ jsx22(Box16, { children: /* @__PURE__ */ jsxs15(Text17, { color: "cyan", children: [
|
|
14005
|
+
/* @__PURE__ */ jsx22(Spinner7, { type: "dots" }),
|
|
12694
14006
|
" Retrieving spend request ",
|
|
12695
14007
|
id,
|
|
12696
14008
|
"..."
|
|
12697
14009
|
] }) });
|
|
12698
14010
|
}
|
|
12699
14011
|
if (phase === "error") {
|
|
12700
|
-
return /* @__PURE__ */
|
|
14012
|
+
return /* @__PURE__ */ jsx22(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsxs15(Text17, { color: "red", children: [
|
|
12701
14013
|
"\u2717 ",
|
|
12702
14014
|
error
|
|
12703
14015
|
] }) });
|
|
12704
14016
|
}
|
|
12705
14017
|
if (phase === "timeout") {
|
|
12706
|
-
return /* @__PURE__ */
|
|
12707
|
-
/* @__PURE__ */
|
|
14018
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
14019
|
+
/* @__PURE__ */ jsxs15(Text17, { color: "yellow", children: [
|
|
12708
14020
|
"\u2717 Timed out waiting for approval after ",
|
|
12709
14021
|
timeout,
|
|
12710
14022
|
"s"
|
|
12711
14023
|
] }),
|
|
12712
|
-
request && /* @__PURE__ */
|
|
12713
|
-
/* @__PURE__ */
|
|
14024
|
+
request && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
14025
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12714
14026
|
"ID: ",
|
|
12715
|
-
/* @__PURE__ */
|
|
14027
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request.id })
|
|
12716
14028
|
] }),
|
|
12717
|
-
/* @__PURE__ */
|
|
14029
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12718
14030
|
"Status: ",
|
|
12719
|
-
/* @__PURE__ */
|
|
14031
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request.status })
|
|
12720
14032
|
] })
|
|
12721
14033
|
] })
|
|
12722
14034
|
] });
|
|
12723
14035
|
}
|
|
12724
14036
|
if (phase === "polling") {
|
|
12725
|
-
return /* @__PURE__ */
|
|
12726
|
-
/* @__PURE__ */
|
|
12727
|
-
/* @__PURE__ */
|
|
14037
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
14038
|
+
/* @__PURE__ */ jsx22(Box16, { children: /* @__PURE__ */ jsxs15(Text17, { color: "cyan", children: [
|
|
14039
|
+
/* @__PURE__ */ jsx22(Spinner7, { type: "dots" }),
|
|
12728
14040
|
" Awaiting approval... (",
|
|
12729
14041
|
elapsed,
|
|
12730
14042
|
"s elapsed)"
|
|
12731
14043
|
] }) }),
|
|
12732
|
-
request?.approval_url && /* @__PURE__ */
|
|
14044
|
+
request?.approval_url && /* @__PURE__ */ jsx22(Box16, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsxs15(Text17, { dimColor: true, children: [
|
|
12733
14045
|
"Approval URL: ",
|
|
12734
|
-
/* @__PURE__ */
|
|
14046
|
+
/* @__PURE__ */ jsx22(Text17, { color: "cyan", children: request.approval_url })
|
|
12735
14047
|
] }) })
|
|
12736
14048
|
] });
|
|
12737
14049
|
}
|
|
12738
14050
|
if (phase === "declined") {
|
|
12739
|
-
return /* @__PURE__ */
|
|
12740
|
-
/* @__PURE__ */
|
|
12741
|
-
/* @__PURE__ */
|
|
12742
|
-
/* @__PURE__ */
|
|
14051
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
14052
|
+
/* @__PURE__ */ jsx22(Text17, { color: "red", children: "\u2717 Spend request declined" }),
|
|
14053
|
+
/* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
14054
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12743
14055
|
"ID: ",
|
|
12744
|
-
/* @__PURE__ */
|
|
14056
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.id })
|
|
12745
14057
|
] }),
|
|
12746
|
-
/* @__PURE__ */
|
|
14058
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12747
14059
|
"Status:",
|
|
12748
14060
|
" ",
|
|
12749
|
-
/* @__PURE__ */
|
|
14061
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, color: "red", children: request?.status })
|
|
12750
14062
|
] }),
|
|
12751
|
-
/* @__PURE__ */
|
|
14063
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12752
14064
|
"Amount:",
|
|
12753
14065
|
" ",
|
|
12754
|
-
/* @__PURE__ */
|
|
14066
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: (() => {
|
|
12755
14067
|
const t = request?.totals.find((t2) => t2.type === "total");
|
|
12756
14068
|
return t ? String(t.amount) : "N/A";
|
|
12757
14069
|
})() })
|
|
12758
14070
|
] }),
|
|
12759
|
-
/* @__PURE__ */
|
|
14071
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12760
14072
|
"Merchant: ",
|
|
12761
|
-
/* @__PURE__ */
|
|
14073
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.merchant_name })
|
|
12762
14074
|
] })
|
|
12763
14075
|
] })
|
|
12764
14076
|
] });
|
|
12765
14077
|
}
|
|
12766
|
-
return /* @__PURE__ */
|
|
12767
|
-
/* @__PURE__ */
|
|
12768
|
-
/* @__PURE__ */
|
|
12769
|
-
/* @__PURE__ */
|
|
14078
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
14079
|
+
/* @__PURE__ */ jsx22(Text17, { color: "green", children: "\u2713 Spend request approved" }),
|
|
14080
|
+
/* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
14081
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12770
14082
|
"ID: ",
|
|
12771
|
-
/* @__PURE__ */
|
|
14083
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.id })
|
|
12772
14084
|
] }),
|
|
12773
|
-
/* @__PURE__ */
|
|
14085
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12774
14086
|
"Status:",
|
|
12775
14087
|
" ",
|
|
12776
|
-
/* @__PURE__ */
|
|
14088
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, color: "green", children: request?.status })
|
|
12777
14089
|
] }),
|
|
12778
|
-
/* @__PURE__ */
|
|
14090
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12779
14091
|
"Amount:",
|
|
12780
14092
|
" ",
|
|
12781
|
-
/* @__PURE__ */
|
|
14093
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: (() => {
|
|
12782
14094
|
const t = request?.totals.find((t2) => t2.type === "total");
|
|
12783
14095
|
return t ? String(t.amount) : "N/A";
|
|
12784
14096
|
})() })
|
|
12785
14097
|
] }),
|
|
12786
|
-
/* @__PURE__ */
|
|
14098
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12787
14099
|
"Merchant: ",
|
|
12788
|
-
/* @__PURE__ */
|
|
14100
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.merchant_name })
|
|
12789
14101
|
] }),
|
|
12790
|
-
/* @__PURE__ */
|
|
14102
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12791
14103
|
"Line Items:",
|
|
12792
14104
|
" ",
|
|
12793
|
-
/* @__PURE__ */
|
|
14105
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
|
|
12794
14106
|
] }),
|
|
12795
|
-
request?.shared_payment_token && /* @__PURE__ */
|
|
12796
|
-
/* @__PURE__ */
|
|
14107
|
+
request?.shared_payment_token && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, children: [
|
|
14108
|
+
/* @__PURE__ */ jsxs15(Text17, { bold: true, children: [
|
|
12797
14109
|
"\x1B]8;;https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens\x07",
|
|
12798
14110
|
"Shared Payment Token",
|
|
12799
14111
|
"\x1B]8;;\x07",
|
|
12800
14112
|
":"
|
|
12801
14113
|
] }),
|
|
12802
|
-
/* @__PURE__ */
|
|
14114
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12803
14115
|
" ",
|
|
12804
14116
|
"Token: ",
|
|
12805
|
-
/* @__PURE__ */
|
|
14117
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request.shared_payment_token.id })
|
|
12806
14118
|
] })
|
|
12807
14119
|
] }),
|
|
12808
|
-
request?.card && /* @__PURE__ */
|
|
12809
|
-
/* @__PURE__ */
|
|
12810
|
-
/* @__PURE__ */
|
|
14120
|
+
request?.card && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, children: [
|
|
14121
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: "Card Details:" }),
|
|
14122
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12811
14123
|
" ",
|
|
12812
14124
|
"Number: ",
|
|
12813
|
-
/* @__PURE__ */
|
|
14125
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.card.number })
|
|
12814
14126
|
] }),
|
|
12815
|
-
/* @__PURE__ */
|
|
14127
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12816
14128
|
" ",
|
|
12817
14129
|
"Brand: ",
|
|
12818
|
-
/* @__PURE__ */
|
|
14130
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.card.brand })
|
|
12819
14131
|
] }),
|
|
12820
|
-
/* @__PURE__ */
|
|
14132
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12821
14133
|
" ",
|
|
12822
14134
|
"Expiry:",
|
|
12823
14135
|
" ",
|
|
12824
|
-
/* @__PURE__ */
|
|
14136
|
+
/* @__PURE__ */ jsxs15(Text17, { bold: true, children: [
|
|
12825
14137
|
String(request?.card.exp_month).padStart(2, "0"),
|
|
12826
14138
|
"/",
|
|
12827
14139
|
request?.card.exp_year
|
|
12828
14140
|
] })
|
|
12829
14141
|
] }),
|
|
12830
|
-
request?.card.cvc && /* @__PURE__ */
|
|
14142
|
+
request?.card.cvc && /* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12831
14143
|
" ",
|
|
12832
14144
|
"CVC: ",
|
|
12833
|
-
/* @__PURE__ */
|
|
14145
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request.card.cvc })
|
|
12834
14146
|
] }),
|
|
12835
|
-
request?.card.valid_until && /* @__PURE__ */
|
|
14147
|
+
request?.card.valid_until && /* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12836
14148
|
" ",
|
|
12837
14149
|
"Valid Until: ",
|
|
12838
|
-
/* @__PURE__ */
|
|
14150
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request.card.valid_until })
|
|
12839
14151
|
] }),
|
|
12840
|
-
request?.card.billing_address && /* @__PURE__ */
|
|
12841
|
-
/* @__PURE__ */
|
|
12842
|
-
/* @__PURE__ */
|
|
14152
|
+
request?.card.billing_address && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, children: [
|
|
14153
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: " Billing Address:" }),
|
|
14154
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12843
14155
|
" ",
|
|
12844
14156
|
request.card.billing_address.name
|
|
12845
14157
|
] }),
|
|
12846
|
-
/* @__PURE__ */
|
|
14158
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12847
14159
|
" ",
|
|
12848
14160
|
request.card.billing_address.line1
|
|
12849
14161
|
] }),
|
|
12850
|
-
request.card.billing_address.line2 && /* @__PURE__ */
|
|
14162
|
+
request.card.billing_address.line2 && /* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12851
14163
|
" ",
|
|
12852
14164
|
request.card.billing_address.line2
|
|
12853
14165
|
] }),
|
|
12854
|
-
/* @__PURE__ */
|
|
14166
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12855
14167
|
" ",
|
|
12856
14168
|
[
|
|
12857
14169
|
request.card.billing_address.city,
|
|
@@ -12859,7 +14171,7 @@ var RetrieveSpendRequest = ({
|
|
|
12859
14171
|
request.card.billing_address.postal_code
|
|
12860
14172
|
].filter(Boolean).join(", ")
|
|
12861
14173
|
] }),
|
|
12862
|
-
/* @__PURE__ */
|
|
14174
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12863
14175
|
" ",
|
|
12864
14176
|
request.card.billing_address.country
|
|
12865
14177
|
] })
|
|
@@ -12870,69 +14182,69 @@ var RetrieveSpendRequest = ({
|
|
|
12870
14182
|
};
|
|
12871
14183
|
|
|
12872
14184
|
// src/commands/spend-request/schema.ts
|
|
12873
|
-
import { z as
|
|
12874
|
-
var createOptions =
|
|
12875
|
-
paymentMethodId:
|
|
12876
|
-
credentialType:
|
|
14185
|
+
import { z as z6 } from "incur";
|
|
14186
|
+
var createOptions = z6.object({
|
|
14187
|
+
paymentMethodId: z6.string().describe("Payment method ID"),
|
|
14188
|
+
credentialType: z6.enum(["shared_payment_token", "card"]).default("card").describe(
|
|
12877
14189
|
'"card" for checkout forms/Stripe Elements; "shared_payment_token" for HTTP 402/machine payment flows'
|
|
12878
14190
|
),
|
|
12879
|
-
networkId:
|
|
14191
|
+
networkId: z6.string().optional().describe(
|
|
12880
14192
|
"Network ID (required for shared_payment_token) \u2014 use `link-cli mpp decode` to extract"
|
|
12881
14193
|
),
|
|
12882
|
-
amount:
|
|
12883
|
-
currency:
|
|
12884
|
-
merchantName:
|
|
14194
|
+
amount: z6.coerce.number().int().positive().max(5e4).describe("Amount in cents, max 50000 ($500.00)"),
|
|
14195
|
+
currency: z6.string().length(3).default("usd").describe("Currency code"),
|
|
14196
|
+
merchantName: z6.string().optional().describe(
|
|
12885
14197
|
"Merchant name (required for card; forbidden for shared_payment_token)"
|
|
12886
14198
|
),
|
|
12887
|
-
merchantUrl:
|
|
14199
|
+
merchantUrl: z6.string().optional().describe(
|
|
12888
14200
|
"Merchant URL (required for card; forbidden for shared_payment_token)"
|
|
12889
14201
|
),
|
|
12890
|
-
context:
|
|
14202
|
+
context: z6.string().min(100).describe(
|
|
12891
14203
|
"Min 100 chars \u2014 describe the purchase and rationale; the user reads this when approving"
|
|
12892
14204
|
),
|
|
12893
|
-
lineItem:
|
|
12894
|
-
total:
|
|
12895
|
-
requestApproval:
|
|
12896
|
-
test:
|
|
14205
|
+
lineItem: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe("Line item (repeatable, key:value format)"),
|
|
14206
|
+
total: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe("Total (repeatable, key:value format)"),
|
|
14207
|
+
requestApproval: z6.boolean().default(true).describe("Request approval and poll until approved/denied/expired"),
|
|
14208
|
+
test: z6.boolean().default(false).describe(
|
|
12897
14209
|
"Use test mode (creates testmode credentials from test card data)"
|
|
12898
14210
|
)
|
|
12899
14211
|
});
|
|
12900
|
-
var retrieveOptions =
|
|
12901
|
-
timeout:
|
|
12902
|
-
interval:
|
|
14212
|
+
var retrieveOptions = z6.object({
|
|
14213
|
+
timeout: z6.coerce.number().default(300).describe("Polling timeout in seconds"),
|
|
14214
|
+
interval: z6.coerce.number().default(0).describe(
|
|
12903
14215
|
"Poll interval in seconds. When > 0, polls until status is terminal or timeout is reached, yielding status on each attempt."
|
|
12904
14216
|
),
|
|
12905
|
-
maxAttempts:
|
|
12906
|
-
include:
|
|
14217
|
+
maxAttempts: z6.coerce.number().default(0).describe("Max poll attempts. 0 = unlimited (use timeout instead)."),
|
|
14218
|
+
include: z6.array(z6.string()).default([]).describe("Include extra data (repeatable, e.g. --include card)")
|
|
12907
14219
|
});
|
|
12908
|
-
var updateOptions =
|
|
12909
|
-
paymentMethodId:
|
|
12910
|
-
amount:
|
|
12911
|
-
merchantUrl:
|
|
12912
|
-
profileId:
|
|
12913
|
-
merchantId:
|
|
12914
|
-
currency:
|
|
12915
|
-
lineItem:
|
|
12916
|
-
total:
|
|
14220
|
+
var updateOptions = z6.object({
|
|
14221
|
+
paymentMethodId: z6.string().optional().describe("Payment method ID"),
|
|
14222
|
+
amount: z6.coerce.number().optional().describe("Amount in cents"),
|
|
14223
|
+
merchantUrl: z6.string().optional().describe("Merchant URL"),
|
|
14224
|
+
profileId: z6.string().optional().describe("Profile ID"),
|
|
14225
|
+
merchantId: z6.string().optional().describe("Merchant ID"),
|
|
14226
|
+
currency: z6.string().optional().describe("Currency code"),
|
|
14227
|
+
lineItem: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe("Line item (repeatable, key:value format)"),
|
|
14228
|
+
total: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe("Total (repeatable, key:value format)")
|
|
12917
14229
|
});
|
|
12918
14230
|
|
|
12919
14231
|
// src/commands/spend-request/update.tsx
|
|
12920
|
-
import { Box as
|
|
14232
|
+
import { Box as Box17, Text as Text18 } from "ink";
|
|
12921
14233
|
import Spinner8 from "ink-spinner";
|
|
12922
|
-
import { useEffect as
|
|
12923
|
-
import { jsx as
|
|
14234
|
+
import { useEffect as useEffect12, useState as useState12 } from "react";
|
|
14235
|
+
import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
12924
14236
|
var UpdateSpendRequest = ({
|
|
12925
14237
|
repository,
|
|
12926
14238
|
id,
|
|
12927
14239
|
params,
|
|
12928
14240
|
onComplete
|
|
12929
14241
|
}) => {
|
|
12930
|
-
const [status, setStatus] =
|
|
14242
|
+
const [status, setStatus] = useState12(
|
|
12931
14243
|
"loading"
|
|
12932
14244
|
);
|
|
12933
|
-
const [request, setRequest] =
|
|
12934
|
-
const [error, setError] =
|
|
12935
|
-
|
|
14245
|
+
const [request, setRequest] = useState12(null);
|
|
14246
|
+
const [error, setError] = useState12("");
|
|
14247
|
+
useEffect12(() => {
|
|
12936
14248
|
const update = async () => {
|
|
12937
14249
|
try {
|
|
12938
14250
|
const result = await repository.updateSpendRequest(id, params);
|
|
@@ -12948,55 +14260,55 @@ var UpdateSpendRequest = ({
|
|
|
12948
14260
|
update();
|
|
12949
14261
|
}, [repository, id, params, onComplete]);
|
|
12950
14262
|
if (status === "loading") {
|
|
12951
|
-
return /* @__PURE__ */
|
|
12952
|
-
/* @__PURE__ */
|
|
14263
|
+
return /* @__PURE__ */ jsx23(Box17, { children: /* @__PURE__ */ jsxs16(Text18, { color: "cyan", children: [
|
|
14264
|
+
/* @__PURE__ */ jsx23(Spinner8, { type: "dots" }),
|
|
12953
14265
|
" Updating spend request ",
|
|
12954
14266
|
id,
|
|
12955
14267
|
"..."
|
|
12956
14268
|
] }) });
|
|
12957
14269
|
}
|
|
12958
14270
|
if (status === "error") {
|
|
12959
|
-
return /* @__PURE__ */
|
|
12960
|
-
/* @__PURE__ */
|
|
12961
|
-
/* @__PURE__ */
|
|
14271
|
+
return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", children: [
|
|
14272
|
+
/* @__PURE__ */ jsx23(Text18, { color: "red", children: "\u2717 Failed to update spend request" }),
|
|
14273
|
+
/* @__PURE__ */ jsx23(Text18, { color: "red", children: error })
|
|
12962
14274
|
] });
|
|
12963
14275
|
}
|
|
12964
|
-
return /* @__PURE__ */
|
|
12965
|
-
/* @__PURE__ */
|
|
12966
|
-
/* @__PURE__ */
|
|
12967
|
-
/* @__PURE__ */
|
|
14276
|
+
return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", children: [
|
|
14277
|
+
/* @__PURE__ */ jsx23(Text18, { color: "green", children: "\u2713 Spend request updated" }),
|
|
14278
|
+
/* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
14279
|
+
/* @__PURE__ */ jsxs16(Text18, { children: [
|
|
12968
14280
|
"ID: ",
|
|
12969
|
-
/* @__PURE__ */
|
|
14281
|
+
/* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.id })
|
|
12970
14282
|
] }),
|
|
12971
|
-
/* @__PURE__ */
|
|
14283
|
+
/* @__PURE__ */ jsxs16(Text18, { children: [
|
|
12972
14284
|
"Status: ",
|
|
12973
|
-
/* @__PURE__ */
|
|
14285
|
+
/* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.status })
|
|
12974
14286
|
] }),
|
|
12975
|
-
/* @__PURE__ */
|
|
14287
|
+
/* @__PURE__ */ jsxs16(Text18, { children: [
|
|
12976
14288
|
"Amount:",
|
|
12977
14289
|
" ",
|
|
12978
|
-
/* @__PURE__ */
|
|
14290
|
+
/* @__PURE__ */ jsx23(Text18, { bold: true, children: (() => {
|
|
12979
14291
|
const t = request?.totals.find((t2) => t2.type === "total");
|
|
12980
14292
|
return t ? String(t.amount) : "N/A";
|
|
12981
14293
|
})() })
|
|
12982
14294
|
] }),
|
|
12983
|
-
/* @__PURE__ */
|
|
14295
|
+
/* @__PURE__ */ jsxs16(Text18, { children: [
|
|
12984
14296
|
"Merchant: ",
|
|
12985
|
-
/* @__PURE__ */
|
|
14297
|
+
/* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.merchant_name })
|
|
12986
14298
|
] }),
|
|
12987
|
-
/* @__PURE__ */
|
|
14299
|
+
/* @__PURE__ */ jsxs16(Text18, { children: [
|
|
12988
14300
|
"Line Items:",
|
|
12989
14301
|
" ",
|
|
12990
|
-
/* @__PURE__ */
|
|
14302
|
+
/* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
|
|
12991
14303
|
] })
|
|
12992
14304
|
] })
|
|
12993
14305
|
] });
|
|
12994
14306
|
};
|
|
12995
14307
|
|
|
12996
14308
|
// src/commands/spend-request/index.tsx
|
|
12997
|
-
import { jsx as
|
|
14309
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
12998
14310
|
function createSpendRequestCli(repository) {
|
|
12999
|
-
const cli2 =
|
|
14311
|
+
const cli2 = Cli6.create("spend-request", {
|
|
13000
14312
|
description: "Spend request management commands"
|
|
13001
14313
|
});
|
|
13002
14314
|
cli2.command("create", {
|
|
@@ -13074,8 +14386,8 @@ function createSpendRequestCli(repository) {
|
|
|
13074
14386
|
};
|
|
13075
14387
|
if (!c.agent && !c.formatExplicit) {
|
|
13076
14388
|
return new Promise((resolve) => {
|
|
13077
|
-
const { waitUntilExit } =
|
|
13078
|
-
/* @__PURE__ */
|
|
14389
|
+
const { waitUntilExit } = render6(
|
|
14390
|
+
/* @__PURE__ */ jsx24(
|
|
13079
14391
|
CreateSpendRequest,
|
|
13080
14392
|
{
|
|
13081
14393
|
repository,
|
|
@@ -13109,8 +14421,8 @@ function createSpendRequestCli(repository) {
|
|
|
13109
14421
|
});
|
|
13110
14422
|
cli2.command("update", {
|
|
13111
14423
|
description: "Update a spend request",
|
|
13112
|
-
args:
|
|
13113
|
-
id:
|
|
14424
|
+
args: z7.object({
|
|
14425
|
+
id: z7.string().describe("Spend request ID")
|
|
13114
14426
|
}),
|
|
13115
14427
|
options: updateOptions,
|
|
13116
14428
|
outputPolicy: "agent-only",
|
|
@@ -13147,8 +14459,8 @@ function createSpendRequestCli(repository) {
|
|
|
13147
14459
|
);
|
|
13148
14460
|
if (!c.agent && !c.formatExplicit) {
|
|
13149
14461
|
return new Promise((resolve) => {
|
|
13150
|
-
const { waitUntilExit } =
|
|
13151
|
-
/* @__PURE__ */
|
|
14462
|
+
const { waitUntilExit } = render6(
|
|
14463
|
+
/* @__PURE__ */ jsx24(
|
|
13152
14464
|
UpdateSpendRequest,
|
|
13153
14465
|
{
|
|
13154
14466
|
repository,
|
|
@@ -13169,8 +14481,8 @@ function createSpendRequestCli(repository) {
|
|
|
13169
14481
|
});
|
|
13170
14482
|
cli2.command("request-approval", {
|
|
13171
14483
|
description: "Request approval for a spend request",
|
|
13172
|
-
args:
|
|
13173
|
-
id:
|
|
14484
|
+
args: z7.object({
|
|
14485
|
+
id: z7.string().describe("Spend request ID")
|
|
13174
14486
|
}),
|
|
13175
14487
|
outputPolicy: "agent-only",
|
|
13176
14488
|
async *run(c) {
|
|
@@ -13188,8 +14500,8 @@ function createSpendRequestCli(repository) {
|
|
|
13188
14500
|
const id = c.args.id;
|
|
13189
14501
|
if (!c.agent && !c.formatExplicit) {
|
|
13190
14502
|
return new Promise((resolve) => {
|
|
13191
|
-
const { waitUntilExit } =
|
|
13192
|
-
/* @__PURE__ */
|
|
14503
|
+
const { waitUntilExit } = render6(
|
|
14504
|
+
/* @__PURE__ */ jsx24(
|
|
13193
14505
|
RequestApproval,
|
|
13194
14506
|
{
|
|
13195
14507
|
repository,
|
|
@@ -13218,8 +14530,8 @@ function createSpendRequestCli(repository) {
|
|
|
13218
14530
|
});
|
|
13219
14531
|
cli2.command("retrieve", {
|
|
13220
14532
|
description: "Retrieve a spend request",
|
|
13221
|
-
args:
|
|
13222
|
-
id:
|
|
14533
|
+
args: z7.object({
|
|
14534
|
+
id: z7.string().describe("Spend request ID")
|
|
13223
14535
|
}),
|
|
13224
14536
|
options: retrieveOptions,
|
|
13225
14537
|
outputPolicy: "agent-only",
|
|
@@ -13244,8 +14556,8 @@ function createSpendRequestCli(repository) {
|
|
|
13244
14556
|
const include = includeArr?.length ? includeArr : void 0;
|
|
13245
14557
|
if (!c.agent && !c.formatExplicit) {
|
|
13246
14558
|
return new Promise((resolve) => {
|
|
13247
|
-
const { waitUntilExit } =
|
|
13248
|
-
/* @__PURE__ */
|
|
14559
|
+
const { waitUntilExit } = render6(
|
|
14560
|
+
/* @__PURE__ */ jsx24(
|
|
13249
14561
|
RetrieveSpendRequest,
|
|
13250
14562
|
{
|
|
13251
14563
|
repository,
|
|
@@ -13603,7 +14915,7 @@ var ResourceFactory = class {
|
|
|
13603
14915
|
};
|
|
13604
14916
|
|
|
13605
14917
|
// src/cli.tsx
|
|
13606
|
-
var cliVersion = "0.
|
|
14918
|
+
var cliVersion = "0.3.0";
|
|
13607
14919
|
var buildNumber = "1";
|
|
13608
14920
|
var cliName = "@stripe/link-cli";
|
|
13609
14921
|
var defaultHeaders = {
|
|
@@ -13617,7 +14929,7 @@ var spendRequestRepo = factory.createSpendRequestResource();
|
|
|
13617
14929
|
var notifier = updateNotifier({
|
|
13618
14930
|
pkg: { name: cliName, version: cliVersion }
|
|
13619
14931
|
});
|
|
13620
|
-
var cli =
|
|
14932
|
+
var cli = Cli7.create("link-cli", {
|
|
13621
14933
|
description: "Create a secure, one-time payment credential from a Link wallet to let agents complete purchases on behalf of users.",
|
|
13622
14934
|
version: `${cliVersion} (build ${buildNumber})`
|
|
13623
14935
|
});
|
|
@@ -13627,6 +14939,20 @@ cli.command(
|
|
|
13627
14939
|
createPaymentMethodsCli(() => factory.createPaymentMethodsResource())
|
|
13628
14940
|
);
|
|
13629
14941
|
cli.command(createMppCli(spendRequestRepo));
|
|
14942
|
+
cli.command(
|
|
14943
|
+
createDemoCli(
|
|
14944
|
+
authRepo,
|
|
14945
|
+
spendRequestRepo,
|
|
14946
|
+
() => factory.createPaymentMethodsResource()
|
|
14947
|
+
)
|
|
14948
|
+
);
|
|
14949
|
+
cli.command(
|
|
14950
|
+
createOnboardCli(
|
|
14951
|
+
authRepo,
|
|
14952
|
+
spendRequestRepo,
|
|
14953
|
+
() => factory.createPaymentMethodsResource()
|
|
14954
|
+
)
|
|
14955
|
+
);
|
|
13630
14956
|
var isAgent = process.argv.includes("--format") || process.argv.includes("--mcp");
|
|
13631
14957
|
if (!isAgent) {
|
|
13632
14958
|
notifier.notify({ defer: false });
|