@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.
Files changed (3) hide show
  1. package/README.md +19 -1
  2. package/dist/cli.js +1761 -437
  3. 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 z7 = options.includePrerelease ? "-0" : "";
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${z7} <${+M + 1}.0.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${z7} <${M}.${+m + 1}.0-0`;
8717
+ ret = `>=${M}.${m}.0${z8} <${M}.${+m + 1}.0-0`;
8718
8718
  } else {
8719
- ret = `>=${M}.${m}.0${z7} <${+M + 1}.0.0-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}${z7} <${M}.${m}.${+p + 1}-0`;
8736
+ ret = `>=${M}.${m}.${p}${z8} <${M}.${m}.${+p + 1}-0`;
8737
8737
  } else {
8738
- ret = `>=${M}.${m}.${p}${z7} <${M}.${+m + 1}.0-0`;
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 Cli5 } from "incur";
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: "Link CLI Authentication" }) }),
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 jsx3 } from "react/jsx-runtime";
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__ */ jsx3(
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__ */ jsx3(Logout, { authResource, onComplete: () => {
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/mpp/index.tsx
11584
- import { Cli as Cli2, z as z3 } from "incur";
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 Box4, Text as Text4 } from "ink";
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 useEffect3, useState as useState3 } from "react";
11699
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
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] = useState3("retrieving");
11805
- const [result, setResult] = useState3(null);
11806
- const [error, setError] = useState3(null);
11807
- useEffect3(() => {
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__ */ jsxs3(Text4, { color: "red", children: [
12556
+ return /* @__PURE__ */ jsxs5(Text7, { color: "red", children: [
11877
12557
  "Error: ",
11878
12558
  error
11879
12559
  ] });
11880
12560
  }
11881
- return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", children: [
11882
- step !== "done" && /* @__PURE__ */ jsx5(Box4, { children: /* @__PURE__ */ jsxs3(Text4, { color: "cyan", children: [
11883
- /* @__PURE__ */ jsx5(Spinner2, { type: "dots" }),
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__ */ jsxs3(Box4, { flexDirection: "column", children: [
11889
- /* @__PURE__ */ jsxs3(Text4, { color: "green", children: [
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__ */ jsx5(Text4, { children: result.body })
12573
+ /* @__PURE__ */ jsx8(Text7, { children: result.body })
11894
12574
  ] })
11895
12575
  ] });
11896
12576
  }
11897
12577
 
11898
- // src/commands/mpp/schema.ts
11899
- import { z as z2 } from "incur";
11900
- var payOptions = z2.object({
11901
- spendRequestId: z2.string().describe(
11902
- 'Approved spend request ID with credential_type "shared_payment_token"'
11903
- ),
11904
- method: z2.string().optional().describe("HTTP method (default: GET, or POST if --data is provided)"),
11905
- data: z2.string().optional().describe("Request body (implies POST if --method is not set)"),
11906
- header: z2.array(z2.string()).default([]).describe('Request header in "Name: Value" format (repeatable)')
11907
- });
11908
- var decodeOptions = z2.object({
11909
- challenge: z2.string().describe(
11910
- "Raw WWW-Authenticate header value; may include multiple payment challenges"
11911
- )
11912
- });
11913
-
11914
- // src/commands/mpp/index.tsx
11915
- import { jsx as jsx6 } from "react/jsx-runtime";
11916
- function createMppCli(repository) {
11917
- const cli2 = Cli2.create("mpp", {
11918
- description: "Machine payment protocol (MPP) commands"
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: z3.object({
11923
- url: z3.string().describe("URL to pay")
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 } = render2(
11948
- /* @__PURE__ */ jsx6(
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 } = render2(
11995
- /* @__PURE__ */ jsx6(DecodeChallengeView, { decoded })
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 Cli3 } from "incur";
12008
- import { render as render3 } from "ink";
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 Box5, Text as Text5, useApp, useInput as useInput2 } from "ink";
12012
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
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
- useInput2((_input, key) => {
13406
+ useInput6((_input, key) => {
12017
13407
  if (key.return) {
12018
13408
  openUrl(WALLET_URL);
12019
13409
  exit();
12020
13410
  }
12021
13411
  });
12022
- return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", paddingY: 1, children: [
12023
- /* @__PURE__ */ jsx7(Box5, { marginBottom: 1, children: /* @__PURE__ */ jsx7(Text5, { bold: true, children: "Add Payment Method" }) }),
12024
- /* @__PURE__ */ jsxs4(
12025
- Box5,
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__ */ jsxs4(Text5, { children: [
13423
+ /* @__PURE__ */ jsxs10(Text12, { children: [
12034
13424
  "Open:",
12035
13425
  " ",
12036
- /* @__PURE__ */ jsx7(Text5, { bold: true, color: "cyan", children: WALLET_URL })
13426
+ /* @__PURE__ */ jsx16(Text12, { bold: true, color: "cyan", children: WALLET_URL })
12037
13427
  ] }),
12038
- /* @__PURE__ */ jsx7(Text5, { dimColor: true, children: "Press Enter to open in browser" })
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 Box6, Text as Text6 } from "ink";
13436
+ import { Box as Box12, Text as Text13 } from "ink";
12047
13437
  import Spinner3 from "ink-spinner";
12048
- import { useEffect as useEffect4, useState as useState4 } from "react";
12049
- import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
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] = useState4(
13444
+ const [status, setStatus] = useState9(
12055
13445
  "loading"
12056
13446
  );
12057
- const [methods, setMethods] = useState4([]);
12058
- const [error, setError] = useState4("");
12059
- useEffect4(() => {
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__ */ jsx8(Box6, { children: /* @__PURE__ */ jsxs5(Text6, { color: "cyan", children: [
12076
- /* @__PURE__ */ jsx8(Spinner3, { type: "dots" }),
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__ */ jsxs5(Box6, { flexDirection: "column", children: [
12082
- /* @__PURE__ */ jsx8(Text6, { color: "red", children: "\u2717 Failed to load payment methods" }),
12083
- /* @__PURE__ */ jsx8(Text6, { color: "red", children: error })
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__ */ jsx8(Box6, { children: /* @__PURE__ */ jsx8(Text6, { dimColor: true, children: "No payment methods found" }) });
13477
+ return /* @__PURE__ */ jsx17(Box12, { children: /* @__PURE__ */ jsx17(Text13, { dimColor: true, children: "No payment methods found" }) });
12088
13478
  }
12089
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
12090
- /* @__PURE__ */ jsx8(Text6, { bold: true, children: "Payment Methods" }),
12091
- /* @__PURE__ */ jsx8(Box6, { flexDirection: "column", marginTop: 1, children: methods.map((pm) => {
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__ */ jsx8(Box6, { paddingX: 2, children: /* @__PURE__ */ jsxs5(Text6, { children: [
12096
- /* @__PURE__ */ jsx8(Text6, { dimColor: true, children: pm.id }),
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__ */ jsx8(Text6, { color: "green", children: " (default)" }) : ""
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 jsx9 } from "react/jsx-runtime";
13499
+ import { jsx as jsx18 } from "react/jsx-runtime";
12110
13500
  function createPaymentMethodsCli(createResource) {
12111
- const cli2 = Cli3.create("payment-methods", {
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 } = render3(
12133
- /* @__PURE__ */ jsx9(PaymentMethodsList, { resource, onComplete: () => {
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 } = render3(/* @__PURE__ */ jsx9(AddPaymentMethod, {}));
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 Cli4, z as z6 } from "incur";
12173
- import { render as render4 } from "ink";
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 z4 } from "zod";
12177
- var LineItemSchema = z4.object({
12178
- name: z4.string(),
12179
- url: z4.string().optional(),
12180
- image_url: z4.string().optional(),
12181
- description: z4.string().optional(),
12182
- sku: z4.string().optional(),
12183
- quantity: z4.coerce.number().optional(),
12184
- unit_amount: z4.coerce.number().optional(),
12185
- product_url: z4.string().optional()
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 = z4.object({
12188
- type: z4.string(),
12189
- display_text: z4.string(),
12190
- amount: z4.coerce.number()
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 z4.ZodError) throw formatZodError(err, "Line item");
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 z4.ZodError) throw formatZodError(err, "Total");
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 Box9, Text as Text9 } from "ink";
13620
+ import { Box as Box14, Text as Text15 } from "ink";
12231
13621
  import Spinner5 from "ink-spinner";
12232
- import { useCallback, useEffect as useEffect6, useState as useState5 } from "react";
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 Box8, Text as Text8 } from "ink";
13625
+ import { Box as Box13, Text as Text14 } from "ink";
12290
13626
  import Spinner4 from "ink-spinner";
12291
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
13627
+ import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
12292
13628
  var ApprovalWaitingView = ({
12293
13629
  status,
12294
13630
  approvalUrl
12295
- }) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", paddingY: 1, children: [
12296
- /* @__PURE__ */ jsxs7(
12297
- Box8,
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__ */ jsxs7(Text8, { children: [
13641
+ /* @__PURE__ */ jsxs12(Text14, { children: [
12306
13642
  "Approve at:",
12307
13643
  " ",
12308
- /* @__PURE__ */ jsx11(Text8, { bold: true, color: "cyan", children: approvalUrl })
13644
+ /* @__PURE__ */ jsx19(Text14, { bold: true, color: "cyan", children: approvalUrl })
12309
13645
  ] }),
12310
- /* @__PURE__ */ jsx11(Text8, { dimColor: true, children: "Press Enter to open in browser" })
13646
+ /* @__PURE__ */ jsx19(Text14, { dimColor: true, children: "Press Enter to open in browser" })
12311
13647
  ]
12312
13648
  }
12313
13649
  ),
12314
- /* @__PURE__ */ jsx11(AppDownloadQrCodes, {}),
12315
- /* @__PURE__ */ jsx11(Box8, { marginTop: 1, children: status === "polling" ? /* @__PURE__ */ jsxs7(Text8, { color: "cyan", children: [
12316
- /* @__PURE__ */ jsx11(Spinner4, { type: "dots" }),
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__ */ jsx11(Text8, { dimColor: true, children: "Waiting..." }) })
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 useInput3 } from "ink";
12323
- import { useEffect as useEffect5 } from "react";
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
- useInput3(
13671
+ useInput7(
12362
13672
  (_input, key) => {
12363
13673
  if (key.return && approvalUrl) openUrl(approvalUrl);
12364
13674
  },
12365
13675
  { isActive: isWaiting }
12366
13676
  );
12367
- useEffect5(() => {
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
- useEffect5(() => {
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 jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
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] = useState5("creating");
12414
- const [request, setRequest] = useState5(null);
12415
- const [error, setError] = useState5("");
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 = useCallback(
13727
+ const onSuccess = useCallback2(
12418
13728
  (result) => setRequest(result),
12419
13729
  []
12420
13730
  );
12421
- const onError = useCallback((msg) => setError(msg), []);
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
- useEffect6(() => {
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__ */ jsx12(Box9, { children: /* @__PURE__ */ jsxs8(Text9, { color: "cyan", children: [
12453
- /* @__PURE__ */ jsx12(Spinner5, { type: "dots" }),
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__ */ jsxs8(Box9, { flexDirection: "column", children: [
12459
- /* @__PURE__ */ jsx12(Text9, { color: "red", children: "\u2717 Failed to create spend request" }),
12460
- /* @__PURE__ */ jsx12(Text9, { color: "red", children: error })
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__ */ jsxs8(Box9, { flexDirection: "column", children: [
12465
- /* @__PURE__ */ jsx12(Text9, { color: "green", children: "\u2713 Spend request created" }),
12466
- /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
12467
- /* @__PURE__ */ jsxs8(Text9, { children: [
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__ */ jsx12(Text9, { bold: true, children: request?.id })
13779
+ /* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.id })
12470
13780
  ] }),
12471
- /* @__PURE__ */ jsxs8(Text9, { children: [
13781
+ /* @__PURE__ */ jsxs13(Text15, { children: [
12472
13782
  "Status: ",
12473
- /* @__PURE__ */ jsx12(Text9, { bold: true, children: request?.status })
13783
+ /* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.status })
12474
13784
  ] }),
12475
- /* @__PURE__ */ jsxs8(Text9, { children: [
13785
+ /* @__PURE__ */ jsxs13(Text15, { children: [
12476
13786
  "Amount:",
12477
13787
  " ",
12478
- /* @__PURE__ */ jsx12(Text9, { bold: true, children: (() => {
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__ */ jsxs8(Text9, { children: [
13793
+ /* @__PURE__ */ jsxs13(Text15, { children: [
12484
13794
  "Merchant: ",
12485
- /* @__PURE__ */ jsx12(Text9, { bold: true, children: request?.merchant_name })
13795
+ /* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.merchant_name })
12486
13796
  ] }),
12487
- /* @__PURE__ */ jsxs8(Text9, { children: [
13797
+ /* @__PURE__ */ jsxs13(Text15, { children: [
12488
13798
  "Line Items:",
12489
13799
  " ",
12490
- /* @__PURE__ */ jsx12(Text9, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") || "N/A" })
13800
+ /* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") || "N/A" })
12491
13801
  ] })
12492
13802
  ] }),
12493
- /* @__PURE__ */ jsx12(AppDownloadQrCodes, {})
13803
+ /* @__PURE__ */ jsx20(AppDownloadQrCodes, {})
12494
13804
  ] });
12495
13805
  }
12496
- return /* @__PURE__ */ jsxs8(Fragment, { children: [
12497
- /* @__PURE__ */ jsx12(Box9, { children: /* @__PURE__ */ jsxs8(Text9, { color: "green", children: [
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__ */ jsx12(Text9, { bold: true, children: request?.id }),
13809
+ /* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.id }),
12500
13810
  ")"
12501
13811
  ] }) }),
12502
- /* @__PURE__ */ jsx12(
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 Box10, Text as Text10 } from "ink";
13823
+ import { Box as Box15, Text as Text16 } from "ink";
12514
13824
  import Spinner6 from "ink-spinner";
12515
- import { useCallback as useCallback2, useEffect as useEffect7, useState as useState6 } from "react";
12516
- import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
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] = useState6("requesting");
12523
- const [approvalUrl, setApprovalUrl] = useState6("");
12524
- const [result, setResult] = useState6(null);
12525
- const [error, setError] = useState6("");
12526
- const onSuccess = useCallback2((r) => setResult(r), []);
12527
- const onError = useCallback2((msg) => setError(msg), []);
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
- useEffect7(() => {
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__ */ jsx13(Box10, { children: /* @__PURE__ */ jsxs9(Text10, { color: "cyan", children: [
12553
- /* @__PURE__ */ jsx13(Spinner6, { type: "dots" }),
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__ */ jsxs9(Box10, { flexDirection: "column", children: [
12559
- /* @__PURE__ */ jsx13(Text10, { color: "red", children: "\u2717 Failed to request approval" }),
12560
- /* @__PURE__ */ jsx13(Text10, { color: "red", children: error })
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__ */ jsxs9(Box10, { flexDirection: "column", children: [
12565
- /* @__PURE__ */ jsx13(Text10, { color: "green", children: "\u2713 Approval completed" }),
12566
- /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
12567
- /* @__PURE__ */ jsxs9(Text10, { children: [
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__ */ jsx13(Text10, { bold: true, children: result?.id })
13879
+ /* @__PURE__ */ jsx21(Text16, { bold: true, children: result?.id })
12570
13880
  ] }),
12571
- /* @__PURE__ */ jsxs9(Text10, { children: [
13881
+ /* @__PURE__ */ jsxs14(Text16, { children: [
12572
13882
  "Status: ",
12573
- /* @__PURE__ */ jsx13(Text10, { bold: true, children: result?.status })
13883
+ /* @__PURE__ */ jsx21(Text16, { bold: true, children: result?.status })
12574
13884
  ] }),
12575
- /* @__PURE__ */ jsxs9(Text10, { children: [
13885
+ /* @__PURE__ */ jsxs14(Text16, { children: [
12576
13886
  "Amount:",
12577
13887
  " ",
12578
- /* @__PURE__ */ jsx13(Text10, { bold: true, children: (() => {
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__ */ jsxs9(Text10, { children: [
13893
+ /* @__PURE__ */ jsxs14(Text16, { children: [
12584
13894
  "Merchant: ",
12585
- /* @__PURE__ */ jsx13(Text10, { bold: true, children: result?.merchant_name })
13895
+ /* @__PURE__ */ jsx21(Text16, { bold: true, children: result?.merchant_name })
12586
13896
  ] })
12587
13897
  ] })
12588
13898
  ] });
12589
13899
  }
12590
- return /* @__PURE__ */ jsx13(
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 Box11, Text as Text11 } from "ink";
13910
+ import { Box as Box16, Text as Text17 } from "ink";
12601
13911
  import Spinner7 from "ink-spinner";
12602
- import { useEffect as useEffect8, useRef, useState as useState7 } from "react";
12603
- import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
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] = useState7("fetching");
12612
- const [request, setRequest] = useState7(null);
12613
- const [error, setError] = useState7("");
12614
- const [elapsed, setElapsed] = useState7(0);
12615
- const startTimeRef = useRef(Date.now());
12616
- const pollRef = useRef(null);
12617
- const timerRef = useRef(null);
12618
- useEffect8(() => {
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
- useEffect8(() => {
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
- useEffect8(() => {
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__ */ jsx14(Box11, { children: /* @__PURE__ */ jsxs10(Text11, { color: "cyan", children: [
12693
- /* @__PURE__ */ jsx14(Spinner7, { type: "dots" }),
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__ */ jsx14(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsxs10(Text11, { color: "red", children: [
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__ */ jsxs10(Box11, { flexDirection: "column", children: [
12707
- /* @__PURE__ */ jsxs10(Text11, { color: "yellow", children: [
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__ */ jsxs10(Box11, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
12713
- /* @__PURE__ */ jsxs10(Text11, { children: [
14022
+ request && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14023
+ /* @__PURE__ */ jsxs15(Text17, { children: [
12714
14024
  "ID: ",
12715
- /* @__PURE__ */ jsx14(Text11, { bold: true, children: request.id })
14025
+ /* @__PURE__ */ jsx22(Text17, { bold: true, children: request.id })
12716
14026
  ] }),
12717
- /* @__PURE__ */ jsxs10(Text11, { children: [
14027
+ /* @__PURE__ */ jsxs15(Text17, { children: [
12718
14028
  "Status: ",
12719
- /* @__PURE__ */ jsx14(Text11, { bold: true, children: request.status })
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__ */ jsxs10(Box11, { flexDirection: "column", children: [
12726
- /* @__PURE__ */ jsx14(Box11, { children: /* @__PURE__ */ jsxs10(Text11, { color: "cyan", children: [
12727
- /* @__PURE__ */ jsx14(Spinner7, { type: "dots" }),
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__ */ jsx14(Box11, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsxs10(Text11, { dimColor: true, children: [
14042
+ request?.approval_url && /* @__PURE__ */ jsx22(Box16, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsxs15(Text17, { dimColor: true, children: [
12733
14043
  "Approval URL: ",
12734
- /* @__PURE__ */ jsx14(Text11, { color: "cyan", children: request.approval_url })
14044
+ /* @__PURE__ */ jsx22(Text17, { color: "cyan", children: request.approval_url })
12735
14045
  ] }) })
12736
14046
  ] });
12737
14047
  }
12738
14048
  if (phase === "declined") {
12739
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", children: [
12740
- /* @__PURE__ */ jsx14(Text11, { color: "red", children: "\u2717 Spend request declined" }),
12741
- /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
12742
- /* @__PURE__ */ jsxs10(Text11, { children: [
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__ */ jsx14(Text11, { bold: true, children: request?.id })
14054
+ /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.id })
12745
14055
  ] }),
12746
- /* @__PURE__ */ jsxs10(Text11, { children: [
14056
+ /* @__PURE__ */ jsxs15(Text17, { children: [
12747
14057
  "Status:",
12748
14058
  " ",
12749
- /* @__PURE__ */ jsx14(Text11, { bold: true, color: "red", children: request?.status })
14059
+ /* @__PURE__ */ jsx22(Text17, { bold: true, color: "red", children: request?.status })
12750
14060
  ] }),
12751
- /* @__PURE__ */ jsxs10(Text11, { children: [
14061
+ /* @__PURE__ */ jsxs15(Text17, { children: [
12752
14062
  "Amount:",
12753
14063
  " ",
12754
- /* @__PURE__ */ jsx14(Text11, { bold: true, children: (() => {
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__ */ jsxs10(Text11, { children: [
14069
+ /* @__PURE__ */ jsxs15(Text17, { children: [
12760
14070
  "Merchant: ",
12761
- /* @__PURE__ */ jsx14(Text11, { bold: true, children: request?.merchant_name })
14071
+ /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.merchant_name })
12762
14072
  ] })
12763
14073
  ] })
12764
14074
  ] });
12765
14075
  }
12766
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", children: [
12767
- /* @__PURE__ */ jsx14(Text11, { color: "green", children: "\u2713 Spend request approved" }),
12768
- /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
12769
- /* @__PURE__ */ jsxs10(Text11, { children: [
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__ */ jsx14(Text11, { bold: true, children: request?.id })
14081
+ /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.id })
12772
14082
  ] }),
12773
- /* @__PURE__ */ jsxs10(Text11, { children: [
14083
+ /* @__PURE__ */ jsxs15(Text17, { children: [
12774
14084
  "Status:",
12775
14085
  " ",
12776
- /* @__PURE__ */ jsx14(Text11, { bold: true, color: "green", children: request?.status })
14086
+ /* @__PURE__ */ jsx22(Text17, { bold: true, color: "green", children: request?.status })
12777
14087
  ] }),
12778
- /* @__PURE__ */ jsxs10(Text11, { children: [
14088
+ /* @__PURE__ */ jsxs15(Text17, { children: [
12779
14089
  "Amount:",
12780
14090
  " ",
12781
- /* @__PURE__ */ jsx14(Text11, { bold: true, children: (() => {
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__ */ jsxs10(Text11, { children: [
14096
+ /* @__PURE__ */ jsxs15(Text17, { children: [
12787
14097
  "Merchant: ",
12788
- /* @__PURE__ */ jsx14(Text11, { bold: true, children: request?.merchant_name })
14098
+ /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.merchant_name })
12789
14099
  ] }),
12790
- /* @__PURE__ */ jsxs10(Text11, { children: [
14100
+ /* @__PURE__ */ jsxs15(Text17, { children: [
12791
14101
  "Line Items:",
12792
14102
  " ",
12793
- /* @__PURE__ */ jsx14(Text11, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
14103
+ /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
12794
14104
  ] }),
12795
- request?.shared_payment_token && /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", marginTop: 1, children: [
12796
- /* @__PURE__ */ jsxs10(Text11, { bold: true, children: [
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__ */ jsxs10(Text11, { children: [
14112
+ /* @__PURE__ */ jsxs15(Text17, { children: [
12803
14113
  " ",
12804
14114
  "Token: ",
12805
- /* @__PURE__ */ jsx14(Text11, { bold: true, children: request.shared_payment_token.id })
14115
+ /* @__PURE__ */ jsx22(Text17, { bold: true, children: request.shared_payment_token.id })
12806
14116
  ] })
12807
14117
  ] }),
12808
- request?.card && /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", marginTop: 1, children: [
12809
- /* @__PURE__ */ jsx14(Text11, { bold: true, children: "Card Details:" }),
12810
- /* @__PURE__ */ jsxs10(Text11, { children: [
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__ */ jsx14(Text11, { bold: true, children: request?.card.number })
14123
+ /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.card.number })
12814
14124
  ] }),
12815
- /* @__PURE__ */ jsxs10(Text11, { children: [
14125
+ /* @__PURE__ */ jsxs15(Text17, { children: [
12816
14126
  " ",
12817
14127
  "Brand: ",
12818
- /* @__PURE__ */ jsx14(Text11, { bold: true, children: request?.card.brand })
14128
+ /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.card.brand })
12819
14129
  ] }),
12820
- /* @__PURE__ */ jsxs10(Text11, { children: [
14130
+ /* @__PURE__ */ jsxs15(Text17, { children: [
12821
14131
  " ",
12822
14132
  "Expiry:",
12823
14133
  " ",
12824
- /* @__PURE__ */ jsxs10(Text11, { bold: true, children: [
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__ */ jsxs10(Text11, { children: [
14140
+ request?.card.cvc && /* @__PURE__ */ jsxs15(Text17, { children: [
12831
14141
  " ",
12832
14142
  "CVC: ",
12833
- /* @__PURE__ */ jsx14(Text11, { bold: true, children: request.card.cvc })
14143
+ /* @__PURE__ */ jsx22(Text17, { bold: true, children: request.card.cvc })
12834
14144
  ] }),
12835
- request?.card.valid_until && /* @__PURE__ */ jsxs10(Text11, { children: [
14145
+ request?.card.valid_until && /* @__PURE__ */ jsxs15(Text17, { children: [
12836
14146
  " ",
12837
14147
  "Valid Until: ",
12838
- /* @__PURE__ */ jsx14(Text11, { bold: true, children: request.card.valid_until })
14148
+ /* @__PURE__ */ jsx22(Text17, { bold: true, children: request.card.valid_until })
12839
14149
  ] }),
12840
- request?.card.billing_address && /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", marginTop: 1, children: [
12841
- /* @__PURE__ */ jsx14(Text11, { bold: true, children: " Billing Address:" }),
12842
- /* @__PURE__ */ jsxs10(Text11, { children: [
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__ */ jsxs10(Text11, { children: [
14156
+ /* @__PURE__ */ jsxs15(Text17, { children: [
12847
14157
  " ",
12848
14158
  request.card.billing_address.line1
12849
14159
  ] }),
12850
- request.card.billing_address.line2 && /* @__PURE__ */ jsxs10(Text11, { children: [
14160
+ request.card.billing_address.line2 && /* @__PURE__ */ jsxs15(Text17, { children: [
12851
14161
  " ",
12852
14162
  request.card.billing_address.line2
12853
14163
  ] }),
12854
- /* @__PURE__ */ jsxs10(Text11, { children: [
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__ */ jsxs10(Text11, { children: [
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 z5 } from "incur";
12874
- var createOptions = z5.object({
12875
- paymentMethodId: z5.string().describe("Payment method ID"),
12876
- credentialType: z5.enum(["shared_payment_token", "card"]).default("card").describe(
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: z5.string().optional().describe(
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: z5.coerce.number().int().positive().max(5e4).describe("Amount in cents, max 50000 ($500.00)"),
12883
- currency: z5.string().length(3).default("usd").describe("Currency code"),
12884
- merchantName: z5.string().optional().describe(
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: z5.string().optional().describe(
14197
+ merchantUrl: z6.string().optional().describe(
12888
14198
  "Merchant URL (required for card; forbidden for shared_payment_token)"
12889
14199
  ),
12890
- context: z5.string().min(100).describe(
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: z5.array(z5.union([z5.string(), z5.record(z5.string(), z5.unknown())])).default([]).describe("Line item (repeatable, key:value format)"),
12894
- total: z5.array(z5.union([z5.string(), z5.record(z5.string(), z5.unknown())])).default([]).describe("Total (repeatable, key:value format)"),
12895
- requestApproval: z5.boolean().default(true).describe("Request approval and poll until approved/denied/expired"),
12896
- test: z5.boolean().default(false).describe(
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 = z5.object({
12901
- timeout: z5.coerce.number().default(300).describe("Polling timeout in seconds"),
12902
- interval: z5.coerce.number().default(0).describe(
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: z5.coerce.number().default(0).describe("Max poll attempts. 0 = unlimited (use timeout instead)."),
12906
- include: z5.array(z5.string()).default([]).describe("Include extra data (repeatable, e.g. --include card)")
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 = z5.object({
12909
- paymentMethodId: z5.string().optional().describe("Payment method ID"),
12910
- amount: z5.coerce.number().optional().describe("Amount in cents"),
12911
- merchantUrl: z5.string().optional().describe("Merchant URL"),
12912
- profileId: z5.string().optional().describe("Profile ID"),
12913
- merchantId: z5.string().optional().describe("Merchant ID"),
12914
- currency: z5.string().optional().describe("Currency code"),
12915
- lineItem: z5.array(z5.union([z5.string(), z5.record(z5.string(), z5.unknown())])).default([]).describe("Line item (repeatable, key:value format)"),
12916
- total: z5.array(z5.union([z5.string(), z5.record(z5.string(), z5.unknown())])).default([]).describe("Total (repeatable, key:value format)")
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 Box12, Text as Text12 } from "ink";
14230
+ import { Box as Box17, Text as Text18 } from "ink";
12921
14231
  import Spinner8 from "ink-spinner";
12922
- import { useEffect as useEffect9, useState as useState8 } from "react";
12923
- import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
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] = useState8(
14240
+ const [status, setStatus] = useState13(
12931
14241
  "loading"
12932
14242
  );
12933
- const [request, setRequest] = useState8(null);
12934
- const [error, setError] = useState8("");
12935
- useEffect9(() => {
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__ */ jsx15(Box12, { children: /* @__PURE__ */ jsxs11(Text12, { color: "cyan", children: [
12952
- /* @__PURE__ */ jsx15(Spinner8, { type: "dots" }),
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__ */ jsxs11(Box12, { flexDirection: "column", children: [
12960
- /* @__PURE__ */ jsx15(Text12, { color: "red", children: "\u2717 Failed to update spend request" }),
12961
- /* @__PURE__ */ jsx15(Text12, { color: "red", children: error })
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__ */ jsxs11(Box12, { flexDirection: "column", children: [
12965
- /* @__PURE__ */ jsx15(Text12, { color: "green", children: "\u2713 Spend request updated" }),
12966
- /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
12967
- /* @__PURE__ */ jsxs11(Text12, { children: [
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__ */ jsx15(Text12, { bold: true, children: request?.id })
14279
+ /* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.id })
12970
14280
  ] }),
12971
- /* @__PURE__ */ jsxs11(Text12, { children: [
14281
+ /* @__PURE__ */ jsxs16(Text18, { children: [
12972
14282
  "Status: ",
12973
- /* @__PURE__ */ jsx15(Text12, { bold: true, children: request?.status })
14283
+ /* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.status })
12974
14284
  ] }),
12975
- /* @__PURE__ */ jsxs11(Text12, { children: [
14285
+ /* @__PURE__ */ jsxs16(Text18, { children: [
12976
14286
  "Amount:",
12977
14287
  " ",
12978
- /* @__PURE__ */ jsx15(Text12, { bold: true, children: (() => {
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__ */ jsxs11(Text12, { children: [
14293
+ /* @__PURE__ */ jsxs16(Text18, { children: [
12984
14294
  "Merchant: ",
12985
- /* @__PURE__ */ jsx15(Text12, { bold: true, children: request?.merchant_name })
14295
+ /* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.merchant_name })
12986
14296
  ] }),
12987
- /* @__PURE__ */ jsxs11(Text12, { children: [
14297
+ /* @__PURE__ */ jsxs16(Text18, { children: [
12988
14298
  "Line Items:",
12989
14299
  " ",
12990
- /* @__PURE__ */ jsx15(Text12, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
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 jsx16 } from "react/jsx-runtime";
14307
+ import { jsx as jsx24 } from "react/jsx-runtime";
12998
14308
  function createSpendRequestCli(repository) {
12999
- const cli2 = Cli4.create("spend-request", {
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 } = render4(
13078
- /* @__PURE__ */ jsx16(
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: z6.object({
13113
- id: z6.string().describe("Spend request 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 } = render4(
13151
- /* @__PURE__ */ jsx16(
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: z6.object({
13173
- id: z6.string().describe("Spend request 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 } = render4(
13192
- /* @__PURE__ */ jsx16(
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: z6.object({
13222
- id: z6.string().describe("Spend request 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 } = render4(
13248
- /* @__PURE__ */ jsx16(
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.2.3";
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 = Cli5.create("link-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 });