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