@stripe/link-cli 0.2.2 → 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 +1760 -424
- package/package.json +3 -3
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
|
|
@@ -11525,6 +11525,7 @@ function createAuthCli(authResource, updateInfo) {
|
|
|
11525
11525
|
const maxAttempts = opts.maxAttempts;
|
|
11526
11526
|
const deadline = Date.now() + opts.timeout * 1e3;
|
|
11527
11527
|
let attempts = 0;
|
|
11528
|
+
let previousAttemptData;
|
|
11528
11529
|
while (true) {
|
|
11529
11530
|
const pending = storage.getPendingDeviceAuth();
|
|
11530
11531
|
if (pending && !storage.isAuthenticated()) {
|
|
@@ -11567,7 +11568,11 @@ function createAuthCli(authResource, updateInfo) {
|
|
|
11567
11568
|
yield status;
|
|
11568
11569
|
return;
|
|
11569
11570
|
}
|
|
11570
|
-
|
|
11571
|
+
const snapshot = JSON.stringify(status);
|
|
11572
|
+
if (snapshot !== previousAttemptData) {
|
|
11573
|
+
previousAttemptData = snapshot;
|
|
11574
|
+
yield status;
|
|
11575
|
+
}
|
|
11571
11576
|
await new Promise((resolve) => setTimeout(resolve, interval * 1e3));
|
|
11572
11577
|
}
|
|
11573
11578
|
}
|
|
@@ -11575,10 +11580,686 @@ function createAuthCli(authResource, updateInfo) {
|
|
|
11575
11580
|
return cli2;
|
|
11576
11581
|
}
|
|
11577
11582
|
|
|
11578
|
-
// src/commands/
|
|
11579
|
-
import { Cli as Cli2, z as
|
|
11583
|
+
// src/commands/demo/index.tsx
|
|
11584
|
+
import { Cli as Cli2, z as z2 } from "incur";
|
|
11580
11585
|
import { render as render2 } from "ink";
|
|
11581
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
|
+
|
|
11582
12263
|
// src/commands/mpp/decode.ts
|
|
11583
12264
|
import { Challenge } from "mppx";
|
|
11584
12265
|
function getString(value, path5, required = true) {
|
|
@@ -11657,41 +12338,14 @@ function decodeStripeChallenge(challengeHeader) {
|
|
|
11657
12338
|
};
|
|
11658
12339
|
}
|
|
11659
12340
|
|
|
11660
|
-
// src/commands/mpp/decode-view.tsx
|
|
11661
|
-
import { Box as Box3, Text as Text3 } from "ink";
|
|
11662
|
-
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
11663
|
-
function DecodeChallengeView({
|
|
11664
|
-
decoded
|
|
11665
|
-
}) {
|
|
11666
|
-
return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", children: [
|
|
11667
|
-
/* @__PURE__ */ jsx4(Text3, { color: "green", children: "\u2713 Stripe challenge decoded" }),
|
|
11668
|
-
/* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
11669
|
-
/* @__PURE__ */ jsxs2(Text3, { children: [
|
|
11670
|
-
"ID: ",
|
|
11671
|
-
/* @__PURE__ */ jsx4(Text3, { bold: true, children: decoded.id })
|
|
11672
|
-
] }),
|
|
11673
|
-
/* @__PURE__ */ jsxs2(Text3, { children: [
|
|
11674
|
-
"Realm: ",
|
|
11675
|
-
/* @__PURE__ */ jsx4(Text3, { bold: true, children: decoded.realm })
|
|
11676
|
-
] }),
|
|
11677
|
-
/* @__PURE__ */ jsxs2(Text3, { children: [
|
|
11678
|
-
"Network ID: ",
|
|
11679
|
-
/* @__PURE__ */ jsx4(Text3, { bold: true, children: decoded.network_id })
|
|
11680
|
-
] }),
|
|
11681
|
-
/* @__PURE__ */ jsx4(Text3, { children: "Request JSON:" }),
|
|
11682
|
-
/* @__PURE__ */ jsx4(Text3, { children: JSON.stringify(decoded.request_json, null, 2) })
|
|
11683
|
-
] })
|
|
11684
|
-
] });
|
|
11685
|
-
}
|
|
11686
|
-
|
|
11687
12341
|
// src/commands/mpp/pay.tsx
|
|
11688
|
-
import { Box as
|
|
12342
|
+
import { Box as Box6, Text as Text7 } from "ink";
|
|
11689
12343
|
import Spinner2 from "ink-spinner";
|
|
11690
12344
|
import { Credential, Method } from "mppx";
|
|
11691
12345
|
import { Mppx, Transport } from "mppx/client";
|
|
11692
12346
|
import { Methods as StripeMethods } from "mppx/stripe";
|
|
11693
|
-
import { useEffect as
|
|
11694
|
-
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";
|
|
11695
12349
|
function buildHeaders(data, headers) {
|
|
11696
12350
|
const result = {};
|
|
11697
12351
|
if (data !== void 0) {
|
|
@@ -11796,10 +12450,10 @@ function MppPay({
|
|
|
11796
12450
|
repository,
|
|
11797
12451
|
onComplete
|
|
11798
12452
|
}) {
|
|
11799
|
-
const [step, setStep] =
|
|
11800
|
-
const [result, setResult] =
|
|
11801
|
-
const [error, setError] =
|
|
11802
|
-
|
|
12453
|
+
const [step, setStep] = useState4("retrieving");
|
|
12454
|
+
const [result, setResult] = useState4(null);
|
|
12455
|
+
const [error, setError] = useState4(null);
|
|
12456
|
+
useEffect4(() => {
|
|
11803
12457
|
(async () => {
|
|
11804
12458
|
try {
|
|
11805
12459
|
setStep("retrieving");
|
|
@@ -11868,54 +12522,640 @@ function MppPay({
|
|
|
11868
12522
|
done: "Done"
|
|
11869
12523
|
};
|
|
11870
12524
|
if (error) {
|
|
11871
|
-
return /* @__PURE__ */
|
|
12525
|
+
return /* @__PURE__ */ jsxs5(Text7, { color: "red", children: [
|
|
11872
12526
|
"Error: ",
|
|
11873
12527
|
error
|
|
11874
12528
|
] });
|
|
11875
12529
|
}
|
|
11876
|
-
return /* @__PURE__ */
|
|
11877
|
-
step !== "done" && /* @__PURE__ */
|
|
11878
|
-
/* @__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" }),
|
|
11879
12533
|
" ",
|
|
11880
12534
|
stepLabels[step],
|
|
11881
12535
|
"..."
|
|
11882
12536
|
] }) }),
|
|
11883
|
-
result && /* @__PURE__ */
|
|
11884
|
-
/* @__PURE__ */
|
|
12537
|
+
result && /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
12538
|
+
/* @__PURE__ */ jsxs5(Text7, { color: "green", children: [
|
|
11885
12539
|
"HTTP ",
|
|
11886
12540
|
result.status
|
|
11887
12541
|
] }),
|
|
11888
|
-
/* @__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) })
|
|
11889
13129
|
] })
|
|
11890
13130
|
] });
|
|
11891
13131
|
}
|
|
11892
13132
|
|
|
11893
13133
|
// src/commands/mpp/schema.ts
|
|
11894
|
-
import { z as
|
|
11895
|
-
var payOptions =
|
|
11896
|
-
spendRequestId:
|
|
13134
|
+
import { z as z3 } from "incur";
|
|
13135
|
+
var payOptions = z3.object({
|
|
13136
|
+
spendRequestId: z3.string().describe(
|
|
11897
13137
|
'Approved spend request ID with credential_type "shared_payment_token"'
|
|
11898
13138
|
),
|
|
11899
|
-
method:
|
|
11900
|
-
data:
|
|
11901
|
-
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)')
|
|
11902
13142
|
});
|
|
11903
|
-
var decodeOptions =
|
|
11904
|
-
challenge:
|
|
13143
|
+
var decodeOptions = z3.object({
|
|
13144
|
+
challenge: z3.string().describe(
|
|
11905
13145
|
"Raw WWW-Authenticate header value; may include multiple payment challenges"
|
|
11906
13146
|
)
|
|
11907
13147
|
});
|
|
11908
13148
|
|
|
11909
13149
|
// src/commands/mpp/index.tsx
|
|
11910
|
-
import { jsx as
|
|
13150
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
11911
13151
|
function createMppCli(repository) {
|
|
11912
|
-
const cli2 =
|
|
13152
|
+
const cli2 = Cli3.create("mpp", {
|
|
11913
13153
|
description: "Machine payment protocol (MPP) commands"
|
|
11914
13154
|
});
|
|
11915
13155
|
cli2.command("pay", {
|
|
11916
13156
|
description: "Complete a machine payment protocol (MPP) payment using an approved spend request",
|
|
11917
|
-
args:
|
|
11918
|
-
url:
|
|
13157
|
+
args: z4.object({
|
|
13158
|
+
url: z4.string().describe("URL to pay")
|
|
11919
13159
|
}),
|
|
11920
13160
|
options: payOptions,
|
|
11921
13161
|
alias: { method: "X", data: "d", header: "H" },
|
|
@@ -11939,8 +13179,8 @@ function createMppCli(repository) {
|
|
|
11939
13179
|
const headers = opts.header?.length ? opts.header : void 0;
|
|
11940
13180
|
if (!c.agent && !c.formatExplicit) {
|
|
11941
13181
|
return new Promise((resolve) => {
|
|
11942
|
-
const { waitUntilExit } =
|
|
11943
|
-
/* @__PURE__ */
|
|
13182
|
+
const { waitUntilExit } = render3(
|
|
13183
|
+
/* @__PURE__ */ jsx13(
|
|
11944
13184
|
MppPay,
|
|
11945
13185
|
{
|
|
11946
13186
|
url,
|
|
@@ -11986,8 +13226,8 @@ function createMppCli(repository) {
|
|
|
11986
13226
|
const decoded = decodeStripeChallenge(c.options.challenge);
|
|
11987
13227
|
if (!c.agent && !c.formatExplicit) {
|
|
11988
13228
|
return new Promise((resolve) => {
|
|
11989
|
-
const { waitUntilExit } =
|
|
11990
|
-
/* @__PURE__ */
|
|
13229
|
+
const { waitUntilExit } = render3(
|
|
13230
|
+
/* @__PURE__ */ jsx13(DecodeChallengeView, { decoded })
|
|
11991
13231
|
);
|
|
11992
13232
|
waitUntilExit().then(() => resolve(decoded));
|
|
11993
13233
|
});
|
|
@@ -11998,26 +13238,183 @@ function createMppCli(repository) {
|
|
|
11998
13238
|
return cli2;
|
|
11999
13239
|
}
|
|
12000
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
|
+
|
|
12001
13398
|
// src/commands/payment-methods/index.tsx
|
|
12002
|
-
import { Cli as
|
|
12003
|
-
import { render as
|
|
13399
|
+
import { Cli as Cli5 } from "incur";
|
|
13400
|
+
import { render as render5 } from "ink";
|
|
12004
13401
|
|
|
12005
13402
|
// src/commands/payment-methods/add.tsx
|
|
12006
|
-
import { Box as
|
|
12007
|
-
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";
|
|
12008
13405
|
var WALLET_URL = "https://app.link.com/wallet";
|
|
12009
13406
|
var AddPaymentMethod = () => {
|
|
12010
13407
|
const { exit } = useApp();
|
|
12011
|
-
|
|
13408
|
+
useInput6((_input, key) => {
|
|
12012
13409
|
if (key.return) {
|
|
12013
13410
|
openUrl(WALLET_URL);
|
|
12014
13411
|
exit();
|
|
12015
13412
|
}
|
|
12016
13413
|
});
|
|
12017
|
-
return /* @__PURE__ */
|
|
12018
|
-
/* @__PURE__ */
|
|
12019
|
-
/* @__PURE__ */
|
|
12020
|
-
|
|
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,
|
|
12021
13418
|
{
|
|
12022
13419
|
flexDirection: "column",
|
|
12023
13420
|
borderStyle: "round",
|
|
@@ -12025,12 +13422,12 @@ var AddPaymentMethod = () => {
|
|
|
12025
13422
|
paddingX: 2,
|
|
12026
13423
|
paddingY: 1,
|
|
12027
13424
|
children: [
|
|
12028
|
-
/* @__PURE__ */
|
|
13425
|
+
/* @__PURE__ */ jsxs10(Text12, { children: [
|
|
12029
13426
|
"Open:",
|
|
12030
13427
|
" ",
|
|
12031
|
-
/* @__PURE__ */
|
|
13428
|
+
/* @__PURE__ */ jsx16(Text12, { bold: true, color: "cyan", children: WALLET_URL })
|
|
12032
13429
|
] }),
|
|
12033
|
-
/* @__PURE__ */
|
|
13430
|
+
/* @__PURE__ */ jsx16(Text12, { dimColor: true, children: "Press Enter to open in browser" })
|
|
12034
13431
|
]
|
|
12035
13432
|
}
|
|
12036
13433
|
)
|
|
@@ -12038,20 +13435,20 @@ var AddPaymentMethod = () => {
|
|
|
12038
13435
|
};
|
|
12039
13436
|
|
|
12040
13437
|
// src/commands/payment-methods/list.tsx
|
|
12041
|
-
import { Box as
|
|
13438
|
+
import { Box as Box12, Text as Text13 } from "ink";
|
|
12042
13439
|
import Spinner3 from "ink-spinner";
|
|
12043
|
-
import { useEffect as
|
|
12044
|
-
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";
|
|
12045
13442
|
var PaymentMethodsList = ({
|
|
12046
13443
|
resource,
|
|
12047
13444
|
onComplete
|
|
12048
13445
|
}) => {
|
|
12049
|
-
const [status, setStatus] =
|
|
13446
|
+
const [status, setStatus] = useState8(
|
|
12050
13447
|
"loading"
|
|
12051
13448
|
);
|
|
12052
|
-
const [methods, setMethods] =
|
|
12053
|
-
const [error, setError] =
|
|
12054
|
-
|
|
13449
|
+
const [methods, setMethods] = useState8([]);
|
|
13450
|
+
const [error, setError] = useState8("");
|
|
13451
|
+
useEffect7(() => {
|
|
12055
13452
|
const fetch2 = async () => {
|
|
12056
13453
|
try {
|
|
12057
13454
|
const result = await resource.listPaymentMethods();
|
|
@@ -12067,43 +13464,43 @@ var PaymentMethodsList = ({
|
|
|
12067
13464
|
fetch2();
|
|
12068
13465
|
}, [resource, onComplete]);
|
|
12069
13466
|
if (status === "loading") {
|
|
12070
|
-
return /* @__PURE__ */
|
|
12071
|
-
/* @__PURE__ */
|
|
13467
|
+
return /* @__PURE__ */ jsx17(Box12, { children: /* @__PURE__ */ jsxs11(Text13, { color: "cyan", children: [
|
|
13468
|
+
/* @__PURE__ */ jsx17(Spinner3, { type: "dots" }),
|
|
12072
13469
|
" Loading payment methods..."
|
|
12073
13470
|
] }) });
|
|
12074
13471
|
}
|
|
12075
13472
|
if (status === "error") {
|
|
12076
|
-
return /* @__PURE__ */
|
|
12077
|
-
/* @__PURE__ */
|
|
12078
|
-
/* @__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 })
|
|
12079
13476
|
] });
|
|
12080
13477
|
}
|
|
12081
13478
|
if (methods.length === 0) {
|
|
12082
|
-
return /* @__PURE__ */
|
|
13479
|
+
return /* @__PURE__ */ jsx17(Box12, { children: /* @__PURE__ */ jsx17(Text13, { dimColor: true, children: "No payment methods found" }) });
|
|
12083
13480
|
}
|
|
12084
|
-
return /* @__PURE__ */
|
|
12085
|
-
/* @__PURE__ */
|
|
12086
|
-
/* @__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) => {
|
|
12087
13484
|
const label = pm.card_details?.brand ?? pm.bank_account_details?.bank_name ?? "Bank account";
|
|
12088
13485
|
const last4 = pm.card_details?.last4 ?? pm.bank_account_details?.last4;
|
|
12089
13486
|
const suffix = [pm.nickname ? `(${pm.nickname})` : ""].filter(Boolean).join(" ");
|
|
12090
|
-
return /* @__PURE__ */
|
|
12091
|
-
/* @__PURE__ */
|
|
13487
|
+
return /* @__PURE__ */ jsx17(Box12, { paddingX: 2, children: /* @__PURE__ */ jsxs11(Text13, { children: [
|
|
13488
|
+
/* @__PURE__ */ jsx17(Text13, { dimColor: true, children: pm.id }),
|
|
12092
13489
|
" ",
|
|
12093
13490
|
label,
|
|
12094
13491
|
" ****",
|
|
12095
13492
|
last4,
|
|
12096
13493
|
suffix ? ` ${suffix}` : "",
|
|
12097
|
-
pm.is_default ? /* @__PURE__ */
|
|
13494
|
+
pm.is_default ? /* @__PURE__ */ jsx17(Text13, { color: "green", children: " (default)" }) : ""
|
|
12098
13495
|
] }) }, pm.id);
|
|
12099
13496
|
}) })
|
|
12100
13497
|
] });
|
|
12101
13498
|
};
|
|
12102
13499
|
|
|
12103
13500
|
// src/commands/payment-methods/index.tsx
|
|
12104
|
-
import { jsx as
|
|
13501
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
12105
13502
|
function createPaymentMethodsCli(createResource) {
|
|
12106
|
-
const cli2 =
|
|
13503
|
+
const cli2 = Cli5.create("payment-methods", {
|
|
12107
13504
|
description: "Payment methods management commands"
|
|
12108
13505
|
});
|
|
12109
13506
|
cli2.command("list", {
|
|
@@ -12124,8 +13521,8 @@ function createPaymentMethodsCli(createResource) {
|
|
|
12124
13521
|
const resource = createResource();
|
|
12125
13522
|
if (!c.agent && !c.formatExplicit) {
|
|
12126
13523
|
return new Promise((resolve) => {
|
|
12127
|
-
const { waitUntilExit } =
|
|
12128
|
-
/* @__PURE__ */
|
|
13524
|
+
const { waitUntilExit } = render5(
|
|
13525
|
+
/* @__PURE__ */ jsx18(PaymentMethodsList, { resource, onComplete: () => {
|
|
12129
13526
|
} })
|
|
12130
13527
|
);
|
|
12131
13528
|
waitUntilExit().then(async () => {
|
|
@@ -12153,7 +13550,7 @@ function createPaymentMethodsCli(createResource) {
|
|
|
12153
13550
|
}
|
|
12154
13551
|
if (!c.agent && !c.formatExplicit) {
|
|
12155
13552
|
return new Promise((resolve) => {
|
|
12156
|
-
const { waitUntilExit } =
|
|
13553
|
+
const { waitUntilExit } = render5(/* @__PURE__ */ jsx18(AddPaymentMethod, {}));
|
|
12157
13554
|
waitUntilExit().then(() => resolve({ url: WALLET_URL }));
|
|
12158
13555
|
});
|
|
12159
13556
|
}
|
|
@@ -12164,25 +13561,25 @@ function createPaymentMethodsCli(createResource) {
|
|
|
12164
13561
|
}
|
|
12165
13562
|
|
|
12166
13563
|
// src/commands/spend-request/index.tsx
|
|
12167
|
-
import { Cli as
|
|
12168
|
-
import { render as
|
|
13564
|
+
import { Cli as Cli6, z as z7 } from "incur";
|
|
13565
|
+
import { render as render6 } from "ink";
|
|
12169
13566
|
|
|
12170
13567
|
// src/utils/line-item-parser.ts
|
|
12171
|
-
import { z as
|
|
12172
|
-
var LineItemSchema =
|
|
12173
|
-
name:
|
|
12174
|
-
url:
|
|
12175
|
-
image_url:
|
|
12176
|
-
description:
|
|
12177
|
-
sku:
|
|
12178
|
-
quantity:
|
|
12179
|
-
unit_amount:
|
|
12180
|
-
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()
|
|
12181
13578
|
}).strict();
|
|
12182
|
-
var TotalSchema =
|
|
12183
|
-
type:
|
|
12184
|
-
display_text:
|
|
12185
|
-
amount:
|
|
13579
|
+
var TotalSchema = z5.object({
|
|
13580
|
+
type: z5.string(),
|
|
13581
|
+
display_text: z5.string(),
|
|
13582
|
+
amount: z5.coerce.number()
|
|
12186
13583
|
}).strict();
|
|
12187
13584
|
function parseKvString(raw) {
|
|
12188
13585
|
const result = {};
|
|
@@ -12207,7 +13604,7 @@ function parseLineItemFlag(raw) {
|
|
|
12207
13604
|
try {
|
|
12208
13605
|
return LineItemSchema.parse(obj);
|
|
12209
13606
|
} catch (err) {
|
|
12210
|
-
if (err instanceof
|
|
13607
|
+
if (err instanceof z5.ZodError) throw formatZodError(err, "Line item");
|
|
12211
13608
|
throw err;
|
|
12212
13609
|
}
|
|
12213
13610
|
}
|
|
@@ -12216,80 +13613,26 @@ function parseTotalFlag(raw) {
|
|
|
12216
13613
|
try {
|
|
12217
13614
|
return TotalSchema.parse(obj);
|
|
12218
13615
|
} catch (err) {
|
|
12219
|
-
if (err instanceof
|
|
13616
|
+
if (err instanceof z5.ZodError) throw formatZodError(err, "Total");
|
|
12220
13617
|
throw err;
|
|
12221
13618
|
}
|
|
12222
13619
|
}
|
|
12223
13620
|
|
|
12224
13621
|
// src/commands/spend-request/create.tsx
|
|
12225
|
-
import { Box as
|
|
13622
|
+
import { Box as Box14, Text as Text15 } from "ink";
|
|
12226
13623
|
import Spinner5 from "ink-spinner";
|
|
12227
|
-
import { useCallback, useEffect as
|
|
12228
|
-
|
|
12229
|
-
// src/commands/spend-request/app-download-qr-codes.tsx
|
|
12230
|
-
import { Box as Box7, Text as Text7 } from "ink";
|
|
12231
|
-
import { useMemo } from "react";
|
|
12232
|
-
|
|
12233
|
-
// src/utils/render-qr-matrix.ts
|
|
12234
|
-
import QRCode from "qrcode";
|
|
12235
|
-
function renderQrMatrix(url) {
|
|
12236
|
-
const qr = QRCode.create(url, { errorCorrectionLevel: "L" });
|
|
12237
|
-
const size = qr.modules.size;
|
|
12238
|
-
const data = qr.modules.data;
|
|
12239
|
-
const quiet = 1;
|
|
12240
|
-
const total = size + quiet * 2;
|
|
12241
|
-
const matrix = Array.from(
|
|
12242
|
-
{ length: total },
|
|
12243
|
-
(_, r) => Array.from({ length: total }, (_2, c) => {
|
|
12244
|
-
if (r < quiet || r >= size + quiet || c < quiet || c >= size + quiet) {
|
|
12245
|
-
return false;
|
|
12246
|
-
}
|
|
12247
|
-
return data[(r - quiet) * size + (c - quiet)] === 1;
|
|
12248
|
-
})
|
|
12249
|
-
);
|
|
12250
|
-
const lines = [];
|
|
12251
|
-
for (let r = 0; r < total; r += 2) {
|
|
12252
|
-
let line = "";
|
|
12253
|
-
for (let c = 0; c < total; c++) {
|
|
12254
|
-
const top = matrix[r][c];
|
|
12255
|
-
const bottom = r + 1 < total ? matrix[r + 1][c] : false;
|
|
12256
|
-
if (top && bottom) line += "\u2588";
|
|
12257
|
-
else if (top) line += "\u2580";
|
|
12258
|
-
else if (bottom) line += "\u2584";
|
|
12259
|
-
else line += " ";
|
|
12260
|
-
}
|
|
12261
|
-
lines.push(line);
|
|
12262
|
-
}
|
|
12263
|
-
return lines;
|
|
12264
|
-
}
|
|
12265
|
-
|
|
12266
|
-
// src/commands/spend-request/app-download-qr-codes.tsx
|
|
12267
|
-
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
12268
|
-
var DOWNLOAD_URL = "https://link.com/download";
|
|
12269
|
-
var AppDownloadQrCodes = () => {
|
|
12270
|
-
const qrLines = useMemo(() => renderQrMatrix(DOWNLOAD_URL), []);
|
|
12271
|
-
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", marginTop: 1, children: [
|
|
12272
|
-
/* @__PURE__ */ jsx10(Text7, { dimColor: true, children: "New! Get the Link app to approve spend requests easily" }),
|
|
12273
|
-
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", alignItems: "flex-start", marginTop: 1, children: [
|
|
12274
|
-
qrLines.map((line, i) => (
|
|
12275
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: stable static array
|
|
12276
|
-
/* @__PURE__ */ jsx10(Text7, { children: line }, i)
|
|
12277
|
-
)),
|
|
12278
|
-
/* @__PURE__ */ jsx10(Text7, { dimColor: true, children: DOWNLOAD_URL })
|
|
12279
|
-
] })
|
|
12280
|
-
] });
|
|
12281
|
-
};
|
|
13624
|
+
import { useCallback as useCallback2, useEffect as useEffect9, useState as useState9 } from "react";
|
|
12282
13625
|
|
|
12283
13626
|
// src/commands/spend-request/approval-waiting-view.tsx
|
|
12284
|
-
import { Box as
|
|
13627
|
+
import { Box as Box13, Text as Text14 } from "ink";
|
|
12285
13628
|
import Spinner4 from "ink-spinner";
|
|
12286
|
-
import { jsx as
|
|
13629
|
+
import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
12287
13630
|
var ApprovalWaitingView = ({
|
|
12288
13631
|
status,
|
|
12289
13632
|
approvalUrl
|
|
12290
|
-
}) => /* @__PURE__ */
|
|
12291
|
-
/* @__PURE__ */
|
|
12292
|
-
|
|
13633
|
+
}) => /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingY: 1, children: [
|
|
13634
|
+
/* @__PURE__ */ jsxs12(
|
|
13635
|
+
Box13,
|
|
12293
13636
|
{
|
|
12294
13637
|
flexDirection: "column",
|
|
12295
13638
|
borderStyle: "round",
|
|
@@ -12297,51 +13640,25 @@ var ApprovalWaitingView = ({
|
|
|
12297
13640
|
paddingX: 2,
|
|
12298
13641
|
paddingY: 1,
|
|
12299
13642
|
children: [
|
|
12300
|
-
/* @__PURE__ */
|
|
13643
|
+
/* @__PURE__ */ jsxs12(Text14, { children: [
|
|
12301
13644
|
"Approve at:",
|
|
12302
13645
|
" ",
|
|
12303
|
-
/* @__PURE__ */
|
|
13646
|
+
/* @__PURE__ */ jsx19(Text14, { bold: true, color: "cyan", children: approvalUrl })
|
|
12304
13647
|
] }),
|
|
12305
|
-
/* @__PURE__ */
|
|
13648
|
+
/* @__PURE__ */ jsx19(Text14, { dimColor: true, children: "Press Enter to open in browser" })
|
|
12306
13649
|
]
|
|
12307
13650
|
}
|
|
12308
13651
|
),
|
|
12309
|
-
/* @__PURE__ */
|
|
12310
|
-
/* @__PURE__ */
|
|
12311
|
-
/* @__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" }),
|
|
12312
13655
|
" Waiting for approval..."
|
|
12313
|
-
] }) : /* @__PURE__ */
|
|
13656
|
+
] }) : /* @__PURE__ */ jsx19(Text14, { dimColor: true, children: "Waiting..." }) })
|
|
12314
13657
|
] });
|
|
12315
13658
|
|
|
12316
13659
|
// src/commands/spend-request/use-approval-polling.ts
|
|
12317
|
-
import { useInput as
|
|
12318
|
-
import { useEffect as
|
|
12319
|
-
|
|
12320
|
-
// src/utils/poll-until-approved.ts
|
|
12321
|
-
function pollUntilApproved(repository, id, options = {}) {
|
|
12322
|
-
const pollIntervalMs = options.pollIntervalMs ?? 2e3;
|
|
12323
|
-
const timeoutMs = options.timeoutMs ?? 3e5;
|
|
12324
|
-
const startTime = Date.now();
|
|
12325
|
-
const poll = async () => {
|
|
12326
|
-
const elapsed = Date.now() - startTime;
|
|
12327
|
-
if (elapsed > timeoutMs) {
|
|
12328
|
-
throw new Error("Approval polling timed out");
|
|
12329
|
-
}
|
|
12330
|
-
const request = await repository.getSpendRequest(id);
|
|
12331
|
-
if (!request) {
|
|
12332
|
-
throw new Error(`Spend request ${id} not found`);
|
|
12333
|
-
}
|
|
12334
|
-
if (request.status !== "created" && request.status !== "pending_approval") {
|
|
12335
|
-
return request;
|
|
12336
|
-
}
|
|
12337
|
-
options.onProgress?.(Math.floor(elapsed / 1e3));
|
|
12338
|
-
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
12339
|
-
return poll();
|
|
12340
|
-
};
|
|
12341
|
-
return poll();
|
|
12342
|
-
}
|
|
12343
|
-
|
|
12344
|
-
// src/commands/spend-request/use-approval-polling.ts
|
|
13660
|
+
import { useInput as useInput7 } from "ink";
|
|
13661
|
+
import { useEffect as useEffect8 } from "react";
|
|
12345
13662
|
function useApprovalPolling({
|
|
12346
13663
|
status,
|
|
12347
13664
|
setStatus,
|
|
@@ -12353,18 +13670,18 @@ function useApprovalPolling({
|
|
|
12353
13670
|
onError
|
|
12354
13671
|
}) {
|
|
12355
13672
|
const isWaiting = status === "waiting" || status === "polling";
|
|
12356
|
-
|
|
13673
|
+
useInput7(
|
|
12357
13674
|
(_input, key) => {
|
|
12358
13675
|
if (key.return && approvalUrl) openUrl(approvalUrl);
|
|
12359
13676
|
},
|
|
12360
13677
|
{ isActive: isWaiting }
|
|
12361
13678
|
);
|
|
12362
|
-
|
|
13679
|
+
useEffect8(() => {
|
|
12363
13680
|
if (status !== "waiting") return;
|
|
12364
13681
|
const timeout = setTimeout(() => setStatus("polling"), 1e3);
|
|
12365
13682
|
return () => clearTimeout(timeout);
|
|
12366
13683
|
}, [status, setStatus]);
|
|
12367
|
-
|
|
13684
|
+
useEffect8(() => {
|
|
12368
13685
|
if (status !== "polling" || !requestId) return;
|
|
12369
13686
|
let cancelled = false;
|
|
12370
13687
|
const poll = async () => {
|
|
@@ -12398,22 +13715,22 @@ function useApprovalPolling({
|
|
|
12398
13715
|
}
|
|
12399
13716
|
|
|
12400
13717
|
// src/commands/spend-request/create.tsx
|
|
12401
|
-
import { Fragment, jsx as
|
|
13718
|
+
import { Fragment as Fragment3, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
12402
13719
|
var CreateSpendRequest = ({
|
|
12403
13720
|
repository,
|
|
12404
13721
|
params,
|
|
12405
13722
|
requestApproval = false,
|
|
12406
13723
|
onComplete
|
|
12407
13724
|
}) => {
|
|
12408
|
-
const [status, setStatus] =
|
|
12409
|
-
const [request, setRequest] =
|
|
12410
|
-
const [error, setError] =
|
|
13725
|
+
const [status, setStatus] = useState9("creating");
|
|
13726
|
+
const [request, setRequest] = useState9(null);
|
|
13727
|
+
const [error, setError] = useState9("");
|
|
12411
13728
|
const approvalUrl = request?.approval_url ?? "";
|
|
12412
|
-
const onSuccess =
|
|
13729
|
+
const onSuccess = useCallback2(
|
|
12413
13730
|
(result) => setRequest(result),
|
|
12414
13731
|
[]
|
|
12415
13732
|
);
|
|
12416
|
-
const onError =
|
|
13733
|
+
const onError = useCallback2((msg) => setError(msg), []);
|
|
12417
13734
|
useApprovalPolling({
|
|
12418
13735
|
status,
|
|
12419
13736
|
setStatus,
|
|
@@ -12424,7 +13741,7 @@ var CreateSpendRequest = ({
|
|
|
12424
13741
|
onSuccess,
|
|
12425
13742
|
onError
|
|
12426
13743
|
});
|
|
12427
|
-
|
|
13744
|
+
useEffect9(() => {
|
|
12428
13745
|
const create = async () => {
|
|
12429
13746
|
try {
|
|
12430
13747
|
const result = await repository.createSpendRequest(params);
|
|
@@ -12444,57 +13761,57 @@ var CreateSpendRequest = ({
|
|
|
12444
13761
|
create();
|
|
12445
13762
|
}, [repository, params, requestApproval, onComplete]);
|
|
12446
13763
|
if (status === "creating") {
|
|
12447
|
-
return /* @__PURE__ */
|
|
12448
|
-
/* @__PURE__ */
|
|
13764
|
+
return /* @__PURE__ */ jsx20(Box14, { children: /* @__PURE__ */ jsxs13(Text15, { color: "cyan", children: [
|
|
13765
|
+
/* @__PURE__ */ jsx20(Spinner5, { type: "dots" }),
|
|
12449
13766
|
" Creating spend request..."
|
|
12450
13767
|
] }) });
|
|
12451
13768
|
}
|
|
12452
13769
|
if (status === "error") {
|
|
12453
|
-
return /* @__PURE__ */
|
|
12454
|
-
/* @__PURE__ */
|
|
12455
|
-
/* @__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 })
|
|
12456
13773
|
] });
|
|
12457
13774
|
}
|
|
12458
13775
|
if (status === "success") {
|
|
12459
|
-
return /* @__PURE__ */
|
|
12460
|
-
/* @__PURE__ */
|
|
12461
|
-
/* @__PURE__ */
|
|
12462
|
-
/* @__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: [
|
|
12463
13780
|
"ID: ",
|
|
12464
|
-
/* @__PURE__ */
|
|
13781
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.id })
|
|
12465
13782
|
] }),
|
|
12466
|
-
/* @__PURE__ */
|
|
13783
|
+
/* @__PURE__ */ jsxs13(Text15, { children: [
|
|
12467
13784
|
"Status: ",
|
|
12468
|
-
/* @__PURE__ */
|
|
13785
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.status })
|
|
12469
13786
|
] }),
|
|
12470
|
-
/* @__PURE__ */
|
|
13787
|
+
/* @__PURE__ */ jsxs13(Text15, { children: [
|
|
12471
13788
|
"Amount:",
|
|
12472
13789
|
" ",
|
|
12473
|
-
/* @__PURE__ */
|
|
13790
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: (() => {
|
|
12474
13791
|
const t = request?.totals.find((t2) => t2.type === "total");
|
|
12475
13792
|
return t ? String(t.amount) : "N/A";
|
|
12476
13793
|
})() })
|
|
12477
13794
|
] }),
|
|
12478
|
-
/* @__PURE__ */
|
|
13795
|
+
/* @__PURE__ */ jsxs13(Text15, { children: [
|
|
12479
13796
|
"Merchant: ",
|
|
12480
|
-
/* @__PURE__ */
|
|
13797
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.merchant_name })
|
|
12481
13798
|
] }),
|
|
12482
|
-
/* @__PURE__ */
|
|
13799
|
+
/* @__PURE__ */ jsxs13(Text15, { children: [
|
|
12483
13800
|
"Line Items:",
|
|
12484
13801
|
" ",
|
|
12485
|
-
/* @__PURE__ */
|
|
13802
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") || "N/A" })
|
|
12486
13803
|
] })
|
|
12487
13804
|
] }),
|
|
12488
|
-
/* @__PURE__ */
|
|
13805
|
+
/* @__PURE__ */ jsx20(AppDownloadQrCodes, {})
|
|
12489
13806
|
] });
|
|
12490
13807
|
}
|
|
12491
|
-
return /* @__PURE__ */
|
|
12492
|
-
/* @__PURE__ */
|
|
13808
|
+
return /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
13809
|
+
/* @__PURE__ */ jsx20(Box14, { children: /* @__PURE__ */ jsxs13(Text15, { color: "green", children: [
|
|
12493
13810
|
"\u2713 Spend request created (ID: ",
|
|
12494
|
-
/* @__PURE__ */
|
|
13811
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.id }),
|
|
12495
13812
|
")"
|
|
12496
13813
|
] }) }),
|
|
12497
|
-
/* @__PURE__ */
|
|
13814
|
+
/* @__PURE__ */ jsx20(
|
|
12498
13815
|
ApprovalWaitingView,
|
|
12499
13816
|
{
|
|
12500
13817
|
status,
|
|
@@ -12505,21 +13822,21 @@ var CreateSpendRequest = ({
|
|
|
12505
13822
|
};
|
|
12506
13823
|
|
|
12507
13824
|
// src/commands/spend-request/request-approval.tsx
|
|
12508
|
-
import { Box as
|
|
13825
|
+
import { Box as Box15, Text as Text16 } from "ink";
|
|
12509
13826
|
import Spinner6 from "ink-spinner";
|
|
12510
|
-
import { useCallback as
|
|
12511
|
-
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";
|
|
12512
13829
|
var RequestApproval = ({
|
|
12513
13830
|
repository,
|
|
12514
13831
|
id,
|
|
12515
13832
|
onComplete
|
|
12516
13833
|
}) => {
|
|
12517
|
-
const [status, setStatus] =
|
|
12518
|
-
const [approvalUrl, setApprovalUrl] =
|
|
12519
|
-
const [result, setResult] =
|
|
12520
|
-
const [error, setError] =
|
|
12521
|
-
const onSuccess =
|
|
12522
|
-
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), []);
|
|
12523
13840
|
useApprovalPolling({
|
|
12524
13841
|
status,
|
|
12525
13842
|
setStatus,
|
|
@@ -12530,7 +13847,7 @@ var RequestApproval = ({
|
|
|
12530
13847
|
onSuccess,
|
|
12531
13848
|
onError
|
|
12532
13849
|
});
|
|
12533
|
-
|
|
13850
|
+
useEffect10(() => {
|
|
12534
13851
|
const request = async () => {
|
|
12535
13852
|
try {
|
|
12536
13853
|
const res = await repository.requestApproval(id);
|
|
@@ -12544,45 +13861,45 @@ var RequestApproval = ({
|
|
|
12544
13861
|
request();
|
|
12545
13862
|
}, [repository, id]);
|
|
12546
13863
|
if (status === "requesting") {
|
|
12547
|
-
return /* @__PURE__ */
|
|
12548
|
-
/* @__PURE__ */
|
|
13864
|
+
return /* @__PURE__ */ jsx21(Box15, { children: /* @__PURE__ */ jsxs14(Text16, { color: "cyan", children: [
|
|
13865
|
+
/* @__PURE__ */ jsx21(Spinner6, { type: "dots" }),
|
|
12549
13866
|
" Requesting approval..."
|
|
12550
13867
|
] }) });
|
|
12551
13868
|
}
|
|
12552
13869
|
if (status === "error") {
|
|
12553
|
-
return /* @__PURE__ */
|
|
12554
|
-
/* @__PURE__ */
|
|
12555
|
-
/* @__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 })
|
|
12556
13873
|
] });
|
|
12557
13874
|
}
|
|
12558
13875
|
if (status === "success") {
|
|
12559
|
-
return /* @__PURE__ */
|
|
12560
|
-
/* @__PURE__ */
|
|
12561
|
-
/* @__PURE__ */
|
|
12562
|
-
/* @__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: [
|
|
12563
13880
|
"ID: ",
|
|
12564
|
-
/* @__PURE__ */
|
|
13881
|
+
/* @__PURE__ */ jsx21(Text16, { bold: true, children: result?.id })
|
|
12565
13882
|
] }),
|
|
12566
|
-
/* @__PURE__ */
|
|
13883
|
+
/* @__PURE__ */ jsxs14(Text16, { children: [
|
|
12567
13884
|
"Status: ",
|
|
12568
|
-
/* @__PURE__ */
|
|
13885
|
+
/* @__PURE__ */ jsx21(Text16, { bold: true, children: result?.status })
|
|
12569
13886
|
] }),
|
|
12570
|
-
/* @__PURE__ */
|
|
13887
|
+
/* @__PURE__ */ jsxs14(Text16, { children: [
|
|
12571
13888
|
"Amount:",
|
|
12572
13889
|
" ",
|
|
12573
|
-
/* @__PURE__ */
|
|
13890
|
+
/* @__PURE__ */ jsx21(Text16, { bold: true, children: (() => {
|
|
12574
13891
|
const t = result?.totals.find((t2) => t2.type === "total");
|
|
12575
13892
|
return t ? String(t.amount) : "N/A";
|
|
12576
13893
|
})() })
|
|
12577
13894
|
] }),
|
|
12578
|
-
/* @__PURE__ */
|
|
13895
|
+
/* @__PURE__ */ jsxs14(Text16, { children: [
|
|
12579
13896
|
"Merchant: ",
|
|
12580
|
-
/* @__PURE__ */
|
|
13897
|
+
/* @__PURE__ */ jsx21(Text16, { bold: true, children: result?.merchant_name })
|
|
12581
13898
|
] })
|
|
12582
13899
|
] })
|
|
12583
13900
|
] });
|
|
12584
13901
|
}
|
|
12585
|
-
return /* @__PURE__ */
|
|
13902
|
+
return /* @__PURE__ */ jsx21(
|
|
12586
13903
|
ApprovalWaitingView,
|
|
12587
13904
|
{
|
|
12588
13905
|
status,
|
|
@@ -12592,10 +13909,10 @@ var RequestApproval = ({
|
|
|
12592
13909
|
};
|
|
12593
13910
|
|
|
12594
13911
|
// src/commands/spend-request/retrieve.tsx
|
|
12595
|
-
import { Box as
|
|
13912
|
+
import { Box as Box16, Text as Text17 } from "ink";
|
|
12596
13913
|
import Spinner7 from "ink-spinner";
|
|
12597
|
-
import { useEffect as
|
|
12598
|
-
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";
|
|
12599
13916
|
var RetrieveSpendRequest = ({
|
|
12600
13917
|
repository,
|
|
12601
13918
|
id,
|
|
@@ -12603,20 +13920,20 @@ var RetrieveSpendRequest = ({
|
|
|
12603
13920
|
include,
|
|
12604
13921
|
onComplete
|
|
12605
13922
|
}) => {
|
|
12606
|
-
const [phase, setPhase] =
|
|
12607
|
-
const [request, setRequest] =
|
|
12608
|
-
const [error, setError] =
|
|
12609
|
-
const [elapsed, setElapsed] =
|
|
12610
|
-
const startTimeRef =
|
|
12611
|
-
const pollRef =
|
|
12612
|
-
const timerRef =
|
|
12613
|
-
|
|
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(() => {
|
|
12614
13931
|
return () => {
|
|
12615
13932
|
if (pollRef.current) clearInterval(pollRef.current);
|
|
12616
13933
|
if (timerRef.current) clearInterval(timerRef.current);
|
|
12617
13934
|
};
|
|
12618
13935
|
}, []);
|
|
12619
|
-
|
|
13936
|
+
useEffect11(() => {
|
|
12620
13937
|
const fetch2 = async () => {
|
|
12621
13938
|
try {
|
|
12622
13939
|
const result = await repository.getSpendRequest(id, { include });
|
|
@@ -12645,7 +13962,7 @@ var RetrieveSpendRequest = ({
|
|
|
12645
13962
|
};
|
|
12646
13963
|
fetch2();
|
|
12647
13964
|
}, [repository, id, include, onComplete]);
|
|
12648
|
-
|
|
13965
|
+
useEffect11(() => {
|
|
12649
13966
|
if (phase !== "polling") return;
|
|
12650
13967
|
timerRef.current = setInterval(() => {
|
|
12651
13968
|
const secs = Math.floor((Date.now() - startTimeRef.current) / 1e3);
|
|
@@ -12684,169 +14001,169 @@ var RetrieveSpendRequest = ({
|
|
|
12684
14001
|
};
|
|
12685
14002
|
}, [phase, repository, id, include, timeout, onComplete]);
|
|
12686
14003
|
if (phase === "fetching") {
|
|
12687
|
-
return /* @__PURE__ */
|
|
12688
|
-
/* @__PURE__ */
|
|
14004
|
+
return /* @__PURE__ */ jsx22(Box16, { children: /* @__PURE__ */ jsxs15(Text17, { color: "cyan", children: [
|
|
14005
|
+
/* @__PURE__ */ jsx22(Spinner7, { type: "dots" }),
|
|
12689
14006
|
" Retrieving spend request ",
|
|
12690
14007
|
id,
|
|
12691
14008
|
"..."
|
|
12692
14009
|
] }) });
|
|
12693
14010
|
}
|
|
12694
14011
|
if (phase === "error") {
|
|
12695
|
-
return /* @__PURE__ */
|
|
14012
|
+
return /* @__PURE__ */ jsx22(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsxs15(Text17, { color: "red", children: [
|
|
12696
14013
|
"\u2717 ",
|
|
12697
14014
|
error
|
|
12698
14015
|
] }) });
|
|
12699
14016
|
}
|
|
12700
14017
|
if (phase === "timeout") {
|
|
12701
|
-
return /* @__PURE__ */
|
|
12702
|
-
/* @__PURE__ */
|
|
14018
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
14019
|
+
/* @__PURE__ */ jsxs15(Text17, { color: "yellow", children: [
|
|
12703
14020
|
"\u2717 Timed out waiting for approval after ",
|
|
12704
14021
|
timeout,
|
|
12705
14022
|
"s"
|
|
12706
14023
|
] }),
|
|
12707
|
-
request && /* @__PURE__ */
|
|
12708
|
-
/* @__PURE__ */
|
|
14024
|
+
request && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
14025
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12709
14026
|
"ID: ",
|
|
12710
|
-
/* @__PURE__ */
|
|
14027
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request.id })
|
|
12711
14028
|
] }),
|
|
12712
|
-
/* @__PURE__ */
|
|
14029
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12713
14030
|
"Status: ",
|
|
12714
|
-
/* @__PURE__ */
|
|
14031
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request.status })
|
|
12715
14032
|
] })
|
|
12716
14033
|
] })
|
|
12717
14034
|
] });
|
|
12718
14035
|
}
|
|
12719
14036
|
if (phase === "polling") {
|
|
12720
|
-
return /* @__PURE__ */
|
|
12721
|
-
/* @__PURE__ */
|
|
12722
|
-
/* @__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" }),
|
|
12723
14040
|
" Awaiting approval... (",
|
|
12724
14041
|
elapsed,
|
|
12725
14042
|
"s elapsed)"
|
|
12726
14043
|
] }) }),
|
|
12727
|
-
request?.approval_url && /* @__PURE__ */
|
|
14044
|
+
request?.approval_url && /* @__PURE__ */ jsx22(Box16, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsxs15(Text17, { dimColor: true, children: [
|
|
12728
14045
|
"Approval URL: ",
|
|
12729
|
-
/* @__PURE__ */
|
|
14046
|
+
/* @__PURE__ */ jsx22(Text17, { color: "cyan", children: request.approval_url })
|
|
12730
14047
|
] }) })
|
|
12731
14048
|
] });
|
|
12732
14049
|
}
|
|
12733
14050
|
if (phase === "declined") {
|
|
12734
|
-
return /* @__PURE__ */
|
|
12735
|
-
/* @__PURE__ */
|
|
12736
|
-
/* @__PURE__ */
|
|
12737
|
-
/* @__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: [
|
|
12738
14055
|
"ID: ",
|
|
12739
|
-
/* @__PURE__ */
|
|
14056
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.id })
|
|
12740
14057
|
] }),
|
|
12741
|
-
/* @__PURE__ */
|
|
14058
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12742
14059
|
"Status:",
|
|
12743
14060
|
" ",
|
|
12744
|
-
/* @__PURE__ */
|
|
14061
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, color: "red", children: request?.status })
|
|
12745
14062
|
] }),
|
|
12746
|
-
/* @__PURE__ */
|
|
14063
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12747
14064
|
"Amount:",
|
|
12748
14065
|
" ",
|
|
12749
|
-
/* @__PURE__ */
|
|
14066
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: (() => {
|
|
12750
14067
|
const t = request?.totals.find((t2) => t2.type === "total");
|
|
12751
14068
|
return t ? String(t.amount) : "N/A";
|
|
12752
14069
|
})() })
|
|
12753
14070
|
] }),
|
|
12754
|
-
/* @__PURE__ */
|
|
14071
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12755
14072
|
"Merchant: ",
|
|
12756
|
-
/* @__PURE__ */
|
|
14073
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.merchant_name })
|
|
12757
14074
|
] })
|
|
12758
14075
|
] })
|
|
12759
14076
|
] });
|
|
12760
14077
|
}
|
|
12761
|
-
return /* @__PURE__ */
|
|
12762
|
-
/* @__PURE__ */
|
|
12763
|
-
/* @__PURE__ */
|
|
12764
|
-
/* @__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: [
|
|
12765
14082
|
"ID: ",
|
|
12766
|
-
/* @__PURE__ */
|
|
14083
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.id })
|
|
12767
14084
|
] }),
|
|
12768
|
-
/* @__PURE__ */
|
|
14085
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12769
14086
|
"Status:",
|
|
12770
14087
|
" ",
|
|
12771
|
-
/* @__PURE__ */
|
|
14088
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, color: "green", children: request?.status })
|
|
12772
14089
|
] }),
|
|
12773
|
-
/* @__PURE__ */
|
|
14090
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12774
14091
|
"Amount:",
|
|
12775
14092
|
" ",
|
|
12776
|
-
/* @__PURE__ */
|
|
14093
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: (() => {
|
|
12777
14094
|
const t = request?.totals.find((t2) => t2.type === "total");
|
|
12778
14095
|
return t ? String(t.amount) : "N/A";
|
|
12779
14096
|
})() })
|
|
12780
14097
|
] }),
|
|
12781
|
-
/* @__PURE__ */
|
|
14098
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12782
14099
|
"Merchant: ",
|
|
12783
|
-
/* @__PURE__ */
|
|
14100
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.merchant_name })
|
|
12784
14101
|
] }),
|
|
12785
|
-
/* @__PURE__ */
|
|
14102
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12786
14103
|
"Line Items:",
|
|
12787
14104
|
" ",
|
|
12788
|
-
/* @__PURE__ */
|
|
14105
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
|
|
12789
14106
|
] }),
|
|
12790
|
-
request?.shared_payment_token && /* @__PURE__ */
|
|
12791
|
-
/* @__PURE__ */
|
|
14107
|
+
request?.shared_payment_token && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, children: [
|
|
14108
|
+
/* @__PURE__ */ jsxs15(Text17, { bold: true, children: [
|
|
12792
14109
|
"\x1B]8;;https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens\x07",
|
|
12793
14110
|
"Shared Payment Token",
|
|
12794
14111
|
"\x1B]8;;\x07",
|
|
12795
14112
|
":"
|
|
12796
14113
|
] }),
|
|
12797
|
-
/* @__PURE__ */
|
|
14114
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12798
14115
|
" ",
|
|
12799
14116
|
"Token: ",
|
|
12800
|
-
/* @__PURE__ */
|
|
14117
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request.shared_payment_token.id })
|
|
12801
14118
|
] })
|
|
12802
14119
|
] }),
|
|
12803
|
-
request?.card && /* @__PURE__ */
|
|
12804
|
-
/* @__PURE__ */
|
|
12805
|
-
/* @__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: [
|
|
12806
14123
|
" ",
|
|
12807
14124
|
"Number: ",
|
|
12808
|
-
/* @__PURE__ */
|
|
14125
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.card.number })
|
|
12809
14126
|
] }),
|
|
12810
|
-
/* @__PURE__ */
|
|
14127
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12811
14128
|
" ",
|
|
12812
14129
|
"Brand: ",
|
|
12813
|
-
/* @__PURE__ */
|
|
14130
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.card.brand })
|
|
12814
14131
|
] }),
|
|
12815
|
-
/* @__PURE__ */
|
|
14132
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12816
14133
|
" ",
|
|
12817
14134
|
"Expiry:",
|
|
12818
14135
|
" ",
|
|
12819
|
-
/* @__PURE__ */
|
|
14136
|
+
/* @__PURE__ */ jsxs15(Text17, { bold: true, children: [
|
|
12820
14137
|
String(request?.card.exp_month).padStart(2, "0"),
|
|
12821
14138
|
"/",
|
|
12822
14139
|
request?.card.exp_year
|
|
12823
14140
|
] })
|
|
12824
14141
|
] }),
|
|
12825
|
-
request?.card.cvc && /* @__PURE__ */
|
|
14142
|
+
request?.card.cvc && /* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12826
14143
|
" ",
|
|
12827
14144
|
"CVC: ",
|
|
12828
|
-
/* @__PURE__ */
|
|
14145
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request.card.cvc })
|
|
12829
14146
|
] }),
|
|
12830
|
-
request?.card.valid_until && /* @__PURE__ */
|
|
14147
|
+
request?.card.valid_until && /* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12831
14148
|
" ",
|
|
12832
14149
|
"Valid Until: ",
|
|
12833
|
-
/* @__PURE__ */
|
|
14150
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request.card.valid_until })
|
|
12834
14151
|
] }),
|
|
12835
|
-
request?.card.billing_address && /* @__PURE__ */
|
|
12836
|
-
/* @__PURE__ */
|
|
12837
|
-
/* @__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: [
|
|
12838
14155
|
" ",
|
|
12839
14156
|
request.card.billing_address.name
|
|
12840
14157
|
] }),
|
|
12841
|
-
/* @__PURE__ */
|
|
14158
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12842
14159
|
" ",
|
|
12843
14160
|
request.card.billing_address.line1
|
|
12844
14161
|
] }),
|
|
12845
|
-
request.card.billing_address.line2 && /* @__PURE__ */
|
|
14162
|
+
request.card.billing_address.line2 && /* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12846
14163
|
" ",
|
|
12847
14164
|
request.card.billing_address.line2
|
|
12848
14165
|
] }),
|
|
12849
|
-
/* @__PURE__ */
|
|
14166
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12850
14167
|
" ",
|
|
12851
14168
|
[
|
|
12852
14169
|
request.card.billing_address.city,
|
|
@@ -12854,7 +14171,7 @@ var RetrieveSpendRequest = ({
|
|
|
12854
14171
|
request.card.billing_address.postal_code
|
|
12855
14172
|
].filter(Boolean).join(", ")
|
|
12856
14173
|
] }),
|
|
12857
|
-
/* @__PURE__ */
|
|
14174
|
+
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
12858
14175
|
" ",
|
|
12859
14176
|
request.card.billing_address.country
|
|
12860
14177
|
] })
|
|
@@ -12865,69 +14182,69 @@ var RetrieveSpendRequest = ({
|
|
|
12865
14182
|
};
|
|
12866
14183
|
|
|
12867
14184
|
// src/commands/spend-request/schema.ts
|
|
12868
|
-
import { z as
|
|
12869
|
-
var createOptions =
|
|
12870
|
-
paymentMethodId:
|
|
12871
|
-
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(
|
|
12872
14189
|
'"card" for checkout forms/Stripe Elements; "shared_payment_token" for HTTP 402/machine payment flows'
|
|
12873
14190
|
),
|
|
12874
|
-
networkId:
|
|
14191
|
+
networkId: z6.string().optional().describe(
|
|
12875
14192
|
"Network ID (required for shared_payment_token) \u2014 use `link-cli mpp decode` to extract"
|
|
12876
14193
|
),
|
|
12877
|
-
amount:
|
|
12878
|
-
currency:
|
|
12879
|
-
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(
|
|
12880
14197
|
"Merchant name (required for card; forbidden for shared_payment_token)"
|
|
12881
14198
|
),
|
|
12882
|
-
merchantUrl:
|
|
14199
|
+
merchantUrl: z6.string().optional().describe(
|
|
12883
14200
|
"Merchant URL (required for card; forbidden for shared_payment_token)"
|
|
12884
14201
|
),
|
|
12885
|
-
context:
|
|
14202
|
+
context: z6.string().min(100).describe(
|
|
12886
14203
|
"Min 100 chars \u2014 describe the purchase and rationale; the user reads this when approving"
|
|
12887
14204
|
),
|
|
12888
|
-
lineItem:
|
|
12889
|
-
total:
|
|
12890
|
-
requestApproval:
|
|
12891
|
-
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(
|
|
12892
14209
|
"Use test mode (creates testmode credentials from test card data)"
|
|
12893
14210
|
)
|
|
12894
14211
|
});
|
|
12895
|
-
var retrieveOptions =
|
|
12896
|
-
timeout:
|
|
12897
|
-
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(
|
|
12898
14215
|
"Poll interval in seconds. When > 0, polls until status is terminal or timeout is reached, yielding status on each attempt."
|
|
12899
14216
|
),
|
|
12900
|
-
maxAttempts:
|
|
12901
|
-
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)")
|
|
12902
14219
|
});
|
|
12903
|
-
var updateOptions =
|
|
12904
|
-
paymentMethodId:
|
|
12905
|
-
amount:
|
|
12906
|
-
merchantUrl:
|
|
12907
|
-
profileId:
|
|
12908
|
-
merchantId:
|
|
12909
|
-
currency:
|
|
12910
|
-
lineItem:
|
|
12911
|
-
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)")
|
|
12912
14229
|
});
|
|
12913
14230
|
|
|
12914
14231
|
// src/commands/spend-request/update.tsx
|
|
12915
|
-
import { Box as
|
|
14232
|
+
import { Box as Box17, Text as Text18 } from "ink";
|
|
12916
14233
|
import Spinner8 from "ink-spinner";
|
|
12917
|
-
import { useEffect as
|
|
12918
|
-
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";
|
|
12919
14236
|
var UpdateSpendRequest = ({
|
|
12920
14237
|
repository,
|
|
12921
14238
|
id,
|
|
12922
14239
|
params,
|
|
12923
14240
|
onComplete
|
|
12924
14241
|
}) => {
|
|
12925
|
-
const [status, setStatus] =
|
|
14242
|
+
const [status, setStatus] = useState12(
|
|
12926
14243
|
"loading"
|
|
12927
14244
|
);
|
|
12928
|
-
const [request, setRequest] =
|
|
12929
|
-
const [error, setError] =
|
|
12930
|
-
|
|
14245
|
+
const [request, setRequest] = useState12(null);
|
|
14246
|
+
const [error, setError] = useState12("");
|
|
14247
|
+
useEffect12(() => {
|
|
12931
14248
|
const update = async () => {
|
|
12932
14249
|
try {
|
|
12933
14250
|
const result = await repository.updateSpendRequest(id, params);
|
|
@@ -12943,55 +14260,55 @@ var UpdateSpendRequest = ({
|
|
|
12943
14260
|
update();
|
|
12944
14261
|
}, [repository, id, params, onComplete]);
|
|
12945
14262
|
if (status === "loading") {
|
|
12946
|
-
return /* @__PURE__ */
|
|
12947
|
-
/* @__PURE__ */
|
|
14263
|
+
return /* @__PURE__ */ jsx23(Box17, { children: /* @__PURE__ */ jsxs16(Text18, { color: "cyan", children: [
|
|
14264
|
+
/* @__PURE__ */ jsx23(Spinner8, { type: "dots" }),
|
|
12948
14265
|
" Updating spend request ",
|
|
12949
14266
|
id,
|
|
12950
14267
|
"..."
|
|
12951
14268
|
] }) });
|
|
12952
14269
|
}
|
|
12953
14270
|
if (status === "error") {
|
|
12954
|
-
return /* @__PURE__ */
|
|
12955
|
-
/* @__PURE__ */
|
|
12956
|
-
/* @__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 })
|
|
12957
14274
|
] });
|
|
12958
14275
|
}
|
|
12959
|
-
return /* @__PURE__ */
|
|
12960
|
-
/* @__PURE__ */
|
|
12961
|
-
/* @__PURE__ */
|
|
12962
|
-
/* @__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: [
|
|
12963
14280
|
"ID: ",
|
|
12964
|
-
/* @__PURE__ */
|
|
14281
|
+
/* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.id })
|
|
12965
14282
|
] }),
|
|
12966
|
-
/* @__PURE__ */
|
|
14283
|
+
/* @__PURE__ */ jsxs16(Text18, { children: [
|
|
12967
14284
|
"Status: ",
|
|
12968
|
-
/* @__PURE__ */
|
|
14285
|
+
/* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.status })
|
|
12969
14286
|
] }),
|
|
12970
|
-
/* @__PURE__ */
|
|
14287
|
+
/* @__PURE__ */ jsxs16(Text18, { children: [
|
|
12971
14288
|
"Amount:",
|
|
12972
14289
|
" ",
|
|
12973
|
-
/* @__PURE__ */
|
|
14290
|
+
/* @__PURE__ */ jsx23(Text18, { bold: true, children: (() => {
|
|
12974
14291
|
const t = request?.totals.find((t2) => t2.type === "total");
|
|
12975
14292
|
return t ? String(t.amount) : "N/A";
|
|
12976
14293
|
})() })
|
|
12977
14294
|
] }),
|
|
12978
|
-
/* @__PURE__ */
|
|
14295
|
+
/* @__PURE__ */ jsxs16(Text18, { children: [
|
|
12979
14296
|
"Merchant: ",
|
|
12980
|
-
/* @__PURE__ */
|
|
14297
|
+
/* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.merchant_name })
|
|
12981
14298
|
] }),
|
|
12982
|
-
/* @__PURE__ */
|
|
14299
|
+
/* @__PURE__ */ jsxs16(Text18, { children: [
|
|
12983
14300
|
"Line Items:",
|
|
12984
14301
|
" ",
|
|
12985
|
-
/* @__PURE__ */
|
|
14302
|
+
/* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
|
|
12986
14303
|
] })
|
|
12987
14304
|
] })
|
|
12988
14305
|
] });
|
|
12989
14306
|
};
|
|
12990
14307
|
|
|
12991
14308
|
// src/commands/spend-request/index.tsx
|
|
12992
|
-
import { jsx as
|
|
14309
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
12993
14310
|
function createSpendRequestCli(repository) {
|
|
12994
|
-
const cli2 =
|
|
14311
|
+
const cli2 = Cli6.create("spend-request", {
|
|
12995
14312
|
description: "Spend request management commands"
|
|
12996
14313
|
});
|
|
12997
14314
|
cli2.command("create", {
|
|
@@ -13069,8 +14386,8 @@ function createSpendRequestCli(repository) {
|
|
|
13069
14386
|
};
|
|
13070
14387
|
if (!c.agent && !c.formatExplicit) {
|
|
13071
14388
|
return new Promise((resolve) => {
|
|
13072
|
-
const { waitUntilExit } =
|
|
13073
|
-
/* @__PURE__ */
|
|
14389
|
+
const { waitUntilExit } = render6(
|
|
14390
|
+
/* @__PURE__ */ jsx24(
|
|
13074
14391
|
CreateSpendRequest,
|
|
13075
14392
|
{
|
|
13076
14393
|
repository,
|
|
@@ -13104,8 +14421,8 @@ function createSpendRequestCli(repository) {
|
|
|
13104
14421
|
});
|
|
13105
14422
|
cli2.command("update", {
|
|
13106
14423
|
description: "Update a spend request",
|
|
13107
|
-
args:
|
|
13108
|
-
id:
|
|
14424
|
+
args: z7.object({
|
|
14425
|
+
id: z7.string().describe("Spend request ID")
|
|
13109
14426
|
}),
|
|
13110
14427
|
options: updateOptions,
|
|
13111
14428
|
outputPolicy: "agent-only",
|
|
@@ -13142,8 +14459,8 @@ function createSpendRequestCli(repository) {
|
|
|
13142
14459
|
);
|
|
13143
14460
|
if (!c.agent && !c.formatExplicit) {
|
|
13144
14461
|
return new Promise((resolve) => {
|
|
13145
|
-
const { waitUntilExit } =
|
|
13146
|
-
/* @__PURE__ */
|
|
14462
|
+
const { waitUntilExit } = render6(
|
|
14463
|
+
/* @__PURE__ */ jsx24(
|
|
13147
14464
|
UpdateSpendRequest,
|
|
13148
14465
|
{
|
|
13149
14466
|
repository,
|
|
@@ -13164,8 +14481,8 @@ function createSpendRequestCli(repository) {
|
|
|
13164
14481
|
});
|
|
13165
14482
|
cli2.command("request-approval", {
|
|
13166
14483
|
description: "Request approval for a spend request",
|
|
13167
|
-
args:
|
|
13168
|
-
id:
|
|
14484
|
+
args: z7.object({
|
|
14485
|
+
id: z7.string().describe("Spend request ID")
|
|
13169
14486
|
}),
|
|
13170
14487
|
outputPolicy: "agent-only",
|
|
13171
14488
|
async *run(c) {
|
|
@@ -13183,8 +14500,8 @@ function createSpendRequestCli(repository) {
|
|
|
13183
14500
|
const id = c.args.id;
|
|
13184
14501
|
if (!c.agent && !c.formatExplicit) {
|
|
13185
14502
|
return new Promise((resolve) => {
|
|
13186
|
-
const { waitUntilExit } =
|
|
13187
|
-
/* @__PURE__ */
|
|
14503
|
+
const { waitUntilExit } = render6(
|
|
14504
|
+
/* @__PURE__ */ jsx24(
|
|
13188
14505
|
RequestApproval,
|
|
13189
14506
|
{
|
|
13190
14507
|
repository,
|
|
@@ -13213,8 +14530,8 @@ function createSpendRequestCli(repository) {
|
|
|
13213
14530
|
});
|
|
13214
14531
|
cli2.command("retrieve", {
|
|
13215
14532
|
description: "Retrieve a spend request",
|
|
13216
|
-
args:
|
|
13217
|
-
id:
|
|
14533
|
+
args: z7.object({
|
|
14534
|
+
id: z7.string().describe("Spend request ID")
|
|
13218
14535
|
}),
|
|
13219
14536
|
options: retrieveOptions,
|
|
13220
14537
|
outputPolicy: "agent-only",
|
|
@@ -13239,8 +14556,8 @@ function createSpendRequestCli(repository) {
|
|
|
13239
14556
|
const include = includeArr?.length ? includeArr : void 0;
|
|
13240
14557
|
if (!c.agent && !c.formatExplicit) {
|
|
13241
14558
|
return new Promise((resolve) => {
|
|
13242
|
-
const { waitUntilExit } =
|
|
13243
|
-
/* @__PURE__ */
|
|
14559
|
+
const { waitUntilExit } = render6(
|
|
14560
|
+
/* @__PURE__ */ jsx24(
|
|
13244
14561
|
RetrieveSpendRequest,
|
|
13245
14562
|
{
|
|
13246
14563
|
repository,
|
|
@@ -13267,6 +14584,7 @@ function createSpendRequestCli(repository) {
|
|
|
13267
14584
|
]);
|
|
13268
14585
|
const deadline = Date.now() + timeout * 1e3;
|
|
13269
14586
|
let attempts = 0;
|
|
14587
|
+
let previousAttemptData;
|
|
13270
14588
|
while (true) {
|
|
13271
14589
|
const request = await repository.getSpendRequest(id, { include });
|
|
13272
14590
|
if (!request) {
|
|
@@ -13285,7 +14603,11 @@ function createSpendRequestCli(repository) {
|
|
|
13285
14603
|
yield request;
|
|
13286
14604
|
return;
|
|
13287
14605
|
}
|
|
13288
|
-
|
|
14606
|
+
const snapshot = JSON.stringify(request);
|
|
14607
|
+
if (snapshot !== previousAttemptData) {
|
|
14608
|
+
previousAttemptData = snapshot;
|
|
14609
|
+
yield request;
|
|
14610
|
+
}
|
|
13289
14611
|
await new Promise((resolve) => setTimeout(resolve, interval * 1e3));
|
|
13290
14612
|
}
|
|
13291
14613
|
}
|
|
@@ -13593,7 +14915,7 @@ var ResourceFactory = class {
|
|
|
13593
14915
|
};
|
|
13594
14916
|
|
|
13595
14917
|
// src/cli.tsx
|
|
13596
|
-
var cliVersion = "0.
|
|
14918
|
+
var cliVersion = "0.3.0";
|
|
13597
14919
|
var buildNumber = "1";
|
|
13598
14920
|
var cliName = "@stripe/link-cli";
|
|
13599
14921
|
var defaultHeaders = {
|
|
@@ -13607,7 +14929,7 @@ var spendRequestRepo = factory.createSpendRequestResource();
|
|
|
13607
14929
|
var notifier = updateNotifier({
|
|
13608
14930
|
pkg: { name: cliName, version: cliVersion }
|
|
13609
14931
|
});
|
|
13610
|
-
var cli =
|
|
14932
|
+
var cli = Cli7.create("link-cli", {
|
|
13611
14933
|
description: "Create a secure, one-time payment credential from a Link wallet to let agents complete purchases on behalf of users.",
|
|
13612
14934
|
version: `${cliVersion} (build ${buildNumber})`
|
|
13613
14935
|
});
|
|
@@ -13617,6 +14939,20 @@ cli.command(
|
|
|
13617
14939
|
createPaymentMethodsCli(() => factory.createPaymentMethodsResource())
|
|
13618
14940
|
);
|
|
13619
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
|
+
);
|
|
13620
14956
|
var isAgent = process.argv.includes("--format") || process.argv.includes("--mcp");
|
|
13621
14957
|
if (!isAgent) {
|
|
13622
14958
|
notifier.notify({ defer: false });
|